Get ready for 1000+ members (#14313)

The goal of this PR is to test if Twenty can support a large number of
members, a question which was raised by a large company that is
considering moving away from Salesforce.

I was expecting the currentWorkspaceMembersState to cause a lot more
issue. It would be very hard to get rid of it in the context of actors,
I think that would require a big refactoring. I thought we'd have to do
it but it turns out the perf are pretty good. One thing we need to
improve is the pagination on the roles page, we'll wait for @Bonapara to
update that
This commit is contained in:
Félix Malfait
2025-09-05 12:37:22 +02:00
committed by GitHub
parent 094d670590
commit d5f88e566c
246 changed files with 5467 additions and 703 deletions
@@ -2,14 +2,16 @@ import { SubscriptionInfoContainer } from '@/billing/components/SubscriptionInfo
import { SubscriptionInfoRowContainer } from '@/billing/components/SubscriptionInfoRowContainer';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useEndSubscriptionTrialPeriod } from '@/billing/hooks/useEndSubscriptionTrialPeriod';
import { formatMonthlyPrices } from '@/billing/utils/formatMonthlyPrices';
import {
isMonthlyPlan as isMonthlyPlanFn,
isYearlyPlan as isYearlyPlanFn,
isProPlan as isProPlanFn,
isEnterprisePlan as isEnterprisePlanFn,
getIntervalLabel,
isEnterprisePlan as isEnterprisePlanFn,
isMonthlyPlan as isMonthlyPlanFn,
isProPlan as isProPlanFn,
isYearlyPlan as isYearlyPlanFn,
} from '@/billing/utils/subscriptionFlags';
import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
import { useModal } from '@/ui/layout/modal/hooks/useModal';
@@ -23,10 +25,10 @@ import {
H2Title,
IconArrowUp,
IconCalendarEvent,
IconCalendarRepeat,
IconCircleX,
IconTag,
IconUsers,
IconCalendarRepeat,
} from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { Section } from 'twenty-ui/layout';
@@ -42,8 +44,6 @@ import {
useSwitchSubscriptionToYearlyIntervalMutation,
} from '~/generated-metadata/graphql';
import { beautifyExactDate } from '~/utils/date-utils';
import { useEndSubscriptionTrialPeriod } from '@/billing/hooks/useEndSubscriptionTrialPeriod';
import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap';
const SWITCH_BILLING_INTERVAL_MODAL_ID = 'switch-billing-interval-modal';
@@ -251,7 +251,7 @@ export const SettingsBillingSubscriptionInfo = () => {
subtitle={t`You will be charged $${yearlyPrice} per user per month billed annually. A prorata with your current subscription will be applied.`}
onConfirmClick={switchInterval}
confirmButtonText={t`Confirm`}
confirmButtonAccent={'blue'}
confirmButtonAccent="blue"
/>
<ConfirmationModal
modalId={SWITCH_BILLING_PLAN_MODAL_ID}
@@ -263,7 +263,7 @@ export const SettingsBillingSubscriptionInfo = () => {
}
onConfirmClick={switchPlan}
confirmButtonText={t`Confirm`}
confirmButtonAccent={'blue'}
confirmButtonAccent="blue"
/>
<ConfirmationModal
modalId={END_TRIAL_PERIOD_MODAL_ID}
@@ -271,7 +271,7 @@ export const SettingsBillingSubscriptionInfo = () => {
subtitle={t`We will activate your paid plan. Do you want to proceed?`}
onConfirmClick={endTrialPeriod}
confirmButtonText={t`Confirm`}
confirmButtonAccent={'blue'}
confirmButtonAccent="blue"
loading={isEndTrialPeriodLoading}
/>
</Section>
@@ -1,20 +1,20 @@
import { t } from '@lingui/core/macro';
import { useMutation } from '@apollo/client';
import { t } from '@lingui/core/macro';
import { useMemo, useState } from 'react';
import { H2Title } from 'twenty-ui/display';
import { UPDATE_SUBSCRIPTION_ITEM_PRICE } from '@/billing/graphql/mutations/updateSubscriptionItemPrice';
import { findMeteredPriceInCurrentWorkspaceSubscriptions } from '@/billing/utils/findPriceInCurrentWorkspaceSubscriptions';
import { Select } from '@/ui/input/components/Select';
import { getIntervalLabel } from '@/billing/utils/subscriptionFlags';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { formatNumber } from '~/utils/format/number';
import { Select } from '@/ui/input/components/Select';
import {
type BillingPriceOutput,
type BillingSubscriptionItem,
SubscriptionInterval,
} from '~/generated/graphql';
import { findOrThrow } from '~/utils/array/findOrThrow';
import { getIntervalLabel } from '@/billing/utils/subscriptionFlags';
import { formatNumber } from '~/utils/format/number';
const compareByAmountAsc = (a: BillingPriceOutput, b: BillingPriceOutput) =>
a.amount - b.amount;
@@ -86,7 +86,7 @@ export const MeteredPriceSelector = ({
description={t`Number of new credits allocated every ${recurringInterval}`}
/>
<Select
dropdownId={'settings-billing-metered-price'}
dropdownId="settings-billing-metered-price"
options={options}
value={currentMeteredBillingPrice?.stripePriceId}
onChange={handleChange}