@open-mercato/core 0.7.1-develop.7195.1.b5efd1d51a → 0.7.1-develop.7201.1.9bd6c88e3d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/modules/auth/lib/setup-app.js +1 -0
  2. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  3. package/dist/modules/catalog/components/products/ProductUomSection.js +27 -22
  4. package/dist/modules/catalog/components/products/ProductUomSection.js.map +2 -2
  5. package/dist/modules/entities/lib/field-definitions.js +9 -0
  6. package/dist/modules/entities/lib/field-definitions.js.map +2 -2
  7. package/dist/modules/entities/lib/install-from-ce.js +1 -0
  8. package/dist/modules/entities/lib/install-from-ce.js.map +2 -2
  9. package/dist/modules/eudr/search.js +3 -5
  10. package/dist/modules/eudr/search.js.map +2 -2
  11. package/dist/modules/sales/components/documents/PaymentDialog.js +14 -11
  12. package/dist/modules/sales/components/documents/PaymentDialog.js.map +2 -2
  13. package/dist/modules/sales/lib/lineSnapshots.js +3 -0
  14. package/dist/modules/sales/lib/lineSnapshots.js.map +2 -2
  15. package/dist/modules/wms/components/backend/AdjustInventoryDialog.js +8 -6
  16. package/dist/modules/wms/components/backend/AdjustInventoryDialog.js.map +2 -2
  17. package/dist/modules/wms/search.js +4 -4
  18. package/dist/modules/wms/search.js.map +2 -2
  19. package/package.json +7 -7
  20. package/src/modules/auth/lib/setup-app.ts +6 -0
  21. package/src/modules/catalog/components/products/ProductUomSection.tsx +31 -21
  22. package/src/modules/entities/lib/field-definitions.ts +15 -0
  23. package/src/modules/entities/lib/install-from-ce.ts +1 -0
  24. package/src/modules/eudr/i18n/de.json +4 -1
  25. package/src/modules/eudr/i18n/en.json +4 -1
  26. package/src/modules/eudr/i18n/es.json +4 -1
  27. package/src/modules/eudr/i18n/ko.json +4 -1
  28. package/src/modules/eudr/i18n/pl.json +4 -1
  29. package/src/modules/eudr/search.ts +3 -5
  30. package/src/modules/sales/components/documents/PaymentDialog.tsx +18 -11
  31. package/src/modules/sales/i18n/de.json +1 -0
  32. package/src/modules/sales/i18n/en.json +1 -0
  33. package/src/modules/sales/i18n/es.json +1 -0
  34. package/src/modules/sales/i18n/ko.json +1 -0
  35. package/src/modules/sales/i18n/pl.json +1 -0
  36. package/src/modules/sales/lib/lineSnapshots.ts +22 -3
  37. package/src/modules/wms/components/backend/AdjustInventoryDialog.tsx +6 -5
  38. package/src/modules/wms/i18n/de.json +4 -0
  39. package/src/modules/wms/i18n/en.json +4 -0
  40. package/src/modules/wms/i18n/es.json +4 -0
  41. package/src/modules/wms/i18n/ko.json +4 -0
  42. package/src/modules/wms/i18n/pl.json +4 -0
  43. package/src/modules/wms/search.ts +4 -4
@@ -331,6 +331,7 @@ async function setupInitialTenant(em, options) {
331
331
  await mod.setup.onTenantCreated({ em, tenantId, organizationId });
332
332
  }
333
333
  }
334
+ await ensureCustomRoleAcls(em, tenantId, resolvedModules);
334
335
  return {
335
336
  tenantId,
336
337
  organizationId,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/auth/lib/setup-app.ts"],
4
- "sourcesContent": ["import { hash } from 'bcryptjs'\nimport { randomBytes } from 'node:crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { Role, RoleAcl, User, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { Tenant, Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { rebuildHierarchyForTenant } from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport { normalizeTenantId } from './tenantAccess'\nimport { computeEmailHash, emailHashLookupValues } from '@open-mercato/core/modules/auth/lib/emailHash'\nimport { getDefaultEncryptionMaps, type Module } from '@open-mercato/shared/modules/registry'\nimport { isEncryptionDebugEnabled, isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { EncryptionMap } from '@open-mercato/core/modules/entities/data/entities'\nimport { createKmsService } from '@open-mercato/shared/lib/encryption/kms'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('auth').child({ component: 'setup' })\n\nconst DEFAULT_ROLE_NAMES = ['employee', 'admin', 'superadmin'] as const\nconst DEMO_SUPERADMIN_EMAIL = 'superadmin@acme.com'\nconst DEFAULT_DERIVED_EMAIL_DOMAIN = DEMO_SUPERADMIN_EMAIL.split('@')[1] ?? 'acme.com'\n\nexport type EnsureRolesOptions = {\n roleNames?: string[]\n tenantId?: string\n}\n\nasync function ensureRolesInContext(\n em: EntityManager,\n roleNames: string[],\n tenantId: string,\n) {\n for (const name of roleNames) {\n const existing = await findOneWithDecryption(em, Role, { name, tenantId }, {}, { tenantId, organizationId: null })\n if (existing) continue\n em.persist(em.create(Role, { name, tenantId, minActiveHolders: name === 'admin' ? 1 : 0, createdAt: new Date() }))\n }\n}\n\nexport async function ensureRoles(em: EntityManager, options: EnsureRolesOptions = {}) {\n const roleNames = options.roleNames ?? [...DEFAULT_ROLE_NAMES]\n const tenantId = normalizeTenantId(options.tenantId ?? null)\n if (!tenantId) {\n throw new Error('ensureRoles requires a tenantId \u2014 global roles are not supported')\n }\n await em.transactional(async (tem) => {\n await ensureRolesInContext(tem, roleNames, tenantId)\n await tem.flush()\n })\n}\n\nasync function findRoleByName(\n em: EntityManager,\n name: string,\n tenantId: string | null,\n): Promise<Role | null> {\n const normalizedTenant = normalizeTenantId(tenantId ?? null) ?? null\n return findOneWithDecryption(em, Role, { name, tenantId: normalizedTenant }, {}, { tenantId: normalizedTenant, organizationId: null })\n}\n\nasync function findRoleByNameOrFail(\n em: EntityManager,\n name: string,\n tenantId: string | null,\n): Promise<Role> {\n const role = await findRoleByName(em, name, tenantId)\n if (!role) throw new Error(`ROLE_NOT_FOUND:${name}`)\n return role\n}\n\ntype PrimaryUserInput = {\n email: string\n password?: string\n hashedPassword?: string | null\n firstName?: string | null\n lastName?: string | null\n displayName?: string | null\n confirm?: boolean\n}\n\nconst DERIVED_EMAIL_ENV = {\n admin: 'OM_INIT_ADMIN_EMAIL',\n employee: 'OM_INIT_EMPLOYEE_EMAIL',\n} as const\n\nexport type DemoUserRole = 'superadmin' | 'admin' | 'employee'\nexport type DemoUserEmail = { role: DemoUserRole; email: string }\n\n/**\n * Returns the canonical list of demo user emails the setup path may have\n * seeded. Honors OM_INIT_ADMIN_EMAIL / OM_INIT_EMPLOYEE_EMAIL exactly the\n * same way the derived-user seeding branch does, so the deactivation loop\n * never drifts from the seeding loop.\n *\n * Pure function \u2014 no side effects, safe to unit-test in isolation.\n *\n * @internal Exported for tests; not part of the public auth API.\n */\nexport function resolveDemoUserEmails(): DemoUserEmail[] {\n const adminEmail = readEnvValue(DERIVED_EMAIL_ENV.admin) ?? `admin@${DEFAULT_DERIVED_EMAIL_DOMAIN}`\n const employeeEmail = readEnvValue(DERIVED_EMAIL_ENV.employee) ?? `employee@${DEFAULT_DERIVED_EMAIL_DOMAIN}`\n return [\n { role: 'superadmin', email: DEMO_SUPERADMIN_EMAIL },\n { role: 'admin', email: adminEmail },\n { role: 'employee', email: employeeEmail },\n ]\n}\n\nexport type SetupInitialTenantOptions = {\n orgName: string\n primaryUser: PrimaryUserInput\n roleNames?: string[]\n includeDerivedUsers?: boolean\n failIfUserExists?: boolean\n primaryUserRoles?: string[]\n includeSuperadminRole?: boolean\n /** Optional list of enabled modules. When provided, module setup hooks are called. */\n modules?: Module[]\n /**\n * Optional global slug to persist on the new Organization. When set, a\n * pre-flight uniqueness check across all tenants throws OrgSlugExistsError\n * on collision so scriptable callers can fail loudly instead of silently\n * reusing or clobbering an existing organization.\n */\n orgSlug?: string\n /**\n * Opt-in flag that allows seeding the derived admin/employee accounts when\n * the OM_INIT_ADMIN_PASSWORD / OM_INIT_EMPLOYEE_PASSWORD env vars are unset.\n *\n * - In non-production (`NODE_ENV !== 'production'`): the demo accounts are\n * seeded with the well-known `'secret'` password so local dev workflows\n * (e.g. `mercato init`) stay predictable for developers.\n * - In production: the fallback uses a randomly generated 96-bit password\n * (printed once by the CLI) so an operator who deliberately opts in still\n * avoids the historical hardcoded credential. When this flag is false/unset\n * in production and the env vars are missing, `DerivedUserPasswordRequiredError`\n * is thrown instead of silently seeding any account.\n */\n allowDemoDerivedPasswords?: boolean\n}\n\nexport class OrgSlugExistsError extends Error {\n constructor(public readonly slug: string) {\n super(`ORG_SLUG_EXISTS: an organization with slug \"${slug}\" already exists`)\n this.name = 'OrgSlugExistsError'\n }\n}\n\nexport class DerivedUserPasswordRequiredError extends Error {\n constructor(public readonly missing: string[]) {\n super(\n `DERIVED_USER_PASSWORD_REQUIRED: missing ${missing.join(', ')} (set the env vars, pass allowDemoDerivedPasswords: true / --include-demo-users in non-production, or disable derived user seeding)`,\n )\n this.name = 'DerivedUserPasswordRequiredError'\n }\n}\n\nexport type SetupInitialTenantResult = {\n tenantId: string\n organizationId: string\n users: Array<{ user: User; roles: string[]; created: boolean; generatedPassword?: string | null }>\n reusedExistingUser: boolean\n}\n\nexport async function setupInitialTenant(\n em: EntityManager,\n options: SetupInitialTenantOptions,\n): Promise<SetupInitialTenantResult> {\n const {\n primaryUser,\n includeDerivedUsers = true,\n failIfUserExists = false,\n primaryUserRoles,\n includeSuperadminRole = true,\n } = options\n const primaryRolesInput = primaryUserRoles && primaryUserRoles.length ? primaryUserRoles : ['superadmin']\n const primaryRoles = includeSuperadminRole\n ? primaryRolesInput\n : primaryRolesInput.filter((role) => role !== 'superadmin')\n if (primaryRoles.length === 0) {\n throw new Error('PRIMARY_ROLES_REQUIRED')\n }\n const defaultRoleNames = options.roleNames ?? [...DEFAULT_ROLE_NAMES]\n const resolvedRoleNames = includeSuperadminRole\n ? defaultRoleNames\n : defaultRoleNames.filter((role) => role !== 'superadmin')\n const roleNames = Array.from(new Set([...resolvedRoleNames, ...primaryRoles]))\n const resolvedModules = options.modules ?? tryGetModules()\n const defaultEncryptionMaps = getDefaultEncryptionMaps(resolvedModules)\n\n const mainEmail = primaryUser.email\n const existingUserByHash = await findOneWithDecryption(\n em,\n User,\n { emailHash: { $in: emailHashLookupValues(mainEmail) }, deletedAt: null },\n {},\n { tenantId: null, organizationId: null },\n )\n const existingUser = existingUserByHash ?? await findOneWithDecryption(\n em,\n User,\n { email: mainEmail, deletedAt: null },\n {},\n { tenantId: null, organizationId: null },\n )\n if (existingUser && failIfUserExists) {\n throw new Error('USER_EXISTS')\n }\n\n if (options.orgSlug) {\n const slugConflict = await findOneWithDecryption(\n em,\n Organization,\n { slug: options.orgSlug },\n {},\n { tenantId: null, organizationId: null },\n )\n if (slugConflict) {\n throw new OrgSlugExistsError(options.orgSlug)\n }\n }\n\n let tenantId: string | undefined\n let organizationId: string | undefined\n let reusedExistingUser = false\n const userSnapshots: Array<{ user: User; roles: string[]; created: boolean; generatedPassword?: string | null }> = []\n\n await em.transactional(async (tem) => {\n if (!existingUser) return\n reusedExistingUser = true\n tenantId = existingUser.tenantId ? String(existingUser.tenantId) : undefined\n organizationId = existingUser.organizationId ? String(existingUser.organizationId) : undefined\n const roleTenantId = normalizeTenantId(existingUser.tenantId ?? null) ?? null\n if (!roleTenantId) {\n throw new Error('Cannot reuse a user without a tenantId \u2014 global roles are not supported')\n }\n\n await ensureRolesInContext(tem, roleNames, roleTenantId)\n await tem.flush()\n\n const requiredRoleSet = new Set([...roleNames, ...primaryRoles])\n const links = await findWithDecryption(\n tem,\n UserRole,\n { user: existingUser },\n { populate: ['role'] },\n { tenantId: roleTenantId, organizationId: null },\n )\n const currentRoles = new Set(links.map((link) => link.role.name))\n for (const roleName of requiredRoleSet) {\n if (!currentRoles.has(roleName)) {\n const role = await findRoleByNameOrFail(tem, roleName, roleTenantId)\n tem.persist(tem.create(UserRole, { user: existingUser, role, createdAt: new Date() }))\n }\n }\n await tem.flush()\n const roles = Array.from(new Set([...currentRoles, ...roleNames]))\n userSnapshots.push({ user: existingUser, roles, created: false })\n })\n\n if (!existingUser) {\n const baseUsers: Array<{\n email: string\n roles: string[]\n name?: string | null\n passwordHash?: string | null\n generatedPassword?: string | null\n }> = [\n { email: primaryUser.email, roles: primaryRoles, name: resolvePrimaryName(primaryUser) },\n ]\n if (includeDerivedUsers) {\n const adminOverride = readEnvValue(DERIVED_EMAIL_ENV.admin)\n const employeeOverride = readEnvValue(DERIVED_EMAIL_ENV.employee)\n const adminEmail = adminOverride ?? `admin@${DEFAULT_DERIVED_EMAIL_DOMAIN}`\n const employeeEmail = employeeOverride ?? `employee@${DEFAULT_DERIVED_EMAIL_DOMAIN}`\n const envAdminPwd = readEnvValue('OM_INIT_ADMIN_PASSWORD')\n const envEmployeePwd = readEnvValue('OM_INIT_EMPLOYEE_PASSWORD')\n const isProduction = process.env.NODE_ENV === 'production'\n const allowDemo = options.allowDemoDerivedPasswords === true\n if (isProduction && !allowDemo) {\n const missing: string[] = []\n if (!envAdminPwd) missing.push('OM_INIT_ADMIN_PASSWORD')\n if (!envEmployeePwd) missing.push('OM_INIT_EMPLOYEE_PASSWORD')\n if (missing.length) {\n throw new DerivedUserPasswordRequiredError(missing)\n }\n }\n // In non-production, fall back to the well-known DEMO_DERIVED_PASSWORD so\n // local dev workflows stay predictable (admin@/employee@ login with the\n // documented demo password). In production we either have env-supplied\n // values, or \u2014 for opt-in --include-demo-users flows \u2014 we generate a\n // random one-time password and surface it via `generatedPassword` so the\n // operator can capture it. Production callers without env vars and\n // without the opt-in already threw above.\n const fallbackAdminPwd = isProduction ? generateDerivedPassword() : DEMO_DERIVED_PASSWORD\n const fallbackEmployeePwd = isProduction ? generateDerivedPassword() : DEMO_DERIVED_PASSWORD\n const adminPasswordPlain = envAdminPwd ?? fallbackAdminPwd\n const employeePasswordPlain = envEmployeePwd ?? fallbackEmployeePwd\n const adminPasswordHash = await hash(adminPasswordPlain, 10)\n const employeePasswordHash = await hash(employeePasswordPlain, 10)\n addUniqueBaseUser(baseUsers, {\n email: adminEmail,\n roles: ['admin'],\n passwordHash: adminPasswordHash,\n generatedPassword: envAdminPwd ? null : adminPasswordPlain,\n })\n addUniqueBaseUser(baseUsers, {\n email: employeeEmail,\n roles: ['employee'],\n passwordHash: employeePasswordHash,\n generatedPassword: envEmployeePwd ? null : employeePasswordPlain,\n })\n }\n const passwordHash = await resolvePasswordHash(primaryUser)\n\n await em.transactional(async (tem) => {\n const tenant = tem.create(Tenant, {\n name: options.orgName,\n isActive: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n tem.persist(tenant)\n await tem.flush()\n\n const organization = tem.create(Organization, {\n name: options.orgName,\n slug: options.orgSlug ?? null,\n tenant,\n isActive: true,\n depth: 0,\n ancestorIds: [],\n childIds: [],\n descendantIds: [],\n logoPreserveAspectRatio: false,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n tem.persist(organization)\n await tem.flush()\n\n tenantId = String(tenant.id)\n organizationId = String(organization.id)\n const roleTenantId = tenantId\n\n if (isTenantDataEncryptionEnabled()) {\n try {\n const kms = createKmsService()\n if (kms.isHealthy()) {\n if (isEncryptionDebugEnabled()) {\n logger.info('Provisioning tenant DEK', { tenantId: String(tenant.id) })\n }\n await kms.createTenantDek(String(tenant.id))\n if (isEncryptionDebugEnabled()) {\n logger.info('Created tenant DEK during setup', { tenantId: String(tenant.id) })\n }\n } else {\n if (isEncryptionDebugEnabled()) {\n logger.warn('KMS not healthy, skipping tenant DEK creation', { tenantId: String(tenant.id) })\n }\n }\n } catch (err) {\n if (isEncryptionDebugEnabled()) {\n logger.warn('Failed to create tenant DEK', { err })\n }\n }\n }\n\n await ensureRolesInContext(tem, roleNames, roleTenantId)\n await tem.flush()\n\n if (isTenantDataEncryptionEnabled()) {\n // System-scoped maps are resolved from module code, never from a tenant row.\n // Persisting one here would make the tenant-scoped encryption CLIs believe they\n // own that entity and re-wrap its `system:<entityId>` ciphertext under the tenant\n // DEK, which runtime decryption can no longer read.\n for (const spec of defaultEncryptionMaps) {\n if (spec.keyScope === 'system') continue\n const existing = await findOneWithDecryption(tem, EncryptionMap, { entityId: spec.entityId, tenantId: tenant.id, organizationId: organization.id, deletedAt: null }, {}, { tenantId: String(tenant.id), organizationId: String(organization.id) })\n if (!existing) {\n tem.persist(tem.create(EncryptionMap, {\n entityId: spec.entityId,\n tenantId: tenant.id,\n organizationId: organization.id,\n fieldsJson: spec.fields,\n isActive: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n }))\n } else {\n existing.fieldsJson = spec.fields\n existing.isActive = true\n }\n }\n await tem.flush()\n }\n })\n\n await em.transactional(async (tem) => {\n if (!tenantId || !organizationId) return\n const roleTenantId = tenantId\n const encryptionService = isTenantDataEncryptionEnabled()\n ? new TenantDataEncryptionService(tem as any, { kms: createKmsService() })\n : null\n if (encryptionService) {\n await encryptionService.invalidateMap('auth:user', String(tenantId), String(organizationId))\n await encryptionService.invalidateMap('auth:user', String(tenantId), null)\n }\n\n for (const base of baseUsers) {\n const resolvedPasswordHash = base.passwordHash ?? passwordHash\n let user = await findOneWithDecryption(tem, User, { email: base.email }, {}, { tenantId: tenantId ?? null, organizationId: organizationId ?? null })\n const confirm = primaryUser.confirm ?? true\n const encryptedPayload = encryptionService\n ? await encryptionService.encryptEntityPayload('auth:user', { email: base.email }, tenantId, organizationId)\n : { email: base.email, emailHash: computeEmailHash(base.email) }\n if (user) {\n user.passwordHash = resolvedPasswordHash\n user.organizationId = organizationId\n user.tenantId = tenantId\n if (isTenantDataEncryptionEnabled()) {\n user.email = encryptedPayload.email as any\n user.emailHash = (encryptedPayload as any).emailHash ?? computeEmailHash(base.email)\n }\n if (base.name) user.name = base.name\n if (confirm) user.isConfirmed = true\n tem.persist(user)\n userSnapshots.push({ user, roles: base.roles, created: false, generatedPassword: base.generatedPassword ?? null })\n } else {\n user = tem.create(User, {\n email: (encryptedPayload as any).email ?? base.email,\n emailHash: isTenantDataEncryptionEnabled() ? (encryptedPayload as any).emailHash ?? computeEmailHash(base.email) : undefined,\n passwordHash: resolvedPasswordHash,\n organizationId,\n tenantId,\n name: base.name ?? undefined,\n isConfirmed: confirm,\n createdAt: new Date(),\n })\n tem.persist(user)\n userSnapshots.push({ user, roles: base.roles, created: true, generatedPassword: base.generatedPassword ?? null })\n }\n await tem.flush()\n for (const roleName of base.roles) {\n const role = await findRoleByNameOrFail(tem, roleName, roleTenantId)\n const existingLink = await findOneWithDecryption(tem, UserRole, { user, role }, {}, { tenantId: tenantId ?? null, organizationId: null })\n if (!existingLink) tem.persist(tem.create(UserRole, { user, role, createdAt: new Date() }))\n }\n await tem.flush()\n }\n })\n }\n\n if (!tenantId || !organizationId) {\n throw new Error('SETUP_FAILED')\n }\n\n if (!reusedExistingUser) {\n await rebuildHierarchyForTenant(em, tenantId)\n }\n\n await ensureDefaultRoleAcls(em, tenantId, resolvedModules, { includeSuperadminRole })\n await deactivateDemoUsersIfSelfOnboardingEnabled(em)\n\n // Call module onTenantCreated hooks\n for (const mod of resolvedModules) {\n if (mod.setup?.onTenantCreated) {\n await mod.setup.onTenantCreated({ em, tenantId, organizationId })\n }\n }\n\n return {\n tenantId,\n organizationId,\n users: userSnapshots,\n reusedExistingUser,\n }\n}\n\nfunction resolvePrimaryName(input: PrimaryUserInput): string | null {\n if (input.displayName && input.displayName.trim()) return input.displayName.trim()\n const parts = [input.firstName, input.lastName].map((value) => value?.trim()).filter(Boolean)\n if (parts.length) return parts.join(' ')\n return null\n}\n\nfunction readEnvValue(key: string): string | undefined {\n const value = process.env[key]\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\nfunction addUniqueBaseUser(\n baseUsers: Array<{ email: string; roles: string[]; name?: string | null; passwordHash?: string | null; generatedPassword?: string | null }>,\n entry: { email: string; roles: string[]; name?: string | null; passwordHash?: string | null; generatedPassword?: string | null },\n) {\n if (!entry.email) return\n const normalized = entry.email.toLowerCase()\n if (baseUsers.some((user) => user.email.toLowerCase() === normalized)) return\n baseUsers.push(entry)\n}\n\n/**\n * Well-known demo password seeded for derived admin@/employee@ accounts in\n * non-production (`NODE_ENV !== 'production'`) when no env override is set.\n * Keeps the documented `yarn dev` / `mercato init` DX predictable. Never used\n * in production: the production branch either consumes env-supplied values or\n * falls back to `generateDerivedPassword()` so credentials remain non-guessable.\n */\nconst DEMO_DERIVED_PASSWORD = 'secret'\n\n/**\n * Generate a 16-character base64url password (96 bits of entropy) for a derived\n * demo user in production when no env override is provided AND the caller\n * explicitly opted in via `allowDemoDerivedPasswords`. Surfaced via the\n * `users[].generatedPassword` snapshot so CLI callers can print it to the\n * operator \u2014 there is no other recovery path for these credentials.\n */\nfunction generateDerivedPassword(): string {\n return randomBytes(12).toString('base64url')\n}\n\nfunction isDemoModeEnabled(): boolean {\n const parsed = parseBooleanToken(process.env.DEMO_MODE ?? '')\n return parsed === false ? false : true\n}\n\nfunction shouldKeepDemoSuperadminDuringInit(): boolean {\n if (process.env.OM_INIT_FLOW !== 'true') return false\n if (!readEnvValue('OM_INIT_SUPERADMIN_EMAIL')) return false\n return isDemoModeEnabled()\n}\n\nasync function resolvePasswordHash(input: PrimaryUserInput): Promise<string | null> {\n if (typeof input.hashedPassword === 'string') return input.hashedPassword\n if (input.password) return hash(input.password, 10)\n return null\n}\n\nexport async function ensureDefaultRoleAcls(\n em: EntityManager,\n tenantId: string,\n modules: Module[],\n options: { includeSuperadminRole?: boolean } = {},\n) {\n const includeSuperadminRole = options.includeSuperadminRole ?? true\n const roleTenantId = normalizeTenantId(tenantId) ?? null\n const superadminRole = includeSuperadminRole ? await findRoleByName(em, 'superadmin', roleTenantId) : null\n const adminRole = await findRoleByName(em, 'admin', roleTenantId)\n const employeeRole = await findRoleByName(em, 'employee', roleTenantId)\n\n // Merge features from all enabled modules' setup configs\n const builtInRoles = ['superadmin', 'admin', 'employee'] as const\n const superadminFeatures: string[] = []\n const adminFeatures: string[] = []\n const employeeFeatures: string[] = []\n const customRoleFeatures = new Map<string, string[]>()\n\n for (const mod of modules) {\n const roleFeatures = mod.setup?.defaultRoleFeatures\n if (!roleFeatures) continue\n if (roleFeatures.superadmin) superadminFeatures.push(...roleFeatures.superadmin)\n if (roleFeatures.admin) adminFeatures.push(...roleFeatures.admin)\n if (roleFeatures.employee) employeeFeatures.push(...roleFeatures.employee)\n\n // Collect features for custom roles (any key not in builtInRoles)\n for (const [roleName, features] of Object.entries(roleFeatures)) {\n if ((builtInRoles as readonly string[]).includes(roleName)) continue\n if (!Array.isArray(features)) continue\n const existing = customRoleFeatures.get(roleName) ?? []\n existing.push(...features)\n customRoleFeatures.set(roleName, existing)\n }\n }\n\n logger.info('Seeded default role features', {\n superadmin: superadminFeatures,\n admin: adminFeatures,\n employee: employeeFeatures,\n ...(customRoleFeatures.size > 0\n ? Object.fromEntries(customRoleFeatures)\n : {}),\n })\n\n if (includeSuperadminRole && superadminRole) {\n await ensureRoleAclFor(em, superadminRole, tenantId, superadminFeatures, { isSuperAdmin: true })\n }\n if (adminRole) {\n await ensureRoleAclFor(em, adminRole, tenantId, adminFeatures)\n }\n if (employeeRole) {\n await ensureRoleAclFor(em, employeeRole, tenantId, employeeFeatures)\n }\n\n // Seed ACLs for custom roles defined by app modules.\n // NOTE: Custom roles may not exist yet if they are created in seedDefaults\n // (which runs after this function). In that case, use ensureCustomRoleAcls()\n // after seedDefaults to pick them up.\n for (const [roleName, features] of customRoleFeatures) {\n const role = await findRoleByName(em, roleName, roleTenantId)\n if (role) {\n await ensureRoleAclFor(em, role, tenantId, features)\n }\n }\n}\n\n/**\n * Seed ACLs for custom roles defined in module defaultRoleFeatures.\n * Call this AFTER seedDefaults to pick up roles created by app modules.\n * Safe to call multiple times \u2014 ensureRoleAclFor merges features idempotently.\n */\nexport async function ensureCustomRoleAcls(\n em: EntityManager,\n tenantId: string,\n modules?: Module[],\n): Promise<void> {\n const resolvedModules = modules ?? tryGetModules()\n const roleTenantId = normalizeTenantId(tenantId) ?? null\n const builtInRoles = ['superadmin', 'admin', 'employee']\n const customRoleFeatures = new Map<string, string[]>()\n\n for (const mod of resolvedModules) {\n const roleFeatures = mod.setup?.defaultRoleFeatures\n if (!roleFeatures) continue\n for (const [roleName, features] of Object.entries(roleFeatures)) {\n if (builtInRoles.includes(roleName)) continue\n if (!Array.isArray(features)) continue\n const existing = customRoleFeatures.get(roleName) ?? []\n existing.push(...features)\n customRoleFeatures.set(roleName, existing)\n }\n }\n\n if (customRoleFeatures.size === 0) return\n\n let seeded = 0\n for (const [roleName, features] of customRoleFeatures) {\n const role = await findRoleByName(em, roleName, roleTenantId)\n if (role) {\n await ensureRoleAclFor(em, role, tenantId, features)\n seeded++\n }\n }\n if (seeded > 0) {\n logger.info('Seeded custom role ACLs', { seeded })\n }\n}\n\nasync function ensureRoleAclFor(\n em: EntityManager,\n role: Role,\n tenantId: string,\n features: string[],\n options: { isSuperAdmin?: boolean } = {},\n) {\n // Deduplicated on the create path too, not only on merge. `ensureDefaultRoleAcls` concatenates\n // every enabled module's declared features, and two modules legitimately declare the same\n // grant (the example module asks for `payment_gateways.view`, and so does that module itself),\n // so a first init used to store the string twice while the second run silently collapsed it.\n // Same list, two shapes, depending only on how many times setup had run.\n const uniqueFeatures = Array.from(new Set(features))\n const existing = await findOneWithDecryption(em, RoleAcl, { role, tenantId }, {}, { tenantId, organizationId: null })\n if (!existing) {\n const acl = em.create(RoleAcl, {\n role,\n tenantId,\n featuresJson: uniqueFeatures,\n isSuperAdmin: !!options.isSuperAdmin,\n createdAt: new Date(),\n })\n await em.persist(acl).flush()\n return\n }\n const currentFeatures = Array.isArray(existing.featuresJson) ? existing.featuresJson : []\n const merged = Array.from(new Set([...currentFeatures, ...uniqueFeatures]))\n const changed =\n merged.length !== currentFeatures.length ||\n merged.some((value, index) => value !== currentFeatures[index])\n if (changed) existing.featuresJson = merged\n if (options.isSuperAdmin && !existing.isSuperAdmin) {\n existing.isSuperAdmin = true\n }\n if (changed || options.isSuperAdmin) {\n await em.persist(existing).flush()\n }\n}\n\n/**\n * Neutralizes every demo account the setup path may have seeded\n * (superadmin, admin, employee \u2014 honoring OM_INIT_*_EMAIL overrides)\n * when SELF_SERVICE_ONBOARDING_ENABLED is on and the operator did not\n * opt in to keeping demo credentials via shouldKeepDemoSuperadminDuringInit.\n *\n * Each user is processed in its own try/catch so a single failure (e.g.\n * decryption error on a legacy row) does not skip the remaining accounts.\n *\n * @internal Exported for tests; not part of the public auth API.\n */\nexport async function deactivateDemoUsersIfSelfOnboardingEnabled(em: EntityManager) {\n if (process.env.SELF_SERVICE_ONBOARDING_ENABLED !== 'true') return\n if (shouldKeepDemoSuperadminDuringInit()) return\n for (const { role, email } of resolveDemoUserEmails()) {\n try {\n const user = await findOneWithDecryption(\n em,\n User,\n { email },\n {},\n { tenantId: null, organizationId: null },\n )\n if (!user) continue\n let dirty = false\n if (user.passwordHash) {\n user.passwordHash = null\n dirty = true\n }\n if (user.isConfirmed !== false) {\n user.isConfirmed = false\n dirty = true\n }\n if (dirty) {\n await em.persist(user).flush()\n }\n } catch (error) {\n logger.error('Failed to deactivate demo user', { role, email, err: error })\n }\n }\n}\n\n/**\n * @deprecated Renamed to {@link deactivateDemoUsersIfSelfOnboardingEnabled}\n * because the helper now neutralizes admin/employee demo accounts in addition\n * to superadmin (security tracker finding #5). Kept as an internal alias to\n * avoid breaking any out-of-tree caller that imported the old name; will be\n * removed in a future major.\n */\nconst deactivateDemoSuperAdminIfSelfOnboardingEnabled = deactivateDemoUsersIfSelfOnboardingEnabled\n\n/** Try to get modules from runtime registry; returns empty array if not yet registered. */\nfunction tryGetModules(): Module[] {\n try {\n const { getModules } = require('@open-mercato/shared/lib/modules/registry')\n return getModules()\n } catch {\n return []\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { hash } from 'bcryptjs'\nimport { randomBytes } from 'node:crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { Role, RoleAcl, User, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { Tenant, Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { rebuildHierarchyForTenant } from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport { normalizeTenantId } from './tenantAccess'\nimport { computeEmailHash, emailHashLookupValues } from '@open-mercato/core/modules/auth/lib/emailHash'\nimport { getDefaultEncryptionMaps, type Module } from '@open-mercato/shared/modules/registry'\nimport { isEncryptionDebugEnabled, isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { EncryptionMap } from '@open-mercato/core/modules/entities/data/entities'\nimport { createKmsService } from '@open-mercato/shared/lib/encryption/kms'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('auth').child({ component: 'setup' })\n\nconst DEFAULT_ROLE_NAMES = ['employee', 'admin', 'superadmin'] as const\nconst DEMO_SUPERADMIN_EMAIL = 'superadmin@acme.com'\nconst DEFAULT_DERIVED_EMAIL_DOMAIN = DEMO_SUPERADMIN_EMAIL.split('@')[1] ?? 'acme.com'\n\nexport type EnsureRolesOptions = {\n roleNames?: string[]\n tenantId?: string\n}\n\nasync function ensureRolesInContext(\n em: EntityManager,\n roleNames: string[],\n tenantId: string,\n) {\n for (const name of roleNames) {\n const existing = await findOneWithDecryption(em, Role, { name, tenantId }, {}, { tenantId, organizationId: null })\n if (existing) continue\n em.persist(em.create(Role, { name, tenantId, minActiveHolders: name === 'admin' ? 1 : 0, createdAt: new Date() }))\n }\n}\n\nexport async function ensureRoles(em: EntityManager, options: EnsureRolesOptions = {}) {\n const roleNames = options.roleNames ?? [...DEFAULT_ROLE_NAMES]\n const tenantId = normalizeTenantId(options.tenantId ?? null)\n if (!tenantId) {\n throw new Error('ensureRoles requires a tenantId \u2014 global roles are not supported')\n }\n await em.transactional(async (tem) => {\n await ensureRolesInContext(tem, roleNames, tenantId)\n await tem.flush()\n })\n}\n\nasync function findRoleByName(\n em: EntityManager,\n name: string,\n tenantId: string | null,\n): Promise<Role | null> {\n const normalizedTenant = normalizeTenantId(tenantId ?? null) ?? null\n return findOneWithDecryption(em, Role, { name, tenantId: normalizedTenant }, {}, { tenantId: normalizedTenant, organizationId: null })\n}\n\nasync function findRoleByNameOrFail(\n em: EntityManager,\n name: string,\n tenantId: string | null,\n): Promise<Role> {\n const role = await findRoleByName(em, name, tenantId)\n if (!role) throw new Error(`ROLE_NOT_FOUND:${name}`)\n return role\n}\n\ntype PrimaryUserInput = {\n email: string\n password?: string\n hashedPassword?: string | null\n firstName?: string | null\n lastName?: string | null\n displayName?: string | null\n confirm?: boolean\n}\n\nconst DERIVED_EMAIL_ENV = {\n admin: 'OM_INIT_ADMIN_EMAIL',\n employee: 'OM_INIT_EMPLOYEE_EMAIL',\n} as const\n\nexport type DemoUserRole = 'superadmin' | 'admin' | 'employee'\nexport type DemoUserEmail = { role: DemoUserRole; email: string }\n\n/**\n * Returns the canonical list of demo user emails the setup path may have\n * seeded. Honors OM_INIT_ADMIN_EMAIL / OM_INIT_EMPLOYEE_EMAIL exactly the\n * same way the derived-user seeding branch does, so the deactivation loop\n * never drifts from the seeding loop.\n *\n * Pure function \u2014 no side effects, safe to unit-test in isolation.\n *\n * @internal Exported for tests; not part of the public auth API.\n */\nexport function resolveDemoUserEmails(): DemoUserEmail[] {\n const adminEmail = readEnvValue(DERIVED_EMAIL_ENV.admin) ?? `admin@${DEFAULT_DERIVED_EMAIL_DOMAIN}`\n const employeeEmail = readEnvValue(DERIVED_EMAIL_ENV.employee) ?? `employee@${DEFAULT_DERIVED_EMAIL_DOMAIN}`\n return [\n { role: 'superadmin', email: DEMO_SUPERADMIN_EMAIL },\n { role: 'admin', email: adminEmail },\n { role: 'employee', email: employeeEmail },\n ]\n}\n\nexport type SetupInitialTenantOptions = {\n orgName: string\n primaryUser: PrimaryUserInput\n roleNames?: string[]\n includeDerivedUsers?: boolean\n failIfUserExists?: boolean\n primaryUserRoles?: string[]\n includeSuperadminRole?: boolean\n /** Optional list of enabled modules. When provided, module setup hooks are called. */\n modules?: Module[]\n /**\n * Optional global slug to persist on the new Organization. When set, a\n * pre-flight uniqueness check across all tenants throws OrgSlugExistsError\n * on collision so scriptable callers can fail loudly instead of silently\n * reusing or clobbering an existing organization.\n */\n orgSlug?: string\n /**\n * Opt-in flag that allows seeding the derived admin/employee accounts when\n * the OM_INIT_ADMIN_PASSWORD / OM_INIT_EMPLOYEE_PASSWORD env vars are unset.\n *\n * - In non-production (`NODE_ENV !== 'production'`): the demo accounts are\n * seeded with the well-known `'secret'` password so local dev workflows\n * (e.g. `mercato init`) stay predictable for developers.\n * - In production: the fallback uses a randomly generated 96-bit password\n * (printed once by the CLI) so an operator who deliberately opts in still\n * avoids the historical hardcoded credential. When this flag is false/unset\n * in production and the env vars are missing, `DerivedUserPasswordRequiredError`\n * is thrown instead of silently seeding any account.\n */\n allowDemoDerivedPasswords?: boolean\n}\n\nexport class OrgSlugExistsError extends Error {\n constructor(public readonly slug: string) {\n super(`ORG_SLUG_EXISTS: an organization with slug \"${slug}\" already exists`)\n this.name = 'OrgSlugExistsError'\n }\n}\n\nexport class DerivedUserPasswordRequiredError extends Error {\n constructor(public readonly missing: string[]) {\n super(\n `DERIVED_USER_PASSWORD_REQUIRED: missing ${missing.join(', ')} (set the env vars, pass allowDemoDerivedPasswords: true / --include-demo-users in non-production, or disable derived user seeding)`,\n )\n this.name = 'DerivedUserPasswordRequiredError'\n }\n}\n\nexport type SetupInitialTenantResult = {\n tenantId: string\n organizationId: string\n users: Array<{ user: User; roles: string[]; created: boolean; generatedPassword?: string | null }>\n reusedExistingUser: boolean\n}\n\nexport async function setupInitialTenant(\n em: EntityManager,\n options: SetupInitialTenantOptions,\n): Promise<SetupInitialTenantResult> {\n const {\n primaryUser,\n includeDerivedUsers = true,\n failIfUserExists = false,\n primaryUserRoles,\n includeSuperadminRole = true,\n } = options\n const primaryRolesInput = primaryUserRoles && primaryUserRoles.length ? primaryUserRoles : ['superadmin']\n const primaryRoles = includeSuperadminRole\n ? primaryRolesInput\n : primaryRolesInput.filter((role) => role !== 'superadmin')\n if (primaryRoles.length === 0) {\n throw new Error('PRIMARY_ROLES_REQUIRED')\n }\n const defaultRoleNames = options.roleNames ?? [...DEFAULT_ROLE_NAMES]\n const resolvedRoleNames = includeSuperadminRole\n ? defaultRoleNames\n : defaultRoleNames.filter((role) => role !== 'superadmin')\n const roleNames = Array.from(new Set([...resolvedRoleNames, ...primaryRoles]))\n const resolvedModules = options.modules ?? tryGetModules()\n const defaultEncryptionMaps = getDefaultEncryptionMaps(resolvedModules)\n\n const mainEmail = primaryUser.email\n const existingUserByHash = await findOneWithDecryption(\n em,\n User,\n { emailHash: { $in: emailHashLookupValues(mainEmail) }, deletedAt: null },\n {},\n { tenantId: null, organizationId: null },\n )\n const existingUser = existingUserByHash ?? await findOneWithDecryption(\n em,\n User,\n { email: mainEmail, deletedAt: null },\n {},\n { tenantId: null, organizationId: null },\n )\n if (existingUser && failIfUserExists) {\n throw new Error('USER_EXISTS')\n }\n\n if (options.orgSlug) {\n const slugConflict = await findOneWithDecryption(\n em,\n Organization,\n { slug: options.orgSlug },\n {},\n { tenantId: null, organizationId: null },\n )\n if (slugConflict) {\n throw new OrgSlugExistsError(options.orgSlug)\n }\n }\n\n let tenantId: string | undefined\n let organizationId: string | undefined\n let reusedExistingUser = false\n const userSnapshots: Array<{ user: User; roles: string[]; created: boolean; generatedPassword?: string | null }> = []\n\n await em.transactional(async (tem) => {\n if (!existingUser) return\n reusedExistingUser = true\n tenantId = existingUser.tenantId ? String(existingUser.tenantId) : undefined\n organizationId = existingUser.organizationId ? String(existingUser.organizationId) : undefined\n const roleTenantId = normalizeTenantId(existingUser.tenantId ?? null) ?? null\n if (!roleTenantId) {\n throw new Error('Cannot reuse a user without a tenantId \u2014 global roles are not supported')\n }\n\n await ensureRolesInContext(tem, roleNames, roleTenantId)\n await tem.flush()\n\n const requiredRoleSet = new Set([...roleNames, ...primaryRoles])\n const links = await findWithDecryption(\n tem,\n UserRole,\n { user: existingUser },\n { populate: ['role'] },\n { tenantId: roleTenantId, organizationId: null },\n )\n const currentRoles = new Set(links.map((link) => link.role.name))\n for (const roleName of requiredRoleSet) {\n if (!currentRoles.has(roleName)) {\n const role = await findRoleByNameOrFail(tem, roleName, roleTenantId)\n tem.persist(tem.create(UserRole, { user: existingUser, role, createdAt: new Date() }))\n }\n }\n await tem.flush()\n const roles = Array.from(new Set([...currentRoles, ...roleNames]))\n userSnapshots.push({ user: existingUser, roles, created: false })\n })\n\n if (!existingUser) {\n const baseUsers: Array<{\n email: string\n roles: string[]\n name?: string | null\n passwordHash?: string | null\n generatedPassword?: string | null\n }> = [\n { email: primaryUser.email, roles: primaryRoles, name: resolvePrimaryName(primaryUser) },\n ]\n if (includeDerivedUsers) {\n const adminOverride = readEnvValue(DERIVED_EMAIL_ENV.admin)\n const employeeOverride = readEnvValue(DERIVED_EMAIL_ENV.employee)\n const adminEmail = adminOverride ?? `admin@${DEFAULT_DERIVED_EMAIL_DOMAIN}`\n const employeeEmail = employeeOverride ?? `employee@${DEFAULT_DERIVED_EMAIL_DOMAIN}`\n const envAdminPwd = readEnvValue('OM_INIT_ADMIN_PASSWORD')\n const envEmployeePwd = readEnvValue('OM_INIT_EMPLOYEE_PASSWORD')\n const isProduction = process.env.NODE_ENV === 'production'\n const allowDemo = options.allowDemoDerivedPasswords === true\n if (isProduction && !allowDemo) {\n const missing: string[] = []\n if (!envAdminPwd) missing.push('OM_INIT_ADMIN_PASSWORD')\n if (!envEmployeePwd) missing.push('OM_INIT_EMPLOYEE_PASSWORD')\n if (missing.length) {\n throw new DerivedUserPasswordRequiredError(missing)\n }\n }\n // In non-production, fall back to the well-known DEMO_DERIVED_PASSWORD so\n // local dev workflows stay predictable (admin@/employee@ login with the\n // documented demo password). In production we either have env-supplied\n // values, or \u2014 for opt-in --include-demo-users flows \u2014 we generate a\n // random one-time password and surface it via `generatedPassword` so the\n // operator can capture it. Production callers without env vars and\n // without the opt-in already threw above.\n const fallbackAdminPwd = isProduction ? generateDerivedPassword() : DEMO_DERIVED_PASSWORD\n const fallbackEmployeePwd = isProduction ? generateDerivedPassword() : DEMO_DERIVED_PASSWORD\n const adminPasswordPlain = envAdminPwd ?? fallbackAdminPwd\n const employeePasswordPlain = envEmployeePwd ?? fallbackEmployeePwd\n const adminPasswordHash = await hash(adminPasswordPlain, 10)\n const employeePasswordHash = await hash(employeePasswordPlain, 10)\n addUniqueBaseUser(baseUsers, {\n email: adminEmail,\n roles: ['admin'],\n passwordHash: adminPasswordHash,\n generatedPassword: envAdminPwd ? null : adminPasswordPlain,\n })\n addUniqueBaseUser(baseUsers, {\n email: employeeEmail,\n roles: ['employee'],\n passwordHash: employeePasswordHash,\n generatedPassword: envEmployeePwd ? null : employeePasswordPlain,\n })\n }\n const passwordHash = await resolvePasswordHash(primaryUser)\n\n await em.transactional(async (tem) => {\n const tenant = tem.create(Tenant, {\n name: options.orgName,\n isActive: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n tem.persist(tenant)\n await tem.flush()\n\n const organization = tem.create(Organization, {\n name: options.orgName,\n slug: options.orgSlug ?? null,\n tenant,\n isActive: true,\n depth: 0,\n ancestorIds: [],\n childIds: [],\n descendantIds: [],\n logoPreserveAspectRatio: false,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n tem.persist(organization)\n await tem.flush()\n\n tenantId = String(tenant.id)\n organizationId = String(organization.id)\n const roleTenantId = tenantId\n\n if (isTenantDataEncryptionEnabled()) {\n try {\n const kms = createKmsService()\n if (kms.isHealthy()) {\n if (isEncryptionDebugEnabled()) {\n logger.info('Provisioning tenant DEK', { tenantId: String(tenant.id) })\n }\n await kms.createTenantDek(String(tenant.id))\n if (isEncryptionDebugEnabled()) {\n logger.info('Created tenant DEK during setup', { tenantId: String(tenant.id) })\n }\n } else {\n if (isEncryptionDebugEnabled()) {\n logger.warn('KMS not healthy, skipping tenant DEK creation', { tenantId: String(tenant.id) })\n }\n }\n } catch (err) {\n if (isEncryptionDebugEnabled()) {\n logger.warn('Failed to create tenant DEK', { err })\n }\n }\n }\n\n await ensureRolesInContext(tem, roleNames, roleTenantId)\n await tem.flush()\n\n if (isTenantDataEncryptionEnabled()) {\n // System-scoped maps are resolved from module code, never from a tenant row.\n // Persisting one here would make the tenant-scoped encryption CLIs believe they\n // own that entity and re-wrap its `system:<entityId>` ciphertext under the tenant\n // DEK, which runtime decryption can no longer read.\n for (const spec of defaultEncryptionMaps) {\n if (spec.keyScope === 'system') continue\n const existing = await findOneWithDecryption(tem, EncryptionMap, { entityId: spec.entityId, tenantId: tenant.id, organizationId: organization.id, deletedAt: null }, {}, { tenantId: String(tenant.id), organizationId: String(organization.id) })\n if (!existing) {\n tem.persist(tem.create(EncryptionMap, {\n entityId: spec.entityId,\n tenantId: tenant.id,\n organizationId: organization.id,\n fieldsJson: spec.fields,\n isActive: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n }))\n } else {\n existing.fieldsJson = spec.fields\n existing.isActive = true\n }\n }\n await tem.flush()\n }\n })\n\n await em.transactional(async (tem) => {\n if (!tenantId || !organizationId) return\n const roleTenantId = tenantId\n const encryptionService = isTenantDataEncryptionEnabled()\n ? new TenantDataEncryptionService(tem as any, { kms: createKmsService() })\n : null\n if (encryptionService) {\n await encryptionService.invalidateMap('auth:user', String(tenantId), String(organizationId))\n await encryptionService.invalidateMap('auth:user', String(tenantId), null)\n }\n\n for (const base of baseUsers) {\n const resolvedPasswordHash = base.passwordHash ?? passwordHash\n let user = await findOneWithDecryption(tem, User, { email: base.email }, {}, { tenantId: tenantId ?? null, organizationId: organizationId ?? null })\n const confirm = primaryUser.confirm ?? true\n const encryptedPayload = encryptionService\n ? await encryptionService.encryptEntityPayload('auth:user', { email: base.email }, tenantId, organizationId)\n : { email: base.email, emailHash: computeEmailHash(base.email) }\n if (user) {\n user.passwordHash = resolvedPasswordHash\n user.organizationId = organizationId\n user.tenantId = tenantId\n if (isTenantDataEncryptionEnabled()) {\n user.email = encryptedPayload.email as any\n user.emailHash = (encryptedPayload as any).emailHash ?? computeEmailHash(base.email)\n }\n if (base.name) user.name = base.name\n if (confirm) user.isConfirmed = true\n tem.persist(user)\n userSnapshots.push({ user, roles: base.roles, created: false, generatedPassword: base.generatedPassword ?? null })\n } else {\n user = tem.create(User, {\n email: (encryptedPayload as any).email ?? base.email,\n emailHash: isTenantDataEncryptionEnabled() ? (encryptedPayload as any).emailHash ?? computeEmailHash(base.email) : undefined,\n passwordHash: resolvedPasswordHash,\n organizationId,\n tenantId,\n name: base.name ?? undefined,\n isConfirmed: confirm,\n createdAt: new Date(),\n })\n tem.persist(user)\n userSnapshots.push({ user, roles: base.roles, created: true, generatedPassword: base.generatedPassword ?? null })\n }\n await tem.flush()\n for (const roleName of base.roles) {\n const role = await findRoleByNameOrFail(tem, roleName, roleTenantId)\n const existingLink = await findOneWithDecryption(tem, UserRole, { user, role }, {}, { tenantId: tenantId ?? null, organizationId: null })\n if (!existingLink) tem.persist(tem.create(UserRole, { user, role, createdAt: new Date() }))\n }\n await tem.flush()\n }\n })\n }\n\n if (!tenantId || !organizationId) {\n throw new Error('SETUP_FAILED')\n }\n\n if (!reusedExistingUser) {\n await rebuildHierarchyForTenant(em, tenantId)\n }\n\n await ensureDefaultRoleAcls(em, tenantId, resolvedModules, { includeSuperadminRole })\n await deactivateDemoUsersIfSelfOnboardingEnabled(em)\n\n // Call module onTenantCreated hooks\n for (const mod of resolvedModules) {\n if (mod.setup?.onTenantCreated) {\n await mod.setup.onTenantCreated({ em, tenantId, organizationId })\n }\n }\n\n // Custom roles are often created inside onTenantCreated hooks, so they did not\n // exist yet when ensureDefaultRoleAcls ran above and were skipped there. Sync\n // again now that the hooks have had a chance to create them \u2014 idempotent, same\n // pattern as the `mercato auth sync-role-acls` CLI command.\n await ensureCustomRoleAcls(em, tenantId, resolvedModules)\n\n return {\n tenantId,\n organizationId,\n users: userSnapshots,\n reusedExistingUser,\n }\n}\n\nfunction resolvePrimaryName(input: PrimaryUserInput): string | null {\n if (input.displayName && input.displayName.trim()) return input.displayName.trim()\n const parts = [input.firstName, input.lastName].map((value) => value?.trim()).filter(Boolean)\n if (parts.length) return parts.join(' ')\n return null\n}\n\nfunction readEnvValue(key: string): string | undefined {\n const value = process.env[key]\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\nfunction addUniqueBaseUser(\n baseUsers: Array<{ email: string; roles: string[]; name?: string | null; passwordHash?: string | null; generatedPassword?: string | null }>,\n entry: { email: string; roles: string[]; name?: string | null; passwordHash?: string | null; generatedPassword?: string | null },\n) {\n if (!entry.email) return\n const normalized = entry.email.toLowerCase()\n if (baseUsers.some((user) => user.email.toLowerCase() === normalized)) return\n baseUsers.push(entry)\n}\n\n/**\n * Well-known demo password seeded for derived admin@/employee@ accounts in\n * non-production (`NODE_ENV !== 'production'`) when no env override is set.\n * Keeps the documented `yarn dev` / `mercato init` DX predictable. Never used\n * in production: the production branch either consumes env-supplied values or\n * falls back to `generateDerivedPassword()` so credentials remain non-guessable.\n */\nconst DEMO_DERIVED_PASSWORD = 'secret'\n\n/**\n * Generate a 16-character base64url password (96 bits of entropy) for a derived\n * demo user in production when no env override is provided AND the caller\n * explicitly opted in via `allowDemoDerivedPasswords`. Surfaced via the\n * `users[].generatedPassword` snapshot so CLI callers can print it to the\n * operator \u2014 there is no other recovery path for these credentials.\n */\nfunction generateDerivedPassword(): string {\n return randomBytes(12).toString('base64url')\n}\n\nfunction isDemoModeEnabled(): boolean {\n const parsed = parseBooleanToken(process.env.DEMO_MODE ?? '')\n return parsed === false ? false : true\n}\n\nfunction shouldKeepDemoSuperadminDuringInit(): boolean {\n if (process.env.OM_INIT_FLOW !== 'true') return false\n if (!readEnvValue('OM_INIT_SUPERADMIN_EMAIL')) return false\n return isDemoModeEnabled()\n}\n\nasync function resolvePasswordHash(input: PrimaryUserInput): Promise<string | null> {\n if (typeof input.hashedPassword === 'string') return input.hashedPassword\n if (input.password) return hash(input.password, 10)\n return null\n}\n\nexport async function ensureDefaultRoleAcls(\n em: EntityManager,\n tenantId: string,\n modules: Module[],\n options: { includeSuperadminRole?: boolean } = {},\n) {\n const includeSuperadminRole = options.includeSuperadminRole ?? true\n const roleTenantId = normalizeTenantId(tenantId) ?? null\n const superadminRole = includeSuperadminRole ? await findRoleByName(em, 'superadmin', roleTenantId) : null\n const adminRole = await findRoleByName(em, 'admin', roleTenantId)\n const employeeRole = await findRoleByName(em, 'employee', roleTenantId)\n\n // Merge features from all enabled modules' setup configs\n const builtInRoles = ['superadmin', 'admin', 'employee'] as const\n const superadminFeatures: string[] = []\n const adminFeatures: string[] = []\n const employeeFeatures: string[] = []\n const customRoleFeatures = new Map<string, string[]>()\n\n for (const mod of modules) {\n const roleFeatures = mod.setup?.defaultRoleFeatures\n if (!roleFeatures) continue\n if (roleFeatures.superadmin) superadminFeatures.push(...roleFeatures.superadmin)\n if (roleFeatures.admin) adminFeatures.push(...roleFeatures.admin)\n if (roleFeatures.employee) employeeFeatures.push(...roleFeatures.employee)\n\n // Collect features for custom roles (any key not in builtInRoles)\n for (const [roleName, features] of Object.entries(roleFeatures)) {\n if ((builtInRoles as readonly string[]).includes(roleName)) continue\n if (!Array.isArray(features)) continue\n const existing = customRoleFeatures.get(roleName) ?? []\n existing.push(...features)\n customRoleFeatures.set(roleName, existing)\n }\n }\n\n logger.info('Seeded default role features', {\n superadmin: superadminFeatures,\n admin: adminFeatures,\n employee: employeeFeatures,\n ...(customRoleFeatures.size > 0\n ? Object.fromEntries(customRoleFeatures)\n : {}),\n })\n\n if (includeSuperadminRole && superadminRole) {\n await ensureRoleAclFor(em, superadminRole, tenantId, superadminFeatures, { isSuperAdmin: true })\n }\n if (adminRole) {\n await ensureRoleAclFor(em, adminRole, tenantId, adminFeatures)\n }\n if (employeeRole) {\n await ensureRoleAclFor(em, employeeRole, tenantId, employeeFeatures)\n }\n\n // Seed ACLs for custom roles defined by app modules.\n // NOTE: Custom roles may not exist yet if they are created in seedDefaults\n // (which runs after this function). In that case, use ensureCustomRoleAcls()\n // after seedDefaults to pick them up.\n for (const [roleName, features] of customRoleFeatures) {\n const role = await findRoleByName(em, roleName, roleTenantId)\n if (role) {\n await ensureRoleAclFor(em, role, tenantId, features)\n }\n }\n}\n\n/**\n * Seed ACLs for custom roles defined in module defaultRoleFeatures.\n * Call this AFTER seedDefaults to pick up roles created by app modules.\n * Safe to call multiple times \u2014 ensureRoleAclFor merges features idempotently.\n */\nexport async function ensureCustomRoleAcls(\n em: EntityManager,\n tenantId: string,\n modules?: Module[],\n): Promise<void> {\n const resolvedModules = modules ?? tryGetModules()\n const roleTenantId = normalizeTenantId(tenantId) ?? null\n const builtInRoles = ['superadmin', 'admin', 'employee']\n const customRoleFeatures = new Map<string, string[]>()\n\n for (const mod of resolvedModules) {\n const roleFeatures = mod.setup?.defaultRoleFeatures\n if (!roleFeatures) continue\n for (const [roleName, features] of Object.entries(roleFeatures)) {\n if (builtInRoles.includes(roleName)) continue\n if (!Array.isArray(features)) continue\n const existing = customRoleFeatures.get(roleName) ?? []\n existing.push(...features)\n customRoleFeatures.set(roleName, existing)\n }\n }\n\n if (customRoleFeatures.size === 0) return\n\n let seeded = 0\n for (const [roleName, features] of customRoleFeatures) {\n const role = await findRoleByName(em, roleName, roleTenantId)\n if (role) {\n await ensureRoleAclFor(em, role, tenantId, features)\n seeded++\n }\n }\n if (seeded > 0) {\n logger.info('Seeded custom role ACLs', { seeded })\n }\n}\n\nasync function ensureRoleAclFor(\n em: EntityManager,\n role: Role,\n tenantId: string,\n features: string[],\n options: { isSuperAdmin?: boolean } = {},\n) {\n // Deduplicated on the create path too, not only on merge. `ensureDefaultRoleAcls` concatenates\n // every enabled module's declared features, and two modules legitimately declare the same\n // grant (the example module asks for `payment_gateways.view`, and so does that module itself),\n // so a first init used to store the string twice while the second run silently collapsed it.\n // Same list, two shapes, depending only on how many times setup had run.\n const uniqueFeatures = Array.from(new Set(features))\n const existing = await findOneWithDecryption(em, RoleAcl, { role, tenantId }, {}, { tenantId, organizationId: null })\n if (!existing) {\n const acl = em.create(RoleAcl, {\n role,\n tenantId,\n featuresJson: uniqueFeatures,\n isSuperAdmin: !!options.isSuperAdmin,\n createdAt: new Date(),\n })\n await em.persist(acl).flush()\n return\n }\n const currentFeatures = Array.isArray(existing.featuresJson) ? existing.featuresJson : []\n const merged = Array.from(new Set([...currentFeatures, ...uniqueFeatures]))\n const changed =\n merged.length !== currentFeatures.length ||\n merged.some((value, index) => value !== currentFeatures[index])\n if (changed) existing.featuresJson = merged\n if (options.isSuperAdmin && !existing.isSuperAdmin) {\n existing.isSuperAdmin = true\n }\n if (changed || options.isSuperAdmin) {\n await em.persist(existing).flush()\n }\n}\n\n/**\n * Neutralizes every demo account the setup path may have seeded\n * (superadmin, admin, employee \u2014 honoring OM_INIT_*_EMAIL overrides)\n * when SELF_SERVICE_ONBOARDING_ENABLED is on and the operator did not\n * opt in to keeping demo credentials via shouldKeepDemoSuperadminDuringInit.\n *\n * Each user is processed in its own try/catch so a single failure (e.g.\n * decryption error on a legacy row) does not skip the remaining accounts.\n *\n * @internal Exported for tests; not part of the public auth API.\n */\nexport async function deactivateDemoUsersIfSelfOnboardingEnabled(em: EntityManager) {\n if (process.env.SELF_SERVICE_ONBOARDING_ENABLED !== 'true') return\n if (shouldKeepDemoSuperadminDuringInit()) return\n for (const { role, email } of resolveDemoUserEmails()) {\n try {\n const user = await findOneWithDecryption(\n em,\n User,\n { email },\n {},\n { tenantId: null, organizationId: null },\n )\n if (!user) continue\n let dirty = false\n if (user.passwordHash) {\n user.passwordHash = null\n dirty = true\n }\n if (user.isConfirmed !== false) {\n user.isConfirmed = false\n dirty = true\n }\n if (dirty) {\n await em.persist(user).flush()\n }\n } catch (error) {\n logger.error('Failed to deactivate demo user', { role, email, err: error })\n }\n }\n}\n\n/**\n * @deprecated Renamed to {@link deactivateDemoUsersIfSelfOnboardingEnabled}\n * because the helper now neutralizes admin/employee demo accounts in addition\n * to superadmin (security tracker finding #5). Kept as an internal alias to\n * avoid breaking any out-of-tree caller that imported the old name; will be\n * removed in a future major.\n */\nconst deactivateDemoSuperAdminIfSelfOnboardingEnabled = deactivateDemoUsersIfSelfOnboardingEnabled\n\n/** Try to get modules from runtime registry; returns empty array if not yet registered. */\nfunction tryGetModules(): Module[] {\n try {\n const { getModules } = require('@open-mercato/shared/lib/modules/registry')\n return getModules()\n } catch {\n return []\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,YAAY;AACrB,SAAS,mBAAmB;AAE5B,SAAS,MAAM,SAAS,MAAM,gBAAgB;AAC9C,SAAS,QAAQ,oBAAoB;AACrC,SAAS,iCAAiC;AAC1C,SAAS,yBAAyB;AAClC,SAAS,kBAAkB,6BAA6B;AACxD,SAAS,gCAA6C;AACtD,SAAS,0BAA0B,qCAAqC;AACxE,SAAS,qBAAqB;AAC9B,SAAS,wBAAwB;AACjC,SAAS,mCAAmC;AAC5C,SAAS,uBAAuB,0BAA0B;AAC1D,SAAS,yBAAyB;AAClC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,MAAM,EAAE,MAAM,EAAE,WAAW,QAAQ,CAAC;AAEhE,MAAM,qBAAqB,CAAC,YAAY,SAAS,YAAY;AAC7D,MAAM,wBAAwB;AAC9B,MAAM,+BAA+B,sBAAsB,MAAM,GAAG,EAAE,CAAC,KAAK;AAO5E,eAAe,qBACb,IACA,WACA,UACA;AACA,aAAW,QAAQ,WAAW;AAC5B,UAAM,WAAW,MAAM,sBAAsB,IAAI,MAAM,EAAE,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,UAAU,gBAAgB,KAAK,CAAC;AACjH,QAAI,SAAU;AACd,OAAG,QAAQ,GAAG,OAAO,MAAM,EAAE,MAAM,UAAU,kBAAkB,SAAS,UAAU,IAAI,GAAG,WAAW,oBAAI,KAAK,EAAE,CAAC,CAAC;AAAA,EACnH;AACF;AAEA,eAAsB,YAAY,IAAmB,UAA8B,CAAC,GAAG;AACrF,QAAM,YAAY,QAAQ,aAAa,CAAC,GAAG,kBAAkB;AAC7D,QAAM,WAAW,kBAAkB,QAAQ,YAAY,IAAI;AAC3D,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,uEAAkE;AAAA,EACpF;AACA,QAAM,GAAG,cAAc,OAAO,QAAQ;AACpC,UAAM,qBAAqB,KAAK,WAAW,QAAQ;AACnD,UAAM,IAAI,MAAM;AAAA,EAClB,CAAC;AACH;AAEA,eAAe,eACb,IACA,MACA,UACsB;AACtB,QAAM,mBAAmB,kBAAkB,YAAY,IAAI,KAAK;AAChE,SAAO,sBAAsB,IAAI,MAAM,EAAE,MAAM,UAAU,iBAAiB,GAAG,CAAC,GAAG,EAAE,UAAU,kBAAkB,gBAAgB,KAAK,CAAC;AACvI;AAEA,eAAe,qBACb,IACA,MACA,UACe;AACf,QAAM,OAAO,MAAM,eAAe,IAAI,MAAM,QAAQ;AACpD,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,kBAAkB,IAAI,EAAE;AACnD,SAAO;AACT;AAYA,MAAM,oBAAoB;AAAA,EACxB,OAAO;AAAA,EACP,UAAU;AACZ;AAeO,SAAS,wBAAyC;AACvD,QAAM,aAAa,aAAa,kBAAkB,KAAK,KAAK,SAAS,4BAA4B;AACjG,QAAM,gBAAgB,aAAa,kBAAkB,QAAQ,KAAK,YAAY,4BAA4B;AAC1G,SAAO;AAAA,IACL,EAAE,MAAM,cAAc,OAAO,sBAAsB;AAAA,IACnD,EAAE,MAAM,SAAS,OAAO,WAAW;AAAA,IACnC,EAAE,MAAM,YAAY,OAAO,cAAc;AAAA,EAC3C;AACF;AAmCO,MAAM,2BAA2B,MAAM;AAAA,EAC5C,YAA4B,MAAc;AACxC,UAAM,+CAA+C,IAAI,kBAAkB;AADjD;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,yCAAyC,MAAM;AAAA,EAC1D,YAA4B,SAAmB;AAC7C;AAAA,MACE,2CAA2C,QAAQ,KAAK,IAAI,CAAC;AAAA,IAC/D;AAH0B;AAI1B,SAAK,OAAO;AAAA,EACd;AACF;AASA,eAAsB,mBACpB,IACA,SACmC;AACnC,QAAM;AAAA,IACJ;AAAA,IACA,sBAAsB;AAAA,IACtB,mBAAmB;AAAA,IACnB;AAAA,IACA,wBAAwB;AAAA,EAC1B,IAAI;AACJ,QAAM,oBAAoB,oBAAoB,iBAAiB,SAAS,mBAAmB,CAAC,YAAY;AACxG,QAAM,eAAe,wBACjB,oBACA,kBAAkB,OAAO,CAAC,SAAS,SAAS,YAAY;AAC5D,MAAI,aAAa,WAAW,GAAG;AAC7B,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,QAAM,mBAAmB,QAAQ,aAAa,CAAC,GAAG,kBAAkB;AACpE,QAAM,oBAAoB,wBACtB,mBACA,iBAAiB,OAAO,CAAC,SAAS,SAAS,YAAY;AAC3D,QAAM,YAAY,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,mBAAmB,GAAG,YAAY,CAAC,CAAC;AAC7E,QAAM,kBAAkB,QAAQ,WAAW,cAAc;AACzD,QAAM,wBAAwB,yBAAyB,eAAe;AAEtE,QAAM,YAAY,YAAY;AAC9B,QAAM,qBAAqB,MAAM;AAAA,IAC/B;AAAA,IACA;AAAA,IACA,EAAE,WAAW,EAAE,KAAK,sBAAsB,SAAS,EAAE,GAAG,WAAW,KAAK;AAAA,IACxE,CAAC;AAAA,IACD,EAAE,UAAU,MAAM,gBAAgB,KAAK;AAAA,EACzC;AACA,QAAM,eAAe,sBAAsB,MAAM;AAAA,IAC/C;AAAA,IACA;AAAA,IACA,EAAE,OAAO,WAAW,WAAW,KAAK;AAAA,IACpC,CAAC;AAAA,IACD,EAAE,UAAU,MAAM,gBAAgB,KAAK;AAAA,EACzC;AACA,MAAI,gBAAgB,kBAAkB;AACpC,UAAM,IAAI,MAAM,aAAa;AAAA,EAC/B;AAEA,MAAI,QAAQ,SAAS;AACnB,UAAM,eAAe,MAAM;AAAA,MACzB;AAAA,MACA;AAAA,MACA,EAAE,MAAM,QAAQ,QAAQ;AAAA,MACxB,CAAC;AAAA,MACD,EAAE,UAAU,MAAM,gBAAgB,KAAK;AAAA,IACzC;AACA,QAAI,cAAc;AAChB,YAAM,IAAI,mBAAmB,QAAQ,OAAO;AAAA,IAC9C;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACJ,MAAI,qBAAqB;AACzB,QAAM,gBAA6G,CAAC;AAEpH,QAAM,GAAG,cAAc,OAAO,QAAQ;AACpC,QAAI,CAAC,aAAc;AACnB,yBAAqB;AACrB,eAAW,aAAa,WAAW,OAAO,aAAa,QAAQ,IAAI;AACnE,qBAAiB,aAAa,iBAAiB,OAAO,aAAa,cAAc,IAAI;AACrF,UAAM,eAAe,kBAAkB,aAAa,YAAY,IAAI,KAAK;AACzE,QAAI,CAAC,cAAc;AACjB,YAAM,IAAI,MAAM,8EAAyE;AAAA,IAC3F;AAEA,UAAM,qBAAqB,KAAK,WAAW,YAAY;AACvD,UAAM,IAAI,MAAM;AAEhB,UAAM,kBAAkB,oBAAI,IAAI,CAAC,GAAG,WAAW,GAAG,YAAY,CAAC;AAC/D,UAAM,QAAQ,MAAM;AAAA,MAClB;AAAA,MACA;AAAA,MACA,EAAE,MAAM,aAAa;AAAA,MACrB,EAAE,UAAU,CAAC,MAAM,EAAE;AAAA,MACrB,EAAE,UAAU,cAAc,gBAAgB,KAAK;AAAA,IACjD;AACA,UAAM,eAAe,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,KAAK,KAAK,IAAI,CAAC;AAChE,eAAW,YAAY,iBAAiB;AACtC,UAAI,CAAC,aAAa,IAAI,QAAQ,GAAG;AAC/B,cAAM,OAAO,MAAM,qBAAqB,KAAK,UAAU,YAAY;AACnE,YAAI,QAAQ,IAAI,OAAO,UAAU,EAAE,MAAM,cAAc,MAAM,WAAW,oBAAI,KAAK,EAAE,CAAC,CAAC;AAAA,MACvF;AAAA,IACF;AACA,UAAM,IAAI,MAAM;AAChB,UAAM,QAAQ,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,cAAc,GAAG,SAAS,CAAC,CAAC;AACjE,kBAAc,KAAK,EAAE,MAAM,cAAc,OAAO,SAAS,MAAM,CAAC;AAAA,EAClE,CAAC;AAED,MAAI,CAAC,cAAc;AACjB,UAAM,YAMD;AAAA,MACH,EAAE,OAAO,YAAY,OAAO,OAAO,cAAc,MAAM,mBAAmB,WAAW,EAAE;AAAA,IACzF;AACA,QAAI,qBAAqB;AACvB,YAAM,gBAAgB,aAAa,kBAAkB,KAAK;AAC1D,YAAM,mBAAmB,aAAa,kBAAkB,QAAQ;AAChE,YAAM,aAAa,iBAAiB,SAAS,4BAA4B;AACzE,YAAM,gBAAgB,oBAAoB,YAAY,4BAA4B;AAClF,YAAM,cAAc,aAAa,wBAAwB;AACzD,YAAM,iBAAiB,aAAa,2BAA2B;AAC/D,YAAM,eAAe,QAAQ,IAAI,aAAa;AAC9C,YAAM,YAAY,QAAQ,8BAA8B;AACxD,UAAI,gBAAgB,CAAC,WAAW;AAC9B,cAAM,UAAoB,CAAC;AAC3B,YAAI,CAAC,YAAa,SAAQ,KAAK,wBAAwB;AACvD,YAAI,CAAC,eAAgB,SAAQ,KAAK,2BAA2B;AAC7D,YAAI,QAAQ,QAAQ;AAClB,gBAAM,IAAI,iCAAiC,OAAO;AAAA,QACpD;AAAA,MACF;AAQA,YAAM,mBAAmB,eAAe,wBAAwB,IAAI;AACpE,YAAM,sBAAsB,eAAe,wBAAwB,IAAI;AACvE,YAAM,qBAAqB,eAAe;AAC1C,YAAM,wBAAwB,kBAAkB;AAChD,YAAM,oBAAoB,MAAM,KAAK,oBAAoB,EAAE;AAC3D,YAAM,uBAAuB,MAAM,KAAK,uBAAuB,EAAE;AACjE,wBAAkB,WAAW;AAAA,QAC3B,OAAO;AAAA,QACP,OAAO,CAAC,OAAO;AAAA,QACf,cAAc;AAAA,QACd,mBAAmB,cAAc,OAAO;AAAA,MAC1C,CAAC;AACD,wBAAkB,WAAW;AAAA,QAC3B,OAAO;AAAA,QACP,OAAO,CAAC,UAAU;AAAA,QAClB,cAAc;AAAA,QACd,mBAAmB,iBAAiB,OAAO;AAAA,MAC7C,CAAC;AAAA,IACH;AACA,UAAM,eAAe,MAAM,oBAAoB,WAAW;AAE1D,UAAM,GAAG,cAAc,OAAO,QAAQ;AACpC,YAAM,SAAS,IAAI,OAAO,QAAQ;AAAA,QAChC,MAAM,QAAQ;AAAA,QACd,UAAU;AAAA,QACV,WAAW,oBAAI,KAAK;AAAA,QACpB,WAAW,oBAAI,KAAK;AAAA,MACtB,CAAC;AACD,UAAI,QAAQ,MAAM;AAClB,YAAM,IAAI,MAAM;AAEhB,YAAM,eAAe,IAAI,OAAO,cAAc;AAAA,QAC5C,MAAM,QAAQ;AAAA,QACd,MAAM,QAAQ,WAAW;AAAA,QACzB;AAAA,QACA,UAAU;AAAA,QACV,OAAO;AAAA,QACP,aAAa,CAAC;AAAA,QACd,UAAU,CAAC;AAAA,QACX,eAAe,CAAC;AAAA,QAChB,yBAAyB;AAAA,QACzB,WAAW,oBAAI,KAAK;AAAA,QACpB,WAAW,oBAAI,KAAK;AAAA,MACtB,CAAC;AACD,UAAI,QAAQ,YAAY;AACxB,YAAM,IAAI,MAAM;AAEhB,iBAAW,OAAO,OAAO,EAAE;AAC3B,uBAAiB,OAAO,aAAa,EAAE;AACvC,YAAM,eAAe;AAErB,UAAI,8BAA8B,GAAG;AACnC,YAAI;AACF,gBAAM,MAAM,iBAAiB;AAC7B,cAAI,IAAI,UAAU,GAAG;AACnB,gBAAI,yBAAyB,GAAG;AAC9B,qBAAO,KAAK,2BAA2B,EAAE,UAAU,OAAO,OAAO,EAAE,EAAE,CAAC;AAAA,YACxE;AACA,kBAAM,IAAI,gBAAgB,OAAO,OAAO,EAAE,CAAC;AAC3C,gBAAI,yBAAyB,GAAG;AAC9B,qBAAO,KAAK,mCAAmC,EAAE,UAAU,OAAO,OAAO,EAAE,EAAE,CAAC;AAAA,YAChF;AAAA,UACF,OAAO;AACL,gBAAI,yBAAyB,GAAG;AAC9B,qBAAO,KAAK,iDAAiD,EAAE,UAAU,OAAO,OAAO,EAAE,EAAE,CAAC;AAAA,YAC9F;AAAA,UACF;AAAA,QACF,SAAS,KAAK;AACZ,cAAI,yBAAyB,GAAG;AAC9B,mBAAO,KAAK,+BAA+B,EAAE,IAAI,CAAC;AAAA,UACpD;AAAA,QACF;AAAA,MACF;AAEA,YAAM,qBAAqB,KAAK,WAAW,YAAY;AACvD,YAAM,IAAI,MAAM;AAEhB,UAAI,8BAA8B,GAAG;AAKnC,mBAAW,QAAQ,uBAAuB;AACxC,cAAI,KAAK,aAAa,SAAU;AAChC,gBAAM,WAAW,MAAM,sBAAsB,KAAK,eAAe,EAAE,UAAU,KAAK,UAAU,UAAU,OAAO,IAAI,gBAAgB,aAAa,IAAI,WAAW,KAAK,GAAG,CAAC,GAAG,EAAE,UAAU,OAAO,OAAO,EAAE,GAAG,gBAAgB,OAAO,aAAa,EAAE,EAAE,CAAC;AACjP,cAAI,CAAC,UAAU;AACb,gBAAI,QAAQ,IAAI,OAAO,eAAe;AAAA,cACpC,UAAU,KAAK;AAAA,cACf,UAAU,OAAO;AAAA,cACjB,gBAAgB,aAAa;AAAA,cAC7B,YAAY,KAAK;AAAA,cACjB,UAAU;AAAA,cACV,WAAW,oBAAI,KAAK;AAAA,cACpB,WAAW,oBAAI,KAAK;AAAA,YACtB,CAAC,CAAC;AAAA,UACJ,OAAO;AACL,qBAAS,aAAa,KAAK;AAC3B,qBAAS,WAAW;AAAA,UACtB;AAAA,QACF;AACA,cAAM,IAAI,MAAM;AAAA,MAClB;AAAA,IACF,CAAC;AAED,UAAM,GAAG,cAAc,OAAO,QAAQ;AACpC,UAAI,CAAC,YAAY,CAAC,eAAgB;AAClC,YAAM,eAAe;AACrB,YAAM,oBAAoB,8BAA8B,IACpD,IAAI,4BAA4B,KAAY,EAAE,KAAK,iBAAiB,EAAE,CAAC,IACvE;AACJ,UAAI,mBAAmB;AACrB,cAAM,kBAAkB,cAAc,aAAa,OAAO,QAAQ,GAAG,OAAO,cAAc,CAAC;AAC3F,cAAM,kBAAkB,cAAc,aAAa,OAAO,QAAQ,GAAG,IAAI;AAAA,MAC3E;AAEA,iBAAW,QAAQ,WAAW;AAC5B,cAAM,uBAAuB,KAAK,gBAAgB;AAClD,YAAI,OAAO,MAAM,sBAAsB,KAAK,MAAM,EAAE,OAAO,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE,UAAU,YAAY,MAAM,gBAAgB,kBAAkB,KAAK,CAAC;AACnJ,cAAM,UAAU,YAAY,WAAW;AACvC,cAAM,mBAAmB,oBACrB,MAAM,kBAAkB,qBAAqB,aAAa,EAAE,OAAO,KAAK,MAAM,GAAG,UAAU,cAAc,IACzG,EAAE,OAAO,KAAK,OAAO,WAAW,iBAAiB,KAAK,KAAK,EAAE;AACjE,YAAI,MAAM;AACR,eAAK,eAAe;AACpB,eAAK,iBAAiB;AACtB,eAAK,WAAW;AAChB,cAAI,8BAA8B,GAAG;AACnC,iBAAK,QAAQ,iBAAiB;AAC9B,iBAAK,YAAa,iBAAyB,aAAa,iBAAiB,KAAK,KAAK;AAAA,UACrF;AACA,cAAI,KAAK,KAAM,MAAK,OAAO,KAAK;AAChC,cAAI,QAAS,MAAK,cAAc;AAChC,cAAI,QAAQ,IAAI;AAChB,wBAAc,KAAK,EAAE,MAAM,OAAO,KAAK,OAAO,SAAS,OAAO,mBAAmB,KAAK,qBAAqB,KAAK,CAAC;AAAA,QACnH,OAAO;AACL,iBAAO,IAAI,OAAO,MAAM;AAAA,YACtB,OAAQ,iBAAyB,SAAS,KAAK;AAAA,YAC/C,WAAW,8BAA8B,IAAK,iBAAyB,aAAa,iBAAiB,KAAK,KAAK,IAAI;AAAA,YACnH,cAAc;AAAA,YACd;AAAA,YACA;AAAA,YACA,MAAM,KAAK,QAAQ;AAAA,YACnB,aAAa;AAAA,YACb,WAAW,oBAAI,KAAK;AAAA,UACtB,CAAC;AACD,cAAI,QAAQ,IAAI;AAChB,wBAAc,KAAK,EAAE,MAAM,OAAO,KAAK,OAAO,SAAS,MAAM,mBAAmB,KAAK,qBAAqB,KAAK,CAAC;AAAA,QAClH;AACA,cAAM,IAAI,MAAM;AAChB,mBAAW,YAAY,KAAK,OAAO;AACjC,gBAAM,OAAO,MAAM,qBAAqB,KAAK,UAAU,YAAY;AACnE,gBAAM,eAAe,MAAM,sBAAsB,KAAK,UAAU,EAAE,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,UAAU,YAAY,MAAM,gBAAgB,KAAK,CAAC;AACxI,cAAI,CAAC,aAAc,KAAI,QAAQ,IAAI,OAAO,UAAU,EAAE,MAAM,MAAM,WAAW,oBAAI,KAAK,EAAE,CAAC,CAAC;AAAA,QAC5F;AACA,cAAM,IAAI,MAAM;AAAA,MAClB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,CAAC,YAAY,CAAC,gBAAgB;AAChC,UAAM,IAAI,MAAM,cAAc;AAAA,EAChC;AAEA,MAAI,CAAC,oBAAoB;AACvB,UAAM,0BAA0B,IAAI,QAAQ;AAAA,EAC9C;AAEA,QAAM,sBAAsB,IAAI,UAAU,iBAAiB,EAAE,sBAAsB,CAAC;AACpF,QAAM,2CAA2C,EAAE;AAGnD,aAAW,OAAO,iBAAiB;AACjC,QAAI,IAAI,OAAO,iBAAiB;AAC9B,YAAM,IAAI,MAAM,gBAAgB,EAAE,IAAI,UAAU,eAAe,CAAC;AAAA,IAClE;AAAA,EACF;AAMA,QAAM,qBAAqB,IAAI,UAAU,eAAe;AAExD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,OAAwC;AAClE,MAAI,MAAM,eAAe,MAAM,YAAY,KAAK,EAAG,QAAO,MAAM,YAAY,KAAK;AACjF,QAAM,QAAQ,CAAC,MAAM,WAAW,MAAM,QAAQ,EAAE,IAAI,CAAC,UAAU,OAAO,KAAK,CAAC,EAAE,OAAO,OAAO;AAC5F,MAAI,MAAM,OAAQ,QAAO,MAAM,KAAK,GAAG;AACvC,SAAO;AACT;AAEA,SAAS,aAAa,KAAiC;AACrD,QAAM,QAAQ,QAAQ,IAAI,GAAG;AAC7B,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAEA,SAAS,kBACP,WACA,OACA;AACA,MAAI,CAAC,MAAM,MAAO;AAClB,QAAM,aAAa,MAAM,MAAM,YAAY;AAC3C,MAAI,UAAU,KAAK,CAAC,SAAS,KAAK,MAAM,YAAY,MAAM,UAAU,EAAG;AACvE,YAAU,KAAK,KAAK;AACtB;AASA,MAAM,wBAAwB;AAS9B,SAAS,0BAAkC;AACzC,SAAO,YAAY,EAAE,EAAE,SAAS,WAAW;AAC7C;AAEA,SAAS,oBAA6B;AACpC,QAAM,SAAS,kBAAkB,QAAQ,IAAI,aAAa,EAAE;AAC5D,SAAO,WAAW,QAAQ,QAAQ;AACpC;AAEA,SAAS,qCAA8C;AACrD,MAAI,QAAQ,IAAI,iBAAiB,OAAQ,QAAO;AAChD,MAAI,CAAC,aAAa,0BAA0B,EAAG,QAAO;AACtD,SAAO,kBAAkB;AAC3B;AAEA,eAAe,oBAAoB,OAAiD;AAClF,MAAI,OAAO,MAAM,mBAAmB,SAAU,QAAO,MAAM;AAC3D,MAAI,MAAM,SAAU,QAAO,KAAK,MAAM,UAAU,EAAE;AAClD,SAAO;AACT;AAEA,eAAsB,sBACpB,IACA,UACA,SACA,UAA+C,CAAC,GAChD;AACA,QAAM,wBAAwB,QAAQ,yBAAyB;AAC/D,QAAM,eAAe,kBAAkB,QAAQ,KAAK;AACpD,QAAM,iBAAiB,wBAAwB,MAAM,eAAe,IAAI,cAAc,YAAY,IAAI;AACtG,QAAM,YAAY,MAAM,eAAe,IAAI,SAAS,YAAY;AAChE,QAAM,eAAe,MAAM,eAAe,IAAI,YAAY,YAAY;AAGtE,QAAM,eAAe,CAAC,cAAc,SAAS,UAAU;AACvD,QAAM,qBAA+B,CAAC;AACtC,QAAM,gBAA0B,CAAC;AACjC,QAAM,mBAA6B,CAAC;AACpC,QAAM,qBAAqB,oBAAI,IAAsB;AAErD,aAAW,OAAO,SAAS;AACzB,UAAM,eAAe,IAAI,OAAO;AAChC,QAAI,CAAC,aAAc;AACnB,QAAI,aAAa,WAAY,oBAAmB,KAAK,GAAG,aAAa,UAAU;AAC/E,QAAI,aAAa,MAAO,eAAc,KAAK,GAAG,aAAa,KAAK;AAChE,QAAI,aAAa,SAAU,kBAAiB,KAAK,GAAG,aAAa,QAAQ;AAGzE,eAAW,CAAC,UAAU,QAAQ,KAAK,OAAO,QAAQ,YAAY,GAAG;AAC/D,UAAK,aAAmC,SAAS,QAAQ,EAAG;AAC5D,UAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG;AAC9B,YAAM,WAAW,mBAAmB,IAAI,QAAQ,KAAK,CAAC;AACtD,eAAS,KAAK,GAAG,QAAQ;AACzB,yBAAmB,IAAI,UAAU,QAAQ;AAAA,IAC3C;AAAA,EACF;AAEA,SAAO,KAAK,gCAAgC;AAAA,IAC1C,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,UAAU;AAAA,IACV,GAAI,mBAAmB,OAAO,IAC1B,OAAO,YAAY,kBAAkB,IACrC,CAAC;AAAA,EACP,CAAC;AAED,MAAI,yBAAyB,gBAAgB;AAC3C,UAAM,iBAAiB,IAAI,gBAAgB,UAAU,oBAAoB,EAAE,cAAc,KAAK,CAAC;AAAA,EACjG;AACA,MAAI,WAAW;AACb,UAAM,iBAAiB,IAAI,WAAW,UAAU,aAAa;AAAA,EAC/D;AACA,MAAI,cAAc;AAChB,UAAM,iBAAiB,IAAI,cAAc,UAAU,gBAAgB;AAAA,EACrE;AAMA,aAAW,CAAC,UAAU,QAAQ,KAAK,oBAAoB;AACrD,UAAM,OAAO,MAAM,eAAe,IAAI,UAAU,YAAY;AAC5D,QAAI,MAAM;AACR,YAAM,iBAAiB,IAAI,MAAM,UAAU,QAAQ;AAAA,IACrD;AAAA,EACF;AACF;AAOA,eAAsB,qBACpB,IACA,UACA,SACe;AACf,QAAM,kBAAkB,WAAW,cAAc;AACjD,QAAM,eAAe,kBAAkB,QAAQ,KAAK;AACpD,QAAM,eAAe,CAAC,cAAc,SAAS,UAAU;AACvD,QAAM,qBAAqB,oBAAI,IAAsB;AAErD,aAAW,OAAO,iBAAiB;AACjC,UAAM,eAAe,IAAI,OAAO;AAChC,QAAI,CAAC,aAAc;AACnB,eAAW,CAAC,UAAU,QAAQ,KAAK,OAAO,QAAQ,YAAY,GAAG;AAC/D,UAAI,aAAa,SAAS,QAAQ,EAAG;AACrC,UAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG;AAC9B,YAAM,WAAW,mBAAmB,IAAI,QAAQ,KAAK,CAAC;AACtD,eAAS,KAAK,GAAG,QAAQ;AACzB,yBAAmB,IAAI,UAAU,QAAQ;AAAA,IAC3C;AAAA,EACF;AAEA,MAAI,mBAAmB,SAAS,EAAG;AAEnC,MAAI,SAAS;AACb,aAAW,CAAC,UAAU,QAAQ,KAAK,oBAAoB;AACrD,UAAM,OAAO,MAAM,eAAe,IAAI,UAAU,YAAY;AAC5D,QAAI,MAAM;AACR,YAAM,iBAAiB,IAAI,MAAM,UAAU,QAAQ;AACnD;AAAA,IACF;AAAA,EACF;AACA,MAAI,SAAS,GAAG;AACd,WAAO,KAAK,2BAA2B,EAAE,OAAO,CAAC;AAAA,EACnD;AACF;AAEA,eAAe,iBACb,IACA,MACA,UACA,UACA,UAAsC,CAAC,GACvC;AAMA,QAAM,iBAAiB,MAAM,KAAK,IAAI,IAAI,QAAQ,CAAC;AACnD,QAAM,WAAW,MAAM,sBAAsB,IAAI,SAAS,EAAE,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,UAAU,gBAAgB,KAAK,CAAC;AACpH,MAAI,CAAC,UAAU;AACb,UAAM,MAAM,GAAG,OAAO,SAAS;AAAA,MAC7B;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,cAAc,CAAC,CAAC,QAAQ;AAAA,MACxB,WAAW,oBAAI,KAAK;AAAA,IACtB,CAAC;AACD,UAAM,GAAG,QAAQ,GAAG,EAAE,MAAM;AAC5B;AAAA,EACF;AACA,QAAM,kBAAkB,MAAM,QAAQ,SAAS,YAAY,IAAI,SAAS,eAAe,CAAC;AACxF,QAAM,SAAS,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,iBAAiB,GAAG,cAAc,CAAC,CAAC;AAC1E,QAAM,UACJ,OAAO,WAAW,gBAAgB,UAClC,OAAO,KAAK,CAAC,OAAO,UAAU,UAAU,gBAAgB,KAAK,CAAC;AAChE,MAAI,QAAS,UAAS,eAAe;AACrC,MAAI,QAAQ,gBAAgB,CAAC,SAAS,cAAc;AAClD,aAAS,eAAe;AAAA,EAC1B;AACA,MAAI,WAAW,QAAQ,cAAc;AACnC,UAAM,GAAG,QAAQ,QAAQ,EAAE,MAAM;AAAA,EACnC;AACF;AAaA,eAAsB,2CAA2C,IAAmB;AAClF,MAAI,QAAQ,IAAI,oCAAoC,OAAQ;AAC5D,MAAI,mCAAmC,EAAG;AAC1C,aAAW,EAAE,MAAM,MAAM,KAAK,sBAAsB,GAAG;AACrD,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB;AAAA,QACA;AAAA,QACA,EAAE,MAAM;AAAA,QACR,CAAC;AAAA,QACD,EAAE,UAAU,MAAM,gBAAgB,KAAK;AAAA,MACzC;AACA,UAAI,CAAC,KAAM;AACX,UAAI,QAAQ;AACZ,UAAI,KAAK,cAAc;AACrB,aAAK,eAAe;AACpB,gBAAQ;AAAA,MACV;AACA,UAAI,KAAK,gBAAgB,OAAO;AAC9B,aAAK,cAAc;AACnB,gBAAQ;AAAA,MACV;AACA,UAAI,OAAO;AACT,cAAM,GAAG,QAAQ,IAAI,EAAE,MAAM;AAAA,MAC/B;AAAA,IACF,SAAS,OAAO;AACd,aAAO,MAAM,kCAAkC,EAAE,MAAM,OAAO,KAAK,MAAM,CAAC;AAAA,IAC5E;AAAA,EACF;AACF;AASA,MAAM,kDAAkD;AAGxD,SAAS,gBAA0B;AACjC,MAAI;AACF,UAAM,EAAE,WAAW,IAAI,QAAQ,2CAA2C;AAC1E,WAAO,WAAW;AAAA,EACpB,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import { useT } from "@open-mercato/shared/lib/i18n/context";
4
+ import { useT, useLocale } from "@open-mercato/shared/lib/i18n/context";
5
+ import { parseLocaleNumber } from "@open-mercato/shared/lib/number";
5
6
  import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
6
7
  import { Button } from "@open-mercato/ui/primitives/button";
7
8
  import { Checkbox } from "@open-mercato/ui/primitives/checkbox";
@@ -32,18 +33,19 @@ const REFERENCE_UNIT_OPTIONS = REFERENCE_UNIT_CODES.map((code) => ({
32
33
  i18nKey: `catalog.products.unitPrice.options.${code}`,
33
34
  fallback: REFERENCE_UNIT_DISPLAY[code] ?? code
34
35
  }));
35
- function normalizeDecimalInput(value) {
36
- return value.replace(/,/g, ".");
36
+ function normalizeDecimalInput(value, locale) {
37
+ const parsed = parseLocaleNumber(value, locale);
38
+ return parsed === null ? value : String(parsed);
37
39
  }
38
- function toPositiveNumber(value) {
40
+ function toPositiveNumber(value, locale) {
39
41
  if (typeof value === "number") {
40
42
  return Number.isFinite(value) && value > 0 ? value : null;
41
43
  }
42
44
  if (typeof value !== "string") return null;
43
45
  const normalized = toTrimmedOrNull(value);
44
46
  if (!normalized) return null;
45
- const numeric = Number(normalized.replace(",", "."));
46
- return Number.isFinite(numeric) && numeric > 0 ? numeric : null;
47
+ const numeric = parseLocaleNumber(normalized, locale);
48
+ return numeric !== null && numeric > 0 ? numeric : null;
47
49
  }
48
50
  function toSortValue(value) {
49
51
  const numeric = Number(value);
@@ -54,7 +56,7 @@ function formatPreviewNumber(value) {
54
56
  const rounded = Math.round(value * 1e6) / 1e6;
55
57
  return Number.isInteger(rounded) ? String(rounded) : rounded.toString();
56
58
  }
57
- function normalizeConversions(value) {
59
+ function normalizeConversions(value, locale) {
58
60
  if (!Array.isArray(value)) return [];
59
61
  const normalized = value.map((entry) => {
60
62
  if (!entry || typeof entry !== "object") return null;
@@ -62,7 +64,7 @@ function normalizeConversions(value) {
62
64
  return {
63
65
  id: toTrimmedOrNull(row.id) ?? null,
64
66
  unitCode: toTrimmedOrNull(row.unitCode) ?? "",
65
- toBaseFactor: toTrimmedOrNull(row.toBaseFactor) ? normalizeDecimalInput(toTrimmedOrNull(row.toBaseFactor)) : "",
67
+ toBaseFactor: toTrimmedOrNull(row.toBaseFactor) ? normalizeDecimalInput(toTrimmedOrNull(row.toBaseFactor), locale) : "",
66
68
  sortOrder: toTrimmedOrNull(row.sortOrder) ?? "",
67
69
  isActive: row.isActive !== false
68
70
  };
@@ -97,13 +99,14 @@ function ProductUomSection({
97
99
  embedded = false
98
100
  }) {
99
101
  const t = useT();
102
+ const locale = useLocale();
100
103
  const { enabled: unitPriceDisplayEnabled } = useUnitPriceDisplayEnabled();
101
104
  const [unitOptions, setUnitOptions] = React.useState([]);
102
105
  const [loadingUnits, setLoadingUnits] = React.useState(false);
103
106
  const [errorLoadingUnits, setErrorLoadingUnits] = React.useState(false);
104
107
  const conversions = React.useMemo(
105
- () => normalizeConversions(values.unitConversions),
106
- [values.unitConversions]
108
+ () => normalizeConversions(values.unitConversions, locale),
109
+ [values.unitConversions, locale]
107
110
  );
108
111
  React.useEffect(() => {
109
112
  let cancelled = false;
@@ -191,11 +194,11 @@ function ProductUomSection({
191
194
  const defaultUnit = toTrimmedOrNull(values.defaultUnit) ?? "";
192
195
  const defaultSalesUnit = toTrimmedOrNull(values.defaultSalesUnit) ?? "";
193
196
  const defaultSalesQuantityRaw = toTrimmedOrNull(values.defaultSalesUnitQuantity) ?? "1";
194
- const defaultSalesQuantity = normalizeDecimalInput(defaultSalesQuantityRaw);
197
+ const defaultSalesQuantity = normalizeDecimalInput(defaultSalesQuantityRaw, locale);
195
198
  const unitPriceEnabled = Boolean(values.unitPriceEnabled);
196
199
  const unitPriceReferenceUnit = toTrimmedOrNull(values.unitPriceReferenceUnit) ?? "";
197
200
  const unitPriceBaseQuantityRaw = toTrimmedOrNull(values.unitPriceBaseQuantity) ?? "";
198
- const unitPriceBaseQuantity = normalizeDecimalInput(unitPriceBaseQuantityRaw);
201
+ const unitPriceBaseQuantity = normalizeDecimalInput(unitPriceBaseQuantityRaw, locale);
199
202
  const baseUnitLabel = findUnitLabel(defaultUnit) ?? defaultUnit;
200
203
  const salesUnitLabel = findUnitLabel(defaultSalesUnit || defaultUnit) ?? defaultSalesUnit ?? defaultUnit;
201
204
  const defaultSalesFactor = React.useMemo(() => {
@@ -204,13 +207,13 @@ function ProductUomSection({
204
207
  if (!defaultUnitKey || !defaultSalesKey) return null;
205
208
  if (defaultSalesKey === defaultUnitKey) return 1;
206
209
  const row = conversions.find(
207
- (entry) => entry.isActive && entry.unitCode.toLowerCase() === defaultSalesKey && toPositiveNumber(entry.toBaseFactor) !== null
210
+ (entry) => entry.isActive && entry.unitCode.toLowerCase() === defaultSalesKey && toPositiveNumber(entry.toBaseFactor, locale) !== null
208
211
  );
209
- return row ? toPositiveNumber(row.toBaseFactor) : null;
210
- }, [conversions, defaultSalesUnit, defaultUnit]);
211
- const defaultSalesQuantityNumber = toPositiveNumber(defaultSalesQuantity);
212
+ return row ? toPositiveNumber(row.toBaseFactor, locale) : null;
213
+ }, [conversions, defaultSalesUnit, defaultUnit, locale]);
214
+ const defaultSalesQuantityNumber = toPositiveNumber(defaultSalesQuantity, locale);
212
215
  const defaultSalesQuantityNormalized = defaultSalesQuantityNumber && defaultSalesFactor ? defaultSalesQuantityNumber * defaultSalesFactor : null;
213
- const unitPriceBaseQuantityNumber = toPositiveNumber(unitPriceBaseQuantity);
216
+ const unitPriceBaseQuantityNumber = toPositiveNumber(unitPriceBaseQuantity, locale);
214
217
  const validConversions = conversions.filter(
215
218
  (entry) => toTrimmedOrNull(entry.unitCode) && toTrimmedOrNull(entry.toBaseFactor)
216
219
  );
@@ -288,7 +291,7 @@ function ProductUomSection({
288
291
  value: defaultSalesQuantity,
289
292
  onChange: (event) => setValue(
290
293
  "defaultSalesUnitQuantity",
291
- normalizeDecimalInput(event.target.value)
294
+ normalizeDecimalInput(event.target.value, locale)
292
295
  ),
293
296
  placeholder: "1"
294
297
  }
@@ -418,7 +421,7 @@ function ProductUomSection({
418
421
  value: unitPriceBaseQuantity,
419
422
  onChange: (event) => setValue(
420
423
  "unitPriceBaseQuantity",
421
- normalizeDecimalInput(event.target.value)
424
+ normalizeDecimalInput(event.target.value, locale)
422
425
  ),
423
426
  placeholder: "1"
424
427
  }
@@ -458,7 +461,7 @@ function ProductUomSection({
458
461
  "catalog.products.uom.emptyConversions",
459
462
  "No conversions configured yet."
460
463
  ) }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: conversions.map((entry, index) => {
461
- const conversionFactor = toPositiveNumber(entry.toBaseFactor);
464
+ const conversionFactor = toPositiveNumber(entry.toBaseFactor, locale);
462
465
  const conversionPreviewText = entry.unitCode && conversionFactor !== null ? t(
463
466
  "catalog.products.uom.conversionPreview",
464
467
  "1 {{fromUnit}} = {{factor}} {{baseUnit}}",
@@ -501,7 +504,7 @@ function ProductUomSection({
501
504
  inputMode: "decimal",
502
505
  value: entry.toBaseFactor,
503
506
  onChange: (event) => updateConversion(index, {
504
- toBaseFactor: normalizeDecimalInput(event.target.value)
507
+ toBaseFactor: normalizeDecimalInput(event.target.value, locale)
505
508
  }),
506
509
  placeholder: "1"
507
510
  }
@@ -593,6 +596,8 @@ function ProductUomSection({
593
596
  );
594
597
  }
595
598
  export {
596
- ProductUomSection
599
+ ProductUomSection,
600
+ normalizeDecimalInput,
601
+ toPositiveNumber
597
602
  };
598
603
  //# sourceMappingURL=ProductUomSection.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/catalog/components/products/ProductUomSection.tsx"],
4
- "sourcesContent": ["\"use client\";\n\nimport * as React from \"react\";\nimport { useT } from \"@open-mercato/shared/lib/i18n/context\";\nimport { apiCall } from \"@open-mercato/ui/backend/utils/apiCall\";\nimport { Button } from \"@open-mercato/ui/primitives/button\";\nimport { Checkbox } from \"@open-mercato/ui/primitives/checkbox\";\nimport { Input } from \"@open-mercato/ui/primitives/input\";\nimport { Label } from \"@open-mercato/ui/primitives/label\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@open-mercato/ui/primitives/select\";\nimport { Spinner } from \"@open-mercato/ui/primitives/spinner\";\nimport { ArrowDown, ArrowUp, Plus, Trash2 } from \"lucide-react\";\nimport type {\n ProductFormValues,\n ProductUnitConversionDraft,\n} from \"./productForm\";\nimport { createProductUnitConversionDraft } from \"./productForm\";\nimport { REFERENCE_UNIT_CODES } from \"@open-mercato/shared/lib/units/unitCodes\";\nimport { toTrimmedOrNull } from \"./productFormUtils\";\nimport { useUnitPriceDisplayEnabled } from \"./hooks/useUnitPriceDisplayEnabled\";\n\ntype UnitDictionaryEntry = {\n id?: string;\n value?: string;\n label?: string;\n};\n\ntype UnitDictionaryResponse = {\n entries?: UnitDictionaryEntry[];\n};\n\ntype UnitOption = {\n value: string;\n label: string;\n};\n\ntype ProductUomSectionProps = {\n values: ProductFormValues;\n errors: Record<string, string>;\n setValue: (id: string, value: unknown) => void;\n embedded?: boolean;\n};\n\nconst REFERENCE_UNIT_DISPLAY: Record<string, string> = {\n kg: \"1 kg\", l: \"1 l\", m2: \"1 m\\u00B2\", m3: \"1 m\\u00B3\", pc: \"1 pc\",\n};\nconst REFERENCE_UNIT_OPTIONS = REFERENCE_UNIT_CODES.map((code) => ({\n value: code,\n i18nKey: `catalog.products.unitPrice.options.${code}` as const,\n fallback: REFERENCE_UNIT_DISPLAY[code] ?? code,\n}));\n\nfunction normalizeDecimalInput(value: string): string {\n return value.replace(/,/g, \".\");\n}\n\nfunction toPositiveNumber(value: unknown): number | null {\n if (typeof value === \"number\") {\n return Number.isFinite(value) && value > 0 ? value : null;\n }\n if (typeof value !== \"string\") return null;\n const normalized = toTrimmedOrNull(value);\n if (!normalized) return null;\n const numeric = Number(normalized.replace(\",\", \".\"));\n return Number.isFinite(numeric) && numeric > 0 ? numeric : null;\n}\n\nfunction toSortValue(value: string): number {\n const numeric = Number(value);\n return Number.isFinite(numeric) ? numeric : Number.MAX_SAFE_INTEGER;\n}\n\nfunction formatPreviewNumber(value: number): string {\n if (!Number.isFinite(value)) return \"0\";\n const rounded = Math.round(value * 1_000_000) / 1_000_000;\n return Number.isInteger(rounded) ? String(rounded) : rounded.toString();\n}\n\nfunction normalizeConversions(value: unknown): ProductUnitConversionDraft[] {\n if (!Array.isArray(value)) return [];\n const normalized = value\n .map((entry) => {\n if (!entry || typeof entry !== \"object\") return null;\n const row = entry as ProductUnitConversionDraft;\n return {\n id: toTrimmedOrNull(row.id) ?? null,\n unitCode: toTrimmedOrNull(row.unitCode) ?? \"\",\n toBaseFactor: toTrimmedOrNull(row.toBaseFactor)\n ? normalizeDecimalInput(toTrimmedOrNull(row.toBaseFactor) as string)\n : \"\",\n sortOrder: toTrimmedOrNull(row.sortOrder) ?? \"\",\n isActive: row.isActive !== false,\n } satisfies ProductUnitConversionDraft;\n })\n .filter((entry): entry is ProductUnitConversionDraft => Boolean(entry));\n normalized.sort((left, right) => {\n const leftOrder = toSortValue(left.sortOrder);\n const rightOrder = toSortValue(right.sortOrder);\n if (leftOrder === rightOrder) return left.unitCode.localeCompare(right.unitCode);\n return leftOrder - rightOrder;\n });\n return normalized.map((entry, index) => ({\n ...entry,\n sortOrder: String((index + 1) * 10),\n }));\n}\n\nfunction buildUnitOptions(\n entries: UnitDictionaryEntry[] | undefined,\n): UnitOption[] {\n const list = Array.isArray(entries) ? entries : [];\n const options = list\n .map((entry) => {\n const value = toTrimmedOrNull(entry.value);\n if (!value) return null;\n return {\n value,\n label: toTrimmedOrNull(entry.label) ?? value,\n } satisfies UnitOption;\n })\n .filter((entry): entry is UnitOption => Boolean(entry));\n return options.sort((left, right) => left.label.localeCompare(right.label));\n}\n\nexport function ProductUomSection({\n values,\n errors,\n setValue,\n embedded = false,\n}: ProductUomSectionProps) {\n const t = useT();\n const { enabled: unitPriceDisplayEnabled } = useUnitPriceDisplayEnabled();\n const [unitOptions, setUnitOptions] = React.useState<UnitOption[]>([]);\n const [loadingUnits, setLoadingUnits] = React.useState(false);\n const [errorLoadingUnits, setErrorLoadingUnits] = React.useState(false);\n const conversions = React.useMemo(\n () => normalizeConversions(values.unitConversions),\n [values.unitConversions],\n );\n\n React.useEffect(() => {\n let cancelled = false;\n async function loadUnits() {\n setLoadingUnits(true);\n setErrorLoadingUnits(false);\n try {\n const response = await apiCall<UnitDictionaryResponse>(\n \"/api/catalog/dictionaries/unit\",\n undefined,\n { fallback: { entries: [] } },\n );\n if (cancelled) return;\n setUnitOptions(buildUnitOptions(response.result?.entries));\n } catch {\n if (!cancelled) {\n setUnitOptions([]);\n setErrorLoadingUnits(true);\n }\n } finally {\n if (!cancelled) setLoadingUnits(false);\n }\n }\n void loadUnits();\n return () => {\n cancelled = true;\n };\n }, []);\n\n const findUnitLabel = React.useCallback(\n (value: string | null | undefined) => {\n const code = toTrimmedOrNull(value);\n if (!code) return null;\n const option = unitOptions.find((entry) => entry.value === code);\n return option?.label ?? code;\n },\n [unitOptions],\n );\n\n const setConversions = React.useCallback(\n (next: ProductUnitConversionDraft[]) => {\n const normalized = next.map((entry, index) => ({\n ...entry,\n sortOrder: String((index + 1) * 10),\n }));\n setValue(\"unitConversions\", normalized);\n },\n [setValue],\n );\n\n const addConversion = React.useCallback(() => {\n const next = [\n ...conversions,\n createProductUnitConversionDraft({\n sortOrder: String((conversions.length + 1) * 10),\n }),\n ];\n setConversions(next);\n }, [conversions, setConversions]);\n\n const updateConversion = React.useCallback(\n (index: number, patch: Partial<ProductUnitConversionDraft>) => {\n const next = conversions.map((entry, rowIndex) =>\n rowIndex === index ? { ...entry, ...patch } : entry,\n );\n setConversions(next);\n },\n [conversions, setConversions],\n );\n\n const removeConversion = React.useCallback(\n (index: number) => {\n const next = conversions.filter((_entry, rowIndex) => rowIndex !== index);\n setConversions(next);\n },\n [conversions, setConversions],\n );\n\n const moveConversion = React.useCallback(\n (index: number, direction: \"up\" | \"down\") => {\n const targetIndex = direction === \"up\" ? index - 1 : index + 1;\n if (targetIndex < 0 || targetIndex >= conversions.length) return;\n const next = [...conversions];\n const source = next[index];\n next[index] = next[targetIndex];\n next[targetIndex] = source;\n setConversions(next);\n },\n [conversions, setConversions],\n );\n\n const defaultUnit = toTrimmedOrNull(values.defaultUnit) ?? \"\";\n const defaultSalesUnit = toTrimmedOrNull(values.defaultSalesUnit) ?? \"\";\n const defaultSalesQuantityRaw =\n toTrimmedOrNull(values.defaultSalesUnitQuantity) ?? \"1\";\n const defaultSalesQuantity = normalizeDecimalInput(defaultSalesQuantityRaw);\n const unitPriceEnabled = Boolean(values.unitPriceEnabled);\n const unitPriceReferenceUnit =\n toTrimmedOrNull(values.unitPriceReferenceUnit) ?? \"\";\n const unitPriceBaseQuantityRaw =\n toTrimmedOrNull(values.unitPriceBaseQuantity) ?? \"\";\n const unitPriceBaseQuantity = normalizeDecimalInput(unitPriceBaseQuantityRaw);\n\n const baseUnitLabel = findUnitLabel(defaultUnit) ?? defaultUnit;\n const salesUnitLabel =\n findUnitLabel(defaultSalesUnit || defaultUnit) ??\n defaultSalesUnit ??\n defaultUnit;\n\n const defaultSalesFactor = React.useMemo(() => {\n const defaultUnitKey = defaultUnit.toLowerCase();\n const defaultSalesKey = (defaultSalesUnit || defaultUnit).toLowerCase();\n if (!defaultUnitKey || !defaultSalesKey) return null;\n if (defaultSalesKey === defaultUnitKey) return 1;\n const row = conversions.find(\n (entry) =>\n entry.isActive &&\n entry.unitCode.toLowerCase() === defaultSalesKey &&\n toPositiveNumber(entry.toBaseFactor) !== null,\n );\n return row ? toPositiveNumber(row.toBaseFactor) : null;\n }, [conversions, defaultSalesUnit, defaultUnit]);\n\n const defaultSalesQuantityNumber = toPositiveNumber(defaultSalesQuantity);\n const defaultSalesQuantityNormalized =\n defaultSalesQuantityNumber && defaultSalesFactor\n ? defaultSalesQuantityNumber * defaultSalesFactor\n : null;\n const unitPriceBaseQuantityNumber = toPositiveNumber(unitPriceBaseQuantity);\n\n const validConversions = conversions.filter(\n (entry) =>\n toTrimmedOrNull(entry.unitCode) && toTrimmedOrNull(entry.toBaseFactor),\n );\n const conversionPreviewItems = validConversions\n .slice(0, 3)\n .map((entry) => {\n const label = findUnitLabel(entry.unitCode) ?? entry.unitCode;\n const baseLabel = findUnitLabel(defaultUnit) ?? defaultUnit;\n const factor = toTrimmedOrNull(entry.toBaseFactor) ?? \"1\";\n return `1 ${label} = ${factor} ${baseLabel || t(\"catalog.products.uom.baseUnit\", \"base unit\")}`;\n });\n const conversionPreview =\n validConversions.length > 3\n ? `${conversionPreviewItems.join(\" \u2022 \")} (+${validConversions.length - 3})`\n : conversionPreviewItems.join(\" \u2022 \");\n\n return (\n <div\n className={\n embedded ? \"space-y-5\" : \"space-y-5 rounded-lg border bg-card p-4\"\n }\n >\n <div className=\"space-y-1\">\n <h3 className=\"text-sm font-semibold\">\n {t(\"catalog.products.uom.title\", \"Units of measure\")}\n </h3>\n <p className=\"text-xs text-muted-foreground\">\n {t(\n \"catalog.products.uom.description\",\n \"Set base unit, sales unit, and packaging conversions.\",\n )}\n </p>\n </div>\n\n {loadingUnits ? (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <Spinner className=\"h-3 w-3\" />\n {t(\"catalog.products.uom.loadingUnits\", \"Loading units...\")}\n </div>\n ) : null}\n\n {errorLoadingUnits ? (\n <p className=\"text-xs text-destructive\">\n {t(\n \"catalog.products.uom.errors.loadUnits\",\n \"Failed to load units. Please try refreshing the page.\",\n )}\n </p>\n ) : null}\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-base-unit\">{t(\"catalog.products.uom.baseUnit\", \"Base unit\")}</Label>\n <Select\n value={defaultUnit || undefined}\n onValueChange={(value) => setValue(\"defaultUnit\", value || null)}\n disabled={loadingUnits}\n >\n <SelectTrigger id=\"catalog-product-uom-base-unit\">\n <SelectValue placeholder={t(\"catalog.products.uom.selectUnit\", \"Select unit\")} />\n </SelectTrigger>\n <SelectContent>\n {unitOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {errors.defaultUnit ? (\n <p className=\"text-xs text-destructive\">{errors.defaultUnit}</p>\n ) : null}\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-sales-unit\">\n {t(\"catalog.products.uom.defaultSalesUnit\", \"Default sales unit\")}\n </Label>\n <Select\n value={defaultSalesUnit || undefined}\n onValueChange={(value) => setValue(\"defaultSalesUnit\", value || null)}\n disabled={loadingUnits}\n >\n <SelectTrigger id=\"catalog-product-uom-sales-unit\">\n <SelectValue placeholder={t(\"catalog.products.uom.selectUnit\", \"Select unit\")} />\n </SelectTrigger>\n <SelectContent>\n {unitOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {errors.defaultSalesUnit ? (\n <p className=\"text-xs text-destructive\">{errors.defaultSalesUnit}</p>\n ) : null}\n </div>\n\n <div className=\"space-y-2 md:col-span-2\">\n <Label htmlFor=\"catalog-product-uom-default-sales-quantity\">\n {t(\n \"catalog.products.uom.defaultSalesQuantityLabel\",\n \"Default line quantity (in sales unit)\",\n )}\n </Label>\n <Input\n id=\"catalog-product-uom-default-sales-quantity\"\n type=\"text\"\n inputMode=\"decimal\"\n value={defaultSalesQuantity}\n onChange={(event) =>\n setValue(\n \"defaultSalesUnitQuantity\",\n normalizeDecimalInput(event.target.value),\n )\n }\n placeholder=\"1\"\n />\n <p className=\"text-xs text-muted-foreground\">\n {t(\n \"catalog.products.uom.defaultSalesQuantityHint\",\n \"Used to prefill quantity in quote/order lines. Value is interpreted in Default sales unit.\",\n )}\n </p>\n {defaultSalesQuantityNumber && salesUnitLabel ? (\n <p className=\"text-xs text-muted-foreground\">\n {defaultSalesQuantityNormalized && baseUnitLabel\n ? t(\n \"catalog.products.uom.defaultSalesQuantityPreviewWithNormalization\",\n \"Default line: {{quantity}} {{salesUnit}} (= {{normalized}} {{baseUnit}}).\",\n {\n quantity: formatPreviewNumber(defaultSalesQuantityNumber),\n salesUnit: salesUnitLabel,\n normalized: formatPreviewNumber(\n defaultSalesQuantityNormalized,\n ),\n baseUnit: baseUnitLabel,\n },\n )\n : t(\n \"catalog.products.uom.defaultSalesQuantityPreview\",\n \"Default line: {{quantity}} {{salesUnit}}.\",\n {\n quantity: formatPreviewNumber(defaultSalesQuantityNumber),\n salesUnit: salesUnitLabel,\n },\n )}\n </p>\n ) : null}\n {errors.defaultSalesUnitQuantity ? (\n <p className=\"text-xs text-destructive\">\n {errors.defaultSalesUnitQuantity}\n </p>\n ) : null}\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-rounding-mode\">\n {t(\"catalog.products.uom.roundingMode\", \"Rounding mode\")}\n </Label>\n <Select\n value={values.uomRoundingMode ?? \"half_up\"}\n onValueChange={(value) => setValue(\"uomRoundingMode\", value)}\n >\n <SelectTrigger id=\"catalog-product-uom-rounding-mode\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"half_up\">\n {t(\"catalog.products.uom.roundingModeHalfUp\", \"Half up (default)\")}\n </SelectItem>\n <SelectItem value=\"down\">\n {t(\"catalog.products.uom.roundingModeDown\", \"Round down\")}\n </SelectItem>\n <SelectItem value=\"up\">\n {t(\"catalog.products.uom.roundingModeUp\", \"Round up\")}\n </SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-rounding-scale\">\n {t(\"catalog.products.uom.roundingScale\", \"Rounding scale (decimal places)\")}\n </Label>\n <Input\n id=\"catalog-product-uom-rounding-scale\"\n type=\"number\"\n min={0}\n max={6}\n value={values.uomRoundingScale ?? 4}\n onChange={(event) => {\n const parsed = Number.parseInt(event.target.value, 10);\n setValue(\"uomRoundingScale\", Number.isFinite(parsed) ? Math.max(0, Math.min(6, parsed)) : 4);\n }}\n />\n </div>\n </div>\n\n {unitPriceDisplayEnabled ? (\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"catalog-product-unit-price-enabled\"\n checked={unitPriceEnabled}\n onCheckedChange={(checked) =>\n setValue(\"unitPriceEnabled\", checked === true)\n }\n />\n <Label\n htmlFor=\"catalog-product-unit-price-enabled\"\n className=\"text-sm\"\n >\n {t(\n \"catalog.products.unitPrice.enable\",\n \"Enable EU unit price display\",\n )}\n </Label>\n </div>\n\n {unitPriceEnabled ? (\n <div className=\"grid gap-3 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-reference-unit\">\n {t(\n \"catalog.products.unitPrice.referenceUnit\",\n \"Reference unit\",\n )}\n </Label>\n <Select\n value={unitPriceReferenceUnit || undefined}\n onValueChange={(value) => setValue(\"unitPriceReferenceUnit\", value || null)}\n >\n <SelectTrigger id=\"catalog-product-uom-reference-unit\">\n <SelectValue\n placeholder={t(\n \"catalog.products.unitPrice.selectReferenceUnit\",\n \"Select reference unit\",\n )}\n />\n </SelectTrigger>\n <SelectContent>\n {REFERENCE_UNIT_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {t(option.i18nKey, option.fallback)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-unit-price-base-quantity\">\n {t(\n \"catalog.products.unitPrice.baseQuantity\",\n \"Reference quantity (in base unit)\",\n )}\n </Label>\n <Input\n id=\"catalog-product-uom-unit-price-base-quantity\"\n type=\"text\"\n inputMode=\"decimal\"\n value={unitPriceBaseQuantity}\n onChange={(event) =>\n setValue(\n \"unitPriceBaseQuantity\",\n normalizeDecimalInput(event.target.value),\n )\n }\n placeholder=\"1\"\n />\n </div>\n </div>\n ) : null}\n {unitPriceEnabled ? (\n <p className=\"text-xs text-muted-foreground\">\n {unitPriceReferenceUnit && unitPriceBaseQuantityNumber\n ? t(\n \"catalog.products.unitPrice.hintWithPreview\",\n \"Show calculated price per {{quantity}} {{unit}}. For most products use 1 (for example: 1 kg, 1 l, 1 m\u00B2).\",\n {\n quantity: formatPreviewNumber(unitPriceBaseQuantityNumber),\n unit: unitPriceReferenceUnit,\n },\n )\n : t(\n \"catalog.products.unitPrice.hint\",\n \"Show calculated price per selected reference unit. In most cases set quantity to 1.\",\n )}\n </p>\n ) : null}\n </div>\n ) : null}\n\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-2\">\n <Label className=\"text-sm\">\n {t(\"catalog.products.uom.conversions\", \"Product conversions\")}\n </Label>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addConversion}\n >\n <Plus className=\"mr-1.5 h-4 w-4\" />\n {t(\"catalog.products.uom.addConversion\", \"Add conversion\")}\n </Button>\n </div>\n\n {conversions.length === 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\n \"catalog.products.uom.emptyConversions\",\n \"No conversions configured yet.\",\n )}\n </p>\n ) : (\n <div className=\"space-y-2\">\n {conversions.map((entry, index) => {\n const conversionFactor = toPositiveNumber(entry.toBaseFactor);\n const conversionPreviewText =\n entry.unitCode && conversionFactor !== null\n ? t(\n \"catalog.products.uom.conversionPreview\",\n \"1 {{fromUnit}} = {{factor}} {{baseUnit}}\",\n {\n fromUnit: findUnitLabel(entry.unitCode) ?? entry.unitCode,\n factor: formatPreviewNumber(conversionFactor),\n baseUnit:\n findUnitLabel(defaultUnit) ??\n defaultUnit ??\n t(\"catalog.products.uom.baseUnit\", \"base unit\"),\n },\n )\n : null;\n\n return (\n <div\n key={entry.id ?? `uom-conversion-${index}`}\n className=\"grid gap-3 rounded-md border p-3 md:grid-cols-12\"\n >\n <div className=\"space-y-1 md:col-span-4\">\n <Label htmlFor={`catalog-product-uom-conversion-unit-${index}`} className=\"text-xs text-muted-foreground\">\n {t(\"catalog.products.uom.conversionUnit\", \"Sales unit\")}\n </Label>\n <Select\n value={entry.unitCode || undefined}\n onValueChange={(value) => updateConversion(index, { unitCode: value ?? '' })}\n disabled={loadingUnits}\n >\n <SelectTrigger id={`catalog-product-uom-conversion-unit-${index}`}>\n <SelectValue placeholder={t(\"catalog.products.uom.selectUnit\", \"Select unit\")} />\n </SelectTrigger>\n <SelectContent>\n {unitOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-1 md:col-span-3\">\n <Label htmlFor={`catalog-product-uom-conversion-factor-${index}`} className=\"text-xs text-muted-foreground\">\n {t(\n \"catalog.products.uom.toBaseFactor\",\n \"Base units per 1 sales unit\",\n )}\n </Label>\n <Input\n id={`catalog-product-uom-conversion-factor-${index}`}\n type=\"text\"\n inputMode=\"decimal\"\n value={entry.toBaseFactor}\n onChange={(event) =>\n updateConversion(index, {\n toBaseFactor: normalizeDecimalInput(event.target.value),\n })\n }\n placeholder=\"1\"\n />\n </div>\n\n <div className=\"flex items-end gap-2 md:col-span-3\">\n <div className=\"inline-flex h-9 rounded-md border\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-9 w-9 rounded-none border-r\"\n onClick={() => moveConversion(index, \"up\")}\n disabled={index === 0}\n aria-label={t(\n \"catalog.products.uom.moveUp\",\n \"Move conversion up\",\n )}\n >\n <ArrowUp className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-9 w-9 rounded-none\"\n onClick={() => moveConversion(index, \"down\")}\n disabled={index === conversions.length - 1}\n aria-label={t(\n \"catalog.products.uom.moveDown\",\n \"Move conversion down\",\n )}\n >\n <ArrowDown className=\"h-4 w-4\" />\n </Button>\n </div>\n <label\n htmlFor={`catalog-product-uom-conversion-active-${index}`}\n className=\"flex items-center gap-2 px-1 text-xs text-muted-foreground\"\n >\n <Checkbox\n id={`catalog-product-uom-conversion-active-${index}`}\n checked={entry.isActive}\n onCheckedChange={(checked) =>\n updateConversion(index, { isActive: checked === true })\n }\n />\n {t(\"catalog.products.uom.active\", \"Active\")}\n </label>\n </div>\n\n <div className=\"flex items-end justify-end md:col-span-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"text-destructive\"\n onClick={() => removeConversion(index)}\n aria-label={t(\n \"catalog.products.uom.removeConversion\",\n \"Remove conversion\",\n )}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n\n {conversionPreviewText && (\n <p className=\"text-xs text-muted-foreground md:col-span-12\">\n {conversionPreviewText}\n </p>\n )}\n </div>\n );\n })}\n </div>\n )}\n\n {conversions.length > 1 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\n \"catalog.products.uom.conversionOrderHint\",\n \"Use arrows to reorder conversion priority.\",\n )}\n </p>\n ) : null}\n\n {conversionPreview ? (\n <p className=\"text-xs text-muted-foreground\">{conversionPreview}</p>\n ) : null}\n </div>\n </div>\n );\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\";\n\nimport * as React from \"react\";\nimport { useT, useLocale } from \"@open-mercato/shared/lib/i18n/context\";\nimport { parseLocaleNumber } from \"@open-mercato/shared/lib/number\";\nimport { apiCall } from \"@open-mercato/ui/backend/utils/apiCall\";\nimport { Button } from \"@open-mercato/ui/primitives/button\";\nimport { Checkbox } from \"@open-mercato/ui/primitives/checkbox\";\nimport { Input } from \"@open-mercato/ui/primitives/input\";\nimport { Label } from \"@open-mercato/ui/primitives/label\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@open-mercato/ui/primitives/select\";\nimport { Spinner } from \"@open-mercato/ui/primitives/spinner\";\nimport { ArrowDown, ArrowUp, Plus, Trash2 } from \"lucide-react\";\nimport type {\n ProductFormValues,\n ProductUnitConversionDraft,\n} from \"./productForm\";\nimport { createProductUnitConversionDraft } from \"./productForm\";\nimport { REFERENCE_UNIT_CODES } from \"@open-mercato/shared/lib/units/unitCodes\";\nimport { toTrimmedOrNull } from \"./productFormUtils\";\nimport { useUnitPriceDisplayEnabled } from \"./hooks/useUnitPriceDisplayEnabled\";\n\ntype UnitDictionaryEntry = {\n id?: string;\n value?: string;\n label?: string;\n};\n\ntype UnitDictionaryResponse = {\n entries?: UnitDictionaryEntry[];\n};\n\ntype UnitOption = {\n value: string;\n label: string;\n};\n\ntype ProductUomSectionProps = {\n values: ProductFormValues;\n errors: Record<string, string>;\n setValue: (id: string, value: unknown) => void;\n embedded?: boolean;\n};\n\nconst REFERENCE_UNIT_DISPLAY: Record<string, string> = {\n kg: \"1 kg\", l: \"1 l\", m2: \"1 m\\u00B2\", m3: \"1 m\\u00B3\", pc: \"1 pc\",\n};\nconst REFERENCE_UNIT_OPTIONS = REFERENCE_UNIT_CODES.map((code) => ({\n value: code,\n i18nKey: `catalog.products.unitPrice.options.${code}` as const,\n fallback: REFERENCE_UNIT_DISPLAY[code] ?? code,\n}));\n\n// Keeps the field submit-ready (dot-decimal, no grouping characters) while the user is\n// still typing. Falls back to the raw value when it isn't parseable yet (empty, a lone\n// sign) so the field stays editable. Delegates to `parseLocaleNumber` instead of a\n// hard-coded `,` -> `.` swap, which broke on grouped input such as `1 234,56` (#5828).\nexport function normalizeDecimalInput(value: string, locale?: string): string {\n const parsed = parseLocaleNumber(value, locale);\n return parsed === null ? value : String(parsed);\n}\n\nexport function toPositiveNumber(value: unknown, locale?: string): number | null {\n if (typeof value === \"number\") {\n return Number.isFinite(value) && value > 0 ? value : null;\n }\n if (typeof value !== \"string\") return null;\n const normalized = toTrimmedOrNull(value);\n if (!normalized) return null;\n const numeric = parseLocaleNumber(normalized, locale);\n return numeric !== null && numeric > 0 ? numeric : null;\n}\n\nfunction toSortValue(value: string): number {\n const numeric = Number(value);\n return Number.isFinite(numeric) ? numeric : Number.MAX_SAFE_INTEGER;\n}\n\nfunction formatPreviewNumber(value: number): string {\n if (!Number.isFinite(value)) return \"0\";\n const rounded = Math.round(value * 1_000_000) / 1_000_000;\n return Number.isInteger(rounded) ? String(rounded) : rounded.toString();\n}\n\nfunction normalizeConversions(\n value: unknown,\n locale?: string,\n): ProductUnitConversionDraft[] {\n if (!Array.isArray(value)) return [];\n const normalized = value\n .map((entry) => {\n if (!entry || typeof entry !== \"object\") return null;\n const row = entry as ProductUnitConversionDraft;\n return {\n id: toTrimmedOrNull(row.id) ?? null,\n unitCode: toTrimmedOrNull(row.unitCode) ?? \"\",\n toBaseFactor: toTrimmedOrNull(row.toBaseFactor)\n ? normalizeDecimalInput(toTrimmedOrNull(row.toBaseFactor) as string, locale)\n : \"\",\n sortOrder: toTrimmedOrNull(row.sortOrder) ?? \"\",\n isActive: row.isActive !== false,\n } satisfies ProductUnitConversionDraft;\n })\n .filter((entry): entry is ProductUnitConversionDraft => Boolean(entry));\n normalized.sort((left, right) => {\n const leftOrder = toSortValue(left.sortOrder);\n const rightOrder = toSortValue(right.sortOrder);\n if (leftOrder === rightOrder) return left.unitCode.localeCompare(right.unitCode);\n return leftOrder - rightOrder;\n });\n return normalized.map((entry, index) => ({\n ...entry,\n sortOrder: String((index + 1) * 10),\n }));\n}\n\nfunction buildUnitOptions(\n entries: UnitDictionaryEntry[] | undefined,\n): UnitOption[] {\n const list = Array.isArray(entries) ? entries : [];\n const options = list\n .map((entry) => {\n const value = toTrimmedOrNull(entry.value);\n if (!value) return null;\n return {\n value,\n label: toTrimmedOrNull(entry.label) ?? value,\n } satisfies UnitOption;\n })\n .filter((entry): entry is UnitOption => Boolean(entry));\n return options.sort((left, right) => left.label.localeCompare(right.label));\n}\n\nexport function ProductUomSection({\n values,\n errors,\n setValue,\n embedded = false,\n}: ProductUomSectionProps) {\n const t = useT();\n const locale = useLocale();\n const { enabled: unitPriceDisplayEnabled } = useUnitPriceDisplayEnabled();\n const [unitOptions, setUnitOptions] = React.useState<UnitOption[]>([]);\n const [loadingUnits, setLoadingUnits] = React.useState(false);\n const [errorLoadingUnits, setErrorLoadingUnits] = React.useState(false);\n const conversions = React.useMemo(\n () => normalizeConversions(values.unitConversions, locale),\n [values.unitConversions, locale],\n );\n\n React.useEffect(() => {\n let cancelled = false;\n async function loadUnits() {\n setLoadingUnits(true);\n setErrorLoadingUnits(false);\n try {\n const response = await apiCall<UnitDictionaryResponse>(\n \"/api/catalog/dictionaries/unit\",\n undefined,\n { fallback: { entries: [] } },\n );\n if (cancelled) return;\n setUnitOptions(buildUnitOptions(response.result?.entries));\n } catch {\n if (!cancelled) {\n setUnitOptions([]);\n setErrorLoadingUnits(true);\n }\n } finally {\n if (!cancelled) setLoadingUnits(false);\n }\n }\n void loadUnits();\n return () => {\n cancelled = true;\n };\n }, []);\n\n const findUnitLabel = React.useCallback(\n (value: string | null | undefined) => {\n const code = toTrimmedOrNull(value);\n if (!code) return null;\n const option = unitOptions.find((entry) => entry.value === code);\n return option?.label ?? code;\n },\n [unitOptions],\n );\n\n const setConversions = React.useCallback(\n (next: ProductUnitConversionDraft[]) => {\n const normalized = next.map((entry, index) => ({\n ...entry,\n sortOrder: String((index + 1) * 10),\n }));\n setValue(\"unitConversions\", normalized);\n },\n [setValue],\n );\n\n const addConversion = React.useCallback(() => {\n const next = [\n ...conversions,\n createProductUnitConversionDraft({\n sortOrder: String((conversions.length + 1) * 10),\n }),\n ];\n setConversions(next);\n }, [conversions, setConversions]);\n\n const updateConversion = React.useCallback(\n (index: number, patch: Partial<ProductUnitConversionDraft>) => {\n const next = conversions.map((entry, rowIndex) =>\n rowIndex === index ? { ...entry, ...patch } : entry,\n );\n setConversions(next);\n },\n [conversions, setConversions],\n );\n\n const removeConversion = React.useCallback(\n (index: number) => {\n const next = conversions.filter((_entry, rowIndex) => rowIndex !== index);\n setConversions(next);\n },\n [conversions, setConversions],\n );\n\n const moveConversion = React.useCallback(\n (index: number, direction: \"up\" | \"down\") => {\n const targetIndex = direction === \"up\" ? index - 1 : index + 1;\n if (targetIndex < 0 || targetIndex >= conversions.length) return;\n const next = [...conversions];\n const source = next[index];\n next[index] = next[targetIndex];\n next[targetIndex] = source;\n setConversions(next);\n },\n [conversions, setConversions],\n );\n\n const defaultUnit = toTrimmedOrNull(values.defaultUnit) ?? \"\";\n const defaultSalesUnit = toTrimmedOrNull(values.defaultSalesUnit) ?? \"\";\n const defaultSalesQuantityRaw =\n toTrimmedOrNull(values.defaultSalesUnitQuantity) ?? \"1\";\n const defaultSalesQuantity = normalizeDecimalInput(defaultSalesQuantityRaw, locale);\n const unitPriceEnabled = Boolean(values.unitPriceEnabled);\n const unitPriceReferenceUnit =\n toTrimmedOrNull(values.unitPriceReferenceUnit) ?? \"\";\n const unitPriceBaseQuantityRaw =\n toTrimmedOrNull(values.unitPriceBaseQuantity) ?? \"\";\n const unitPriceBaseQuantity = normalizeDecimalInput(unitPriceBaseQuantityRaw, locale);\n\n const baseUnitLabel = findUnitLabel(defaultUnit) ?? defaultUnit;\n const salesUnitLabel =\n findUnitLabel(defaultSalesUnit || defaultUnit) ??\n defaultSalesUnit ??\n defaultUnit;\n\n const defaultSalesFactor = React.useMemo(() => {\n const defaultUnitKey = defaultUnit.toLowerCase();\n const defaultSalesKey = (defaultSalesUnit || defaultUnit).toLowerCase();\n if (!defaultUnitKey || !defaultSalesKey) return null;\n if (defaultSalesKey === defaultUnitKey) return 1;\n const row = conversions.find(\n (entry) =>\n entry.isActive &&\n entry.unitCode.toLowerCase() === defaultSalesKey &&\n toPositiveNumber(entry.toBaseFactor, locale) !== null,\n );\n return row ? toPositiveNumber(row.toBaseFactor, locale) : null;\n }, [conversions, defaultSalesUnit, defaultUnit, locale]);\n\n const defaultSalesQuantityNumber = toPositiveNumber(defaultSalesQuantity, locale);\n const defaultSalesQuantityNormalized =\n defaultSalesQuantityNumber && defaultSalesFactor\n ? defaultSalesQuantityNumber * defaultSalesFactor\n : null;\n const unitPriceBaseQuantityNumber = toPositiveNumber(unitPriceBaseQuantity, locale);\n\n const validConversions = conversions.filter(\n (entry) =>\n toTrimmedOrNull(entry.unitCode) && toTrimmedOrNull(entry.toBaseFactor),\n );\n const conversionPreviewItems = validConversions\n .slice(0, 3)\n .map((entry) => {\n const label = findUnitLabel(entry.unitCode) ?? entry.unitCode;\n const baseLabel = findUnitLabel(defaultUnit) ?? defaultUnit;\n const factor = toTrimmedOrNull(entry.toBaseFactor) ?? \"1\";\n return `1 ${label} = ${factor} ${baseLabel || t(\"catalog.products.uom.baseUnit\", \"base unit\")}`;\n });\n const conversionPreview =\n validConversions.length > 3\n ? `${conversionPreviewItems.join(\" \u2022 \")} (+${validConversions.length - 3})`\n : conversionPreviewItems.join(\" \u2022 \");\n\n return (\n <div\n className={\n embedded ? \"space-y-5\" : \"space-y-5 rounded-lg border bg-card p-4\"\n }\n >\n <div className=\"space-y-1\">\n <h3 className=\"text-sm font-semibold\">\n {t(\"catalog.products.uom.title\", \"Units of measure\")}\n </h3>\n <p className=\"text-xs text-muted-foreground\">\n {t(\n \"catalog.products.uom.description\",\n \"Set base unit, sales unit, and packaging conversions.\",\n )}\n </p>\n </div>\n\n {loadingUnits ? (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <Spinner className=\"h-3 w-3\" />\n {t(\"catalog.products.uom.loadingUnits\", \"Loading units...\")}\n </div>\n ) : null}\n\n {errorLoadingUnits ? (\n <p className=\"text-xs text-destructive\">\n {t(\n \"catalog.products.uom.errors.loadUnits\",\n \"Failed to load units. Please try refreshing the page.\",\n )}\n </p>\n ) : null}\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-base-unit\">{t(\"catalog.products.uom.baseUnit\", \"Base unit\")}</Label>\n <Select\n value={defaultUnit || undefined}\n onValueChange={(value) => setValue(\"defaultUnit\", value || null)}\n disabled={loadingUnits}\n >\n <SelectTrigger id=\"catalog-product-uom-base-unit\">\n <SelectValue placeholder={t(\"catalog.products.uom.selectUnit\", \"Select unit\")} />\n </SelectTrigger>\n <SelectContent>\n {unitOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {errors.defaultUnit ? (\n <p className=\"text-xs text-destructive\">{errors.defaultUnit}</p>\n ) : null}\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-sales-unit\">\n {t(\"catalog.products.uom.defaultSalesUnit\", \"Default sales unit\")}\n </Label>\n <Select\n value={defaultSalesUnit || undefined}\n onValueChange={(value) => setValue(\"defaultSalesUnit\", value || null)}\n disabled={loadingUnits}\n >\n <SelectTrigger id=\"catalog-product-uom-sales-unit\">\n <SelectValue placeholder={t(\"catalog.products.uom.selectUnit\", \"Select unit\")} />\n </SelectTrigger>\n <SelectContent>\n {unitOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {errors.defaultSalesUnit ? (\n <p className=\"text-xs text-destructive\">{errors.defaultSalesUnit}</p>\n ) : null}\n </div>\n\n <div className=\"space-y-2 md:col-span-2\">\n <Label htmlFor=\"catalog-product-uom-default-sales-quantity\">\n {t(\n \"catalog.products.uom.defaultSalesQuantityLabel\",\n \"Default line quantity (in sales unit)\",\n )}\n </Label>\n <Input\n id=\"catalog-product-uom-default-sales-quantity\"\n type=\"text\"\n inputMode=\"decimal\"\n value={defaultSalesQuantity}\n onChange={(event) =>\n setValue(\n \"defaultSalesUnitQuantity\",\n normalizeDecimalInput(event.target.value, locale),\n )\n }\n placeholder=\"1\"\n />\n <p className=\"text-xs text-muted-foreground\">\n {t(\n \"catalog.products.uom.defaultSalesQuantityHint\",\n \"Used to prefill quantity in quote/order lines. Value is interpreted in Default sales unit.\",\n )}\n </p>\n {defaultSalesQuantityNumber && salesUnitLabel ? (\n <p className=\"text-xs text-muted-foreground\">\n {defaultSalesQuantityNormalized && baseUnitLabel\n ? t(\n \"catalog.products.uom.defaultSalesQuantityPreviewWithNormalization\",\n \"Default line: {{quantity}} {{salesUnit}} (= {{normalized}} {{baseUnit}}).\",\n {\n quantity: formatPreviewNumber(defaultSalesQuantityNumber),\n salesUnit: salesUnitLabel,\n normalized: formatPreviewNumber(\n defaultSalesQuantityNormalized,\n ),\n baseUnit: baseUnitLabel,\n },\n )\n : t(\n \"catalog.products.uom.defaultSalesQuantityPreview\",\n \"Default line: {{quantity}} {{salesUnit}}.\",\n {\n quantity: formatPreviewNumber(defaultSalesQuantityNumber),\n salesUnit: salesUnitLabel,\n },\n )}\n </p>\n ) : null}\n {errors.defaultSalesUnitQuantity ? (\n <p className=\"text-xs text-destructive\">\n {errors.defaultSalesUnitQuantity}\n </p>\n ) : null}\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-rounding-mode\">\n {t(\"catalog.products.uom.roundingMode\", \"Rounding mode\")}\n </Label>\n <Select\n value={values.uomRoundingMode ?? \"half_up\"}\n onValueChange={(value) => setValue(\"uomRoundingMode\", value)}\n >\n <SelectTrigger id=\"catalog-product-uom-rounding-mode\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"half_up\">\n {t(\"catalog.products.uom.roundingModeHalfUp\", \"Half up (default)\")}\n </SelectItem>\n <SelectItem value=\"down\">\n {t(\"catalog.products.uom.roundingModeDown\", \"Round down\")}\n </SelectItem>\n <SelectItem value=\"up\">\n {t(\"catalog.products.uom.roundingModeUp\", \"Round up\")}\n </SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-rounding-scale\">\n {t(\"catalog.products.uom.roundingScale\", \"Rounding scale (decimal places)\")}\n </Label>\n <Input\n id=\"catalog-product-uom-rounding-scale\"\n type=\"number\"\n min={0}\n max={6}\n value={values.uomRoundingScale ?? 4}\n onChange={(event) => {\n const parsed = Number.parseInt(event.target.value, 10);\n setValue(\"uomRoundingScale\", Number.isFinite(parsed) ? Math.max(0, Math.min(6, parsed)) : 4);\n }}\n />\n </div>\n </div>\n\n {unitPriceDisplayEnabled ? (\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"catalog-product-unit-price-enabled\"\n checked={unitPriceEnabled}\n onCheckedChange={(checked) =>\n setValue(\"unitPriceEnabled\", checked === true)\n }\n />\n <Label\n htmlFor=\"catalog-product-unit-price-enabled\"\n className=\"text-sm\"\n >\n {t(\n \"catalog.products.unitPrice.enable\",\n \"Enable EU unit price display\",\n )}\n </Label>\n </div>\n\n {unitPriceEnabled ? (\n <div className=\"grid gap-3 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-reference-unit\">\n {t(\n \"catalog.products.unitPrice.referenceUnit\",\n \"Reference unit\",\n )}\n </Label>\n <Select\n value={unitPriceReferenceUnit || undefined}\n onValueChange={(value) => setValue(\"unitPriceReferenceUnit\", value || null)}\n >\n <SelectTrigger id=\"catalog-product-uom-reference-unit\">\n <SelectValue\n placeholder={t(\n \"catalog.products.unitPrice.selectReferenceUnit\",\n \"Select reference unit\",\n )}\n />\n </SelectTrigger>\n <SelectContent>\n {REFERENCE_UNIT_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {t(option.i18nKey, option.fallback)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"catalog-product-uom-unit-price-base-quantity\">\n {t(\n \"catalog.products.unitPrice.baseQuantity\",\n \"Reference quantity (in base unit)\",\n )}\n </Label>\n <Input\n id=\"catalog-product-uom-unit-price-base-quantity\"\n type=\"text\"\n inputMode=\"decimal\"\n value={unitPriceBaseQuantity}\n onChange={(event) =>\n setValue(\n \"unitPriceBaseQuantity\",\n normalizeDecimalInput(event.target.value, locale),\n )\n }\n placeholder=\"1\"\n />\n </div>\n </div>\n ) : null}\n {unitPriceEnabled ? (\n <p className=\"text-xs text-muted-foreground\">\n {unitPriceReferenceUnit && unitPriceBaseQuantityNumber\n ? t(\n \"catalog.products.unitPrice.hintWithPreview\",\n \"Show calculated price per {{quantity}} {{unit}}. For most products use 1 (for example: 1 kg, 1 l, 1 m\u00B2).\",\n {\n quantity: formatPreviewNumber(unitPriceBaseQuantityNumber),\n unit: unitPriceReferenceUnit,\n },\n )\n : t(\n \"catalog.products.unitPrice.hint\",\n \"Show calculated price per selected reference unit. In most cases set quantity to 1.\",\n )}\n </p>\n ) : null}\n </div>\n ) : null}\n\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-2\">\n <Label className=\"text-sm\">\n {t(\"catalog.products.uom.conversions\", \"Product conversions\")}\n </Label>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addConversion}\n >\n <Plus className=\"mr-1.5 h-4 w-4\" />\n {t(\"catalog.products.uom.addConversion\", \"Add conversion\")}\n </Button>\n </div>\n\n {conversions.length === 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\n \"catalog.products.uom.emptyConversions\",\n \"No conversions configured yet.\",\n )}\n </p>\n ) : (\n <div className=\"space-y-2\">\n {conversions.map((entry, index) => {\n const conversionFactor = toPositiveNumber(entry.toBaseFactor, locale);\n const conversionPreviewText =\n entry.unitCode && conversionFactor !== null\n ? t(\n \"catalog.products.uom.conversionPreview\",\n \"1 {{fromUnit}} = {{factor}} {{baseUnit}}\",\n {\n fromUnit: findUnitLabel(entry.unitCode) ?? entry.unitCode,\n factor: formatPreviewNumber(conversionFactor),\n baseUnit:\n findUnitLabel(defaultUnit) ??\n defaultUnit ??\n t(\"catalog.products.uom.baseUnit\", \"base unit\"),\n },\n )\n : null;\n\n return (\n <div\n key={entry.id ?? `uom-conversion-${index}`}\n className=\"grid gap-3 rounded-md border p-3 md:grid-cols-12\"\n >\n <div className=\"space-y-1 md:col-span-4\">\n <Label htmlFor={`catalog-product-uom-conversion-unit-${index}`} className=\"text-xs text-muted-foreground\">\n {t(\"catalog.products.uom.conversionUnit\", \"Sales unit\")}\n </Label>\n <Select\n value={entry.unitCode || undefined}\n onValueChange={(value) => updateConversion(index, { unitCode: value ?? '' })}\n disabled={loadingUnits}\n >\n <SelectTrigger id={`catalog-product-uom-conversion-unit-${index}`}>\n <SelectValue placeholder={t(\"catalog.products.uom.selectUnit\", \"Select unit\")} />\n </SelectTrigger>\n <SelectContent>\n {unitOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-1 md:col-span-3\">\n <Label htmlFor={`catalog-product-uom-conversion-factor-${index}`} className=\"text-xs text-muted-foreground\">\n {t(\n \"catalog.products.uom.toBaseFactor\",\n \"Base units per 1 sales unit\",\n )}\n </Label>\n <Input\n id={`catalog-product-uom-conversion-factor-${index}`}\n type=\"text\"\n inputMode=\"decimal\"\n value={entry.toBaseFactor}\n onChange={(event) =>\n updateConversion(index, {\n toBaseFactor: normalizeDecimalInput(event.target.value, locale),\n })\n }\n placeholder=\"1\"\n />\n </div>\n\n <div className=\"flex items-end gap-2 md:col-span-3\">\n <div className=\"inline-flex h-9 rounded-md border\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-9 w-9 rounded-none border-r\"\n onClick={() => moveConversion(index, \"up\")}\n disabled={index === 0}\n aria-label={t(\n \"catalog.products.uom.moveUp\",\n \"Move conversion up\",\n )}\n >\n <ArrowUp className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-9 w-9 rounded-none\"\n onClick={() => moveConversion(index, \"down\")}\n disabled={index === conversions.length - 1}\n aria-label={t(\n \"catalog.products.uom.moveDown\",\n \"Move conversion down\",\n )}\n >\n <ArrowDown className=\"h-4 w-4\" />\n </Button>\n </div>\n <label\n htmlFor={`catalog-product-uom-conversion-active-${index}`}\n className=\"flex items-center gap-2 px-1 text-xs text-muted-foreground\"\n >\n <Checkbox\n id={`catalog-product-uom-conversion-active-${index}`}\n checked={entry.isActive}\n onCheckedChange={(checked) =>\n updateConversion(index, { isActive: checked === true })\n }\n />\n {t(\"catalog.products.uom.active\", \"Active\")}\n </label>\n </div>\n\n <div className=\"flex items-end justify-end md:col-span-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"text-destructive\"\n onClick={() => removeConversion(index)}\n aria-label={t(\n \"catalog.products.uom.removeConversion\",\n \"Remove conversion\",\n )}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n\n {conversionPreviewText && (\n <p className=\"text-xs text-muted-foreground md:col-span-12\">\n {conversionPreviewText}\n </p>\n )}\n </div>\n );\n })}\n </div>\n )}\n\n {conversions.length > 1 ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\n \"catalog.products.uom.conversionOrderHint\",\n \"Use arrows to reorder conversion priority.\",\n )}\n </p>\n ) : null}\n\n {conversionPreview ? (\n <p className=\"text-xs text-muted-foreground\">{conversionPreview}</p>\n ) : null}\n </div>\n </div>\n );\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }