@open-mercato/core 0.5.1-develop.2800.bfe2178a4f → 0.5.1-develop.2851.2854b4507f
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/dist/generated/entities/action_log/index.js +4 -0
- package/dist/generated/entities/action_log/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/audit_logs/data/entities.js +10 -1
- package/dist/modules/audit_logs/data/entities.js.map +2 -2
- package/dist/modules/audit_logs/data/validators.js +2 -0
- package/dist/modules/audit_logs/data/validators.js.map +2 -2
- package/dist/modules/audit_logs/migrations/Migration20260423202109.js +15 -0
- package/dist/modules/audit_logs/migrations/Migration20260423202109.js.map +7 -0
- package/dist/modules/audit_logs/services/accessLogService.js +3 -2
- package/dist/modules/audit_logs/services/accessLogService.js.map +3 -3
- package/dist/modules/audit_logs/services/actionLogService.js +13 -2
- package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/customers/api/entity-roles-factory.js +3 -18
- package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
- package/dist/modules/customers/api/interactions/cancel/route.js +7 -2
- package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/complete/route.js +7 -2
- package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +45 -44
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/commands/comments.js +6 -0
- package/dist/modules/customers/commands/comments.js.map +2 -2
- package/dist/modules/customers/components/detail/AssignRoleDialog.js +41 -13
- package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyDetailHeader.js +30 -0
- package/dist/modules/customers/components/detail/CompanyDetailHeader.js.map +2 -2
- package/dist/modules/customers/components/detail/DealDetailHeader.js +32 -0
- package/dist/modules/customers/components/detail/DealDetailHeader.js.map +2 -2
- package/dist/modules/customers/components/detail/DealWonPopup.js +2 -2
- package/dist/modules/customers/components/detail/DealWonPopup.js.map +2 -2
- package/dist/modules/customers/components/detail/InlineActivityComposer.js +62 -6
- package/dist/modules/customers/components/detail/InlineActivityComposer.js.map +2 -2
- package/dist/modules/customers/components/detail/ObjectHistoryButton.js +39 -0
- package/dist/modules/customers/components/detail/ObjectHistoryButton.js.map +7 -0
- package/dist/modules/customers/components/detail/PersonDetailHeader.js +30 -0
- package/dist/modules/customers/components/detail/PersonDetailHeader.js.map +2 -2
- package/dist/modules/customers/components/detail/RolesSection.js +14 -4
- package/dist/modules/customers/components/detail/RolesSection.js.map +3 -3
- package/dist/modules/customers/components/formConfig.js +16 -2
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/customers/lib/displayName.js +15 -0
- package/dist/modules/customers/lib/displayName.js.map +7 -0
- package/dist/modules/customers/lib/interactionReadModel.js +1 -2
- package/dist/modules/customers/lib/interactionReadModel.js.map +2 -2
- package/dist/modules/customers/lib/operationMetadata.js +21 -0
- package/dist/modules/customers/lib/operationMetadata.js.map +7 -0
- package/dist/modules/messages/components/MessagesInboxPageClient.js +106 -107
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
- package/dist/modules/messages/components/useMessagesInboxBulkActions.js +235 -0
- package/dist/modules/messages/components/useMessagesInboxBulkActions.js.map +7 -0
- package/generated/entities/action_log/index.ts +2 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +3 -3
- package/src/modules/audit_logs/data/entities.ts +7 -0
- package/src/modules/audit_logs/data/validators.ts +2 -0
- package/src/modules/audit_logs/migrations/.snapshot-open-mercato.json +51 -5
- package/src/modules/audit_logs/migrations/Migration20260423202109.ts +15 -0
- package/src/modules/audit_logs/services/accessLogService.ts +1 -3
- package/src/modules/audit_logs/services/actionLogService.ts +11 -6
- package/src/modules/auth/cli.ts +1 -1
- package/src/modules/customers/api/entity-roles-factory.ts +3 -23
- package/src/modules/customers/api/interactions/cancel/route.ts +7 -2
- package/src/modules/customers/api/interactions/complete/route.ts +7 -2
- package/src/modules/customers/backend/customers/deals/page.tsx +48 -44
- package/src/modules/customers/commands/comments.ts +6 -0
- package/src/modules/customers/components/detail/AssignRoleDialog.tsx +37 -9
- package/src/modules/customers/components/detail/CompanyDetailHeader.tsx +25 -0
- package/src/modules/customers/components/detail/DealDetailHeader.tsx +29 -0
- package/src/modules/customers/components/detail/DealWonPopup.tsx +2 -2
- package/src/modules/customers/components/detail/InlineActivityComposer.tsx +65 -6
- package/src/modules/customers/components/detail/ObjectHistoryButton.tsx +47 -0
- package/src/modules/customers/components/detail/PersonDetailHeader.tsx +25 -0
- package/src/modules/customers/components/detail/RolesSection.tsx +20 -1
- package/src/modules/customers/components/formConfig.tsx +14 -2
- package/src/modules/customers/i18n/de.json +12 -0
- package/src/modules/customers/i18n/en.json +12 -0
- package/src/modules/customers/i18n/es.json +13 -1
- package/src/modules/customers/i18n/pl.json +13 -1
- package/src/modules/customers/lib/displayName.ts +16 -0
- package/src/modules/customers/lib/interactionReadModel.ts +1 -7
- package/src/modules/customers/lib/operationMetadata.ts +38 -0
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +17 -29
- package/src/modules/messages/components/useMessagesInboxBulkActions.ts +324 -0
- package/src/modules/messages/i18n/de.json +8 -0
- package/src/modules/messages/i18n/en.json +8 -0
- package/src/modules/messages/i18n/es.json +8 -0
- package/src/modules/messages/i18n/pl.json +8 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/components/detail/PersonDetailHeader.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Phone, Mail, Building2, Trash2, Pencil } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { PersonTagsDialog } from './PersonTagsDialog'\nimport { useCustomerDictionary, invalidateCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { renderDictionaryIcon } from '../../../dictionaries/components/dictionaryAppearance'\nimport type { TagSummary } from './types'\nimport type { TagsSectionController } from '@open-mercato/ui/backend/detail'\nimport type { PersonOverview } from '../formConfig'\nimport type { CustomerDictionaryMap } from '@open-mercato/core/modules/customers/lib/dictionaries'\nimport { getInitials, formatFallbackLabel } from './utils'\n\ntype PersonDetailHeaderProps = {\n data: PersonOverview\n onTagsChange: (tags: TagSummary[]) => void\n tagsSectionControllerRef: React.RefObject<TagsSectionController | null>\n onSave: () => void\n onDelete: () => Promise<void>\n isDirty: boolean\n isSaving: boolean\n /** Callback to focus a specific field in the Zone 1 CrudForm by field name. */\n onFocusField?: (fieldName: string) => void\n /**\n * @deprecated Kept for backward compatibility. The \"+ Link company\" header CTA was removed;\n * company linking now happens exclusively through the Zone 2 Companies tab via\n * `PersonCompaniesSection`. This prop is a no-op and will be removed in a future major release.\n */\n onOpenCompaniesTab?: () => void\n /** Callback to reload person data after tags dialog save. */\n onDataReload?: () => void\n}\n\nfunction DictionaryBadge({ value, map, categoryIcon, className }: { value: string; map: CustomerDictionaryMap | undefined; categoryIcon?: React.ReactNode; className?: string }) {\n const entry = map?.[value]\n const color = entry?.color ?? null\n const icon = entry?.icon ?? null\n const label = entry?.label ?? formatFallbackLabel(value)\n const colorStyle: React.CSSProperties | undefined = color\n ? { color, borderColor: color, backgroundColor: `${color}1A` }\n : undefined\n return (\n <Badge\n variant=\"outline\"\n className={cn(\n 'rounded-sm gap-1.5 text-xs font-medium',\n className,\n )}\n style={colorStyle}\n >\n {icon ? renderDictionaryIcon(icon, 'size-2.5') : categoryIcon ?? null}\n {label}\n </Badge>\n )\n}\n\n/** Renders a tag badge with color-based text/border/background from TagSummary.color. */\nfunction TagBadge({ tag }: { tag: TagSummary }) {\n const colorStyle: React.CSSProperties | undefined = tag.color\n ? { color: tag.color, borderColor: tag.color, backgroundColor: `${tag.color}1A` }\n : undefined\n return (\n <Badge variant=\"outline\" className=\"rounded-sm gap-1.5 text-xs font-medium\" style={colorStyle}>\n {tag.label}\n </Badge>\n )\n}\n\nexport function PersonDetailHeader({\n data,\n onTagsChange,\n tagsSectionControllerRef,\n onSave,\n onDelete,\n isDirty,\n isSaving,\n onFocusField,\n onOpenCompaniesTab,\n onDataReload,\n}: PersonDetailHeaderProps) {\n const t = useT()\n const queryClient = useQueryClient()\n const [manageTagsOpen, setManageTagsOpen] = React.useState(false)\n const person = data.person\n const profile = data.profile\n const displayName = person.displayName || t('customers.people.detail.untitled', 'Untitled')\n\n const jobTitle = profile?.jobTitle ?? null\n const linkedCompanies = React.useMemo(() => {\n const items = Array.isArray(data.companies) && data.companies.length > 0\n ? data.companies\n : data.company\n ? [{ ...data.company, isPrimary: Boolean(data.isPrimary) }]\n : []\n return items\n }, [data.companies, data.company, data.isPrimary])\n const visibleCompanies = React.useMemo(() => linkedCompanies.slice(0, 3), [linkedCompanies])\n const hiddenCompaniesCount = Math.max(0, linkedCompanies.length - visibleCompanies.length)\n const visibleTags = React.useMemo(() => data.tags.slice(0, 6), [data.tags])\n const hiddenTagsCount = Math.max(0, data.tags.length - visibleTags.length)\n const primaryCompany = linkedCompanies.find((entry) => entry.isPrimary) ?? linkedCompanies[0] ?? null\n const companyName = primaryCompany?.displayName ?? null\n const companyId = primaryCompany?.id ?? profile?.companyEntityId ?? null\n\n // Fetch dictionary maps for colored badge rendering (scoped to person's organization)\n const personOrgId = person.organizationId ?? null\n const { data: statusDict } = useCustomerDictionary('statuses', 0, personOrgId)\n const { data: lifecycleDict } = useCustomerDictionary('lifecycle-stages', 0, personOrgId)\n const { data: sourceDict } = useCustomerDictionary('sources', 0, personOrgId)\n const { data: temperatureDict } = useCustomerDictionary('temperature', 0, personOrgId)\n const { data: renewalQuarterDict } = useCustomerDictionary('renewal-quarters', 0, personOrgId)\n\n return (\n <div className=\"rounded-lg border bg-card px-6 py-5\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:gap-5\">\n {/* Avatar */}\n <div className=\"flex size-18 shrink-0 items-center justify-center rounded-full bg-muted text-xl font-bold text-muted-foreground\">\n {getInitials(displayName)}\n </div>\n\n {/* Person info */}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h1 className=\"truncate text-2xl font-bold text-foreground\">{displayName}</h1>\n {data.isPrimary && (\n <span className=\"shrink-0 rounded-sm bg-status-warning-bg px-1.5 py-0.5 text-overline font-bold text-status-warning-text\">\n {t('customers.people.detail.header.primary', 'PRIMARY')}\n </span>\n )}\n </div>\n\n {/* Subtitle: job title + company link */}\n {(jobTitle || companyName) && (\n <p className=\"mt-0.5 text-sm text-muted-foreground\">\n {jobTitle}\n {jobTitle && companyName && ` ${t('customers.people.detail.header.at', 'at')} `}\n {companyName && companyId && (\n <Link href={`/backend/customers/companies-v2/${companyId}`} className=\"text-primary hover:underline\">\n {companyName}\n </Link>\n )}\n {companyName && !companyId && companyName}\n </p>\n )}\n\n {/* Contact row */}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-5 gap-y-1 text-sm text-muted-foreground\">\n {person.primaryPhone && (\n <span className=\"inline-flex items-center gap-1.5\">\n <Phone className=\"size-3.5\" />\n <a href={`tel:${person.primaryPhone}`} className=\"hover:text-foreground\">{person.primaryPhone}</a>\n </span>\n )}\n {person.primaryEmail && (\n <span className=\"inline-flex items-center gap-1.5\">\n <Mail className=\"size-3.5\" />\n <a href={`mailto:${person.primaryEmail}`} className=\"hover:text-foreground\">{person.primaryEmail}</a>\n </span>\n )}\n </div>\n\n {/* Company chips (annotation 1a) */}\n {linkedCompanies.length > 0 && (\n <div className=\"mt-1.5 flex flex-wrap items-center gap-2 text-sm\">\n <span className=\"text-muted-foreground\">\n <Building2 className=\"mr-1 inline size-3.5\" />\n {t('customers.people.detail.header.companies', 'Companies')} ({linkedCompanies.length}):\n </span>\n {visibleCompanies.map((company) => (\n <Link\n key={company.id}\n href={`/backend/customers/companies-v2/${company.id}`}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-sm border px-2 py-0.5 text-xs font-semibold transition-colors hover:bg-status-info-bg',\n company.isPrimary\n ? 'border-status-info-border bg-status-info-bg text-status-info-text'\n : 'border-border bg-background text-foreground',\n )}\n >\n <Building2 className=\"size-3\" />\n {company.displayName}\n {company.isPrimary ? (\n <span className=\"rounded-sm bg-status-info-icon px-1 py-px text-overline font-bold text-white\">\n {t('customers.people.detail.header.primary', 'PRIMARY')}\n </span>\n ) : null}\n </Link>\n ))}\n {hiddenCompaniesCount > 0 ? (\n <Badge variant=\"outline\" className=\"rounded-sm text-xs font-semibold\">\n +{hiddenCompaniesCount} {t('customers.people.detail.header.more', 'more')}\n </Badge>\n ) : null}\n </div>\n )}\n\n {/* Status badges + inline tags + manage tags */}\n <div className=\"mt-2.5 flex flex-wrap items-center gap-2\">\n {person.status && (\n <DictionaryBadge value={person.status} map={statusDict?.map} />\n )}\n {person.lifecycleStage && (\n <DictionaryBadge value={person.lifecycleStage} map={lifecycleDict?.map} />\n )}\n {person.source && (\n <DictionaryBadge value={person.source} map={sourceDict?.map} />\n )}\n {person.temperature && (\n <DictionaryBadge value={person.temperature} map={temperatureDict?.map} />\n )}\n {person.renewalQuarter && (\n <DictionaryBadge value={person.renewalQuarter} map={renewalQuarterDict?.map} />\n )}\n {/* Inline tag pills */}\n {visibleTags.map((tag) => (\n <TagBadge key={tag.id ?? tag.label} tag={tag} />\n ))}\n {hiddenTagsCount > 0 ? (\n <Badge variant=\"outline\" className=\"rounded-sm gap-1.5 text-xs font-medium\">\n +{hiddenTagsCount} {t('customers.people.detail.header.more', 'more')}\n </Badge>\n ) : null}\n {/* Manage tags \u2014 opens dialog directly */}\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-auto rounded-sm px-2 py-1 text-xs font-medium text-muted-foreground hover:text-foreground\"\n onClick={() => setManageTagsOpen(true)}\n >\n <Pencil className=\"mr-1 size-3\" />\n {t('customers.people.detail.actions.manageTags', 'Edit tags')}\n </Button>\n </div>\n </div>\n\n {/* Right side: actions */}\n <div className=\"flex w-full shrink-0 items-center justify-start gap-2 sm:w-auto sm:justify-end\">\n <IconButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n aria-label={t('customers.people.detail.actions.delete', 'Delete')}\n onClick={() => {\n void onDelete()\n }}\n >\n <Trash2 className=\"size-4\" />\n </IconButton>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={onSave}\n disabled={!isDirty || isSaving}\n >\n {t('customers.people.detail.actions.save', 'Save')}\n </Button>\n </div>\n </div>\n <PersonTagsDialog\n open={manageTagsOpen}\n onClose={() => setManageTagsOpen(false)}\n personId={person.id}\n personOrganizationId={person.organizationId ?? null}\n personData={{\n status: person.status,\n lifecycleStage: person.lifecycleStage,\n source: person.source,\n temperature: person.temperature,\n renewalQuarter: person.renewalQuarter,\n jobTitle: data.profile?.jobTitle ?? null,\n customFields: data.customFields,\n tags: data.tags,\n }}\n onSaved={() => {\n // Invalidate dictionary caches so header badges pick up fresh colors\n void invalidateCustomerDictionary(queryClient, 'statuses')\n void invalidateCustomerDictionary(queryClient, 'lifecycle-stages')\n void invalidateCustomerDictionary(queryClient, 'sources')\n void invalidateCustomerDictionary(queryClient, 'temperature')\n void invalidateCustomerDictionary(queryClient, 'renewal-quarters')\n onDataReload?.()\n }}\n />\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Phone, Mail, Building2, Trash2, Pencil } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { ObjectHistoryButton } from './ObjectHistoryButton'\nimport { PersonTagsDialog } from './PersonTagsDialog'\nimport { useCustomerDictionary, invalidateCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { renderDictionaryIcon } from '../../../dictionaries/components/dictionaryAppearance'\nimport type { TagSummary } from './types'\nimport type { TagsSectionController } from '@open-mercato/ui/backend/detail'\nimport type { PersonOverview } from '../formConfig'\nimport type { CustomerDictionaryMap } from '@open-mercato/core/modules/customers/lib/dictionaries'\nimport { getInitials, formatFallbackLabel } from './utils'\n\nconst HEADER_ICON_BUTTON_CLASS = 'size-8 rounded-md'\n\ntype PersonDetailHeaderProps = {\n data: PersonOverview\n onTagsChange: (tags: TagSummary[]) => void\n tagsSectionControllerRef: React.RefObject<TagsSectionController | null>\n onSave: () => void\n onDelete: () => Promise<void>\n isDirty: boolean\n isSaving: boolean\n /** Callback to focus a specific field in the Zone 1 CrudForm by field name. */\n onFocusField?: (fieldName: string) => void\n /**\n * @deprecated Kept for backward compatibility. The \"+ Link company\" header CTA was removed;\n * company linking now happens exclusively through the Zone 2 Companies tab via\n * `PersonCompaniesSection`. This prop is a no-op and will be removed in a future major release.\n */\n onOpenCompaniesTab?: () => void\n /** Callback to reload person data after tags dialog save. */\n onDataReload?: () => void\n}\n\nfunction DictionaryBadge({ value, map, categoryIcon, className }: { value: string; map: CustomerDictionaryMap | undefined; categoryIcon?: React.ReactNode; className?: string }) {\n const entry = map?.[value]\n const color = entry?.color ?? null\n const icon = entry?.icon ?? null\n const label = entry?.label ?? formatFallbackLabel(value)\n const colorStyle: React.CSSProperties | undefined = color\n ? { color, borderColor: color, backgroundColor: `${color}1A` }\n : undefined\n return (\n <Badge\n variant=\"outline\"\n className={cn(\n 'rounded-sm gap-1.5 text-xs font-medium',\n className,\n )}\n style={colorStyle}\n >\n {icon ? renderDictionaryIcon(icon, 'size-2.5') : categoryIcon ?? null}\n {label}\n </Badge>\n )\n}\n\n/** Renders a tag badge with color-based text/border/background from TagSummary.color. */\nfunction TagBadge({ tag }: { tag: TagSummary }) {\n const colorStyle: React.CSSProperties | undefined = tag.color\n ? { color: tag.color, borderColor: tag.color, backgroundColor: `${tag.color}1A` }\n : undefined\n return (\n <Badge variant=\"outline\" className=\"rounded-sm gap-1.5 text-xs font-medium\" style={colorStyle}>\n {tag.label}\n </Badge>\n )\n}\n\nexport function PersonDetailHeader({\n data,\n onTagsChange,\n tagsSectionControllerRef,\n onSave,\n onDelete,\n isDirty,\n isSaving,\n onFocusField,\n onOpenCompaniesTab,\n onDataReload,\n}: PersonDetailHeaderProps) {\n const t = useT()\n const queryClient = useQueryClient()\n const [manageTagsOpen, setManageTagsOpen] = React.useState(false)\n const person = data.person\n const profile = data.profile\n const displayName = person.displayName || t('customers.people.detail.untitled', 'Untitled')\n\n const jobTitle = profile?.jobTitle ?? null\n const linkedCompanies = React.useMemo(() => {\n const items = Array.isArray(data.companies) && data.companies.length > 0\n ? data.companies\n : data.company\n ? [{ ...data.company, isPrimary: Boolean(data.isPrimary) }]\n : []\n return items\n }, [data.companies, data.company, data.isPrimary])\n const visibleCompanies = React.useMemo(() => linkedCompanies.slice(0, 3), [linkedCompanies])\n const hiddenCompaniesCount = Math.max(0, linkedCompanies.length - visibleCompanies.length)\n const visibleTags = React.useMemo(() => data.tags.slice(0, 6), [data.tags])\n const hiddenTagsCount = Math.max(0, data.tags.length - visibleTags.length)\n const primaryCompany = linkedCompanies.find((entry) => entry.isPrimary) ?? linkedCompanies[0] ?? null\n const companyName = primaryCompany?.displayName ?? null\n const companyId = primaryCompany?.id ?? profile?.companyEntityId ?? null\n\n // Fetch dictionary maps for colored badge rendering (scoped to person's organization)\n const personOrgId = person.organizationId ?? null\n const { data: statusDict } = useCustomerDictionary('statuses', 0, personOrgId)\n const { data: lifecycleDict } = useCustomerDictionary('lifecycle-stages', 0, personOrgId)\n const { data: sourceDict } = useCustomerDictionary('sources', 0, personOrgId)\n const { data: temperatureDict } = useCustomerDictionary('temperature', 0, personOrgId)\n const { data: renewalQuarterDict } = useCustomerDictionary('renewal-quarters', 0, personOrgId)\n\n return (\n <div className=\"rounded-lg border bg-card px-6 py-5\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:gap-5\">\n {/* Avatar */}\n <div className=\"flex size-18 shrink-0 items-center justify-center rounded-full bg-muted text-xl font-bold text-muted-foreground\">\n {getInitials(displayName)}\n </div>\n\n {/* Person info */}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h1 className=\"truncate text-2xl font-bold text-foreground\">{displayName}</h1>\n {data.isPrimary && (\n <span className=\"shrink-0 rounded-sm bg-status-warning-bg px-1.5 py-0.5 text-overline font-bold text-status-warning-text\">\n {t('customers.people.detail.header.primary', 'PRIMARY')}\n </span>\n )}\n </div>\n\n {/* Subtitle: job title + company link */}\n {(jobTitle || companyName) && (\n <p className=\"mt-0.5 text-sm text-muted-foreground\">\n {jobTitle}\n {jobTitle && companyName && ` ${t('customers.people.detail.header.at', 'at')} `}\n {companyName && companyId && (\n <Link href={`/backend/customers/companies-v2/${companyId}`} className=\"text-primary hover:underline\">\n {companyName}\n </Link>\n )}\n {companyName && !companyId && companyName}\n </p>\n )}\n\n {/* Contact row */}\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-5 gap-y-1 text-sm text-muted-foreground\">\n {person.primaryPhone && (\n <span className=\"inline-flex items-center gap-1.5\">\n <Phone className=\"size-3.5\" />\n <a href={`tel:${person.primaryPhone}`} className=\"hover:text-foreground\">{person.primaryPhone}</a>\n </span>\n )}\n {person.primaryEmail && (\n <span className=\"inline-flex items-center gap-1.5\">\n <Mail className=\"size-3.5\" />\n <a href={`mailto:${person.primaryEmail}`} className=\"hover:text-foreground\">{person.primaryEmail}</a>\n </span>\n )}\n </div>\n\n {/* Company chips (annotation 1a) */}\n {linkedCompanies.length > 0 && (\n <div className=\"mt-1.5 flex flex-wrap items-center gap-2 text-sm\">\n <span className=\"text-muted-foreground\">\n <Building2 className=\"mr-1 inline size-3.5\" />\n {t('customers.people.detail.header.companies', 'Companies')} ({linkedCompanies.length}):\n </span>\n {visibleCompanies.map((company) => (\n <Link\n key={company.id}\n href={`/backend/customers/companies-v2/${company.id}`}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-sm border px-2 py-0.5 text-xs font-semibold transition-colors hover:bg-status-info-bg',\n company.isPrimary\n ? 'border-status-info-border bg-status-info-bg text-status-info-text'\n : 'border-border bg-background text-foreground',\n )}\n >\n <Building2 className=\"size-3\" />\n {company.displayName}\n {company.isPrimary ? (\n <span className=\"rounded-sm bg-status-info-icon px-1 py-px text-overline font-bold text-white\">\n {t('customers.people.detail.header.primary', 'PRIMARY')}\n </span>\n ) : null}\n </Link>\n ))}\n {hiddenCompaniesCount > 0 ? (\n <Badge variant=\"outline\" className=\"rounded-sm text-xs font-semibold\">\n +{hiddenCompaniesCount} {t('customers.people.detail.header.more', 'more')}\n </Badge>\n ) : null}\n </div>\n )}\n\n {/* Status badges + inline tags + manage tags */}\n <div className=\"mt-2.5 flex flex-wrap items-center gap-2\">\n {person.status && (\n <DictionaryBadge value={person.status} map={statusDict?.map} />\n )}\n {person.lifecycleStage && (\n <DictionaryBadge value={person.lifecycleStage} map={lifecycleDict?.map} />\n )}\n {person.source && (\n <DictionaryBadge value={person.source} map={sourceDict?.map} />\n )}\n {person.temperature && (\n <DictionaryBadge value={person.temperature} map={temperatureDict?.map} />\n )}\n {person.renewalQuarter && (\n <DictionaryBadge value={person.renewalQuarter} map={renewalQuarterDict?.map} />\n )}\n {/* Inline tag pills */}\n {visibleTags.map((tag) => (\n <TagBadge key={tag.id ?? tag.label} tag={tag} />\n ))}\n {hiddenTagsCount > 0 ? (\n <Badge variant=\"outline\" className=\"rounded-sm gap-1.5 text-xs font-medium\">\n +{hiddenTagsCount} {t('customers.people.detail.header.more', 'more')}\n </Badge>\n ) : null}\n {/* Manage tags \u2014 opens dialog directly */}\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-auto rounded-sm px-2 py-1 text-xs font-medium text-muted-foreground hover:text-foreground\"\n onClick={() => setManageTagsOpen(true)}\n >\n <Pencil className=\"mr-1 size-3\" />\n {t('customers.people.detail.actions.manageTags', 'Edit tags')}\n </Button>\n </div>\n </div>\n\n {/* Right side: actions */}\n <div className=\"flex w-full shrink-0 items-center justify-start gap-2 sm:w-auto sm:justify-end\">\n <SendObjectMessageDialog\n object={{\n entityModule: 'customers',\n entityType: 'person',\n entityId: person.id,\n previewData: {\n title: displayName,\n subtitle: person.primaryEmail ?? companyName ?? undefined,\n },\n }}\n viewHref={`/backend/customers/people-v2/${person.id}`}\n buttonVariant=\"outline\"\n buttonSize=\"icon\"\n buttonClassName={HEADER_ICON_BUTTON_CLASS}\n buttonLabel={t('customers.people.detail.actions.sendMessage', 'Send message')}\n />\n <ObjectHistoryButton\n resourceKind=\"customers.person\"\n resourceId={person.id}\n organizationId={person.organizationId ?? undefined}\n />\n <IconButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n aria-label={t('customers.people.detail.actions.delete', 'Delete')}\n onClick={() => {\n void onDelete()\n }}\n >\n <Trash2 className=\"size-4\" />\n </IconButton>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={onSave}\n disabled={!isDirty || isSaving}\n >\n {t('customers.people.detail.actions.save', 'Save')}\n </Button>\n </div>\n </div>\n <PersonTagsDialog\n open={manageTagsOpen}\n onClose={() => setManageTagsOpen(false)}\n personId={person.id}\n personOrganizationId={person.organizationId ?? null}\n personData={{\n status: person.status,\n lifecycleStage: person.lifecycleStage,\n source: person.source,\n temperature: person.temperature,\n renewalQuarter: person.renewalQuarter,\n jobTitle: data.profile?.jobTitle ?? null,\n customFields: data.customFields,\n tags: data.tags,\n }}\n onSaved={() => {\n // Invalidate dictionary caches so header badges pick up fresh colors\n void invalidateCustomerDictionary(queryClient, 'statuses')\n void invalidateCustomerDictionary(queryClient, 'lifecycle-stages')\n void invalidateCustomerDictionary(queryClient, 'sources')\n void invalidateCustomerDictionary(queryClient, 'temperature')\n void invalidateCustomerDictionary(queryClient, 'renewal-quarters')\n onDataReload?.()\n }}\n />\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7,11 +7,18 @@ import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
|
7
7
|
import { apiCallOrThrow, readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
8
8
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
9
9
|
import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
|
|
10
|
+
import { useBackendChrome } from "@open-mercato/ui/backend/BackendChromeProvider";
|
|
10
11
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
12
|
+
import { hasFeature } from "@open-mercato/shared/security/features";
|
|
11
13
|
import { RoleAssignmentRow } from "./RoleAssignmentRow.js";
|
|
12
14
|
import { AssignRoleDialog } from "./AssignRoleDialog.js";
|
|
13
15
|
function RolesSection({ entityType, entityId, entityName }) {
|
|
14
16
|
const t = useT();
|
|
17
|
+
const { payload } = useBackendChrome();
|
|
18
|
+
const canManageRoleTypes = React.useMemo(
|
|
19
|
+
() => hasFeature(payload?.grantedFeatures ?? [], "customers.settings.manage"),
|
|
20
|
+
[payload?.grantedFeatures]
|
|
21
|
+
);
|
|
15
22
|
const [roles, setRoles] = React.useState([]);
|
|
16
23
|
const [roleTypes, setRoleTypes] = React.useState([]);
|
|
17
24
|
const [loading, setLoading] = React.useState(true);
|
|
@@ -68,9 +75,9 @@ function RolesSection({ entityType, entityId, entityName }) {
|
|
|
68
75
|
let active = true;
|
|
69
76
|
readApiResultOrThrow(
|
|
70
77
|
"/api/customers/dictionaries/person-company-roles"
|
|
71
|
-
).then((
|
|
78
|
+
).then((payload2) => {
|
|
72
79
|
if (!active) return;
|
|
73
|
-
const entries = (Array.isArray(
|
|
80
|
+
const entries = (Array.isArray(payload2?.items) ? payload2.items : []).map((item) => {
|
|
74
81
|
const id = typeof item.id === "string" ? item.id : null;
|
|
75
82
|
const value = typeof item.value === "string" ? item.value.trim() : "";
|
|
76
83
|
if (!id || value.length === 0) return null;
|
|
@@ -135,9 +142,11 @@ function RolesSection({ entityType, entityId, entityName }) {
|
|
|
135
142
|
if (loading) {
|
|
136
143
|
return /* @__PURE__ */ jsx("div", { className: "py-2 text-sm text-muted-foreground", children: t("customers.roles.loading", "Loading roles...") });
|
|
137
144
|
}
|
|
145
|
+
const resolvedEntityName = entityName && entityName.trim().length ? entityName.trim() : entityType === "company" ? t("customers.roles.defaultEntityName.company", "this company") : t("customers.roles.defaultEntityName.person", "this person");
|
|
146
|
+
const groupTitle = entityType === "company" ? t("customers.roles.groupTitle.company", "Roles at {{name}}", { name: resolvedEntityName }) : t("customers.roles.groupTitle.person", "My roles with {{name}}", { name: resolvedEntityName });
|
|
138
147
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
139
148
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
140
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold", children:
|
|
149
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold", children: groupTitle }),
|
|
141
150
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: entityType === "company" ? t("customers.roles.subtitle.company", "Who is responsible for this company on your side") : t("customers.roles.subtitle.person", "Who owns the relationship on your side") })
|
|
142
151
|
] }),
|
|
143
152
|
cards.length > 0 ? /* @__PURE__ */ jsx(
|
|
@@ -223,7 +232,8 @@ function RolesSection({ entityType, entityId, entityName }) {
|
|
|
223
232
|
entityName: entityName && entityName.trim().length ? entityName : entityType === "company" ? t("customers.roles.dialog.defaultEntity.company", "this company") : t("customers.roles.dialog.defaultEntity.person", "this person"),
|
|
224
233
|
existingRoleTypes: assignedRoleTypes,
|
|
225
234
|
existingAssignments: roles,
|
|
226
|
-
initialRoleType
|
|
235
|
+
initialRoleType,
|
|
236
|
+
canManageRoleTypes
|
|
227
237
|
}
|
|
228
238
|
)
|
|
229
239
|
] });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/components/detail/RolesSection.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Plus } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport type { DictionaryEntryOption } from '@open-mercato/core/modules/dictionaries/lib/clientEntries'\nimport { RoleAssignmentRow, type RoleAssignment } from './RoleAssignmentRow'\nimport { AssignRoleDialog } from './AssignRoleDialog'\n\ninterface RolesSectionProps {\n entityType: 'company' | 'person'\n entityId: string\n entityName?: string | null\n}\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\nexport function RolesSection({ entityType, entityId, entityName }: RolesSectionProps) {\n const t = useT()\n const [roles, setRoles] = React.useState<RoleAssignment[]>([])\n const [roleTypes, setRoleTypes] = React.useState<DictionaryEntryOption[]>([])\n const [loading, setLoading] = React.useState(true)\n const [dialogOpen, setDialogOpen] = React.useState(false)\n const [initialRoleType, setInitialRoleType] = React.useState<string | null>(null)\n const hasConfiguredRoleTypes = roleTypes.length > 0\n\n const basePath = entityType === 'company' ? 'companies' : 'people'\n const resourceKind = entityType === 'company' ? 'customers.company' : 'customers.person'\n const mutationContextId = React.useMemo(\n () => `customer-roles:${entityType}:${entityId}`,\n [entityId, entityType],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n entityType: 'company' | 'person'\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: mutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const mutationContext = React.useMemo(\n () => ({\n formId: mutationContextId,\n resourceKind,\n resourceId: entityId,\n entityType,\n retryLastMutation,\n }),\n [entityId, entityType, mutationContextId, resourceKind, retryLastMutation],\n )\n const runMutationWithContext = React.useCallback<GuardedMutationRunner>(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>) => {\n return runMutation({\n operation,\n mutationPayload,\n context: mutationContext,\n })\n },\n [mutationContext, runMutation],\n )\n\n const loadRoles = React.useCallback(async () => {\n try {\n const data = await readApiResultOrThrow<{ items?: RoleAssignment[] }>(\n `/api/customers/${basePath}/${entityId}/roles`,\n )\n setRoles(Array.isArray(data?.items) ? data.items : [])\n } catch (error) {\n console.error('customers.roles.load failed', error)\n flash(t('customers.roles.loadFailed', 'Failed to load role assignments.'), 'error')\n setRoles([])\n }\n setLoading(false)\n }, [basePath, entityId, t])\n\n React.useEffect(() => {\n loadRoles()\n }, [loadRoles])\n\n React.useEffect(() => {\n let active = true\n\n readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n '/api/customers/dictionaries/person-company-roles',\n )\n .then((payload) => {\n if (!active) return\n const entries = (Array.isArray(payload?.items) ? payload.items : [])\n .map((item) => {\n const id = typeof item.id === 'string' ? item.id : null\n const value = typeof item.value === 'string' ? item.value.trim() : ''\n if (!id || value.length === 0) return null\n return {\n id,\n value,\n label:\n typeof item.label === 'string' && item.label.trim().length > 0\n ? item.label.trim()\n : value,\n color: typeof item.color === 'string' ? item.color : null,\n icon: typeof item.icon === 'string' ? item.icon : null,\n } satisfies DictionaryEntryOption\n })\n .filter((entry): entry is DictionaryEntryOption => entry !== null)\n\n setRoleTypes(entries)\n })\n .catch((error) => {\n if (!active) return\n console.error('customers.roles.roleTypes failed', error)\n setRoleTypes([])\n })\n\n return () => {\n active = false\n }\n }, [])\n\n const assignedRoleTypes = React.useMemo(\n () => new Set(roles.map((role) => role.roleType)),\n [roles],\n )\n\n const handleDialogAssign = React.useCallback(async (roleType: string, userId: string) => {\n await runMutationWithContext(\n () =>\n apiCallOrThrow(`/api/customers/${basePath}/${entityId}/roles`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ roleType, userId }),\n }),\n { roleType, userId },\n )\n flash(t('customers.roles.assigned', 'Role assigned'), 'success')\n await loadRoles()\n }, [basePath, entityId, loadRoles, runMutationWithContext, t])\n\n const getRoleTypeLabel = React.useCallback(\n (value: string) => roleTypes.find((roleType) => roleType.value === value)?.label ?? value,\n [roleTypes],\n )\n\n const cards = React.useMemo(() => {\n const roleByType = new Map(roles.map((role) => [role.roleType, role]))\n const configuredCards = roleTypes.map((roleType) => ({\n roleType: roleType.value,\n roleTypeLabel: roleType.label,\n role: roleByType.get(roleType.value) ?? null,\n }))\n const configuredRoleTypes = new Set(roleTypes.map((roleType) => roleType.value))\n const extraAssignedCards = roles\n .filter((role) => !configuredRoleTypes.has(role.roleType))\n .map((role) => ({\n roleType: role.roleType,\n roleTypeLabel: getRoleTypeLabel(role.roleType),\n role,\n }))\n\n return [...configuredCards, ...extraAssignedCards]\n }, [getRoleTypeLabel, roleTypes, roles])\n\n const openDialog = React.useCallback((roleType?: string | null) => {\n if (!hasConfiguredRoleTypes) return\n setInitialRoleType(roleType ?? null)\n setDialogOpen(true)\n }, [hasConfiguredRoleTypes])\n\n if (loading) {\n return (\n <div className=\"py-2 text-sm text-muted-foreground\">\n {t('customers.roles.loading', 'Loading roles...')}\n </div>\n )\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"space-y-1\">\n <div className=\"text-sm font-semibold\">{t('customers.roles.groupTitle', 'Roles')}</div>\n <p className=\"text-xs text-muted-foreground\">\n {entityType === 'company'\n ? t('customers.roles.subtitle.company', 'Who is responsible for this company on your side')\n : t('customers.roles.subtitle.person', 'Who owns the relationship on your side')}\n </p>\n </div>\n\n {cards.length > 0 ? (\n <div\n className=\"grid items-start gap-4\"\n style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 18rem), 1fr))' }}\n >\n {cards.map((entry) =>\n entry.role ? (\n <RoleAssignmentRow\n key={entry.role.id}\n role={entry.role}\n roleTypeLabel={entry.roleTypeLabel}\n runMutationWithContext={runMutationWithContext}\n entityType={entityType}\n entityId={entityId}\n onRemoved={loadRoles}\n onUpdated={loadRoles}\n />\n ) : (\n <div key={entry.roleType} className=\"flex h-full min-w-0 flex-col rounded-xl border border-dashed bg-muted/20 p-4\">\n <div className=\"break-words text-overline font-semibold uppercase tracking-[0.08em] text-muted-foreground\">\n {entry.roleTypeLabel}\n </div>\n <div className=\"mt-4 text-sm font-medium text-foreground\">\n {t('customers.roles.emptySlot', 'Not assigned')}\n </div>\n <p className=\"mt-2 text-xs text-muted-foreground\">\n {t('customers.roles.emptyState', 'No roles assigned yet. Click below to assign a person.')}\n </p>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"mt-4 self-start\"\n onClick={() => openDialog(entry.roleType)}\n >\n {t('customers.roles.choosePerson', 'Choose person')}\n </Button>\n </div>\n ),\n )}\n </div>\n ) : hasConfiguredRoleTypes ? (\n <div className=\"rounded-lg border border-dashed p-4 text-center\">\n <p className=\"text-xs text-muted-foreground\">\n {t('customers.roles.emptyState', 'No roles assigned yet. Click below to assign a person.')}\n </p>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"mt-2\"\n onClick={() => openDialog(null)}\n >\n {t('customers.roles.choosePerson', 'Choose person')}\n </Button>\n </div>\n ) : (\n <div className=\"rounded-lg border border-dashed p-4 text-center\">\n <div className=\"text-sm font-medium text-foreground\">\n {t('customers.roles.noRoleTypesTitle', 'No role types configured')}\n </div>\n <p className=\"mt-2 text-xs text-muted-foreground\">\n {t(\n 'customers.roles.noRoleTypesDescription',\n 'Create role types in Customers config before assigning owners here.',\n )}\n </p>\n <Button asChild type=\"button\" variant=\"outline\" size=\"sm\" className=\"mt-3\">\n <Link href=\"/backend/config/customers\">\n {t('customers.roles.configureRoleTypes', 'Configure role types')}\n </Link>\n </Button>\n </div>\n )}\n\n {hasConfiguredRoleTypes ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => openDialog(null)}\n >\n <Plus className=\"mr-1 size-3.5\" />\n {t('customers.roles.addRole', 'Add role')}\n </Button>\n ) : (\n <Button asChild type=\"button\" variant=\"ghost\" size=\"sm\">\n <Link href=\"/backend/config/customers\">\n {t('customers.roles.configureRoleTypes', 'Configure role types')}\n </Link>\n </Button>\n )}\n\n <AssignRoleDialog\n open={dialogOpen}\n onClose={() => {\n setDialogOpen(false)\n setInitialRoleType(null)\n }}\n onAssign={handleDialogAssign}\n roleTypes={roleTypes}\n entityName={\n entityName && entityName.trim().length\n ? entityName\n : entityType === 'company'\n ? t('customers.roles.dialog.defaultEntity.company', 'this company')\n : t('customers.roles.dialog.defaultEntity.person', 'this person')\n }\n existingRoleTypes={assignedRoleTypes}\n existingAssignments={roles}\n initialRoleType={initialRoleType}\n />\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Plus } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useBackendChrome } from '@open-mercato/ui/backend/BackendChromeProvider'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { hasFeature } from '@open-mercato/shared/security/features'\nimport type { DictionaryEntryOption } from '@open-mercato/core/modules/dictionaries/lib/clientEntries'\nimport { RoleAssignmentRow, type RoleAssignment } from './RoleAssignmentRow'\nimport { AssignRoleDialog } from './AssignRoleDialog'\n\ninterface RolesSectionProps {\n entityType: 'company' | 'person'\n entityId: string\n entityName?: string | null\n}\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\nexport function RolesSection({ entityType, entityId, entityName }: RolesSectionProps) {\n const t = useT()\n const { payload } = useBackendChrome()\n const canManageRoleTypes = React.useMemo(\n () => hasFeature(payload?.grantedFeatures ?? [], 'customers.settings.manage'),\n [payload?.grantedFeatures],\n )\n const [roles, setRoles] = React.useState<RoleAssignment[]>([])\n const [roleTypes, setRoleTypes] = React.useState<DictionaryEntryOption[]>([])\n const [loading, setLoading] = React.useState(true)\n const [dialogOpen, setDialogOpen] = React.useState(false)\n const [initialRoleType, setInitialRoleType] = React.useState<string | null>(null)\n const hasConfiguredRoleTypes = roleTypes.length > 0\n\n const basePath = entityType === 'company' ? 'companies' : 'people'\n const resourceKind = entityType === 'company' ? 'customers.company' : 'customers.person'\n const mutationContextId = React.useMemo(\n () => `customer-roles:${entityType}:${entityId}`,\n [entityId, entityType],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n entityType: 'company' | 'person'\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: mutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const mutationContext = React.useMemo(\n () => ({\n formId: mutationContextId,\n resourceKind,\n resourceId: entityId,\n entityType,\n retryLastMutation,\n }),\n [entityId, entityType, mutationContextId, resourceKind, retryLastMutation],\n )\n const runMutationWithContext = React.useCallback<GuardedMutationRunner>(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>) => {\n return runMutation({\n operation,\n mutationPayload,\n context: mutationContext,\n })\n },\n [mutationContext, runMutation],\n )\n\n const loadRoles = React.useCallback(async () => {\n try {\n const data = await readApiResultOrThrow<{ items?: RoleAssignment[] }>(\n `/api/customers/${basePath}/${entityId}/roles`,\n )\n setRoles(Array.isArray(data?.items) ? data.items : [])\n } catch (error) {\n console.error('customers.roles.load failed', error)\n flash(t('customers.roles.loadFailed', 'Failed to load role assignments.'), 'error')\n setRoles([])\n }\n setLoading(false)\n }, [basePath, entityId, t])\n\n React.useEffect(() => {\n loadRoles()\n }, [loadRoles])\n\n React.useEffect(() => {\n let active = true\n\n readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n '/api/customers/dictionaries/person-company-roles',\n )\n .then((payload) => {\n if (!active) return\n const entries = (Array.isArray(payload?.items) ? payload.items : [])\n .map((item) => {\n const id = typeof item.id === 'string' ? item.id : null\n const value = typeof item.value === 'string' ? item.value.trim() : ''\n if (!id || value.length === 0) return null\n return {\n id,\n value,\n label:\n typeof item.label === 'string' && item.label.trim().length > 0\n ? item.label.trim()\n : value,\n color: typeof item.color === 'string' ? item.color : null,\n icon: typeof item.icon === 'string' ? item.icon : null,\n } satisfies DictionaryEntryOption\n })\n .filter((entry): entry is DictionaryEntryOption => entry !== null)\n\n setRoleTypes(entries)\n })\n .catch((error) => {\n if (!active) return\n console.error('customers.roles.roleTypes failed', error)\n setRoleTypes([])\n })\n\n return () => {\n active = false\n }\n }, [])\n\n const assignedRoleTypes = React.useMemo(\n () => new Set(roles.map((role) => role.roleType)),\n [roles],\n )\n\n const handleDialogAssign = React.useCallback(async (roleType: string, userId: string) => {\n await runMutationWithContext(\n () =>\n apiCallOrThrow(`/api/customers/${basePath}/${entityId}/roles`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ roleType, userId }),\n }),\n { roleType, userId },\n )\n flash(t('customers.roles.assigned', 'Role assigned'), 'success')\n await loadRoles()\n }, [basePath, entityId, loadRoles, runMutationWithContext, t])\n\n const getRoleTypeLabel = React.useCallback(\n (value: string) => roleTypes.find((roleType) => roleType.value === value)?.label ?? value,\n [roleTypes],\n )\n\n const cards = React.useMemo(() => {\n const roleByType = new Map(roles.map((role) => [role.roleType, role]))\n const configuredCards = roleTypes.map((roleType) => ({\n roleType: roleType.value,\n roleTypeLabel: roleType.label,\n role: roleByType.get(roleType.value) ?? null,\n }))\n const configuredRoleTypes = new Set(roleTypes.map((roleType) => roleType.value))\n const extraAssignedCards = roles\n .filter((role) => !configuredRoleTypes.has(role.roleType))\n .map((role) => ({\n roleType: role.roleType,\n roleTypeLabel: getRoleTypeLabel(role.roleType),\n role,\n }))\n\n return [...configuredCards, ...extraAssignedCards]\n }, [getRoleTypeLabel, roleTypes, roles])\n\n const openDialog = React.useCallback((roleType?: string | null) => {\n if (!hasConfiguredRoleTypes) return\n setInitialRoleType(roleType ?? null)\n setDialogOpen(true)\n }, [hasConfiguredRoleTypes])\n\n if (loading) {\n return (\n <div className=\"py-2 text-sm text-muted-foreground\">\n {t('customers.roles.loading', 'Loading roles...')}\n </div>\n )\n }\n\n const resolvedEntityName =\n entityName && entityName.trim().length\n ? entityName.trim()\n : entityType === 'company'\n ? t('customers.roles.defaultEntityName.company', 'this company')\n : t('customers.roles.defaultEntityName.person', 'this person')\n const groupTitle =\n entityType === 'company'\n ? t('customers.roles.groupTitle.company', 'Roles at {{name}}', { name: resolvedEntityName })\n : t('customers.roles.groupTitle.person', 'My roles with {{name}}', { name: resolvedEntityName })\n\n return (\n <div className=\"space-y-4\">\n <div className=\"space-y-1\">\n <div className=\"text-sm font-semibold\">{groupTitle}</div>\n <p className=\"text-xs text-muted-foreground\">\n {entityType === 'company'\n ? t('customers.roles.subtitle.company', 'Who is responsible for this company on your side')\n : t('customers.roles.subtitle.person', 'Who owns the relationship on your side')}\n </p>\n </div>\n\n {cards.length > 0 ? (\n <div\n className=\"grid items-start gap-4\"\n style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 18rem), 1fr))' }}\n >\n {cards.map((entry) =>\n entry.role ? (\n <RoleAssignmentRow\n key={entry.role.id}\n role={entry.role}\n roleTypeLabel={entry.roleTypeLabel}\n runMutationWithContext={runMutationWithContext}\n entityType={entityType}\n entityId={entityId}\n onRemoved={loadRoles}\n onUpdated={loadRoles}\n />\n ) : (\n <div key={entry.roleType} className=\"flex h-full min-w-0 flex-col rounded-xl border border-dashed bg-muted/20 p-4\">\n <div className=\"break-words text-overline font-semibold uppercase tracking-[0.08em] text-muted-foreground\">\n {entry.roleTypeLabel}\n </div>\n <div className=\"mt-4 text-sm font-medium text-foreground\">\n {t('customers.roles.emptySlot', 'Not assigned')}\n </div>\n <p className=\"mt-2 text-xs text-muted-foreground\">\n {t('customers.roles.emptyState', 'No roles assigned yet. Click below to assign a person.')}\n </p>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"mt-4 self-start\"\n onClick={() => openDialog(entry.roleType)}\n >\n {t('customers.roles.choosePerson', 'Choose person')}\n </Button>\n </div>\n ),\n )}\n </div>\n ) : hasConfiguredRoleTypes ? (\n <div className=\"rounded-lg border border-dashed p-4 text-center\">\n <p className=\"text-xs text-muted-foreground\">\n {t('customers.roles.emptyState', 'No roles assigned yet. Click below to assign a person.')}\n </p>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"mt-2\"\n onClick={() => openDialog(null)}\n >\n {t('customers.roles.choosePerson', 'Choose person')}\n </Button>\n </div>\n ) : (\n <div className=\"rounded-lg border border-dashed p-4 text-center\">\n <div className=\"text-sm font-medium text-foreground\">\n {t('customers.roles.noRoleTypesTitle', 'No role types configured')}\n </div>\n <p className=\"mt-2 text-xs text-muted-foreground\">\n {t(\n 'customers.roles.noRoleTypesDescription',\n 'Create role types in Customers config before assigning owners here.',\n )}\n </p>\n <Button asChild type=\"button\" variant=\"outline\" size=\"sm\" className=\"mt-3\">\n <Link href=\"/backend/config/customers\">\n {t('customers.roles.configureRoleTypes', 'Configure role types')}\n </Link>\n </Button>\n </div>\n )}\n\n {hasConfiguredRoleTypes ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => openDialog(null)}\n >\n <Plus className=\"mr-1 size-3.5\" />\n {t('customers.roles.addRole', 'Add role')}\n </Button>\n ) : (\n <Button asChild type=\"button\" variant=\"ghost\" size=\"sm\">\n <Link href=\"/backend/config/customers\">\n {t('customers.roles.configureRoleTypes', 'Configure role types')}\n </Link>\n </Button>\n )}\n\n <AssignRoleDialog\n open={dialogOpen}\n onClose={() => {\n setDialogOpen(false)\n setInitialRoleType(null)\n }}\n onAssign={handleDialogAssign}\n roleTypes={roleTypes}\n entityName={\n entityName && entityName.trim().length\n ? entityName\n : entityType === 'company'\n ? t('customers.roles.dialog.defaultEntity.company', 'this company')\n : t('customers.roles.dialog.defaultEntity.person', 'this person')\n }\n existingRoleTypes={assignedRoleTypes}\n existingAssignments={roles}\n initialRoleType={initialRoleType}\n canManageRoleTypes={canManageRoleTypes}\n />\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["payload"]
|
|
7
7
|
}
|
|
@@ -1157,7 +1157,14 @@ const createCompanyEditGroups = (t) => [
|
|
|
1157
1157
|
id: "roles",
|
|
1158
1158
|
title: t("customers.roles.groupTitle", "Roles"),
|
|
1159
1159
|
column: 1,
|
|
1160
|
-
component: ({ values }) => values.id ? /* @__PURE__ */ jsx(
|
|
1160
|
+
component: ({ values }) => values.id ? /* @__PURE__ */ jsx(
|
|
1161
|
+
RolesSection,
|
|
1162
|
+
{
|
|
1163
|
+
entityType: "company",
|
|
1164
|
+
entityId: values.id,
|
|
1165
|
+
entityName: typeof values.displayName === "string" ? values.displayName : null
|
|
1166
|
+
}
|
|
1167
|
+
) : null
|
|
1161
1168
|
},
|
|
1162
1169
|
{
|
|
1163
1170
|
id: "profile",
|
|
@@ -1240,7 +1247,14 @@ const createPersonEditGroups = (t) => [
|
|
|
1240
1247
|
id: "roles",
|
|
1241
1248
|
title: t("customers.roles.groupTitle", "Roles"),
|
|
1242
1249
|
column: 1,
|
|
1243
|
-
component: ({ values }) => values.id ? /* @__PURE__ */ jsx(
|
|
1250
|
+
component: ({ values }) => values.id ? /* @__PURE__ */ jsx(
|
|
1251
|
+
RolesSection,
|
|
1252
|
+
{
|
|
1253
|
+
entityType: "person",
|
|
1254
|
+
entityId: values.id,
|
|
1255
|
+
entityName: typeof values.displayName === "string" ? values.displayName : null
|
|
1256
|
+
}
|
|
1257
|
+
) : null
|
|
1244
1258
|
},
|
|
1245
1259
|
{
|
|
1246
1260
|
id: "social",
|