@plumile/backoffice-react 0.1.97 → 0.1.99

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.
@@ -1 +1 @@
1
- {"version":3,"file":"backoffice-react.js","names":[],"sources":["../../src/i18n/createI18nInstance.ts","../../src/i18n/locales/en/backofficeReact.json","../../src/i18n/locales/fr/backofficeReact.json","../../src/i18n/mergeResourceLanguages.ts","../../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 ...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\n const { formatter } = instance.services;\n if (formatter != null) {\n formatter.add('number', (value, lng) => {\n return formatValue(value, 'number', lng);\n });\n formatter.add('currency', (value, lng) => {\n return formatValue(value, 'currency', lng);\n });\n formatter.add('percent', (value, lng) => {\n return formatValue(value, 'percent', lng);\n });\n formatter.add('date', (value, lng) => {\n return formatValue(value, 'date', lng);\n });\n formatter.add('datetime', (value, lng) => {\n return formatValue(value, 'datetime', lng);\n });\n }\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\nexport const __test = {\n getLocale,\n stringifyValue,\n formatValue,\n} as const;\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 \"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 \"expired\": \"This reset link has expired.\",\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 \"policyViolation\": \"Your new password does not meet policy requirements.\"\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 \"invalidEmail\": \"Please enter a valid email address.\",\n \"rateLimited\": \"Too many requests. Please wait and try again.\",\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 \"alreadyVerified\": \"This email address is already verified.\",\n \"expired\": \"This verification link has expired.\",\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 \"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 \"history\": \"\",\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 \"title\": \"\"\n },\n \"overview\": \"\",\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 \"review\": {\n \"status\": {\n \"approved\": \"Approved\",\n \"changesRequested\": \"Changes requested\",\n \"pending\": \"Pending\",\n \"unknown\": \"Unknown\"\n }\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 \"output\": \"Output\"\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 \"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 \"expired\": \"Ce lien de réinitialisation a expiré.\",\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 \"policyViolation\": \"Votre nouveau mot de passe ne respecte pas la politique de sécurité.\"\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 \"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.\"\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 \"alreadyVerified\": \"Cette adresse email est déjà vérifiée.\",\n \"expired\": \"Ce lien de vérification a expiré.\",\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 \"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 \"history\": \"\",\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 \"title\": \"\"\n },\n \"overview\": \"\",\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 \"review\": {\n \"status\": {\n \"approved\": \"Approuvé\",\n \"changesRequested\": \"Modifications demandées\",\n \"pending\": \"En attente\",\n \"unknown\": \"Inconnu\"\n }\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 \"output\": \"Résultat\"\n }\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\nexport const __test = {\n isResourceObject,\n asResourceLanguage,\n} as const;\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","import en from './locales/en/backofficeReact.json' with { type: 'json' };\nimport fr from './locales/fr/backofficeReact.json' with { type: 'json' };\nimport type { Resource, ResourceLanguage } from 'i18next';\n\nimport { mergeResourceLanguages } from './mergeResourceLanguages.js';\n\nexport const backofficeReactI18nResources = {\n en: { backofficeReact: en },\n fr: { backofficeReact: fr },\n} as const;\n\nexport type BackofficeReactI18nResources = typeof backofficeReactI18nResources;\n\nconst isResourceLanguage = (value: unknown): value is ResourceLanguage => {\n return typeof value === 'object' && value != null;\n};\n\nconst asResourceLanguage = (value: unknown): ResourceLanguage => {\n if (isResourceLanguage(value)) {\n return value;\n }\n return {};\n};\n\n/**\n * Returns i18next resources with the package-owned `backofficeReact` namespace\n * registered for every supported package locale.\n *\n * Applications using package auth screens outside `BackofficeProvider` should\n * pass their app resources through this helper before initializing i18next.\n * App-provided `backofficeReact` keys are deep-merged on top so product-specific\n * overrides can replace package copy without dropping nested package defaults.\n */\nexport function withBackofficeReactI18nResources(\n appResources: Resource = {},\n): Resource {\n const output: Resource = {};\n const packageResources = backofficeReactI18nResources as Resource;\n const locales = new Set([\n ...Object.keys(packageResources),\n ...Object.keys(appResources),\n ]);\n\n locales.forEach((locale) => {\n const packageLocale = asResourceLanguage(packageResources[locale]);\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 };\n });\n\n return output;\n}\n","import { useSyncExternalStore, type JSX, type ReactNode } from 'react';\nimport * as ReactRelay from 'react-relay';\n\nimport {\n getEnvironment,\n getRelayTransportSnapshot,\n subscribeRelayTransport,\n} from './environment.js';\n\nconst { RelayEnvironmentProvider } = ReactRelay;\n\ntype Props = {\n children: ReactNode;\n};\n\nexport const RelayProvider = ({ children }: Props): JSX.Element => {\n useSyncExternalStore(\n subscribeRelayTransport,\n () => {\n return getRelayTransportSnapshot().generation;\n },\n () => {\n return getRelayTransportSnapshot().generation;\n },\n );\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 <Component prepared={prepared} />;\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 const __test = {\n normalizeBaseSegment,\n normalizePath,\n buildScopedPath,\n buildScopedAbsolutePath,\n buildRelativePath,\n resolveActiveEntityIdFromRoute,\n toSearchParams,\n} as const;\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 const __test = {\n buildFacetCacheKey,\n resolveManifest,\n toError,\n};\n\nexport default createBackofficeEntityRegistry;\n","import { StrictMode, useEffect, useMemo, type JSX } from 'react';\nimport { createInstance } 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 { withBackofficeReactI18nResources } 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\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 return withBackofficeReactI18nResources(props.i18n?.resources ?? {});\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 ?? ['backofficeReact', 'translations', 'ui'];\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 {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport * as ReactRelay from 'react-relay';\nimport type { GraphQLSubscriptionConfig, OperationType } from 'relay-runtime';\n\nimport {\n getRelayTransportSnapshot,\n subscribeRelayTransport,\n} from '../relay/environment.js';\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 completed: boolean;\n error: Error | null;\n};\n\ntype Hop = { dispose: () => void } | null;\n\nconst EMPTY_DEPS: readonly unknown[] = [];\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 const [isSubscribed, setIsSubscribed] = useState(false);\n const [completed, setCompleted] = useState(false);\n const transportGeneration = useSyncExternalStore(\n subscribeRelayTransport,\n () => {\n return getRelayTransportSnapshot().generation;\n },\n () => {\n return getRelayTransportSnapshot().generation;\n },\n );\n\n const { enabled = true, deps = EMPTY_DEPS, getVariables } = options;\n\n const resolveVariables = useCallback((): 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 resolvedVariables = useMemo<T['variables'] | null>(() => {\n return resolveVariables();\n }, [resolveVariables]);\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 canSubscribe = isEnabled && variables != null;\n const active = canSubscribe && isSubscribed && error == null && !completed;\n\n const disposableRef = useRef<Hop>(null);\n\n useEffect(() => {\n let disposable: Hop = null;\n const variables = resolveVariables();\n\n if (isEnabled && variables != null) {\n setError(null);\n setCompleted(false);\n setIsSubscribed(true);\n\n disposable = relayApi.requestSubscription<T>(environment, {\n subscription: config.subscription,\n variables,\n cacheConfig: config.cacheConfig,\n configs: config.configs,\n onCompleted: () => {\n setCompleted(true);\n setIsSubscribed(false);\n config.onCompleted?.();\n },\n onError: (nextError) => {\n setError(nextError);\n setIsSubscribed(false);\n config.onError?.(nextError);\n },\n onNext: config.onNext,\n updater: config.updater,\n });\n\n disposableRef.current = disposable;\n }\n\n return () => {\n setIsSubscribed(false);\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 }, [\n config,\n deps,\n environment,\n isEnabled,\n resolveVariables,\n stableVarsString,\n transportGeneration,\n ]);\n\n return { active, completed, 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 { useBackofficeReactTranslation } from './useBackofficeReactTranslation.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 } = useBackofficeReactTranslation();\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 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 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);\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/locales/en/backofficeReact.json","../../src/i18n/locales/fr/backofficeReact.json","../../src/i18n/mergeResourceLanguages.ts","../../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 ...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\n const { formatter } = instance.services;\n if (formatter != null) {\n formatter.add('number', (value, lng) => {\n return formatValue(value, 'number', lng);\n });\n formatter.add('currency', (value, lng) => {\n return formatValue(value, 'currency', lng);\n });\n formatter.add('percent', (value, lng) => {\n return formatValue(value, 'percent', lng);\n });\n formatter.add('date', (value, lng) => {\n return formatValue(value, 'date', lng);\n });\n formatter.add('datetime', (value, lng) => {\n return formatValue(value, 'datetime', lng);\n });\n }\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\nexport const __test = {\n getLocale,\n stringifyValue,\n formatValue,\n} as const;\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 \"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 \"expired\": \"This reset link has expired.\",\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 \"policyViolation\": \"Your new password does not meet policy requirements.\"\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 \"invalidEmail\": \"Please enter a valid email address.\",\n \"rateLimited\": \"Too many requests. Please wait and try again.\",\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 \"alreadyVerified\": \"This email address is already verified.\",\n \"expired\": \"This verification link has expired.\",\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 \"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 \"history\": \"\",\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 \"title\": \"\"\n },\n \"overview\": \"\",\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 \"review\": {\n \"status\": {\n \"approved\": \"Approved\",\n \"changesRequested\": \"Changes requested\",\n \"pending\": \"Pending\",\n \"unknown\": \"Unknown\"\n }\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 \"output\": \"Output\"\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 \"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 \"expired\": \"Ce lien de réinitialisation a expiré.\",\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 \"policyViolation\": \"Votre nouveau mot de passe ne respecte pas la politique de sécurité.\"\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 \"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.\"\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 \"alreadyVerified\": \"Cette adresse email est déjà vérifiée.\",\n \"expired\": \"Ce lien de vérification a expiré.\",\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 \"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 \"history\": \"\",\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 \"title\": \"\"\n },\n \"overview\": \"\",\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 \"review\": {\n \"status\": {\n \"approved\": \"Approuvé\",\n \"changesRequested\": \"Modifications demandées\",\n \"pending\": \"En attente\",\n \"unknown\": \"Inconnu\"\n }\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 \"output\": \"Résultat\"\n }\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\nexport const __test = {\n isResourceObject,\n asResourceLanguage,\n} as const;\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","import en from './locales/en/backofficeReact.json' with { type: 'json' };\nimport fr from './locales/fr/backofficeReact.json' with { type: 'json' };\nimport type { Resource, ResourceLanguage } from 'i18next';\n\nimport { mergeResourceLanguages } from './mergeResourceLanguages.js';\n\nexport const backofficeReactI18nResources = {\n en: { backofficeReact: en },\n fr: { backofficeReact: fr },\n} as const;\n\nexport type BackofficeReactI18nResources = typeof backofficeReactI18nResources;\n\nconst isResourceLanguage = (value: unknown): value is ResourceLanguage => {\n return typeof value === 'object' && value != null;\n};\n\nconst asResourceLanguage = (value: unknown): ResourceLanguage => {\n if (isResourceLanguage(value)) {\n return value;\n }\n return {};\n};\n\n/**\n * Returns i18next resources with the package-owned `backofficeReact` namespace\n * registered for every supported package locale.\n *\n * Applications using package auth screens outside `BackofficeProvider` should\n * pass their app resources through this helper before initializing i18next.\n * App-provided `backofficeReact` keys are deep-merged on top so product-specific\n * overrides can replace package copy without dropping nested package defaults.\n */\nexport function withBackofficeReactI18nResources(\n appResources: Resource = {},\n): Resource {\n const output: Resource = {};\n const packageResources = backofficeReactI18nResources as Resource;\n const locales = new Set([\n ...Object.keys(packageResources),\n ...Object.keys(appResources),\n ]);\n\n locales.forEach((locale) => {\n const packageLocale = asResourceLanguage(packageResources[locale]);\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 };\n });\n\n return output;\n}\n","import { useSyncExternalStore, type JSX, type ReactNode } from 'react';\nimport * as ReactRelay from 'react-relay';\n\nimport {\n getEnvironment,\n getRelayTransportSnapshot,\n subscribeRelayTransport,\n} from './environment.js';\n\nconst { RelayEnvironmentProvider } = ReactRelay;\n\ntype Props = {\n children: ReactNode;\n};\n\nexport const RelayProvider = ({ children }: Props): JSX.Element => {\n useSyncExternalStore(\n subscribeRelayTransport,\n () => {\n return getRelayTransportSnapshot().generation;\n },\n () => {\n return getRelayTransportSnapshot().generation;\n },\n );\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 <Component prepared={prepared} />;\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 const __test = {\n normalizeBaseSegment,\n normalizePath,\n buildScopedPath,\n buildScopedAbsolutePath,\n buildRelativePath,\n resolveActiveEntityIdFromRoute,\n toSearchParams,\n} as const;\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 const __test = {\n buildFacetCacheKey,\n resolveManifest,\n toError,\n};\n\nexport default createBackofficeEntityRegistry;\n","import {\n StrictMode,\n useEffect,\n useMemo,\n useState,\n type ContextType,\n type JSX,\n} from 'react';\nimport { createInstance } 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 { withBackofficeReactI18nResources } 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 {\n createBackofficeRoutes,\n type BackofficeRouterContext,\n} 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\ntype RouterShellProps = {\n routes: ReturnType<typeof createBackofficeRoutes>;\n instrumentations?: BackofficeProviderProps['instrumentations'];\n};\n\ntype BackofficeRouterInstance = {\n context: NonNullable<ContextType<typeof RoutingContext>>;\n cleanup: () => void;\n};\n\nconst useBackofficeRouterInstance = ({\n routes,\n context,\n instrumentations,\n}: {\n routes: ReturnType<typeof createBackofficeRoutes>;\n context: BackofficeRouterContext;\n instrumentations?: BackofficeProviderProps['instrumentations'];\n}): BackofficeRouterInstance | null => {\n const [router, setRouter] = useState<BackofficeRouterInstance | null>(null);\n\n useEffect(() => {\n const nextRouter = createRouter(routes, {\n context,\n instrumentations,\n });\n setRouter(nextRouter);\n\n return () => {\n nextRouter.cleanup();\n };\n }, [context, instrumentations, routes]);\n\n return router;\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 = useBackofficeRouterInstance({\n routes,\n context: routerContext,\n instrumentations,\n });\n\n if (router == null) {\n return <BackofficeRouteFallback />;\n }\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 return withBackofficeReactI18nResources(props.i18n?.resources ?? {});\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 ?? ['backofficeReact', 'translations', 'ui'];\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 {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport * as ReactRelay from 'react-relay';\nimport type { GraphQLSubscriptionConfig, OperationType } from 'relay-runtime';\n\nimport {\n getRelayTransportSnapshot,\n subscribeRelayTransport,\n} from '../relay/environment.js';\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 completed: boolean;\n error: Error | null;\n};\n\ntype Hop = { dispose: () => void } | null;\n\nconst EMPTY_DEPS: readonly unknown[] = [];\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 const [isSubscribed, setIsSubscribed] = useState(false);\n const [completed, setCompleted] = useState(false);\n const transportGeneration = useSyncExternalStore(\n subscribeRelayTransport,\n () => {\n return getRelayTransportSnapshot().generation;\n },\n () => {\n return getRelayTransportSnapshot().generation;\n },\n );\n\n const { enabled = true, deps = EMPTY_DEPS, getVariables } = options;\n\n const resolveVariables = useCallback((): 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 resolvedVariables = useMemo<T['variables'] | null>(() => {\n return resolveVariables();\n }, [resolveVariables]);\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 canSubscribe = isEnabled && variables != null;\n const active = canSubscribe && isSubscribed && error == null && !completed;\n\n const disposableRef = useRef<Hop>(null);\n\n useEffect(() => {\n let disposable: Hop = null;\n const variables = resolveVariables();\n\n if (isEnabled && variables != null) {\n setError(null);\n setCompleted(false);\n setIsSubscribed(true);\n\n disposable = relayApi.requestSubscription<T>(environment, {\n subscription: config.subscription,\n variables,\n cacheConfig: config.cacheConfig,\n configs: config.configs,\n onCompleted: () => {\n setCompleted(true);\n setIsSubscribed(false);\n config.onCompleted?.();\n },\n onError: (nextError) => {\n setError(nextError);\n setIsSubscribed(false);\n config.onError?.(nextError);\n },\n onNext: config.onNext,\n updater: config.updater,\n });\n\n disposableRef.current = disposable;\n }\n\n return () => {\n setIsSubscribed(false);\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 }, [\n config,\n deps,\n environment,\n isEnabled,\n resolveVariables,\n stableVarsString,\n transportGeneration,\n ]);\n\n return { active, completed, 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 { useBackofficeReactTranslation } from './useBackofficeReactTranslation.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 } = useBackofficeReactTranslation();\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 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 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);\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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"}