@open-mercato/core 0.4.5-develop-4849712ccb → 0.4.5-develop-539cff4960

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 (22) hide show
  1. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +28 -15
  2. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  3. package/dist/modules/staff/translations.js +9 -0
  4. package/dist/modules/staff/translations.js.map +7 -0
  5. package/dist/modules/translations/components/TranslationDrawerAction.js +97 -0
  6. package/dist/modules/translations/components/TranslationDrawerAction.js.map +7 -0
  7. package/dist/modules/translations/lib/extract-record-id.js +31 -2
  8. package/dist/modules/translations/lib/extract-record-id.js.map +2 -2
  9. package/dist/modules/translations/lib/resolve-field-list.js +3 -0
  10. package/dist/modules/translations/lib/resolve-field-list.js.map +2 -2
  11. package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js +105 -36
  12. package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js.map +2 -2
  13. package/dist/modules/translations/widgets/injection-table.js +18 -29
  14. package/dist/modules/translations/widgets/injection-table.js.map +2 -2
  15. package/package.json +2 -2
  16. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +8 -0
  17. package/src/modules/staff/translations.ts +5 -0
  18. package/src/modules/translations/components/TranslationDrawerAction.tsx +107 -0
  19. package/src/modules/translations/lib/extract-record-id.ts +47 -3
  20. package/src/modules/translations/lib/resolve-field-list.ts +4 -0
  21. package/src/modules/translations/widgets/injection/translation-manager/widget.client.tsx +108 -36
  22. package/src/modules/translations/widgets/injection-table.ts +19 -33
@@ -28,6 +28,7 @@ import {
28
28
  } from "@open-mercato/core/modules/staff/components/detail/dictionaries";
29
29
  import { JobHistorySection } from "@open-mercato/core/modules/staff/components/detail/JobHistorySection";
30
30
  import { Plus } from "lucide-react";
31
+ import { TranslationDrawerAction } from "@open-mercato/core/modules/translations/components/TranslationDrawerAction";
31
32
  const MARKDOWN_CLASSNAME = "text-sm text-muted-foreground break-words [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs";
32
33
  function StaffTeamMemberDetailPage({ params }) {
33
34
  const memberId = params?.id;
@@ -241,23 +242,35 @@ function StaffTeamMemberDetailPage({ params }) {
241
242
  const roleLabels = Array.isArray(memberRecord?.roleNames) && memberRecord?.roleNames.length ? memberRecord?.roleNames : [t("staff.teamMembers.detail.roles.unassigned", "No roles assigned")];
242
243
  const userEmail = memberRecord?.user?.email ?? null;
243
244
  return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
244
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center justify-between gap-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
245
- /* @__PURE__ */ jsxs(
246
- Link,
245
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
246
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
247
+ /* @__PURE__ */ jsxs(
248
+ Link,
249
+ {
250
+ href: "/backend/staff/team-members",
251
+ className: "inline-flex items-center text-sm text-muted-foreground hover:text-foreground",
252
+ children: [
253
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "mr-1 text-base", children: "\u2190" }),
254
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("staff.teamMembers.detail.back", "Back to team members") })
255
+ ]
256
+ }
257
+ ),
258
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
259
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold text-foreground", children: displayName || t("staff.teamMembers.detail.untitled", "Unnamed team member") }),
260
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("staff.teamMembers.detail.subtitle", "Team member profile and activity") })
261
+ ] })
262
+ ] }),
263
+ /* @__PURE__ */ jsx(
264
+ TranslationDrawerAction,
247
265
  {
248
- href: "/backend/staff/team-members",
249
- className: "inline-flex items-center text-sm text-muted-foreground hover:text-foreground",
250
- children: [
251
- /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "mr-1 text-base", children: "\u2190" }),
252
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("staff.teamMembers.detail.back", "Back to team members") })
253
- ]
266
+ config: memberId ? {
267
+ entityType: "staff:staff_team_member",
268
+ recordId: memberId,
269
+ baseValues: memberRecord ?? void 0
270
+ } : null
254
271
  }
255
- ),
256
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
257
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold text-foreground", children: displayName || t("staff.teamMembers.detail.untitled", "Unnamed team member") }),
258
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("staff.teamMembers.detail.subtitle", "Team member profile and activity") })
259
- ] })
260
- ] }) }),
272
+ )
273
+ ] }),
261
274
  /* @__PURE__ */ jsx("div", { className: "border-b", children: /* @__PURE__ */ jsx(
262
275
  "nav",
263
276
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/staff/backend/staff/team-members/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { AvailabilityRulesEditor } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { buildMemberScheduleItems } from '@open-mercato/core/modules/staff/lib/memberSchedule'\nimport { TeamMemberForm, buildTeamMemberPayload, type TeamMemberFormValues } from '@open-mercato/core/modules/staff/components/TeamMemberForm'\nimport { NotesSection } from '@open-mercato/ui/backend/detail'\nimport { ActivitiesSection, type SectionAction } from '@open-mercato/ui/backend/detail'\nimport { AddressesSection as SharedAddressesSection } from '@open-mercato/ui/backend/detail'\nimport { renderDictionaryColor, renderDictionaryIcon, ICON_SUGGESTIONS } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { createStaffNotesAdapter } from '@open-mercato/core/modules/staff/components/detail/notesAdapter'\nimport { createStaffActivitiesAdapter } from '@open-mercato/core/modules/staff/components/detail/activitiesAdapter'\nimport { createStaffAddressAdapter, createStaffAddressTypesAdapter } from '@open-mercato/core/modules/staff/components/detail/addressesAdapter'\nimport { MarkdownContent } from '@open-mercato/ui/backend/markdown/MarkdownContent'\nimport {\n createStaffDictionaryEntry,\n loadStaffDictionary,\n type DictionaryEntryOption,\n} from '@open-mercato/core/modules/staff/components/detail/dictionaries'\nimport { JobHistorySection } from '@open-mercato/core/modules/staff/components/detail/JobHistorySection'\nimport type { DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport { Plus } from 'lucide-react'\n\nconst MARKDOWN_CLASSNAME =\n 'text-sm text-muted-foreground break-words [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs'\n\ntype TeamMemberRecord = {\n id: string\n teamId?: string | null\n team_id?: string | null\n displayName: string\n display_name?: string\n description?: string | null\n userId?: string | null\n user_id?: string | null\n roleIds?: string[]\n role_ids?: string[]\n roleNames?: string[]\n tags?: string[]\n isActive?: boolean\n is_active?: boolean\n availabilityRuleSetId?: string | null\n availability_rule_set_id?: string | null\n user?: { id?: string; email?: string | null } | null\n team?: { id?: string; name?: string | null } | null\n customFields?: Record<string, unknown> | null\n} & Record<string, unknown>\n\ntype TeamMemberResponse = {\n items?: TeamMemberRecord[]\n}\n\nexport default function StaffTeamMemberDetailPage({ params }: { params?: { id?: string } }) {\n const memberId = params?.id\n const t = useT()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n const router = useRouter()\n const searchParams = useSearchParams()\n const [initialValues, setInitialValues] = React.useState<TeamMemberFormValues | null>(null)\n const [memberRecord, setMemberRecord] = React.useState<TeamMemberRecord | null>(null)\n const [availabilityRuleSetId, setAvailabilityRuleSetId] = React.useState<string | null>(null)\n const [activePanel, setActivePanel] = React.useState<'details' | 'availability' | 'jobHistory'>('details')\n const [activeTab, setActiveTab] = React.useState<'notes' | 'activities' | 'addresses'>('notes')\n const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)\n const [activityDictionaryId, setActivityDictionaryId] = React.useState<string | null>(null)\n const [activityTypeEntries, setActivityTypeEntries] = React.useState<DictionaryEntryOption[]>([])\n const flashShownRef = React.useRef(false)\n\n const notesAdapter = React.useMemo(() => createStaffNotesAdapter(detailTranslator), [detailTranslator])\n const activitiesAdapter = React.useMemo(() => createStaffActivitiesAdapter(detailTranslator), [detailTranslator])\n const addressesAdapter = React.useMemo(() => createStaffAddressAdapter(detailTranslator), [detailTranslator])\n const addressTypesAdapter = React.useMemo(() => createStaffAddressTypesAdapter(detailTranslator), [detailTranslator])\n\n const activityTypeLabels = React.useMemo<DictionarySelectLabels>(() => ({\n placeholder: t('staff.teamMembers.detail.activities.dictionary.placeholder', 'Select an activity type'),\n addLabel: t('staff.teamMembers.detail.activities.dictionary.add', 'Add type'),\n addPrompt: t('staff.teamMembers.detail.activities.dictionary.prompt', 'Name the type'),\n dialogTitle: t('staff.teamMembers.detail.activities.dictionary.dialogTitle', 'Add activity type'),\n valueLabel: t('staff.teamMembers.detail.activities.dictionary.valueLabel', 'Name'),\n valuePlaceholder: t('staff.teamMembers.detail.activities.dictionary.valuePlaceholder', 'Name'),\n labelLabel: t('staff.teamMembers.detail.activities.dictionary.labelLabel', 'Label'),\n labelPlaceholder: t('staff.teamMembers.detail.activities.dictionary.labelPlaceholder', 'Display name shown in UI'),\n emptyError: t('staff.teamMembers.detail.activities.dictionary.emptyError', 'Please enter a name'),\n cancelLabel: t('staff.teamMembers.detail.activities.dictionary.cancel', 'Cancel'),\n saveLabel: t('staff.teamMembers.detail.activities.dictionary.save', 'Save'),\n saveShortcutHint: t('staff.teamMembers.detail.activities.dictionary.saveShortcut', '\u2318/Ctrl + Enter'),\n errorLoad: t('staff.teamMembers.detail.activities.dictionary.errorLoad', 'Failed to load options'),\n errorSave: t('staff.teamMembers.detail.activities.dictionary.errorSave', 'Failed to save option'),\n loadingLabel: t('staff.teamMembers.detail.activities.dictionary.loading', 'Loading\u2026'),\n manageTitle: t('staff.teamMembers.detail.activities.dictionary.manage', 'Manage dictionary'),\n }), [t])\n\n const loadActivityOptions = React.useCallback(async () => {\n const { dictionary, entries } = await loadStaffDictionary('activityTypes')\n setActivityDictionaryId(dictionary?.id ?? null)\n setActivityTypeEntries(entries)\n return entries\n }, [])\n\n const createActivityOption = React.useCallback(\n async (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => {\n const entry = await createStaffDictionaryEntry('activityTypes', input)\n if (!entry) {\n throw new Error(t('staff.teamMembers.detail.activities.dictionary.errorSave', 'Failed to save option'))\n }\n return entry\n },\n [t],\n )\n\n React.useEffect(() => {\n loadActivityOptions().catch(() => {})\n }, [loadActivityOptions])\n\n const activityTypeMap = React.useMemo(\n () => new Map(activityTypeEntries.map((entry) => [entry.value, entry])),\n [activityTypeEntries],\n )\n\n const resolveActivityPresentation = React.useCallback(\n (activity: { activityType: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const entry = activityTypeMap.get(activity.activityType)\n return {\n label: entry?.label ?? activity.activityType,\n icon: entry?.icon ?? activity.appearanceIcon ?? null,\n color: entry?.color ?? activity.appearanceColor ?? null,\n }\n },\n [activityTypeMap],\n )\n\n const manageActivityHref = React.useMemo(() => {\n if (!activityDictionaryId) return '/backend/config/dictionaries'\n return `/backend/config/dictionaries?dictionaryId=${encodeURIComponent(activityDictionaryId)}`\n }, [activityDictionaryId])\n\n const appearanceLabels = React.useMemo(() => ({\n colorLabel: t('staff.teamMembers.detail.activities.appearance.colorLabel', 'Color'),\n colorHelp: t('staff.teamMembers.detail.activities.appearance.colorHelp', 'Pick a highlight color for this entry.'),\n colorClearLabel: t('staff.teamMembers.detail.activities.appearance.colorClear', 'Remove color'),\n iconLabel: t('staff.teamMembers.detail.activities.appearance.iconLabel', 'Icon or emoji'),\n iconPlaceholder: t('staff.teamMembers.detail.activities.appearance.iconPlaceholder', 'Type an emoji or pick one of the suggestions.'),\n iconPickerTriggerLabel: t('staff.teamMembers.detail.activities.appearance.iconBrowse', 'Browse icons and emojis'),\n iconSearchPlaceholder: t('staff.teamMembers.detail.activities.appearance.iconSearchPlaceholder', 'Search icons or emojis\u2026'),\n iconSearchEmptyLabel: t('staff.teamMembers.detail.activities.appearance.iconSearchEmpty', 'No icons match your search.'),\n iconSuggestionsLabel: t('staff.teamMembers.detail.activities.appearance.iconSuggestions', 'Suggestions'),\n iconClearLabel: t('staff.teamMembers.detail.activities.appearance.iconClear', 'Remove icon'),\n previewEmptyLabel: t('staff.teamMembers.detail.activities.appearance.previewEmpty', 'No appearance selected'),\n }), [t])\n\n const renderCustomFields = React.useCallback((activity: { id?: string; customFields?: Array<{ key: string; label?: string | null; value: unknown }> }) => {\n const entries = Array.isArray(activity.customFields) ? activity.customFields : []\n if (!entries.length) return null\n const emptyLabel = t('staff.teamMembers.detail.activities.customFields.empty', 'Not provided')\n return (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {entries.map((entry, index) => {\n const label = entry.label ?? entry.key\n const value = entry.value\n const hasValue = !(value == null || value === '' || (Array.isArray(value) && value.length === 0))\n const content = hasValue\n ? Array.isArray(value)\n ? value.map((item) => String(item)).join(', ')\n : String(value)\n : emptyLabel\n return (\n <div\n key={`activity-${activity.id ?? 'row'}-custom-${index}`}\n className=\"rounded-md border border-border/60 bg-muted/10 px-3 py-2\"\n >\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n <div className=\"mt-1 text-sm text-foreground\">{content}</div>\n </div>\n )\n })}\n </div>\n )\n }, [t])\n\n React.useEffect(() => {\n if (!memberId) return\n const memberIdValue = memberId\n let cancelled = false\n async function loadMember() {\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1', ids: memberIdValue })\n const payload = await readApiResultOrThrow<TeamMemberResponse>(\n `/api/staff/team-members?${params.toString()}`,\n undefined,\n { errorMessage: t('staff.teamMembers.form.errors.load', 'Failed to load team member.') },\n )\n const record = Array.isArray(payload.items) ? payload.items[0] : null\n if (!record) throw new Error(t('staff.teamMembers.form.errors.notFound', 'Team member not found.'))\n const customFields = extractCustomFieldEntries(record)\n if (!cancelled) {\n const resolvedTeamId = record.teamId ?? record.team_id ?? null\n const normalizedRoleIds = normalizeStringList(resolvePreferredArray(record.roleIds, record.role_ids))\n setInitialValues({\n id: record.id,\n teamId: resolvedTeamId,\n userId: record.userId ?? record.user_id ?? null,\n displayName: record.displayName ?? record.display_name ?? '',\n description: record.description ?? '',\n roleIds: normalizedRoleIds,\n tags: normalizeStringList(record.tags),\n isActive: record.isActive ?? record.is_active ?? true,\n ...customFields,\n })\n setMemberRecord(record)\n setAvailabilityRuleSetId(\n typeof record.availabilityRuleSetId === 'string'\n ? record.availabilityRuleSetId\n : typeof record.availability_rule_set_id === 'string'\n ? record.availability_rule_set_id\n : null,\n )\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('staff.teamMembers.form.errors.load', 'Failed to load team member.')\n flash(message, 'error')\n }\n }\n void loadMember()\n return () => { cancelled = true }\n }, [memberId, t])\n\n React.useEffect(() => {\n if (!searchParams) return\n const created = searchParams.get('created') === '1'\n if (created && !flashShownRef.current) {\n flashShownRef.current = true\n flash(t('staff.teamMembers.flash.createdAvailability', 'Saved. You can now set availability.'), 'success')\n const nextParams = new URLSearchParams(searchParams.toString())\n nextParams.delete('created')\n const nextQuery = nextParams.toString()\n const nextPath = memberId\n ? `/backend/staff/team-members/${encodeURIComponent(memberId)}${nextQuery ? `?${nextQuery}` : ''}`\n : `/backend/staff/team-members${nextQuery ? `?${nextQuery}` : ''}`\n router.replace(nextPath)\n }\n }, [memberId, router, searchParams, t])\n\n const handleSubmit = React.useCallback(async (values: TeamMemberFormValues) => {\n if (!memberId) return\n const payload = buildTeamMemberPayload(values, { id: memberId })\n await updateCrud('staff/team-members', payload, {\n errorMessage: t('staff.teamMembers.form.errors.update', 'Failed to update team member.'),\n })\n flash(t('staff.teamMembers.form.flash.updated', 'Team member updated.'), 'success')\n router.push('/backend/staff/team-members')\n }, [memberId, router, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!memberId) return\n await deleteCrud('staff/team-members', memberId, {\n errorMessage: t('staff.teamMembers.form.errors.delete', 'Failed to delete team member.'),\n })\n flash(t('staff.teamMembers.form.flash.deleted', 'Team member deleted.'), 'success')\n router.push('/backend/staff/team-members')\n }, [memberId, router, t])\n\n const handleRulesetChange = React.useCallback(async (nextId: string | null) => {\n if (!memberId) return\n await updateCrud('staff/team-members', { id: memberId, availabilityRuleSetId: nextId }, {\n errorMessage: t('staff.teamMembers.availability.ruleset.updateError', 'Failed to update schedule.'),\n })\n setAvailabilityRuleSetId(nextId)\n flash(t('staff.teamMembers.availability.ruleset.updateSuccess', 'Schedule updated.'), 'success')\n }, [memberId, t])\n\n const panelTabs = React.useMemo(() => ([\n { id: 'details' as const, label: t('staff.teamMembers.detail.tabs.details', 'Details') },\n { id: 'availability' as const, label: t('staff.teamMembers.detail.tabs.availability', 'Availability') },\n { id: 'jobHistory' as const, label: t('staff.teamMembers.detail.tabs.jobHistory', 'Job history') },\n ]), [t])\n\n const tabs = React.useMemo(() => ([\n { id: 'notes' as const, label: t('staff.teamMembers.detail.tabs.notes', 'Notes') },\n { id: 'activities' as const, label: t('staff.teamMembers.detail.tabs.activities', 'Activities') },\n { id: 'addresses' as const, label: t('staff.teamMembers.detail.tabs.addresses', 'Addresses') },\n ]), [t])\n\n const resolvedInitialValues = initialValues ?? {\n roleIds: [],\n isActive: true,\n }\n\n const displayName = memberRecord?.displayName ?? memberRecord?.display_name ?? resolvedInitialValues.displayName ?? ''\n const teamLabel = memberRecord?.team?.name ?? t('staff.teamMembers.detail.team.unassigned', 'Unassigned team')\n const roleLabels = Array.isArray(memberRecord?.roleNames) && memberRecord?.roleNames.length\n ? memberRecord?.roleNames\n : [t('staff.teamMembers.detail.roles.unassigned', 'No roles assigned')]\n const userEmail = memberRecord?.user?.email ?? null\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <Link\n href=\"/backend/staff/team-members\"\n className=\"inline-flex items-center text-sm text-muted-foreground hover:text-foreground\"\n >\n <span aria-hidden className=\"mr-1 text-base\">\u2190</span>\n <span className=\"sr-only\">{t('staff.teamMembers.detail.back', 'Back to team members')}</span>\n </Link>\n <div className=\"space-y-1\">\n <h1 className=\"text-2xl font-semibold text-foreground\">\n {displayName || t('staff.teamMembers.detail.untitled', 'Unnamed team member')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.teamMembers.detail.subtitle', 'Team member profile and activity')}\n </p>\n </div>\n </div>\n </div>\n\n <div className=\"border-b\">\n <nav\n className=\"flex flex-wrap items-center gap-5 text-sm\"\n aria-label={t('staff.teamMembers.detail.tabs.label', 'Team member sections')}\n >\n {panelTabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activePanel === tab.id}\n onClick={() => setActivePanel(tab.id)}\n className={`relative -mb-px border-b-2 px-0 py-2 text-sm font-medium transition-colors ${\n activePanel === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </nav>\n </div>\n\n {activePanel === 'details' ? (\n <>\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,2fr),minmax(0,1.1fr)]\">\n <div className=\"space-y-6\">\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.highlights', 'Highlights')}\n </h2>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.team', 'Team')}\n </p>\n <p className=\"text-base text-foreground\">{teamLabel}</p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.roles', 'Roles')}\n </p>\n <p className=\"text-base text-foreground\">{roleLabels.join(', ')}</p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.user', 'User')}\n </p>\n <p className=\"text-base text-foreground\">\n {userEmail ?? t('staff.teamMembers.detail.fields.userEmpty', 'No user linked')}\n </p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.status', 'Status')}\n </p>\n <p className=\"text-base text-foreground\">\n {memberRecord?.isActive ?? memberRecord?.is_active\n ? t('staff.teamMembers.detail.status.active', 'Active')\n : t('staff.teamMembers.detail.status.inactive', 'Inactive')}\n </p>\n </div>\n </div>\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex gap-2\">\n {tabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n onClick={() => setActiveTab(tab.id)}\n className={`relative -mb-px border-b-2 px-0 py-1 text-sm font-medium transition-colors ${\n activeTab === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </div>\n {sectionAction ? (\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={sectionAction.disabled}\n onClick={() => sectionAction.onClick()}\n >\n {sectionAction.icon ?? (activeTab === 'addresses' ? <Plus className=\"mr-2 h-4 w-4\" /> : null)}\n {sectionAction.label}\n </Button>\n ) : null}\n </div>\n {activeTab === 'notes' ? (\n <NotesSection\n entityId={memberId ?? null}\n emptyLabel={t('staff.teamMembers.detail.notes.empty', 'No notes yet.')}\n viewerUserId={null}\n viewerName={null}\n viewerEmail={null}\n addActionLabel={t('staff.teamMembers.detail.notes.add', 'Add note')}\n emptyState={{\n title: t('staff.teamMembers.detail.notes.emptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('staff.teamMembers.detail.notes.emptyAction', 'Add a note'),\n }}\n onActionChange={setSectionAction}\n translator={detailTranslator}\n labelPrefix=\"staff.teamMembers.detail.notes\"\n inlineLabelPrefix=\"staff.teamMembers.detail.inline\"\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n />\n ) : null}\n {activeTab === 'activities' ? (\n <ActivitiesSection\n entityId={memberId ?? null}\n addActionLabel={t('staff.teamMembers.detail.activities.add', 'Log activity')}\n emptyState={{\n title: t('staff.teamMembers.detail.activities.emptyTitle', 'No activities yet'),\n actionLabel: t('staff.teamMembers.detail.activities.emptyAction', 'Add an activity'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={activitiesAdapter}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n resolveActivityPresentation={resolveActivityPresentation}\n renderCustomFields={renderCustomFields}\n labelPrefix=\"staff.teamMembers.detail.activities\"\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n appearanceLabels={appearanceLabels}\n manageHref={manageActivityHref}\n customFieldEntityIds={['staff:staff_team_member_activity']}\n />\n ) : null}\n {activeTab === 'addresses' ? (\n <SharedAddressesSection\n entityId={memberId ?? null}\n emptyLabel={t('staff.teamMembers.detail.addresses.empty', 'No addresses yet.')}\n addActionLabel={t('staff.teamMembers.detail.addresses.add', 'Add address')}\n emptyState={{\n title: t('staff.teamMembers.detail.addresses.emptyTitle', 'No addresses yet'),\n actionLabel: t('staff.teamMembers.detail.addresses.emptyAction', 'Add an address'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={addressesAdapter}\n addressTypesAdapter={addressTypesAdapter}\n labelPrefix=\"staff.teamMembers.detail.addresses\"\n />\n ) : null}\n </div>\n </div>\n <div className=\"space-y-4\">\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.details', 'Member details')}\n </h2>\n <div className=\"space-y-2\">\n {memberRecord?.description ? (\n <MarkdownContent body={memberRecord.description} format=\"markdown\" className={MARKDOWN_CLASSNAME} />\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.teamMembers.detail.descriptionEmpty', 'No description provided.')}\n </p>\n )}\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.formTitle', 'Member settings')}\n </h2>\n <TeamMemberForm\n embedded\n title={t('staff.teamMembers.form.editTitle', 'Edit team member')}\n backHref=\"/backend/staff/team-members\"\n cancelHref=\"/backend/staff/team-members\"\n initialValues={resolvedInitialValues}\n onSubmit={handleSubmit}\n onDelete={handleDelete}\n isLoading={!initialValues}\n loadingMessage={t('staff.teamMembers.form.loading', 'Loading team member...')}\n />\n </div>\n </>\n ) : activePanel === 'availability' ? (\n <AvailabilityRulesEditor\n subjectType=\"member\"\n subjectId={memberId ?? ''}\n labelPrefix=\"staff.teamMembers\"\n mode=\"availability\"\n rulesetId={availabilityRuleSetId}\n onRulesetChange={handleRulesetChange}\n buildScheduleItems={({ availabilityRules, translate: translateLabel }) => (\n buildMemberScheduleItems({ availabilityRules, translate: translateLabel })\n )}\n />\n ) : (\n <div className=\"rounded-lg border bg-card p-4\">\n <JobHistorySection memberId={memberId ?? null} />\n </div>\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n\nfunction normalizeStringList(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n return value\n .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))\n .filter((entry) => entry.length > 0)\n}\n\nfunction resolvePreferredArray<T>(primary?: T[] | null, fallback?: T[] | null): T[] | undefined {\n if (Array.isArray(primary) && primary.length) return primary\n if (Array.isArray(fallback) && fallback.length) return fallback\n return Array.isArray(primary) ? primary : Array.isArray(fallback) ? fallback : undefined\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { AvailabilityRulesEditor } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { buildMemberScheduleItems } from '@open-mercato/core/modules/staff/lib/memberSchedule'\nimport { TeamMemberForm, buildTeamMemberPayload, type TeamMemberFormValues } from '@open-mercato/core/modules/staff/components/TeamMemberForm'\nimport { NotesSection } from '@open-mercato/ui/backend/detail'\nimport { ActivitiesSection, type SectionAction } from '@open-mercato/ui/backend/detail'\nimport { AddressesSection as SharedAddressesSection } from '@open-mercato/ui/backend/detail'\nimport { renderDictionaryColor, renderDictionaryIcon, ICON_SUGGESTIONS } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { createStaffNotesAdapter } from '@open-mercato/core/modules/staff/components/detail/notesAdapter'\nimport { createStaffActivitiesAdapter } from '@open-mercato/core/modules/staff/components/detail/activitiesAdapter'\nimport { createStaffAddressAdapter, createStaffAddressTypesAdapter } from '@open-mercato/core/modules/staff/components/detail/addressesAdapter'\nimport { MarkdownContent } from '@open-mercato/ui/backend/markdown/MarkdownContent'\nimport {\n createStaffDictionaryEntry,\n loadStaffDictionary,\n type DictionaryEntryOption,\n} from '@open-mercato/core/modules/staff/components/detail/dictionaries'\nimport { JobHistorySection } from '@open-mercato/core/modules/staff/components/detail/JobHistorySection'\nimport type { DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport { Plus } from 'lucide-react'\nimport { TranslationDrawerAction } from '@open-mercato/core/modules/translations/components/TranslationDrawerAction'\n\nconst MARKDOWN_CLASSNAME =\n 'text-sm text-muted-foreground break-words [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs'\n\ntype TeamMemberRecord = {\n id: string\n teamId?: string | null\n team_id?: string | null\n displayName: string\n display_name?: string\n description?: string | null\n userId?: string | null\n user_id?: string | null\n roleIds?: string[]\n role_ids?: string[]\n roleNames?: string[]\n tags?: string[]\n isActive?: boolean\n is_active?: boolean\n availabilityRuleSetId?: string | null\n availability_rule_set_id?: string | null\n user?: { id?: string; email?: string | null } | null\n team?: { id?: string; name?: string | null } | null\n customFields?: Record<string, unknown> | null\n} & Record<string, unknown>\n\ntype TeamMemberResponse = {\n items?: TeamMemberRecord[]\n}\n\nexport default function StaffTeamMemberDetailPage({ params }: { params?: { id?: string } }) {\n const memberId = params?.id\n const t = useT()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n const router = useRouter()\n const searchParams = useSearchParams()\n const [initialValues, setInitialValues] = React.useState<TeamMemberFormValues | null>(null)\n const [memberRecord, setMemberRecord] = React.useState<TeamMemberRecord | null>(null)\n const [availabilityRuleSetId, setAvailabilityRuleSetId] = React.useState<string | null>(null)\n const [activePanel, setActivePanel] = React.useState<'details' | 'availability' | 'jobHistory'>('details')\n const [activeTab, setActiveTab] = React.useState<'notes' | 'activities' | 'addresses'>('notes')\n const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)\n const [activityDictionaryId, setActivityDictionaryId] = React.useState<string | null>(null)\n const [activityTypeEntries, setActivityTypeEntries] = React.useState<DictionaryEntryOption[]>([])\n const flashShownRef = React.useRef(false)\n\n const notesAdapter = React.useMemo(() => createStaffNotesAdapter(detailTranslator), [detailTranslator])\n const activitiesAdapter = React.useMemo(() => createStaffActivitiesAdapter(detailTranslator), [detailTranslator])\n const addressesAdapter = React.useMemo(() => createStaffAddressAdapter(detailTranslator), [detailTranslator])\n const addressTypesAdapter = React.useMemo(() => createStaffAddressTypesAdapter(detailTranslator), [detailTranslator])\n\n const activityTypeLabels = React.useMemo<DictionarySelectLabels>(() => ({\n placeholder: t('staff.teamMembers.detail.activities.dictionary.placeholder', 'Select an activity type'),\n addLabel: t('staff.teamMembers.detail.activities.dictionary.add', 'Add type'),\n addPrompt: t('staff.teamMembers.detail.activities.dictionary.prompt', 'Name the type'),\n dialogTitle: t('staff.teamMembers.detail.activities.dictionary.dialogTitle', 'Add activity type'),\n valueLabel: t('staff.teamMembers.detail.activities.dictionary.valueLabel', 'Name'),\n valuePlaceholder: t('staff.teamMembers.detail.activities.dictionary.valuePlaceholder', 'Name'),\n labelLabel: t('staff.teamMembers.detail.activities.dictionary.labelLabel', 'Label'),\n labelPlaceholder: t('staff.teamMembers.detail.activities.dictionary.labelPlaceholder', 'Display name shown in UI'),\n emptyError: t('staff.teamMembers.detail.activities.dictionary.emptyError', 'Please enter a name'),\n cancelLabel: t('staff.teamMembers.detail.activities.dictionary.cancel', 'Cancel'),\n saveLabel: t('staff.teamMembers.detail.activities.dictionary.save', 'Save'),\n saveShortcutHint: t('staff.teamMembers.detail.activities.dictionary.saveShortcut', '\u2318/Ctrl + Enter'),\n errorLoad: t('staff.teamMembers.detail.activities.dictionary.errorLoad', 'Failed to load options'),\n errorSave: t('staff.teamMembers.detail.activities.dictionary.errorSave', 'Failed to save option'),\n loadingLabel: t('staff.teamMembers.detail.activities.dictionary.loading', 'Loading\u2026'),\n manageTitle: t('staff.teamMembers.detail.activities.dictionary.manage', 'Manage dictionary'),\n }), [t])\n\n const loadActivityOptions = React.useCallback(async () => {\n const { dictionary, entries } = await loadStaffDictionary('activityTypes')\n setActivityDictionaryId(dictionary?.id ?? null)\n setActivityTypeEntries(entries)\n return entries\n }, [])\n\n const createActivityOption = React.useCallback(\n async (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => {\n const entry = await createStaffDictionaryEntry('activityTypes', input)\n if (!entry) {\n throw new Error(t('staff.teamMembers.detail.activities.dictionary.errorSave', 'Failed to save option'))\n }\n return entry\n },\n [t],\n )\n\n React.useEffect(() => {\n loadActivityOptions().catch(() => {})\n }, [loadActivityOptions])\n\n const activityTypeMap = React.useMemo(\n () => new Map(activityTypeEntries.map((entry) => [entry.value, entry])),\n [activityTypeEntries],\n )\n\n const resolveActivityPresentation = React.useCallback(\n (activity: { activityType: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const entry = activityTypeMap.get(activity.activityType)\n return {\n label: entry?.label ?? activity.activityType,\n icon: entry?.icon ?? activity.appearanceIcon ?? null,\n color: entry?.color ?? activity.appearanceColor ?? null,\n }\n },\n [activityTypeMap],\n )\n\n const manageActivityHref = React.useMemo(() => {\n if (!activityDictionaryId) return '/backend/config/dictionaries'\n return `/backend/config/dictionaries?dictionaryId=${encodeURIComponent(activityDictionaryId)}`\n }, [activityDictionaryId])\n\n const appearanceLabels = React.useMemo(() => ({\n colorLabel: t('staff.teamMembers.detail.activities.appearance.colorLabel', 'Color'),\n colorHelp: t('staff.teamMembers.detail.activities.appearance.colorHelp', 'Pick a highlight color for this entry.'),\n colorClearLabel: t('staff.teamMembers.detail.activities.appearance.colorClear', 'Remove color'),\n iconLabel: t('staff.teamMembers.detail.activities.appearance.iconLabel', 'Icon or emoji'),\n iconPlaceholder: t('staff.teamMembers.detail.activities.appearance.iconPlaceholder', 'Type an emoji or pick one of the suggestions.'),\n iconPickerTriggerLabel: t('staff.teamMembers.detail.activities.appearance.iconBrowse', 'Browse icons and emojis'),\n iconSearchPlaceholder: t('staff.teamMembers.detail.activities.appearance.iconSearchPlaceholder', 'Search icons or emojis\u2026'),\n iconSearchEmptyLabel: t('staff.teamMembers.detail.activities.appearance.iconSearchEmpty', 'No icons match your search.'),\n iconSuggestionsLabel: t('staff.teamMembers.detail.activities.appearance.iconSuggestions', 'Suggestions'),\n iconClearLabel: t('staff.teamMembers.detail.activities.appearance.iconClear', 'Remove icon'),\n previewEmptyLabel: t('staff.teamMembers.detail.activities.appearance.previewEmpty', 'No appearance selected'),\n }), [t])\n\n const renderCustomFields = React.useCallback((activity: { id?: string; customFields?: Array<{ key: string; label?: string | null; value: unknown }> }) => {\n const entries = Array.isArray(activity.customFields) ? activity.customFields : []\n if (!entries.length) return null\n const emptyLabel = t('staff.teamMembers.detail.activities.customFields.empty', 'Not provided')\n return (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {entries.map((entry, index) => {\n const label = entry.label ?? entry.key\n const value = entry.value\n const hasValue = !(value == null || value === '' || (Array.isArray(value) && value.length === 0))\n const content = hasValue\n ? Array.isArray(value)\n ? value.map((item) => String(item)).join(', ')\n : String(value)\n : emptyLabel\n return (\n <div\n key={`activity-${activity.id ?? 'row'}-custom-${index}`}\n className=\"rounded-md border border-border/60 bg-muted/10 px-3 py-2\"\n >\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n <div className=\"mt-1 text-sm text-foreground\">{content}</div>\n </div>\n )\n })}\n </div>\n )\n }, [t])\n\n React.useEffect(() => {\n if (!memberId) return\n const memberIdValue = memberId\n let cancelled = false\n async function loadMember() {\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1', ids: memberIdValue })\n const payload = await readApiResultOrThrow<TeamMemberResponse>(\n `/api/staff/team-members?${params.toString()}`,\n undefined,\n { errorMessage: t('staff.teamMembers.form.errors.load', 'Failed to load team member.') },\n )\n const record = Array.isArray(payload.items) ? payload.items[0] : null\n if (!record) throw new Error(t('staff.teamMembers.form.errors.notFound', 'Team member not found.'))\n const customFields = extractCustomFieldEntries(record)\n if (!cancelled) {\n const resolvedTeamId = record.teamId ?? record.team_id ?? null\n const normalizedRoleIds = normalizeStringList(resolvePreferredArray(record.roleIds, record.role_ids))\n setInitialValues({\n id: record.id,\n teamId: resolvedTeamId,\n userId: record.userId ?? record.user_id ?? null,\n displayName: record.displayName ?? record.display_name ?? '',\n description: record.description ?? '',\n roleIds: normalizedRoleIds,\n tags: normalizeStringList(record.tags),\n isActive: record.isActive ?? record.is_active ?? true,\n ...customFields,\n })\n setMemberRecord(record)\n setAvailabilityRuleSetId(\n typeof record.availabilityRuleSetId === 'string'\n ? record.availabilityRuleSetId\n : typeof record.availability_rule_set_id === 'string'\n ? record.availability_rule_set_id\n : null,\n )\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('staff.teamMembers.form.errors.load', 'Failed to load team member.')\n flash(message, 'error')\n }\n }\n void loadMember()\n return () => { cancelled = true }\n }, [memberId, t])\n\n React.useEffect(() => {\n if (!searchParams) return\n const created = searchParams.get('created') === '1'\n if (created && !flashShownRef.current) {\n flashShownRef.current = true\n flash(t('staff.teamMembers.flash.createdAvailability', 'Saved. You can now set availability.'), 'success')\n const nextParams = new URLSearchParams(searchParams.toString())\n nextParams.delete('created')\n const nextQuery = nextParams.toString()\n const nextPath = memberId\n ? `/backend/staff/team-members/${encodeURIComponent(memberId)}${nextQuery ? `?${nextQuery}` : ''}`\n : `/backend/staff/team-members${nextQuery ? `?${nextQuery}` : ''}`\n router.replace(nextPath)\n }\n }, [memberId, router, searchParams, t])\n\n const handleSubmit = React.useCallback(async (values: TeamMemberFormValues) => {\n if (!memberId) return\n const payload = buildTeamMemberPayload(values, { id: memberId })\n await updateCrud('staff/team-members', payload, {\n errorMessage: t('staff.teamMembers.form.errors.update', 'Failed to update team member.'),\n })\n flash(t('staff.teamMembers.form.flash.updated', 'Team member updated.'), 'success')\n router.push('/backend/staff/team-members')\n }, [memberId, router, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!memberId) return\n await deleteCrud('staff/team-members', memberId, {\n errorMessage: t('staff.teamMembers.form.errors.delete', 'Failed to delete team member.'),\n })\n flash(t('staff.teamMembers.form.flash.deleted', 'Team member deleted.'), 'success')\n router.push('/backend/staff/team-members')\n }, [memberId, router, t])\n\n const handleRulesetChange = React.useCallback(async (nextId: string | null) => {\n if (!memberId) return\n await updateCrud('staff/team-members', { id: memberId, availabilityRuleSetId: nextId }, {\n errorMessage: t('staff.teamMembers.availability.ruleset.updateError', 'Failed to update schedule.'),\n })\n setAvailabilityRuleSetId(nextId)\n flash(t('staff.teamMembers.availability.ruleset.updateSuccess', 'Schedule updated.'), 'success')\n }, [memberId, t])\n\n const panelTabs = React.useMemo(() => ([\n { id: 'details' as const, label: t('staff.teamMembers.detail.tabs.details', 'Details') },\n { id: 'availability' as const, label: t('staff.teamMembers.detail.tabs.availability', 'Availability') },\n { id: 'jobHistory' as const, label: t('staff.teamMembers.detail.tabs.jobHistory', 'Job history') },\n ]), [t])\n\n const tabs = React.useMemo(() => ([\n { id: 'notes' as const, label: t('staff.teamMembers.detail.tabs.notes', 'Notes') },\n { id: 'activities' as const, label: t('staff.teamMembers.detail.tabs.activities', 'Activities') },\n { id: 'addresses' as const, label: t('staff.teamMembers.detail.tabs.addresses', 'Addresses') },\n ]), [t])\n\n const resolvedInitialValues = initialValues ?? {\n roleIds: [],\n isActive: true,\n }\n\n const displayName = memberRecord?.displayName ?? memberRecord?.display_name ?? resolvedInitialValues.displayName ?? ''\n const teamLabel = memberRecord?.team?.name ?? t('staff.teamMembers.detail.team.unassigned', 'Unassigned team')\n const roleLabels = Array.isArray(memberRecord?.roleNames) && memberRecord?.roleNames.length\n ? memberRecord?.roleNames\n : [t('staff.teamMembers.detail.roles.unassigned', 'No roles assigned')]\n const userEmail = memberRecord?.user?.email ?? null\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <Link\n href=\"/backend/staff/team-members\"\n className=\"inline-flex items-center text-sm text-muted-foreground hover:text-foreground\"\n >\n <span aria-hidden className=\"mr-1 text-base\">\u2190</span>\n <span className=\"sr-only\">{t('staff.teamMembers.detail.back', 'Back to team members')}</span>\n </Link>\n <div className=\"space-y-1\">\n <h1 className=\"text-2xl font-semibold text-foreground\">\n {displayName || t('staff.teamMembers.detail.untitled', 'Unnamed team member')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.teamMembers.detail.subtitle', 'Team member profile and activity')}\n </p>\n </div>\n </div>\n <TranslationDrawerAction\n config={memberId ? {\n entityType: 'staff:staff_team_member',\n recordId: memberId,\n baseValues: memberRecord ?? undefined,\n } : null}\n />\n </div>\n\n <div className=\"border-b\">\n <nav\n className=\"flex flex-wrap items-center gap-5 text-sm\"\n aria-label={t('staff.teamMembers.detail.tabs.label', 'Team member sections')}\n >\n {panelTabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activePanel === tab.id}\n onClick={() => setActivePanel(tab.id)}\n className={`relative -mb-px border-b-2 px-0 py-2 text-sm font-medium transition-colors ${\n activePanel === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </nav>\n </div>\n\n {activePanel === 'details' ? (\n <>\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,2fr),minmax(0,1.1fr)]\">\n <div className=\"space-y-6\">\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.highlights', 'Highlights')}\n </h2>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.team', 'Team')}\n </p>\n <p className=\"text-base text-foreground\">{teamLabel}</p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.roles', 'Roles')}\n </p>\n <p className=\"text-base text-foreground\">{roleLabels.join(', ')}</p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.user', 'User')}\n </p>\n <p className=\"text-base text-foreground\">\n {userEmail ?? t('staff.teamMembers.detail.fields.userEmpty', 'No user linked')}\n </p>\n </div>\n <div>\n <p className=\"text-xs font-medium uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.fields.status', 'Status')}\n </p>\n <p className=\"text-base text-foreground\">\n {memberRecord?.isActive ?? memberRecord?.is_active\n ? t('staff.teamMembers.detail.status.active', 'Active')\n : t('staff.teamMembers.detail.status.inactive', 'Inactive')}\n </p>\n </div>\n </div>\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex gap-2\">\n {tabs.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n onClick={() => setActiveTab(tab.id)}\n className={`relative -mb-px border-b-2 px-0 py-1 text-sm font-medium transition-colors ${\n activeTab === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </div>\n {sectionAction ? (\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={sectionAction.disabled}\n onClick={() => sectionAction.onClick()}\n >\n {sectionAction.icon ?? (activeTab === 'addresses' ? <Plus className=\"mr-2 h-4 w-4\" /> : null)}\n {sectionAction.label}\n </Button>\n ) : null}\n </div>\n {activeTab === 'notes' ? (\n <NotesSection\n entityId={memberId ?? null}\n emptyLabel={t('staff.teamMembers.detail.notes.empty', 'No notes yet.')}\n viewerUserId={null}\n viewerName={null}\n viewerEmail={null}\n addActionLabel={t('staff.teamMembers.detail.notes.add', 'Add note')}\n emptyState={{\n title: t('staff.teamMembers.detail.notes.emptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('staff.teamMembers.detail.notes.emptyAction', 'Add a note'),\n }}\n onActionChange={setSectionAction}\n translator={detailTranslator}\n labelPrefix=\"staff.teamMembers.detail.notes\"\n inlineLabelPrefix=\"staff.teamMembers.detail.inline\"\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n />\n ) : null}\n {activeTab === 'activities' ? (\n <ActivitiesSection\n entityId={memberId ?? null}\n addActionLabel={t('staff.teamMembers.detail.activities.add', 'Log activity')}\n emptyState={{\n title: t('staff.teamMembers.detail.activities.emptyTitle', 'No activities yet'),\n actionLabel: t('staff.teamMembers.detail.activities.emptyAction', 'Add an activity'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={activitiesAdapter}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n resolveActivityPresentation={resolveActivityPresentation}\n renderCustomFields={renderCustomFields}\n labelPrefix=\"staff.teamMembers.detail.activities\"\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n appearanceLabels={appearanceLabels}\n manageHref={manageActivityHref}\n customFieldEntityIds={['staff:staff_team_member_activity']}\n />\n ) : null}\n {activeTab === 'addresses' ? (\n <SharedAddressesSection\n entityId={memberId ?? null}\n emptyLabel={t('staff.teamMembers.detail.addresses.empty', 'No addresses yet.')}\n addActionLabel={t('staff.teamMembers.detail.addresses.add', 'Add address')}\n emptyState={{\n title: t('staff.teamMembers.detail.addresses.emptyTitle', 'No addresses yet'),\n actionLabel: t('staff.teamMembers.detail.addresses.emptyAction', 'Add an address'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={addressesAdapter}\n addressTypesAdapter={addressTypesAdapter}\n labelPrefix=\"staff.teamMembers.detail.addresses\"\n />\n ) : null}\n </div>\n </div>\n <div className=\"space-y-4\">\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.details', 'Member details')}\n </h2>\n <div className=\"space-y-2\">\n {memberRecord?.description ? (\n <MarkdownContent body={memberRecord.description} format=\"markdown\" className={MARKDOWN_CLASSNAME} />\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.teamMembers.detail.descriptionEmpty', 'No description provided.')}\n </p>\n )}\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('staff.teamMembers.detail.formTitle', 'Member settings')}\n </h2>\n <TeamMemberForm\n embedded\n title={t('staff.teamMembers.form.editTitle', 'Edit team member')}\n backHref=\"/backend/staff/team-members\"\n cancelHref=\"/backend/staff/team-members\"\n initialValues={resolvedInitialValues}\n onSubmit={handleSubmit}\n onDelete={handleDelete}\n isLoading={!initialValues}\n loadingMessage={t('staff.teamMembers.form.loading', 'Loading team member...')}\n />\n </div>\n </>\n ) : activePanel === 'availability' ? (\n <AvailabilityRulesEditor\n subjectType=\"member\"\n subjectId={memberId ?? ''}\n labelPrefix=\"staff.teamMembers\"\n mode=\"availability\"\n rulesetId={availabilityRuleSetId}\n onRulesetChange={handleRulesetChange}\n buildScheduleItems={({ availabilityRules, translate: translateLabel }) => (\n buildMemberScheduleItems({ availabilityRules, translate: translateLabel })\n )}\n />\n ) : (\n <div className=\"rounded-lg border bg-card p-4\">\n <JobHistorySection memberId={memberId ?? null} />\n </div>\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n\nfunction normalizeStringList(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n return value\n .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))\n .filter((entry) => entry.length > 0)\n}\n\nfunction resolvePreferredArray<T>(primary?: T[] | null, fallback?: T[] | null): T[] | undefined {\n if (Array.isArray(primary) && primary.length) return primary\n if (Array.isArray(fallback) && fallback.length) return fallback\n return Array.isArray(primary) ? primary : Array.isArray(fallback) ? fallback : undefined\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["params"]
7
7
  }
@@ -0,0 +1,9 @@
1
+ const translatableFields = {
2
+ "staff:staff_team_member": ["display_name", "description"]
3
+ };
4
+ var translations_default = translatableFields;
5
+ export {
6
+ translations_default as default,
7
+ translatableFields
8
+ };
9
+ //# sourceMappingURL=translations.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/staff/translations.ts"],
4
+ "sourcesContent": ["export const translatableFields: Record<string, string[]> = {\n 'staff:staff_team_member': ['display_name', 'description'],\n}\n\nexport default translatableFields\n"],
5
+ "mappings": "AAAO,MAAM,qBAA+C;AAAA,EAC1D,2BAA2B,CAAC,gBAAgB,aAAa;AAC3D;AAEA,IAAO,uBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,97 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Languages, X } from "lucide-react";
5
+ import { Button } from "@open-mercato/ui/primitives/button";
6
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
7
+ import { TranslationManager } from "./TranslationManager.js";
8
+ function TranslationDrawerAction({ config }) {
9
+ const t = useT();
10
+ const [open, setOpen] = React.useState(false);
11
+ const canRender = Boolean(
12
+ config?.entityType && config?.recordId && String(config.recordId).trim().length > 0
13
+ );
14
+ React.useEffect(() => {
15
+ if (!open || !canRender) return;
16
+ const prev = document.body.style.overflow;
17
+ document.body.style.overflow = "hidden";
18
+ return () => {
19
+ document.body.style.overflow = prev;
20
+ };
21
+ }, [canRender, open]);
22
+ React.useEffect(() => {
23
+ if (!open || !canRender) return;
24
+ const handleKeyDown = (event) => {
25
+ if (event.key === "Escape") {
26
+ setOpen(false);
27
+ }
28
+ };
29
+ document.addEventListener("keydown", handleKeyDown);
30
+ return () => document.removeEventListener("keydown", handleKeyDown);
31
+ }, [canRender, open]);
32
+ if (!canRender) return null;
33
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
34
+ /* @__PURE__ */ jsx(
35
+ Button,
36
+ {
37
+ type: "button",
38
+ variant: "ghost",
39
+ size: "icon",
40
+ onClick: () => setOpen(true),
41
+ "aria-label": t("translations.widgets.translationManager.fullManager", "Translation manager"),
42
+ title: t("translations.widgets.translationManager.fullManager", "Translation manager"),
43
+ children: /* @__PURE__ */ jsx(Languages, { className: "size-4" })
44
+ }
45
+ ),
46
+ open ? /* @__PURE__ */ jsxs(Fragment, { children: [
47
+ /* @__PURE__ */ jsx(
48
+ "div",
49
+ {
50
+ className: "fixed inset-0 z-40 bg-black/20",
51
+ onClick: () => setOpen(false),
52
+ "aria-hidden": "true"
53
+ }
54
+ ),
55
+ /* @__PURE__ */ jsx(
56
+ "div",
57
+ {
58
+ className: "fixed right-0 top-0 z-50 h-full w-full max-w-4xl border-l bg-background shadow-lg",
59
+ role: "dialog",
60
+ "aria-modal": "true",
61
+ "aria-label": t("translations.widgets.translationManager.groupLabel", "Translations"),
62
+ children: /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col", children: [
63
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 border-b px-4 py-3", children: [
64
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
65
+ /* @__PURE__ */ jsx("h2", { className: "font-semibold", children: t("translations.widgets.translationManager.groupLabel", "Translations") }),
66
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("translations.widgets.translationManager.groupDescription", "Manage translations for this record across supported locales.") })
67
+ ] }),
68
+ /* @__PURE__ */ jsx(
69
+ Button,
70
+ {
71
+ variant: "ghost",
72
+ size: "icon",
73
+ onClick: () => setOpen(false),
74
+ "aria-label": t("ui.dialog.close.ariaLabel", "Close"),
75
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
76
+ }
77
+ )
78
+ ] }),
79
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: /* @__PURE__ */ jsx(
80
+ TranslationManager,
81
+ {
82
+ mode: "embedded",
83
+ entityType: config.entityType,
84
+ recordId: config.recordId,
85
+ baseValues: config.baseValues
86
+ }
87
+ ) })
88
+ ] })
89
+ }
90
+ )
91
+ ] }) : null
92
+ ] });
93
+ }
94
+ export {
95
+ TranslationDrawerAction
96
+ };
97
+ //# sourceMappingURL=TranslationDrawerAction.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/translations/components/TranslationDrawerAction.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Languages, X } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { TranslationManager } from './TranslationManager'\n\nexport type TranslationDrawerActionConfig = {\n entityType: string\n recordId: string\n baseValues?: Record<string, unknown>\n}\n\nexport type TranslationDrawerActionProps = {\n config: TranslationDrawerActionConfig | null\n}\n\nexport function TranslationDrawerAction({ config }: TranslationDrawerActionProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n\n const canRender = Boolean(\n config?.entityType && config?.recordId && String(config.recordId).trim().length > 0,\n )\n\n React.useEffect(() => {\n if (!open || !canRender) return\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.body.style.overflow = prev\n }\n }, [canRender, open])\n\n React.useEffect(() => {\n if (!open || !canRender) return\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setOpen(false)\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [canRender, open])\n\n if (!canRender) return null\n\n return (\n <>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setOpen(true)}\n aria-label={t('translations.widgets.translationManager.fullManager', 'Translation manager')}\n title={t('translations.widgets.translationManager.fullManager', 'Translation manager')}\n >\n <Languages className=\"size-4\" />\n </Button>\n {open ? (\n <>\n <div\n className=\"fixed inset-0 z-40 bg-black/20\"\n onClick={() => setOpen(false)}\n aria-hidden=\"true\"\n />\n <div\n className=\"fixed right-0 top-0 z-50 h-full w-full max-w-4xl border-l bg-background shadow-lg\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={t('translations.widgets.translationManager.groupLabel', 'Translations')}\n >\n <div className=\"flex h-full flex-col\">\n <div className=\"flex items-start justify-between gap-3 border-b px-4 py-3\">\n <div className=\"space-y-1\">\n <h2 className=\"font-semibold\">\n {t('translations.widgets.translationManager.groupLabel', 'Translations')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('translations.widgets.translationManager.groupDescription', 'Manage translations for this record across supported locales.')}\n </p>\n </div>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setOpen(false)}\n aria-label={t('ui.dialog.close.ariaLabel', 'Close')}\n >\n <X className=\"size-4\" />\n </Button>\n </div>\n <div className=\"flex-1 overflow-y-auto px-4 py-4\">\n <TranslationManager\n mode=\"embedded\"\n entityType={config!.entityType}\n recordId={config!.recordId}\n baseValues={config!.baseValues}\n />\n </div>\n </div>\n </div>\n </>\n ) : null}\n </>\n )\n}\n"],
5
+ "mappings": ";AA0DQ,SAGA,UAHA,KAiBQ,YAjBR;AAxDR,YAAY,WAAW;AACvB,SAAS,WAAW,SAAS;AAC7B,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,SAAS,0BAA0B;AAY5B,SAAS,wBAAwB,EAAE,OAAO,GAAiC;AAChF,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAE5C,QAAM,YAAY;AAAA,IAChB,QAAQ,cAAc,QAAQ,YAAY,OAAO,OAAO,QAAQ,EAAE,KAAK,EAAE,SAAS;AAAA,EACpF;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAQ,CAAC,UAAW;AACzB,UAAM,OAAO,SAAS,KAAK,MAAM;AACjC,aAAS,KAAK,MAAM,WAAW;AAC/B,WAAO,MAAM;AACX,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,WAAW,IAAI,CAAC;AAEpB,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAQ,CAAC,UAAW;AACzB,UAAM,gBAAgB,CAAC,UAAyB;AAC9C,UAAI,MAAM,QAAQ,UAAU;AAC1B,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AACA,aAAS,iBAAiB,WAAW,aAAa;AAClD,WAAO,MAAM,SAAS,oBAAoB,WAAW,aAAa;AAAA,EACpE,GAAG,CAAC,WAAW,IAAI,CAAC;AAEpB,MAAI,CAAC,UAAW,QAAO;AAEvB,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,SAAS,MAAM,QAAQ,IAAI;AAAA,QAC3B,cAAY,EAAE,uDAAuD,qBAAqB;AAAA,QAC1F,OAAO,EAAE,uDAAuD,qBAAqB;AAAA,QAErF,8BAAC,aAAU,WAAU,UAAS;AAAA;AAAA,IAChC;AAAA,IACC,OACC,iCACE;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,SAAS,MAAM,QAAQ,KAAK;AAAA,UAC5B,eAAY;AAAA;AAAA,MACd;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,MAAK;AAAA,UACL,cAAW;AAAA,UACX,cAAY,EAAE,sDAAsD,cAAc;AAAA,UAElF,+BAAC,SAAI,WAAU,wBACb;AAAA,iCAAC,SAAI,WAAU,6DACb;AAAA,mCAAC,SAAI,WAAU,aACb;AAAA,oCAAC,QAAG,WAAU,iBACX,YAAE,sDAAsD,cAAc,GACzE;AAAA,gBACA,oBAAC,OAAE,WAAU,iCACV,YAAE,4DAA4D,+DAA+D,GAChI;AAAA,iBACF;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS,MAAM,QAAQ,KAAK;AAAA,kBAC5B,cAAY,EAAE,6BAA6B,OAAO;AAAA,kBAElD,8BAAC,KAAE,WAAU,UAAS;AAAA;AAAA,cACxB;AAAA,eACF;AAAA,YACA,oBAAC,SAAI,WAAU,oCACb;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,YAAY,OAAQ;AAAA,gBACpB,UAAU,OAAQ;AAAA,gBAClB,YAAY,OAAQ;AAAA;AAAA,YACtB,GACF;AAAA,aACF;AAAA;AAAA,MACF;AAAA,OACF,IACE;AAAA,KACN;AAEJ;",
6
+ "names": []
7
+ }
@@ -1,9 +1,38 @@
1
+ const UUID_LIKE_PATTERN = /^[0-9a-f-]{20,}$/i;
2
+ function readParam(params, key) {
3
+ const value = params[key];
4
+ if (!value) {
5
+ return void 0;
6
+ }
7
+ if (Array.isArray(value)) {
8
+ return value.find((entry) => typeof entry === "string" && entry.length > 0);
9
+ }
10
+ return value;
11
+ }
1
12
  function extractRecordId(params) {
2
- if (params.id) return String(Array.isArray(params.id) ? params.id[0] : params.id);
13
+ const directRecordId = readParam(params, "recordId");
14
+ if (directRecordId) {
15
+ return directRecordId;
16
+ }
17
+ const directId = readParam(params, "id");
18
+ if (directId) {
19
+ return directId;
20
+ }
21
+ const orderedIdCandidates = Object.entries(params).filter(
22
+ ([key]) => key !== "recordId" && key !== "id" && key.toLowerCase().endsWith("id")
23
+ ).reverse();
24
+ for (const [, value] of orderedIdCandidates) {
25
+ const segments = Array.isArray(value) ? value : [value];
26
+ for (const seg of segments) {
27
+ if (seg && UUID_LIKE_PATTERN.test(seg)) {
28
+ return seg;
29
+ }
30
+ }
31
+ }
3
32
  for (const [, value] of Object.entries(params)) {
4
33
  const segments = Array.isArray(value) ? value : [value];
5
34
  for (const seg of segments) {
6
- if (seg && /^[0-9a-f-]{20,}$/i.test(seg)) return seg;
35
+ if (seg && UUID_LIKE_PATTERN.test(seg)) return seg;
7
36
  }
8
37
  }
9
38
  return void 0;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/translations/lib/extract-record-id.ts"],
4
- "sourcesContent": ["export function extractRecordId(params: Record<string, string | string[]>): string | undefined {\n if (params.id) return String(Array.isArray(params.id) ? params.id[0] : params.id)\n for (const [, value] of Object.entries(params)) {\n const segments = Array.isArray(value) ? value : [value]\n for (const seg of segments) {\n if (seg && /^[0-9a-f-]{20,}$/i.test(seg)) return seg\n }\n }\n return undefined\n}\n"],
5
- "mappings": "AAAO,SAAS,gBAAgB,QAA+D;AAC7F,MAAI,OAAO,GAAI,QAAO,OAAO,MAAM,QAAQ,OAAO,EAAE,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,EAAE;AAChF,aAAW,CAAC,EAAE,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC9C,UAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AACtD,eAAW,OAAO,UAAU;AAC1B,UAAI,OAAO,oBAAoB,KAAK,GAAG,EAAG,QAAO;AAAA,IACnD;AAAA,EACF;AACA,SAAO;AACT;",
4
+ "sourcesContent": ["const UUID_LIKE_PATTERN = /^[0-9a-f-]{20,}$/i\n\ntype Params = Record<string, string | string[]>\n\nfunction readParam(params: Params, key: string) {\n const value = params[key]\n\n if (!value) {\n return undefined\n }\n\n if (Array.isArray(value)) {\n return value.find((entry) => typeof entry === 'string' && entry.length > 0)\n }\n\n return value\n}\n\nexport function extractRecordId(params: Params) {\n const directRecordId = readParam(params, 'recordId')\n if (directRecordId) {\n return directRecordId\n }\n\n const directId = readParam(params, 'id')\n if (directId) {\n return directId\n }\n\n const orderedIdCandidates = Object.entries(params)\n .filter(\n ([key]) =>\n key !== 'recordId' && key !== 'id' && key.toLowerCase().endsWith('id'),\n )\n .reverse()\n\n for (const [, value] of orderedIdCandidates) {\n const segments = Array.isArray(value) ? value : [value]\n for (const seg of segments) {\n if (seg && UUID_LIKE_PATTERN.test(seg)) {\n return seg\n }\n }\n }\n\n for (const [, value] of Object.entries(params)) {\n const segments = Array.isArray(value) ? value : [value]\n for (const seg of segments) {\n if (seg && UUID_LIKE_PATTERN.test(seg)) return seg\n }\n }\n\n return undefined\n}\n"],
5
+ "mappings": "AAAA,MAAM,oBAAoB;AAI1B,SAAS,UAAU,QAAgB,KAAa;AAC9C,QAAM,QAAQ,OAAO,GAAG;AAExB,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,KAAK,CAAC,UAAU,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC;AAAA,EAC5E;AAEA,SAAO;AACT;AAEO,SAAS,gBAAgB,QAAgB;AAC9C,QAAM,iBAAiB,UAAU,QAAQ,UAAU;AACnD,MAAI,gBAAgB;AAClB,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,UAAU,QAAQ,IAAI;AACvC,MAAI,UAAU;AACZ,WAAO;AAAA,EACT;AAEA,QAAM,sBAAsB,OAAO,QAAQ,MAAM,EAC9C;AAAA,IACC,CAAC,CAAC,GAAG,MACH,QAAQ,cAAc,QAAQ,QAAQ,IAAI,YAAY,EAAE,SAAS,IAAI;AAAA,EACzE,EACC,QAAQ;AAEX,aAAW,CAAC,EAAE,KAAK,KAAK,qBAAqB;AAC3C,UAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AACtD,eAAW,OAAO,UAAU;AAC1B,UAAI,OAAO,kBAAkB,KAAK,GAAG,GAAG;AACtC,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,aAAW,CAAC,EAAE,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC9C,UAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AACtD,eAAW,OAAO,UAAU;AAC1B,UAAI,OAAO,kBAAkB,KAAK,GAAG,EAAG,QAAO;AAAA,IACjD;AAAA,EACF;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -15,7 +15,9 @@ function resolveFieldList(entityType, explicitFields, customFieldDefs) {
15
15
  }
16
16
  const registered = getTranslatableFields(entityType);
17
17
  const fields = [];
18
+ let hasExplicitList = false;
18
19
  if (registered) {
20
+ hasExplicitList = true;
19
21
  for (const key of registered) {
20
22
  fields.push({
21
23
  key,
@@ -43,6 +45,7 @@ function resolveFieldList(entityType, explicitFields, customFieldDefs) {
43
45
  }
44
46
  }
45
47
  }
48
+ if (hasExplicitList) return fields;
46
49
  for (const def of customFieldDefs) {
47
50
  const key = typeof def.key === "string" ? def.key.trim() : "";
48
51
  if (!key) continue;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/translations/lib/resolve-field-list.ts"],
4
- "sourcesContent": ["import { getTranslatableFields } from '@open-mercato/shared/lib/localization/translatable-fields'\nimport { getEntityFields } from '#generated/entity-fields-registry'\nimport { isTranslatableField } from './translatable-fields'\nimport { formatFieldLabel } from './helpers'\n\nexport type ResolvedField = { key: string; label: string; multiline: boolean }\n\nfunction isMultiline(key: string): boolean {\n return key === 'description' || key.includes('description') || key.includes('content')\n}\n\nexport function resolveFieldList(\n entityType: string,\n explicitFields: string[] | undefined,\n customFieldDefs: Array<{ key: string; kind: string; label?: string }>,\n): ResolvedField[] {\n if (explicitFields?.length) {\n return explicitFields.map((key) => ({\n key,\n label: formatFieldLabel(key),\n multiline: isMultiline(key),\n }))\n }\n\n const registered = getTranslatableFields(entityType)\n const fields: ResolvedField[] = []\n\n if (registered) {\n for (const key of registered) {\n fields.push({\n key,\n label: formatFieldLabel(key),\n multiline: isMultiline(key),\n })\n }\n } else {\n const parts = entityType.split(':')\n const entitySlug = parts[1]\n if (entitySlug) {\n const mod = getEntityFields(entitySlug)\n if (mod) {\n for (const raw of Object.values(mod)) {\n if (typeof raw !== 'string' || !raw.trim()) continue\n const value = raw.trim()\n if (isTranslatableField(value) && !fields.some((f) => f.key === value)) {\n fields.push({\n key: value,\n label: formatFieldLabel(value),\n multiline: isMultiline(value),\n })\n }\n }\n }\n }\n }\n\n for (const def of customFieldDefs) {\n const key = typeof def.key === 'string' ? def.key.trim() : ''\n if (!key) continue\n if (def.kind !== 'text' && def.kind !== 'multiline' && def.kind !== 'richtext') continue\n if (fields.some((f) => f.key === key)) continue\n const label = typeof def.label === 'string' && def.label.trim().length ? def.label : formatFieldLabel(key)\n fields.push({ key, label, multiline: def.kind === 'multiline' || def.kind === 'richtext' })\n }\n\n return fields\n}\n"],
5
- "mappings": "AAAA,SAAS,6BAA6B;AACtC,SAAS,uBAAuB;AAChC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAIjC,SAAS,YAAY,KAAsB;AACzC,SAAO,QAAQ,iBAAiB,IAAI,SAAS,aAAa,KAAK,IAAI,SAAS,SAAS;AACvF;AAEO,SAAS,iBACd,YACA,gBACA,iBACiB;AACjB,MAAI,gBAAgB,QAAQ;AAC1B,WAAO,eAAe,IAAI,CAAC,SAAS;AAAA,MAClC;AAAA,MACA,OAAO,iBAAiB,GAAG;AAAA,MAC3B,WAAW,YAAY,GAAG;AAAA,IAC5B,EAAE;AAAA,EACJ;AAEA,QAAM,aAAa,sBAAsB,UAAU;AACnD,QAAM,SAA0B,CAAC;AAEjC,MAAI,YAAY;AACd,eAAW,OAAO,YAAY;AAC5B,aAAO,KAAK;AAAA,QACV;AAAA,QACA,OAAO,iBAAiB,GAAG;AAAA,QAC3B,WAAW,YAAY,GAAG;AAAA,MAC5B,CAAC;AAAA,IACH;AAAA,EACF,OAAO;AACL,UAAM,QAAQ,WAAW,MAAM,GAAG;AAClC,UAAM,aAAa,MAAM,CAAC;AAC1B,QAAI,YAAY;AACd,YAAM,MAAM,gBAAgB,UAAU;AACtC,UAAI,KAAK;AACP,mBAAW,OAAO,OAAO,OAAO,GAAG,GAAG;AACpC,cAAI,OAAO,QAAQ,YAAY,CAAC,IAAI,KAAK,EAAG;AAC5C,gBAAM,QAAQ,IAAI,KAAK;AACvB,cAAI,oBAAoB,KAAK,KAAK,CAAC,OAAO,KAAK,CAAC,MAAM,EAAE,QAAQ,KAAK,GAAG;AACtE,mBAAO,KAAK;AAAA,cACV,KAAK;AAAA,cACL,OAAO,iBAAiB,KAAK;AAAA,cAC7B,WAAW,YAAY,KAAK;AAAA,YAC9B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,aAAW,OAAO,iBAAiB;AACjC,UAAM,MAAM,OAAO,IAAI,QAAQ,WAAW,IAAI,IAAI,KAAK,IAAI;AAC3D,QAAI,CAAC,IAAK;AACV,QAAI,IAAI,SAAS,UAAU,IAAI,SAAS,eAAe,IAAI,SAAS,WAAY;AAChF,QAAI,OAAO,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,EAAG;AACvC,UAAM,QAAQ,OAAO,IAAI,UAAU,YAAY,IAAI,MAAM,KAAK,EAAE,SAAS,IAAI,QAAQ,iBAAiB,GAAG;AACzG,WAAO,KAAK,EAAE,KAAK,OAAO,WAAW,IAAI,SAAS,eAAe,IAAI,SAAS,WAAW,CAAC;AAAA,EAC5F;AAEA,SAAO;AACT;",
4
+ "sourcesContent": ["import { getTranslatableFields } from '@open-mercato/shared/lib/localization/translatable-fields'\nimport { getEntityFields } from '#generated/entity-fields-registry'\nimport { isTranslatableField } from './translatable-fields'\nimport { formatFieldLabel } from './helpers'\n\nexport type ResolvedField = { key: string; label: string; multiline: boolean }\n\nfunction isMultiline(key: string): boolean {\n return key === 'description' || key.includes('description') || key.includes('content')\n}\n\nexport function resolveFieldList(\n entityType: string,\n explicitFields: string[] | undefined,\n customFieldDefs: Array<{ key: string; kind: string; label?: string }>,\n): ResolvedField[] {\n if (explicitFields?.length) {\n return explicitFields.map((key) => ({\n key,\n label: formatFieldLabel(key),\n multiline: isMultiline(key),\n }))\n }\n\n const registered = getTranslatableFields(entityType)\n const fields: ResolvedField[] = []\n let hasExplicitList = false\n\n if (registered) {\n hasExplicitList = true\n for (const key of registered) {\n fields.push({\n key,\n label: formatFieldLabel(key),\n multiline: isMultiline(key),\n })\n }\n } else {\n const parts = entityType.split(':')\n const entitySlug = parts[1]\n if (entitySlug) {\n const mod = getEntityFields(entitySlug)\n if (mod) {\n for (const raw of Object.values(mod)) {\n if (typeof raw !== 'string' || !raw.trim()) continue\n const value = raw.trim()\n if (isTranslatableField(value) && !fields.some((f) => f.key === value)) {\n fields.push({\n key: value,\n label: formatFieldLabel(value),\n multiline: isMultiline(value),\n })\n }\n }\n }\n }\n }\n\n if (hasExplicitList) return fields\n\n for (const def of customFieldDefs) {\n const key = typeof def.key === 'string' ? def.key.trim() : ''\n if (!key) continue\n if (def.kind !== 'text' && def.kind !== 'multiline' && def.kind !== 'richtext') continue\n if (fields.some((f) => f.key === key)) continue\n const label = typeof def.label === 'string' && def.label.trim().length ? def.label : formatFieldLabel(key)\n fields.push({ key, label, multiline: def.kind === 'multiline' || def.kind === 'richtext' })\n }\n\n return fields\n}\n"],
5
+ "mappings": "AAAA,SAAS,6BAA6B;AACtC,SAAS,uBAAuB;AAChC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAIjC,SAAS,YAAY,KAAsB;AACzC,SAAO,QAAQ,iBAAiB,IAAI,SAAS,aAAa,KAAK,IAAI,SAAS,SAAS;AACvF;AAEO,SAAS,iBACd,YACA,gBACA,iBACiB;AACjB,MAAI,gBAAgB,QAAQ;AAC1B,WAAO,eAAe,IAAI,CAAC,SAAS;AAAA,MAClC;AAAA,MACA,OAAO,iBAAiB,GAAG;AAAA,MAC3B,WAAW,YAAY,GAAG;AAAA,IAC5B,EAAE;AAAA,EACJ;AAEA,QAAM,aAAa,sBAAsB,UAAU;AACnD,QAAM,SAA0B,CAAC;AACjC,MAAI,kBAAkB;AAEtB,MAAI,YAAY;AACd,sBAAkB;AAClB,eAAW,OAAO,YAAY;AAC5B,aAAO,KAAK;AAAA,QACV;AAAA,QACA,OAAO,iBAAiB,GAAG;AAAA,QAC3B,WAAW,YAAY,GAAG;AAAA,MAC5B,CAAC;AAAA,IACH;AAAA,EACF,OAAO;AACL,UAAM,QAAQ,WAAW,MAAM,GAAG;AAClC,UAAM,aAAa,MAAM,CAAC;AAC1B,QAAI,YAAY;AACd,YAAM,MAAM,gBAAgB,UAAU;AACtC,UAAI,KAAK;AACP,mBAAW,OAAO,OAAO,OAAO,GAAG,GAAG;AACpC,cAAI,OAAO,QAAQ,YAAY,CAAC,IAAI,KAAK,EAAG;AAC5C,gBAAM,QAAQ,IAAI,KAAK;AACvB,cAAI,oBAAoB,KAAK,KAAK,CAAC,OAAO,KAAK,CAAC,MAAM,EAAE,QAAQ,KAAK,GAAG;AACtE,mBAAO,KAAK;AAAA,cACV,KAAK;AAAA,cACL,OAAO,iBAAiB,KAAK;AAAA,cAC7B,WAAW,YAAY,KAAK;AAAA,YAC9B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,gBAAiB,QAAO;AAE5B,aAAW,OAAO,iBAAiB;AACjC,UAAM,MAAM,OAAO,IAAI,QAAQ,WAAW,IAAI,IAAI,KAAK,IAAI;AAC3D,QAAI,CAAC,IAAK;AACV,QAAI,IAAI,SAAS,UAAU,IAAI,SAAS,eAAe,IAAI,SAAS,WAAY;AAChF,QAAI,OAAO,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,EAAG;AACvC,UAAM,QAAQ,OAAO,IAAI,UAAU,YAAY,IAAI,MAAM,KAAK,EAAE,SAAS,IAAI,QAAQ,iBAAiB,GAAG;AACzG,WAAO,KAAK,EAAE,KAAK,OAAO,WAAW,IAAI,SAAS,eAAe,IAAI,SAAS,WAAW,CAAC;AAAA,EAC5F;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }