@carlonicora/nextjs-jsonapi 4.1.1 → 4.2.0
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/scripts/vitest-slow-tests.reporter.spec.ts +54 -0
- 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 +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/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.test.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 +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__/use-ui-text.test.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 +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
|
@@ -5,6 +5,7 @@ import { ActionBarProvider, useSharedContext } from "@/contexts";
|
|
|
5
5
|
import { ModuleWithPermissions } from "@/permissions";
|
|
6
6
|
import { cn, useIsMobile } from "@/utils";
|
|
7
7
|
import { InfoIcon } from "lucide-react";
|
|
8
|
+
import { useTranslations } from "next-intl";
|
|
8
9
|
import { ReactNode } from "react";
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -35,13 +36,15 @@ export function RoundPageContainerTitle({
|
|
|
35
36
|
showDetails,
|
|
36
37
|
setShowDetails,
|
|
37
38
|
}: RoundPageContainerTitleProps) {
|
|
39
|
+
const t = useTranslations();
|
|
38
40
|
const { title } = useSharedContext();
|
|
39
41
|
const isMobile = useIsMobile();
|
|
40
42
|
|
|
41
43
|
// A Tooltip on the toggle stays stuck on screen after a tap, so a phone gets
|
|
42
44
|
// the bare button. The aria-label carries the same text the tooltip shows,
|
|
43
45
|
// since the icon alone does not name the control.
|
|
44
|
-
const
|
|
46
|
+
const toggleVerb = showDetails ? t("ui.labels.hide") : t("ui.labels.show");
|
|
47
|
+
const detailsLabel = `${toggleVerb} ${typeof detailsTitle === "string" ? detailsTitle : t("ui.labels.details_fallback")}`;
|
|
45
48
|
const detailsToggle = details ? (
|
|
46
49
|
isMobile ? (
|
|
47
50
|
<Button
|
|
@@ -71,7 +74,7 @@ export function RoundPageContainerTitle({
|
|
|
71
74
|
{detailsIcon ?? <InfoIcon />}
|
|
72
75
|
</TooltipTrigger>
|
|
73
76
|
<TooltipContent>
|
|
74
|
-
{
|
|
77
|
+
{toggleVerb} {detailsTitle ?? t("ui.labels.details_fallback")}
|
|
75
78
|
</TooltipContent>
|
|
76
79
|
</Tooltip>
|
|
77
80
|
)
|
|
@@ -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
|
});
|
|
@@ -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
|
}
|