@plumile/backoffice-react 0.1.76 → 0.1.77

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.
Files changed (74) hide show
  1. package/README.md +25 -0
  2. package/lib/esm/{AcceptInvitationScreen-CNLlSbdx.js → AcceptInvitationScreen-b2yaZeKV.js} +4 -4
  3. package/lib/esm/{AcceptInvitationScreen-CNLlSbdx.js.map → AcceptInvitationScreen-b2yaZeKV.js.map} +1 -1
  4. package/lib/esm/{AuthPanel-ttoH9YxE.js → AuthPanel-Co626p7A.js} +2 -2
  5. package/lib/esm/{AuthPanel-ttoH9YxE.js.map → AuthPanel-Co626p7A.js.map} +1 -1
  6. package/lib/esm/{BackofficeAcceptInvitationPage-DRxM1Z-U.js → BackofficeAcceptInvitationPage-BBHL6VvG.js} +7 -7
  7. package/lib/esm/{BackofficeAcceptInvitationPage-DRxM1Z-U.js.map → BackofficeAcceptInvitationPage-BBHL6VvG.js.map} +1 -1
  8. package/lib/esm/{sprinkles.css.ts.vanilla-n-NK_DwW.js → BackofficeConfigContext-R0t1owTI.js} +1 -1
  9. package/lib/esm/BackofficeConfigContext-R0t1owTI.js.map +1 -0
  10. package/lib/esm/{BackofficeDashboardPage-GKyT60qb.js → BackofficeDashboardPage-B5_YC1ZL.js} +5 -5
  11. package/lib/esm/BackofficeDashboardPage-B5_YC1ZL.js.map +1 -0
  12. package/lib/esm/{BackofficeDetailPayload-B-qs8Fai.js → BackofficeDetailPayload-CbsZKnw6.js} +10 -10
  13. package/lib/esm/BackofficeDetailPayload-CbsZKnw6.js.map +1 -0
  14. package/lib/esm/{BackofficeEntityActionFormDialog-FHlW0vys.js → BackofficeEntityActionFormDialog-BzKi1eyv.js} +19 -19
  15. package/lib/esm/BackofficeEntityActionFormDialog-BzKi1eyv.js.map +1 -0
  16. package/lib/esm/{BackofficeEntityDetailPage-CdrMXu5a.js → BackofficeEntityDetailPage-CE4tl5ZJ.js} +11 -11
  17. package/lib/esm/BackofficeEntityDetailPage-CE4tl5ZJ.js.map +1 -0
  18. package/lib/esm/{BackofficeEntityListPage-BxgqdFpL.js → BackofficeEntityListPage-CtLI15-L.js} +7 -7
  19. package/lib/esm/BackofficeEntityListPage-CtLI15-L.js.map +1 -0
  20. package/lib/esm/{BackofficeLayoutPage-k9OkLfXb.js → BackofficeLayoutPage-DpvTzwtK.js} +6 -6
  21. package/lib/esm/BackofficeLayoutPage-DpvTzwtK.js.map +1 -0
  22. package/lib/esm/{BackofficeLoginPage-C64BcxMw.js → BackofficeLoginPage-BeihjFO2.js} +8 -8
  23. package/lib/esm/{BackofficeLoginPage-C64BcxMw.js.map → BackofficeLoginPage-BeihjFO2.js.map} +1 -1
  24. package/lib/esm/{BackofficePasswordResetCompletePage-BA7kkUxN.js → BackofficePasswordResetCompletePage-CkaMaFNT.js} +6 -6
  25. package/lib/esm/{BackofficePasswordResetCompletePage-BA7kkUxN.js.map → BackofficePasswordResetCompletePage-CkaMaFNT.js.map} +1 -1
  26. package/lib/esm/{BackofficePasswordResetRequestPage-B9wSZcyn.js → BackofficePasswordResetRequestPage-Df8mcLkC.js} +6 -6
  27. package/lib/esm/{BackofficePasswordResetRequestPage-B9wSZcyn.js.map → BackofficePasswordResetRequestPage-Df8mcLkC.js.map} +1 -1
  28. package/lib/esm/{BackofficeRightPageLayout-ktdlvTZJ.js → BackofficeRightPageLayout-D7QRE8KZ.js} +3 -3
  29. package/lib/esm/BackofficeRightPageLayout-D7QRE8KZ.js.map +1 -0
  30. package/lib/esm/{BackofficeVerifyEmailPage-CAzMqo2K.js → BackofficeVerifyEmailPage-CE_6-wGN.js} +5 -5
  31. package/lib/esm/{BackofficeVerifyEmailPage-CAzMqo2K.js.map → BackofficeVerifyEmailPage-CE_6-wGN.js.map} +1 -1
  32. package/lib/esm/{EntityFilterValue-SC_jkj4O.js → EntityFilterValue-Bn9VGr2M.js} +2 -2
  33. package/lib/esm/{EntityFilterValue-SC_jkj4O.js.map → EntityFilterValue-Bn9VGr2M.js.map} +1 -1
  34. package/lib/esm/{EntityIdPickerDialog-FMC5Hx_I.js → EntityIdPickerDialog-D_qAlrU6.js} +4 -4
  35. package/lib/esm/EntityIdPickerDialog-D_qAlrU6.js.map +1 -0
  36. package/lib/esm/{LazyBackofficeEntityActionFormDialog-DOEgoIfT.js → LazyBackofficeEntityActionFormDialog-DndjG4kV.js} +5 -5
  37. package/lib/esm/LazyBackofficeEntityActionFormDialog-DndjG4kV.js.map +1 -0
  38. package/lib/esm/{PasswordResetCompleteScreen-C4GQdavL.js → PasswordResetCompleteScreen-CWxk9AWl.js} +3 -3
  39. package/lib/esm/{PasswordResetCompleteScreen-C4GQdavL.js.map → PasswordResetCompleteScreen-CWxk9AWl.js.map} +1 -1
  40. package/lib/esm/{PasswordResetRequestScreen-aUY_yDqM.js → PasswordResetRequestScreen-DBIHOmsl.js} +3 -3
  41. package/lib/esm/{PasswordResetRequestScreen-aUY_yDqM.js.map → PasswordResetRequestScreen-DBIHOmsl.js.map} +1 -1
  42. package/lib/esm/{VerifyEmailScreen-2DuHJgdY.js → VerifyEmailScreen-CUOEwmf1.js} +2 -2
  43. package/lib/esm/{VerifyEmailScreen-2DuHJgdY.js.map → VerifyEmailScreen-CUOEwmf1.js.map} +1 -1
  44. package/lib/esm/backoffice-react.js +21 -21
  45. package/lib/esm/backoffice-react.js.map +1 -1
  46. package/lib/esm/loginPage.css-4M4PrzUn.js +12 -0
  47. package/lib/esm/loginPage.css-4M4PrzUn.js.map +1 -0
  48. package/lib/esm/style.css +1 -1
  49. package/lib/esm/{synchronizeAuthStatusQuery-lLR0XFew.js → synchronizeAuthStatusQuery-Ba776lwa.js} +4 -4
  50. package/lib/esm/{synchronizeAuthStatusQuery-lLR0XFew.js.map → synchronizeAuthStatusQuery-Ba776lwa.js.map} +1 -1
  51. package/lib/esm/{useAuth-AczFS-oL.js → useAuth-DzWJXJ_9.js} +2 -2
  52. package/lib/esm/{useAuth-AczFS-oL.js.map → useAuth-DzWJXJ_9.js.map} +1 -1
  53. package/lib/esm/{useBackofficeAuth-BHteJQzA.js → useBackofficeAuth-L9k9i9BI.js} +3 -3
  54. package/lib/esm/{useBackofficeAuth-BHteJQzA.js.map → useBackofficeAuth-L9k9i9BI.js.map} +1 -1
  55. package/lib/esm/{useBackofficeLazyValue-B0-ckL6e.js → useBackofficeLazyValue-Dnii1_dE.js} +2 -2
  56. package/lib/esm/{useBackofficeLazyValue-B0-ckL6e.js.map → useBackofficeLazyValue-Dnii1_dE.js.map} +1 -1
  57. package/lib/types/auth/login/loginPage.css.d.ts.map +1 -1
  58. package/lib/types/components/backoffice/detail/backofficeDetailRelationLink.css.d.ts.map +1 -1
  59. package/lib/types/components/backoffice/filters/backofficeFilterAction.css.d.ts.map +1 -1
  60. package/lib/types/components/backoffice/layout/backofficeSidebarActions.css.d.ts.map +1 -1
  61. package/lib/types/components/backoffice/routing/backofficeRoutePendingBar.css.d.ts.map +1 -1
  62. package/package.json +5 -5
  63. package/lib/esm/BackofficeDashboardPage-GKyT60qb.js.map +0 -1
  64. package/lib/esm/BackofficeDetailPayload-B-qs8Fai.js.map +0 -1
  65. package/lib/esm/BackofficeEntityActionFormDialog-FHlW0vys.js.map +0 -1
  66. package/lib/esm/BackofficeEntityDetailPage-CdrMXu5a.js.map +0 -1
  67. package/lib/esm/BackofficeEntityListPage-BxgqdFpL.js.map +0 -1
  68. package/lib/esm/BackofficeLayoutPage-k9OkLfXb.js.map +0 -1
  69. package/lib/esm/BackofficeRightPageLayout-ktdlvTZJ.js.map +0 -1
  70. package/lib/esm/EntityIdPickerDialog-FMC5Hx_I.js.map +0 -1
  71. package/lib/esm/LazyBackofficeEntityActionFormDialog-DOEgoIfT.js.map +0 -1
  72. package/lib/esm/loginPage.css-BDQJNcSr.js +0 -12
  73. package/lib/esm/loginPage.css-BDQJNcSr.js.map +0 -1
  74. package/lib/esm/sprinkles.css.ts.vanilla-n-NK_DwW.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"backoffice-react.js","names":[],"sources":["../../src/i18n/createI18nInstance.ts","../../src/i18n/mergeResourceLanguages.ts","../../src/i18n/locales/en/backofficeReact.json","../../src/i18n/locales/fr/backofficeReact.json","../../src/i18n/locales/en/shared.json","../../src/i18n/locales/fr/shared.json","../../src/i18n/resources.ts","../../src/relay/RelayProvider.tsx","../../src/router/createBackofficeRoutes.tsx","../../src/components/backoffice/routing/backofficeRouteFallback.css.ts","../../src/components/backoffice/routing/BackofficeRouteFallback.tsx","../../src/components/backoffice/routing/backofficeRoutePendingBar.css.ts","../../src/components/backoffice/routing/BackofficeRoutePendingBar.tsx","../../src/provider/entityRegistry.ts","../../src/provider/BackofficeProvider.tsx","../../src/provider/lazyValue.ts","../../src/filters/filterHelpers.ts","../../src/components/backoffice/filters/backofficeFilterAction.css.ts","../../src/components/backoffice/filters/BackofficeFilterAction.tsx","../../src/components/backoffice/overview/backofficeOverviewLayout.css.ts","../../src/components/backoffice/overview/BackofficeOverviewLayout.tsx","../../src/components/backoffice/refs/backofficeRelatedCountLink.css.ts","../../src/components/backoffice/refs/BackofficeRelatedCountLink.tsx","../../src/components/backoffice/scaffolds/backofficeTabbedDetailShell.css.ts","../../src/components/backoffice/scaffolds/BackofficeTabbedDetailShell.tsx","../../src/components/backoffice/shared/backofficeInlineFilterRow.css.ts","../../src/components/backoffice/shared/BackofficeInlineFilterRow.tsx","../../src/hooks/useConditionalSubscription.ts","../../src/hooks/useCopyToClipboard.ts","../../src/hooks/useRefetchNeededReload.ts","../../src/i18n/useReviewStatusLabel.ts","../../src/modules/base64.ts","../../src/modules/formatFileSize.ts","../../src/relay/createInlineReader.ts","../../src/relay/identityView.ts"],"sourcesContent":["import { createInstance, type i18n, type InitOptions } from 'i18next';\nimport LanguageDetector, {\n type DetectorOptions,\n} from 'i18next-browser-languagedetector';\nimport { initReactI18next } from 'react-i18next';\n\ntype InitOptionsWithDetection = Omit<\n InitOptions,\n 'resources' | 'lng' | 'fallbackLng'\n> & {\n detection?: DetectorOptions;\n};\n\ntype InitConfig = InitOptions & {\n detection?: DetectorOptions;\n};\n\nexport interface CreateI18nOptions {\n resources: NonNullable<InitOptions['resources']>;\n lng?: InitOptions['lng'];\n fallbackLng?: InitOptions['fallbackLng'];\n initOptions?: InitOptionsWithDetection;\n instance?: i18n;\n useLanguageDetector?: boolean;\n detection?: DetectorOptions;\n}\n\nconst DEFAULT_NUMBER_OPTIONS = { maximumFractionDigits: 2 };\nconst DEFAULT_CURRENCY_OPTIONS = {\n style: 'currency',\n currency: 'USD',\n maximumFractionDigits: 2,\n} as const;\nconst DEFAULT_DATE_OPTIONS = { dateStyle: 'medium' } as const;\nconst DEFAULT_DATE_TIME_OPTIONS = {\n dateStyle: 'medium',\n timeStyle: 'short',\n} as const;\nconst DEFAULT_PERCENT_OPTIONS = { style: 'percent' } as const;\n\n/** Returns the best locale value from i18next's language list. */\nfunction getLocale(lng: string | string[] | undefined): string | undefined {\n if (Array.isArray(lng)) {\n return lng[0];\n }\n return lng;\n}\n\n/** Stringify interpolation values while avoiding Object.prototype defaults. */\nfunction stringifyValue(value: unknown): string {\n if (value == null) {\n return '';\n }\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean' ||\n typeof value === 'bigint'\n ) {\n return String(value);\n }\n if (typeof value === 'symbol') {\n return value.toString();\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\n/** Format interpolated values using Intl based on the i18n format token. */\nfunction formatValue(\n value: unknown,\n format: string | undefined,\n lng: string | string[] | undefined,\n): string {\n if (format == null) {\n return stringifyValue(value);\n }\n\n const locale = getLocale(lng);\n\n if (format === 'number' && typeof value === 'number') {\n return new Intl.NumberFormat(locale, DEFAULT_NUMBER_OPTIONS).format(value);\n }\n\n if (format === 'currency' && typeof value === 'number') {\n return new Intl.NumberFormat(locale, DEFAULT_CURRENCY_OPTIONS).format(\n value,\n );\n }\n\n if (format === 'percent' && typeof value === 'number') {\n return new Intl.NumberFormat(locale, DEFAULT_PERCENT_OPTIONS).format(value);\n }\n\n if ((format === 'date' || format === 'datetime') && value != null) {\n let date: Date;\n if (value instanceof Date) {\n date = value;\n } else if (typeof value === 'string' || typeof value === 'number') {\n date = new Date(value);\n } else {\n date = new Date(stringifyValue(value));\n }\n if (!Number.isNaN(date.getTime())) {\n if (format === 'date') {\n return new Intl.DateTimeFormat(locale, DEFAULT_DATE_OPTIONS).format(\n date,\n );\n }\n return new Intl.DateTimeFormat(locale, DEFAULT_DATE_TIME_OPTIONS).format(\n date,\n );\n }\n }\n\n return stringifyValue(value);\n}\n\n/**\n * Create and initialize an i18next instance configured for React.\n * Each frontend can call this helper with its own resource bundles.\n */\nexport async function createI18nInstance(\n options: CreateI18nOptions,\n): Promise<i18n> {\n const {\n resources,\n lng,\n fallbackLng = 'en',\n initOptions = {},\n instance = createInstance(),\n useLanguageDetector = false,\n detection,\n } = options;\n\n const { interpolation, ...restInitOptions } = initOptions;\n\n if (useLanguageDetector) {\n instance.use(LanguageDetector);\n }\n instance.use(initReactI18next);\n\n try {\n const initConfig: InitConfig = {\n ...restInitOptions,\n resources,\n fallbackLng,\n interpolation: {\n escapeValue: false,\n format: interpolation?.format ?? formatValue,\n ...interpolation,\n },\n };\n if (lng != null) {\n initConfig.lng = lng;\n }\n\n const detectionOptions = detection ?? initOptions.detection;\n if (useLanguageDetector && detectionOptions != null) {\n initConfig.detection = detectionOptions;\n }\n\n await instance.init(initConfig);\n } catch {\n // Swallow initialization errors to avoid blocking the UI.\n // Callers can inspect the instance for diagnostics if needed.\n }\n\n return instance;\n}\n","import type { ResourceLanguage } from 'i18next';\n\nconst isResourceObject = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value != null && !Array.isArray(value);\n};\n\nconst asResourceLanguage = (value: unknown): ResourceLanguage => {\n if (isResourceObject(value)) {\n return value as ResourceLanguage;\n }\n return {};\n};\n\n/**\n * Deep-merges i18next resource language objects while preserving nested keys.\n */\nexport function mergeResourceLanguages(\n ...values: readonly ResourceLanguage[]\n): ResourceLanguage {\n const output: ResourceLanguage = {};\n\n values.forEach((value) => {\n Object.entries(value).forEach(([key, nextValue]) => {\n const currentValue = output[key];\n if (isResourceObject(currentValue) && isResourceObject(nextValue)) {\n output[key] = mergeResourceLanguages(\n asResourceLanguage(currentValue),\n asResourceLanguage(nextValue),\n );\n return;\n }\n output[key] = nextValue;\n });\n });\n\n return output;\n}\n","{\n \"actions\": {\n \"form\": {\n \"cancel\": \"Cancel\",\n \"errors\": {\n \"invalidJson\": \"{{label}} must be valid JSON.\",\n \"invalidJsonArray\": \"{{label}} must be a valid JSON array.\",\n \"invalidJsonObject\": \"{{label}} must be a valid JSON object.\",\n \"invalidNumber\": \"{{label}} must be a valid number.\",\n \"invalidPayload\": \"The submitted payload is invalid.\",\n \"required\": \"{{label}} is required.\"\n }\n },\n \"view\": \"View\"\n },\n \"auth\": {\n \"acceptInvitation\": {\n \"errors\": {\n \"alreadyAccepted\": \"This invitation has already been accepted.\",\n \"default\": \"Unable to accept this invitation. Please try again.\",\n \"emailMismatch\": \"This invitation was sent to a different email address.\",\n \"expired\": \"This invitation has expired.\",\n \"invalidToken\": \"This invitation token is invalid.\",\n \"passwordMismatch\": \"Password and confirmation do not match.\",\n \"passwordPolicyViolation\": \"Your password does not meet policy requirements.\",\n \"rateLimited\": \"Too many attempts. Please wait and try again.\"\n }\n },\n \"loginFlow\": {\n \"errors\": {\n \"accountLocked\": \"Your account is locked.\",\n \"invalidCredentials\": \"Invalid email or password.\",\n \"invalidEmail\": \"Please enter a valid email address.\",\n \"rateLimited\": \"Too many attempts. Please wait and try again.\",\n \"tryAgain\": \"Something went wrong. Please try again.\"\n }\n },\n \"mfa\": {\n \"errors\": {\n \"expired\": \"Your verification challenge has expired.\",\n \"invalidChallenge\": \"Your verification challenge is invalid.\",\n \"invalidCode\": \"The verification code is invalid.\",\n \"tooManyAttempts\": \"Too many invalid codes. Please try again later.\",\n \"verificationFailed\": \"Unable to verify the code. Please try again.\"\n }\n },\n \"passkey\": {\n \"errors\": {\n \"challengeExpired\": \"Your passkey challenge has expired.\",\n \"failed\": \"Passkey authentication failed. Please try again.\",\n \"invalidAssertion\": \"The passkey response is invalid.\",\n \"invalidChallenge\": \"The passkey challenge is invalid.\",\n \"invalidEmail\": \"Please enter a valid email address.\",\n \"notAvailable\": \"Passkeys are not available on this device.\",\n \"notFound\": \"No passkey found for this account.\"\n }\n },\n \"passwordResetComplete\": {\n \"errors\": {\n \"expired\": \"This reset link has expired.\",\n \"invalid\": \"This reset link is invalid.\",\n \"policyViolation\": \"Your new password does not meet policy requirements.\"\n }\n },\n \"passwordResetRequest\": {\n \"errors\": {\n \"invalidEmail\": \"Please enter a valid email address.\",\n \"rateLimited\": \"Too many requests. Please wait and try again.\",\n \"startFailed\": \"Unable to start password reset. Please try again.\"\n }\n },\n \"verifyEmail\": {\n \"errors\": {\n \"alreadyVerified\": \"This email address is already verified.\",\n \"expired\": \"This verification link has expired.\",\n \"invalid\": \"This verification link is invalid.\"\n }\n }\n },\n \"common\": {\n \"actions\": {\n \"change\": \"Change\",\n \"clear\": \"Clear\",\n \"close\": \"Close\",\n \"copied\": \"Copied\",\n \"copy\": \"Copy\",\n \"pick\": \"Pick\",\n \"retry\": \"Retry\"\n },\n \"boolean\": {\n \"no\": \"No\",\n \"yes\": \"Yes\"\n },\n \"loading\": \"Loading...\",\n \"notAvailable\": \"N/A\"\n },\n \"dashboard\": {\n \"actions\": {\n \"openList\": \"Open list\",\n \"openTool\": \"Open tool\"\n },\n \"subtitle\": \"Overview of the Work context.\",\n \"title\": \"Dashboard\"\n },\n \"detail\": {\n \"notFound\": \"Not found\"\n },\n \"emptyState\": {\n \"listEmpty\": {\n \"description\": \"There are no records to display.\",\n \"title\": \"No results\"\n },\n \"listEmptyFiltered\": {\n \"actions\": {\n \"reset\": \"Reset filters\"\n },\n \"description\": \"No results match the current filters.\"\n }\n },\n \"filters\": {\n \"actions\": {\n \"filterBy\": \"Filter by {{label}}\"\n },\n \"all\": \"All {{label}}\",\n \"allFilters\": \"All filters\",\n \"allFiltersWithCount_one\": \"All filters ({{count}})\",\n \"allFiltersWithCount_other\": \"All filters ({{count}})\",\n \"boolean\": {\n \"no\": \"No\",\n \"yes\": \"Yes\"\n },\n \"placeholders\": {\n \"search\": \"Search {{label}}\",\n \"unresolved\": \"Unresolved ID\"\n },\n \"sections\": {\n \"default\": \"Filters\"\n }\n },\n \"flags\": {\n \"agentManaged\": {\n \"agentManaged\": \"Agent managed\",\n \"userManaged\": \"User managed\"\n },\n \"capability\": {\n \"allowed\": \"Allowed\",\n \"denied\": \"Denied\"\n },\n \"default\": {\n \"default\": \"Default\",\n \"notDefault\": \"Not default\"\n },\n \"deployedProduction\": {\n \"deployed\": \"Deployed\",\n \"notDeployed\": \"Not deployed\"\n },\n \"enabled\": {\n \"disabled\": \"Disabled\",\n \"enabled\": \"Enabled\"\n },\n \"encrypted\": {\n \"encrypted\": \"Encrypted\",\n \"notEncrypted\": \"Not encrypted\"\n },\n \"failure\": {\n \"failed\": \"Failed\",\n \"ok\": \"OK\"\n },\n \"forced\": {\n \"forced\": \"Forced\",\n \"normal\": \"Normal\"\n },\n \"locked\": {\n \"locked\": \"Locked\",\n \"unlocked\": \"Unlocked\"\n }\n },\n \"format\": {\n \"currency\": \"{{value, currency}}\",\n \"number\": \"{{value, number}}\",\n \"percent\": \"{{value, percent}}\"\n },\n \"list\": {\n \"actions\": {\n \"refresh\": \"Refresh\",\n \"retry\": \"Retry\"\n },\n \"errors\": {\n \"tableFailed\": \"The table failed to load.\",\n \"title\": \"Table error\"\n },\n \"loadMore\": {\n \"end\": \"End of results\",\n \"loading\": \"Loading more…\",\n \"more\": \"More results available\"\n },\n \"showing\": \"Showing {{shown, number}} of {{total, number}}\"\n },\n \"picker\": {\n \"errors\": {\n \"loadFailed\": \"Failed to load.\"\n },\n \"searchPlaceholder\": {\n \"default\": \"Search...\"\n },\n \"searchRequired\": \"Enter an ID to search.\",\n \"title\": \"Select an ID\",\n \"unavailable\": \"Picker not available for {{entity}}.\"\n },\n \"relations\": {\n \"labelWithCount_one\": \"{{label}} ({{count}})\",\n \"labelWithCount_other\": \"{{label}} ({{count}})\",\n \"menu\": {\n \"label\": \"Relations\"\n },\n \"viewList\": \"View list\"\n },\n \"sidebar\": {\n \"actions\": {\n \"pin\": \"Pin\",\n \"reorder\": \"Reorder\",\n \"unpin\": \"Unpin\"\n },\n \"items\": {\n \"dashboard\": \"Dashboard\"\n },\n \"profile\": {\n \"actions\": {\n \"signOut\": \"Sign out\"\n },\n \"menuAriaLabel\": \"Open profile menu\",\n \"title\": \"Profile\",\n \"unknownUser\": \"Unknown user\"\n },\n \"search\": {\n \"placeholder\": \"Search…\"\n },\n \"sections\": {\n \"pinned\": \"Pinned\"\n }\n },\n \"tools\": {\n \"docs\": {\n \"inputExampleTitle\": \"Example JSON\",\n \"inputTypeTitle\": \"Input type (GraphQL)\"\n },\n \"errors\": {\n \"description\": \"We couldn't run this tool.\",\n \"details\": \"Error details\",\n \"label\": \"Error\",\n \"missingScope\": \"Missing initiative scope.\",\n \"title\": \"Tool error\",\n \"unknown\": \"Unknown error\"\n },\n \"forms\": {\n \"actions\": {\n \"insertExample\": \"Insert example\",\n \"run\": \"Run\"\n },\n \"inputJsonLabel\": \"Input JSON\"\n },\n \"meta\": {\n \"idLabel\": \"Tool\"\n },\n \"output\": \"Output\",\n \"project\": {\n \"actions\": {\n \"pick\": \"Pick\"\n },\n \"description\": \"Select the project to analyze.\",\n \"empty\": \"Choose a project to run tools.\",\n \"pickerTitle\": \"Select a project\",\n \"placeholder\": \"Project ID\",\n \"title\": \"Project\"\n },\n \"scope\": {\n \"actions\": {\n \"pick\": \"Pick\"\n },\n \"description\": \"Select the initiative scope for tools.\",\n \"empty\": \"Choose an initiative to run tools.\",\n \"pickerTitle\": \"Select an initiative\",\n \"placeholder\": \"Initiative ID\",\n \"title\": \"Scope\"\n }\n }\n}\n","{\n \"actions\": {\n \"form\": {\n \"cancel\": \"Annuler\",\n \"errors\": {\n \"invalidJson\": \"{{label}} doit être un JSON valide.\",\n \"invalidJsonArray\": \"{{label}} doit être un tableau JSON valide.\",\n \"invalidJsonObject\": \"{{label}} doit être un objet JSON valide.\",\n \"invalidNumber\": \"{{label}} doit être un nombre valide.\",\n \"invalidPayload\": \"La charge utile soumise est invalide.\",\n \"required\": \"{{label}} est requis.\"\n }\n },\n \"view\": \"Voir\"\n },\n \"auth\": {\n \"acceptInvitation\": {\n \"errors\": {\n \"alreadyAccepted\": \"Cette invitation a déjà été acceptée.\",\n \"default\": \"Impossible d'accepter cette invitation. Veuillez réessayer.\",\n \"emailMismatch\": \"Cette invitation a été envoyée à une autre adresse email.\",\n \"expired\": \"Cette invitation a expiré.\",\n \"invalidToken\": \"Ce jeton d'invitation est invalide.\",\n \"passwordMismatch\": \"Le mot de passe et sa confirmation ne correspondent pas.\",\n \"passwordPolicyViolation\": \"Votre mot de passe ne respecte pas la politique de sécurité.\",\n \"rateLimited\": \"Trop de tentatives. Veuillez patienter puis réessayer.\"\n }\n },\n \"loginFlow\": {\n \"errors\": {\n \"accountLocked\": \"Votre compte est verrouillé.\",\n \"invalidCredentials\": \"Email ou mot de passe invalide.\",\n \"invalidEmail\": \"Veuillez saisir une adresse email valide.\",\n \"rateLimited\": \"Trop de tentatives. Veuillez patienter puis réessayer.\",\n \"tryAgain\": \"Une erreur est survenue. Veuillez réessayer.\"\n }\n },\n \"mfa\": {\n \"errors\": {\n \"expired\": \"Votre challenge de vérification a expiré.\",\n \"invalidChallenge\": \"Votre challenge de vérification est invalide.\",\n \"invalidCode\": \"Le code de vérification est invalide.\",\n \"tooManyAttempts\": \"Trop de codes invalides. Veuillez réessayer plus tard.\",\n \"verificationFailed\": \"Impossible de vérifier le code. Veuillez réessayer.\"\n }\n },\n \"passkey\": {\n \"errors\": {\n \"challengeExpired\": \"Votre challenge passkey a expiré.\",\n \"failed\": \"L'authentification passkey a échoué. Veuillez réessayer.\",\n \"invalidAssertion\": \"La réponse passkey est invalide.\",\n \"invalidChallenge\": \"Le challenge passkey est invalide.\",\n \"invalidEmail\": \"Veuillez saisir une adresse email valide.\",\n \"notAvailable\": \"Les passkeys ne sont pas disponibles sur cet appareil.\",\n \"notFound\": \"Aucune passkey trouvée pour ce compte.\"\n }\n },\n \"passwordResetComplete\": {\n \"errors\": {\n \"expired\": \"Ce lien de réinitialisation a expiré.\",\n \"invalid\": \"Ce lien de réinitialisation est invalide.\",\n \"policyViolation\": \"Votre nouveau mot de passe ne respecte pas la politique de sécurité.\"\n }\n },\n \"passwordResetRequest\": {\n \"errors\": {\n \"invalidEmail\": \"Veuillez saisir une adresse email valide.\",\n \"rateLimited\": \"Trop de demandes. Veuillez patienter puis réessayer.\",\n \"startFailed\": \"Impossible de démarrer la réinitialisation du mot de passe. Veuillez réessayer.\"\n }\n },\n \"verifyEmail\": {\n \"errors\": {\n \"alreadyVerified\": \"Cette adresse email est déjà vérifiée.\",\n \"expired\": \"Ce lien de vérification a expiré.\",\n \"invalid\": \"Ce lien de vérification est invalide.\"\n }\n }\n },\n \"common\": {\n \"actions\": {\n \"change\": \"Modifier\",\n \"clear\": \"Effacer\",\n \"close\": \"Fermer\",\n \"copied\": \"Copié\",\n \"copy\": \"Copier\",\n \"pick\": \"Choisir\",\n \"retry\": \"Réessayer\"\n },\n \"boolean\": {\n \"no\": \"Non\",\n \"yes\": \"Oui\"\n },\n \"loading\": \"Chargement...\",\n \"notAvailable\": \"N/D\"\n },\n \"dashboard\": {\n \"actions\": {\n \"openList\": \"Ouvrir la liste\",\n \"openTool\": \"Ouvrir l'outil\"\n },\n \"subtitle\": \"Vue d'ensemble du contexte Work.\",\n \"title\": \"Tableau de bord\"\n },\n \"detail\": {\n \"notFound\": \"Introuvable\"\n },\n \"emptyState\": {\n \"listEmpty\": {\n \"description\": \"Aucun enregistrement à afficher.\",\n \"title\": \"Aucun résultat\"\n },\n \"listEmptyFiltered\": {\n \"actions\": {\n \"reset\": \"Réinitialiser les filtres\"\n },\n \"description\": \"Aucun résultat ne correspond aux filtres actuels.\"\n }\n },\n \"filters\": {\n \"actions\": {\n \"filterBy\": \"Filtrer par {{label}}\"\n },\n \"all\": \"Tous {{label}}\",\n \"allFilters\": \"Tous les filtres\",\n \"allFiltersWithCount_one\": \"Tous les filtres ({{count}})\",\n \"allFiltersWithCount_many\": \"Tous les filtres ({{count}})\",\n \"allFiltersWithCount_other\": \"Tous les filtres ({{count}})\",\n \"boolean\": {\n \"no\": \"Non\",\n \"yes\": \"Oui\"\n },\n \"placeholders\": {\n \"search\": \"Rechercher {{label}}\",\n \"unresolved\": \"ID introuvable\"\n },\n \"sections\": {\n \"default\": \"Filtres\"\n }\n },\n \"flags\": {\n \"agentManaged\": {\n \"agentManaged\": \"Géré par un agent\",\n \"userManaged\": \"Géré par un utilisateur\"\n },\n \"capability\": {\n \"allowed\": \"Autorisé\",\n \"denied\": \"Refusé\"\n },\n \"default\": {\n \"default\": \"Par défaut\",\n \"notDefault\": \"Non par défaut\"\n },\n \"deployedProduction\": {\n \"deployed\": \"Déployé\",\n \"notDeployed\": \"Non déployé\"\n },\n \"enabled\": {\n \"disabled\": \"Désactivé\",\n \"enabled\": \"Activé\"\n },\n \"encrypted\": {\n \"encrypted\": \"Chiffré\",\n \"notEncrypted\": \"Non chiffré\"\n },\n \"failure\": {\n \"failed\": \"Échoué\",\n \"ok\": \"OK\"\n },\n \"forced\": {\n \"forced\": \"Forcé\",\n \"normal\": \"Normal\"\n },\n \"locked\": {\n \"locked\": \"Verrouillé\",\n \"unlocked\": \"Déverrouillé\"\n }\n },\n \"format\": {\n \"currency\": \"{{value, currency}}\",\n \"number\": \"{{value, number}}\",\n \"percent\": \"{{value, percent}}\"\n },\n \"list\": {\n \"actions\": {\n \"refresh\": \"Actualiser\",\n \"retry\": \"Réessayer\"\n },\n \"errors\": {\n \"tableFailed\": \"Le tableau n'a pas pu se charger.\",\n \"title\": \"Erreur du tableau\"\n },\n \"loadMore\": {\n \"end\": \"Fin des résultats\",\n \"loading\": \"Chargement…\",\n \"more\": \"Plus de résultats disponibles\"\n },\n \"showing\": \"Affichage de {{shown, number}} sur {{total, number}}\"\n },\n \"picker\": {\n \"errors\": {\n \"loadFailed\": \"Échec du chargement.\"\n },\n \"searchPlaceholder\": {\n \"default\": \"Rechercher...\"\n },\n \"searchRequired\": \"Saisissez un ID pour rechercher.\",\n \"title\": \"Sélectionner un ID\",\n \"unavailable\": \"Sélecteur indisponible pour {{entity}}.\"\n },\n \"relations\": {\n \"labelWithCount_one\": \"{{label}} ({{count}})\",\n \"labelWithCount_many\": \"{{label}} ({{count}})\",\n \"labelWithCount_other\": \"{{label}} ({{count}})\",\n \"menu\": {\n \"label\": \"Relations\"\n },\n \"viewList\": \"Voir la liste\"\n },\n \"sidebar\": {\n \"actions\": {\n \"pin\": \"Épingler\",\n \"reorder\": \"Réordonner\",\n \"unpin\": \"Désépingler\"\n },\n \"items\": {\n \"dashboard\": \"Tableau de bord\"\n },\n \"profile\": {\n \"actions\": {\n \"signOut\": \"Se déconnecter\"\n },\n \"menuAriaLabel\": \"Ouvrir le menu profil\",\n \"title\": \"Profil\",\n \"unknownUser\": \"Utilisateur inconnu\"\n },\n \"search\": {\n \"placeholder\": \"Rechercher...\"\n },\n \"sections\": {\n \"pinned\": \"Épinglés\"\n }\n },\n \"tools\": {\n \"docs\": {\n \"inputExampleTitle\": \"Exemple JSON\",\n \"inputTypeTitle\": \"Type d'entrée (GraphQL)\"\n },\n \"errors\": {\n \"description\": \"Impossible d'exécuter cet outil.\",\n \"details\": \"Détails de l'erreur\",\n \"label\": \"Erreur\",\n \"missingScope\": \"Portée de l'initiative manquante.\",\n \"title\": \"Erreur d'outil\",\n \"unknown\": \"Erreur inconnue\"\n },\n \"forms\": {\n \"actions\": {\n \"insertExample\": \"Insérer l'exemple\",\n \"run\": \"Exécuter\"\n },\n \"inputJsonLabel\": \"JSON d'entrée\"\n },\n \"meta\": {\n \"idLabel\": \"Outil\"\n },\n \"output\": \"Résultat\",\n \"project\": {\n \"actions\": {\n \"pick\": \"Choisir\"\n },\n \"description\": \"Sélectionner le projet à analyser.\",\n \"empty\": \"Choisissez un projet pour exécuter les outils.\",\n \"pickerTitle\": \"Sélectionner un projet\",\n \"placeholder\": \"ID du projet\",\n \"title\": \"Projet\"\n },\n \"scope\": {\n \"actions\": {\n \"pick\": \"Choisir\"\n },\n \"description\": \"Sélectionner l'initiative pour exécuter les outils.\",\n \"empty\": \"Choisissez une initiative pour exécuter les outils.\",\n \"pickerTitle\": \"Sélectionner une initiative\",\n \"placeholder\": \"ID de l'initiative\",\n \"title\": \"Portée\"\n }\n }\n}\n","{\n \"auth\": {\n \"acceptInvitation\": {\n \"actions\": {\n \"backToLogin\": \"Back to login\"\n },\n \"errors\": {\n \"alreadyAccepted\": \"This invitation has already been accepted.\",\n \"default\": \"Unable to accept invitation.\",\n \"emailMismatch\": \"This invitation was sent to a different email address.\",\n \"expired\": \"Invitation link has expired.\",\n \"invalidToken\": \"Invitation link is invalid.\",\n \"missingToken\": \"Invitation link is missing or invalid.\",\n \"passwordMismatch\": \"Password and confirmation do not match.\",\n \"passwordPolicyViolation\": \"Your password does not meet policy requirements.\",\n \"rateLimited\": \"Too many attempts. Please try again later.\"\n },\n \"form\": {\n \"confirmLabel\": \"Confirm password\",\n \"confirmPlaceholder\": \"Confirm your password\",\n \"passwordLabel\": \"Password\",\n \"passwordPlaceholder\": \"Create a password\",\n \"submit\": \"Accept invitation\"\n },\n \"mfaSubtitle\": \"Enter the verification code to continue.\",\n \"mfaTitle\": \"Verify your identity\",\n \"status\": {\n \"success\": \"Invitation accepted.\",\n \"workingButton\": \"Working...\"\n },\n \"subtitle\": \"Create your account to join.\",\n \"title\": \"Accept invitation\"\n },\n \"emailCapture\": {\n \"continue\": \"Continue\",\n \"description\": \"Enter your work email to continue.\",\n \"emailLabel\": \"Work email\",\n \"emailPlaceholder\": \"you@company.com\",\n \"forgotPassword\": \"Forgot your password?\"\n },\n \"loginFlow\": {\n \"errors\": {\n \"accountLocked\": \"Too many attempts. Try again later.\",\n \"emailRequired\": \"Enter an email address to continue.\",\n \"invalidCredentials\": \"Email or password is incorrect.\",\n \"invalidEmail\": \"Enter a valid email address.\",\n \"passkeyUnavailable\": \"Passkeys are not available.\",\n \"rateLimited\": \"Too many attempts. Please try again later.\",\n \"tryAgain\": \"Something went wrong. Please try again.\"\n },\n \"methods\": {\n \"title\": \"Choose a sign-in method\"\n },\n \"passkey\": {\n \"description\": \"Use the passkey associated with {{email}}.\",\n \"title\": \"Use a passkey\"\n },\n \"subtitle\": {\n \"default\": \"Choose a sign-in method to continue.\",\n \"mfa\": \"Enter the verification code to continue.\"\n },\n \"title\": {\n \"default\": \"Sign in\",\n \"mfa\": \"Two-factor authentication\"\n }\n },\n \"methodChooser\": {\n \"actions\": {\n \"back\": \"Back\"\n },\n \"locked\": \"Too many attempts. Try again later.\",\n \"lockedWithTime\": \"Too many attempts. Try again at {{time}}.\",\n \"methods\": {\n \"other\": \"{{method}}\",\n \"passkey\": \"Passkey\",\n \"password\": \"Password\"\n },\n \"prompt\": \"Choose how to sign in for <strong>{{email}}</strong>.\"\n },\n \"mfa\": {\n \"actions\": {\n \"back\": \"Back\",\n \"submit\": \"Verify\"\n },\n \"errors\": {\n \"expired\": \"Verification session expired. Please try again.\",\n \"invalidChallenge\": \"Verification session is invalid. Restart the login.\",\n \"invalidCode\": \"Invalid code. Try again.\",\n \"shortCode\": \"Enter the 6-digit code.\",\n \"tooManyAttempts\": \"Too many attempts. Try again later.\",\n \"verificationFailed\": \"Verification failed. Try again.\"\n },\n \"form\": {\n \"label\": \"Verification code\",\n \"placeholder\": \"123456\"\n },\n \"helper\": {\n \"default\": \"Enter the 6-digit code from your authenticator.\",\n \"withEmail\": \"Enter the 6-digit code sent to {{email}}.\"\n }\n },\n \"oidc\": {\n \"buttons\": {\n \"apple\": \"Continue with Apple\",\n \"generic\": \"Continue with single sign-on\",\n \"google\": \"Continue with Google\"\n }\n },\n \"passkey\": {\n \"actions\": {\n \"showMethods\": \"Use another method\",\n \"submit\": \"Continue with passkey\",\n \"submitting\": \"Waiting for passkey...\"\n },\n \"errors\": {\n \"challengeExpired\": \"Passkey request expired. Try again.\",\n \"emailRequired\": \"Enter your email to continue.\",\n \"failed\": \"Passkey sign-in failed.\",\n \"invalidAssertion\": \"Passkey response is invalid. Try again.\",\n \"invalidChallenge\": \"Passkey request is invalid. Try again.\",\n \"invalidEmail\": \"Enter a valid email address.\",\n \"locked\": \"Too many attempts. Try again later.\",\n \"lockedWithTime\": \"Too many attempts. Try again at {{time}}.\",\n \"notAvailable\": \"Passkeys are not available on this device.\",\n \"notFound\": \"No passkey found for this account.\"\n },\n \"form\": {\n \"emailLabel\": \"Email\",\n \"emailPlaceholder\": \"you@company.com\"\n },\n \"helper\": \"Use a passkey instead of your password.\"\n },\n \"passwordLogin\": {\n \"forgotPassword\": \"Forgot your password?\",\n \"title\": \"Sign in\"\n },\n \"passwordResetComplete\": {\n \"errors\": {\n \"invalid\": \"Reset link is invalid or expired.\",\n \"minLength\": \"Password must be at least {{minLength}} characters.\",\n \"mismatch\": \"Passwords do not match.\",\n \"missingToken\": \"Reset link is missing or invalid.\"\n },\n \"form\": {\n \"confirmLabel\": \"Confirm password\",\n \"confirmPlaceholder\": \"Re-enter your password\",\n \"description\": \"Enter a new password for your account.\",\n \"passwordLabel\": \"Password\",\n \"passwordPlaceholder\": \"Enter a new password\",\n \"submit\": \"Update password\",\n \"title\": \"New password\"\n },\n \"subtitle\": \"Choose a strong password to secure your account.\",\n \"success\": {\n \"action\": \"Back to login\",\n \"description\": \"Your password has been changed.\",\n \"helper\": \"You can now sign in with your new password.\",\n \"title\": \"Password updated\"\n },\n \"title\": \"Set a new password\"\n },\n \"passwordResetRequest\": {\n \"errors\": {\n \"emailRequired\": \"Enter an email address.\",\n \"startFailed\": \"Unable to start password reset.\"\n },\n \"form\": {\n \"description\": \"Enter the email for your account.\",\n \"emailLabel\": \"Email\",\n \"emailPlaceholder\": \"you@company.com\",\n \"submit\": \"Send reset link\"\n },\n \"sent\": {\n \"action\": \"Send another email\",\n \"description\": \"We sent a reset link to {{email}}.\",\n \"helper\": \"If you don't see it, check spam or try again.\",\n \"title\": \"Check your email\"\n },\n \"title\": \"Reset your password\"\n },\n \"verifyEmail\": {\n \"actions\": {\n \"continue\": \"Continue\",\n \"return\": \"Back to login\"\n },\n \"errors\": {\n \"invalid\": \"Verification link is invalid or expired.\",\n \"missingToken\": \"Verification link is missing or invalid.\"\n },\n \"status\": {\n \"success\": \"Email verified. You can continue.\",\n \"verifying\": \"Verifying...\",\n \"verifyingButton\": \"Verifying\"\n },\n \"subtitle\": \"Confirm your email address to continue.\",\n \"title\": \"Verify your email\"\n }\n },\n \"review\": {\n \"status\": {\n \"approved\": \"Approved\",\n \"changesRequested\": \"Changes requested\",\n \"pending\": \"Pending\",\n \"unknown\": \"Unknown\"\n }\n }\n}\n","{\n \"auth\": {\n \"acceptInvitation\": {\n \"actions\": {\n \"backToLogin\": \"Retour à la connexion\"\n },\n \"errors\": {\n \"alreadyAccepted\": \"Cette invitation a déjà été acceptée.\",\n \"default\": \"Impossible d'accepter l'invitation.\",\n \"emailMismatch\": \"Cette invitation a été envoyée à une autre adresse email.\",\n \"expired\": \"Le lien d'invitation a expiré.\",\n \"invalidToken\": \"Le lien d'invitation est invalide.\",\n \"missingToken\": \"Le lien d'invitation est manquant ou invalide.\",\n \"passwordMismatch\": \"Le mot de passe et sa confirmation ne correspondent pas.\",\n \"passwordPolicyViolation\": \"Votre mot de passe ne respecte pas la politique de sécurité.\",\n \"rateLimited\": \"Trop de tentatives. Veuillez réessayer plus tard.\"\n },\n \"form\": {\n \"confirmLabel\": \"Confirmer le mot de passe\",\n \"confirmPlaceholder\": \"Confirmez votre mot de passe\",\n \"passwordLabel\": \"Mot de passe\",\n \"passwordPlaceholder\": \"Créez un mot de passe\",\n \"submit\": \"Accepter l'invitation\"\n },\n \"mfaSubtitle\": \"Entrez le code de vérification pour continuer.\",\n \"mfaTitle\": \"Vérifiez votre identité\",\n \"status\": {\n \"success\": \"Invitation acceptée.\",\n \"workingButton\": \"Traitement...\"\n },\n \"subtitle\": \"Créez votre compte pour rejoindre.\",\n \"title\": \"Accepter l'invitation\"\n },\n \"emailCapture\": {\n \"continue\": \"Continuer\",\n \"description\": \"Entrez votre email professionnel pour continuer.\",\n \"emailLabel\": \"Email professionnel\",\n \"emailPlaceholder\": \"vous@entreprise.com\",\n \"forgotPassword\": \"Mot de passe oublié ?\"\n },\n \"loginFlow\": {\n \"errors\": {\n \"accountLocked\": \"Trop de tentatives. Veuillez réessayer plus tard.\",\n \"emailRequired\": \"Entrez une adresse email pour continuer.\",\n \"invalidCredentials\": \"Email ou mot de passe incorrect.\",\n \"invalidEmail\": \"Entrez une adresse email valide.\",\n \"passkeyUnavailable\": \"Les passkeys ne sont pas disponibles.\",\n \"rateLimited\": \"Trop de tentatives. Veuillez réessayer plus tard.\",\n \"tryAgain\": \"Une erreur est survenue. Veuillez réessayer.\"\n },\n \"methods\": {\n \"title\": \"Choisissez une méthode de connexion\"\n },\n \"passkey\": {\n \"description\": \"Utilisez la passkey associée à {{email}}.\",\n \"title\": \"Utiliser une passkey\"\n },\n \"subtitle\": {\n \"default\": \"Choisissez une méthode de connexion pour continuer.\",\n \"mfa\": \"Entrez le code de vérification pour continuer.\"\n },\n \"title\": {\n \"default\": \"Se connecter\",\n \"mfa\": \"Authentification à deux facteurs\"\n }\n },\n \"methodChooser\": {\n \"actions\": {\n \"back\": \"Retour\"\n },\n \"locked\": \"Trop de tentatives. Veuillez réessayer plus tard.\",\n \"lockedWithTime\": \"Trop de tentatives. Veuillez réessayer à {{time}}.\",\n \"methods\": {\n \"other\": \"{{method}}\",\n \"passkey\": \"Passkey\",\n \"password\": \"Mot de passe\"\n },\n \"prompt\": \"Choisissez comment vous connecter pour <strong>{{email}}</strong>.\"\n },\n \"mfa\": {\n \"actions\": {\n \"back\": \"Retour\",\n \"submit\": \"Vérifier\"\n },\n \"errors\": {\n \"expired\": \"La session de vérification a expiré. Réessayez.\",\n \"invalidChallenge\": \"La session de vérification est invalide. Reprenez la connexion.\",\n \"invalidCode\": \"Code invalide. Réessayez.\",\n \"shortCode\": \"Entrez le code à 6 chiffres.\",\n \"tooManyAttempts\": \"Trop de tentatives. Veuillez réessayer plus tard.\",\n \"verificationFailed\": \"La vérification a échoué. Réessayez.\"\n },\n \"form\": {\n \"label\": \"Code de vérification\",\n \"placeholder\": \"123456\"\n },\n \"helper\": {\n \"default\": \"Entrez le code à 6 chiffres de votre authentificateur.\",\n \"withEmail\": \"Entrez le code à 6 chiffres envoyé à {{email}}.\"\n }\n },\n \"oidc\": {\n \"buttons\": {\n \"apple\": \"Continuer avec Apple\",\n \"generic\": \"Continuer avec un SSO\",\n \"google\": \"Continuer avec Google\"\n }\n },\n \"passkey\": {\n \"actions\": {\n \"showMethods\": \"Utiliser une autre méthode\",\n \"submit\": \"Continuer avec la passkey\",\n \"submitting\": \"En attente de la passkey...\"\n },\n \"errors\": {\n \"challengeExpired\": \"La demande de passkey a expiré. Réessayez.\",\n \"emailRequired\": \"Entrez votre email pour continuer.\",\n \"failed\": \"Échec de la connexion par passkey.\",\n \"invalidAssertion\": \"La réponse de passkey est invalide. Réessayez.\",\n \"invalidChallenge\": \"La demande de passkey est invalide. Réessayez.\",\n \"invalidEmail\": \"Entrez une adresse email valide.\",\n \"locked\": \"Trop de tentatives. Veuillez réessayer plus tard.\",\n \"lockedWithTime\": \"Trop de tentatives. Veuillez réessayer à {{time}}.\",\n \"notAvailable\": \"Les passkeys ne sont pas disponibles sur cet appareil.\",\n \"notFound\": \"Aucune passkey n'est associée à ce compte.\"\n },\n \"form\": {\n \"emailLabel\": \"Email\",\n \"emailPlaceholder\": \"vous@entreprise.com\"\n },\n \"helper\": \"Utilisez une passkey à la place de votre mot de passe.\"\n },\n \"passwordLogin\": {\n \"forgotPassword\": \"Mot de passe oublié ?\",\n \"title\": \"Se connecter\"\n },\n \"passwordResetComplete\": {\n \"errors\": {\n \"invalid\": \"Le lien de réinitialisation est invalide ou expiré.\",\n \"minLength\": \"Le mot de passe doit contenir au moins {{minLength}} caractères.\",\n \"mismatch\": \"Les mots de passe ne correspondent pas.\",\n \"missingToken\": \"Le lien de réinitialisation est manquant ou invalide.\"\n },\n \"form\": {\n \"confirmLabel\": \"Confirmer le mot de passe\",\n \"confirmPlaceholder\": \"Saisissez à nouveau votre mot de passe\",\n \"description\": \"Entrez un nouveau mot de passe pour votre compte.\",\n \"passwordLabel\": \"Mot de passe\",\n \"passwordPlaceholder\": \"Entrez un nouveau mot de passe\",\n \"submit\": \"Mettre à jour le mot de passe\",\n \"title\": \"Nouveau mot de passe\"\n },\n \"subtitle\": \"Choisissez un mot de passe robuste pour sécuriser votre compte.\",\n \"success\": {\n \"action\": \"Retour à la connexion\",\n \"description\": \"Votre mot de passe a été modifié.\",\n \"helper\": \"Vous pouvez maintenant vous connecter avec votre nouveau mot de passe.\",\n \"title\": \"Mot de passe mis à jour\"\n },\n \"title\": \"Définir un nouveau mot de passe\"\n },\n \"passwordResetRequest\": {\n \"errors\": {\n \"emailRequired\": \"Entrez une adresse email.\",\n \"startFailed\": \"Impossible de démarrer la réinitialisation.\"\n },\n \"form\": {\n \"description\": \"Entrez l'email de votre compte.\",\n \"emailLabel\": \"Email\",\n \"emailPlaceholder\": \"vous@entreprise.com\",\n \"submit\": \"Envoyer le lien de réinitialisation\"\n },\n \"sent\": {\n \"action\": \"Envoyer un autre email\",\n \"description\": \"Nous avons envoyé un lien de réinitialisation à {{email}}.\",\n \"helper\": \"Si vous ne le voyez pas, vérifiez les spams ou réessayez.\",\n \"title\": \"Vérifiez votre email\"\n },\n \"title\": \"Réinitialiser votre mot de passe\"\n },\n \"verifyEmail\": {\n \"actions\": {\n \"continue\": \"Continuer\",\n \"return\": \"Retour à la connexion\"\n },\n \"errors\": {\n \"invalid\": \"Le lien de vérification est invalide ou expiré.\",\n \"missingToken\": \"Le lien de vérification est manquant ou invalide.\"\n },\n \"status\": {\n \"success\": \"Email vérifié. Vous pouvez continuer.\",\n \"verifying\": \"Vérification...\",\n \"verifyingButton\": \"Vérification\"\n },\n \"subtitle\": \"Confirmez votre adresse email pour continuer.\",\n \"title\": \"Vérifier votre email\"\n }\n },\n \"review\": {\n \"status\": {\n \"approved\": \"Approuvé\",\n \"changesRequested\": \"Modifications demandées\",\n \"pending\": \"En attente\",\n \"unknown\": \"Inconnu\"\n }\n }\n}\n","import en from './locales/en/backofficeReact.json' with { type: 'json' };\nimport fr from './locales/fr/backofficeReact.json' with { type: 'json' };\nimport sharedEn from './locales/en/shared.json' with { type: 'json' };\nimport sharedFr from './locales/fr/shared.json' with { type: 'json' };\n\nexport const backofficeReactI18nResources = {\n en: { backofficeReact: en, shared: sharedEn },\n fr: { backofficeReact: fr, shared: sharedFr },\n} as const;\n\nexport type BackofficeReactI18nResources = typeof backofficeReactI18nResources;\n","import { type JSX, type ReactNode } from 'react';\nimport * as ReactRelay from 'react-relay';\n\nimport { getEnvironment } from './environment.js';\n\nconst { RelayEnvironmentProvider } = ReactRelay;\n\ntype Props = {\n children: ReactNode;\n};\n\nexport const RelayProvider = ({ children }: Props): JSX.Element => {\n const environment = getEnvironment();\n\n return (\n <RelayEnvironmentProvider environment={environment}>\n {children}\n </RelayEnvironmentProvider>\n );\n};\n\nexport default RelayProvider;\n","/* eslint-disable no-ternary */\nimport {\n getResourcePage,\n HttpRedirect,\n r,\n type AnyRoute,\n type ResourcePage,\n type Route,\n} from '@plumile/router';\nimport * as ReactRelay from 'react-relay';\nimport type { PreloadedQuery } from 'react-relay';\nimport type { Environment, OperationType } from 'relay-runtime';\n\nimport { BACKOFFICE_LIST_DEFAULTS } from '@plumile/backoffice-core/constants.js';\nimport type {\n BackofficeEntityManifestMap,\n BackofficePreparedDetailLayoutRoute,\n BackofficePreparedDetailPageRoute,\n BackofficePreparedListRoute,\n BackofficePreparedToolRoute,\n BackofficeResolvedDetailLayoutFacetConfigBase,\n} from '@plumile/backoffice-core/types.js';\n\nimport type {\n BackofficeAuthConfig,\n BackofficeDashboardModule,\n BackofficeSidebarConfig,\n} from '../provider/types.js';\nimport {\n buildEntityGroupLookup,\n resolveSidebarGroups,\n} from '../components/backoffice/layout/sidebarUtils.js';\nimport type { BackofficeEntityRegistry } from '../provider/entityRegistry.js';\n\nconst { loadQuery, usePreloadedQuery } = ReactRelay;\n\nexport type CreateBackofficeRoutesInput = {\n basePath: string;\n entityManifest: BackofficeEntityManifestMap;\n entityRegistry: BackofficeEntityRegistry;\n sidebar?: BackofficeSidebarConfig;\n auth: BackofficeAuthConfig;\n dashboard?: BackofficeDashboardModule;\n toolsOperationPage?: ResourcePage | null;\n};\n\nexport type BackofficeRouterContext = {\n relayEnvironment: Environment;\n};\n\nexport const WrapperPageResource: ResourcePage | null = getResourcePage(\n 'WrapperPage',\n // eslint-disable-next-line arrow-body-style\n async () => ({\n default: (await import('@plumile/ui')).WrapperPage,\n }),\n);\n\nexport const BackofficeLayoutPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeLayoutPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeLayoutPage.js'),\n );\n\nexport const BackofficeEntityListPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeEntityListPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeEntityListPage.js'),\n );\n\nexport const BackofficeEntityDetailPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeEntityDetailPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeEntityDetailPage.js'),\n );\n\nexport const BackofficeEntityDetailLayoutPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeEntityDetailLayoutPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeEntityDetailLayoutPage.js'),\n );\n\nexport const BackofficeEntityDetailUnknownPageRedirectResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeEntityDetailUnknownPageRedirect',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeEntityDetailUnknownPageRedirect.js'),\n );\n\nexport const BackofficeDashboardPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeDashboardPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeDashboardPage.js'),\n );\n\nexport const BackofficeLoginPageResource: ResourcePage | null = getResourcePage(\n 'BackofficeLoginPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeLoginPage.js'),\n);\n\nexport const BackofficePasswordResetRequestPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficePasswordResetRequestPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficePasswordResetRequestPage.js'),\n );\n\nexport const BackofficePasswordResetCompletePageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficePasswordResetCompletePage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficePasswordResetCompletePage.js'),\n );\n\nexport const BackofficeVerifyEmailPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeVerifyEmailPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeVerifyEmailPage.js'),\n );\n\nexport const BackofficeAcceptInvitationPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeAcceptInvitationPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeAcceptInvitationPage.js'),\n );\n\ntype PreparedDetailUnknownPage = {\n id: string;\n pagePath: string;\n entityManifest: BackofficeEntityManifestMap[string];\n entityConfig: BackofficeResolvedDetailLayoutFacetConfigBase;\n};\n\ntype PreparedLayout = {\n permissionsQuery: PreloadedQuery<OperationType> | null;\n authStatusQuery: PreloadedQuery<OperationType> | null;\n};\n\nconst normalizeBaseSegment = (value: string): string => {\n const trimmed = value.trim();\n if (trimmed === '' || trimmed === '/') {\n return '';\n }\n return trimmed.replace(/^\\/+|\\/+$/g, '');\n};\n\nconst normalizePath = (value: string): string => {\n const trimmed = value.trim();\n if (trimmed === '') {\n return '/';\n }\n return `/${trimmed}`.replace(/\\/+/g, '/');\n};\n\nconst buildScopedPath = (baseSegment: string, path: string): string => {\n const normalizedPath = path.replace(/^\\/+|\\/+$/g, '');\n if (baseSegment === '') {\n return normalizedPath;\n }\n if (normalizedPath === '') {\n return baseSegment;\n }\n return `${baseSegment}/${normalizedPath}`;\n};\n\nconst buildScopedAbsolutePath = (baseSegment: string, path: string): string => {\n return normalizePath(buildScopedPath(baseSegment, path));\n};\n\nconst buildRelativePath = (\n absolutePath: string,\n baseSegment: string,\n): string => {\n const normalized = normalizePath(absolutePath);\n const basePrefix = baseSegment === '' ? '' : `/${baseSegment}`;\n if (basePrefix !== '' && normalized.startsWith(basePrefix)) {\n return normalized.slice(basePrefix.length).replace(/^\\/+/, '');\n }\n return normalized.replace(/^\\/+/, '');\n};\n\nconst resolveActiveEntityIdFromRoute = (\n route: { routes: AnyRoute[] } | null,\n routeEntityIdMap: WeakMap<AnyRoute, string>,\n): string | null => {\n if (route?.routes == null) {\n return null;\n }\n for (let index = route.routes.length - 1; index >= 0; index -= 1) {\n const routeEntry = route.routes[index];\n if (routeEntry != null) {\n const entityId = routeEntityIdMap.get(routeEntry);\n if (entityId != null) {\n return entityId;\n }\n }\n }\n return null;\n};\n\nconst toSearchParams = (query: Record<string, unknown>): URLSearchParams => {\n const params = new URLSearchParams();\n Object.entries(query).forEach(([key, value]) => {\n if (value == null) {\n return;\n }\n if (Array.isArray(value)) {\n value.forEach((entry) => {\n if (entry == null) {\n return;\n }\n params.append(key, String(entry));\n });\n return;\n }\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n params.set(key, String(value));\n });\n return params;\n};\n\nconst rBackoffice = r<BackofficeRouterContext>;\n\n/**\n * Creates backoffice routes based on the provided configuration\n */\nexport function createBackofficeRoutes(\n input: CreateBackofficeRoutesInput,\n): Route<BackofficeRouterContext, any>[] {\n const { basePath, entityManifest, entityRegistry, sidebar, auth, dashboard } =\n input;\n const baseSegment = normalizeBaseSegment(basePath);\n const loginPath = buildScopedPath(baseSegment, 'login');\n const passwordResetRequestPath = buildScopedPath(baseSegment, 'login/reset');\n const passwordResetCompletePath = buildScopedPath(\n baseSegment,\n 'login/reset/complete',\n );\n const verifyEmailPath = buildScopedPath(baseSegment, 'verify-email');\n const acceptInvitationPath = buildScopedPath(\n baseSegment,\n 'accept-invitation',\n );\n const loginRedirectPath = buildScopedAbsolutePath(baseSegment, 'login');\n const entities = entityManifest;\n const groups = resolveSidebarGroups(entities, sidebar);\n const entityGroupLookup = buildEntityGroupLookup(groups);\n const entityIdToGroupId = new Map<string, string>();\n entityGroupLookup.forEach((value, entityId) => {\n entityIdToGroupId.set(entityId, value.groupId);\n });\n const routeEntityIdMap = new WeakMap<AnyRoute, string>();\n\n const permissionsQuery = sidebar?.permissionsQuery;\n const layoutPrepare = async ({\n context,\n }: {\n context: BackofficeRouterContext;\n }) => {\n const sessionAuth = await auth.session.load();\n const permissionsQueryRef =\n permissionsQuery != null\n ? loadQuery<OperationType>(\n context.relayEnvironment,\n permissionsQuery,\n {},\n )\n : null;\n const authStatusQueryRef =\n sessionAuth.authStatusQuery != null\n ? loadQuery<OperationType>(\n context.relayEnvironment,\n sessionAuth.authStatusQuery,\n {},\n { fetchPolicy: 'network-only' },\n )\n : null;\n return {\n permissionsQuery: permissionsQueryRef,\n authStatusQuery: authStatusQueryRef,\n };\n };\n\n const dashboardRoute = rBackoffice({\n path: '',\n resourcePage: BackofficeDashboardPageResource,\n prepare: async () => {\n if (dashboard == null) {\n return null;\n }\n await dashboard.load();\n return null;\n },\n render: ({ Component }) => {\n if (Component == null) {\n return null;\n }\n return <Component />;\n },\n });\n\n const listEntityEntries = Object.values(entityManifest).filter((entity) => {\n return entity.kind === 'list-detail';\n });\n\n const entityRoutes = listEntityEntries.map((entityManifestItem) => {\n const listPath = entityManifestItem.routes.list;\n const listRelative = buildRelativePath(listPath, baseSegment);\n const children: Route<any, any>[] = [];\n\n if (entityManifestItem.hasList) {\n const listRoute = rBackoffice({\n path: '',\n resourcePage: BackofficeEntityListPageResource,\n prepare: async ({ context, query }) => {\n const entityModule = await entityRegistry.loadListEntity(\n entityManifestItem.id,\n );\n const { config } = entityModule;\n const { list, listUrlCodec, listDefaults } = config;\n if (listUrlCodec == null || listDefaults == null) {\n throw new Error(\n `Backoffice entity ${entityManifestItem.id} does not expose a list configuration.`,\n );\n }\n const params = toSearchParams(query);\n const state = listUrlCodec.parse(params);\n const { pageSize } = BACKOFFICE_LIST_DEFAULTS;\n const variablesBase = {\n where: state.where,\n sort: state.sort ?? listDefaults.sort,\n count: pageSize,\n cursor: null,\n };\n const variables =\n list.buildVariables != null\n ? list.buildVariables(variablesBase)\n : variablesBase;\n const queryRef = loadQuery<OperationType>(\n context.relayEnvironment,\n list.query,\n variables,\n );\n const prepared: BackofficePreparedListRoute = {\n entityId: entityManifestItem.id,\n entityManifest: entityManifestItem,\n entityConfig: config,\n query: queryRef,\n };\n return prepared;\n },\n render: ({ prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedRoute = prepared as BackofficePreparedListRoute;\n return (\n <Component\n entityManifest={preparedRoute.entityManifest}\n config={preparedRoute.entityConfig}\n prepared={preparedRoute}\n />\n );\n },\n });\n routeEntityIdMap.set(listRoute, entityManifestItem.id);\n children.push(listRoute);\n }\n\n const detailLayoutRoute = rBackoffice({\n path: ':id',\n resourcePage: BackofficeEntityDetailLayoutPageResource,\n prepare: async ({ context, variables }) => {\n const entityModule = await entityRegistry.loadDetailLayoutEntity(\n entityManifestItem.id,\n );\n const { config } = entityModule;\n const rawId = String(variables.id ?? '');\n const layoutBuildResult =\n config.layoutPage.buildVariables != null\n ? config.layoutPage.buildVariables({\n id: rawId,\n })\n : { variables: { id: rawId } };\n const layoutQueryRef = loadQuery<OperationType>(\n context.relayEnvironment,\n config.layoutPage.query,\n layoutBuildResult.variables as never,\n );\n return {\n entityId: entityManifestItem.id,\n entityManifest: entityManifestItem,\n entityConfig: config,\n layoutQuery: layoutQueryRef,\n id: rawId,\n } satisfies BackofficePreparedDetailLayoutRoute;\n },\n render: ({ children: detailChildren, prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedRoute = prepared as BackofficePreparedDetailLayoutRoute;\n return (\n <Component\n entityManifest={preparedRoute.entityManifest}\n config={preparedRoute.entityConfig}\n prepared={preparedRoute}\n >\n {detailChildren}\n </Component>\n );\n },\n children: [\n rBackoffice({\n path: '',\n resourcePage: WrapperPageResource,\n prepare: ({ variables }) => {\n const rawId = String(variables.id ?? '').trim();\n return {\n redirectTo:\n rawId === ''\n ? null\n : entityManifestItem.routes.detailPage(\n rawId,\n entityManifestItem.defaultDetailPageId ?? 'overview',\n ),\n };\n },\n render: ({ prepared }) => {\n const redirectTo =\n (prepared as { redirectTo?: string | null }).redirectTo ?? null;\n if (redirectTo == null) {\n return null;\n }\n throw new HttpRedirect(redirectTo);\n },\n }),\n ...(entityManifestItem.detailPages ?? []).map((pageManifest) => {\n return rBackoffice({\n path: pageManifest.pathSegment,\n resourcePage: BackofficeEntityDetailPageResource,\n prepare: async ({ context, variables }) => {\n const rawId = String(variables.id ?? '');\n const pageModule = await entityRegistry.loadDetailPageEntity(\n entityManifestItem.id,\n pageManifest.id,\n );\n const pageBuildResult =\n pageModule.config.page.buildVariables != null\n ? pageModule.config.page.buildVariables({\n id: rawId,\n })\n : { variables: { id: rawId } };\n const pageQueryRef = loadQuery<OperationType>(\n context.relayEnvironment,\n pageModule.config.page.query,\n pageBuildResult.variables as never,\n );\n return {\n entityId: entityManifestItem.id,\n entityManifest: entityManifestItem,\n entityConfig:\n pageModule.config as unknown as BackofficeResolvedDetailLayoutFacetConfigBase,\n detailId: pageBuildResult.detailId,\n id: rawId,\n pageConfig: pageModule.config,\n pageQuery: pageQueryRef,\n pageId: pageManifest.id,\n pagePath: pageManifest.pathSegment,\n } satisfies BackofficePreparedDetailPageRoute;\n },\n render: ({ prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedRoute =\n prepared as BackofficePreparedDetailPageRoute;\n return (\n <Component\n entityManifest={preparedRoute.entityManifest}\n config={preparedRoute.entityConfig}\n prepared={preparedRoute}\n />\n );\n },\n });\n }),\n rBackoffice({\n path: ':pagePath',\n resourcePage: BackofficeEntityDetailUnknownPageRedirectResource,\n prepare: async ({ variables }) => {\n const entityModule = await entityRegistry.loadDetailLayoutEntity(\n entityManifestItem.id,\n );\n const rawId = String(variables.id ?? '');\n const rawPagePath = String(variables.pagePath ?? '');\n return {\n entityManifest: entityManifestItem,\n entityConfig: entityModule.config,\n id: rawId,\n pagePath: rawPagePath,\n };\n },\n render: ({ prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedDetail = prepared as PreparedDetailUnknownPage;\n return (\n <Component\n entityManifest={preparedDetail.entityManifest}\n config={preparedDetail.entityConfig}\n prepared={preparedDetail}\n />\n );\n },\n }),\n ],\n });\n routeEntityIdMap.set(detailLayoutRoute, entityManifestItem.id);\n children.push(detailLayoutRoute);\n\n return rBackoffice({\n path: listRelative,\n children,\n resourcePage: WrapperPageResource,\n });\n });\n\n const toolsRoutes = Object.values(entityManifest)\n .filter((entity) => {\n return entity.kind === 'tool';\n })\n .map((toolManifest) => {\n const toolRelative = buildRelativePath(\n toolManifest.routes.list,\n baseSegment,\n );\n const toolRoute = rBackoffice({\n path: toolRelative,\n resourcePage: input.toolsOperationPage ?? null,\n prepare: async () => {\n const toolModule = await entityRegistry.loadToolEntity(\n toolManifest.id,\n );\n return {\n entityId: toolManifest.id,\n entityManifest: toolManifest,\n entityConfig: toolModule.config,\n } satisfies BackofficePreparedToolRoute;\n },\n render: ({ prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedTool = prepared as BackofficePreparedToolRoute;\n return (\n <Component\n entityManifest={preparedTool.entityManifest}\n operation={preparedTool.entityConfig.tool.operation}\n toolId={preparedTool.entityConfig.id}\n />\n );\n },\n });\n routeEntityIdMap.set(toolRoute, toolManifest.id);\n return toolRoute;\n });\n\n const layoutRoute = rBackoffice({\n path: baseSegment,\n resourcePage: BackofficeLayoutPageResource,\n prepare: layoutPrepare,\n render: ({ children, prepared, route, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedLayout = prepared as PreparedLayout | undefined;\n const activeEntityId = resolveActiveEntityIdFromRoute(\n route,\n routeEntityIdMap,\n );\n const activeGroupId =\n activeEntityId != null\n ? (entityIdToGroupId.get(activeEntityId) ?? null)\n : null;\n let authStatus: {\n isLoggedIn?: boolean | null;\n me?: {\n id: string;\n firstName: string;\n lastName: string;\n email: string;\n initials: string;\n } | null;\n } | null = null;\n const authStatusQuery = auth.session.get()?.authStatusQuery ?? null;\n if (authStatusQuery != null && preparedLayout?.authStatusQuery != null) {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const data = usePreloadedQuery(\n authStatusQuery,\n preparedLayout.authStatusQuery,\n );\n authStatus = data as {\n isLoggedIn?: boolean | null;\n me?: {\n id: string;\n firstName: string;\n lastName: string;\n email: string;\n initials: string;\n } | null;\n };\n const { isLoggedIn } = authStatus;\n if (!isLoggedIn) {\n throw new HttpRedirect(loginRedirectPath);\n }\n }\n const layoutNode = (\n <Component\n permissionsQuery={sidebar?.permissionsQuery}\n prepared={preparedLayout?.permissionsQuery ?? null}\n authStatus={authStatus}\n activeGroupId={activeGroupId}\n >\n {children}\n </Component>\n );\n return layoutNode;\n },\n children: [dashboardRoute, ...entityRoutes, ...toolsRoutes],\n });\n\n const routes: Route<BackofficeRouterContext, any>[] = [\n rBackoffice({\n path: loginPath,\n resourcePage: BackofficeLoginPageResource,\n prepare: async ({ context }) => {\n const loginAuth = await auth.login.load();\n const queryRef = loadQuery<OperationType>(\n context.relayEnvironment,\n loginAuth.loginQuery,\n {},\n );\n return { query: queryRef };\n },\n render: ({ prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n return (\n <Component\n prepared={prepared as { query: PreloadedQuery<OperationType> }}\n />\n );\n },\n }),\n rBackoffice({\n path: passwordResetRequestPath,\n resourcePage: BackofficePasswordResetRequestPageResource,\n prepare: async () => {\n await auth.passwordResetRequest.load();\n return null;\n },\n render: ({ Component }) => {\n if (Component == null) {\n return null;\n }\n return <Component />;\n },\n }),\n rBackoffice({\n path: passwordResetCompletePath,\n resourcePage: BackofficePasswordResetCompletePageResource,\n prepare: async () => {\n await auth.passwordResetComplete.load();\n return null;\n },\n render: ({ Component }) => {\n if (Component == null) {\n return null;\n }\n return <Component />;\n },\n }),\n rBackoffice({\n path: verifyEmailPath,\n resourcePage: BackofficeVerifyEmailPageResource,\n prepare: async () => {\n await auth.verifyEmail.load();\n return null;\n },\n render: ({ Component }) => {\n if (Component == null) {\n return null;\n }\n return <Component />;\n },\n }),\n ];\n\n if (auth.hasAcceptInvitation === true || auth.acceptInvitation != null) {\n routes.push(\n rBackoffice({\n path: acceptInvitationPath,\n resourcePage: BackofficeAcceptInvitationPageResource,\n prepare: async () => {\n await auth.acceptInvitation?.load();\n return null;\n },\n render: ({ Component }) => {\n if (Component == null) {\n return null;\n }\n return <Component />;\n },\n }),\n );\n }\n\n routes.push(layoutRoute);\n\n return routes;\n}\n\nexport default createBackofficeRoutes;\n","import { sprinkles } from '@plumile/ui/theme/sprinkles.css.js';\n\nexport const root = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: 'screen',\n width: 'full',\n gap: 4,\n backgroundColor: 'surfaceSecondary',\n color: 'textSecondary',\n});\n\nexport const label = sprinkles({\n color: 'textMuted',\n});\n","import { type JSX } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport { Spinner } from '@plumile/ui';\n\nimport * as styles from './backofficeRouteFallback.css.js';\n\nexport const BackofficeRouteFallback = (): JSX.Element => {\n const { t, i18n } = useTranslation('backofficeReact', {\n useSuspense: false,\n });\n let label = 'Loading...';\n if (i18n.isInitialized) {\n label = t('common.loading');\n }\n\n return (\n <div\n className={styles.root}\n role=\"status\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n >\n <Spinner size={28} />\n <div className={styles.label}>{label}</div>\n </div>\n );\n};\n\nexport default BackofficeRouteFallback;\n","import { keyframes, style } from '@vanilla-extract/css';\n\nimport { sprinkles } from '@plumile/ui/theme/sprinkles.css.js';\nimport { vars } from '@plumile/ui/theme/themeContract.js';\n\nconst slide = keyframes({\n '0%': { transform: 'translateX(-120%)' },\n '60%': { transform: 'translateX(30%)' },\n '100%': { transform: 'translateX(120%)' },\n});\n\nexport const root = style([\n sprinkles({\n backgroundColor: 'surfaceMuted',\n position: 'fixed',\n top: 0,\n left: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n width: 'full',\n zIndex: 50,\n }),\n {\n height: '3px',\n },\n]);\n\nexport const bar = style([\n sprinkles({\n height: 'full',\n width: '2/5',\n }),\n {\n background: `linear-gradient(90deg, ${vars.colors.primaryLight} 0%, ${vars.colors.primary} 60%, ${vars.colors.primaryLight} 100%)`,\n animation: `${slide} 1.1s ease-in-out infinite`,\n },\n]);\n","import { type JSX } from 'react';\n\nimport * as styles from './backofficeRoutePendingBar.css.js';\n\nexport const BackofficeRoutePendingBar = (): JSX.Element => {\n return (\n <div className={styles.root} aria-hidden=\"true\">\n <div className={styles.bar} />\n </div>\n );\n};\n\nexport default BackofficeRoutePendingBar;\n","import {\n resolveBackofficeLoadedFacetModule,\n type ResolveBackofficeEntityOptions,\n} from '@plumile/backoffice-core/resolve.js';\nimport type {\n BackofficeEntityFacetModuleCacheEntry,\n BackofficeEntityLoadMode,\n BackofficeEntityManifestMap,\n BackofficeListDetailFacetLoaderMap,\n BackofficeToolFacetLoaderMap,\n BackofficeResolvedDetailLayoutFacetModule,\n BackofficeResolvedDetailPageFacetModule,\n BackofficeResolvedListFacetModule,\n BackofficeResolvedPickerFacetModule,\n BackofficeResolvedToolFacetModule,\n} from '@plumile/backoffice-core/types.js';\n\nexport type BackofficeEntityRegistry = {\n getManifest: (entityId: string) => BackofficeEntityManifestMap[string] | null;\n getLoadedListEntity: (\n entityId: string,\n ) => BackofficeResolvedListFacetModule | null;\n getLoadedPickerEntity: (\n entityId: string,\n ) => BackofficeResolvedPickerFacetModule | null;\n getLoadedDetailLayoutEntity: (\n entityId: string,\n ) => BackofficeResolvedDetailLayoutFacetModule | null;\n getLoadedDetailPageEntity: (\n entityId: string,\n pageId: string,\n ) => BackofficeResolvedDetailPageFacetModule | null;\n getLoadedToolEntity: (\n entityId: string,\n ) => BackofficeResolvedToolFacetModule | null;\n loadListEntity: (\n entityId: string,\n options?: { mode?: BackofficeEntityLoadMode },\n ) => Promise<BackofficeResolvedListFacetModule>;\n loadPickerEntity: (\n entityId: string,\n options?: { mode?: BackofficeEntityLoadMode },\n ) => Promise<BackofficeResolvedPickerFacetModule>;\n loadDetailLayoutEntity: (\n entityId: string,\n options?: { mode?: BackofficeEntityLoadMode },\n ) => Promise<BackofficeResolvedDetailLayoutFacetModule>;\n loadDetailPageEntity: (\n entityId: string,\n pageId: string,\n options?: { mode?: BackofficeEntityLoadMode },\n ) => Promise<BackofficeResolvedDetailPageFacetModule>;\n loadToolEntity: (\n entityId: string,\n options?: { mode?: BackofficeEntityLoadMode },\n ) => Promise<BackofficeResolvedToolFacetModule>;\n};\n\nconst DEFAULT_LOAD_MODE: BackofficeEntityLoadMode = 'cache-first';\nconst DETAIL_PAGE_FACET_KIND = 'detail-page';\nconst DETAIL_LAYOUT_FACET_KIND = 'detail-layout';\nconst LIST_FACET_KIND = 'list';\nconst PICKER_FACET_KIND = 'picker';\nconst TOOL_FACET_KIND = 'tool';\n\nconst hasListFacets = (\n manifestItem: BackofficeEntityManifestMap[string],\n): manifestItem is BackofficeEntityManifestMap[string] & {\n facets: BackofficeListDetailFacetLoaderMap;\n} => {\n return manifestItem.kind === 'list-detail';\n};\n\nconst hasToolFacet = (\n manifestItem: BackofficeEntityManifestMap[string],\n): manifestItem is BackofficeEntityManifestMap[string] & {\n facets: BackofficeToolFacetLoaderMap;\n} => {\n return manifestItem.kind === 'tool';\n};\n\nconst buildFacetCacheKey = (\n entityId: string,\n kind: 'list' | 'picker' | 'detail-layout' | 'detail-page' | 'tool',\n pageId?: string,\n): string => {\n if (kind === DETAIL_PAGE_FACET_KIND) {\n return `${entityId}:${kind}:${pageId ?? ''}`;\n }\n return `${entityId}:${kind}`;\n};\n\nconst toError = (error: unknown): Error => {\n if (error instanceof Error) {\n return error;\n }\n return new Error(String(error));\n};\n\nconst getLoadedModule = <TModule>(\n entry: BackofficeEntityFacetModuleCacheEntry | undefined,\n): TModule | null => {\n if (entry?.status !== 'loaded' || entry.module == null) {\n return null;\n }\n return entry.module as TModule;\n};\n\nconst resolveManifest = (\n manifest: BackofficeEntityManifestMap,\n entityId: string,\n): BackofficeEntityManifestMap[string] => {\n const manifestItem = manifest[entityId];\n if (manifestItem == null) {\n throw new Error(`Unknown backoffice entity: ${entityId}`);\n }\n return manifestItem;\n};\n\nexport const createBackofficeEntityRegistry = (\n manifest: BackofficeEntityManifestMap,\n options: ResolveBackofficeEntityOptions,\n): BackofficeEntityRegistry => {\n const cache = new Map<string, BackofficeEntityFacetModuleCacheEntry>();\n\n const getManifest: BackofficeEntityRegistry['getManifest'] = (entityId) => {\n return manifest[entityId] ?? null;\n };\n\n const loadFacet = async <TModule>(\n cacheKey: string,\n loadOptions: { mode?: BackofficeEntityLoadMode } | undefined,\n loader: () => Promise<TModule>,\n ): Promise<TModule> => {\n const resolvedMode = loadOptions?.mode ?? DEFAULT_LOAD_MODE;\n\n if (resolvedMode === 'cache-first') {\n const loadedModule = getLoadedModule<TModule>(cache.get(cacheKey));\n if (loadedModule != null) {\n return loadedModule;\n }\n\n const existing = cache.get(cacheKey);\n if (existing?.status === 'loading' && existing.promise != null) {\n return existing.promise as Promise<TModule>;\n }\n }\n\n const promise = loader()\n .then((module) => {\n cache.set(cacheKey, {\n status: 'loaded',\n module: module as never,\n });\n return module;\n })\n .catch((error: unknown) => {\n const resolvedError = toError(error);\n cache.set(cacheKey, {\n status: 'error',\n error: resolvedError,\n });\n throw resolvedError;\n });\n\n cache.set(cacheKey, {\n status: 'loading',\n promise: promise as never,\n });\n\n return promise;\n };\n\n const getLoadedListEntity: BackofficeEntityRegistry['getLoadedListEntity'] = (\n entityId,\n ) => {\n return getLoadedModule(\n cache.get(buildFacetCacheKey(entityId, LIST_FACET_KIND)),\n );\n };\n\n const getLoadedPickerEntity: BackofficeEntityRegistry['getLoadedPickerEntity'] =\n (entityId) => {\n return getLoadedModule(\n cache.get(buildFacetCacheKey(entityId, PICKER_FACET_KIND)),\n );\n };\n\n const getLoadedDetailLayoutEntity: BackofficeEntityRegistry['getLoadedDetailLayoutEntity'] =\n (entityId) => {\n return getLoadedModule(\n cache.get(buildFacetCacheKey(entityId, DETAIL_LAYOUT_FACET_KIND)),\n );\n };\n\n const getLoadedDetailPageEntity: BackofficeEntityRegistry['getLoadedDetailPageEntity'] =\n (entityId, pageId) => {\n return getLoadedModule(\n cache.get(buildFacetCacheKey(entityId, DETAIL_PAGE_FACET_KIND, pageId)),\n );\n };\n\n const getLoadedToolEntity: BackofficeEntityRegistry['getLoadedToolEntity'] = (\n entityId,\n ) => {\n return getLoadedModule(\n cache.get(buildFacetCacheKey(entityId, TOOL_FACET_KIND)),\n );\n };\n\n const loadListEntity: BackofficeEntityRegistry['loadListEntity'] = async (\n entityId,\n loadOptions,\n ) => {\n const manifestItem = resolveManifest(manifest, entityId);\n if (!hasListFacets(manifestItem)) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a list facet.`,\n );\n }\n const facetLoader = manifestItem.facets.list;\n if (facetLoader == null) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a list facet.`,\n );\n }\n return loadFacet(\n buildFacetCacheKey(entityId, LIST_FACET_KIND),\n loadOptions,\n async () => {\n const loaded = await facetLoader();\n const resolved = resolveBackofficeLoadedFacetModule(\n manifestItem,\n loaded,\n options,\n );\n if (resolved.kind !== LIST_FACET_KIND) {\n throw new Error(\n `Backoffice entity ${entityId} did not resolve to a list facet.`,\n );\n }\n return resolved;\n },\n );\n };\n\n const loadPickerEntity: BackofficeEntityRegistry['loadPickerEntity'] = async (\n entityId,\n loadOptions,\n ) => {\n const manifestItem = resolveManifest(manifest, entityId);\n if (!hasListFacets(manifestItem)) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a picker facet.`,\n );\n }\n const facetLoader = manifestItem.facets.picker;\n if (facetLoader == null) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a picker facet.`,\n );\n }\n return loadFacet(\n buildFacetCacheKey(entityId, PICKER_FACET_KIND),\n loadOptions,\n async () => {\n const loaded = await facetLoader();\n const resolved = resolveBackofficeLoadedFacetModule(\n manifestItem,\n loaded,\n options,\n );\n if (resolved.kind !== PICKER_FACET_KIND) {\n throw new Error(\n `Backoffice entity ${entityId} did not resolve to a picker facet.`,\n );\n }\n return resolved;\n },\n );\n };\n\n const loadDetailLayoutEntity: BackofficeEntityRegistry['loadDetailLayoutEntity'] =\n async (entityId, loadOptions) => {\n const manifestItem = resolveManifest(manifest, entityId);\n if (!hasListFacets(manifestItem)) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a detail-layout facet.`,\n );\n }\n return loadFacet(\n buildFacetCacheKey(entityId, DETAIL_LAYOUT_FACET_KIND),\n loadOptions,\n async () => {\n const loaded = await manifestItem.facets.detailLayout();\n const resolved = resolveBackofficeLoadedFacetModule(\n manifestItem,\n loaded,\n options,\n );\n if (resolved.kind !== DETAIL_LAYOUT_FACET_KIND) {\n throw new Error(\n `Backoffice entity ${entityId} did not resolve to a detail-layout facet.`,\n );\n }\n return resolved;\n },\n );\n };\n\n const loadDetailPageEntity: BackofficeEntityRegistry['loadDetailPageEntity'] =\n async (entityId, pageId, loadOptions) => {\n const manifestItem = resolveManifest(manifest, entityId);\n if (!hasListFacets(manifestItem)) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a detail-page facet.`,\n );\n }\n return loadFacet(\n buildFacetCacheKey(entityId, DETAIL_PAGE_FACET_KIND, pageId),\n loadOptions,\n async () => {\n const loaded = await manifestItem.facets.detailPage(pageId);\n const resolved = resolveBackofficeLoadedFacetModule(\n manifestItem,\n loaded,\n options,\n );\n if (resolved.kind !== DETAIL_PAGE_FACET_KIND) {\n throw new Error(\n `Backoffice entity ${entityId} did not resolve to a detail-page facet.`,\n );\n }\n return resolved;\n },\n );\n };\n\n const loadToolEntity: BackofficeEntityRegistry['loadToolEntity'] = async (\n entityId,\n loadOptions,\n ) => {\n const manifestItem = resolveManifest(manifest, entityId);\n if (!hasToolFacet(manifestItem)) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a tool facet.`,\n );\n }\n return loadFacet(\n buildFacetCacheKey(entityId, TOOL_FACET_KIND),\n loadOptions,\n async () => {\n const loaded = await manifestItem.facets.tool();\n const resolved = resolveBackofficeLoadedFacetModule(\n manifestItem,\n loaded,\n options,\n );\n if (resolved.kind !== TOOL_FACET_KIND) {\n throw new Error(\n `Backoffice entity ${entityId} did not resolve to a tool facet.`,\n );\n }\n return resolved;\n },\n );\n };\n\n return {\n getManifest,\n getLoadedListEntity,\n getLoadedPickerEntity,\n getLoadedDetailLayoutEntity,\n getLoadedDetailPageEntity,\n getLoadedToolEntity,\n loadListEntity,\n loadPickerEntity,\n loadDetailLayoutEntity,\n loadDetailPageEntity,\n loadToolEntity,\n };\n};\n\nexport default createBackofficeEntityRegistry;\n","import { StrictMode, useEffect, useMemo, type JSX } from 'react';\nimport {\n createInstance,\n type Resource,\n type ResourceKey,\n type ResourceLanguage,\n} from 'i18next';\nimport { I18nextProvider } from 'react-i18next';\nimport { createRouter, RouterRenderer, RoutingContext } from '@plumile/router';\nimport { ThemeProvider } from '@plumile/ui';\n\nimport { type BackofficeEntityManifestMap } from '@plumile/backoffice-core/types.js';\n\nimport { createI18nInstance } from '../i18n/createI18nInstance.js';\nimport { mergeResourceLanguages } from '../i18n/mergeResourceLanguages.js';\nimport { backofficeReactI18nResources } from '../i18n/resources.js';\nimport { RelayProvider } from '../relay/RelayProvider.js';\nimport { useRelayEnvironment } from '../relay/useRelayEnvironment.js';\nimport { configureRelayEnvironment } from '../relay/environment.js';\nimport { BackofficeConfigProvider } from './BackofficeConfigContext.js';\nimport { createBackofficeRoutes } from '../router/createBackofficeRoutes.js';\nimport type { BackofficeProviderProps } from './types.js';\nimport { BackofficeRouteFallback } from '../components/backoffice/routing/BackofficeRouteFallback.js';\nimport { BackofficeRoutePendingBar } from '../components/backoffice/routing/BackofficeRoutePendingBar.js';\nimport { createBackofficeEntityRegistry } from './entityRegistry.js';\n\nconst normalizeAbsolutePath = (value: string): string => {\n if (value.trim() === '' || value === '/') {\n return '/';\n }\n if (!value.startsWith('/')) {\n return `/${value}`;\n }\n if (value.endsWith('/')) {\n return value.slice(0, -1);\n }\n return value;\n};\n\nconst prefixRoutePath = (basePath: string, value: string): string => {\n const normalizedPath = normalizeAbsolutePath(value);\n const normalizedBasePath = normalizeAbsolutePath(basePath);\n if (normalizedBasePath === '/') {\n return normalizedPath;\n }\n if (\n normalizedPath === normalizedBasePath ||\n normalizedPath.startsWith(`${normalizedBasePath}/`)\n ) {\n return normalizedPath;\n }\n if (normalizedPath === '/') {\n return normalizedBasePath;\n }\n return `${normalizedBasePath}${normalizedPath}`;\n};\n\nconst resolveEntityManifest = (\n manifest: BackofficeEntityManifestMap,\n basePath: string,\n): BackofficeEntityManifestMap => {\n return Object.fromEntries(\n Object.entries(manifest).map(([entityId, item]) => {\n return [\n entityId,\n {\n ...item,\n routes: {\n list: prefixRoutePath(basePath, item.routes.list),\n detail: (id: string) => {\n return prefixRoutePath(basePath, item.routes.detail(id));\n },\n detailPage: (id: string, pageId: string) => {\n return prefixRoutePath(\n basePath,\n item.routes.detailPage(id, pageId),\n );\n },\n },\n } satisfies BackofficeEntityManifestMap[string],\n ] as const;\n }),\n );\n};\n\nconst asResourceLanguage = (\n value: ResourceKey | undefined,\n): ResourceLanguage => {\n if (typeof value === 'object' && !Array.isArray(value)) {\n return value as ResourceLanguage;\n }\n return {};\n};\n\ntype RouterShellProps = {\n routes: ReturnType<typeof createBackofficeRoutes>;\n instrumentations?: BackofficeProviderProps['instrumentations'];\n};\n\nconst RouterShell = ({\n routes,\n instrumentations,\n}: RouterShellProps): JSX.Element => {\n const relayEnvironment = useRelayEnvironment();\n\n const routerContext = useMemo(() => {\n return { relayEnvironment };\n }, [relayEnvironment]);\n\n const router = useMemo(() => {\n return createRouter(routes, {\n context: routerContext,\n instrumentations,\n });\n }, [instrumentations, routes, routerContext]);\n\n useEffect(() => {\n return () => {\n router.cleanup();\n };\n }, [router]);\n\n return (\n <RoutingContext.Provider value={router.context}>\n <RouterRenderer\n enableTransition\n fallback={<BackofficeRouteFallback />}\n pending={<BackofficeRoutePendingBar />}\n />\n </RoutingContext.Provider>\n );\n};\n\nexport const BackofficeProvider = (\n props: BackofficeProviderProps,\n): JSX.Element => {\n const basePath = normalizeAbsolutePath(props.basePath ?? '/');\n\n const entityManifest = useMemo(() => {\n return resolveEntityManifest(props.entityManifest, basePath);\n }, [basePath, props.entityManifest]);\n const entityRegistry = useMemo(() => {\n return createBackofficeEntityRegistry(entityManifest, { basePath });\n }, [basePath, entityManifest]);\n\n const graphQLConfig = props.graphql;\n\n useEffect(() => {\n const httpUrl = graphQLConfig.httpUrl ?? graphQLConfig.endpoint;\n const wsUrl = graphQLConfig.wsUrl ?? graphQLConfig.wsEndpoint;\n configureRelayEnvironment({\n httpUrl,\n wsUrl,\n getDataId: graphQLConfig.getDataId,\n logEvents: graphQLConfig.logEvents,\n getAuthHeaders: graphQLConfig.getAuthHeaders,\n });\n }, [\n graphQLConfig.endpoint,\n graphQLConfig.getAuthHeaders,\n graphQLConfig.getDataId,\n graphQLConfig.httpUrl,\n graphQLConfig.logEvents,\n graphQLConfig.wsEndpoint,\n graphQLConfig.wsUrl,\n ]);\n\n const mergedResources = useMemo(() => {\n const appResources: Resource = props.i18n?.resources ?? {};\n const backofficeReactResources = backofficeReactI18nResources as Resource;\n const locales = new Set([\n ...Object.keys(backofficeReactResources),\n ...Object.keys(appResources),\n ]);\n\n const output: Resource = {};\n\n locales.forEach((locale) => {\n const packageLocale = asResourceLanguage(\n backofficeReactResources[locale],\n );\n const appLocale = asResourceLanguage(appResources[locale]);\n output[locale] = {\n ...packageLocale,\n ...appLocale,\n backofficeReact: mergeResourceLanguages(\n asResourceLanguage(packageLocale.backofficeReact),\n asResourceLanguage(appLocale.backofficeReact),\n ),\n shared: mergeResourceLanguages(\n asResourceLanguage(packageLocale.shared),\n asResourceLanguage(appLocale.shared),\n ),\n };\n });\n\n return output;\n }, [props.i18n?.resources]);\n\n const i18nInstance = useMemo(() => {\n return props.i18n?.instance ?? createInstance();\n }, [props.i18n?.instance]);\n\n useEffect(() => {\n const initOptions = props.i18n?.initOptions ?? {};\n const defaultNs = initOptions.defaultNS ?? 'translations';\n const ns = initOptions.ns ?? [\n 'backofficeReact',\n 'translations',\n 'shared',\n 'ui',\n ];\n createI18nInstance({\n resources: mergedResources,\n lng: props.i18n?.lng,\n fallbackLng: props.i18n?.fallbackLng,\n initOptions: {\n ...initOptions,\n defaultNS: defaultNs,\n ns,\n },\n instance: i18nInstance,\n useLanguageDetector: props.i18n?.useLanguageDetector,\n detection: props.i18n?.detection,\n // eslint-disable-next-line no-console\n }).catch(console.error);\n }, [\n i18nInstance,\n mergedResources,\n props.i18n?.initOptions,\n props.i18n?.detection,\n props.i18n?.fallbackLng,\n props.i18n?.lng,\n props.i18n?.useLanguageDetector,\n ]);\n\n const configValue = useMemo(() => {\n return {\n basePath,\n entities: entityManifest,\n entityManifest,\n entityRegistry,\n filterColumnAliases: props.filterColumnAliases,\n sidebar: props.sidebar,\n dashboard: props.dashboard,\n auth: props.auth,\n graphql: props.graphql,\n };\n }, [\n basePath,\n props.auth,\n props.dashboard,\n entityManifest,\n entityRegistry,\n props.filterColumnAliases,\n props.graphql,\n props.sidebar,\n ]);\n\n const routes = useMemo(() => {\n return createBackofficeRoutes({\n basePath,\n entityManifest,\n entityRegistry,\n sidebar: props.sidebar,\n auth: props.auth,\n dashboard: props.dashboard,\n toolsOperationPage: props.toolsOperationPage,\n });\n }, [\n basePath,\n entityManifest,\n entityRegistry,\n props.auth,\n props.dashboard,\n props.toolsOperationPage,\n props.sidebar,\n ]);\n\n return (\n <StrictMode>\n <I18nextProvider i18n={i18nInstance}>\n <ThemeProvider>\n <RelayProvider>\n <BackofficeConfigProvider value={configValue}>\n <RouterShell\n routes={routes}\n instrumentations={props.instrumentations}\n />\n </BackofficeConfigProvider>\n </RelayProvider>\n </ThemeProvider>\n </I18nextProvider>\n </StrictMode>\n );\n};\n\nexport default BackofficeProvider;\n","export type BackofficeLazyValue<TValue> = {\n get: () => TValue | null;\n load: () => Promise<TValue>;\n};\n\nexport const createBackofficeLazyValue = <TValue>(\n loader: () => Promise<TValue>,\n): BackofficeLazyValue<TValue> => {\n let loadedValue: TValue | null = null;\n let inFlightPromise: Promise<TValue> | null = null;\n\n return {\n get: () => {\n return loadedValue;\n },\n load: async () => {\n if (loadedValue != null) {\n return loadedValue;\n }\n if (inFlightPromise != null) {\n return inFlightPromise;\n }\n\n inFlightPromise = loader()\n .then((value) => {\n loadedValue = value;\n return value;\n })\n .finally(() => {\n inFlightPromise = null;\n });\n\n return inFlightPromise;\n },\n };\n};\n\nexport default createBackofficeLazyValue;\n","import type {\n BackofficeFilterSpec,\n BackofficeResolvedListFacetConfig,\n} from '@plumile/backoffice-core/types.js';\n\n/** Compare two optional path arrays for equality. */\nconst arePathsEqual = (\n left?: readonly string[],\n right?: readonly string[],\n): boolean => {\n const leftPath = left ?? [];\n const rightPath = right ?? [];\n if (leftPath.length !== rightPath.length) {\n return false;\n }\n return leftPath.every((segment, index) => {\n return segment === rightPath[index];\n });\n};\n\n/** Find a filter spec by its where key and optional path. */\nexport function resolveFilterForWhereKey<\n Where extends Record<string, unknown>,\n Sort extends string,\n>(\n config: BackofficeResolvedListFacetConfig<Where, Sort>,\n whereKey: string,\n path?: readonly string[],\n): BackofficeFilterSpec<Where> | null {\n const { filters } = config.list;\n const match = filters.find((filter) => {\n return (\n String(filter.whereKey ?? filter.id) === whereKey &&\n arePathsEqual(filter.path, path)\n );\n });\n return match ?? null;\n}\n\n/** Resolve a filter spec from a column id, alias, or contains field. */\nexport function resolveFilterForColumn<\n Where extends Record<string, unknown>,\n Sort extends string,\n>(\n config: BackofficeResolvedListFacetConfig<Where, Sort>,\n columnId: string,\n columnAliases?: Record<string, string>,\n): BackofficeFilterSpec<Where> | null {\n const direct = resolveFilterForWhereKey(config, columnId);\n if (direct != null) {\n return direct;\n }\n\n const { [columnId]: alias } = columnAliases ?? {};\n if (alias != null) {\n const aliasFilter = resolveFilterForWhereKey(config, alias);\n if (aliasFilter != null) {\n return aliasFilter;\n }\n }\n\n const containsKey = `${columnId}Contains`;\n const containsFilter = resolveFilterForWhereKey(config, containsKey);\n if (containsFilter != null) {\n return containsFilter;\n }\n\n return null;\n}\n\n/** Check if a value is compatible with a filter spec. */\nexport function canFilterValue<Where extends Record<string, unknown>>(\n filter: BackofficeFilterSpec<Where>,\n value: unknown,\n): boolean {\n if (value == null) {\n return false;\n }\n\n if (filter.kind === 'boolean') {\n return typeof value === 'boolean';\n }\n\n if (filter.kind === 'entityId') {\n return typeof value === 'string' && value.trim() !== '';\n }\n\n if (typeof value === 'number') {\n return String(value).trim() !== '';\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return value.trim() !== '';\n}\n","import { style } from '@vanilla-extract/css';\n\nimport { vars } from '@plumile/ui/theme/themeContract.js';\nimport { sprinkles } from '@plumile/ui/theme/sprinkles.css.js';\n\nexport const action = style([\n sprinkles({\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 6,\n height: 6,\n padding: 0,\n borderRadius: 'sm',\n borderWidth: 'default',\n borderStyle: 'solid',\n borderColor: 'transparent',\n backgroundColor: 'transparent',\n color: 'textSecondary',\n cursor: 'pointer',\n textDecoration: 'none',\n transitionProperty: 'colors',\n transitionDuration: 120,\n transitionTimingFunction: 'ease',\n }),\n {\n selectors: {\n '&:hover': {\n color: vars.colors.text,\n backgroundColor: vars.colors.surfaceSecondary,\n borderColor: vars.colors.borderSubtle,\n },\n '&:focus-visible': {\n outline: `2px solid ${vars.colors.primary}`,\n outlineOffset: '2px',\n },\n },\n },\n]);\n\nexport const icon = sprinkles({\n width: 4,\n height: 4,\n});\n","import { type JSX } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Link } from '@plumile/router';\nimport { SidebarSearchSvg } from '@plumile/ui';\n\nimport type {\n BackofficeResolvedListFacetConfig,\n I18nLabel,\n} from '@plumile/backoffice-core/types.js';\nimport type { TFunction } from 'i18next';\nimport { buildBackofficeListLink } from '@plumile/backoffice-core/state/buildListHref.js';\nimport { setWhereValue } from '@plumile/backoffice-core/filters/where.js';\n\nimport {\n canFilterValue,\n resolveFilterForColumn,\n resolveFilterForWhereKey,\n} from '../../../filters/filterHelpers.js';\nimport { useBackofficeConfig } from '../../../provider/BackofficeConfigContext.js';\nimport { useBackofficeListFilterContext } from '../scaffolds/BackofficeListFilterContext.js';\nimport { useBackofficeReactTranslation } from '../../../i18n/useBackofficeReactTranslation.js';\n\nimport * as styles from './backofficeFilterAction.css.js';\n\nexport type BackofficeFilterActionProps<\n Where extends Record<string, unknown> = Record<string, unknown>,\n Sort extends string = string,\n> = {\n whereKey: string;\n value: unknown;\n path?: readonly string[];\n label?: string;\n listConfig?: BackofficeResolvedListFacetConfig<Where, Sort>;\n};\n\ntype ListFilterContext<\n Where extends Record<string, unknown>,\n Sort extends string,\n> = {\n config: BackofficeResolvedListFacetConfig<Where, Sort>;\n applyFilter: (\n whereKey: string,\n value: unknown,\n path?: readonly string[],\n ) => void;\n};\n\nconst resolveLabel = (label: I18nLabel, tApp: TFunction): string => {\n return label(tApp);\n};\n\nexport const BackofficeFilterAction = <\n Where extends Record<string, unknown> = Record<string, unknown>,\n Sort extends string = string,\n>(\n props: BackofficeFilterActionProps<Where, Sort>,\n): JSX.Element | null => {\n const { t: tApp } = useTranslation();\n const { t } = useBackofficeReactTranslation();\n const { filterColumnAliases } = useBackofficeConfig();\n const { whereKey, value, path, label, listConfig } = props;\n\n const getContext = useBackofficeListFilterContext as () => ListFilterContext<\n Where,\n Sort\n > | null;\n const context = getContext();\n let config: BackofficeResolvedListFacetConfig<Where, Sort> | null = null;\n\n if (listConfig != null) {\n config = listConfig;\n } else if (context != null) {\n config = context.config;\n }\n\n if (config == null) {\n return null;\n }\n\n let filter = resolveFilterForWhereKey(config, whereKey, path);\n if (filter == null && path == null) {\n filter = resolveFilterForColumn(config, whereKey, filterColumnAliases);\n }\n if (filter == null || !canFilterValue(filter, value)) {\n return null;\n }\n\n const filterLabel = resolveLabel(filter.label, tApp);\n const actionLabel =\n label ??\n t('filters.actions.filterBy', {\n label: filterLabel,\n });\n const icon = (\n <SidebarSearchSvg\n width={14}\n height={14}\n className={styles.icon}\n aria-hidden=\"true\"\n />\n );\n\n if (context != null) {\n return (\n <button\n type=\"button\"\n className={styles.action}\n title={actionLabel}\n aria-label={actionLabel}\n onClick={() => {\n context.applyFilter(whereKey, value, path);\n }}\n >\n {icon}\n </button>\n );\n }\n\n const baseWhere =\n config.listDefaults?.where ?? config.list.defaultState?.where ?? null;\n const nextWhere = setWhereValue(\n baseWhere,\n whereKey as keyof Where,\n value,\n path,\n );\n const to = buildBackofficeListLink(config, { where: nextWhere });\n\n return (\n <Link to={to} className={styles.action}>\n {icon}\n </Link>\n );\n};\n\nexport default BackofficeFilterAction;\n","import { sprinkles } from '@plumile/ui/theme/sprinkles.css.js';\n\nexport const container = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n});\n\nexport const summary = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nexport const content = sprinkles({\n display: 'grid',\n gridTemplateColumns: 'twoFrOneFr',\n gap: 6,\n alignItems: 'flex-start',\n});\n\nexport const primary = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n});\n\nexport const aside = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n});\n","import { type JSX, type ReactNode } from 'react';\n\nimport { cx } from '@plumile/ui';\n\nimport * as styles from './backofficeOverviewLayout.css.js';\n\nexport type BackofficeOverviewLayoutProps = {\n summary?: ReactNode;\n aside?: ReactNode;\n children?: ReactNode;\n className?: string;\n};\n\nexport const BackofficeOverviewLayout = ({\n summary,\n aside,\n children,\n className,\n}: BackofficeOverviewLayoutProps): JSX.Element => {\n let summaryNode: JSX.Element | null = null;\n if (summary != null) {\n summaryNode = <div className={styles.summary}>{summary}</div>;\n }\n\n let asideNode: JSX.Element | null = null;\n if (aside != null) {\n asideNode = <aside className={styles.aside}>{aside}</aside>;\n }\n\n let primaryNode: JSX.Element | null = null;\n if (children != null || asideNode != null) {\n primaryNode = <div className={styles.primary}>{children}</div>;\n }\n\n return (\n <div className={cx(styles.container, className)}>\n {summaryNode}\n <div className={styles.content}>\n {primaryNode}\n {asideNode}\n </div>\n </div>\n );\n};\n\nexport default BackofficeOverviewLayout;\n","import { sprinkles } from '@plumile/ui/theme/sprinkles.css.js';\n\nexport const link = sprinkles({\n color: 'text',\n textDecoration: 'underline',\n textDecorationColor: 'borderStrong',\n});\n","import { useContext, type JSX, type MouseEvent } from 'react';\nimport { Link, RoutingContext } from '@plumile/router';\n\nimport {\n buildBackofficeFallbackListHref,\n buildBackofficeListLink,\n} from '@plumile/backoffice-core/state/buildListHref.js';\nimport { useBackofficeConfig } from '../../../provider/BackofficeConfigContext.js';\n\nimport * as styles from './backofficeRelatedCountLink.css.js';\n\nexport type BackofficeRelatedCountLinkProps<\n Where extends Record<string, unknown> = Record<string, unknown>,\n> = {\n count: number | null | undefined;\n entity: string;\n where: Where;\n};\n\nexport const BackofficeRelatedCountLink = <\n Where extends Record<string, unknown> = Record<string, unknown>,\n>({\n count,\n entity,\n where,\n}: BackofficeRelatedCountLinkProps<Where>): JSX.Element => {\n const { entities, entityRegistry } = useBackofficeConfig();\n const routing = useContext(RoutingContext);\n let resolvedCount = 0;\n if (typeof count === 'number' && Number.isFinite(count)) {\n resolvedCount = count;\n }\n const entityManifest = entities[entity];\n if (entityManifest == null) {\n return <span>{resolvedCount}</span>;\n }\n const loadedEntity = entityRegistry.getLoadedListEntity(entity);\n let to: string | { pathname: string; search: string };\n if (loadedEntity == null) {\n to = buildBackofficeFallbackListHref(entityManifest.routes.list, where);\n } else {\n to = buildBackofficeListLink(loadedEntity.config, { where });\n }\n\n const navigateWithLoadedEntity = async (\n history: NonNullable<typeof routing>['history'],\n ): Promise<void> => {\n const listEntity = await entityRegistry.loadListEntity(entity);\n const next = buildBackofficeListLink(listEntity.config, { where });\n\n let search = '';\n if (next.search !== '') {\n search = `?${next.search}`;\n }\n\n history.push({\n pathname: next.pathname,\n search,\n hash: '',\n });\n };\n\n const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {\n if (\n loadedEntity != null ||\n routing == null ||\n event.defaultPrevented ||\n event.button !== 0 ||\n event.metaKey ||\n event.altKey ||\n event.ctrlKey ||\n event.shiftKey\n ) {\n return;\n }\n\n event.preventDefault();\n const { history } = routing;\n navigateWithLoadedEntity(history).catch(() => {\n if (typeof to === 'string') {\n const fallbackUrl = new URL(to, window.location.origin);\n history.push({\n pathname: fallbackUrl.pathname,\n search: fallbackUrl.search,\n hash: fallbackUrl.hash,\n });\n return;\n }\n\n let search = '';\n if (to.search !== '') {\n search = `?${to.search}`;\n }\n\n history.push({\n pathname: to.pathname,\n search,\n hash: '',\n });\n });\n };\n\n return (\n <Link to={to} className={styles.link} onClick={handleClick}>\n {resolvedCount}\n </Link>\n );\n};\n\nexport default BackofficeRelatedCountLink;\n","import { sprinkles } from '@plumile/ui/theme/sprinkles.css.js';\n\nexport const tabs = sprinkles({\n display: 'flex',\n});\n\nexport const tabBody = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n","import { type JSX, type ReactNode } from 'react';\n\nimport {\n BackofficeTabs,\n DetailPageTemplate,\n type BackofficeTabItem,\n} from '@plumile/ui';\n\nimport * as styles from './backofficeTabbedDetailShell.css.js';\n\nexport type BackofficeTabbedDetailShellProps = {\n headerNode: ReactNode;\n tabs?: readonly BackofficeTabItem[];\n activeId?: string;\n children: ReactNode;\n};\n\nexport const BackofficeTabbedDetailShell = ({\n headerNode,\n tabs,\n activeId,\n children,\n}: BackofficeTabbedDetailShellProps): JSX.Element => {\n let tabsNode: JSX.Element | null = null;\n if (tabs != null && tabs.length > 1 && activeId != null) {\n tabsNode = (\n <BackofficeTabs\n items={tabs}\n activeId={activeId}\n onChange={() => {}}\n className={styles.tabs}\n />\n );\n }\n\n return (\n <DetailPageTemplate headerNode={headerNode} tabsNode={tabsNode}>\n <div className={styles.tabBody}>{children}</div>\n </DetailPageTemplate>\n );\n};\n\nexport default BackofficeTabbedDetailShell;\n","import { sprinkles } from '@plumile/ui/theme/sprinkles.css.js';\n\nexport const container = sprinkles({\n display: 'inline-flex',\n alignItems: 'center',\n gap: 1,\n flexWrap: 'wrap',\n});\n","import { type JSX, type ReactNode } from 'react';\n\nimport { cx } from '@plumile/ui';\n\nimport * as styles from './backofficeInlineFilterRow.css.js';\n\nexport type BackofficeInlineFilterRowProps = {\n children: ReactNode;\n className?: string;\n};\n\nexport const BackofficeInlineFilterRow = ({\n children,\n className,\n}: BackofficeInlineFilterRowProps): JSX.Element => {\n return <span className={cx(styles.container, className)}>{children}</span>;\n};\n\nexport default BackofficeInlineFilterRow;\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport * as ReactRelay from 'react-relay';\nimport type { GraphQLSubscriptionConfig, OperationType } from 'relay-runtime';\n\ntype ConditionalOptions<T extends OperationType> = {\n enabled: boolean;\n deps?: readonly unknown[];\n getVariables?: () => T['variables'];\n};\n\ntype UseConditionalSubscriptionReturn = {\n active: boolean;\n error: Error | null;\n};\n\ntype Hop = { dispose: () => void } | null;\n\n/** Resolve the Relay module regardless of whether the package exposes a default export shim. */\nfunction resolveRelayApi(): typeof ReactRelay {\n const relayDefault: unknown = Reflect.get(ReactRelay, 'default');\n if (relayDefault != null) {\n return relayDefault as typeof ReactRelay;\n }\n return ReactRelay;\n}\n\nconst relayApi = resolveRelayApi();\n\n/** Subscribe only when the caller enables the subscription and variables exist. */\nexport function useConditionalSubscription<T extends OperationType>(\n config: GraphQLSubscriptionConfig<T>,\n options: ConditionalOptions<T>,\n): UseConditionalSubscriptionReturn {\n const environment = relayApi.useRelayEnvironment();\n const [error, setError] = useState<Error | null>(null);\n\n const { enabled = true, deps = [], getVariables } = options;\n\n const resolvedVariables = useMemo<T['variables'] | null>(() => {\n try {\n if (getVariables != null) {\n return getVariables();\n }\n return config.variables;\n } catch {\n return null;\n }\n }, [config.variables, getVariables]);\n\n const stableVarsString = useMemo(() => {\n if (resolvedVariables == null) {\n return null;\n }\n try {\n return JSON.stringify(resolvedVariables);\n } catch {\n return null;\n }\n }, [resolvedVariables]);\n\n const isEnabled = Boolean(enabled);\n const variables = resolvedVariables;\n const active = isEnabled && variables != null;\n\n const disposableRef = useRef<Hop>(null);\n\n useEffect(() => {\n let disposable: Hop = null;\n\n if (isEnabled && variables != null) {\n setError(null);\n\n disposable = relayApi.requestSubscription<T>(environment, {\n subscription: config.subscription,\n variables,\n cacheConfig: config.cacheConfig,\n configs: config.configs,\n onCompleted: config.onCompleted,\n onError: (nextError) => {\n setError(nextError);\n config.onError?.(nextError);\n },\n onNext: config.onNext,\n updater: config.updater,\n });\n\n disposableRef.current = disposable;\n }\n\n return () => {\n if (disposable != null) {\n try {\n disposable.dispose();\n } catch {\n // ignore cleanup failures\n }\n }\n if (disposableRef.current === disposable) {\n disposableRef.current = null;\n }\n };\n }, [config, deps, environment, isEnabled, stableVarsString, variables]);\n\n return { active, error };\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\n\nconst FALLBACK_TEXTAREA_ID = 'copy-to-clipboard-fallback';\n\n/** Write to the clipboard via a hidden textarea when the Clipboard API is unavailable. */\nfunction writeWithFallback(value: string): void {\n if (typeof document === 'undefined') {\n return;\n }\n\n let textarea = document.getElementById(\n FALLBACK_TEXTAREA_ID,\n ) as HTMLTextAreaElement | null;\n\n if (textarea == null) {\n textarea = document.createElement('textarea');\n textarea.id = FALLBACK_TEXTAREA_ID;\n textarea.setAttribute('readonly', '');\n textarea.style.position = 'absolute';\n textarea.style.left = '-9999px';\n textarea.style.top = '0';\n textarea.style.opacity = '0';\n document.body.appendChild(textarea);\n }\n\n textarea.value = value;\n textarea.select();\n document.execCommand('copy');\n}\n\n/** Copy text with best-effort browser fallback and short-lived copied state. */\nexport function useCopyToClipboard(timeoutMs = 2000): {\n copiedKey: string | null;\n copy: (value: string, key?: string) => Promise<boolean>;\n} {\n const [copiedKey, setCopiedKey] = useState<string | null>(null);\n const timeoutRef = useRef<number | null>(null);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current != null) {\n window.clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n const copy = useCallback(\n async (value: string, key?: string): Promise<boolean> => {\n let succeeded = false;\n\n let clipboard: Clipboard | undefined;\n if (typeof navigator !== 'undefined') {\n clipboard = navigator.clipboard;\n }\n\n if (clipboard != null) {\n try {\n await clipboard.writeText(value);\n succeeded = true;\n } catch {\n succeeded = false;\n }\n }\n\n if (!succeeded) {\n writeWithFallback(value);\n succeeded = true;\n }\n\n if (key != null) {\n setCopiedKey(key);\n if (timeoutRef.current != null) {\n window.clearTimeout(timeoutRef.current);\n }\n timeoutRef.current = window.setTimeout(() => {\n setCopiedKey(null);\n timeoutRef.current = null;\n }, timeoutMs);\n }\n\n return succeeded;\n },\n [timeoutMs],\n );\n\n return { copiedKey, copy };\n}\n\nexport default useCopyToClipboard;\n","import { useCallback, useEffect, useState } from 'react';\n\ntype RefetchReason = string;\n\ntype UseRefetchNeededReloadReturn = {\n reason: RefetchReason | null;\n onRefetchNeeded: (reason: RefetchReason | null | undefined) => void;\n reload: () => void;\n clear: () => void;\n};\n\n/** Track refetch-needed interruptions and expose a reload helper. */\nexport function useRefetchNeededReload(\n resetKey: string | null | undefined,\n): UseRefetchNeededReloadReturn {\n const [reason, setReason] = useState<RefetchReason | null>(null);\n\n useEffect(() => {\n setReason(null);\n }, [resetKey]);\n\n const onRefetchNeeded = useCallback(\n (nextReason: RefetchReason | null | undefined) => {\n setReason(nextReason ?? 'UNKNOWN');\n },\n [],\n );\n\n const reload = useCallback(() => {\n window.location.reload();\n }, []);\n\n const clear = useCallback(() => {\n setReason(null);\n }, []);\n\n return { reason, onRefetchNeeded, reload, clear };\n}\n","import type { ReviewStatus } from '../modules/sharedSchemaTypes.js';\nimport { useSharedTranslation } from './useSharedTranslation.js';\n\nexport type ReviewStatusLabeler = {\n getReviewStatusLabel: (status: ReviewStatus | null | undefined) => string;\n};\n\n/** Provide shared i18n labels for review status values. */\nexport function useReviewStatusLabel(): ReviewStatusLabeler {\n const { t } = useSharedTranslation();\n\n /** Resolve a translated label for a review status value. */\n function getReviewStatusLabel(\n status: ReviewStatus | null | undefined,\n ): string {\n if (status == null) {\n return t('review.status.unknown');\n }\n switch (status) {\n case 'APPROVED':\n return t('review.status.approved');\n case 'CHANGES_REQUESTED':\n return t('review.status.changesRequested');\n case 'PENDING':\n return t('review.status.pending');\n default:\n return t('review.status.unknown');\n }\n }\n\n return { getReviewStatusLabel };\n}\n","/** Encode a UTF-8 string into base64 using native helpers when available. */\nexport function encodeUtf8ToBase64(value: string): string {\n if (typeof value !== 'string') {\n throw new TypeError('encodeUtf8ToBase64 expects a string input.');\n }\n\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(value, 'utf-8').toString('base64');\n }\n\n if (typeof globalThis.btoa === 'function') {\n const utf8 = encodeURIComponent(value).replace(\n /%([0-9A-F]{2})/g,\n (_, code) => {\n return String.fromCharCode(Number.parseInt(code, 16));\n },\n );\n return globalThis.btoa(utf8);\n }\n\n throw new Error('No base64 encoder is available in this environment.');\n}\n\n/** Decode a base64 string into UTF-8 using native helpers when available. */\nexport function decodeBase64ToUtf8(value: string): string {\n if (typeof value !== 'string') {\n throw new TypeError('decodeBase64ToUtf8 expects a string input.');\n }\n\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(value, 'base64').toString('utf-8');\n }\n\n if (typeof globalThis.atob === 'function') {\n const binary = globalThis.atob(value);\n const percentEncoded = Array.from(binary, (char) => {\n const code = char.charCodeAt(0).toString(16).padStart(2, '0');\n return `%${code}`;\n }).join('');\n return decodeURIComponent(percentEncoded);\n }\n\n throw new Error('No base64 decoder is available in this environment.');\n}\n","export type FileSizeUnit = 'B' | 'KB' | 'MB' | 'GB' | 'TB';\n\nexport type FileSizeResult = {\n value: number;\n unit: FileSizeUnit;\n displayValue: string;\n};\n\nconst SIZE_UNITS: readonly FileSizeUnit[] = ['B', 'KB', 'MB', 'GB', 'TB'];\nconst BASE = 1024;\n\nconst roundValue = (value: number, unit: FileSizeUnit): string => {\n if (unit === 'B') {\n return Math.round(value).toString();\n }\n let decimals = 1;\n if (value >= 10) {\n decimals = 0;\n }\n return value.toFixed(decimals);\n};\n\n/** Format a byte count into a human-readable value and unit. */\nexport function formatFileSize(bytes: number): FileSizeResult {\n let normalized = bytes;\n if (!Number.isFinite(bytes)) {\n normalized = 0;\n }\n const isNegative = normalized < 0;\n let value = Math.abs(normalized);\n let unitIndex = 0;\n\n while (value >= BASE && unitIndex < SIZE_UNITS.length - 1) {\n value /= BASE;\n unitIndex += 1;\n }\n\n const unit = SIZE_UNITS[unitIndex] ?? 'B';\n let signedValue = value;\n if (isNegative) {\n signedValue = -value;\n }\n\n return {\n value: signedValue,\n unit,\n displayValue: roundValue(signedValue, unit),\n };\n}\n","import * as ReactRelay from 'react-relay';\nimport type { GraphQLTaggedNode } from 'relay-runtime';\n\nconst { readInlineData } = ReactRelay;\n\n// Reserved for non-React helpers that explicitly read `@inline` fragments.\nexport const createInlineDataReader = <TRef, TData>(\n fragment: GraphQLTaggedNode,\n): ((ref: TRef) => TData) => {\n return (ref: TRef) => {\n return readInlineData(fragment, ref as never) as TData;\n };\n};\n\n// Legacy alias kept for incremental migration of existing callsites.\nexport const createInlineReader = createInlineDataReader;\n","// Only use this in the React page/layout pipeline after `useFragment` has\n// already resolved a typed fragment payload into `FragmentData`.\nexport const identityView = <TData>(data: TData): TData => {\n return data;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"backoffice-react.js","names":[],"sources":["../../src/i18n/createI18nInstance.ts","../../src/i18n/mergeResourceLanguages.ts","../../src/i18n/locales/en/backofficeReact.json","../../src/i18n/locales/fr/backofficeReact.json","../../src/i18n/locales/en/shared.json","../../src/i18n/locales/fr/shared.json","../../src/i18n/resources.ts","../../src/relay/RelayProvider.tsx","../../src/router/createBackofficeRoutes.tsx","../../src/components/backoffice/routing/backofficeRouteFallback.css.ts","../../src/components/backoffice/routing/BackofficeRouteFallback.tsx","../../src/components/backoffice/routing/backofficeRoutePendingBar.css.ts","../../src/components/backoffice/routing/BackofficeRoutePendingBar.tsx","../../src/provider/entityRegistry.ts","../../src/provider/BackofficeProvider.tsx","../../src/provider/lazyValue.ts","../../src/filters/filterHelpers.ts","../../src/components/backoffice/filters/backofficeFilterAction.css.ts","../../src/components/backoffice/filters/BackofficeFilterAction.tsx","../../src/components/backoffice/overview/backofficeOverviewLayout.css.ts","../../src/components/backoffice/overview/BackofficeOverviewLayout.tsx","../../src/components/backoffice/refs/backofficeRelatedCountLink.css.ts","../../src/components/backoffice/refs/BackofficeRelatedCountLink.tsx","../../src/components/backoffice/scaffolds/backofficeTabbedDetailShell.css.ts","../../src/components/backoffice/scaffolds/BackofficeTabbedDetailShell.tsx","../../src/components/backoffice/shared/backofficeInlineFilterRow.css.ts","../../src/components/backoffice/shared/BackofficeInlineFilterRow.tsx","../../src/hooks/useConditionalSubscription.ts","../../src/hooks/useCopyToClipboard.ts","../../src/hooks/useRefetchNeededReload.ts","../../src/i18n/useReviewStatusLabel.ts","../../src/modules/base64.ts","../../src/modules/formatFileSize.ts","../../src/relay/createInlineReader.ts","../../src/relay/identityView.ts"],"sourcesContent":["import { createInstance, type i18n, type InitOptions } from 'i18next';\nimport LanguageDetector, {\n type DetectorOptions,\n} from 'i18next-browser-languagedetector';\nimport { initReactI18next } from 'react-i18next';\n\ntype InitOptionsWithDetection = Omit<\n InitOptions,\n 'resources' | 'lng' | 'fallbackLng'\n> & {\n detection?: DetectorOptions;\n};\n\ntype InitConfig = InitOptions & {\n detection?: DetectorOptions;\n};\n\nexport interface CreateI18nOptions {\n resources: NonNullable<InitOptions['resources']>;\n lng?: InitOptions['lng'];\n fallbackLng?: InitOptions['fallbackLng'];\n initOptions?: InitOptionsWithDetection;\n instance?: i18n;\n useLanguageDetector?: boolean;\n detection?: DetectorOptions;\n}\n\nconst DEFAULT_NUMBER_OPTIONS = { maximumFractionDigits: 2 };\nconst DEFAULT_CURRENCY_OPTIONS = {\n style: 'currency',\n currency: 'USD',\n maximumFractionDigits: 2,\n} as const;\nconst DEFAULT_DATE_OPTIONS = { dateStyle: 'medium' } as const;\nconst DEFAULT_DATE_TIME_OPTIONS = {\n dateStyle: 'medium',\n timeStyle: 'short',\n} as const;\nconst DEFAULT_PERCENT_OPTIONS = { style: 'percent' } as const;\n\n/** Returns the best locale value from i18next's language list. */\nfunction getLocale(lng: string | string[] | undefined): string | undefined {\n if (Array.isArray(lng)) {\n return lng[0];\n }\n return lng;\n}\n\n/** Stringify interpolation values while avoiding Object.prototype defaults. */\nfunction stringifyValue(value: unknown): string {\n if (value == null) {\n return '';\n }\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean' ||\n typeof value === 'bigint'\n ) {\n return String(value);\n }\n if (typeof value === 'symbol') {\n return value.toString();\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\n/** Format interpolated values using Intl based on the i18n format token. */\nfunction formatValue(\n value: unknown,\n format: string | undefined,\n lng: string | string[] | undefined,\n): string {\n if (format == null) {\n return stringifyValue(value);\n }\n\n const locale = getLocale(lng);\n\n if (format === 'number' && typeof value === 'number') {\n return new Intl.NumberFormat(locale, DEFAULT_NUMBER_OPTIONS).format(value);\n }\n\n if (format === 'currency' && typeof value === 'number') {\n return new Intl.NumberFormat(locale, DEFAULT_CURRENCY_OPTIONS).format(\n value,\n );\n }\n\n if (format === 'percent' && typeof value === 'number') {\n return new Intl.NumberFormat(locale, DEFAULT_PERCENT_OPTIONS).format(value);\n }\n\n if ((format === 'date' || format === 'datetime') && value != null) {\n let date: Date;\n if (value instanceof Date) {\n date = value;\n } else if (typeof value === 'string' || typeof value === 'number') {\n date = new Date(value);\n } else {\n date = new Date(stringifyValue(value));\n }\n if (!Number.isNaN(date.getTime())) {\n if (format === 'date') {\n return new Intl.DateTimeFormat(locale, DEFAULT_DATE_OPTIONS).format(\n date,\n );\n }\n return new Intl.DateTimeFormat(locale, DEFAULT_DATE_TIME_OPTIONS).format(\n date,\n );\n }\n }\n\n return stringifyValue(value);\n}\n\n/**\n * Create and initialize an i18next instance configured for React.\n * Each frontend can call this helper with its own resource bundles.\n */\nexport async function createI18nInstance(\n options: CreateI18nOptions,\n): Promise<i18n> {\n const {\n resources,\n lng,\n fallbackLng = 'en',\n initOptions = {},\n instance = createInstance(),\n useLanguageDetector = false,\n detection,\n } = options;\n\n const { interpolation, ...restInitOptions } = initOptions;\n\n if (useLanguageDetector) {\n instance.use(LanguageDetector);\n }\n instance.use(initReactI18next);\n\n try {\n const initConfig: InitConfig = {\n ...restInitOptions,\n resources,\n fallbackLng,\n interpolation: {\n escapeValue: false,\n format: interpolation?.format ?? formatValue,\n ...interpolation,\n },\n };\n if (lng != null) {\n initConfig.lng = lng;\n }\n\n const detectionOptions = detection ?? initOptions.detection;\n if (useLanguageDetector && detectionOptions != null) {\n initConfig.detection = detectionOptions;\n }\n\n await instance.init(initConfig);\n } catch {\n // Swallow initialization errors to avoid blocking the UI.\n // Callers can inspect the instance for diagnostics if needed.\n }\n\n return instance;\n}\n","import type { ResourceLanguage } from 'i18next';\n\nconst isResourceObject = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value != null && !Array.isArray(value);\n};\n\nconst asResourceLanguage = (value: unknown): ResourceLanguage => {\n if (isResourceObject(value)) {\n return value as ResourceLanguage;\n }\n return {};\n};\n\n/**\n * Deep-merges i18next resource language objects while preserving nested keys.\n */\nexport function mergeResourceLanguages(\n ...values: readonly ResourceLanguage[]\n): ResourceLanguage {\n const output: ResourceLanguage = {};\n\n values.forEach((value) => {\n Object.entries(value).forEach(([key, nextValue]) => {\n const currentValue = output[key];\n if (isResourceObject(currentValue) && isResourceObject(nextValue)) {\n output[key] = mergeResourceLanguages(\n asResourceLanguage(currentValue),\n asResourceLanguage(nextValue),\n );\n return;\n }\n output[key] = nextValue;\n });\n });\n\n return output;\n}\n","{\n \"actions\": {\n \"form\": {\n \"cancel\": \"Cancel\",\n \"errors\": {\n \"invalidJson\": \"{{label}} must be valid JSON.\",\n \"invalidJsonArray\": \"{{label}} must be a valid JSON array.\",\n \"invalidJsonObject\": \"{{label}} must be a valid JSON object.\",\n \"invalidNumber\": \"{{label}} must be a valid number.\",\n \"invalidPayload\": \"The submitted payload is invalid.\",\n \"required\": \"{{label}} is required.\"\n }\n },\n \"view\": \"View\"\n },\n \"auth\": {\n \"acceptInvitation\": {\n \"errors\": {\n \"alreadyAccepted\": \"This invitation has already been accepted.\",\n \"default\": \"Unable to accept this invitation. Please try again.\",\n \"emailMismatch\": \"This invitation was sent to a different email address.\",\n \"expired\": \"This invitation has expired.\",\n \"invalidToken\": \"This invitation token is invalid.\",\n \"passwordMismatch\": \"Password and confirmation do not match.\",\n \"passwordPolicyViolation\": \"Your password does not meet policy requirements.\",\n \"rateLimited\": \"Too many attempts. Please wait and try again.\"\n }\n },\n \"loginFlow\": {\n \"errors\": {\n \"accountLocked\": \"Your account is locked.\",\n \"invalidCredentials\": \"Invalid email or password.\",\n \"invalidEmail\": \"Please enter a valid email address.\",\n \"rateLimited\": \"Too many attempts. Please wait and try again.\",\n \"tryAgain\": \"Something went wrong. Please try again.\"\n }\n },\n \"mfa\": {\n \"errors\": {\n \"expired\": \"Your verification challenge has expired.\",\n \"invalidChallenge\": \"Your verification challenge is invalid.\",\n \"invalidCode\": \"The verification code is invalid.\",\n \"tooManyAttempts\": \"Too many invalid codes. Please try again later.\",\n \"verificationFailed\": \"Unable to verify the code. Please try again.\"\n }\n },\n \"passkey\": {\n \"errors\": {\n \"challengeExpired\": \"Your passkey challenge has expired.\",\n \"failed\": \"Passkey authentication failed. Please try again.\",\n \"invalidAssertion\": \"The passkey response is invalid.\",\n \"invalidChallenge\": \"The passkey challenge is invalid.\",\n \"invalidEmail\": \"Please enter a valid email address.\",\n \"notAvailable\": \"Passkeys are not available on this device.\",\n \"notFound\": \"No passkey found for this account.\"\n }\n },\n \"passwordResetComplete\": {\n \"errors\": {\n \"expired\": \"This reset link has expired.\",\n \"invalid\": \"This reset link is invalid.\",\n \"policyViolation\": \"Your new password does not meet policy requirements.\"\n }\n },\n \"passwordResetRequest\": {\n \"errors\": {\n \"invalidEmail\": \"Please enter a valid email address.\",\n \"rateLimited\": \"Too many requests. Please wait and try again.\",\n \"startFailed\": \"Unable to start password reset. Please try again.\"\n }\n },\n \"verifyEmail\": {\n \"errors\": {\n \"alreadyVerified\": \"This email address is already verified.\",\n \"expired\": \"This verification link has expired.\",\n \"invalid\": \"This verification link is invalid.\"\n }\n }\n },\n \"common\": {\n \"actions\": {\n \"change\": \"Change\",\n \"clear\": \"Clear\",\n \"close\": \"Close\",\n \"copied\": \"Copied\",\n \"copy\": \"Copy\",\n \"pick\": \"Pick\",\n \"retry\": \"Retry\"\n },\n \"boolean\": {\n \"no\": \"No\",\n \"yes\": \"Yes\"\n },\n \"loading\": \"Loading...\",\n \"notAvailable\": \"N/A\"\n },\n \"dashboard\": {\n \"actions\": {\n \"openList\": \"Open list\",\n \"openTool\": \"Open tool\"\n },\n \"subtitle\": \"Overview of the Work context.\",\n \"title\": \"Dashboard\"\n },\n \"detail\": {\n \"notFound\": \"Not found\"\n },\n \"emptyState\": {\n \"listEmpty\": {\n \"description\": \"There are no records to display.\",\n \"title\": \"No results\"\n },\n \"listEmptyFiltered\": {\n \"actions\": {\n \"reset\": \"Reset filters\"\n },\n \"description\": \"No results match the current filters.\"\n }\n },\n \"filters\": {\n \"actions\": {\n \"filterBy\": \"Filter by {{label}}\"\n },\n \"all\": \"All {{label}}\",\n \"allFilters\": \"All filters\",\n \"allFiltersWithCount_one\": \"All filters ({{count}})\",\n \"allFiltersWithCount_other\": \"All filters ({{count}})\",\n \"boolean\": {\n \"no\": \"No\",\n \"yes\": \"Yes\"\n },\n \"placeholders\": {\n \"search\": \"Search {{label}}\",\n \"unresolved\": \"Unresolved ID\"\n },\n \"sections\": {\n \"default\": \"Filters\"\n }\n },\n \"flags\": {\n \"agentManaged\": {\n \"agentManaged\": \"Agent managed\",\n \"userManaged\": \"User managed\"\n },\n \"capability\": {\n \"allowed\": \"Allowed\",\n \"denied\": \"Denied\"\n },\n \"default\": {\n \"default\": \"Default\",\n \"notDefault\": \"Not default\"\n },\n \"deployedProduction\": {\n \"deployed\": \"Deployed\",\n \"notDeployed\": \"Not deployed\"\n },\n \"enabled\": {\n \"disabled\": \"Disabled\",\n \"enabled\": \"Enabled\"\n },\n \"encrypted\": {\n \"encrypted\": \"Encrypted\",\n \"notEncrypted\": \"Not encrypted\"\n },\n \"failure\": {\n \"failed\": \"Failed\",\n \"ok\": \"OK\"\n },\n \"forced\": {\n \"forced\": \"Forced\",\n \"normal\": \"Normal\"\n },\n \"locked\": {\n \"locked\": \"Locked\",\n \"unlocked\": \"Unlocked\"\n }\n },\n \"format\": {\n \"currency\": \"{{value, currency}}\",\n \"number\": \"{{value, number}}\",\n \"percent\": \"{{value, percent}}\"\n },\n \"list\": {\n \"actions\": {\n \"refresh\": \"Refresh\",\n \"retry\": \"Retry\"\n },\n \"errors\": {\n \"tableFailed\": \"The table failed to load.\",\n \"title\": \"Table error\"\n },\n \"loadMore\": {\n \"end\": \"End of results\",\n \"loading\": \"Loading more…\",\n \"more\": \"More results available\"\n },\n \"showing\": \"Showing {{shown, number}} of {{total, number}}\"\n },\n \"picker\": {\n \"errors\": {\n \"loadFailed\": \"Failed to load.\"\n },\n \"searchPlaceholder\": {\n \"default\": \"Search...\"\n },\n \"searchRequired\": \"Enter an ID to search.\",\n \"title\": \"Select an ID\",\n \"unavailable\": \"Picker not available for {{entity}}.\"\n },\n \"relations\": {\n \"labelWithCount_one\": \"{{label}} ({{count}})\",\n \"labelWithCount_other\": \"{{label}} ({{count}})\",\n \"menu\": {\n \"label\": \"Relations\"\n },\n \"viewList\": \"View list\"\n },\n \"sidebar\": {\n \"actions\": {\n \"pin\": \"Pin\",\n \"reorder\": \"Reorder\",\n \"unpin\": \"Unpin\"\n },\n \"items\": {\n \"dashboard\": \"Dashboard\"\n },\n \"profile\": {\n \"actions\": {\n \"signOut\": \"Sign out\"\n },\n \"menuAriaLabel\": \"Open profile menu\",\n \"title\": \"Profile\",\n \"unknownUser\": \"Unknown user\"\n },\n \"search\": {\n \"placeholder\": \"Search…\"\n },\n \"sections\": {\n \"pinned\": \"Pinned\"\n }\n },\n \"tools\": {\n \"docs\": {\n \"inputExampleTitle\": \"Example JSON\",\n \"inputTypeTitle\": \"Input type (GraphQL)\"\n },\n \"errors\": {\n \"description\": \"We couldn't run this tool.\",\n \"details\": \"Error details\",\n \"label\": \"Error\",\n \"missingScope\": \"Missing initiative scope.\",\n \"title\": \"Tool error\",\n \"unknown\": \"Unknown error\"\n },\n \"forms\": {\n \"actions\": {\n \"insertExample\": \"Insert example\",\n \"run\": \"Run\"\n },\n \"inputJsonLabel\": \"Input JSON\"\n },\n \"meta\": {\n \"idLabel\": \"Tool\"\n },\n \"output\": \"Output\",\n \"project\": {\n \"actions\": {\n \"pick\": \"Pick\"\n },\n \"description\": \"Select the project to analyze.\",\n \"empty\": \"Choose a project to run tools.\",\n \"pickerTitle\": \"Select a project\",\n \"placeholder\": \"Project ID\",\n \"title\": \"Project\"\n },\n \"scope\": {\n \"actions\": {\n \"pick\": \"Pick\"\n },\n \"description\": \"Select the initiative scope for tools.\",\n \"empty\": \"Choose an initiative to run tools.\",\n \"pickerTitle\": \"Select an initiative\",\n \"placeholder\": \"Initiative ID\",\n \"title\": \"Scope\"\n }\n }\n}\n","{\n \"actions\": {\n \"form\": {\n \"cancel\": \"Annuler\",\n \"errors\": {\n \"invalidJson\": \"{{label}} doit être un JSON valide.\",\n \"invalidJsonArray\": \"{{label}} doit être un tableau JSON valide.\",\n \"invalidJsonObject\": \"{{label}} doit être un objet JSON valide.\",\n \"invalidNumber\": \"{{label}} doit être un nombre valide.\",\n \"invalidPayload\": \"La charge utile soumise est invalide.\",\n \"required\": \"{{label}} est requis.\"\n }\n },\n \"view\": \"Voir\"\n },\n \"auth\": {\n \"acceptInvitation\": {\n \"errors\": {\n \"alreadyAccepted\": \"Cette invitation a déjà été acceptée.\",\n \"default\": \"Impossible d'accepter cette invitation. Veuillez réessayer.\",\n \"emailMismatch\": \"Cette invitation a été envoyée à une autre adresse email.\",\n \"expired\": \"Cette invitation a expiré.\",\n \"invalidToken\": \"Ce jeton d'invitation est invalide.\",\n \"passwordMismatch\": \"Le mot de passe et sa confirmation ne correspondent pas.\",\n \"passwordPolicyViolation\": \"Votre mot de passe ne respecte pas la politique de sécurité.\",\n \"rateLimited\": \"Trop de tentatives. Veuillez patienter puis réessayer.\"\n }\n },\n \"loginFlow\": {\n \"errors\": {\n \"accountLocked\": \"Votre compte est verrouillé.\",\n \"invalidCredentials\": \"Email ou mot de passe invalide.\",\n \"invalidEmail\": \"Veuillez saisir une adresse email valide.\",\n \"rateLimited\": \"Trop de tentatives. Veuillez patienter puis réessayer.\",\n \"tryAgain\": \"Une erreur est survenue. Veuillez réessayer.\"\n }\n },\n \"mfa\": {\n \"errors\": {\n \"expired\": \"Votre challenge de vérification a expiré.\",\n \"invalidChallenge\": \"Votre challenge de vérification est invalide.\",\n \"invalidCode\": \"Le code de vérification est invalide.\",\n \"tooManyAttempts\": \"Trop de codes invalides. Veuillez réessayer plus tard.\",\n \"verificationFailed\": \"Impossible de vérifier le code. Veuillez réessayer.\"\n }\n },\n \"passkey\": {\n \"errors\": {\n \"challengeExpired\": \"Votre challenge passkey a expiré.\",\n \"failed\": \"L'authentification passkey a échoué. Veuillez réessayer.\",\n \"invalidAssertion\": \"La réponse passkey est invalide.\",\n \"invalidChallenge\": \"Le challenge passkey est invalide.\",\n \"invalidEmail\": \"Veuillez saisir une adresse email valide.\",\n \"notAvailable\": \"Les passkeys ne sont pas disponibles sur cet appareil.\",\n \"notFound\": \"Aucune passkey trouvée pour ce compte.\"\n }\n },\n \"passwordResetComplete\": {\n \"errors\": {\n \"expired\": \"Ce lien de réinitialisation a expiré.\",\n \"invalid\": \"Ce lien de réinitialisation est invalide.\",\n \"policyViolation\": \"Votre nouveau mot de passe ne respecte pas la politique de sécurité.\"\n }\n },\n \"passwordResetRequest\": {\n \"errors\": {\n \"invalidEmail\": \"Veuillez saisir une adresse email valide.\",\n \"rateLimited\": \"Trop de demandes. Veuillez patienter puis réessayer.\",\n \"startFailed\": \"Impossible de démarrer la réinitialisation du mot de passe. Veuillez réessayer.\"\n }\n },\n \"verifyEmail\": {\n \"errors\": {\n \"alreadyVerified\": \"Cette adresse email est déjà vérifiée.\",\n \"expired\": \"Ce lien de vérification a expiré.\",\n \"invalid\": \"Ce lien de vérification est invalide.\"\n }\n }\n },\n \"common\": {\n \"actions\": {\n \"change\": \"Modifier\",\n \"clear\": \"Effacer\",\n \"close\": \"Fermer\",\n \"copied\": \"Copié\",\n \"copy\": \"Copier\",\n \"pick\": \"Choisir\",\n \"retry\": \"Réessayer\"\n },\n \"boolean\": {\n \"no\": \"Non\",\n \"yes\": \"Oui\"\n },\n \"loading\": \"Chargement...\",\n \"notAvailable\": \"N/D\"\n },\n \"dashboard\": {\n \"actions\": {\n \"openList\": \"Ouvrir la liste\",\n \"openTool\": \"Ouvrir l'outil\"\n },\n \"subtitle\": \"Vue d'ensemble du contexte Work.\",\n \"title\": \"Tableau de bord\"\n },\n \"detail\": {\n \"notFound\": \"Introuvable\"\n },\n \"emptyState\": {\n \"listEmpty\": {\n \"description\": \"Aucun enregistrement à afficher.\",\n \"title\": \"Aucun résultat\"\n },\n \"listEmptyFiltered\": {\n \"actions\": {\n \"reset\": \"Réinitialiser les filtres\"\n },\n \"description\": \"Aucun résultat ne correspond aux filtres actuels.\"\n }\n },\n \"filters\": {\n \"actions\": {\n \"filterBy\": \"Filtrer par {{label}}\"\n },\n \"all\": \"Tous {{label}}\",\n \"allFilters\": \"Tous les filtres\",\n \"allFiltersWithCount_one\": \"Tous les filtres ({{count}})\",\n \"allFiltersWithCount_many\": \"Tous les filtres ({{count}})\",\n \"allFiltersWithCount_other\": \"Tous les filtres ({{count}})\",\n \"boolean\": {\n \"no\": \"Non\",\n \"yes\": \"Oui\"\n },\n \"placeholders\": {\n \"search\": \"Rechercher {{label}}\",\n \"unresolved\": \"ID introuvable\"\n },\n \"sections\": {\n \"default\": \"Filtres\"\n }\n },\n \"flags\": {\n \"agentManaged\": {\n \"agentManaged\": \"Géré par un agent\",\n \"userManaged\": \"Géré par un utilisateur\"\n },\n \"capability\": {\n \"allowed\": \"Autorisé\",\n \"denied\": \"Refusé\"\n },\n \"default\": {\n \"default\": \"Par défaut\",\n \"notDefault\": \"Non par défaut\"\n },\n \"deployedProduction\": {\n \"deployed\": \"Déployé\",\n \"notDeployed\": \"Non déployé\"\n },\n \"enabled\": {\n \"disabled\": \"Désactivé\",\n \"enabled\": \"Activé\"\n },\n \"encrypted\": {\n \"encrypted\": \"Chiffré\",\n \"notEncrypted\": \"Non chiffré\"\n },\n \"failure\": {\n \"failed\": \"Échoué\",\n \"ok\": \"OK\"\n },\n \"forced\": {\n \"forced\": \"Forcé\",\n \"normal\": \"Normal\"\n },\n \"locked\": {\n \"locked\": \"Verrouillé\",\n \"unlocked\": \"Déverrouillé\"\n }\n },\n \"format\": {\n \"currency\": \"{{value, currency}}\",\n \"number\": \"{{value, number}}\",\n \"percent\": \"{{value, percent}}\"\n },\n \"list\": {\n \"actions\": {\n \"refresh\": \"Actualiser\",\n \"retry\": \"Réessayer\"\n },\n \"errors\": {\n \"tableFailed\": \"Le tableau n'a pas pu se charger.\",\n \"title\": \"Erreur du tableau\"\n },\n \"loadMore\": {\n \"end\": \"Fin des résultats\",\n \"loading\": \"Chargement…\",\n \"more\": \"Plus de résultats disponibles\"\n },\n \"showing\": \"Affichage de {{shown, number}} sur {{total, number}}\"\n },\n \"picker\": {\n \"errors\": {\n \"loadFailed\": \"Échec du chargement.\"\n },\n \"searchPlaceholder\": {\n \"default\": \"Rechercher...\"\n },\n \"searchRequired\": \"Saisissez un ID pour rechercher.\",\n \"title\": \"Sélectionner un ID\",\n \"unavailable\": \"Sélecteur indisponible pour {{entity}}.\"\n },\n \"relations\": {\n \"labelWithCount_one\": \"{{label}} ({{count}})\",\n \"labelWithCount_many\": \"{{label}} ({{count}})\",\n \"labelWithCount_other\": \"{{label}} ({{count}})\",\n \"menu\": {\n \"label\": \"Relations\"\n },\n \"viewList\": \"Voir la liste\"\n },\n \"sidebar\": {\n \"actions\": {\n \"pin\": \"Épingler\",\n \"reorder\": \"Réordonner\",\n \"unpin\": \"Désépingler\"\n },\n \"items\": {\n \"dashboard\": \"Tableau de bord\"\n },\n \"profile\": {\n \"actions\": {\n \"signOut\": \"Se déconnecter\"\n },\n \"menuAriaLabel\": \"Ouvrir le menu profil\",\n \"title\": \"Profil\",\n \"unknownUser\": \"Utilisateur inconnu\"\n },\n \"search\": {\n \"placeholder\": \"Rechercher...\"\n },\n \"sections\": {\n \"pinned\": \"Épinglés\"\n }\n },\n \"tools\": {\n \"docs\": {\n \"inputExampleTitle\": \"Exemple JSON\",\n \"inputTypeTitle\": \"Type d'entrée (GraphQL)\"\n },\n \"errors\": {\n \"description\": \"Impossible d'exécuter cet outil.\",\n \"details\": \"Détails de l'erreur\",\n \"label\": \"Erreur\",\n \"missingScope\": \"Portée de l'initiative manquante.\",\n \"title\": \"Erreur d'outil\",\n \"unknown\": \"Erreur inconnue\"\n },\n \"forms\": {\n \"actions\": {\n \"insertExample\": \"Insérer l'exemple\",\n \"run\": \"Exécuter\"\n },\n \"inputJsonLabel\": \"JSON d'entrée\"\n },\n \"meta\": {\n \"idLabel\": \"Outil\"\n },\n \"output\": \"Résultat\",\n \"project\": {\n \"actions\": {\n \"pick\": \"Choisir\"\n },\n \"description\": \"Sélectionner le projet à analyser.\",\n \"empty\": \"Choisissez un projet pour exécuter les outils.\",\n \"pickerTitle\": \"Sélectionner un projet\",\n \"placeholder\": \"ID du projet\",\n \"title\": \"Projet\"\n },\n \"scope\": {\n \"actions\": {\n \"pick\": \"Choisir\"\n },\n \"description\": \"Sélectionner l'initiative pour exécuter les outils.\",\n \"empty\": \"Choisissez une initiative pour exécuter les outils.\",\n \"pickerTitle\": \"Sélectionner une initiative\",\n \"placeholder\": \"ID de l'initiative\",\n \"title\": \"Portée\"\n }\n }\n}\n","{\n \"auth\": {\n \"acceptInvitation\": {\n \"actions\": {\n \"backToLogin\": \"Back to login\"\n },\n \"errors\": {\n \"alreadyAccepted\": \"This invitation has already been accepted.\",\n \"default\": \"Unable to accept invitation.\",\n \"emailMismatch\": \"This invitation was sent to a different email address.\",\n \"expired\": \"Invitation link has expired.\",\n \"invalidToken\": \"Invitation link is invalid.\",\n \"missingToken\": \"Invitation link is missing or invalid.\",\n \"passwordMismatch\": \"Password and confirmation do not match.\",\n \"passwordPolicyViolation\": \"Your password does not meet policy requirements.\",\n \"rateLimited\": \"Too many attempts. Please try again later.\"\n },\n \"form\": {\n \"confirmLabel\": \"Confirm password\",\n \"confirmPlaceholder\": \"Confirm your password\",\n \"passwordLabel\": \"Password\",\n \"passwordPlaceholder\": \"Create a password\",\n \"submit\": \"Accept invitation\"\n },\n \"mfaSubtitle\": \"Enter the verification code to continue.\",\n \"mfaTitle\": \"Verify your identity\",\n \"status\": {\n \"success\": \"Invitation accepted.\",\n \"workingButton\": \"Working...\"\n },\n \"subtitle\": \"Create your account to join.\",\n \"title\": \"Accept invitation\"\n },\n \"emailCapture\": {\n \"continue\": \"Continue\",\n \"description\": \"Enter your work email to continue.\",\n \"emailLabel\": \"Work email\",\n \"emailPlaceholder\": \"you@company.com\",\n \"forgotPassword\": \"Forgot your password?\"\n },\n \"loginFlow\": {\n \"errors\": {\n \"accountLocked\": \"Too many attempts. Try again later.\",\n \"emailRequired\": \"Enter an email address to continue.\",\n \"invalidCredentials\": \"Email or password is incorrect.\",\n \"invalidEmail\": \"Enter a valid email address.\",\n \"passkeyUnavailable\": \"Passkeys are not available.\",\n \"rateLimited\": \"Too many attempts. Please try again later.\",\n \"tryAgain\": \"Something went wrong. Please try again.\"\n },\n \"methods\": {\n \"title\": \"Choose a sign-in method\"\n },\n \"passkey\": {\n \"description\": \"Use the passkey associated with {{email}}.\",\n \"title\": \"Use a passkey\"\n },\n \"subtitle\": {\n \"default\": \"Choose a sign-in method to continue.\",\n \"mfa\": \"Enter the verification code to continue.\"\n },\n \"title\": {\n \"default\": \"Sign in\",\n \"mfa\": \"Two-factor authentication\"\n }\n },\n \"methodChooser\": {\n \"actions\": {\n \"back\": \"Back\"\n },\n \"locked\": \"Too many attempts. Try again later.\",\n \"lockedWithTime\": \"Too many attempts. Try again at {{time}}.\",\n \"methods\": {\n \"other\": \"{{method}}\",\n \"passkey\": \"Passkey\",\n \"password\": \"Password\"\n },\n \"prompt\": \"Choose how to sign in for <strong>{{email}}</strong>.\"\n },\n \"mfa\": {\n \"actions\": {\n \"back\": \"Back\",\n \"submit\": \"Verify\"\n },\n \"errors\": {\n \"expired\": \"Verification session expired. Please try again.\",\n \"invalidChallenge\": \"Verification session is invalid. Restart the login.\",\n \"invalidCode\": \"Invalid code. Try again.\",\n \"shortCode\": \"Enter the 6-digit code.\",\n \"tooManyAttempts\": \"Too many attempts. Try again later.\",\n \"verificationFailed\": \"Verification failed. Try again.\"\n },\n \"form\": {\n \"label\": \"Verification code\",\n \"placeholder\": \"123456\"\n },\n \"helper\": {\n \"default\": \"Enter the 6-digit code from your authenticator.\",\n \"withEmail\": \"Enter the 6-digit code sent to {{email}}.\"\n }\n },\n \"oidc\": {\n \"buttons\": {\n \"apple\": \"Continue with Apple\",\n \"generic\": \"Continue with single sign-on\",\n \"google\": \"Continue with Google\"\n }\n },\n \"passkey\": {\n \"actions\": {\n \"showMethods\": \"Use another method\",\n \"submit\": \"Continue with passkey\",\n \"submitting\": \"Waiting for passkey...\"\n },\n \"errors\": {\n \"challengeExpired\": \"Passkey request expired. Try again.\",\n \"emailRequired\": \"Enter your email to continue.\",\n \"failed\": \"Passkey sign-in failed.\",\n \"invalidAssertion\": \"Passkey response is invalid. Try again.\",\n \"invalidChallenge\": \"Passkey request is invalid. Try again.\",\n \"invalidEmail\": \"Enter a valid email address.\",\n \"locked\": \"Too many attempts. Try again later.\",\n \"lockedWithTime\": \"Too many attempts. Try again at {{time}}.\",\n \"notAvailable\": \"Passkeys are not available on this device.\",\n \"notFound\": \"No passkey found for this account.\"\n },\n \"form\": {\n \"emailLabel\": \"Email\",\n \"emailPlaceholder\": \"you@company.com\"\n },\n \"helper\": \"Use a passkey instead of your password.\"\n },\n \"passwordLogin\": {\n \"forgotPassword\": \"Forgot your password?\",\n \"title\": \"Sign in\"\n },\n \"passwordResetComplete\": {\n \"errors\": {\n \"invalid\": \"Reset link is invalid or expired.\",\n \"minLength\": \"Password must be at least {{minLength}} characters.\",\n \"mismatch\": \"Passwords do not match.\",\n \"missingToken\": \"Reset link is missing or invalid.\"\n },\n \"form\": {\n \"confirmLabel\": \"Confirm password\",\n \"confirmPlaceholder\": \"Re-enter your password\",\n \"description\": \"Enter a new password for your account.\",\n \"passwordLabel\": \"Password\",\n \"passwordPlaceholder\": \"Enter a new password\",\n \"submit\": \"Update password\",\n \"title\": \"New password\"\n },\n \"subtitle\": \"Choose a strong password to secure your account.\",\n \"success\": {\n \"action\": \"Back to login\",\n \"description\": \"Your password has been changed.\",\n \"helper\": \"You can now sign in with your new password.\",\n \"title\": \"Password updated\"\n },\n \"title\": \"Set a new password\"\n },\n \"passwordResetRequest\": {\n \"errors\": {\n \"emailRequired\": \"Enter an email address.\",\n \"startFailed\": \"Unable to start password reset.\"\n },\n \"form\": {\n \"description\": \"Enter the email for your account.\",\n \"emailLabel\": \"Email\",\n \"emailPlaceholder\": \"you@company.com\",\n \"submit\": \"Send reset link\"\n },\n \"sent\": {\n \"action\": \"Send another email\",\n \"description\": \"We sent a reset link to {{email}}.\",\n \"helper\": \"If you don't see it, check spam or try again.\",\n \"title\": \"Check your email\"\n },\n \"title\": \"Reset your password\"\n },\n \"verifyEmail\": {\n \"actions\": {\n \"continue\": \"Continue\",\n \"return\": \"Back to login\"\n },\n \"errors\": {\n \"invalid\": \"Verification link is invalid or expired.\",\n \"missingToken\": \"Verification link is missing or invalid.\"\n },\n \"status\": {\n \"success\": \"Email verified. You can continue.\",\n \"verifying\": \"Verifying...\",\n \"verifyingButton\": \"Verifying\"\n },\n \"subtitle\": \"Confirm your email address to continue.\",\n \"title\": \"Verify your email\"\n }\n },\n \"review\": {\n \"status\": {\n \"approved\": \"Approved\",\n \"changesRequested\": \"Changes requested\",\n \"pending\": \"Pending\",\n \"unknown\": \"Unknown\"\n }\n }\n}\n","{\n \"auth\": {\n \"acceptInvitation\": {\n \"actions\": {\n \"backToLogin\": \"Retour à la connexion\"\n },\n \"errors\": {\n \"alreadyAccepted\": \"Cette invitation a déjà été acceptée.\",\n \"default\": \"Impossible d'accepter l'invitation.\",\n \"emailMismatch\": \"Cette invitation a été envoyée à une autre adresse email.\",\n \"expired\": \"Le lien d'invitation a expiré.\",\n \"invalidToken\": \"Le lien d'invitation est invalide.\",\n \"missingToken\": \"Le lien d'invitation est manquant ou invalide.\",\n \"passwordMismatch\": \"Le mot de passe et sa confirmation ne correspondent pas.\",\n \"passwordPolicyViolation\": \"Votre mot de passe ne respecte pas la politique de sécurité.\",\n \"rateLimited\": \"Trop de tentatives. Veuillez réessayer plus tard.\"\n },\n \"form\": {\n \"confirmLabel\": \"Confirmer le mot de passe\",\n \"confirmPlaceholder\": \"Confirmez votre mot de passe\",\n \"passwordLabel\": \"Mot de passe\",\n \"passwordPlaceholder\": \"Créez un mot de passe\",\n \"submit\": \"Accepter l'invitation\"\n },\n \"mfaSubtitle\": \"Entrez le code de vérification pour continuer.\",\n \"mfaTitle\": \"Vérifiez votre identité\",\n \"status\": {\n \"success\": \"Invitation acceptée.\",\n \"workingButton\": \"Traitement...\"\n },\n \"subtitle\": \"Créez votre compte pour rejoindre.\",\n \"title\": \"Accepter l'invitation\"\n },\n \"emailCapture\": {\n \"continue\": \"Continuer\",\n \"description\": \"Entrez votre email professionnel pour continuer.\",\n \"emailLabel\": \"Email professionnel\",\n \"emailPlaceholder\": \"vous@entreprise.com\",\n \"forgotPassword\": \"Mot de passe oublié ?\"\n },\n \"loginFlow\": {\n \"errors\": {\n \"accountLocked\": \"Trop de tentatives. Veuillez réessayer plus tard.\",\n \"emailRequired\": \"Entrez une adresse email pour continuer.\",\n \"invalidCredentials\": \"Email ou mot de passe incorrect.\",\n \"invalidEmail\": \"Entrez une adresse email valide.\",\n \"passkeyUnavailable\": \"Les passkeys ne sont pas disponibles.\",\n \"rateLimited\": \"Trop de tentatives. Veuillez réessayer plus tard.\",\n \"tryAgain\": \"Une erreur est survenue. Veuillez réessayer.\"\n },\n \"methods\": {\n \"title\": \"Choisissez une méthode de connexion\"\n },\n \"passkey\": {\n \"description\": \"Utilisez la passkey associée à {{email}}.\",\n \"title\": \"Utiliser une passkey\"\n },\n \"subtitle\": {\n \"default\": \"Choisissez une méthode de connexion pour continuer.\",\n \"mfa\": \"Entrez le code de vérification pour continuer.\"\n },\n \"title\": {\n \"default\": \"Se connecter\",\n \"mfa\": \"Authentification à deux facteurs\"\n }\n },\n \"methodChooser\": {\n \"actions\": {\n \"back\": \"Retour\"\n },\n \"locked\": \"Trop de tentatives. Veuillez réessayer plus tard.\",\n \"lockedWithTime\": \"Trop de tentatives. Veuillez réessayer à {{time}}.\",\n \"methods\": {\n \"other\": \"{{method}}\",\n \"passkey\": \"Passkey\",\n \"password\": \"Mot de passe\"\n },\n \"prompt\": \"Choisissez comment vous connecter pour <strong>{{email}}</strong>.\"\n },\n \"mfa\": {\n \"actions\": {\n \"back\": \"Retour\",\n \"submit\": \"Vérifier\"\n },\n \"errors\": {\n \"expired\": \"La session de vérification a expiré. Réessayez.\",\n \"invalidChallenge\": \"La session de vérification est invalide. Reprenez la connexion.\",\n \"invalidCode\": \"Code invalide. Réessayez.\",\n \"shortCode\": \"Entrez le code à 6 chiffres.\",\n \"tooManyAttempts\": \"Trop de tentatives. Veuillez réessayer plus tard.\",\n \"verificationFailed\": \"La vérification a échoué. Réessayez.\"\n },\n \"form\": {\n \"label\": \"Code de vérification\",\n \"placeholder\": \"123456\"\n },\n \"helper\": {\n \"default\": \"Entrez le code à 6 chiffres de votre authentificateur.\",\n \"withEmail\": \"Entrez le code à 6 chiffres envoyé à {{email}}.\"\n }\n },\n \"oidc\": {\n \"buttons\": {\n \"apple\": \"Continuer avec Apple\",\n \"generic\": \"Continuer avec un SSO\",\n \"google\": \"Continuer avec Google\"\n }\n },\n \"passkey\": {\n \"actions\": {\n \"showMethods\": \"Utiliser une autre méthode\",\n \"submit\": \"Continuer avec la passkey\",\n \"submitting\": \"En attente de la passkey...\"\n },\n \"errors\": {\n \"challengeExpired\": \"La demande de passkey a expiré. Réessayez.\",\n \"emailRequired\": \"Entrez votre email pour continuer.\",\n \"failed\": \"Échec de la connexion par passkey.\",\n \"invalidAssertion\": \"La réponse de passkey est invalide. Réessayez.\",\n \"invalidChallenge\": \"La demande de passkey est invalide. Réessayez.\",\n \"invalidEmail\": \"Entrez une adresse email valide.\",\n \"locked\": \"Trop de tentatives. Veuillez réessayer plus tard.\",\n \"lockedWithTime\": \"Trop de tentatives. Veuillez réessayer à {{time}}.\",\n \"notAvailable\": \"Les passkeys ne sont pas disponibles sur cet appareil.\",\n \"notFound\": \"Aucune passkey n'est associée à ce compte.\"\n },\n \"form\": {\n \"emailLabel\": \"Email\",\n \"emailPlaceholder\": \"vous@entreprise.com\"\n },\n \"helper\": \"Utilisez une passkey à la place de votre mot de passe.\"\n },\n \"passwordLogin\": {\n \"forgotPassword\": \"Mot de passe oublié ?\",\n \"title\": \"Se connecter\"\n },\n \"passwordResetComplete\": {\n \"errors\": {\n \"invalid\": \"Le lien de réinitialisation est invalide ou expiré.\",\n \"minLength\": \"Le mot de passe doit contenir au moins {{minLength}} caractères.\",\n \"mismatch\": \"Les mots de passe ne correspondent pas.\",\n \"missingToken\": \"Le lien de réinitialisation est manquant ou invalide.\"\n },\n \"form\": {\n \"confirmLabel\": \"Confirmer le mot de passe\",\n \"confirmPlaceholder\": \"Saisissez à nouveau votre mot de passe\",\n \"description\": \"Entrez un nouveau mot de passe pour votre compte.\",\n \"passwordLabel\": \"Mot de passe\",\n \"passwordPlaceholder\": \"Entrez un nouveau mot de passe\",\n \"submit\": \"Mettre à jour le mot de passe\",\n \"title\": \"Nouveau mot de passe\"\n },\n \"subtitle\": \"Choisissez un mot de passe robuste pour sécuriser votre compte.\",\n \"success\": {\n \"action\": \"Retour à la connexion\",\n \"description\": \"Votre mot de passe a été modifié.\",\n \"helper\": \"Vous pouvez maintenant vous connecter avec votre nouveau mot de passe.\",\n \"title\": \"Mot de passe mis à jour\"\n },\n \"title\": \"Définir un nouveau mot de passe\"\n },\n \"passwordResetRequest\": {\n \"errors\": {\n \"emailRequired\": \"Entrez une adresse email.\",\n \"startFailed\": \"Impossible de démarrer la réinitialisation.\"\n },\n \"form\": {\n \"description\": \"Entrez l'email de votre compte.\",\n \"emailLabel\": \"Email\",\n \"emailPlaceholder\": \"vous@entreprise.com\",\n \"submit\": \"Envoyer le lien de réinitialisation\"\n },\n \"sent\": {\n \"action\": \"Envoyer un autre email\",\n \"description\": \"Nous avons envoyé un lien de réinitialisation à {{email}}.\",\n \"helper\": \"Si vous ne le voyez pas, vérifiez les spams ou réessayez.\",\n \"title\": \"Vérifiez votre email\"\n },\n \"title\": \"Réinitialiser votre mot de passe\"\n },\n \"verifyEmail\": {\n \"actions\": {\n \"continue\": \"Continuer\",\n \"return\": \"Retour à la connexion\"\n },\n \"errors\": {\n \"invalid\": \"Le lien de vérification est invalide ou expiré.\",\n \"missingToken\": \"Le lien de vérification est manquant ou invalide.\"\n },\n \"status\": {\n \"success\": \"Email vérifié. Vous pouvez continuer.\",\n \"verifying\": \"Vérification...\",\n \"verifyingButton\": \"Vérification\"\n },\n \"subtitle\": \"Confirmez votre adresse email pour continuer.\",\n \"title\": \"Vérifier votre email\"\n }\n },\n \"review\": {\n \"status\": {\n \"approved\": \"Approuvé\",\n \"changesRequested\": \"Modifications demandées\",\n \"pending\": \"En attente\",\n \"unknown\": \"Inconnu\"\n }\n }\n}\n","import en from './locales/en/backofficeReact.json' with { type: 'json' };\nimport fr from './locales/fr/backofficeReact.json' with { type: 'json' };\nimport sharedEn from './locales/en/shared.json' with { type: 'json' };\nimport sharedFr from './locales/fr/shared.json' with { type: 'json' };\n\nexport const backofficeReactI18nResources = {\n en: { backofficeReact: en, shared: sharedEn },\n fr: { backofficeReact: fr, shared: sharedFr },\n} as const;\n\nexport type BackofficeReactI18nResources = typeof backofficeReactI18nResources;\n","import { type JSX, type ReactNode } from 'react';\nimport * as ReactRelay from 'react-relay';\n\nimport { getEnvironment } from './environment.js';\n\nconst { RelayEnvironmentProvider } = ReactRelay;\n\ntype Props = {\n children: ReactNode;\n};\n\nexport const RelayProvider = ({ children }: Props): JSX.Element => {\n const environment = getEnvironment();\n\n return (\n <RelayEnvironmentProvider environment={environment}>\n {children}\n </RelayEnvironmentProvider>\n );\n};\n\nexport default RelayProvider;\n","/* eslint-disable no-ternary */\nimport {\n getResourcePage,\n HttpRedirect,\n r,\n type AnyRoute,\n type ResourcePage,\n type Route,\n} from '@plumile/router';\nimport * as ReactRelay from 'react-relay';\nimport type { PreloadedQuery } from 'react-relay';\nimport type { Environment, OperationType } from 'relay-runtime';\n\nimport { BACKOFFICE_LIST_DEFAULTS } from '@plumile/backoffice-core/constants.js';\nimport type {\n BackofficeEntityManifestMap,\n BackofficePreparedDetailLayoutRoute,\n BackofficePreparedDetailPageRoute,\n BackofficePreparedListRoute,\n BackofficePreparedToolRoute,\n BackofficeResolvedDetailLayoutFacetConfigBase,\n} from '@plumile/backoffice-core/types.js';\n\nimport type {\n BackofficeAuthConfig,\n BackofficeDashboardModule,\n BackofficeSidebarConfig,\n} from '../provider/types.js';\nimport {\n buildEntityGroupLookup,\n resolveSidebarGroups,\n} from '../components/backoffice/layout/sidebarUtils.js';\nimport type { BackofficeEntityRegistry } from '../provider/entityRegistry.js';\n\nconst { loadQuery, usePreloadedQuery } = ReactRelay;\n\nexport type CreateBackofficeRoutesInput = {\n basePath: string;\n entityManifest: BackofficeEntityManifestMap;\n entityRegistry: BackofficeEntityRegistry;\n sidebar?: BackofficeSidebarConfig;\n auth: BackofficeAuthConfig;\n dashboard?: BackofficeDashboardModule;\n toolsOperationPage?: ResourcePage | null;\n};\n\nexport type BackofficeRouterContext = {\n relayEnvironment: Environment;\n};\n\nexport const WrapperPageResource: ResourcePage | null = getResourcePage(\n 'WrapperPage',\n // eslint-disable-next-line arrow-body-style\n async () => ({\n default: (await import('@plumile/ui')).WrapperPage,\n }),\n);\n\nexport const BackofficeLayoutPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeLayoutPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeLayoutPage.js'),\n );\n\nexport const BackofficeEntityListPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeEntityListPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeEntityListPage.js'),\n );\n\nexport const BackofficeEntityDetailPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeEntityDetailPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeEntityDetailPage.js'),\n );\n\nexport const BackofficeEntityDetailLayoutPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeEntityDetailLayoutPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeEntityDetailLayoutPage.js'),\n );\n\nexport const BackofficeEntityDetailUnknownPageRedirectResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeEntityDetailUnknownPageRedirect',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeEntityDetailUnknownPageRedirect.js'),\n );\n\nexport const BackofficeDashboardPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeDashboardPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeDashboardPage.js'),\n );\n\nexport const BackofficeLoginPageResource: ResourcePage | null = getResourcePage(\n 'BackofficeLoginPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeLoginPage.js'),\n);\n\nexport const BackofficePasswordResetRequestPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficePasswordResetRequestPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficePasswordResetRequestPage.js'),\n );\n\nexport const BackofficePasswordResetCompletePageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficePasswordResetCompletePage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficePasswordResetCompletePage.js'),\n );\n\nexport const BackofficeVerifyEmailPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeVerifyEmailPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeVerifyEmailPage.js'),\n );\n\nexport const BackofficeAcceptInvitationPageResource: ResourcePage | null =\n getResourcePage(\n 'BackofficeAcceptInvitationPage',\n // eslint-disable-next-line arrow-body-style\n async () => import('../pages/BackofficeAcceptInvitationPage.js'),\n );\n\ntype PreparedDetailUnknownPage = {\n id: string;\n pagePath: string;\n entityManifest: BackofficeEntityManifestMap[string];\n entityConfig: BackofficeResolvedDetailLayoutFacetConfigBase;\n};\n\ntype PreparedLayout = {\n permissionsQuery: PreloadedQuery<OperationType> | null;\n authStatusQuery: PreloadedQuery<OperationType> | null;\n};\n\nconst normalizeBaseSegment = (value: string): string => {\n const trimmed = value.trim();\n if (trimmed === '' || trimmed === '/') {\n return '';\n }\n return trimmed.replace(/^\\/+|\\/+$/g, '');\n};\n\nconst normalizePath = (value: string): string => {\n const trimmed = value.trim();\n if (trimmed === '') {\n return '/';\n }\n return `/${trimmed}`.replace(/\\/+/g, '/');\n};\n\nconst buildScopedPath = (baseSegment: string, path: string): string => {\n const normalizedPath = path.replace(/^\\/+|\\/+$/g, '');\n if (baseSegment === '') {\n return normalizedPath;\n }\n if (normalizedPath === '') {\n return baseSegment;\n }\n return `${baseSegment}/${normalizedPath}`;\n};\n\nconst buildScopedAbsolutePath = (baseSegment: string, path: string): string => {\n return normalizePath(buildScopedPath(baseSegment, path));\n};\n\nconst buildRelativePath = (\n absolutePath: string,\n baseSegment: string,\n): string => {\n const normalized = normalizePath(absolutePath);\n const basePrefix = baseSegment === '' ? '' : `/${baseSegment}`;\n if (basePrefix !== '' && normalized.startsWith(basePrefix)) {\n return normalized.slice(basePrefix.length).replace(/^\\/+/, '');\n }\n return normalized.replace(/^\\/+/, '');\n};\n\nconst resolveActiveEntityIdFromRoute = (\n route: { routes: AnyRoute[] } | null,\n routeEntityIdMap: WeakMap<AnyRoute, string>,\n): string | null => {\n if (route?.routes == null) {\n return null;\n }\n for (let index = route.routes.length - 1; index >= 0; index -= 1) {\n const routeEntry = route.routes[index];\n if (routeEntry != null) {\n const entityId = routeEntityIdMap.get(routeEntry);\n if (entityId != null) {\n return entityId;\n }\n }\n }\n return null;\n};\n\nconst toSearchParams = (query: Record<string, unknown>): URLSearchParams => {\n const params = new URLSearchParams();\n Object.entries(query).forEach(([key, value]) => {\n if (value == null) {\n return;\n }\n if (Array.isArray(value)) {\n value.forEach((entry) => {\n if (entry == null) {\n return;\n }\n params.append(key, String(entry));\n });\n return;\n }\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n params.set(key, String(value));\n });\n return params;\n};\n\nconst rBackoffice = r<BackofficeRouterContext>;\n\n/**\n * Creates backoffice routes based on the provided configuration\n */\nexport function createBackofficeRoutes(\n input: CreateBackofficeRoutesInput,\n): Route<BackofficeRouterContext, any>[] {\n const { basePath, entityManifest, entityRegistry, sidebar, auth, dashboard } =\n input;\n const baseSegment = normalizeBaseSegment(basePath);\n const loginPath = buildScopedPath(baseSegment, 'login');\n const passwordResetRequestPath = buildScopedPath(baseSegment, 'login/reset');\n const passwordResetCompletePath = buildScopedPath(\n baseSegment,\n 'login/reset/complete',\n );\n const verifyEmailPath = buildScopedPath(baseSegment, 'verify-email');\n const acceptInvitationPath = buildScopedPath(\n baseSegment,\n 'accept-invitation',\n );\n const loginRedirectPath = buildScopedAbsolutePath(baseSegment, 'login');\n const entities = entityManifest;\n const groups = resolveSidebarGroups(entities, sidebar);\n const entityGroupLookup = buildEntityGroupLookup(groups);\n const entityIdToGroupId = new Map<string, string>();\n entityGroupLookup.forEach((value, entityId) => {\n entityIdToGroupId.set(entityId, value.groupId);\n });\n const routeEntityIdMap = new WeakMap<AnyRoute, string>();\n\n const permissionsQuery = sidebar?.permissionsQuery;\n const layoutPrepare = async ({\n context,\n }: {\n context: BackofficeRouterContext;\n }) => {\n const sessionAuth = await auth.session.load();\n const permissionsQueryRef =\n permissionsQuery != null\n ? loadQuery<OperationType>(\n context.relayEnvironment,\n permissionsQuery,\n {},\n )\n : null;\n const authStatusQueryRef =\n sessionAuth.authStatusQuery != null\n ? loadQuery<OperationType>(\n context.relayEnvironment,\n sessionAuth.authStatusQuery,\n {},\n { fetchPolicy: 'network-only' },\n )\n : null;\n return {\n permissionsQuery: permissionsQueryRef,\n authStatusQuery: authStatusQueryRef,\n };\n };\n\n const dashboardRoute = rBackoffice({\n path: '',\n resourcePage: BackofficeDashboardPageResource,\n prepare: async () => {\n if (dashboard == null) {\n return null;\n }\n await dashboard.load();\n return null;\n },\n render: ({ Component }) => {\n if (Component == null) {\n return null;\n }\n return <Component />;\n },\n });\n\n const listEntityEntries = Object.values(entityManifest).filter((entity) => {\n return entity.kind === 'list-detail';\n });\n\n const entityRoutes = listEntityEntries.map((entityManifestItem) => {\n const listPath = entityManifestItem.routes.list;\n const listRelative = buildRelativePath(listPath, baseSegment);\n const children: Route<any, any>[] = [];\n\n if (entityManifestItem.hasList) {\n const listRoute = rBackoffice({\n path: '',\n resourcePage: BackofficeEntityListPageResource,\n prepare: async ({ context, query }) => {\n const entityModule = await entityRegistry.loadListEntity(\n entityManifestItem.id,\n );\n const { config } = entityModule;\n const { list, listUrlCodec, listDefaults } = config;\n if (listUrlCodec == null || listDefaults == null) {\n throw new Error(\n `Backoffice entity ${entityManifestItem.id} does not expose a list configuration.`,\n );\n }\n const params = toSearchParams(query);\n const state = listUrlCodec.parse(params);\n const { pageSize } = BACKOFFICE_LIST_DEFAULTS;\n const variablesBase = {\n where: state.where,\n sort: state.sort ?? listDefaults.sort,\n count: pageSize,\n cursor: null,\n };\n const variables =\n list.buildVariables != null\n ? list.buildVariables(variablesBase)\n : variablesBase;\n const queryRef = loadQuery<OperationType>(\n context.relayEnvironment,\n list.query,\n variables,\n );\n const prepared: BackofficePreparedListRoute = {\n entityId: entityManifestItem.id,\n entityManifest: entityManifestItem,\n entityConfig: config,\n query: queryRef,\n };\n return prepared;\n },\n render: ({ prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedRoute = prepared as BackofficePreparedListRoute;\n return (\n <Component\n entityManifest={preparedRoute.entityManifest}\n config={preparedRoute.entityConfig}\n prepared={preparedRoute}\n />\n );\n },\n });\n routeEntityIdMap.set(listRoute, entityManifestItem.id);\n children.push(listRoute);\n }\n\n const detailLayoutRoute = rBackoffice({\n path: ':id',\n resourcePage: BackofficeEntityDetailLayoutPageResource,\n prepare: async ({ context, variables }) => {\n const entityModule = await entityRegistry.loadDetailLayoutEntity(\n entityManifestItem.id,\n );\n const { config } = entityModule;\n const rawId = String(variables.id ?? '');\n const layoutBuildResult =\n config.layoutPage.buildVariables != null\n ? config.layoutPage.buildVariables({\n id: rawId,\n })\n : { variables: { id: rawId } };\n const layoutQueryRef = loadQuery<OperationType>(\n context.relayEnvironment,\n config.layoutPage.query,\n layoutBuildResult.variables as never,\n );\n return {\n entityId: entityManifestItem.id,\n entityManifest: entityManifestItem,\n entityConfig: config,\n layoutQuery: layoutQueryRef,\n id: rawId,\n } satisfies BackofficePreparedDetailLayoutRoute;\n },\n render: ({ children: detailChildren, prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedRoute = prepared as BackofficePreparedDetailLayoutRoute;\n return (\n <Component\n entityManifest={preparedRoute.entityManifest}\n config={preparedRoute.entityConfig}\n prepared={preparedRoute}\n >\n {detailChildren}\n </Component>\n );\n },\n children: [\n rBackoffice({\n path: '',\n resourcePage: WrapperPageResource,\n prepare: ({ variables }) => {\n const rawId = String(variables.id ?? '').trim();\n return {\n redirectTo:\n rawId === ''\n ? null\n : entityManifestItem.routes.detailPage(\n rawId,\n entityManifestItem.defaultDetailPageId ?? 'overview',\n ),\n };\n },\n render: ({ prepared }) => {\n const redirectTo =\n (prepared as { redirectTo?: string | null }).redirectTo ?? null;\n if (redirectTo == null) {\n return null;\n }\n throw new HttpRedirect(redirectTo);\n },\n }),\n ...(entityManifestItem.detailPages ?? []).map((pageManifest) => {\n return rBackoffice({\n path: pageManifest.pathSegment,\n resourcePage: BackofficeEntityDetailPageResource,\n prepare: async ({ context, variables }) => {\n const rawId = String(variables.id ?? '');\n const pageModule = await entityRegistry.loadDetailPageEntity(\n entityManifestItem.id,\n pageManifest.id,\n );\n const pageBuildResult =\n pageModule.config.page.buildVariables != null\n ? pageModule.config.page.buildVariables({\n id: rawId,\n })\n : { variables: { id: rawId } };\n const pageQueryRef = loadQuery<OperationType>(\n context.relayEnvironment,\n pageModule.config.page.query,\n pageBuildResult.variables as never,\n );\n return {\n entityId: entityManifestItem.id,\n entityManifest: entityManifestItem,\n entityConfig:\n pageModule.config as unknown as BackofficeResolvedDetailLayoutFacetConfigBase,\n detailId: pageBuildResult.detailId,\n id: rawId,\n pageConfig: pageModule.config,\n pageQuery: pageQueryRef,\n pageId: pageManifest.id,\n pagePath: pageManifest.pathSegment,\n } satisfies BackofficePreparedDetailPageRoute;\n },\n render: ({ prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedRoute =\n prepared as BackofficePreparedDetailPageRoute;\n return (\n <Component\n entityManifest={preparedRoute.entityManifest}\n config={preparedRoute.entityConfig}\n prepared={preparedRoute}\n />\n );\n },\n });\n }),\n rBackoffice({\n path: ':pagePath',\n resourcePage: BackofficeEntityDetailUnknownPageRedirectResource,\n prepare: async ({ variables }) => {\n const entityModule = await entityRegistry.loadDetailLayoutEntity(\n entityManifestItem.id,\n );\n const rawId = String(variables.id ?? '');\n const rawPagePath = String(variables.pagePath ?? '');\n return {\n entityManifest: entityManifestItem,\n entityConfig: entityModule.config,\n id: rawId,\n pagePath: rawPagePath,\n };\n },\n render: ({ prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedDetail = prepared as PreparedDetailUnknownPage;\n return (\n <Component\n entityManifest={preparedDetail.entityManifest}\n config={preparedDetail.entityConfig}\n prepared={preparedDetail}\n />\n );\n },\n }),\n ],\n });\n routeEntityIdMap.set(detailLayoutRoute, entityManifestItem.id);\n children.push(detailLayoutRoute);\n\n return rBackoffice({\n path: listRelative,\n children,\n resourcePage: WrapperPageResource,\n });\n });\n\n const toolsRoutes = Object.values(entityManifest)\n .filter((entity) => {\n return entity.kind === 'tool';\n })\n .map((toolManifest) => {\n const toolRelative = buildRelativePath(\n toolManifest.routes.list,\n baseSegment,\n );\n const toolRoute = rBackoffice({\n path: toolRelative,\n resourcePage: input.toolsOperationPage ?? null,\n prepare: async () => {\n const toolModule = await entityRegistry.loadToolEntity(\n toolManifest.id,\n );\n return {\n entityId: toolManifest.id,\n entityManifest: toolManifest,\n entityConfig: toolModule.config,\n } satisfies BackofficePreparedToolRoute;\n },\n render: ({ prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedTool = prepared as BackofficePreparedToolRoute;\n return (\n <Component\n entityManifest={preparedTool.entityManifest}\n operation={preparedTool.entityConfig.tool.operation}\n toolId={preparedTool.entityConfig.id}\n />\n );\n },\n });\n routeEntityIdMap.set(toolRoute, toolManifest.id);\n return toolRoute;\n });\n\n const layoutRoute = rBackoffice({\n path: baseSegment,\n resourcePage: BackofficeLayoutPageResource,\n prepare: layoutPrepare,\n render: ({ children, prepared, route, Component }) => {\n if (Component == null) {\n return null;\n }\n const preparedLayout = prepared as PreparedLayout | undefined;\n const activeEntityId = resolveActiveEntityIdFromRoute(\n route,\n routeEntityIdMap,\n );\n const activeGroupId =\n activeEntityId != null\n ? (entityIdToGroupId.get(activeEntityId) ?? null)\n : null;\n let authStatus: {\n isLoggedIn?: boolean | null;\n me?: {\n id: string;\n firstName: string;\n lastName: string;\n email: string;\n initials: string;\n } | null;\n } | null = null;\n const authStatusQuery = auth.session.get()?.authStatusQuery ?? null;\n if (authStatusQuery != null && preparedLayout?.authStatusQuery != null) {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const data = usePreloadedQuery(\n authStatusQuery,\n preparedLayout.authStatusQuery,\n );\n authStatus = data as {\n isLoggedIn?: boolean | null;\n me?: {\n id: string;\n firstName: string;\n lastName: string;\n email: string;\n initials: string;\n } | null;\n };\n const { isLoggedIn } = authStatus;\n if (!isLoggedIn) {\n throw new HttpRedirect(loginRedirectPath);\n }\n }\n const layoutNode = (\n <Component\n permissionsQuery={sidebar?.permissionsQuery}\n prepared={preparedLayout?.permissionsQuery ?? null}\n authStatus={authStatus}\n activeGroupId={activeGroupId}\n >\n {children}\n </Component>\n );\n return layoutNode;\n },\n children: [dashboardRoute, ...entityRoutes, ...toolsRoutes],\n });\n\n const routes: Route<BackofficeRouterContext, any>[] = [\n rBackoffice({\n path: loginPath,\n resourcePage: BackofficeLoginPageResource,\n prepare: async ({ context }) => {\n const loginAuth = await auth.login.load();\n const queryRef = loadQuery<OperationType>(\n context.relayEnvironment,\n loginAuth.loginQuery,\n {},\n );\n return { query: queryRef };\n },\n render: ({ prepared, Component }) => {\n if (Component == null) {\n return null;\n }\n return (\n <Component\n prepared={prepared as { query: PreloadedQuery<OperationType> }}\n />\n );\n },\n }),\n rBackoffice({\n path: passwordResetRequestPath,\n resourcePage: BackofficePasswordResetRequestPageResource,\n prepare: async () => {\n await auth.passwordResetRequest.load();\n return null;\n },\n render: ({ Component }) => {\n if (Component == null) {\n return null;\n }\n return <Component />;\n },\n }),\n rBackoffice({\n path: passwordResetCompletePath,\n resourcePage: BackofficePasswordResetCompletePageResource,\n prepare: async () => {\n await auth.passwordResetComplete.load();\n return null;\n },\n render: ({ Component }) => {\n if (Component == null) {\n return null;\n }\n return <Component />;\n },\n }),\n rBackoffice({\n path: verifyEmailPath,\n resourcePage: BackofficeVerifyEmailPageResource,\n prepare: async () => {\n await auth.verifyEmail.load();\n return null;\n },\n render: ({ Component }) => {\n if (Component == null) {\n return null;\n }\n return <Component />;\n },\n }),\n ];\n\n if (auth.hasAcceptInvitation === true || auth.acceptInvitation != null) {\n routes.push(\n rBackoffice({\n path: acceptInvitationPath,\n resourcePage: BackofficeAcceptInvitationPageResource,\n prepare: async () => {\n await auth.acceptInvitation?.load();\n return null;\n },\n render: ({ Component }) => {\n if (Component == null) {\n return null;\n }\n return <Component />;\n },\n }),\n );\n }\n\n routes.push(layoutRoute);\n\n return routes;\n}\n\nexport default createBackofficeRoutes;\n","import { sprinkles } from '@plumile/ui';\n\nexport const root = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: 'screen',\n width: 'full',\n gap: 4,\n backgroundColor: 'surfaceSecondary',\n color: 'textSecondary',\n});\n\nexport const label = sprinkles({\n color: 'textMuted',\n});\n","import { type JSX } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport { Spinner } from '@plumile/ui';\n\nimport * as styles from './backofficeRouteFallback.css.js';\n\nexport const BackofficeRouteFallback = (): JSX.Element => {\n const { t, i18n } = useTranslation('backofficeReact', {\n useSuspense: false,\n });\n let label = 'Loading...';\n if (i18n.isInitialized) {\n label = t('common.loading');\n }\n\n return (\n <div\n className={styles.root}\n role=\"status\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n >\n <Spinner size={28} />\n <div className={styles.label}>{label}</div>\n </div>\n );\n};\n\nexport default BackofficeRouteFallback;\n","import { keyframes, style } from '@vanilla-extract/css';\n\nimport { sprinkles, vars } from '@plumile/ui';\n\nconst slide = keyframes({\n '0%': { transform: 'translateX(-120%)' },\n '60%': { transform: 'translateX(30%)' },\n '100%': { transform: 'translateX(120%)' },\n});\n\nexport const root = style([\n sprinkles({\n backgroundColor: 'surfaceMuted',\n position: 'fixed',\n top: 0,\n left: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n width: 'full',\n zIndex: 50,\n }),\n {\n height: '3px',\n },\n]);\n\nexport const bar = style([\n sprinkles({\n height: 'full',\n width: '2/5',\n }),\n {\n background: `linear-gradient(90deg, ${vars.colors.primaryLight} 0%, ${vars.colors.primary} 60%, ${vars.colors.primaryLight} 100%)`,\n animation: `${slide} 1.1s ease-in-out infinite`,\n },\n]);\n","import { type JSX } from 'react';\n\nimport * as styles from './backofficeRoutePendingBar.css.js';\n\nexport const BackofficeRoutePendingBar = (): JSX.Element => {\n return (\n <div className={styles.root} aria-hidden=\"true\">\n <div className={styles.bar} />\n </div>\n );\n};\n\nexport default BackofficeRoutePendingBar;\n","import {\n resolveBackofficeLoadedFacetModule,\n type ResolveBackofficeEntityOptions,\n} from '@plumile/backoffice-core/resolve.js';\nimport type {\n BackofficeEntityFacetModuleCacheEntry,\n BackofficeEntityLoadMode,\n BackofficeEntityManifestMap,\n BackofficeListDetailFacetLoaderMap,\n BackofficeToolFacetLoaderMap,\n BackofficeResolvedDetailLayoutFacetModule,\n BackofficeResolvedDetailPageFacetModule,\n BackofficeResolvedListFacetModule,\n BackofficeResolvedPickerFacetModule,\n BackofficeResolvedToolFacetModule,\n} from '@plumile/backoffice-core/types.js';\n\nexport type BackofficeEntityRegistry = {\n getManifest: (entityId: string) => BackofficeEntityManifestMap[string] | null;\n getLoadedListEntity: (\n entityId: string,\n ) => BackofficeResolvedListFacetModule | null;\n getLoadedPickerEntity: (\n entityId: string,\n ) => BackofficeResolvedPickerFacetModule | null;\n getLoadedDetailLayoutEntity: (\n entityId: string,\n ) => BackofficeResolvedDetailLayoutFacetModule | null;\n getLoadedDetailPageEntity: (\n entityId: string,\n pageId: string,\n ) => BackofficeResolvedDetailPageFacetModule | null;\n getLoadedToolEntity: (\n entityId: string,\n ) => BackofficeResolvedToolFacetModule | null;\n loadListEntity: (\n entityId: string,\n options?: { mode?: BackofficeEntityLoadMode },\n ) => Promise<BackofficeResolvedListFacetModule>;\n loadPickerEntity: (\n entityId: string,\n options?: { mode?: BackofficeEntityLoadMode },\n ) => Promise<BackofficeResolvedPickerFacetModule>;\n loadDetailLayoutEntity: (\n entityId: string,\n options?: { mode?: BackofficeEntityLoadMode },\n ) => Promise<BackofficeResolvedDetailLayoutFacetModule>;\n loadDetailPageEntity: (\n entityId: string,\n pageId: string,\n options?: { mode?: BackofficeEntityLoadMode },\n ) => Promise<BackofficeResolvedDetailPageFacetModule>;\n loadToolEntity: (\n entityId: string,\n options?: { mode?: BackofficeEntityLoadMode },\n ) => Promise<BackofficeResolvedToolFacetModule>;\n};\n\nconst DEFAULT_LOAD_MODE: BackofficeEntityLoadMode = 'cache-first';\nconst DETAIL_PAGE_FACET_KIND = 'detail-page';\nconst DETAIL_LAYOUT_FACET_KIND = 'detail-layout';\nconst LIST_FACET_KIND = 'list';\nconst PICKER_FACET_KIND = 'picker';\nconst TOOL_FACET_KIND = 'tool';\n\nconst hasListFacets = (\n manifestItem: BackofficeEntityManifestMap[string],\n): manifestItem is BackofficeEntityManifestMap[string] & {\n facets: BackofficeListDetailFacetLoaderMap;\n} => {\n return manifestItem.kind === 'list-detail';\n};\n\nconst hasToolFacet = (\n manifestItem: BackofficeEntityManifestMap[string],\n): manifestItem is BackofficeEntityManifestMap[string] & {\n facets: BackofficeToolFacetLoaderMap;\n} => {\n return manifestItem.kind === 'tool';\n};\n\nconst buildFacetCacheKey = (\n entityId: string,\n kind: 'list' | 'picker' | 'detail-layout' | 'detail-page' | 'tool',\n pageId?: string,\n): string => {\n if (kind === DETAIL_PAGE_FACET_KIND) {\n return `${entityId}:${kind}:${pageId ?? ''}`;\n }\n return `${entityId}:${kind}`;\n};\n\nconst toError = (error: unknown): Error => {\n if (error instanceof Error) {\n return error;\n }\n return new Error(String(error));\n};\n\nconst getLoadedModule = <TModule>(\n entry: BackofficeEntityFacetModuleCacheEntry | undefined,\n): TModule | null => {\n if (entry?.status !== 'loaded' || entry.module == null) {\n return null;\n }\n return entry.module as TModule;\n};\n\nconst resolveManifest = (\n manifest: BackofficeEntityManifestMap,\n entityId: string,\n): BackofficeEntityManifestMap[string] => {\n const manifestItem = manifest[entityId];\n if (manifestItem == null) {\n throw new Error(`Unknown backoffice entity: ${entityId}`);\n }\n return manifestItem;\n};\n\nexport const createBackofficeEntityRegistry = (\n manifest: BackofficeEntityManifestMap,\n options: ResolveBackofficeEntityOptions,\n): BackofficeEntityRegistry => {\n const cache = new Map<string, BackofficeEntityFacetModuleCacheEntry>();\n\n const getManifest: BackofficeEntityRegistry['getManifest'] = (entityId) => {\n return manifest[entityId] ?? null;\n };\n\n const loadFacet = async <TModule>(\n cacheKey: string,\n loadOptions: { mode?: BackofficeEntityLoadMode } | undefined,\n loader: () => Promise<TModule>,\n ): Promise<TModule> => {\n const resolvedMode = loadOptions?.mode ?? DEFAULT_LOAD_MODE;\n\n if (resolvedMode === 'cache-first') {\n const loadedModule = getLoadedModule<TModule>(cache.get(cacheKey));\n if (loadedModule != null) {\n return loadedModule;\n }\n\n const existing = cache.get(cacheKey);\n if (existing?.status === 'loading' && existing.promise != null) {\n return existing.promise as Promise<TModule>;\n }\n }\n\n const promise = loader()\n .then((module) => {\n cache.set(cacheKey, {\n status: 'loaded',\n module: module as never,\n });\n return module;\n })\n .catch((error: unknown) => {\n const resolvedError = toError(error);\n cache.set(cacheKey, {\n status: 'error',\n error: resolvedError,\n });\n throw resolvedError;\n });\n\n cache.set(cacheKey, {\n status: 'loading',\n promise: promise as never,\n });\n\n return promise;\n };\n\n const getLoadedListEntity: BackofficeEntityRegistry['getLoadedListEntity'] = (\n entityId,\n ) => {\n return getLoadedModule(\n cache.get(buildFacetCacheKey(entityId, LIST_FACET_KIND)),\n );\n };\n\n const getLoadedPickerEntity: BackofficeEntityRegistry['getLoadedPickerEntity'] =\n (entityId) => {\n return getLoadedModule(\n cache.get(buildFacetCacheKey(entityId, PICKER_FACET_KIND)),\n );\n };\n\n const getLoadedDetailLayoutEntity: BackofficeEntityRegistry['getLoadedDetailLayoutEntity'] =\n (entityId) => {\n return getLoadedModule(\n cache.get(buildFacetCacheKey(entityId, DETAIL_LAYOUT_FACET_KIND)),\n );\n };\n\n const getLoadedDetailPageEntity: BackofficeEntityRegistry['getLoadedDetailPageEntity'] =\n (entityId, pageId) => {\n return getLoadedModule(\n cache.get(buildFacetCacheKey(entityId, DETAIL_PAGE_FACET_KIND, pageId)),\n );\n };\n\n const getLoadedToolEntity: BackofficeEntityRegistry['getLoadedToolEntity'] = (\n entityId,\n ) => {\n return getLoadedModule(\n cache.get(buildFacetCacheKey(entityId, TOOL_FACET_KIND)),\n );\n };\n\n const loadListEntity: BackofficeEntityRegistry['loadListEntity'] = async (\n entityId,\n loadOptions,\n ) => {\n const manifestItem = resolveManifest(manifest, entityId);\n if (!hasListFacets(manifestItem)) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a list facet.`,\n );\n }\n const facetLoader = manifestItem.facets.list;\n if (facetLoader == null) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a list facet.`,\n );\n }\n return loadFacet(\n buildFacetCacheKey(entityId, LIST_FACET_KIND),\n loadOptions,\n async () => {\n const loaded = await facetLoader();\n const resolved = resolveBackofficeLoadedFacetModule(\n manifestItem,\n loaded,\n options,\n );\n if (resolved.kind !== LIST_FACET_KIND) {\n throw new Error(\n `Backoffice entity ${entityId} did not resolve to a list facet.`,\n );\n }\n return resolved;\n },\n );\n };\n\n const loadPickerEntity: BackofficeEntityRegistry['loadPickerEntity'] = async (\n entityId,\n loadOptions,\n ) => {\n const manifestItem = resolveManifest(manifest, entityId);\n if (!hasListFacets(manifestItem)) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a picker facet.`,\n );\n }\n const facetLoader = manifestItem.facets.picker;\n if (facetLoader == null) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a picker facet.`,\n );\n }\n return loadFacet(\n buildFacetCacheKey(entityId, PICKER_FACET_KIND),\n loadOptions,\n async () => {\n const loaded = await facetLoader();\n const resolved = resolveBackofficeLoadedFacetModule(\n manifestItem,\n loaded,\n options,\n );\n if (resolved.kind !== PICKER_FACET_KIND) {\n throw new Error(\n `Backoffice entity ${entityId} did not resolve to a picker facet.`,\n );\n }\n return resolved;\n },\n );\n };\n\n const loadDetailLayoutEntity: BackofficeEntityRegistry['loadDetailLayoutEntity'] =\n async (entityId, loadOptions) => {\n const manifestItem = resolveManifest(manifest, entityId);\n if (!hasListFacets(manifestItem)) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a detail-layout facet.`,\n );\n }\n return loadFacet(\n buildFacetCacheKey(entityId, DETAIL_LAYOUT_FACET_KIND),\n loadOptions,\n async () => {\n const loaded = await manifestItem.facets.detailLayout();\n const resolved = resolveBackofficeLoadedFacetModule(\n manifestItem,\n loaded,\n options,\n );\n if (resolved.kind !== DETAIL_LAYOUT_FACET_KIND) {\n throw new Error(\n `Backoffice entity ${entityId} did not resolve to a detail-layout facet.`,\n );\n }\n return resolved;\n },\n );\n };\n\n const loadDetailPageEntity: BackofficeEntityRegistry['loadDetailPageEntity'] =\n async (entityId, pageId, loadOptions) => {\n const manifestItem = resolveManifest(manifest, entityId);\n if (!hasListFacets(manifestItem)) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a detail-page facet.`,\n );\n }\n return loadFacet(\n buildFacetCacheKey(entityId, DETAIL_PAGE_FACET_KIND, pageId),\n loadOptions,\n async () => {\n const loaded = await manifestItem.facets.detailPage(pageId);\n const resolved = resolveBackofficeLoadedFacetModule(\n manifestItem,\n loaded,\n options,\n );\n if (resolved.kind !== DETAIL_PAGE_FACET_KIND) {\n throw new Error(\n `Backoffice entity ${entityId} did not resolve to a detail-page facet.`,\n );\n }\n return resolved;\n },\n );\n };\n\n const loadToolEntity: BackofficeEntityRegistry['loadToolEntity'] = async (\n entityId,\n loadOptions,\n ) => {\n const manifestItem = resolveManifest(manifest, entityId);\n if (!hasToolFacet(manifestItem)) {\n throw new Error(\n `Backoffice entity ${entityId} does not expose a tool facet.`,\n );\n }\n return loadFacet(\n buildFacetCacheKey(entityId, TOOL_FACET_KIND),\n loadOptions,\n async () => {\n const loaded = await manifestItem.facets.tool();\n const resolved = resolveBackofficeLoadedFacetModule(\n manifestItem,\n loaded,\n options,\n );\n if (resolved.kind !== TOOL_FACET_KIND) {\n throw new Error(\n `Backoffice entity ${entityId} did not resolve to a tool facet.`,\n );\n }\n return resolved;\n },\n );\n };\n\n return {\n getManifest,\n getLoadedListEntity,\n getLoadedPickerEntity,\n getLoadedDetailLayoutEntity,\n getLoadedDetailPageEntity,\n getLoadedToolEntity,\n loadListEntity,\n loadPickerEntity,\n loadDetailLayoutEntity,\n loadDetailPageEntity,\n loadToolEntity,\n };\n};\n\nexport default createBackofficeEntityRegistry;\n","import { StrictMode, useEffect, useMemo, type JSX } from 'react';\nimport {\n createInstance,\n type Resource,\n type ResourceKey,\n type ResourceLanguage,\n} from 'i18next';\nimport { I18nextProvider } from 'react-i18next';\nimport { createRouter, RouterRenderer, RoutingContext } from '@plumile/router';\nimport { ThemeProvider } from '@plumile/ui';\n\nimport { type BackofficeEntityManifestMap } from '@plumile/backoffice-core/types.js';\n\nimport { createI18nInstance } from '../i18n/createI18nInstance.js';\nimport { mergeResourceLanguages } from '../i18n/mergeResourceLanguages.js';\nimport { backofficeReactI18nResources } from '../i18n/resources.js';\nimport { RelayProvider } from '../relay/RelayProvider.js';\nimport { useRelayEnvironment } from '../relay/useRelayEnvironment.js';\nimport { configureRelayEnvironment } from '../relay/environment.js';\nimport { BackofficeConfigProvider } from './BackofficeConfigContext.js';\nimport { createBackofficeRoutes } from '../router/createBackofficeRoutes.js';\nimport type { BackofficeProviderProps } from './types.js';\nimport { BackofficeRouteFallback } from '../components/backoffice/routing/BackofficeRouteFallback.js';\nimport { BackofficeRoutePendingBar } from '../components/backoffice/routing/BackofficeRoutePendingBar.js';\nimport { createBackofficeEntityRegistry } from './entityRegistry.js';\n\nconst normalizeAbsolutePath = (value: string): string => {\n if (value.trim() === '' || value === '/') {\n return '/';\n }\n if (!value.startsWith('/')) {\n return `/${value}`;\n }\n if (value.endsWith('/')) {\n return value.slice(0, -1);\n }\n return value;\n};\n\nconst prefixRoutePath = (basePath: string, value: string): string => {\n const normalizedPath = normalizeAbsolutePath(value);\n const normalizedBasePath = normalizeAbsolutePath(basePath);\n if (normalizedBasePath === '/') {\n return normalizedPath;\n }\n if (\n normalizedPath === normalizedBasePath ||\n normalizedPath.startsWith(`${normalizedBasePath}/`)\n ) {\n return normalizedPath;\n }\n if (normalizedPath === '/') {\n return normalizedBasePath;\n }\n return `${normalizedBasePath}${normalizedPath}`;\n};\n\nconst resolveEntityManifest = (\n manifest: BackofficeEntityManifestMap,\n basePath: string,\n): BackofficeEntityManifestMap => {\n return Object.fromEntries(\n Object.entries(manifest).map(([entityId, item]) => {\n return [\n entityId,\n {\n ...item,\n routes: {\n list: prefixRoutePath(basePath, item.routes.list),\n detail: (id: string) => {\n return prefixRoutePath(basePath, item.routes.detail(id));\n },\n detailPage: (id: string, pageId: string) => {\n return prefixRoutePath(\n basePath,\n item.routes.detailPage(id, pageId),\n );\n },\n },\n } satisfies BackofficeEntityManifestMap[string],\n ] as const;\n }),\n );\n};\n\nconst asResourceLanguage = (\n value: ResourceKey | undefined,\n): ResourceLanguage => {\n if (typeof value === 'object' && !Array.isArray(value)) {\n return value as ResourceLanguage;\n }\n return {};\n};\n\ntype RouterShellProps = {\n routes: ReturnType<typeof createBackofficeRoutes>;\n instrumentations?: BackofficeProviderProps['instrumentations'];\n};\n\nconst RouterShell = ({\n routes,\n instrumentations,\n}: RouterShellProps): JSX.Element => {\n const relayEnvironment = useRelayEnvironment();\n\n const routerContext = useMemo(() => {\n return { relayEnvironment };\n }, [relayEnvironment]);\n\n const router = useMemo(() => {\n return createRouter(routes, {\n context: routerContext,\n instrumentations,\n });\n }, [instrumentations, routes, routerContext]);\n\n useEffect(() => {\n return () => {\n router.cleanup();\n };\n }, [router]);\n\n return (\n <RoutingContext.Provider value={router.context}>\n <RouterRenderer\n enableTransition\n fallback={<BackofficeRouteFallback />}\n pending={<BackofficeRoutePendingBar />}\n />\n </RoutingContext.Provider>\n );\n};\n\nexport const BackofficeProvider = (\n props: BackofficeProviderProps,\n): JSX.Element => {\n const basePath = normalizeAbsolutePath(props.basePath ?? '/');\n\n const entityManifest = useMemo(() => {\n return resolveEntityManifest(props.entityManifest, basePath);\n }, [basePath, props.entityManifest]);\n const entityRegistry = useMemo(() => {\n return createBackofficeEntityRegistry(entityManifest, { basePath });\n }, [basePath, entityManifest]);\n\n const graphQLConfig = props.graphql;\n\n useEffect(() => {\n const httpUrl = graphQLConfig.httpUrl ?? graphQLConfig.endpoint;\n const wsUrl = graphQLConfig.wsUrl ?? graphQLConfig.wsEndpoint;\n configureRelayEnvironment({\n httpUrl,\n wsUrl,\n getDataId: graphQLConfig.getDataId,\n logEvents: graphQLConfig.logEvents,\n getAuthHeaders: graphQLConfig.getAuthHeaders,\n });\n }, [\n graphQLConfig.endpoint,\n graphQLConfig.getAuthHeaders,\n graphQLConfig.getDataId,\n graphQLConfig.httpUrl,\n graphQLConfig.logEvents,\n graphQLConfig.wsEndpoint,\n graphQLConfig.wsUrl,\n ]);\n\n const mergedResources = useMemo(() => {\n const appResources: Resource = props.i18n?.resources ?? {};\n const backofficeReactResources = backofficeReactI18nResources as Resource;\n const locales = new Set([\n ...Object.keys(backofficeReactResources),\n ...Object.keys(appResources),\n ]);\n\n const output: Resource = {};\n\n locales.forEach((locale) => {\n const packageLocale = asResourceLanguage(\n backofficeReactResources[locale],\n );\n const appLocale = asResourceLanguage(appResources[locale]);\n output[locale] = {\n ...packageLocale,\n ...appLocale,\n backofficeReact: mergeResourceLanguages(\n asResourceLanguage(packageLocale.backofficeReact),\n asResourceLanguage(appLocale.backofficeReact),\n ),\n shared: mergeResourceLanguages(\n asResourceLanguage(packageLocale.shared),\n asResourceLanguage(appLocale.shared),\n ),\n };\n });\n\n return output;\n }, [props.i18n?.resources]);\n\n const i18nInstance = useMemo(() => {\n return props.i18n?.instance ?? createInstance();\n }, [props.i18n?.instance]);\n\n useEffect(() => {\n const initOptions = props.i18n?.initOptions ?? {};\n const defaultNs = initOptions.defaultNS ?? 'translations';\n const ns = initOptions.ns ?? [\n 'backofficeReact',\n 'translations',\n 'shared',\n 'ui',\n ];\n createI18nInstance({\n resources: mergedResources,\n lng: props.i18n?.lng,\n fallbackLng: props.i18n?.fallbackLng,\n initOptions: {\n ...initOptions,\n defaultNS: defaultNs,\n ns,\n },\n instance: i18nInstance,\n useLanguageDetector: props.i18n?.useLanguageDetector,\n detection: props.i18n?.detection,\n // eslint-disable-next-line no-console\n }).catch(console.error);\n }, [\n i18nInstance,\n mergedResources,\n props.i18n?.initOptions,\n props.i18n?.detection,\n props.i18n?.fallbackLng,\n props.i18n?.lng,\n props.i18n?.useLanguageDetector,\n ]);\n\n const configValue = useMemo(() => {\n return {\n basePath,\n entities: entityManifest,\n entityManifest,\n entityRegistry,\n filterColumnAliases: props.filterColumnAliases,\n sidebar: props.sidebar,\n dashboard: props.dashboard,\n auth: props.auth,\n graphql: props.graphql,\n };\n }, [\n basePath,\n props.auth,\n props.dashboard,\n entityManifest,\n entityRegistry,\n props.filterColumnAliases,\n props.graphql,\n props.sidebar,\n ]);\n\n const routes = useMemo(() => {\n return createBackofficeRoutes({\n basePath,\n entityManifest,\n entityRegistry,\n sidebar: props.sidebar,\n auth: props.auth,\n dashboard: props.dashboard,\n toolsOperationPage: props.toolsOperationPage,\n });\n }, [\n basePath,\n entityManifest,\n entityRegistry,\n props.auth,\n props.dashboard,\n props.toolsOperationPage,\n props.sidebar,\n ]);\n\n return (\n <StrictMode>\n <I18nextProvider i18n={i18nInstance}>\n <ThemeProvider>\n <RelayProvider>\n <BackofficeConfigProvider value={configValue}>\n <RouterShell\n routes={routes}\n instrumentations={props.instrumentations}\n />\n </BackofficeConfigProvider>\n </RelayProvider>\n </ThemeProvider>\n </I18nextProvider>\n </StrictMode>\n );\n};\n\nexport default BackofficeProvider;\n","export type BackofficeLazyValue<TValue> = {\n get: () => TValue | null;\n load: () => Promise<TValue>;\n};\n\nexport const createBackofficeLazyValue = <TValue>(\n loader: () => Promise<TValue>,\n): BackofficeLazyValue<TValue> => {\n let loadedValue: TValue | null = null;\n let inFlightPromise: Promise<TValue> | null = null;\n\n return {\n get: () => {\n return loadedValue;\n },\n load: async () => {\n if (loadedValue != null) {\n return loadedValue;\n }\n if (inFlightPromise != null) {\n return inFlightPromise;\n }\n\n inFlightPromise = loader()\n .then((value) => {\n loadedValue = value;\n return value;\n })\n .finally(() => {\n inFlightPromise = null;\n });\n\n return inFlightPromise;\n },\n };\n};\n\nexport default createBackofficeLazyValue;\n","import type {\n BackofficeFilterSpec,\n BackofficeResolvedListFacetConfig,\n} from '@plumile/backoffice-core/types.js';\n\n/** Compare two optional path arrays for equality. */\nconst arePathsEqual = (\n left?: readonly string[],\n right?: readonly string[],\n): boolean => {\n const leftPath = left ?? [];\n const rightPath = right ?? [];\n if (leftPath.length !== rightPath.length) {\n return false;\n }\n return leftPath.every((segment, index) => {\n return segment === rightPath[index];\n });\n};\n\n/** Find a filter spec by its where key and optional path. */\nexport function resolveFilterForWhereKey<\n Where extends Record<string, unknown>,\n Sort extends string,\n>(\n config: BackofficeResolvedListFacetConfig<Where, Sort>,\n whereKey: string,\n path?: readonly string[],\n): BackofficeFilterSpec<Where> | null {\n const { filters } = config.list;\n const match = filters.find((filter) => {\n return (\n String(filter.whereKey ?? filter.id) === whereKey &&\n arePathsEqual(filter.path, path)\n );\n });\n return match ?? null;\n}\n\n/** Resolve a filter spec from a column id, alias, or contains field. */\nexport function resolveFilterForColumn<\n Where extends Record<string, unknown>,\n Sort extends string,\n>(\n config: BackofficeResolvedListFacetConfig<Where, Sort>,\n columnId: string,\n columnAliases?: Record<string, string>,\n): BackofficeFilterSpec<Where> | null {\n const direct = resolveFilterForWhereKey(config, columnId);\n if (direct != null) {\n return direct;\n }\n\n const { [columnId]: alias } = columnAliases ?? {};\n if (alias != null) {\n const aliasFilter = resolveFilterForWhereKey(config, alias);\n if (aliasFilter != null) {\n return aliasFilter;\n }\n }\n\n const containsKey = `${columnId}Contains`;\n const containsFilter = resolveFilterForWhereKey(config, containsKey);\n if (containsFilter != null) {\n return containsFilter;\n }\n\n return null;\n}\n\n/** Check if a value is compatible with a filter spec. */\nexport function canFilterValue<Where extends Record<string, unknown>>(\n filter: BackofficeFilterSpec<Where>,\n value: unknown,\n): boolean {\n if (value == null) {\n return false;\n }\n\n if (filter.kind === 'boolean') {\n return typeof value === 'boolean';\n }\n\n if (filter.kind === 'entityId') {\n return typeof value === 'string' && value.trim() !== '';\n }\n\n if (typeof value === 'number') {\n return String(value).trim() !== '';\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return value.trim() !== '';\n}\n","import { style } from '@vanilla-extract/css';\n\nimport { sprinkles, vars } from '@plumile/ui';\n\nexport const action = style([\n sprinkles({\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 6,\n height: 6,\n padding: 0,\n borderRadius: 'sm',\n borderWidth: 'default',\n borderStyle: 'solid',\n borderColor: 'transparent',\n backgroundColor: 'transparent',\n color: 'textSecondary',\n cursor: 'pointer',\n textDecoration: 'none',\n transitionProperty: 'colors',\n transitionDuration: 120,\n transitionTimingFunction: 'ease',\n }),\n {\n selectors: {\n '&:hover': {\n color: vars.colors.text,\n backgroundColor: vars.colors.surfaceSecondary,\n borderColor: vars.colors.borderSubtle,\n },\n '&:focus-visible': {\n outline: `2px solid ${vars.colors.primary}`,\n outlineOffset: '2px',\n },\n },\n },\n]);\n\nexport const icon = sprinkles({\n width: 4,\n height: 4,\n});\n","import { type JSX } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Link } from '@plumile/router';\nimport { SidebarSearchSvg } from '@plumile/ui';\n\nimport type {\n BackofficeResolvedListFacetConfig,\n I18nLabel,\n} from '@plumile/backoffice-core/types.js';\nimport type { TFunction } from 'i18next';\nimport { buildBackofficeListLink } from '@plumile/backoffice-core/state/buildListHref.js';\nimport { setWhereValue } from '@plumile/backoffice-core/filters/where.js';\n\nimport {\n canFilterValue,\n resolveFilterForColumn,\n resolveFilterForWhereKey,\n} from '../../../filters/filterHelpers.js';\nimport { useBackofficeConfig } from '../../../provider/BackofficeConfigContext.js';\nimport { useBackofficeListFilterContext } from '../scaffolds/BackofficeListFilterContext.js';\nimport { useBackofficeReactTranslation } from '../../../i18n/useBackofficeReactTranslation.js';\n\nimport * as styles from './backofficeFilterAction.css.js';\n\nexport type BackofficeFilterActionProps<\n Where extends Record<string, unknown> = Record<string, unknown>,\n Sort extends string = string,\n> = {\n whereKey: string;\n value: unknown;\n path?: readonly string[];\n label?: string;\n listConfig?: BackofficeResolvedListFacetConfig<Where, Sort>;\n};\n\ntype ListFilterContext<\n Where extends Record<string, unknown>,\n Sort extends string,\n> = {\n config: BackofficeResolvedListFacetConfig<Where, Sort>;\n applyFilter: (\n whereKey: string,\n value: unknown,\n path?: readonly string[],\n ) => void;\n};\n\nconst resolveLabel = (label: I18nLabel, tApp: TFunction): string => {\n return label(tApp);\n};\n\nexport const BackofficeFilterAction = <\n Where extends Record<string, unknown> = Record<string, unknown>,\n Sort extends string = string,\n>(\n props: BackofficeFilterActionProps<Where, Sort>,\n): JSX.Element | null => {\n const { t: tApp } = useTranslation();\n const { t } = useBackofficeReactTranslation();\n const { filterColumnAliases } = useBackofficeConfig();\n const { whereKey, value, path, label, listConfig } = props;\n\n const getContext = useBackofficeListFilterContext as () => ListFilterContext<\n Where,\n Sort\n > | null;\n const context = getContext();\n let config: BackofficeResolvedListFacetConfig<Where, Sort> | null = null;\n\n if (listConfig != null) {\n config = listConfig;\n } else if (context != null) {\n config = context.config;\n }\n\n if (config == null) {\n return null;\n }\n\n let filter = resolveFilterForWhereKey(config, whereKey, path);\n if (filter == null && path == null) {\n filter = resolveFilterForColumn(config, whereKey, filterColumnAliases);\n }\n if (filter == null || !canFilterValue(filter, value)) {\n return null;\n }\n\n const filterLabel = resolveLabel(filter.label, tApp);\n const actionLabel =\n label ??\n t('filters.actions.filterBy', {\n label: filterLabel,\n });\n const icon = (\n <SidebarSearchSvg\n width={14}\n height={14}\n className={styles.icon}\n aria-hidden=\"true\"\n />\n );\n\n if (context != null) {\n return (\n <button\n type=\"button\"\n className={styles.action}\n title={actionLabel}\n aria-label={actionLabel}\n onClick={() => {\n context.applyFilter(whereKey, value, path);\n }}\n >\n {icon}\n </button>\n );\n }\n\n const baseWhere =\n config.listDefaults?.where ?? config.list.defaultState?.where ?? null;\n const nextWhere = setWhereValue(\n baseWhere,\n whereKey as keyof Where,\n value,\n path,\n );\n const to = buildBackofficeListLink(config, { where: nextWhere });\n\n return (\n <Link to={to} className={styles.action}>\n {icon}\n </Link>\n );\n};\n\nexport default BackofficeFilterAction;\n","import { sprinkles } from '@plumile/ui';\n\nexport const container = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n});\n\nexport const summary = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nexport const content = sprinkles({\n display: 'grid',\n gridTemplateColumns: 'twoFrOneFr',\n gap: 6,\n alignItems: 'flex-start',\n});\n\nexport const primary = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n});\n\nexport const aside = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n});\n","import { type JSX, type ReactNode } from 'react';\n\nimport { cx } from '@plumile/ui';\n\nimport * as styles from './backofficeOverviewLayout.css.js';\n\nexport type BackofficeOverviewLayoutProps = {\n summary?: ReactNode;\n aside?: ReactNode;\n children?: ReactNode;\n className?: string;\n};\n\nexport const BackofficeOverviewLayout = ({\n summary,\n aside,\n children,\n className,\n}: BackofficeOverviewLayoutProps): JSX.Element => {\n let summaryNode: JSX.Element | null = null;\n if (summary != null) {\n summaryNode = <div className={styles.summary}>{summary}</div>;\n }\n\n let asideNode: JSX.Element | null = null;\n if (aside != null) {\n asideNode = <aside className={styles.aside}>{aside}</aside>;\n }\n\n let primaryNode: JSX.Element | null = null;\n if (children != null || asideNode != null) {\n primaryNode = <div className={styles.primary}>{children}</div>;\n }\n\n return (\n <div className={cx(styles.container, className)}>\n {summaryNode}\n <div className={styles.content}>\n {primaryNode}\n {asideNode}\n </div>\n </div>\n );\n};\n\nexport default BackofficeOverviewLayout;\n","import { sprinkles } from '@plumile/ui';\n\nexport const link = sprinkles({\n color: 'text',\n textDecoration: 'underline',\n textDecorationColor: 'borderStrong',\n});\n","import { useContext, type JSX, type MouseEvent } from 'react';\nimport { Link, RoutingContext } from '@plumile/router';\n\nimport {\n buildBackofficeFallbackListHref,\n buildBackofficeListLink,\n} from '@plumile/backoffice-core/state/buildListHref.js';\nimport { useBackofficeConfig } from '../../../provider/BackofficeConfigContext.js';\n\nimport * as styles from './backofficeRelatedCountLink.css.js';\n\nexport type BackofficeRelatedCountLinkProps<\n Where extends Record<string, unknown> = Record<string, unknown>,\n> = {\n count: number | null | undefined;\n entity: string;\n where: Where;\n};\n\nexport const BackofficeRelatedCountLink = <\n Where extends Record<string, unknown> = Record<string, unknown>,\n>({\n count,\n entity,\n where,\n}: BackofficeRelatedCountLinkProps<Where>): JSX.Element => {\n const { entities, entityRegistry } = useBackofficeConfig();\n const routing = useContext(RoutingContext);\n let resolvedCount = 0;\n if (typeof count === 'number' && Number.isFinite(count)) {\n resolvedCount = count;\n }\n const entityManifest = entities[entity];\n if (entityManifest == null) {\n return <span>{resolvedCount}</span>;\n }\n const loadedEntity = entityRegistry.getLoadedListEntity(entity);\n let to: string | { pathname: string; search: string };\n if (loadedEntity == null) {\n to = buildBackofficeFallbackListHref(entityManifest.routes.list, where);\n } else {\n to = buildBackofficeListLink(loadedEntity.config, { where });\n }\n\n const navigateWithLoadedEntity = async (\n history: NonNullable<typeof routing>['history'],\n ): Promise<void> => {\n const listEntity = await entityRegistry.loadListEntity(entity);\n const next = buildBackofficeListLink(listEntity.config, { where });\n\n let search = '';\n if (next.search !== '') {\n search = `?${next.search}`;\n }\n\n history.push({\n pathname: next.pathname,\n search,\n hash: '',\n });\n };\n\n const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {\n if (\n loadedEntity != null ||\n routing == null ||\n event.defaultPrevented ||\n event.button !== 0 ||\n event.metaKey ||\n event.altKey ||\n event.ctrlKey ||\n event.shiftKey\n ) {\n return;\n }\n\n event.preventDefault();\n const { history } = routing;\n navigateWithLoadedEntity(history).catch(() => {\n if (typeof to === 'string') {\n const fallbackUrl = new URL(to, window.location.origin);\n history.push({\n pathname: fallbackUrl.pathname,\n search: fallbackUrl.search,\n hash: fallbackUrl.hash,\n });\n return;\n }\n\n let search = '';\n if (to.search !== '') {\n search = `?${to.search}`;\n }\n\n history.push({\n pathname: to.pathname,\n search,\n hash: '',\n });\n });\n };\n\n return (\n <Link to={to} className={styles.link} onClick={handleClick}>\n {resolvedCount}\n </Link>\n );\n};\n\nexport default BackofficeRelatedCountLink;\n","import { sprinkles } from '@plumile/ui';\n\nexport const tabs = sprinkles({\n display: 'flex',\n});\n\nexport const tabBody = sprinkles({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n","import { type JSX, type ReactNode } from 'react';\n\nimport {\n BackofficeTabs,\n DetailPageTemplate,\n type BackofficeTabItem,\n} from '@plumile/ui';\n\nimport * as styles from './backofficeTabbedDetailShell.css.js';\n\nexport type BackofficeTabbedDetailShellProps = {\n headerNode: ReactNode;\n tabs?: readonly BackofficeTabItem[];\n activeId?: string;\n children: ReactNode;\n};\n\nexport const BackofficeTabbedDetailShell = ({\n headerNode,\n tabs,\n activeId,\n children,\n}: BackofficeTabbedDetailShellProps): JSX.Element => {\n let tabsNode: JSX.Element | null = null;\n if (tabs != null && tabs.length > 1 && activeId != null) {\n tabsNode = (\n <BackofficeTabs\n items={tabs}\n activeId={activeId}\n onChange={() => {}}\n className={styles.tabs}\n />\n );\n }\n\n return (\n <DetailPageTemplate headerNode={headerNode} tabsNode={tabsNode}>\n <div className={styles.tabBody}>{children}</div>\n </DetailPageTemplate>\n );\n};\n\nexport default BackofficeTabbedDetailShell;\n","import { sprinkles } from '@plumile/ui';\n\nexport const container = sprinkles({\n display: 'inline-flex',\n alignItems: 'center',\n gap: 1,\n flexWrap: 'wrap',\n});\n","import { type JSX, type ReactNode } from 'react';\n\nimport { cx } from '@plumile/ui';\n\nimport * as styles from './backofficeInlineFilterRow.css.js';\n\nexport type BackofficeInlineFilterRowProps = {\n children: ReactNode;\n className?: string;\n};\n\nexport const BackofficeInlineFilterRow = ({\n children,\n className,\n}: BackofficeInlineFilterRowProps): JSX.Element => {\n return <span className={cx(styles.container, className)}>{children}</span>;\n};\n\nexport default BackofficeInlineFilterRow;\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport * as ReactRelay from 'react-relay';\nimport type { GraphQLSubscriptionConfig, OperationType } from 'relay-runtime';\n\ntype ConditionalOptions<T extends OperationType> = {\n enabled: boolean;\n deps?: readonly unknown[];\n getVariables?: () => T['variables'];\n};\n\ntype UseConditionalSubscriptionReturn = {\n active: boolean;\n error: Error | null;\n};\n\ntype Hop = { dispose: () => void } | null;\n\n/** Resolve the Relay module regardless of whether the package exposes a default export shim. */\nfunction resolveRelayApi(): typeof ReactRelay {\n const relayDefault: unknown = Reflect.get(ReactRelay, 'default');\n if (relayDefault != null) {\n return relayDefault as typeof ReactRelay;\n }\n return ReactRelay;\n}\n\nconst relayApi = resolveRelayApi();\n\n/** Subscribe only when the caller enables the subscription and variables exist. */\nexport function useConditionalSubscription<T extends OperationType>(\n config: GraphQLSubscriptionConfig<T>,\n options: ConditionalOptions<T>,\n): UseConditionalSubscriptionReturn {\n const environment = relayApi.useRelayEnvironment();\n const [error, setError] = useState<Error | null>(null);\n\n const { enabled = true, deps = [], getVariables } = options;\n\n const resolvedVariables = useMemo<T['variables'] | null>(() => {\n try {\n if (getVariables != null) {\n return getVariables();\n }\n return config.variables;\n } catch {\n return null;\n }\n }, [config.variables, getVariables]);\n\n const stableVarsString = useMemo(() => {\n if (resolvedVariables == null) {\n return null;\n }\n try {\n return JSON.stringify(resolvedVariables);\n } catch {\n return null;\n }\n }, [resolvedVariables]);\n\n const isEnabled = Boolean(enabled);\n const variables = resolvedVariables;\n const active = isEnabled && variables != null;\n\n const disposableRef = useRef<Hop>(null);\n\n useEffect(() => {\n let disposable: Hop = null;\n\n if (isEnabled && variables != null) {\n setError(null);\n\n disposable = relayApi.requestSubscription<T>(environment, {\n subscription: config.subscription,\n variables,\n cacheConfig: config.cacheConfig,\n configs: config.configs,\n onCompleted: config.onCompleted,\n onError: (nextError) => {\n setError(nextError);\n config.onError?.(nextError);\n },\n onNext: config.onNext,\n updater: config.updater,\n });\n\n disposableRef.current = disposable;\n }\n\n return () => {\n if (disposable != null) {\n try {\n disposable.dispose();\n } catch {\n // ignore cleanup failures\n }\n }\n if (disposableRef.current === disposable) {\n disposableRef.current = null;\n }\n };\n }, [config, deps, environment, isEnabled, stableVarsString, variables]);\n\n return { active, error };\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\n\nconst FALLBACK_TEXTAREA_ID = 'copy-to-clipboard-fallback';\n\n/** Write to the clipboard via a hidden textarea when the Clipboard API is unavailable. */\nfunction writeWithFallback(value: string): void {\n if (typeof document === 'undefined') {\n return;\n }\n\n let textarea = document.getElementById(\n FALLBACK_TEXTAREA_ID,\n ) as HTMLTextAreaElement | null;\n\n if (textarea == null) {\n textarea = document.createElement('textarea');\n textarea.id = FALLBACK_TEXTAREA_ID;\n textarea.setAttribute('readonly', '');\n textarea.style.position = 'absolute';\n textarea.style.left = '-9999px';\n textarea.style.top = '0';\n textarea.style.opacity = '0';\n document.body.appendChild(textarea);\n }\n\n textarea.value = value;\n textarea.select();\n document.execCommand('copy');\n}\n\n/** Copy text with best-effort browser fallback and short-lived copied state. */\nexport function useCopyToClipboard(timeoutMs = 2000): {\n copiedKey: string | null;\n copy: (value: string, key?: string) => Promise<boolean>;\n} {\n const [copiedKey, setCopiedKey] = useState<string | null>(null);\n const timeoutRef = useRef<number | null>(null);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current != null) {\n window.clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n const copy = useCallback(\n async (value: string, key?: string): Promise<boolean> => {\n let succeeded = false;\n\n let clipboard: Clipboard | undefined;\n if (typeof navigator !== 'undefined') {\n clipboard = navigator.clipboard;\n }\n\n if (clipboard != null) {\n try {\n await clipboard.writeText(value);\n succeeded = true;\n } catch {\n succeeded = false;\n }\n }\n\n if (!succeeded) {\n writeWithFallback(value);\n succeeded = true;\n }\n\n if (key != null) {\n setCopiedKey(key);\n if (timeoutRef.current != null) {\n window.clearTimeout(timeoutRef.current);\n }\n timeoutRef.current = window.setTimeout(() => {\n setCopiedKey(null);\n timeoutRef.current = null;\n }, timeoutMs);\n }\n\n return succeeded;\n },\n [timeoutMs],\n );\n\n return { copiedKey, copy };\n}\n\nexport default useCopyToClipboard;\n","import { useCallback, useEffect, useState } from 'react';\n\ntype RefetchReason = string;\n\ntype UseRefetchNeededReloadReturn = {\n reason: RefetchReason | null;\n onRefetchNeeded: (reason: RefetchReason | null | undefined) => void;\n reload: () => void;\n clear: () => void;\n};\n\n/** Track refetch-needed interruptions and expose a reload helper. */\nexport function useRefetchNeededReload(\n resetKey: string | null | undefined,\n): UseRefetchNeededReloadReturn {\n const [reason, setReason] = useState<RefetchReason | null>(null);\n\n useEffect(() => {\n setReason(null);\n }, [resetKey]);\n\n const onRefetchNeeded = useCallback(\n (nextReason: RefetchReason | null | undefined) => {\n setReason(nextReason ?? 'UNKNOWN');\n },\n [],\n );\n\n const reload = useCallback(() => {\n window.location.reload();\n }, []);\n\n const clear = useCallback(() => {\n setReason(null);\n }, []);\n\n return { reason, onRefetchNeeded, reload, clear };\n}\n","import type { ReviewStatus } from '../modules/sharedSchemaTypes.js';\nimport { useSharedTranslation } from './useSharedTranslation.js';\n\nexport type ReviewStatusLabeler = {\n getReviewStatusLabel: (status: ReviewStatus | null | undefined) => string;\n};\n\n/** Provide shared i18n labels for review status values. */\nexport function useReviewStatusLabel(): ReviewStatusLabeler {\n const { t } = useSharedTranslation();\n\n /** Resolve a translated label for a review status value. */\n function getReviewStatusLabel(\n status: ReviewStatus | null | undefined,\n ): string {\n if (status == null) {\n return t('review.status.unknown');\n }\n switch (status) {\n case 'APPROVED':\n return t('review.status.approved');\n case 'CHANGES_REQUESTED':\n return t('review.status.changesRequested');\n case 'PENDING':\n return t('review.status.pending');\n default:\n return t('review.status.unknown');\n }\n }\n\n return { getReviewStatusLabel };\n}\n","/** Encode a UTF-8 string into base64 using native helpers when available. */\nexport function encodeUtf8ToBase64(value: string): string {\n if (typeof value !== 'string') {\n throw new TypeError('encodeUtf8ToBase64 expects a string input.');\n }\n\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(value, 'utf-8').toString('base64');\n }\n\n if (typeof globalThis.btoa === 'function') {\n const utf8 = encodeURIComponent(value).replace(\n /%([0-9A-F]{2})/g,\n (_, code) => {\n return String.fromCharCode(Number.parseInt(code, 16));\n },\n );\n return globalThis.btoa(utf8);\n }\n\n throw new Error('No base64 encoder is available in this environment.');\n}\n\n/** Decode a base64 string into UTF-8 using native helpers when available. */\nexport function decodeBase64ToUtf8(value: string): string {\n if (typeof value !== 'string') {\n throw new TypeError('decodeBase64ToUtf8 expects a string input.');\n }\n\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(value, 'base64').toString('utf-8');\n }\n\n if (typeof globalThis.atob === 'function') {\n const binary = globalThis.atob(value);\n const percentEncoded = Array.from(binary, (char) => {\n const code = char.charCodeAt(0).toString(16).padStart(2, '0');\n return `%${code}`;\n }).join('');\n return decodeURIComponent(percentEncoded);\n }\n\n throw new Error('No base64 decoder is available in this environment.');\n}\n","export type FileSizeUnit = 'B' | 'KB' | 'MB' | 'GB' | 'TB';\n\nexport type FileSizeResult = {\n value: number;\n unit: FileSizeUnit;\n displayValue: string;\n};\n\nconst SIZE_UNITS: readonly FileSizeUnit[] = ['B', 'KB', 'MB', 'GB', 'TB'];\nconst BASE = 1024;\n\nconst roundValue = (value: number, unit: FileSizeUnit): string => {\n if (unit === 'B') {\n return Math.round(value).toString();\n }\n let decimals = 1;\n if (value >= 10) {\n decimals = 0;\n }\n return value.toFixed(decimals);\n};\n\n/** Format a byte count into a human-readable value and unit. */\nexport function formatFileSize(bytes: number): FileSizeResult {\n let normalized = bytes;\n if (!Number.isFinite(bytes)) {\n normalized = 0;\n }\n const isNegative = normalized < 0;\n let value = Math.abs(normalized);\n let unitIndex = 0;\n\n while (value >= BASE && unitIndex < SIZE_UNITS.length - 1) {\n value /= BASE;\n unitIndex += 1;\n }\n\n const unit = SIZE_UNITS[unitIndex] ?? 'B';\n let signedValue = value;\n if (isNegative) {\n signedValue = -value;\n }\n\n return {\n value: signedValue,\n unit,\n displayValue: roundValue(signedValue, unit),\n };\n}\n","import * as ReactRelay from 'react-relay';\nimport type { GraphQLTaggedNode } from 'relay-runtime';\n\nconst { readInlineData } = ReactRelay;\n\n// Reserved for non-React helpers that explicitly read `@inline` fragments.\nexport const createInlineDataReader = <TRef, TData>(\n fragment: GraphQLTaggedNode,\n): ((ref: TRef) => TData) => {\n return (ref: TRef) => {\n return readInlineData(fragment, ref as never) as TData;\n };\n};\n\n// Legacy alias kept for incremental migration of existing callsites.\nexport const createInlineReader = createInlineDataReader;\n","// Only use this in the React page/layout pipeline after `useFragment` has\n// already resolved a typed fragment payload into `FragmentData`.\nexport const identityView = <TData>(data: TData): TData => {\n return data;\n};\n"],"mappings":"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"}