@carlonicora/nextjs-jsonapi 4.1.2 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AssistantMessageInterface-CubxuBIJ.d.ts → AssistantMessageInterface-DHJJO_Z_.d.ts} +1 -1
- package/dist/{AssistantMessageInterface-sHjI2zPN.d.mts → AssistantMessageInterface-S7oyERTT.d.mts} +1 -1
- package/dist/{BlockNoteEditor-VOYKZOP5.mjs → BlockNoteEditor-HXAHDJA7.mjs} +34 -27
- package/dist/BlockNoteEditor-HXAHDJA7.mjs.map +1 -0
- package/dist/{BlockNoteEditor-BENYTTCZ.js → BlockNoteEditor-LLCZDFLZ.js} +50 -43
- package/dist/BlockNoteEditor-LLCZDFLZ.js.map +1 -0
- package/dist/{BlockNoteViewer-USLR4Y7C.js → BlockNoteViewer-77CCTC2U.js} +3 -3
- package/dist/{BlockNoteViewer-USLR4Y7C.js.map → BlockNoteViewer-77CCTC2U.js.map} +1 -1
- package/dist/{BlockNoteViewer-Y2ZM4ESA.mjs → BlockNoteViewer-MDPO6BXN.mjs} +2 -2
- package/dist/{auth.interface-Df4vXbNz.d.mts → auth.interface-CeFCz9NU.d.mts} +1 -1
- package/dist/{auth.interface-BzI4onI_.d.ts → auth.interface-D1R7VVky.d.ts} +1 -1
- package/dist/billing/index.d.mts +40 -6
- package/dist/billing/index.d.ts +40 -6
- package/dist/billing/index.js +806 -774
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +494 -462
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-6ISF5KNQ.mjs → chunk-2VXDZRYY.mjs} +1103 -816
- package/dist/chunk-2VXDZRYY.mjs.map +1 -0
- package/dist/{chunk-4BZJ4LAB.js → chunk-4ORLPEK7.js} +136 -104
- package/dist/chunk-4ORLPEK7.js.map +1 -0
- package/dist/{chunk-ZZ6E33FO.mjs → chunk-6DRSSW5O.mjs} +36 -4
- package/dist/chunk-6DRSSW5O.mjs.map +1 -0
- package/dist/{chunk-KS6WZP2E.js → chunk-6SMJRAE3.js} +4 -4
- package/dist/{chunk-KS6WZP2E.js.map → chunk-6SMJRAE3.js.map} +1 -1
- package/dist/{chunk-WQ77CUUZ.js → chunk-GPXYQMOZ.js} +7 -7
- package/dist/{chunk-WQ77CUUZ.js.map → chunk-GPXYQMOZ.js.map} +1 -1
- package/dist/{chunk-46S3KPLU.mjs → chunk-ILFFC6BG.mjs} +2 -2
- package/dist/{chunk-HZVVRIJQ.mjs → chunk-IUS2RHCX.mjs} +2 -2
- package/dist/{chunk-F7RNBGHS.js → chunk-KJMMBUOA.js} +3 -3
- package/dist/{chunk-F7RNBGHS.js.map → chunk-KJMMBUOA.js.map} +1 -1
- package/dist/{chunk-ZO3QQ5CQ.mjs → chunk-MQDR6MEE.mjs} +2 -2
- package/dist/{chunk-ORU7KLCY.js → chunk-VDVIUDY5.js} +1445 -1158
- package/dist/chunk-VDVIUDY5.js.map +1 -0
- package/dist/client/index.d.mts +20 -6
- package/dist/client/index.d.ts +20 -6
- package/dist/client/index.js +6 -4
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +5 -3
- package/dist/components/index.d.mts +59 -21
- package/dist/components/index.d.ts +59 -21
- package/dist/components/index.js +4 -4
- package/dist/components/index.mjs +3 -3
- package/dist/{config-CEAq4Ozy.d.mts → config-BP2vyddw.d.mts} +5 -0
- package/dist/{config-DydDHCfb.d.ts → config-BhW2QrRD.d.ts} +5 -0
- package/dist/contexts/index.d.mts +4 -4
- package/dist/contexts/index.d.ts +4 -4
- package/dist/contexts/index.js +4 -4
- package/dist/contexts/index.mjs +3 -3
- package/dist/core/index.d.mts +17 -9
- package/dist/core/index.d.ts +17 -9
- package/dist/core/index.js +4 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +3 -1
- package/dist/features/analytics/index.d.mts +1 -1
- package/dist/features/analytics/index.d.ts +1 -1
- package/dist/features/analytics/index.js +122 -122
- package/dist/features/analytics/index.mjs +4 -4
- package/dist/features/analytics/server.js +4 -4
- package/dist/features/analytics/server.mjs +2 -2
- package/dist/features/help/index.d.mts +1 -1
- package/dist/features/help/index.d.ts +1 -1
- package/dist/features/help/index.js +42 -40
- package/dist/features/help/index.js.map +1 -1
- package/dist/features/help/index.mjs +8 -6
- package/dist/features/help/index.mjs.map +1 -1
- package/dist/features/tokenusage/index.js +125 -125
- package/dist/features/tokenusage/index.mjs +4 -4
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -2
- package/dist/{notification.interface-P_pNfzZQ.d.mts → notification.interface-BB6GPpZY.d.mts} +1 -1
- package/dist/{notification.interface-BNCvnVCW.d.ts → notification.interface-CmAGrijJ.d.ts} +1 -1
- package/dist/{s3.service-CbUa6a-8.d.ts → s3.service-C-4YpEMu.d.ts} +7 -3
- package/dist/{s3.service-DAp8FbuG.d.mts → s3.service-dVj-nxMl.d.mts} +7 -3
- package/dist/server/index.d.mts +4 -4
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +2 -2
- package/dist/{useSocket-BHVIyigI.d.ts → useSocket-B0H9pmmH.d.ts} +1 -1
- package/dist/{useSocket-B9gTbuCj.d.mts → useSocket-B5RQb6v2.d.mts} +1 -1
- package/dist/{user.interface-CrmwJLYW.d.ts → user.interface-C0ZdsBZj.d.ts} +2 -0
- package/dist/{user.interface-CSaGWFbg.d.mts → user.interface-CIWGlbqM.d.mts} +2 -0
- package/package.json +1 -1
- package/src/components/containers/RoundPageContainer.tsx +3 -1
- package/src/components/containers/RoundPageContainerTitle.tsx +5 -2
- package/src/components/editors/BlockNoteEditor.tsx +31 -15
- package/src/components/editors/BlockNoteEditorAiActions.tsx +11 -7
- package/src/components/editors/__tests__/{BlockNoteEditorAiActions.test.ts → BlockNoteEditorAiActions.spec.ts} +10 -3
- package/src/components/errors/ErrorDetails.tsx +5 -2
- package/src/components/forms/CommonAssociationForm.tsx +1 -1
- package/src/components/forms/EntityMultiSelector.tsx +6 -2
- package/src/components/forms/EntitySelector.tsx +4 -1
- package/src/components/forms/FileUploader.tsx +3 -2
- package/src/components/forms/MultiFileUploader.tsx +3 -1
- package/src/components/navigations/Breadcrumb.tsx +3 -2
- package/src/components/navigations/Header.tsx +3 -1
- package/src/components/navigations/MobileNavigationBar.tsx +3 -1
- package/src/components/navigations/__tests__/Header.spec.tsx +2 -2
- package/src/components/tables/__tests__/ContentListTable.spec.tsx +1 -1
- package/src/components/tables/cells/cell.id.tsx +13 -2
- package/src/core/abstracts/AbstractService.ts +3 -3
- package/src/core/abstracts/ClientAbstractService.ts +3 -2
- package/src/features/assistant-action/components/ApprovalActionCard.tsx +74 -5
- package/src/features/assistant-action/components/__tests__/ApprovalActionCard.spec.tsx +81 -0
- package/src/features/assistant-message/components/parts/RelevanceMeter.tsx +4 -1
- package/src/features/assistant-message/components/parts/tabs/UsersTab.tsx +2 -1
- package/src/features/auth/components/buttons/GoogleSignInButton.tsx +4 -1
- package/src/features/auth/components/details/LandingComponent.tsx +1 -1
- package/src/features/auth/components/forms/ForgotPassword.tsx +1 -1
- package/src/features/auth/components/forms/Login.tsx +1 -1
- package/src/features/auth/components/forms/Register.tsx +1 -1
- package/src/features/auth/components/forms/ResetPassword.tsx +2 -2
- package/src/features/auth/components/two-factor/BackupCodesDialog.tsx +1 -1
- package/src/features/auth/components/two-factor/PasskeyButton.tsx +1 -1
- package/src/features/billing/components/cards/BillingUsageSummaryCard.tsx +21 -11
- package/src/features/billing/components/cards/CustomerInfoCard.tsx +16 -13
- package/src/features/billing/components/cards/InvoicesSummaryCard.tsx +33 -21
- package/src/features/billing/components/cards/PaymentMethodSummaryCard.tsx +20 -10
- package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +35 -22
- package/src/features/billing/components/containers/BillingDashboardContainer.tsx +24 -24
- package/src/features/billing/components/utils/__tests__/billing-formatters.spec.ts +63 -0
- package/src/features/billing/components/utils/currency.ts +67 -10
- package/src/features/billing/components/utils/date.ts +9 -4
- package/src/features/billing/components/utils/format-options.ts +20 -0
- package/src/features/billing/components/utils/index.ts +3 -1
- package/src/features/billing/components/utils/useBillingFormatOptions.ts +11 -0
- package/src/features/billing/components/widgets/BillingAlertBanner.tsx +8 -9
- package/src/features/billing/contexts/PriceContext.tsx +18 -6
- package/src/features/billing/i18n-keys.ts +212 -0
- package/src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx +8 -8
- package/src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx +11 -9
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx +4 -4
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx +9 -7
- package/src/features/billing/stripe-invoice/components/containers/InvoicesContainer.tsx +10 -8
- package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +34 -29
- package/src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx +12 -9
- package/src/features/billing/stripe-invoice/components/widgets/InvoiceStatusBadge.tsx +9 -6
- package/src/features/billing/stripe-price/components/details/PriceDetails.tsx +4 -3
- package/src/features/billing/stripe-price/components/forms/PriceArchiver.tsx +3 -1
- package/src/features/billing/stripe-price/components/forms/PriceEditor.tsx +3 -1
- package/src/features/billing/stripe-price/hooks/useStripePriceTableStructure.tsx +6 -5
- package/src/features/billing/stripe-promotion-code/components/PromoCodeInput.tsx +12 -7
- package/src/features/billing/stripe-promotion-code/data/stripe-promotion-code.service.ts +3 -1
- package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +10 -6
- package/src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx +44 -29
- package/src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx +14 -11
- package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +20 -17
- package/src/features/billing/stripe-subscription/components/widgets/IntervalToggle.tsx +4 -2
- package/src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx +16 -7
- package/src/features/billing/stripe-subscription/components/widgets/ProductPricingList.tsx +3 -1
- package/src/features/billing/stripe-subscription/components/widgets/ProductPricingRow.tsx +3 -1
- package/src/features/billing/stripe-subscription/components/widgets/ProrationPreview.tsx +12 -7
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionConfirmation.tsx +11 -8
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx +13 -10
- package/src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx +8 -6
- package/src/features/billing/stripe-subscription/components/wizards/WizardProgressIndicator.tsx +7 -4
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx +4 -2
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPlanSelection.tsx +3 -1
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +37 -28
- package/src/features/billing/stripe-subscription/hooks/useConfirmSubscriptionPayment.ts +9 -7
- package/src/features/billing/stripe-subscription/hooks/useSubscriptionWizard.ts +20 -9
- package/src/features/billing/stripe-usage/components/containers/UsageContainer.tsx +6 -6
- package/src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx +24 -9
- package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +16 -12
- package/src/features/company/components/details/TokenStatusIndicator.tsx +7 -5
- package/src/features/company/components/forms/CompanyEditor.tsx +2 -2
- package/src/features/help/components/HelpArticleBody.tsx +1 -1
- package/src/features/help/components/HelpHeader.tsx +1 -1
- package/src/features/help/components/HelpSideNav.tsx +1 -1
- package/src/features/help/i18n-keys.ts +2 -0
- package/src/features/notification/components/common/NotificationErrorBoundary.tsx +17 -12
- package/src/features/notification/components/containers/NotificationsListContainer.tsx +4 -2
- package/src/features/notification/components/notifications/Notification.tsx +6 -3
- package/src/features/notification/contexts/NotificationContext.tsx +9 -4
- package/src/features/notification/contexts/__tests__/NotificationContext.spec.tsx +13 -0
- package/src/features/notification/hooks/useNotificationTableStructure.tsx +4 -2
- package/src/features/oauth/components/OAuthClientCard.tsx +13 -7
- package/src/features/oauth/components/OAuthClientDetail.tsx +34 -30
- package/src/features/oauth/components/OAuthClientForm.tsx +24 -24
- package/src/features/oauth/components/OAuthClientList.tsx +10 -6
- package/src/features/oauth/components/OAuthClientSecretDisplay.tsx +10 -8
- package/src/features/oauth/components/OAuthRedirectUriInput.tsx +9 -13
- package/src/features/oauth/components/OAuthScopeSelector.tsx +15 -6
- package/src/features/oauth/components/__tests__/OAuthClientForm.spec.tsx +11 -9
- package/src/features/oauth/components/consent/OAuthScopeList.tsx +4 -2
- package/src/features/oauth/hooks/__tests__/useOAuthScopeText.spec.ts +30 -0
- package/src/features/oauth/hooks/index.ts +1 -0
- package/src/features/oauth/hooks/useOAuthClient.ts +11 -5
- package/src/features/oauth/hooks/useOAuthClients.ts +9 -3
- package/src/features/oauth/hooks/useOAuthConsent.ts +11 -5
- package/src/features/oauth/hooks/useOAuthScopeText.ts +33 -0
- package/src/features/rbac/components/RbacByRoleContainer.tsx +7 -6
- package/src/features/rbac/components/RbacContainer.tsx +7 -6
- package/src/features/rbac/contexts/RbacContext.tsx +3 -3
- package/src/features/user/components/containers/UserContainer.tsx +3 -1
- package/src/features/user/data/__tests__/user.spec.ts +66 -0
- package/src/features/user/data/user.interface.ts +2 -0
- package/src/features/user/data/user.service.ts +9 -0
- package/src/features/user/data/user.ts +7 -0
- package/src/features/waitlist/components/forms/WaitlistForm.tsx +1 -1
- package/src/features/waitlist/components/lists/WaitlistList.tsx +1 -1
- package/src/i18n/__tests__/{config.test.ts → config.spec.ts} +21 -1
- package/src/i18n/config.ts +13 -0
- package/src/i18n/index.ts +1 -0
- package/src/shadcnui/custom/multi-select.tsx +47 -14
- package/src/shadcnui/custom/multiple-selector.tsx +14 -2
- package/src/shadcnui/ui/__tests__/{confirm-dialog.test.tsx → confirm-dialog.spec.tsx} +1 -1
- package/src/shadcnui/ui/__tests__/{cursor-controls.test.tsx → cursor-controls.spec.tsx} +1 -1
- package/src/shadcnui/ui/__tests__/{cursor-menu-items.test.tsx → cursor-menu-items.spec.tsx} +1 -1
- package/src/shadcnui/ui/__tests__/{empty-state.test.tsx → empty-state.spec.tsx} +1 -1
- package/src/shadcnui/ui/__tests__/{spinner.test.tsx → spinner.spec.tsx} +1 -1
- package/src/shadcnui/ui/__tests__/use-ui-text.spec.tsx +76 -0
- package/src/shadcnui/ui/breadcrumb.tsx +9 -2
- package/src/shadcnui/ui/carousel.tsx +15 -4
- package/src/shadcnui/ui/dialog.tsx +15 -2
- package/src/shadcnui/ui/sheet.tsx +6 -1
- package/src/shadcnui/ui/sidebar.tsx +33 -7
- package/src/shadcnui/ui/spinner.tsx +6 -1
- package/src/shadcnui/ui/use-ui-text.ts +49 -0
- package/src/utils/__tests__/{date-formatter.test.ts → date-formatter.spec.ts} +24 -1
- package/src/utils/date-formatter.ts +16 -0
- package/src/utils/index.ts +1 -1
- package/dist/BlockNoteEditor-BENYTTCZ.js.map +0 -1
- package/dist/BlockNoteEditor-VOYKZOP5.mjs.map +0 -1
- package/dist/chunk-4BZJ4LAB.js.map +0 -1
- package/dist/chunk-6ISF5KNQ.mjs.map +0 -1
- package/dist/chunk-ORU7KLCY.js.map +0 -1
- package/dist/chunk-ZZ6E33FO.mjs.map +0 -1
- /package/dist/{BlockNoteViewer-Y2ZM4ESA.mjs.map → BlockNoteViewer-MDPO6BXN.mjs.map} +0 -0
- /package/dist/{chunk-46S3KPLU.mjs.map → chunk-ILFFC6BG.mjs.map} +0 -0
- /package/dist/{chunk-HZVVRIJQ.mjs.map → chunk-IUS2RHCX.mjs.map} +0 -0
- /package/dist/{chunk-ZO3QQ5CQ.mjs.map → chunk-MQDR6MEE.mjs.map} +0 -0
- /package/scripts/{check-rtl-classes.test.ts → check-rtl-classes.spec.ts} +0 -0
- /package/src/client/__tests__/{JsonApiClient.test.ts → JsonApiClient.spec.ts} +0 -0
- /package/src/client/__tests__/{request.test.ts → request.spec.ts} +0 -0
- /package/src/client/hooks/__tests__/{useJsonApiGet.test.tsx → useJsonApiGet.spec.tsx} +0 -0
- /package/src/client/hooks/__tests__/{useJsonApiMutation.test.tsx → useJsonApiMutation.spec.tsx} +0 -0
- /package/src/client/hooks/__tests__/{useRehydration.test.ts → useRehydration.spec.ts} +0 -0
- /package/src/components/editors/__tests__/{BlockNoteEditorMentionInlineContent.test.ts → BlockNoteEditorMentionInlineContent.spec.ts} +0 -0
- /package/src/components/forms/__tests__/{CommonEditorDiscardDialog.test.tsx → CommonEditorDiscardDialog.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{EditorSheet.test.tsx → EditorSheet.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{FormCheckbox.test.tsx → FormCheckbox.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{FormDate.test.tsx → FormDate.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{FormInput.test.tsx → FormInput.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{FormSelect.test.tsx → FormSelect.spec.tsx} +0 -0
- /package/src/components/forms/__tests__/{useEditorDialog.test.ts → useEditorDialog.spec.ts} +0 -0
- /package/src/components/grids/__tests__/{ContentListGrid.test.tsx → ContentListGrid.spec.tsx} +0 -0
- /package/src/components/tables/__tests__/{ContentListTable.test.tsx → ContentListTable.rendering.spec.tsx} +0 -0
- /package/src/contexts/__tests__/{DirectionContext.test.tsx → DirectionContext.spec.tsx} +0 -0
- /package/src/contexts/__tests__/{SessionContext.test.tsx → SessionContext.spec.tsx} +0 -0
- /package/src/contexts/__tests__/{SocketContext.session.test.tsx → SocketContext.session.spec.tsx} +0 -0
- /package/src/core/abstracts/__tests__/{AbstractService.locale.test.ts → AbstractService.locale.spec.ts} +0 -0
- /package/src/core/endpoint/__tests__/{EndpointCreator.test.ts → EndpointCreator.spec.ts} +0 -0
- /package/src/core/factories/__tests__/{JsonApiDataFactory.test.ts → JsonApiDataFactory.spec.ts} +0 -0
- /package/src/core/factories/__tests__/{RehydrationFactory.test.ts → RehydrationFactory.spec.ts} +0 -0
- /package/src/core/registry/__tests__/{DataClassRegistry.test.ts → DataClassRegistry.spec.ts} +0 -0
- /package/src/core/registry/__tests__/{ModuleRegistrar.test.ts → ModuleRegistrar.spec.ts} +0 -0
- /package/src/core/registry/__tests__/{viewportStore.test.ts → viewportStore.spec.ts} +0 -0
- /package/src/core/utils/__tests__/{translateResponse.test.ts → translateResponse.spec.ts} +0 -0
- /package/src/features/ai-connection/data/__tests__/{ai-connection.test.ts → ai-connection.spec.ts} +0 -0
- /package/src/features/company/data/{company.test.ts → company.spec.ts} +0 -0
- /package/src/features/company/hooks/{useSubscriptionStatus.test.ts → useSubscriptionStatus.spec.ts} +0 -0
- /package/src/hooks/__tests__/{useDataListRetriever.test.ts → useDataListRetriever.spec.ts} +0 -0
- /package/src/hooks/__tests__/{useDebounce.test.ts → useDebounce.spec.ts} +0 -0
- /package/src/hooks/__tests__/{useSocket.render.test.ts → useSocket.render.spec.ts} +0 -0
- /package/src/lib/__tests__/{direction.test.ts → direction.spec.ts} +0 -0
- /package/src/next-config/__tests__/{createNextConfig.test.ts → createNextConfig.spec.ts} +0 -0
- /package/src/server/__tests__/{JsonApiServer.test.ts → JsonApiServer.spec.ts} +0 -0
- /package/src/server/__tests__/{ServerSession.read.test.ts → ServerSession.read.spec.ts} +0 -0
- /package/src/server/__tests__/{cache.test.ts → cache.spec.ts} +0 -0
- /package/src/server/__tests__/{cachedGet.test.ts → cachedGet.spec.ts} +0 -0
- /package/src/server/__tests__/{request.test.ts → request.spec.ts} +0 -0
- /package/src/shadcnui/ui/__tests__/{sidebar.menu-skeleton.test.tsx → sidebar.menu-skeleton.spec.tsx} +0 -0
- /package/src/shadcnui/ui/__tests__/{sidebar.session.test.tsx → sidebar.session.spec.tsx} +0 -0
- /package/src/unified/__tests__/{JsonApiGet.cache.test.ts → JsonApiGet.cache.spec.ts} +0 -0
- /package/src/unified/__tests__/{JsonApiRequest.test.ts → JsonApiRequest.spec.ts} +0 -0
- /package/src/utils/__tests__/{exists.test.ts → exists.spec.ts} +0 -0
- /package/src/utils/__tests__/{italian-validators.test.ts → italian-validators.spec.ts} +0 -0
- /package/src/utils/__tests__/{map-thenable.test.ts → map-thenable.spec.ts} +0 -0
- /package/src/utils/__tests__/{use-mobile.test.tsx → use-mobile.spec.tsx} +0 -0
- /package/src/utils/{cn.test.ts → cn.spec.ts} +0 -0
- /package/src/utils/{isRemoteImageSrc.test.ts → isRemoteImageSrc.spec.ts} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { BlockNoteSchema, defaultInlineContentSpecs, filterSuggestionItems, PartialBlock } from "@blocknote/core";
|
|
4
|
-
import
|
|
4
|
+
import * as coreLocales from "@blocknote/core/locales";
|
|
5
5
|
import {
|
|
6
6
|
createReactInlineContentSpec,
|
|
7
7
|
DefaultReactSuggestionItem,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
PromptSuggestionMenu,
|
|
24
24
|
useAIDictionary,
|
|
25
25
|
} from "@blocknote/xl-ai";
|
|
26
|
-
import
|
|
26
|
+
import * as aiLocales from "@blocknote/xl-ai/locales";
|
|
27
27
|
import "@blocknote/xl-ai/style.css";
|
|
28
28
|
import { DefaultChatTransport } from "ai";
|
|
29
29
|
import { CheckIcon, LanguagesIcon, SparklesIcon, TypeIcon, WandSparklesIcon, XIcon } from "lucide-react";
|
|
@@ -137,6 +137,7 @@ function isDocumentEmpty(blocks: any[]): boolean {
|
|
|
137
137
|
const createDiffActionsInlineContentSpec = (
|
|
138
138
|
handleAcceptChange: (diffId: string) => void,
|
|
139
139
|
handleRejectChange: (diffId: string) => void,
|
|
140
|
+
labels: { accept: string; reject: string },
|
|
140
141
|
) => {
|
|
141
142
|
return createReactInlineContentSpec(
|
|
142
143
|
{
|
|
@@ -155,7 +156,7 @@ const createDiffActionsInlineContentSpec = (
|
|
|
155
156
|
return (
|
|
156
157
|
<span className="diff-actions-container mx-2 inline-flex items-center gap-1 align-middle">
|
|
157
158
|
<Button
|
|
158
|
-
title=
|
|
159
|
+
title={labels.accept}
|
|
159
160
|
onClick={(e) => {
|
|
160
161
|
e.preventDefault();
|
|
161
162
|
e.stopPropagation();
|
|
@@ -165,7 +166,7 @@ const createDiffActionsInlineContentSpec = (
|
|
|
165
166
|
<CheckIcon className="text-success h-3 w-3" />
|
|
166
167
|
</Button>
|
|
167
168
|
<Button
|
|
168
|
-
title=
|
|
169
|
+
title={labels.reject}
|
|
169
170
|
className="mx-2 p-0"
|
|
170
171
|
onClick={(e) => {
|
|
171
172
|
e.preventDefault();
|
|
@@ -204,6 +205,7 @@ const createDiffActionsInlineContentSpec = (
|
|
|
204
205
|
* with its own canonical prompt. NO prompt text lives in this file.
|
|
205
206
|
*/
|
|
206
207
|
function NarrAIMenu({ actions }: { actions?: BlockNoteAiGenerateAction[] }) {
|
|
208
|
+
const t = useTranslations();
|
|
207
209
|
const editor = useBlockNoteEditor();
|
|
208
210
|
const ai = useExtension(AIExtension);
|
|
209
211
|
const dict = useAIDictionary();
|
|
@@ -269,7 +271,7 @@ function NarrAIMenu({ actions }: { actions?: BlockNoteAiGenerateAction[] }) {
|
|
|
269
271
|
// selection) because they operate on the whole document — they ignore any
|
|
270
272
|
// active selection. Listed first so the first one is the
|
|
271
273
|
// default-highlighted item.
|
|
272
|
-
const generateItems = generateActionItems(actions, invokeGenerate);
|
|
274
|
+
const generateItems = generateActionItems(actions, invokeGenerate, t);
|
|
273
275
|
|
|
274
276
|
if (hasSelection) {
|
|
275
277
|
// Selection-edit items + the always-available whole-document items.
|
|
@@ -280,7 +282,7 @@ function NarrAIMenu({ actions }: { actions?: BlockNoteAiGenerateAction[] }) {
|
|
|
280
282
|
...generateItems,
|
|
281
283
|
{
|
|
282
284
|
key: "improve_writing",
|
|
283
|
-
title: "
|
|
285
|
+
title: t("ui.editor.ai.improve_writing"),
|
|
284
286
|
aliases: ["improve", "rewrite", "polish"],
|
|
285
287
|
icon: <SparklesIcon size={18} />,
|
|
286
288
|
size: "small" as const,
|
|
@@ -295,7 +297,7 @@ function NarrAIMenu({ actions }: { actions?: BlockNoteAiGenerateAction[] }) {
|
|
|
295
297
|
},
|
|
296
298
|
{
|
|
297
299
|
key: "fix_spelling",
|
|
298
|
-
title: "
|
|
300
|
+
title: t("ui.editor.ai.fix_spelling"),
|
|
299
301
|
aliases: ["spelling", "grammar", "typo"],
|
|
300
302
|
icon: <TypeIcon size={18} />,
|
|
301
303
|
size: "small" as const,
|
|
@@ -310,7 +312,7 @@ function NarrAIMenu({ actions }: { actions?: BlockNoteAiGenerateAction[] }) {
|
|
|
310
312
|
},
|
|
311
313
|
{
|
|
312
314
|
key: "translate",
|
|
313
|
-
title: "
|
|
315
|
+
title: t("ui.editor.ai.translate"),
|
|
314
316
|
aliases: ["translate", "language"],
|
|
315
317
|
icon: <LanguagesIcon size={18} />,
|
|
316
318
|
size: "small" as const,
|
|
@@ -322,12 +324,12 @@ function NarrAIMenu({ actions }: { actions?: BlockNoteAiGenerateAction[] }) {
|
|
|
322
324
|
onItemClick: () => {
|
|
323
325
|
expandSelectionToBlocks();
|
|
324
326
|
pendingTypeRef.current = "translate";
|
|
325
|
-
setPrompt("
|
|
327
|
+
setPrompt(t("ui.editor.ai.translate_prefill"));
|
|
326
328
|
},
|
|
327
329
|
},
|
|
328
330
|
{
|
|
329
331
|
key: "simplify",
|
|
330
|
-
title: "
|
|
332
|
+
title: t("ui.editor.ai.simplify"),
|
|
331
333
|
aliases: ["simplify", "easier", "plain"],
|
|
332
334
|
icon: <WandSparklesIcon size={18} />,
|
|
333
335
|
size: "small" as const,
|
|
@@ -347,7 +349,7 @@ function NarrAIMenu({ actions }: { actions?: BlockNoteAiGenerateAction[] }) {
|
|
|
347
349
|
// Free-form typing still works — once the user types, items hide and
|
|
348
350
|
// Enter submits with no type (backend defaults to fill-template).
|
|
349
351
|
return generateItems;
|
|
350
|
-
}, [editor, status, prompt, ai, expandSelectionToBlocks, actions]);
|
|
352
|
+
}, [editor, status, prompt, ai, expandSelectionToBlocks, actions, t]);
|
|
351
353
|
|
|
352
354
|
const handleSubmit = useCallback(
|
|
353
355
|
async (userPrompt: string) => {
|
|
@@ -466,8 +468,12 @@ export default function BlockNoteEditor({
|
|
|
466
468
|
}, []);
|
|
467
469
|
|
|
468
470
|
const DiffActionsInlineContent = useMemo(
|
|
469
|
-
() =>
|
|
470
|
-
|
|
471
|
+
() =>
|
|
472
|
+
createDiffActionsInlineContentSpec(handleAcceptChange, handleRejectChange, {
|
|
473
|
+
accept: t("ui.editor.accept_change"),
|
|
474
|
+
reject: t("ui.editor.reject_change"),
|
|
475
|
+
}),
|
|
476
|
+
[handleAcceptChange, handleRejectChange, t],
|
|
471
477
|
);
|
|
472
478
|
|
|
473
479
|
const mentionSpec = useMemo(
|
|
@@ -657,6 +663,12 @@ export default function BlockNoteEditor({
|
|
|
657
663
|
return undefined;
|
|
658
664
|
}, [processedContent]);
|
|
659
665
|
|
|
666
|
+
// BlockNote's own UI text (slash menu, toolbar, AI menu) in the active
|
|
667
|
+
// language; English when BlockNote has no dictionary for it.
|
|
668
|
+
const language = locale.split("-")[0];
|
|
669
|
+
const coreDictionary = (coreLocales as Record<string, typeof coreLocales.en>)[language] ?? coreLocales.en;
|
|
670
|
+
const aiDictionary = (aiLocales as Record<string, typeof aiLocales.en>)[language] ?? aiLocales.en;
|
|
671
|
+
|
|
660
672
|
const editor = useCreateBlockNote(
|
|
661
673
|
useMemo(
|
|
662
674
|
() => ({
|
|
@@ -667,9 +679,13 @@ export default function BlockNoteEditor({
|
|
|
667
679
|
initialContent: validatedInitialContent,
|
|
668
680
|
uploadFile: uploadImage,
|
|
669
681
|
extensions: aiExtension ? [aiExtension] : undefined,
|
|
670
|
-
dictionary: aiExtension
|
|
682
|
+
dictionary: aiExtension
|
|
683
|
+
? { ...coreDictionary, ai: aiDictionary }
|
|
684
|
+
: coreDictionary === coreLocales.en
|
|
685
|
+
? undefined
|
|
686
|
+
: coreDictionary,
|
|
671
687
|
}),
|
|
672
|
-
[placeholder, t, schema, validatedInitialContent, uploadImage, aiExtension],
|
|
688
|
+
[placeholder, t, schema, validatedInitialContent, uploadImage, aiExtension, coreDictionary, aiDictionary],
|
|
673
689
|
),
|
|
674
690
|
);
|
|
675
691
|
|
|
@@ -21,17 +21,20 @@ export type AiGenerateMenuItem = {
|
|
|
21
21
|
onItemClick: () => void;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
/** `titleKey` is a message key; the caller's translator resolves it to the shown title. */
|
|
25
|
+
type AiGenerateItemDefinition = Omit<AiGenerateMenuItem, "onItemClick" | "title"> & { titleKey: string };
|
|
26
|
+
|
|
27
|
+
const ITEMS: Record<BlockNoteAiGenerateAction, AiGenerateItemDefinition> = {
|
|
25
28
|
"fill-template": {
|
|
26
29
|
key: "generate_from_template",
|
|
27
|
-
|
|
30
|
+
titleKey: "ui.editor.ai.generate_from_template",
|
|
28
31
|
aliases: ["generate", "template", "fill"],
|
|
29
32
|
icon: <LayoutTemplateIcon size={18} />,
|
|
30
33
|
size: "small",
|
|
31
34
|
},
|
|
32
35
|
"draft-impression": {
|
|
33
36
|
key: "suggest_impression",
|
|
34
|
-
|
|
37
|
+
titleKey: "ui.editor.ai.suggest_impression",
|
|
35
38
|
aliases: ["suggest", "impression"],
|
|
36
39
|
icon: <SparklesIcon size={18} />,
|
|
37
40
|
size: "small",
|
|
@@ -41,9 +44,10 @@ const ITEMS: Record<BlockNoteAiGenerateAction, Omit<AiGenerateMenuItem, "onItemC
|
|
|
41
44
|
export function generateActionItems(
|
|
42
45
|
actions: BlockNoteAiGenerateAction[] | undefined,
|
|
43
46
|
invoke: (type: BlockNoteAiGenerateAction) => void,
|
|
47
|
+
translate: (key: string) => string,
|
|
44
48
|
): AiGenerateMenuItem[] {
|
|
45
|
-
return (actions ?? DEFAULT_AI_GENERATE_ACTIONS).map((type) =>
|
|
46
|
-
...ITEMS[type]
|
|
47
|
-
onItemClick: () => invoke(type)
|
|
48
|
-
})
|
|
49
|
+
return (actions ?? DEFAULT_AI_GENERATE_ACTIONS).map((type) => {
|
|
50
|
+
const { titleKey, ...item } = ITEMS[type];
|
|
51
|
+
return { ...item, title: translate(titleKey), onItemClick: () => invoke(type) };
|
|
52
|
+
});
|
|
49
53
|
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { DEFAULT_AI_GENERATE_ACTIONS, generateActionItems } from "../BlockNoteEditorAiActions";
|
|
3
3
|
|
|
4
|
+
/** Stands in for next-intl's translator: returns the English message for each key. */
|
|
5
|
+
const english: Record<string, string> = {
|
|
6
|
+
"ui.editor.ai.generate_from_template": "Generate from Template",
|
|
7
|
+
"ui.editor.ai.suggest_impression": "Suggest impression",
|
|
8
|
+
};
|
|
9
|
+
const translate = (key: string) => english[key] ?? key;
|
|
10
|
+
|
|
4
11
|
describe("generateActionItems", () => {
|
|
5
12
|
it("defaults to Generate from Template only, so existing editors are unchanged", () => {
|
|
6
|
-
const items = generateActionItems(undefined, vi.fn());
|
|
13
|
+
const items = generateActionItems(undefined, vi.fn(), translate);
|
|
7
14
|
expect(DEFAULT_AI_GENERATE_ACTIONS).toEqual(["fill-template"]);
|
|
8
15
|
expect(items.map((i) => i.key)).toEqual(["generate_from_template"]);
|
|
9
16
|
expect(items[0].title).toBe("Generate from Template");
|
|
@@ -11,7 +18,7 @@ describe("generateActionItems", () => {
|
|
|
11
18
|
|
|
12
19
|
it("offers Suggest impression when asked, and invokes with that type", () => {
|
|
13
20
|
const invoke = vi.fn();
|
|
14
|
-
const items = generateActionItems(["draft-impression"], invoke);
|
|
21
|
+
const items = generateActionItems(["draft-impression"], invoke, translate);
|
|
15
22
|
expect(items.map((i) => i.key)).toEqual(["suggest_impression"]);
|
|
16
23
|
expect(items[0].title).toBe("Suggest impression");
|
|
17
24
|
items[0].onItemClick();
|
|
@@ -19,7 +26,7 @@ describe("generateActionItems", () => {
|
|
|
19
26
|
});
|
|
20
27
|
|
|
21
28
|
it("keeps the caller's order when both are requested", () => {
|
|
22
|
-
const items = generateActionItems(["draft-impression", "fill-template"], vi.fn());
|
|
29
|
+
const items = generateActionItems(["draft-impression", "fill-template"], vi.fn(), translate);
|
|
23
30
|
expect(items.map((i) => i.key)).toEqual(["suggest_impression", "generate_from_template"]);
|
|
24
31
|
});
|
|
25
32
|
});
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import Image from "next/image";
|
|
4
5
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../../shadcnui";
|
|
5
6
|
|
|
6
7
|
export function ErrorDetails({ title, message, code }: { title?: string; message: string; code: number }) {
|
|
8
|
+
const t = useTranslations();
|
|
9
|
+
|
|
7
10
|
if (code === 403)
|
|
8
11
|
return (
|
|
9
12
|
<div className="w-xl max-w-xl">
|
|
@@ -12,10 +15,10 @@ export function ErrorDetails({ title, message, code }: { title?: string; message
|
|
|
12
15
|
<CardTitle className="text-foreground flex flex-col items-center gap-y-4 pb-10 text-4xl">
|
|
13
16
|
<Image src="/logo.webp" alt="Logo" width={100} height={100} priority />
|
|
14
17
|
{/* <div>{code}</div> */}
|
|
15
|
-
<div>
|
|
18
|
+
<div>{t("common.errors.unauthorised")}</div>
|
|
16
19
|
</CardTitle>
|
|
17
20
|
<CardDescription className="text-center text-lg">
|
|
18
|
-
|
|
21
|
+
{t("common.errors.unauthorised_description")}
|
|
19
22
|
</CardDescription>
|
|
20
23
|
</CardHeader>
|
|
21
24
|
<CardContent></CardContent>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { useWatch } from "react-hook-form";
|
|
5
|
+
import { useTranslations } from "next-intl";
|
|
5
6
|
import { CheckIcon, ChevronDownIcon, SearchIcon, XIcon } from "lucide-react";
|
|
6
7
|
import { Badge } from "../../shadcnui/ui/badge";
|
|
7
8
|
import { Input } from "../../shadcnui/ui/input";
|
|
@@ -43,8 +44,8 @@ export function EntityMultiSelector<T extends { id: string }>({
|
|
|
43
44
|
id,
|
|
44
45
|
form,
|
|
45
46
|
label,
|
|
46
|
-
placeholder
|
|
47
|
-
emptyText
|
|
47
|
+
placeholder: placeholderProp,
|
|
48
|
+
emptyText: emptyTextProp,
|
|
48
49
|
isRequired = false,
|
|
49
50
|
retriever,
|
|
50
51
|
retrieverParams = {},
|
|
@@ -59,6 +60,9 @@ export function EntityMultiSelector<T extends { id: string }>({
|
|
|
59
60
|
description,
|
|
60
61
|
disabled = false,
|
|
61
62
|
}: EntityMultiSelectorProps<T>) {
|
|
63
|
+
const t = useTranslations();
|
|
64
|
+
const placeholder = placeholderProp ?? t("ui.search.placeholder_global");
|
|
65
|
+
const emptyText = emptyTextProp ?? t("ui.search.no_results_found");
|
|
62
66
|
const [open, setOpen] = useState(false);
|
|
63
67
|
const [searchTerm, setSearchTerm] = useState("");
|
|
64
68
|
const [options, setOptions] = useState<OptionData<T>[]>([]);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { CircleX, Plus, RefreshCwIcon, SearchIcon, XIcon } from "lucide-react";
|
|
5
|
+
import { useTranslations } from "next-intl";
|
|
5
6
|
import { Command, CommandItem, CommandList } from "../../shadcnui/ui/command";
|
|
6
7
|
import { Input } from "../../shadcnui/ui/input";
|
|
7
8
|
import { Popover, PopoverContent, PopoverTrigger } from "../../shadcnui/ui/popover";
|
|
@@ -46,7 +47,7 @@ export default function EntitySelector<T extends BaseEntity>({
|
|
|
46
47
|
ready = true,
|
|
47
48
|
getLabel = (entity: T) => (entity as any).name,
|
|
48
49
|
label,
|
|
49
|
-
placeholder
|
|
50
|
+
placeholder: placeholderProp,
|
|
50
51
|
emptyText,
|
|
51
52
|
isRequired = false,
|
|
52
53
|
disabled = false,
|
|
@@ -62,6 +63,8 @@ export default function EntitySelector<T extends BaseEntity>({
|
|
|
62
63
|
autoSelectSingle = false,
|
|
63
64
|
useFormWatch: shouldUseFormWatch = false,
|
|
64
65
|
}: EntitySelectorProps<T>) {
|
|
66
|
+
const t = useTranslations();
|
|
67
|
+
const placeholder = placeholderProp ?? t("ui.search.placeholder_global");
|
|
65
68
|
const [open, setOpen] = useState<boolean>(false);
|
|
66
69
|
const searchTermRef = useRef<string>("");
|
|
67
70
|
const [searchTerm, setSearchTerm] = useState<string>("");
|
|
@@ -201,7 +201,7 @@ export const FileUploader = forwardRef<HTMLDivElement, FileUploaderProps & React
|
|
|
201
201
|
if (onFilesRejected) {
|
|
202
202
|
const overflowRejections: FileRejection[] = overflow.map((file) => ({
|
|
203
203
|
file: file as FileWithPath,
|
|
204
|
-
errors: [{ code: ErrorCode.TooManyFiles, message:
|
|
204
|
+
errors: [{ code: ErrorCode.TooManyFiles, message: t("common.errors.file_too_many", { max: maxFiles }) }],
|
|
205
205
|
}));
|
|
206
206
|
const allRejections = [...rejectedFiles, ...overflowRejections];
|
|
207
207
|
if (allRejections.length > 0) onFilesRejected(allRejections);
|
|
@@ -290,9 +290,10 @@ export const FileUploaderContent = forwardRef<HTMLDivElement, React.HTMLAttribut
|
|
|
290
290
|
({ children, className, ...props }, ref) => {
|
|
291
291
|
const { orientation } = useFileUpload();
|
|
292
292
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
293
|
+
const t = useTranslations();
|
|
293
294
|
|
|
294
295
|
return (
|
|
295
|
-
<div className={cn("w-full px-1")} ref={containerRef} aria-description="
|
|
296
|
+
<div className={cn("w-full px-1")} ref={containerRef} aria-description={t("ui.labels.file_holder")}>
|
|
296
297
|
<div
|
|
297
298
|
{...props}
|
|
298
299
|
ref={ref}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { FileIcon, FileImageIcon, FileSpreadsheetIcon, FileTextIcon, UploadIcon, XIcon } from "lucide-react";
|
|
4
|
+
import { useTranslations } from "next-intl";
|
|
4
5
|
import Image from "next/image";
|
|
5
6
|
import { useEffect, useState } from "react";
|
|
6
7
|
import { DropzoneOptions } from "react-dropzone";
|
|
@@ -70,6 +71,7 @@ export default function MultiFileUploader({ files, setFiles, multiple = false, c
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
const FilePreviewItem = ({ file, onRemoveClick }: { file: File; onRemoveClick: () => void }) => {
|
|
74
|
+
const t = useTranslations();
|
|
73
75
|
const [objectUrl, setObjectUrl] = useState<string | null>(null);
|
|
74
76
|
|
|
75
77
|
useEffect(() => {
|
|
@@ -106,7 +108,7 @@ const FilePreviewItem = ({ file, onRemoveClick }: { file: File; onRemoveClick: (
|
|
|
106
108
|
<button
|
|
107
109
|
onClick={onRemoveClick}
|
|
108
110
|
className="absolute top-1.5 end-1.5 z-20 rounded-full bg-black/60 p-1 text-white opacity-0 transition-all duration-200 ease-in-out group-hover:opacity-100 hover:bg-red-600 focus:outline-none"
|
|
109
|
-
aria-label="
|
|
111
|
+
aria-label={t("ui.labels.remove_file")}
|
|
110
112
|
>
|
|
111
113
|
<XIcon className="h-3.5 w-3.5" />
|
|
112
114
|
</button>
|
|
@@ -34,6 +34,7 @@ function BreadcrumbDesktop({
|
|
|
34
34
|
rootLabel: string;
|
|
35
35
|
showRoot: boolean;
|
|
36
36
|
}) {
|
|
37
|
+
const t = useTranslations();
|
|
37
38
|
const [open, setOpen] = useState<boolean>(false);
|
|
38
39
|
|
|
39
40
|
return (
|
|
@@ -62,8 +63,8 @@ function BreadcrumbDesktop({
|
|
|
62
63
|
<BreadcrumbSeparator />
|
|
63
64
|
<BreadcrumbItem>
|
|
64
65
|
<DropdownMenu open={open} onOpenChange={setOpen}>
|
|
65
|
-
<DropdownMenuTrigger className="flex items-center gap-1" aria-label="
|
|
66
|
-
<BreadcrumbEllipsis className="h-4 w-4" />
|
|
66
|
+
<DropdownMenuTrigger className="flex items-center gap-1" aria-label={t("ui.labels.toggle_menu")}>
|
|
67
|
+
<BreadcrumbEllipsis className="h-4 w-4" label={t("ui.labels.more")} />
|
|
67
68
|
</DropdownMenuTrigger>
|
|
68
69
|
<DropdownMenuContent align="start">
|
|
69
70
|
{items.slice(1, -ITEMS_TO_DISPLAY + 2).map((item, index) => (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { useIsMobile } from "@/utils";
|
|
4
5
|
import { useIsBreadcrumbRootHidden } from "../../contexts/BreadcrumbRootContext";
|
|
5
6
|
import { useHeaderRootLabel } from "../../contexts/HeaderChildrenContext";
|
|
@@ -31,6 +32,7 @@ export function Header({
|
|
|
31
32
|
className,
|
|
32
33
|
hideSidebarTriggerOnMobile,
|
|
33
34
|
}: HeaderProps) {
|
|
35
|
+
const t = useTranslations();
|
|
34
36
|
const { breadcrumbs } = useSharedContext();
|
|
35
37
|
const rootLabel = useHeaderRootLabel();
|
|
36
38
|
const isRootHidden = useIsBreadcrumbRootHidden();
|
|
@@ -56,7 +58,7 @@ export function Header({
|
|
|
56
58
|
<div className="bg-sidebar flex h-12 w-full flex-row items-center justify-between ps-2 pe-4">
|
|
57
59
|
{isMobile && logo && <div className="flex shrink-0 flex-row items-center pe-1">{logo}</div>}
|
|
58
60
|
{sidebar && !(isMobile && hideSidebarTriggerOnMobile) && (
|
|
59
|
-
<SidebarTrigger aria-label="
|
|
61
|
+
<SidebarTrigger aria-label={t("ui.labels.toggle_sidebar")} id="sidebar-trigger" />
|
|
60
62
|
)}
|
|
61
63
|
{leftContent}
|
|
62
64
|
<div className="flex w-full flex-row items-center justify-start max-md:min-w-0">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { usePathname } from "next/navigation";
|
|
4
5
|
import { useMobileNavigationItems, type MobileNavigationItem } from "../../contexts/MobileNavigationContext";
|
|
5
6
|
import { Link } from "../../shadcnui";
|
|
@@ -50,6 +51,7 @@ function SlotContent({ item }: { item: MobileNavigationItem }) {
|
|
|
50
51
|
* the end of the page.
|
|
51
52
|
*/
|
|
52
53
|
export function MobileNavigationBar({ className }: { className?: string } = {}) {
|
|
54
|
+
const t = useTranslations();
|
|
53
55
|
const items = useMobileNavigationItems();
|
|
54
56
|
const isMobile = useIsMobile();
|
|
55
57
|
const pathname = usePathname();
|
|
@@ -68,7 +70,7 @@ export function MobileNavigationBar({ className }: { className?: string } = {})
|
|
|
68
70
|
// bar when held by the shell instead.
|
|
69
71
|
return (
|
|
70
72
|
<nav
|
|
71
|
-
aria-label="
|
|
73
|
+
aria-label={t("ui.labels.primary_navigation")}
|
|
72
74
|
data-testid="mobile-navigation-bar"
|
|
73
75
|
data-help="shell.mobile-nav"
|
|
74
76
|
className={cn("bg-sidebar flex w-full shrink-0 flex-row items-stretch rounded-lg border", className)}
|
|
@@ -50,11 +50,11 @@ function withShared(node: React.ReactNode) {
|
|
|
50
50
|
describe("Header", () => {
|
|
51
51
|
it("renders the sidebar toggle inside a SidebarProvider", () => {
|
|
52
52
|
render(withShared(<SidebarProvider>{<Header />}</SidebarProvider>));
|
|
53
|
-
expect(screen.queryByRole("button", { name: "
|
|
53
|
+
expect(screen.queryByRole("button", { name: "ui.labels.toggle_sidebar" })).not.toBeNull();
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
it("renders without a SidebarProvider and shows no sidebar toggle", () => {
|
|
57
57
|
expect(() => render(withShared(<Header />))).not.toThrow();
|
|
58
|
-
expect(screen.queryByRole("button", { name: "
|
|
58
|
+
expect(screen.queryByRole("button", { name: "ui.labels.toggle_sidebar" })).toBeNull();
|
|
59
59
|
});
|
|
60
60
|
});
|
|
@@ -5,7 +5,7 @@ import { ContentListTable } from "../ContentListTable";
|
|
|
5
5
|
// `ContentListTable` imports `useTableGenerator` from the `../../hooks` barrel,
|
|
6
6
|
// which resolves columns through the table-generator registry. Mocking the
|
|
7
7
|
// barrel (spreading the actual module so the other exports survive) is the same
|
|
8
|
-
// approach the sibling `ContentListTable.
|
|
8
|
+
// approach the sibling `ContentListTable.rendering.spec.tsx` in this folder uses; one
|
|
9
9
|
// plain column is enough here.
|
|
10
10
|
// `mockColumns.expandToggle` switches the mocked name cell to a button that
|
|
11
11
|
// toggles row expansion (the `renderExpandedRow` suite), plus a second column so
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { ColumnDef } from "@tanstack/react-table";
|
|
2
|
+
import { useTranslations } from "next-intl";
|
|
2
3
|
import { Checkbox } from "../../../shadcnui";
|
|
3
4
|
|
|
5
|
+
function SelectRowCheckbox({
|
|
6
|
+
checked,
|
|
7
|
+
onCheckedChange,
|
|
8
|
+
}: {
|
|
9
|
+
checked: boolean;
|
|
10
|
+
onCheckedChange: (value: boolean) => void;
|
|
11
|
+
}) {
|
|
12
|
+
const t = useTranslations();
|
|
13
|
+
return <Checkbox checked={checked} onCheckedChange={onCheckedChange} aria-label={t("ui.labels.select_row")} />;
|
|
14
|
+
}
|
|
15
|
+
|
|
4
16
|
export const cellId = (params: {
|
|
5
17
|
name: string;
|
|
6
18
|
checkedIds?: string[];
|
|
@@ -12,13 +24,12 @@ export const cellId = (params: {
|
|
|
12
24
|
header: "",
|
|
13
25
|
cell: ({ row }) =>
|
|
14
26
|
params.toggleId ? (
|
|
15
|
-
<
|
|
27
|
+
<SelectRowCheckbox
|
|
16
28
|
checked={params.checkedIds?.includes(row.getValue(params.name)) || false}
|
|
17
29
|
onCheckedChange={(value) => {
|
|
18
30
|
row.toggleSelected(!!value);
|
|
19
31
|
params.toggleId?.(row.getValue(params.name));
|
|
20
32
|
}}
|
|
21
|
-
aria-label="Select row"
|
|
22
33
|
/>
|
|
23
34
|
) : null,
|
|
24
35
|
enableSorting: false,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ApiRequestDataTypeInterface } from "../interfaces/ApiRequestDataTypeInterface";
|
|
2
2
|
import { ApiResponseInterface } from "../interfaces/ApiResponseInterface";
|
|
3
3
|
import { setLastApiResponse } from "./lastApiResponse";
|
|
4
|
-
import { getI18nServerLocale } from "../../i18n/config";
|
|
4
|
+
import { getI18nServerLocale, getI18nSupportedLocales } from "../../i18n/config";
|
|
5
5
|
|
|
6
6
|
export enum HttpMethod {
|
|
7
7
|
GET = "GET",
|
|
@@ -68,8 +68,8 @@ export abstract class AbstractService {
|
|
|
68
68
|
const pathSegments = window.location.pathname.split("/").filter(Boolean);
|
|
69
69
|
const urlLocale = pathSegments[0];
|
|
70
70
|
|
|
71
|
-
// Validate against supported locales (
|
|
72
|
-
const supportedLocales =
|
|
71
|
+
// Validate against the app's supported locales (default ["en"])
|
|
72
|
+
const supportedLocales = getI18nSupportedLocales();
|
|
73
73
|
if (urlLocale && supportedLocales.includes(urlLocale)) {
|
|
74
74
|
return urlLocale;
|
|
75
75
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
ClientJsonApiDelete,
|
|
11
11
|
} from "../../client/JsonApiClient";
|
|
12
12
|
import { setLastApiResponse } from "./lastApiResponse";
|
|
13
|
+
import { getI18nSupportedLocales } from "../../i18n/config";
|
|
13
14
|
// Duplicated to avoid importing from AbstractService which pulls in server code
|
|
14
15
|
// These are exported so client services can import them from here instead of core
|
|
15
16
|
export enum ClientHttpMethod {
|
|
@@ -72,8 +73,8 @@ export abstract class ClientAbstractService {
|
|
|
72
73
|
const pathSegments = window.location.pathname.split("/").filter(Boolean);
|
|
73
74
|
const urlLocale = pathSegments[0];
|
|
74
75
|
|
|
75
|
-
// Validate against supported locales (
|
|
76
|
-
const supportedLocales =
|
|
76
|
+
// Validate against the app's supported locales (default ["en"])
|
|
77
|
+
const supportedLocales = getI18nSupportedLocales();
|
|
77
78
|
if (urlLocale && supportedLocales.includes(urlLocale)) {
|
|
78
79
|
return urlLocale;
|
|
79
80
|
}
|
|
@@ -108,6 +108,69 @@ function humaniseKey(key: string): string {
|
|
|
108
108
|
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* The untyped call signature of next-intl's `t`. Every key below is computed
|
|
113
|
+
* from wire identifiers, so the generated message-key union cannot describe it.
|
|
114
|
+
*/
|
|
115
|
+
type LooseTranslate = ((key: string, values?: Record<string, unknown>) => string) & {
|
|
116
|
+
has?: (key: string) => boolean;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Translates `key` when the host app defines it, else `undefined`. `t.has` is
|
|
121
|
+
* the only non-throwing probe: a plain `t()` on a missing key either throws or
|
|
122
|
+
* returns the key path, depending on the app's next-intl error handler.
|
|
123
|
+
*/
|
|
124
|
+
function translateIfPresent(t: LooseTranslate, key: string, values?: Record<string, unknown>): string | undefined {
|
|
125
|
+
if (typeof t.has !== "function" || !t.has(key)) return undefined;
|
|
126
|
+
try {
|
|
127
|
+
const text = t(key, values);
|
|
128
|
+
return text && text !== key ? text : undefined;
|
|
129
|
+
} catch {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Message-key segments a JSON:API type may be filed under in the app's
|
|
136
|
+
* `features.<feature>` namespace: `npcs` → `npc`, `knives` → `knife`,
|
|
137
|
+
* `stories` → `story`, `scene-cornerstones` → `scene-cornerstone` and
|
|
138
|
+
* `sceneCornerstone`.
|
|
139
|
+
*/
|
|
140
|
+
function featureKeyCandidates(type: string): string[] {
|
|
141
|
+
if (!type) return [];
|
|
142
|
+
const singulars = new Set<string>();
|
|
143
|
+
if (type.endsWith("ives")) singulars.add(`${type.slice(0, -4)}ife`);
|
|
144
|
+
if (type.endsWith("ies")) singulars.add(`${type.slice(0, -3)}y`);
|
|
145
|
+
if (type.endsWith("s")) singulars.add(type.slice(0, -1));
|
|
146
|
+
singulars.add(type);
|
|
147
|
+
const candidates = new Set<string>();
|
|
148
|
+
for (const singular of singulars) {
|
|
149
|
+
candidates.add(singular);
|
|
150
|
+
candidates.add(singular.replace(/[-_]+([a-z0-9])/g, (_, letter: string) => letter.toUpperCase()));
|
|
151
|
+
}
|
|
152
|
+
return [...candidates];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Label of an attribute or relationship, in the active language. Tries, in
|
|
157
|
+
* order: the entity editor's own label (`features.<feature>.fields.<key>.label`),
|
|
158
|
+
* the generic `ui.fields.<key>`, then the humanised key — so a field no app
|
|
159
|
+
* catalogue knows still renders exactly as it did before translation existed.
|
|
160
|
+
*/
|
|
161
|
+
function fieldLabel(t: LooseTranslate, type: string, key: string): string {
|
|
162
|
+
for (const feature of featureKeyCandidates(type)) {
|
|
163
|
+
const label = translateIfPresent(t, `features.${feature}.fields.${key}.label`);
|
|
164
|
+
if (label !== undefined) return label;
|
|
165
|
+
}
|
|
166
|
+
return translateIfPresent(t, `ui.fields.${key}`) ?? humaniseKey(key);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Singular entity name from the app's `entities.<type>` plural message, else the humanised type. */
|
|
170
|
+
function entityName(t: LooseTranslate, type: string): string {
|
|
171
|
+
return translateIfPresent(t, `entities.${type}`, { count: 1 }) ?? humaniseType(type);
|
|
172
|
+
}
|
|
173
|
+
|
|
111
174
|
/**
|
|
112
175
|
* Plain text of a BlockNote document, without importing BlockNote: walk the
|
|
113
176
|
* blocks, concatenate the text runs of each block's `content`, recurse into
|
|
@@ -336,7 +399,9 @@ export function ApprovalActionCard({ actionId, summary, onResolved, hiddenRelati
|
|
|
336
399
|
const presented = useMemo(() => parseOperatorProposal(actionProposal), [actionProposal]);
|
|
337
400
|
const legacy = useMemo(() => (presented ? undefined : parseLegacyProposal(toolArgs)), [presented, toolArgs]);
|
|
338
401
|
const isUpdate = toolName === "update_entity";
|
|
339
|
-
const
|
|
402
|
+
const entityType = presented?.type ?? legacy?.type ?? "";
|
|
403
|
+
const loose = t as unknown as LooseTranslate;
|
|
404
|
+
const entityLabel = entityName(loose, entityType);
|
|
340
405
|
const targetLabel = presented?.target?.label ?? "";
|
|
341
406
|
|
|
342
407
|
const rows = useMemo<ProposalRow[]>(() => {
|
|
@@ -345,7 +410,9 @@ export function ApprovalActionCard({ actionId, summary, onResolved, hiddenRelati
|
|
|
345
410
|
const attributes = presented?.attributes ?? legacy?.attributes;
|
|
346
411
|
const attributeRows = Object.entries(attributes ?? {}).flatMap<ProposalRow>(([key, value]) => {
|
|
347
412
|
const text = formatAttribute(value, yes, no);
|
|
348
|
-
return text === undefined
|
|
413
|
+
return text === undefined
|
|
414
|
+
? []
|
|
415
|
+
: [{ key: `attribute:${key}`, label: fieldLabel(loose, entityType, key), value: text }];
|
|
349
416
|
});
|
|
350
417
|
if (!presented) return attributeRows;
|
|
351
418
|
|
|
@@ -353,7 +420,9 @@ export function ApprovalActionCard({ actionId, summary, onResolved, hiddenRelati
|
|
|
353
420
|
const relationshipRows = Object.entries(presented.relationships ?? {}).flatMap<ProposalRow>(([key, refs]) => {
|
|
354
421
|
if (hidden.has(key)) return [];
|
|
355
422
|
const text = formatRefs(refs);
|
|
356
|
-
return text === undefined
|
|
423
|
+
return text === undefined
|
|
424
|
+
? []
|
|
425
|
+
: [{ key: `relationship:${key}`, label: fieldLabel(loose, entityType, key), value: text }];
|
|
357
426
|
});
|
|
358
427
|
|
|
359
428
|
const linkRows: ProposalRow[] = [];
|
|
@@ -361,7 +430,7 @@ export function ApprovalActionCard({ actionId, summary, onResolved, hiddenRelati
|
|
|
361
430
|
linkRows.push({
|
|
362
431
|
key: "link:relationship",
|
|
363
432
|
label: t("features.assistant.approval.proposal.relationship"),
|
|
364
|
-
value:
|
|
433
|
+
value: fieldLabel(loose, entityType, presented.relationship),
|
|
365
434
|
});
|
|
366
435
|
}
|
|
367
436
|
const targetsText = formatRefs(presented.targets ?? []);
|
|
@@ -374,7 +443,7 @@ export function ApprovalActionCard({ actionId, summary, onResolved, hiddenRelati
|
|
|
374
443
|
}
|
|
375
444
|
|
|
376
445
|
return [...attributeRows, ...relationshipRows, ...linkRows];
|
|
377
|
-
}, [presented, legacy, t, hiddenRelationshipKeys]);
|
|
446
|
+
}, [presented, legacy, t, loose, entityType, hiddenRelationshipKeys]);
|
|
378
447
|
|
|
379
448
|
const hasProposal = rows.length > 0;
|
|
380
449
|
|