@open-mercato/core 0.5.1-develop.2652.0276e72e45 → 0.5.1-develop.2657.a01847a9fa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +26 -0
- package/dist/modules/auth/lib/backendChrome.js +3 -1
- package/dist/modules/auth/lib/backendChrome.js.map +2 -2
- package/dist/modules/auth/services/rbacService.js +8 -2
- package/dist/modules/auth/services/rbacService.js.map +2 -2
- package/dist/modules/customer_accounts/api/password/reset-confirm.js +7 -0
- package/dist/modules/customer_accounts/api/password/reset-confirm.js.map +2 -2
- package/dist/modules/customer_accounts/api/portal/nav.js +77 -0
- package/dist/modules/customer_accounts/api/portal/nav.js.map +7 -0
- package/dist/modules/customer_accounts/api/signup.js +20 -8
- package/dist/modules/customer_accounts/api/signup.js.map +2 -2
- package/dist/modules/customer_accounts/services/customerSessionService.js +32 -0
- package/dist/modules/customer_accounts/services/customerSessionService.js.map +2 -2
- package/dist/modules/directory/api/organizations/route.js +10 -0
- package/dist/modules/directory/api/organizations/route.js.map +3 -3
- package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +13 -2
- package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/create/page.js +12 -2
- package/dist/modules/directory/backend/directory/organizations/create/page.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js +4 -3
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.meta.js +17 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.meta.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.meta.js +11 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.meta.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/page.meta.js +11 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/page.meta.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.meta.js +17 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.meta.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.meta.js +11 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.meta.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/verify/page.meta.js +11 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/verify/page.meta.js.map +7 -0
- package/dist/modules/workflows/lib/activity-executor.js +25 -16
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/package.json +3 -3
- package/src/modules/auth/lib/backendChrome.tsx +3 -1
- package/src/modules/auth/services/rbacService.ts +8 -2
- package/src/modules/customer_accounts/api/password/reset-confirm.ts +9 -0
- package/src/modules/customer_accounts/api/portal/nav.ts +87 -0
- package/src/modules/customer_accounts/api/signup.ts +23 -7
- package/src/modules/customer_accounts/services/customerSessionService.ts +39 -0
- package/src/modules/directory/api/organizations/route.ts +11 -0
- package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +17 -3
- package/src/modules/directory/backend/directory/organizations/create/page.tsx +15 -3
- package/src/modules/directory/i18n/de.json +2 -0
- package/src/modules/directory/i18n/en.json +2 -0
- package/src/modules/directory/i18n/es.json +2 -0
- package/src/modules/directory/i18n/pl.json +2 -0
- package/src/modules/messages/components/message-detail/hooks/useMessageDetails.ts +4 -3
- package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.meta.ts +15 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/login/page.meta.ts +9 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/page.meta.ts +9 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/profile/page.meta.ts +15 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/signup/page.meta.ts +9 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/verify/page.meta.ts +9 -0
- package/src/modules/workflows/lib/activity-executor.ts +52 -24
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/directory/backend/directory/organizations/create/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { E } from '#generated/entities.ids.generated'\nimport { OrganizationSelect } from '@open-mercato/core/modules/directory/components/OrganizationSelect'\nimport { TenantSelect } from '@open-mercato/core/modules/directory/components/TenantSelect'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { type OrganizationTreeNode } from '@open-mercato/core/modules/directory/lib/tree'\n\ntype TreeResponse = {\n items: OrganizationTreeNode[]\n}\n\ntype ChildTreeSelectProps = {\n nodes: OrganizationTreeNode[]\n value: string[]\n onChange: (vals: string[]) => void\n}\n\nfunction ChildTreeSelect({ nodes, value, onChange }: ChildTreeSelectProps) {\n const t = useT()\n const selected = React.useMemo(() => new Set(value), [value])\n const handleToggle = React.useCallback((id: string) => {\n const next = new Set(value)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n onChange(Array.from(next))\n }, [value, onChange])\n\n if (!nodes.length) {\n return (\n <div className=\"text-sm text-muted-foreground\">\n {t('directory.organizations.form.children.empty', 'No organizations available to assign.')}\n </div>\n )\n }\n\n return (\n <div className=\"rounded border px-3 py-2 max-h-64 overflow-auto space-y-2\">\n <TreeCheckboxGroup nodes={nodes} selected={selected} onToggle={handleToggle} level={0} />\n </div>\n )\n}\n\nfunction TreeCheckboxGroup({ nodes, selected, onToggle, level }: { nodes: OrganizationTreeNode[]; selected: Set<string>; onToggle: (id: string) => void; level: number }) {\n return (\n <div className={level === 0 ? 'space-y-1' : 'space-y-1 pl-5'}>\n {nodes.map((node) => (\n <div key={node.id} className=\"space-y-1\">\n <label className=\"inline-flex items-start gap-2 text-sm\">\n <input\n type=\"checkbox\"\n className=\"size-4 mt-0.5\"\n checked={selected.has(node.id)}\n onChange={() => onToggle(node.id)}\n />\n <span>{node.name}</span>\n </label>\n {node.children?.length ? (\n <TreeCheckboxGroup nodes={node.children} selected={selected} onToggle={onToggle} level={level + 1} />\n ) : null}\n </div>\n ))}\n </div>\n )\n}\n\nexport default function CreateOrganizationPage() {\n const [tree, setTree] = React.useState<OrganizationTreeNode[]>([])\n const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)\n const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)\n const t = useT()\n\n const loadTree = React.useCallback(async (tenantId: string | null) => {\n const params = new URLSearchParams({ view: 'tree', includeInactive: 'true' })\n if (tenantId) params.set('tenantId', tenantId)\n try {\n const call = await apiCall<TreeResponse>(`/api/directory/organizations?${params.toString()}`)\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n setTree(items)\n } catch {\n setTree([])\n }\n }, [])\n\n React.useEffect(() => {\n let cancelled = false\n async function bootstrap() {\n try {\n const call = await apiCall<{ isSuperAdmin?: boolean }>('/api/auth/roles?page=1&pageSize=1')\n if (!cancelled) setActorIsSuperAdmin(Boolean(call.result?.isSuperAdmin))\n } catch {\n if (!cancelled) setActorIsSuperAdmin(false)\n }\n if (!cancelled) await loadTree(null)\n }\n bootstrap()\n return () => { cancelled = true }\n }, [loadTree])\n\n React.useEffect(() => {\n if (!actorIsSuperAdmin) return\n void loadTree(selectedTenantId)\n }, [actorIsSuperAdmin, loadTree, selectedTenantId])\n\n const fields = React.useMemo<CrudField[]>(() => [\n ...(actorIsSuperAdmin ? [\n {\n id: 'tenantId',\n label: t('directory.organizations.form.field.tenant', 'Tenant'),\n type: 'custom',\n required: true,\n component: ({ value, setValue }) => (\n <TenantSelect\n id=\"tenantId\"\n value={typeof value === 'string' ? value : selectedTenantId}\n onChange={(next) => {\n const normalized = next ?? null\n setSelectedTenantId(normalized)\n setValue(normalized)\n }}\n includeEmptyOption\n emptyOptionLabel={t('directory.organizations.form.tenant.select', 'Select tenant')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n />\n ),\n } as CrudField,\n ] : []),\n { id: 'name', label: t('directory.organizations.form.field.name', 'Name'), type: 'text', required: true },\n {\n id: 'parentId',\n label: t('directory.organizations.form.field.parent', 'Parent'),\n type: 'custom',\n component: ({ id, value, setValue }) => (\n <OrganizationSelect\n id={id}\n value={value ? String(value) : null}\n onChange={(next) => setValue(next ?? '')}\n tenantId={selectedTenantId}\n fetchOnMount={true}\n includeEmptyOption\n emptyOptionLabel={t('directory.organizations.form.rootOption', '\u2014 Root level \u2014')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n />\n ),\n },\n {\n id: 'childIds',\n label: t('directory.organizations.form.field.children', 'Children'),\n type: 'custom',\n component: ({ value, setValue }) => (\n <ChildTreeSelect\n nodes={tree}\n value={Array.isArray(value) ? value : []}\n onChange={(vals) => setValue(vals)}\n />\n ),\n },\n { id: 'isActive', label: t('directory.organizations.form.field.isActive', 'Active'), type: 'checkbox' },\n ], [actorIsSuperAdmin, selectedTenantId, t, tree])\n\n const detailFields = React.useMemo(() => (\n actorIsSuperAdmin\n ? ['tenantId', 'name', 'parentId', 'childIds', 'isActive']\n : ['name', 'parentId', 'childIds', 'isActive']\n ), [actorIsSuperAdmin])\n\n const groups: CrudFormGroup[] = React.useMemo(() => ([\n { id: 'details', title: t('directory.organizations.form.group.details', 'Details'), column: 1, fields: detailFields },\n { id: 'custom', title: t('directory.organizations.form.group.customFields', 'Custom Data'), column: 2, kind: 'customFields' },\n ]), [detailFields, t])\n const formTitle = t('directory.nav.organizations.create', 'Create Organization')\n const successMessage = encodeURIComponent(t('directory.organizations.flash.created', 'Organization created'))\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n title={formTitle}\n backHref=\"/backend/directory/organizations\"\n fields={fields}\n groups={groups}\n entityId={E.directory.organization}\n initialValues={{ tenantId: selectedTenantId ?? null, name: '', parentId: '', childIds: [], isActive: true }}\n submitLabel={t('directory.organizations.form.action.create', 'Create')}\n cancelHref=\"/backend/directory/organizations\"\n successRedirect={`/backend/directory/organizations?flash=${successMessage}&type=success`}\n onSubmit={async (values) => {\n await submitCreateOrganization({\n values: values as Record<string, unknown>,\n actorIsSuperAdmin,\n selectedTenantId,\n messages: {\n tenantRequired: t('directory.organizations.errors.tenantRequired', 'Tenant selection is required for super administrators'),\n },\n })\n }}\n />\n </PageBody>\n </Page>\n )\n}\n\ntype CreateOrganizationPayload = {\n name: string\n isActive: boolean\n parentId: string | null\n childIds: string[]\n tenantId?: string\n customFields?: Record<string, unknown>\n}\n\ntype CreateOrganizationRequest = (payload: CreateOrganizationPayload) => Promise<void>\n\nasync function defaultCreateOrganizationRequest(payload: CreateOrganizationPayload) {\n await createCrud('directory/organizations', payload)\n}\n\nexport async function submitCreateOrganization(options: {\n values: Record<string, unknown>\n actorIsSuperAdmin: boolean\n selectedTenantId: string | null\n createOrganization?: CreateOrganizationRequest\n messages?: {\n tenantRequired?: string\n }\n}): Promise<void> {\n const {\n values,\n actorIsSuperAdmin,\n selectedTenantId,\n createOrganization = defaultCreateOrganizationRequest,\n messages,\n } = options\n\n const customFields = collectCustomFieldValues(values)\n\n const tenantValue =\n typeof values.tenantId === 'string' && values.tenantId.trim().length\n ? values.tenantId.trim()\n : selectedTenantId\n\n if (actorIsSuperAdmin && !tenantValue) {\n const message = messages?.tenantRequired ?? 'Tenant selection is required for super administrators'\n throw createCrudFormError(message, {\n tenantId: message,\n })\n }\n\n const payload: CreateOrganizationPayload = {\n name: typeof values.name === 'string' ? values.name : '',\n isActive: values.isActive !== false,\n parentId: typeof values.parentId === 'string' && values.parentId.length\n ? values.parentId\n : null,\n childIds: Array.isArray(values.childIds) ? values.childIds.filter((id): id is string => typeof id === 'string') : [],\n }\n\n if (tenantValue) payload.tenantId = tenantValue\n if (Object.keys(customFields).length > 0) payload.customFields = customFields\n\n await createOrganization(payload)\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { E } from '#generated/entities.ids.generated'\nimport { OrganizationSelect } from '@open-mercato/core/modules/directory/components/OrganizationSelect'\nimport { TenantSelect } from '@open-mercato/core/modules/directory/components/TenantSelect'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { type OrganizationTreeNode } from '@open-mercato/core/modules/directory/lib/tree'\n\ntype TreeResponse = {\n items: OrganizationTreeNode[]\n}\n\ntype ChildTreeSelectProps = {\n nodes: OrganizationTreeNode[]\n value: string[]\n onChange: (vals: string[]) => void\n}\n\nfunction ChildTreeSelect({ nodes, value, onChange }: ChildTreeSelectProps) {\n const t = useT()\n const selected = React.useMemo(() => new Set(value), [value])\n const handleToggle = React.useCallback((id: string) => {\n const next = new Set(value)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n onChange(Array.from(next))\n }, [value, onChange])\n\n if (!nodes.length) {\n return (\n <div className=\"text-sm text-muted-foreground\">\n {t('directory.organizations.form.children.empty', 'No organizations available to assign.')}\n </div>\n )\n }\n\n return (\n <div className=\"rounded border px-3 py-2 max-h-64 overflow-auto space-y-2\">\n <TreeCheckboxGroup nodes={nodes} selected={selected} onToggle={handleToggle} level={0} />\n </div>\n )\n}\n\nfunction TreeCheckboxGroup({ nodes, selected, onToggle, level }: { nodes: OrganizationTreeNode[]; selected: Set<string>; onToggle: (id: string) => void; level: number }) {\n return (\n <div className={level === 0 ? 'space-y-1' : 'space-y-1 pl-5'}>\n {nodes.map((node) => (\n <div key={node.id} className=\"space-y-1\">\n <label className=\"inline-flex items-start gap-2 text-sm\">\n <input\n type=\"checkbox\"\n className=\"size-4 mt-0.5\"\n checked={selected.has(node.id)}\n onChange={() => onToggle(node.id)}\n />\n <span>{node.name}</span>\n </label>\n {node.children?.length ? (\n <TreeCheckboxGroup nodes={node.children} selected={selected} onToggle={onToggle} level={level + 1} />\n ) : null}\n </div>\n ))}\n </div>\n )\n}\n\nexport default function CreateOrganizationPage() {\n const [tree, setTree] = React.useState<OrganizationTreeNode[]>([])\n const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)\n const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)\n const t = useT()\n\n const loadTree = React.useCallback(async (tenantId: string | null) => {\n const params = new URLSearchParams({ view: 'tree', includeInactive: 'true' })\n if (tenantId) params.set('tenantId', tenantId)\n try {\n const call = await apiCall<TreeResponse>(`/api/directory/organizations?${params.toString()}`)\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n setTree(items)\n } catch {\n setTree([])\n }\n }, [])\n\n React.useEffect(() => {\n let cancelled = false\n async function bootstrap() {\n try {\n const call = await apiCall<{ isSuperAdmin?: boolean }>('/api/auth/roles?page=1&pageSize=1')\n if (!cancelled) setActorIsSuperAdmin(Boolean(call.result?.isSuperAdmin))\n } catch {\n if (!cancelled) setActorIsSuperAdmin(false)\n }\n if (!cancelled) await loadTree(null)\n }\n bootstrap()\n return () => { cancelled = true }\n }, [loadTree])\n\n React.useEffect(() => {\n if (!actorIsSuperAdmin) return\n void loadTree(selectedTenantId)\n }, [actorIsSuperAdmin, loadTree, selectedTenantId])\n\n const fields = React.useMemo<CrudField[]>(() => [\n ...(actorIsSuperAdmin ? [\n {\n id: 'tenantId',\n label: t('directory.organizations.form.field.tenant', 'Tenant'),\n type: 'custom',\n required: true,\n component: ({ value, setValue }) => (\n <TenantSelect\n id=\"tenantId\"\n value={typeof value === 'string' ? value : selectedTenantId}\n onChange={(next) => {\n const normalized = next ?? null\n setSelectedTenantId(normalized)\n setValue(normalized)\n }}\n includeEmptyOption\n emptyOptionLabel={t('directory.organizations.form.tenant.select', 'Select tenant')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n />\n ),\n } as CrudField,\n ] : []),\n { id: 'name', label: t('directory.organizations.form.field.name', 'Name'), type: 'text', required: true },\n {\n id: 'slug',\n label: t('directory.organizations.form.field.slug', 'Slug'),\n type: 'text',\n description: t('directory.organizations.form.field.slug.description', 'URL-safe identifier used for the customer portal (lowercase letters, numbers, hyphens, underscores). Generated from the name when left blank.'),\n },\n {\n id: 'parentId',\n label: t('directory.organizations.form.field.parent', 'Parent'),\n type: 'custom',\n component: ({ id, value, setValue }) => (\n <OrganizationSelect\n id={id}\n value={value ? String(value) : null}\n onChange={(next) => setValue(next ?? '')}\n tenantId={selectedTenantId}\n fetchOnMount={true}\n includeEmptyOption\n emptyOptionLabel={t('directory.organizations.form.rootOption', '\u2014 Root level \u2014')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n />\n ),\n },\n {\n id: 'childIds',\n label: t('directory.organizations.form.field.children', 'Children'),\n type: 'custom',\n component: ({ value, setValue }) => (\n <ChildTreeSelect\n nodes={tree}\n value={Array.isArray(value) ? value : []}\n onChange={(vals) => setValue(vals)}\n />\n ),\n },\n { id: 'isActive', label: t('directory.organizations.form.field.isActive', 'Active'), type: 'checkbox' },\n ], [actorIsSuperAdmin, selectedTenantId, t, tree])\n\n const detailFields = React.useMemo(() => (\n actorIsSuperAdmin\n ? ['tenantId', 'name', 'slug', 'parentId', 'childIds', 'isActive']\n : ['name', 'slug', 'parentId', 'childIds', 'isActive']\n ), [actorIsSuperAdmin])\n\n const groups: CrudFormGroup[] = React.useMemo(() => ([\n { id: 'details', title: t('directory.organizations.form.group.details', 'Details'), column: 1, fields: detailFields },\n { id: 'custom', title: t('directory.organizations.form.group.customFields', 'Custom Data'), column: 2, kind: 'customFields' },\n ]), [detailFields, t])\n const formTitle = t('directory.nav.organizations.create', 'Create Organization')\n const successMessage = encodeURIComponent(t('directory.organizations.flash.created', 'Organization created'))\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n title={formTitle}\n backHref=\"/backend/directory/organizations\"\n fields={fields}\n groups={groups}\n entityId={E.directory.organization}\n initialValues={{ tenantId: selectedTenantId ?? null, name: '', slug: '', parentId: '', childIds: [], isActive: true }}\n submitLabel={t('directory.organizations.form.action.create', 'Create')}\n cancelHref=\"/backend/directory/organizations\"\n successRedirect={`/backend/directory/organizations?flash=${successMessage}&type=success`}\n onSubmit={async (values) => {\n await submitCreateOrganization({\n values: values as Record<string, unknown>,\n actorIsSuperAdmin,\n selectedTenantId,\n messages: {\n tenantRequired: t('directory.organizations.errors.tenantRequired', 'Tenant selection is required for super administrators'),\n },\n })\n }}\n />\n </PageBody>\n </Page>\n )\n}\n\ntype CreateOrganizationPayload = {\n name: string\n slug?: string | null\n isActive: boolean\n parentId: string | null\n childIds: string[]\n tenantId?: string\n customFields?: Record<string, unknown>\n}\n\ntype CreateOrganizationRequest = (payload: CreateOrganizationPayload) => Promise<void>\n\nasync function defaultCreateOrganizationRequest(payload: CreateOrganizationPayload) {\n await createCrud('directory/organizations', payload)\n}\n\nexport async function submitCreateOrganization(options: {\n values: Record<string, unknown>\n actorIsSuperAdmin: boolean\n selectedTenantId: string | null\n createOrganization?: CreateOrganizationRequest\n messages?: {\n tenantRequired?: string\n }\n}): Promise<void> {\n const {\n values,\n actorIsSuperAdmin,\n selectedTenantId,\n createOrganization = defaultCreateOrganizationRequest,\n messages,\n } = options\n\n const customFields = collectCustomFieldValues(values)\n\n const tenantValue =\n typeof values.tenantId === 'string' && values.tenantId.trim().length\n ? values.tenantId.trim()\n : selectedTenantId\n\n if (actorIsSuperAdmin && !tenantValue) {\n const message = messages?.tenantRequired ?? 'Tenant selection is required for super administrators'\n throw createCrudFormError(message, {\n tenantId: message,\n })\n }\n\n const payload: CreateOrganizationPayload = {\n name: typeof values.name === 'string' ? values.name : '',\n isActive: values.isActive !== false,\n parentId: typeof values.parentId === 'string' && values.parentId.length\n ? values.parentId\n : null,\n childIds: Array.isArray(values.childIds) ? values.childIds.filter((id): id is string => typeof id === 'string') : [],\n }\n\n if (typeof values.slug === 'string') {\n const trimmedSlug = values.slug.trim()\n if (trimmedSlug.length) payload.slug = trimmedSlug\n }\n\n if (tenantValue) payload.tenantId = tenantValue\n if (Object.keys(customFields).length > 0) payload.customFields = customFields\n\n await createOrganization(payload)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -19,8 +19,9 @@ function useMessageDetails(id) {
|
|
|
19
19
|
scopeVersion,
|
|
20
20
|
queryClient
|
|
21
21
|
});
|
|
22
|
-
const
|
|
22
|
+
const invalidateMessageQueries = React.useCallback(
|
|
23
23
|
(payload) => {
|
|
24
|
+
void queryClient.invalidateQueries({ queryKey: ["messages", "list"] });
|
|
24
25
|
void queryClient.invalidateQueries({ queryKey: ["messages", "detail", id] });
|
|
25
26
|
const messageId = typeof payload.messageId === "string" ? payload.messageId : null;
|
|
26
27
|
if (messageId && messageId !== id) {
|
|
@@ -32,9 +33,9 @@ function useMessageDetails(id) {
|
|
|
32
33
|
useAppEvent(
|
|
33
34
|
"messages.message.*",
|
|
34
35
|
(evt) => {
|
|
35
|
-
|
|
36
|
+
invalidateMessageQueries(evt.payload ?? {});
|
|
36
37
|
},
|
|
37
|
-
[
|
|
38
|
+
[invalidateMessageQueries]
|
|
38
39
|
);
|
|
39
40
|
useAppEvent(
|
|
40
41
|
"om:bridge:reconnected",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/messages/components/message-detail/hooks/useMessageDetails.ts"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { useMessageDetailsQueries } from './useMessageDetailsQueries'\nimport { useMessageDetailsActions } from './useMessageDetailsActions'\nimport { useMessageDetailsConversation } from './useMessageDetailsConversation'\n\nexport function useMessageDetails(id: string) {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n\n const queryState = useMessageDetailsQueries({\n id,\n t,\n scopeVersion,\n queryClient,\n })\n\n const
|
|
5
|
-
"mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,YAAY;AACrB,SAAS,mCAAmC;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,gCAAgC;AACzC,SAAS,gCAAgC;AACzC,SAAS,qCAAqC;AAEvC,SAAS,kBAAkB,IAAY;AAC5C,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,4BAA4B;AACjD,QAAM,cAAc,eAAe;AAEnC,QAAM,aAAa,yBAAyB;AAAA,IAC1C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { useMessageDetailsQueries } from './useMessageDetailsQueries'\nimport { useMessageDetailsActions } from './useMessageDetailsActions'\nimport { useMessageDetailsConversation } from './useMessageDetailsConversation'\n\nexport function useMessageDetails(id: string) {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n\n const queryState = useMessageDetailsQueries({\n id,\n t,\n scopeVersion,\n queryClient,\n })\n\n const invalidateMessageQueries = React.useCallback(\n (payload: Record<string, unknown>) => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'list'] })\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', id] })\n const messageId = typeof payload.messageId === 'string' ? payload.messageId : null\n if (messageId && messageId !== id) {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', messageId] })\n }\n },\n [id, queryClient],\n )\n\n useAppEvent(\n 'messages.message.*',\n (evt) => {\n invalidateMessageQueries((evt.payload ?? {}) as Record<string, unknown>)\n },\n [invalidateMessageQueries],\n )\n\n useAppEvent(\n 'om:bridge:reconnected',\n () => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', id] })\n },\n [id, queryClient],\n )\n\n const isArchived = (queryState.detail?.recipients ?? []).some((item) => item.status === 'archived')\n\n const actionState = useMessageDetailsActions({\n id,\n t,\n detail: queryState.detail,\n detailQuery: queryState.detailQuery,\n attachments: queryState.attachments,\n isArchived,\n onDeleted: () => router.push('/backend/messages'),\n refreshDetailWithoutAutoMarkRead: queryState.refreshDetailWithoutAutoMarkRead,\n })\n\n const conversationState = useMessageDetailsConversation({\n detail: queryState.detail,\n t,\n })\n\n const backToList = React.useCallback(() => {\n router.push('/backend/messages')\n }, [router])\n\n return {\n t,\n router,\n backToList,\n ...queryState,\n ...conversationState,\n ...actionState,\n isArchived,\n }\n}\n"],
|
|
5
|
+
"mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,YAAY;AACrB,SAAS,mCAAmC;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,gCAAgC;AACzC,SAAS,gCAAgC;AACzC,SAAS,qCAAqC;AAEvC,SAAS,kBAAkB,IAAY;AAC5C,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,4BAA4B;AACjD,QAAM,cAAc,eAAe;AAEnC,QAAM,aAAa,yBAAyB;AAAA,IAC1C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,2BAA2B,MAAM;AAAA,IACrC,CAAC,YAAqC;AACpC,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,MAAM,EAAE,CAAC;AACrE,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC;AAC3E,YAAM,YAAY,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY;AAC9E,UAAI,aAAa,cAAc,IAAI;AACjC,aAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,SAAS,EAAE,CAAC;AAAA,MACpF;AAAA,IACF;AAAA,IACA,CAAC,IAAI,WAAW;AAAA,EAClB;AAEA;AAAA,IACE;AAAA,IACA,CAAC,QAAQ;AACP,+BAA0B,IAAI,WAAW,CAAC,CAA6B;AAAA,IACzE;AAAA,IACA,CAAC,wBAAwB;AAAA,EAC3B;AAEA;AAAA,IACE;AAAA,IACA,MAAM;AACJ,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC;AAAA,IAC7E;AAAA,IACA,CAAC,IAAI,WAAW;AAAA,EAClB;AAEA,QAAM,cAAc,WAAW,QAAQ,cAAc,CAAC,GAAG,KAAK,CAAC,SAAS,KAAK,WAAW,UAAU;AAElG,QAAM,cAAc,yBAAyB;AAAA,IAC3C;AAAA,IACA;AAAA,IACA,QAAQ,WAAW;AAAA,IACnB,aAAa,WAAW;AAAA,IACxB,aAAa,WAAW;AAAA,IACxB;AAAA,IACA,WAAW,MAAM,OAAO,KAAK,mBAAmB;AAAA,IAChD,kCAAkC,WAAW;AAAA,EAC/C,CAAC;AAED,QAAM,oBAAoB,8BAA8B;AAAA,IACtD,QAAQ,WAAW;AAAA,IACnB;AAAA,EACF,CAAC;AAED,QAAM,aAAa,MAAM,YAAY,MAAM;AACzC,WAAO,KAAK,mBAAmB;AAAA,EACjC,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const metadata = {
|
|
2
|
+
requireCustomerAuth: true,
|
|
3
|
+
titleKey: "portal.dashboard.title",
|
|
4
|
+
title: "Dashboard",
|
|
5
|
+
nav: {
|
|
6
|
+
label: "Dashboard",
|
|
7
|
+
labelKey: "portal.nav.dashboard",
|
|
8
|
+
group: "main",
|
|
9
|
+
order: 10
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var page_meta_default = metadata;
|
|
13
|
+
export {
|
|
14
|
+
page_meta_default as default,
|
|
15
|
+
metadata
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=page.meta.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/dashboard/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n requireCustomerAuth: true,\n titleKey: 'portal.dashboard.title',\n title: 'Dashboard',\n nav: {\n label: 'Dashboard',\n labelKey: 'portal.nav.dashboard',\n group: 'main',\n order: 10,\n },\n}\n\nexport default metadata\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,qBAAqB;AAAA,EACrB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,KAAK;AAAA,IACH,OAAO;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AACF;AAEA,IAAO,oBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/login/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n titleKey: 'portal.nav.login',\n title: 'Log In',\n navHidden: true,\n}\n\nexport default metadata\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AACb;AAEA,IAAO,oBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n titleKey: 'portal.title',\n title: 'Customer Portal',\n navHidden: true,\n}\n\nexport default metadata\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AACb;AAEA,IAAO,oBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const metadata = {
|
|
2
|
+
requireCustomerAuth: true,
|
|
3
|
+
titleKey: "portal.nav.profile",
|
|
4
|
+
title: "Profile",
|
|
5
|
+
nav: {
|
|
6
|
+
label: "Profile",
|
|
7
|
+
labelKey: "portal.nav.profile",
|
|
8
|
+
group: "account",
|
|
9
|
+
order: 10
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var page_meta_default = metadata;
|
|
13
|
+
export {
|
|
14
|
+
page_meta_default as default,
|
|
15
|
+
metadata
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=page.meta.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/profile/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n requireCustomerAuth: true,\n titleKey: 'portal.nav.profile',\n title: 'Profile',\n nav: {\n label: 'Profile',\n labelKey: 'portal.nav.profile',\n group: 'account',\n order: 10,\n },\n}\n\nexport default metadata\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,qBAAqB;AAAA,EACrB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,KAAK;AAAA,IACH,OAAO;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AACF;AAEA,IAAO,oBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/signup/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n titleKey: 'portal.nav.signup',\n title: 'Sign Up',\n navHidden: true,\n}\n\nexport default metadata\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AACb;AAEA,IAAO,oBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/verify/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n titleKey: 'portal.title',\n title: 'Verify Email',\n navHidden: true,\n}\n\nexport default metadata\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AACb;AAEA,IAAO,oBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -469,28 +469,19 @@ async function executeCallApi(em, config, context, container, signal) {
|
|
|
469
469
|
}
|
|
470
470
|
);
|
|
471
471
|
}
|
|
472
|
-
async function
|
|
473
|
-
if (!instance.definitionId) return [];
|
|
472
|
+
async function resolveActiveRoleIdsForUser(em, userId, scope) {
|
|
474
473
|
const { findOneWithDecryption, findWithDecryption } = await import("@open-mercato/shared/lib/encryption/find");
|
|
475
474
|
const { User, UserRole, Role } = await import("../../auth/data/entities.js");
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
id: instance.definitionId,
|
|
480
|
-
tenantId: instance.tenantId
|
|
481
|
-
}, {}, scope);
|
|
482
|
-
const authorUserId = definition?.createdBy;
|
|
483
|
-
if (!authorUserId) return [];
|
|
484
|
-
const author = await findOneWithDecryption(em, User, {
|
|
485
|
-
id: authorUserId,
|
|
486
|
-
tenantId: instance.tenantId,
|
|
475
|
+
const user = await findOneWithDecryption(em, User, {
|
|
476
|
+
id: userId,
|
|
477
|
+
tenantId: scope.tenantId,
|
|
487
478
|
deletedAt: null
|
|
488
479
|
}, {}, scope);
|
|
489
|
-
if (!
|
|
480
|
+
if (!user) return [];
|
|
490
481
|
const userRoles = await findWithDecryption(
|
|
491
482
|
em,
|
|
492
483
|
UserRole,
|
|
493
|
-
{ user:
|
|
484
|
+
{ user: user.id, deletedAt: null },
|
|
494
485
|
{ populate: ["role"] },
|
|
495
486
|
scope
|
|
496
487
|
);
|
|
@@ -498,11 +489,29 @@ async function resolveCallApiRoleIds(em, instance) {
|
|
|
498
489
|
if (roleIds.length === 0) return [];
|
|
499
490
|
const scopedRoles = await findWithDecryption(em, Role, {
|
|
500
491
|
id: { $in: roleIds },
|
|
501
|
-
tenantId:
|
|
492
|
+
tenantId: scope.tenantId,
|
|
502
493
|
deletedAt: null
|
|
503
494
|
}, {}, scope);
|
|
504
495
|
return scopedRoles.map((r) => r.id);
|
|
505
496
|
}
|
|
497
|
+
async function resolveCallApiRoleIds(em, instance) {
|
|
498
|
+
if (!instance.definitionId) return [];
|
|
499
|
+
const { findOneWithDecryption } = await import("@open-mercato/shared/lib/encryption/find");
|
|
500
|
+
const { WorkflowDefinition } = await import("../data/entities.js");
|
|
501
|
+
const scope = { tenantId: instance.tenantId, organizationId: instance.organizationId };
|
|
502
|
+
const initiatorUserId = instance.metadata?.initiatedBy ?? null;
|
|
503
|
+
if (initiatorUserId) {
|
|
504
|
+
return resolveActiveRoleIdsForUser(em, initiatorUserId, scope);
|
|
505
|
+
}
|
|
506
|
+
const definition = await findOneWithDecryption(em, WorkflowDefinition, {
|
|
507
|
+
id: instance.definitionId,
|
|
508
|
+
tenantId: instance.tenantId,
|
|
509
|
+
deletedAt: null
|
|
510
|
+
}, {}, scope);
|
|
511
|
+
const authorUserId = definition?.createdBy;
|
|
512
|
+
if (!authorUserId) return [];
|
|
513
|
+
return resolveActiveRoleIdsForUser(em, authorUserId, scope);
|
|
514
|
+
}
|
|
506
515
|
function buildApiUrl(endpoint) {
|
|
507
516
|
const appUrl = process.env.APP_URL || "http://localhost:3000";
|
|
508
517
|
if (endpoint.startsWith("/")) {
|