diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCustomView.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCustomView.tsx index d32e182f8e..2ecf676cf6 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCustomView.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCustomView.tsx @@ -16,13 +16,17 @@ import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSe import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { viewsFromObjectMetadataItemFamilySelector } from '@/views/states/selectors/viewsFromObjectMetadataItemFamilySelector'; import { ViewKey } from '@/views/types/ViewKey'; -import { ViewType, viewTypeIconMapping } from '@/views/types/ViewType'; +import { + getViewTypeLabel, + ViewType, + viewTypeIconMapping, +} from '@/views/types/ViewType'; import { useDestroyViewFromCurrentState } from '@/views/view-picker/hooks/useDestroyViewFromCurrentState'; import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue'; import { useLingui } from '@lingui/react/macro'; -import { capitalize, isDefined } from 'twenty-shared/utils'; +import { isDefined } from 'twenty-shared/utils'; import { IconCalendar, IconCalendarWeek, @@ -143,7 +147,7 @@ export const ObjectOptionsDropdownCustomView = ({ customViewData?.type ?? ViewType.TABLE, )} text={t`Layout`} - contextualText={`${capitalize(customViewData?.type ?? '')}`} + contextualText={t(getViewTypeLabel(customViewData.type))} contextualTextPosition="right" hasSubMenu /> diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx index fcd177054a..3da7828a2d 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx @@ -17,7 +17,11 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView'; import { type GraphQLView } from '@/views/types/GraphQLView'; -import { ViewType, viewTypeIconMapping } from '@/views/types/ViewType'; +import { + getViewTypeLabel, + ViewType, + viewTypeIconMapping, +} from '@/views/types/ViewType'; import { useGetAvailableFieldsForCalendar } from '@/views/view-picker/hooks/useGetAvailableFieldsForCalendar'; import { useGetAvailableFieldsToGroupRecordsBy } from '@/views/view-picker/hooks/useGetAvailableFieldsToGroupRecordsBy'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; @@ -161,7 +165,7 @@ export const ObjectOptionsDropdownLayoutContent = () => { > { @@ -179,7 +183,7 @@ export const ObjectOptionsDropdownLayoutContent = () => { > { > { objectUniversalIdentifier: personObject.universalIdentifier, icon: 'IconTable', }, + { + universalIdentifier: 'v-2', + name: 'Record table widget', + type: 'TABLE_WIDGET', + objectUniversalIdentifier: personObject.universalIdentifier, + }, ], } as unknown as Manifest; @@ -109,6 +115,11 @@ describe('useComputeApplicationContentForLayoutAndLogic', () => { expect(row.icon).toBe('IconTable'); expect(row.secondary).toContain('Table'); expect(row.secondary).toContain(personObject.labelSingular); + + expect(result.current.viewRows[1].secondary).toContain('Table widget'); + expect(result.current.viewRows[1].secondary).not.toContain( + 'Table_widget', + ); }); }); diff --git a/packages/twenty-front/src/modules/settings/applications/hooks/useComputeApplicationContentForLayoutAndLogic.ts b/packages/twenty-front/src/modules/settings/applications/hooks/useComputeApplicationContentForLayoutAndLogic.ts index 71c993e522..4c9a315461 100644 --- a/packages/twenty-front/src/modules/settings/applications/hooks/useComputeApplicationContentForLayoutAndLogic.ts +++ b/packages/twenty-front/src/modules/settings/applications/hooks/useComputeApplicationContentForLayoutAndLogic.ts @@ -1,9 +1,11 @@ import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; +import { getViewTypeLabel } from '@/views/types/ViewType'; +import { i18n } from '@lingui/core'; import { t } from '@lingui/core/macro'; import { type Manifest } from 'twenty-shared/application'; import { SettingsPath } from 'twenty-shared/types'; -import { capitalize, getSettingsPath, isDefined } from 'twenty-shared/utils'; +import { getSettingsPath, isDefined } from 'twenty-shared/utils'; import { type Application } from '~/generated-metadata/graphql'; import { type ApplicationContentRow } from '~/pages/settings/applications/components/SettingsApplicationContentSubtable'; @@ -60,7 +62,7 @@ export const useComputeApplicationContentForLayoutAndLogic = ({ const viewRows: ApplicationContentRow[] = (manifestContent?.views ?? []).map( (view) => { const objectLabel = resolveLabel(view.objectUniversalIdentifier); - const formattedType = capitalize((view.type ?? 'TABLE').toLowerCase()); + const formattedType = i18n._(getViewTypeLabel(view.type)); return { key: view.universalIdentifier, diff --git a/packages/twenty-front/src/modules/settings/billing/effect-components/EndTrialAfterPaymentMethodEffect.tsx b/packages/twenty-front/src/modules/settings/billing/effect-components/EndTrialAfterPaymentMethodEffect.tsx index 545fad567e..d0ca001667 100644 --- a/packages/twenty-front/src/modules/settings/billing/effect-components/EndTrialAfterPaymentMethodEffect.tsx +++ b/packages/twenty-front/src/modules/settings/billing/effect-components/EndTrialAfterPaymentMethodEffect.tsx @@ -8,7 +8,7 @@ import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; import { isNonEmptyString } from '@sniptt/guards'; import { t } from '@lingui/core/macro'; -import { useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { isDefined } from 'twenty-shared/utils'; import { SubscriptionStatus } from '~/generated-metadata/graphql'; @@ -24,7 +24,9 @@ export const EndTrialAfterPaymentMethodEffect = () => { const searchParams = new URLSearchParams(location.search); const askAiThreadId = searchParams.get(ASK_AI_THREAD_ID_QUERY_PARAM); - const cleanUpQueryParams = () => { + const cleanUpQueryParams = useCallback(() => { + const searchParams = new URLSearchParams(location.search); + searchParams.delete(START_SUBSCRIPTION_AFTER_PAYMENT_METHOD_QUERY_PARAM); searchParams.delete(ASK_AI_THREAD_ID_QUERY_PARAM); @@ -34,9 +36,9 @@ export const EndTrialAfterPaymentMethodEffect = () => { `${location.pathname}${cleanedSearch.length > 0 ? `?${cleanedSearch}` : ''}${location.hash}`, { replace: true }, ); - }; + }, [location.hash, location.pathname, location.search, navigate]); - const startSubscription = async () => { + const startSubscription = useCallback(async () => { if (subscriptionStatus !== SubscriptionStatus.Trialing) { cleanUpQueryParams(); return; @@ -65,23 +67,21 @@ export const EndTrialAfterPaymentMethodEffect = () => { cleanUpQueryParams(); jotaiStore.set(isEndingSubscriptionTrialPeriodState.atom, false); } - }; + }, [ + askAiThreadId, + cleanUpQueryParams, + endTrialPeriod, + enqueueErrorSnackBar, + openAskAiThread, + subscriptionStatus, + ]); useEffect(() => { if (!isDefined(subscriptionStatus)) { return; } void startSubscription(); - }, [ - location.search, - location.pathname, - location.hash, - navigate, - subscriptionStatus, - endTrialPeriod, - openAskAiThread, - enqueueErrorSnackBar, - ]); + }, [startSubscription, subscriptionStatus]); return null; }; diff --git a/packages/twenty-front/src/modules/views/types/ViewType.ts b/packages/twenty-front/src/modules/views/types/ViewType.ts index 9c8663e1c0..1b094ad79f 100644 --- a/packages/twenty-front/src/modules/views/types/ViewType.ts +++ b/packages/twenty-front/src/modules/views/types/ViewType.ts @@ -1,3 +1,5 @@ +import { type MessageDescriptor } from '@lingui/core'; +import { msg } from '@lingui/core/macro'; import { IconCalendar, type IconComponent, @@ -8,6 +10,21 @@ import { export { ViewType } from '~/generated-metadata/graphql'; import { ViewType } from '~/generated-metadata/graphql'; +type ViewTypeLabelKey = `${ViewType}`; + +export const VIEW_TYPE_LABELS = { + [ViewType.TABLE]: msg`Table`, + [ViewType.KANBAN]: msg`Kanban`, + [ViewType.CALENDAR]: msg`Calendar`, + [ViewType.FIELDS_WIDGET]: msg`Fields widget`, + [ViewType.TABLE_WIDGET]: msg`Table widget`, +} satisfies Record; + +export const getViewTypeLabel = ( + viewType: ViewTypeLabelKey = ViewType.TABLE, +): MessageDescriptor => + VIEW_TYPE_LABELS[viewType] ?? VIEW_TYPE_LABELS[ViewType.TABLE]; + const VIEW_TYPE_ICON_MAPPING = [ { icon: IconLayoutKanban, value: ViewType.KANBAN }, { icon: IconTable, value: ViewType.TABLE }, diff --git a/packages/twenty-front/src/modules/views/view-picker/constants/ViewPickerTypeSelectOptions.ts b/packages/twenty-front/src/modules/views/view-picker/constants/ViewPickerTypeSelectOptions.ts index 39f86b7534..4c8bbffd15 100644 --- a/packages/twenty-front/src/modules/views/view-picker/constants/ViewPickerTypeSelectOptions.ts +++ b/packages/twenty-front/src/modules/views/view-picker/constants/ViewPickerTypeSelectOptions.ts @@ -1,20 +1,23 @@ -import { ViewType, viewTypeIconMapping } from '@/views/types/ViewType'; -import { msg } from '@lingui/core/macro'; +import { + VIEW_TYPE_LABELS, + ViewType, + viewTypeIconMapping, +} from '@/views/types/ViewType'; export const VIEW_PICKER_TYPE_SELECT_OPTIONS = [ { value: ViewType.TABLE, - label: msg`Table`, + label: VIEW_TYPE_LABELS[ViewType.TABLE], Icon: viewTypeIconMapping(ViewType.TABLE), }, { value: ViewType.KANBAN, - label: msg`Kanban`, + label: VIEW_TYPE_LABELS[ViewType.KANBAN], Icon: viewTypeIconMapping(ViewType.KANBAN), }, { value: ViewType.CALENDAR, - label: msg`Calendar`, + label: VIEW_TYPE_LABELS[ViewType.CALENDAR], Icon: viewTypeIconMapping(ViewType.CALENDAR), }, ];