@open-mercato/core 0.6.1-develop.3246.1.dbef9d7392 → 0.6.1-develop.3260.1.dc719cac84
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/helpers/integration/authFixtures.js.map +1 -1
- package/dist/helpers/integration/authUi.js +10 -2
- package/dist/helpers/integration/authUi.js.map +2 -2
- package/dist/helpers/integration/salesUi.js +3 -1
- package/dist/helpers/integration/salesUi.js.map +2 -2
- package/dist/modules/api_docs/frontend/docs/api/Explorer.js +10 -1
- package/dist/modules/api_docs/frontend/docs/api/Explorer.js.map +2 -2
- package/dist/modules/audit_logs/lib/display-helpers.js +28 -27
- package/dist/modules/audit_logs/lib/display-helpers.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +21 -3
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +6 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +4 -1
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +56 -2
- package/dist/modules/auth/backend/users/page.js.map +3 -3
- package/dist/modules/auth/commands/users.js +14 -0
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/validators.js +8 -0
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/catalog/ai-agents.js +4 -0
- package/dist/modules/catalog/ai-agents.js.map +2 -2
- package/dist/modules/customers/ai-agents.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +1 -1
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +15 -9
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +26 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +1 -1
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/components/PipelineSettings.js +108 -99
- package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
- package/dist/modules/customers/components/detail/AssignRoleDialog.js +13 -5
- package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js +10 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js +15 -8
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/RolesSection.js +35 -22
- package/dist/modules/customers/components/detail/RolesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/TasksSection.js +2 -2
- package/dist/modules/customers/components/detail/TasksSection.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +73 -64
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +2 -2
- package/dist/modules/customers/components/linking/LinkEntityDialog.js +11 -2
- package/dist/modules/customers/components/linking/LinkEntityDialog.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +2 -9
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +14 -29
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js +10 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
- package/dist/modules/entities/components/EncryptionManager.js +40 -17
- package/dist/modules/entities/components/EncryptionManager.js.map +2 -2
- package/dist/modules/inbox_ops/lib/llmProvider.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +79 -95
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js +49 -70
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRuleSetForm.js +1 -1
- package/dist/modules/planner/components/AvailabilityRuleSetForm.js.map +1 -1
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +19 -19
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/resources/components/ResourceCrudForm.js +1 -1
- package/dist/modules/resources/components/ResourceCrudForm.js.map +1 -1
- package/dist/modules/resources/components/ResourceTypeCrudForm.js +1 -1
- package/dist/modules/resources/components/ResourceTypeCrudForm.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnDialog.js +11 -8
- package/dist/modules/sales/components/documents/ReturnDialog.js.map +2 -2
- package/dist/modules/staff/backend/staff/profile/create/page.js +1 -1
- package/dist/modules/staff/backend/staff/profile/create/page.js.map +2 -2
- package/dist/modules/staff/components/TeamForm.js +1 -1
- package/dist/modules/staff/components/TeamForm.js.map +2 -2
- package/dist/modules/staff/components/TeamMemberForm.js +1 -1
- package/dist/modules/staff/components/TeamMemberForm.js.map +1 -1
- package/dist/modules/staff/components/TeamRoleForm.js +1 -1
- package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
- package/package.json +5 -5
- package/src/helpers/integration/authFixtures.ts +1 -1
- package/src/helpers/integration/authUi.ts +11 -2
- package/src/helpers/integration/salesUi.ts +10 -1
- package/src/modules/api_docs/frontend/docs/api/Explorer.tsx +7 -3
- package/src/modules/audit_logs/lib/display-helpers.tsx +22 -14
- package/src/modules/auth/api/users/route.ts +22 -3
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +9 -1
- package/src/modules/auth/backend/users/create/page.tsx +6 -2
- package/src/modules/auth/backend/users/page.tsx +60 -3
- package/src/modules/auth/commands/users.ts +15 -0
- package/src/modules/auth/data/validators.ts +8 -0
- package/src/modules/auth/i18n/de.json +4 -0
- package/src/modules/auth/i18n/en.json +4 -0
- package/src/modules/auth/i18n/es.json +4 -0
- package/src/modules/auth/i18n/pl.json +4 -0
- package/src/modules/catalog/ai-agents.ts +8 -49
- package/src/modules/customers/ai-agents.ts +4 -51
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +1 -1
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +12 -13
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +17 -13
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +1 -1
- package/src/modules/customers/components/PipelineSettings.tsx +95 -95
- package/src/modules/customers/components/detail/AssignRoleDialog.tsx +7 -4
- package/src/modules/customers/components/detail/EntityTagsDialog.tsx +13 -8
- package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +16 -12
- package/src/modules/customers/components/detail/RolesSection.tsx +34 -31
- package/src/modules/customers/components/detail/TasksSection.tsx +16 -8
- package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +77 -51
- package/src/modules/customers/components/linking/LinkEntityDialog.tsx +7 -3
- package/src/modules/data_sync/backend/data-sync/page.tsx +4 -7
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +19 -54
- package/src/modules/dictionaries/components/DictionariesManager.tsx +7 -4
- package/src/modules/entities/components/EncryptionManager.tsx +50 -21
- package/src/modules/inbox_ops/lib/llmProvider.ts +10 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +101 -146
- package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +60 -103
- package/src/modules/planner/components/AvailabilityRuleSetForm.tsx +1 -1
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +17 -17
- package/src/modules/resources/components/ResourceCrudForm.tsx +1 -1
- package/src/modules/resources/components/ResourceTypeCrudForm.tsx +1 -1
- package/src/modules/sales/components/documents/ReturnDialog.tsx +10 -7
- package/src/modules/sales/i18n/de.json +2 -0
- package/src/modules/sales/i18n/en.json +2 -0
- package/src/modules/sales/i18n/es.json +2 -0
- package/src/modules/sales/i18n/pl.json +2 -0
- package/src/modules/staff/backend/staff/profile/create/page.tsx +1 -1
- package/src/modules/staff/components/TeamForm.tsx +1 -1
- package/src/modules/staff/components/TeamMemberForm.tsx +1 -1
- package/src/modules/staff/components/TeamRoleForm.tsx +1 -1
|
@@ -172,6 +172,7 @@
|
|
|
172
172
|
"auth.users.form.errors.notFound": "User not found",
|
|
173
173
|
"auth.users.form.errors.widgetsAssign": "Failed to assign dashboard widgets to the new user",
|
|
174
174
|
"auth.users.form.field.email": "Email",
|
|
175
|
+
"auth.users.form.field.name": "Display name",
|
|
175
176
|
"auth.users.form.field.newPassword": "New Password",
|
|
176
177
|
"auth.users.form.field.organization": "Organization",
|
|
177
178
|
"auth.users.form.field.password": "Password",
|
|
@@ -190,8 +191,11 @@
|
|
|
190
191
|
"auth.users.form.loading": "Loading user data...",
|
|
191
192
|
"auth.users.form.title.create": "Create User",
|
|
192
193
|
"auth.users.form.title.edit": "Edit User",
|
|
194
|
+
"auth.users.list.columns.name": "Display name",
|
|
193
195
|
"auth.users.list.error.delete": "Failed to delete user",
|
|
194
196
|
"auth.users.list.error.load": "Failed to load users",
|
|
197
|
+
"auth.users.list.filters.name": "Display name",
|
|
198
|
+
"auth.users.list.filters.namePlaceholder": "Filter by display name",
|
|
195
199
|
"auth.users.widgets.errors.load": "Unable to load dashboard widgets. You can configure them later from the user page.",
|
|
196
200
|
"auth.users.widgets.loading": "Loading widgets…",
|
|
197
201
|
"auth.users.widgets.mode.hint": "New users inherit widgets from their assigned roles. Override to pick a custom set.",
|
|
@@ -172,6 +172,7 @@
|
|
|
172
172
|
"auth.users.form.errors.notFound": "Usuario no encontrado",
|
|
173
173
|
"auth.users.form.errors.widgetsAssign": "No se pudieron asignar los widgets del panel al nuevo usuario",
|
|
174
174
|
"auth.users.form.field.email": "Correo electrónico",
|
|
175
|
+
"auth.users.form.field.name": "Nombre visible",
|
|
175
176
|
"auth.users.form.field.newPassword": "Nueva contraseña",
|
|
176
177
|
"auth.users.form.field.organization": "Organización",
|
|
177
178
|
"auth.users.form.field.password": "Contraseña",
|
|
@@ -190,8 +191,11 @@
|
|
|
190
191
|
"auth.users.form.loading": "Cargando datos del usuario...",
|
|
191
192
|
"auth.users.form.title.create": "Crear usuario",
|
|
192
193
|
"auth.users.form.title.edit": "Editar usuario",
|
|
194
|
+
"auth.users.list.columns.name": "Nombre visible",
|
|
193
195
|
"auth.users.list.error.delete": "No se pudo eliminar el usuario",
|
|
194
196
|
"auth.users.list.error.load": "No se pudieron cargar los usuarios",
|
|
197
|
+
"auth.users.list.filters.name": "Nombre visible",
|
|
198
|
+
"auth.users.list.filters.namePlaceholder": "Filtrar por nombre visible",
|
|
195
199
|
"auth.users.widgets.errors.load": "No se pudieron cargar los widgets del panel. Puedes configurarlos más tarde desde la página del usuario.",
|
|
196
200
|
"auth.users.widgets.loading": "Cargando widgets…",
|
|
197
201
|
"auth.users.widgets.mode.hint": "Los nuevos usuarios heredan los widgets de las funciones asignadas. Sobrescribe para elegir un conjunto personalizado.",
|
|
@@ -172,6 +172,7 @@
|
|
|
172
172
|
"auth.users.form.errors.notFound": "Nie znaleziono użytkownika",
|
|
173
173
|
"auth.users.form.errors.widgetsAssign": "Nie udało się przypisać widżetów pulpitu do nowego użytkownika",
|
|
174
174
|
"auth.users.form.field.email": "Email",
|
|
175
|
+
"auth.users.form.field.name": "Nazwa wyświetlana",
|
|
175
176
|
"auth.users.form.field.newPassword": "Nowe hasło",
|
|
176
177
|
"auth.users.form.field.organization": "Organizacja",
|
|
177
178
|
"auth.users.form.field.password": "Hasło",
|
|
@@ -190,8 +191,11 @@
|
|
|
190
191
|
"auth.users.form.loading": "Ładowanie danych użytkownika...",
|
|
191
192
|
"auth.users.form.title.create": "Utwórz użytkownika",
|
|
192
193
|
"auth.users.form.title.edit": "Edytuj użytkownika",
|
|
194
|
+
"auth.users.list.columns.name": "Nazwa wyświetlana",
|
|
193
195
|
"auth.users.list.error.delete": "Nie udało się usunąć użytkownika",
|
|
194
196
|
"auth.users.list.error.load": "Nie udało się wczytać użytkowników",
|
|
197
|
+
"auth.users.list.filters.name": "Nazwa wyświetlana",
|
|
198
|
+
"auth.users.list.filters.namePlaceholder": "Filtruj po nazwie wyświetlanej",
|
|
195
199
|
"auth.users.widgets.errors.load": "Nie udało się załadować widżetów pulpitu. Możesz skonfigurować je później na stronie użytkownika.",
|
|
196
200
|
"auth.users.widgets.loading": "Ładowanie widżetów…",
|
|
197
201
|
"auth.users.widgets.mode.hint": "Nowi użytkownicy dziedziczą widżety z przypisanych ról. Zastąp wybór, aby wskazać własny zestaw.",
|
|
@@ -26,61 +26,16 @@
|
|
|
26
26
|
* prompt-override merges can address sections by name. The composed
|
|
27
27
|
* text is fed into `systemPrompt` so the current runtime continues to
|
|
28
28
|
* work.
|
|
29
|
-
*
|
|
30
|
-
* Local type declarations mirror the public shapes from
|
|
31
|
-
* `@open-mercato/ai-assistant`. `@open-mercato/core` does not depend on
|
|
32
|
-
* `@open-mercato/ai-assistant` (see the companion comment in
|
|
33
|
-
* `ai-tools/types.ts` and the Step 4.7 / 4.8 implementation notes), so
|
|
34
|
-
* the generator imports this file via the app's bundler and the runtime
|
|
35
|
-
* graph resolves through `apps/mercato/.mercato/generated/ai-agents.generated.ts`.
|
|
36
29
|
*/
|
|
37
|
-
import type {
|
|
30
|
+
import type {
|
|
31
|
+
AiAgentDefinition,
|
|
32
|
+
AiAgentPageContextInput,
|
|
33
|
+
} from '@open-mercato/ai-assistant/modules/ai_assistant/lib/ai-agent-definition'
|
|
38
34
|
import {
|
|
39
35
|
hydrateCatalogAssistantContext,
|
|
40
36
|
hydrateMerchandisingAssistantContext,
|
|
41
37
|
} from './ai-agents-context'
|
|
42
38
|
|
|
43
|
-
type AiAgentExecutionMode = 'chat' | 'object'
|
|
44
|
-
type AiAgentMutationPolicy = 'read-only' | 'confirm-required' | 'destructive-confirm-required'
|
|
45
|
-
type AiAgentAcceptedMediaType = 'image' | 'pdf' | 'file'
|
|
46
|
-
type AiAgentDataOperation = 'read' | 'search' | 'aggregate'
|
|
47
|
-
|
|
48
|
-
interface AiAgentPageContextInput {
|
|
49
|
-
entityType: string
|
|
50
|
-
recordId: string
|
|
51
|
-
container: AwilixContainer
|
|
52
|
-
tenantId: string | null
|
|
53
|
-
organizationId: string | null
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
interface AiAgentDataCapabilities {
|
|
57
|
-
entities?: string[]
|
|
58
|
-
operations?: AiAgentDataOperation[]
|
|
59
|
-
searchableFields?: string[]
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
interface AiAgentDefinition {
|
|
63
|
-
id: string
|
|
64
|
-
moduleId: string
|
|
65
|
-
label: string
|
|
66
|
-
description: string
|
|
67
|
-
systemPrompt: string
|
|
68
|
-
allowedTools: string[]
|
|
69
|
-
executionMode?: AiAgentExecutionMode
|
|
70
|
-
defaultModel?: string
|
|
71
|
-
acceptedMediaTypes?: AiAgentAcceptedMediaType[]
|
|
72
|
-
requiredFeatures?: string[]
|
|
73
|
-
uiParts?: string[]
|
|
74
|
-
readOnly?: boolean
|
|
75
|
-
mutationPolicy?: AiAgentMutationPolicy
|
|
76
|
-
maxSteps?: number
|
|
77
|
-
output?: unknown
|
|
78
|
-
resolvePageContext?: (ctx: AiAgentPageContextInput) => Promise<string | null>
|
|
79
|
-
keywords?: string[]
|
|
80
|
-
domain?: string
|
|
81
|
-
dataCapabilities?: AiAgentDataCapabilities
|
|
82
|
-
}
|
|
83
|
-
|
|
84
39
|
type PromptSectionName =
|
|
85
40
|
| 'role'
|
|
86
41
|
| 'scope'
|
|
@@ -314,6 +269,8 @@ const agent: AiAgentDefinition = {
|
|
|
314
269
|
requiredFeatures: [...REQUIRED_FEATURES],
|
|
315
270
|
readOnly: true,
|
|
316
271
|
mutationPolicy: 'read-only',
|
|
272
|
+
defaultProvider: 'openai',
|
|
273
|
+
defaultModel: 'gpt-5-mini',
|
|
317
274
|
keywords: ['catalog', 'products', 'categories', 'variants', 'prices', 'offers', 'media'],
|
|
318
275
|
domain: 'catalog',
|
|
319
276
|
dataCapabilities: {
|
|
@@ -506,6 +463,8 @@ const merchandisingAgent: AiAgentDefinition = {
|
|
|
506
463
|
// the operator via the pending-action approval card. Per-tenant override
|
|
507
464
|
// can downgrade to `read-only` to lock writes without a redeploy.
|
|
508
465
|
mutationPolicy: 'confirm-required',
|
|
466
|
+
defaultProvider: 'openai',
|
|
467
|
+
defaultModel: 'gpt-5-mini',
|
|
509
468
|
keywords: ['catalog', 'merchandising', 'products', 'attributes', 'pricing', 'copy'],
|
|
510
469
|
domain: 'catalog',
|
|
511
470
|
dataCapabilities: {
|
|
@@ -20,60 +20,13 @@
|
|
|
20
20
|
* the structured template is additionally exported so downstream Phases
|
|
21
21
|
* (5.3 prompt-override merge, 5.2 resolvePageContext hydration) can
|
|
22
22
|
* address sections by name.
|
|
23
|
-
*
|
|
24
|
-
* Local type declarations mirror the public shapes from
|
|
25
|
-
* `@open-mercato/ai-assistant`. The customers module does not depend on
|
|
26
|
-
* `@open-mercato/ai-assistant` (see the companion comment in
|
|
27
|
-
* `ai-tools/types.ts`) — the generator imports this file via the app's
|
|
28
|
-
* bundler, so the runtime graph resolves through
|
|
29
|
-
* `apps/mercato/.mercato/generated/ai-agents.generated.ts`. Keeping the
|
|
30
|
-
* type declarations local mirrors how the `customers/ai-tools/types.ts`
|
|
31
|
-
* handles `AiToolDefinition`.
|
|
32
23
|
*/
|
|
33
|
-
import type {
|
|
24
|
+
import type {
|
|
25
|
+
AiAgentDefinition,
|
|
26
|
+
AiAgentPageContextInput,
|
|
27
|
+
} from '@open-mercato/ai-assistant/modules/ai_assistant/lib/ai-agent-definition'
|
|
34
28
|
import { hydrateCustomersAccountContext } from './ai-agents-context'
|
|
35
29
|
|
|
36
|
-
type AiAgentExecutionMode = 'chat' | 'object'
|
|
37
|
-
type AiAgentMutationPolicy = 'read-only' | 'confirm-required' | 'destructive-confirm-required'
|
|
38
|
-
type AiAgentAcceptedMediaType = 'image' | 'pdf' | 'file'
|
|
39
|
-
type AiAgentDataOperation = 'read' | 'search' | 'aggregate'
|
|
40
|
-
|
|
41
|
-
interface AiAgentPageContextInput {
|
|
42
|
-
entityType: string
|
|
43
|
-
recordId: string
|
|
44
|
-
container: AwilixContainer
|
|
45
|
-
tenantId: string | null
|
|
46
|
-
organizationId: string | null
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
interface AiAgentDataCapabilities {
|
|
50
|
-
entities?: string[]
|
|
51
|
-
operations?: AiAgentDataOperation[]
|
|
52
|
-
searchableFields?: string[]
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
interface AiAgentDefinition {
|
|
56
|
-
id: string
|
|
57
|
-
moduleId: string
|
|
58
|
-
label: string
|
|
59
|
-
description: string
|
|
60
|
-
systemPrompt: string
|
|
61
|
-
allowedTools: string[]
|
|
62
|
-
executionMode?: AiAgentExecutionMode
|
|
63
|
-
defaultModel?: string
|
|
64
|
-
acceptedMediaTypes?: AiAgentAcceptedMediaType[]
|
|
65
|
-
requiredFeatures?: string[]
|
|
66
|
-
uiParts?: string[]
|
|
67
|
-
readOnly?: boolean
|
|
68
|
-
mutationPolicy?: AiAgentMutationPolicy
|
|
69
|
-
maxSteps?: number
|
|
70
|
-
output?: unknown
|
|
71
|
-
resolvePageContext?: (ctx: AiAgentPageContextInput) => Promise<string | null>
|
|
72
|
-
keywords?: string[]
|
|
73
|
-
domain?: string
|
|
74
|
-
dataCapabilities?: AiAgentDataCapabilities
|
|
75
|
-
}
|
|
76
|
-
|
|
77
30
|
type PromptSectionName =
|
|
78
31
|
| 'role'
|
|
79
32
|
| 'scope'
|
|
@@ -438,7 +438,7 @@ export default function CompanyDetailV2Page({ params }: { params?: { id?: string
|
|
|
438
438
|
onSubmit={handleFormSubmit}
|
|
439
439
|
onDelete={handleDelete}
|
|
440
440
|
hideFooterActions
|
|
441
|
-
collapsibleGroups={{ pageType: 'company-v2', chevronPosition: '
|
|
441
|
+
collapsibleGroups={{ pageType: 'company-v2', chevronPosition: 'right' }}
|
|
442
442
|
sortableGroups={{ pageType: 'company-v2' }}
|
|
443
443
|
onDirtyChange={setIsDirty}
|
|
444
444
|
/>
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import Link from 'next/link'
|
|
5
|
-
import { Building2, Users } from 'lucide-react'
|
|
5
|
+
import { Building2, UserSearch, Users } from 'lucide-react'
|
|
6
|
+
import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
|
|
6
7
|
import { useRouter, useSearchParams } from 'next/navigation'
|
|
7
8
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
8
9
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
@@ -337,7 +338,7 @@ export default function DealDetailPage({ params }: { params?: { id?: string } })
|
|
|
337
338
|
showVersionHistory={false}
|
|
338
339
|
showCancelAction={false}
|
|
339
340
|
onDirtyChange={setIsDirty}
|
|
340
|
-
collapsibleGroups={{ pageType: 'deal-detail-v3', chevronPosition: '
|
|
341
|
+
collapsibleGroups={{ pageType: 'deal-detail-v3', chevronPosition: 'right' }}
|
|
341
342
|
sortableGroups={{ pageType: 'deal-detail-v3' }}
|
|
342
343
|
initialValues={{
|
|
343
344
|
...data.deal,
|
|
@@ -424,17 +425,15 @@ export default function DealDetailPage({ params }: { params?: { id?: string } })
|
|
|
424
425
|
onScheduleRequested={openSchedule}
|
|
425
426
|
/>
|
|
426
427
|
) : (
|
|
427
|
-
<
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
</div>
|
|
437
|
-
</div>
|
|
428
|
+
<EmptyState
|
|
429
|
+
size="sm"
|
|
430
|
+
icon={<UserSearch className="h-8 w-8" aria-hidden="true" />}
|
|
431
|
+
title={t('customers.deals.detail.activities.selectEntityRequiredTitle', 'Choose a person or company to continue')}
|
|
432
|
+
description={t(
|
|
433
|
+
'customers.deals.detail.activities.selectEntityRequiredDescription',
|
|
434
|
+
'Select the customer record that should receive new deal activities before logging or scheduling anything.',
|
|
435
|
+
)}
|
|
436
|
+
/>
|
|
438
437
|
)}
|
|
439
438
|
<PlannedActivitiesSection
|
|
440
439
|
activities={plannedActivities}
|
|
@@ -11,8 +11,10 @@ import {
|
|
|
11
11
|
SelectTrigger,
|
|
12
12
|
SelectValue,
|
|
13
13
|
} from '@open-mercato/ui/primitives/select'
|
|
14
|
+
import { Layers, MoveRight, Workflow } from 'lucide-react'
|
|
14
15
|
import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
15
16
|
import { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'
|
|
17
|
+
import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
|
|
16
18
|
import { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
17
19
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
18
20
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
@@ -528,11 +530,11 @@ export default function SalesPipelinePage(): React.ReactElement {
|
|
|
528
530
|
</div>
|
|
529
531
|
|
|
530
532
|
{!selectedPipelineId ? (
|
|
531
|
-
<
|
|
532
|
-
<
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
533
|
+
<EmptyState
|
|
534
|
+
icon={<Workflow className="h-8 w-8" aria-hidden="true" />}
|
|
535
|
+
title={translate('customers.deals.pipeline.noPipeline', 'No pipeline selected. Create a pipeline in settings.')}
|
|
536
|
+
className="h-[50vh] w-full"
|
|
537
|
+
/>
|
|
536
538
|
) : dealsQuery.isLoading ? (
|
|
537
539
|
<div className="flex h-[50vh] items-center justify-center">
|
|
538
540
|
<Spinner />
|
|
@@ -561,11 +563,11 @@ export default function SalesPipelinePage(): React.ReactElement {
|
|
|
561
563
|
|
|
562
564
|
<div className="flex flex-col gap-4 pb-6 md:flex-row md:overflow-x-auto">
|
|
563
565
|
{stages.length === 0 ? (
|
|
564
|
-
<
|
|
565
|
-
<
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
566
|
+
<EmptyState
|
|
567
|
+
icon={<Layers className="h-8 w-8" aria-hidden="true" />}
|
|
568
|
+
title={translate('customers.deals.pipeline.noStages', 'Define pipeline stages to start tracking deals.')}
|
|
569
|
+
className="h-[50vh] w-full"
|
|
570
|
+
/>
|
|
569
571
|
) : (
|
|
570
572
|
stages.map((stage) => {
|
|
571
573
|
const stageKey = stage.value ?? null
|
|
@@ -584,9 +586,11 @@ export default function SalesPipelinePage(): React.ReactElement {
|
|
|
584
586
|
{renderLaneHeader(stage, laneDeals.length)}
|
|
585
587
|
<div className="flex flex-1 flex-col gap-3 overflow-y-auto px-4 py-3">
|
|
586
588
|
{sortedLaneDeals.length === 0 ? (
|
|
587
|
-
<
|
|
588
|
-
|
|
589
|
-
|
|
589
|
+
<EmptyState
|
|
590
|
+
size="sm"
|
|
591
|
+
icon={<MoveRight className="h-6 w-6" aria-hidden="true" />}
|
|
592
|
+
title={translate('customers.deals.pipeline.emptyLane', 'No deals in this stage yet.')}
|
|
593
|
+
/>
|
|
590
594
|
) : (
|
|
591
595
|
sortedLaneDeals.map((deal) => {
|
|
592
596
|
const isDragging = draggingId === deal.id
|
|
@@ -446,7 +446,7 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
|
|
|
446
446
|
onSubmit={handleFormSubmit}
|
|
447
447
|
onDelete={handleFormDelete}
|
|
448
448
|
hideFooterActions
|
|
449
|
-
collapsibleGroups={{ pageType: 'person-v2', chevronPosition: '
|
|
449
|
+
collapsibleGroups={{ pageType: 'person-v2', chevronPosition: 'right' }}
|
|
450
450
|
sortableGroups={{ pageType: 'person-v2' }}
|
|
451
451
|
onDirtyChange={setIsDirty}
|
|
452
452
|
/>
|
|
@@ -19,6 +19,12 @@ import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/u
|
|
|
19
19
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
20
20
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
21
21
|
import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
22
|
+
import {
|
|
23
|
+
Accordion,
|
|
24
|
+
AccordionContent,
|
|
25
|
+
AccordionItem,
|
|
26
|
+
AccordionTrigger,
|
|
27
|
+
} from '@open-mercato/ui/primitives/accordion'
|
|
22
28
|
import {
|
|
23
29
|
AppearanceSelector,
|
|
24
30
|
type AppearanceSelectorLabels,
|
|
@@ -207,10 +213,6 @@ export default function PipelineSettings(): React.ReactElement {
|
|
|
207
213
|
await loadPipelines()
|
|
208
214
|
}, [confirm, expandedPipelineId, loadPipelines, t])
|
|
209
215
|
|
|
210
|
-
const toggleExpand = React.useCallback((pipelineId: string) => {
|
|
211
|
-
setExpandedPipelineId((prev) => (prev === pipelineId ? null : pipelineId))
|
|
212
|
-
}, [])
|
|
213
|
-
|
|
214
216
|
const openCreateStage = React.useCallback((pipelineId: string) => {
|
|
215
217
|
setStageForm({ label: '', color: null, icon: null })
|
|
216
218
|
setStageDialog({ mode: 'create', pipelineId })
|
|
@@ -359,35 +361,107 @@ export default function PipelineSettings(): React.ReactElement {
|
|
|
359
361
|
{t('customers.pipelines.empty', 'No pipelines yet. Create one to get started.')}
|
|
360
362
|
</p>
|
|
361
363
|
) : (
|
|
362
|
-
<
|
|
364
|
+
<Accordion
|
|
365
|
+
type="single"
|
|
366
|
+
collapsible
|
|
367
|
+
value={expandedPipelineId ?? ''}
|
|
368
|
+
onValueChange={(next) => setExpandedPipelineId(next || null)}
|
|
369
|
+
className="space-y-2"
|
|
370
|
+
>
|
|
363
371
|
{pipelines.map((pipeline) => {
|
|
364
|
-
const isExpanded = expandedPipelineId === pipeline.id
|
|
365
372
|
const pipelineStages = stages[pipeline.id] ?? []
|
|
366
373
|
const isLoadingStages = loadingStages[pipeline.id] ?? false
|
|
367
374
|
|
|
368
375
|
return (
|
|
369
|
-
<
|
|
370
|
-
<
|
|
371
|
-
<
|
|
376
|
+
<AccordionItem key={pipeline.id} value={pipeline.id}>
|
|
377
|
+
<AccordionTrigger triggerIcon="chevron">
|
|
378
|
+
<span className="flex items-center gap-2">
|
|
372
379
|
<span className="text-sm font-medium">{pipeline.name}</span>
|
|
373
380
|
{pipeline.isDefault ? (
|
|
374
381
|
<span className="rounded-full bg-primary/10 px-2 py-0.5 text-xs text-primary">
|
|
375
382
|
{t('customers.pipelines.defaultBadge', 'Default')}
|
|
376
383
|
</span>
|
|
377
384
|
) : null}
|
|
385
|
+
</span>
|
|
386
|
+
</AccordionTrigger>
|
|
387
|
+
<AccordionContent>
|
|
388
|
+
<div className="mb-3 flex items-center justify-between">
|
|
389
|
+
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
|
390
|
+
{t('customers.pipelines.stages.title', 'Stages')}
|
|
391
|
+
</span>
|
|
392
|
+
<Button size="sm" variant="outline" onClick={() => openCreateStage(pipeline.id)}>
|
|
393
|
+
{t('customers.pipelines.stages.add', 'Add stage')}
|
|
394
|
+
</Button>
|
|
378
395
|
</div>
|
|
379
|
-
|
|
396
|
+
|
|
397
|
+
{isLoadingStages ? (
|
|
398
|
+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
399
|
+
<Spinner className="h-3 w-3" />
|
|
400
|
+
{t('customers.pipelines.stages.loading', 'Loading…')}
|
|
401
|
+
</div>
|
|
402
|
+
) : pipelineStages.length === 0 ? (
|
|
403
|
+
<p className="text-sm text-muted-foreground">
|
|
404
|
+
{t('customers.pipelines.stages.empty', 'No stages yet.')}
|
|
405
|
+
</p>
|
|
406
|
+
) : (
|
|
407
|
+
<div className="divide-y divide-border rounded-md border bg-background">
|
|
408
|
+
{pipelineStages.map((stage, idx) => (
|
|
409
|
+
<div key={stage.id} className="flex items-center justify-between gap-3 px-3 py-2">
|
|
410
|
+
<div className="flex items-center gap-2">
|
|
411
|
+
<span className="w-5 text-center text-xs text-muted-foreground">{idx + 1}</span>
|
|
412
|
+
{stage.color ? renderDictionaryColor(stage.color, 'h-3 w-3 rounded-full') : null}
|
|
413
|
+
{stage.icon ? renderDictionaryIcon(stage.icon, 'h-4 w-4') : null}
|
|
414
|
+
<span className="text-sm">{stage.label}</span>
|
|
415
|
+
</div>
|
|
416
|
+
<div className="flex items-center gap-1">
|
|
417
|
+
<Button
|
|
418
|
+
variant="ghost"
|
|
419
|
+
size="icon"
|
|
420
|
+
className="h-7 w-7"
|
|
421
|
+
disabled={idx === 0}
|
|
422
|
+
onClick={() => void handleMoveStage(stage, 'up')}
|
|
423
|
+
title={t('customers.pipelines.stages.moveUp', 'Move up')}
|
|
424
|
+
>
|
|
425
|
+
↑
|
|
426
|
+
</Button>
|
|
427
|
+
<Button
|
|
428
|
+
variant="ghost"
|
|
429
|
+
size="icon"
|
|
430
|
+
className="h-7 w-7"
|
|
431
|
+
disabled={idx === pipelineStages.length - 1}
|
|
432
|
+
onClick={() => void handleMoveStage(stage, 'down')}
|
|
433
|
+
title={t('customers.pipelines.stages.moveDown', 'Move down')}
|
|
434
|
+
>
|
|
435
|
+
↓
|
|
436
|
+
</Button>
|
|
437
|
+
<Button
|
|
438
|
+
variant="ghost"
|
|
439
|
+
size="sm"
|
|
440
|
+
onClick={() => openEditStage(stage)}
|
|
441
|
+
>
|
|
442
|
+
{t('customers.pipelines.stages.edit', 'Edit')}
|
|
443
|
+
</Button>
|
|
444
|
+
<Button
|
|
445
|
+
variant="ghost"
|
|
446
|
+
size="sm"
|
|
447
|
+
className="text-destructive hover:text-destructive"
|
|
448
|
+
onClick={() => void handleDeleteStage(stage)}
|
|
449
|
+
>
|
|
450
|
+
{t('customers.pipelines.stages.delete', 'Delete')}
|
|
451
|
+
</Button>
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
))}
|
|
455
|
+
</div>
|
|
456
|
+
)}
|
|
457
|
+
|
|
458
|
+
<div className="mt-4 flex items-center justify-end gap-2 border-t pt-3">
|
|
380
459
|
<Button
|
|
381
460
|
variant="ghost"
|
|
382
461
|
size="sm"
|
|
383
|
-
onClick={() =>
|
|
462
|
+
onClick={() => openEditPipeline(pipeline)}
|
|
384
463
|
>
|
|
385
|
-
{
|
|
386
|
-
? t('customers.pipelines.actions.hideStages', 'Hide stages')
|
|
387
|
-
: t('customers.pipelines.actions.manageStages', 'Manage stages')}
|
|
388
|
-
</Button>
|
|
389
|
-
<Button variant="ghost" size="sm" onClick={() => openEditPipeline(pipeline)}>
|
|
390
|
-
{t('customers.pipelines.actions.edit', 'Edit')}
|
|
464
|
+
{t('customers.pipelines.actions.edit', 'Edit pipeline')}
|
|
391
465
|
</Button>
|
|
392
466
|
<Button
|
|
393
467
|
variant="ghost"
|
|
@@ -395,88 +469,14 @@ export default function PipelineSettings(): React.ReactElement {
|
|
|
395
469
|
className="text-destructive hover:text-destructive"
|
|
396
470
|
onClick={() => void handleDeletePipeline(pipeline)}
|
|
397
471
|
>
|
|
398
|
-
{t('customers.pipelines.actions.delete', 'Delete')}
|
|
472
|
+
{t('customers.pipelines.actions.delete', 'Delete pipeline')}
|
|
399
473
|
</Button>
|
|
400
474
|
</div>
|
|
401
|
-
</
|
|
402
|
-
|
|
403
|
-
{isExpanded ? (
|
|
404
|
-
<div className="border-t border-border bg-muted/30 px-4 py-3">
|
|
405
|
-
<div className="mb-3 flex items-center justify-between">
|
|
406
|
-
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
|
407
|
-
{t('customers.pipelines.stages.title', 'Stages')}
|
|
408
|
-
</span>
|
|
409
|
-
<Button size="sm" variant="outline" onClick={() => openCreateStage(pipeline.id)}>
|
|
410
|
-
{t('customers.pipelines.stages.add', 'Add stage')}
|
|
411
|
-
</Button>
|
|
412
|
-
</div>
|
|
413
|
-
|
|
414
|
-
{isLoadingStages ? (
|
|
415
|
-
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
416
|
-
<Spinner className="h-3 w-3" />
|
|
417
|
-
{t('customers.pipelines.stages.loading', 'Loading…')}
|
|
418
|
-
</div>
|
|
419
|
-
) : pipelineStages.length === 0 ? (
|
|
420
|
-
<p className="text-sm text-muted-foreground">
|
|
421
|
-
{t('customers.pipelines.stages.empty', 'No stages yet.')}
|
|
422
|
-
</p>
|
|
423
|
-
) : (
|
|
424
|
-
<div className="divide-y divide-border rounded-md border bg-background">
|
|
425
|
-
{pipelineStages.map((stage, idx) => (
|
|
426
|
-
<div key={stage.id} className="flex items-center justify-between gap-3 px-3 py-2">
|
|
427
|
-
<div className="flex items-center gap-2">
|
|
428
|
-
<span className="w-5 text-center text-xs text-muted-foreground">{idx + 1}</span>
|
|
429
|
-
{stage.color ? renderDictionaryColor(stage.color, 'h-3 w-3 rounded-full') : null}
|
|
430
|
-
{stage.icon ? renderDictionaryIcon(stage.icon, 'h-4 w-4') : null}
|
|
431
|
-
<span className="text-sm">{stage.label}</span>
|
|
432
|
-
</div>
|
|
433
|
-
<div className="flex items-center gap-1">
|
|
434
|
-
<Button
|
|
435
|
-
variant="ghost"
|
|
436
|
-
size="icon"
|
|
437
|
-
className="h-7 w-7"
|
|
438
|
-
disabled={idx === 0}
|
|
439
|
-
onClick={() => void handleMoveStage(stage, 'up')}
|
|
440
|
-
title={t('customers.pipelines.stages.moveUp', 'Move up')}
|
|
441
|
-
>
|
|
442
|
-
↑
|
|
443
|
-
</Button>
|
|
444
|
-
<Button
|
|
445
|
-
variant="ghost"
|
|
446
|
-
size="icon"
|
|
447
|
-
className="h-7 w-7"
|
|
448
|
-
disabled={idx === pipelineStages.length - 1}
|
|
449
|
-
onClick={() => void handleMoveStage(stage, 'down')}
|
|
450
|
-
title={t('customers.pipelines.stages.moveDown', 'Move down')}
|
|
451
|
-
>
|
|
452
|
-
↓
|
|
453
|
-
</Button>
|
|
454
|
-
<Button
|
|
455
|
-
variant="ghost"
|
|
456
|
-
size="sm"
|
|
457
|
-
onClick={() => openEditStage(stage)}
|
|
458
|
-
>
|
|
459
|
-
{t('customers.pipelines.stages.edit', 'Edit')}
|
|
460
|
-
</Button>
|
|
461
|
-
<Button
|
|
462
|
-
variant="ghost"
|
|
463
|
-
size="sm"
|
|
464
|
-
className="text-destructive hover:text-destructive"
|
|
465
|
-
onClick={() => void handleDeleteStage(stage)}
|
|
466
|
-
>
|
|
467
|
-
{t('customers.pipelines.stages.delete', 'Delete')}
|
|
468
|
-
</Button>
|
|
469
|
-
</div>
|
|
470
|
-
</div>
|
|
471
|
-
))}
|
|
472
|
-
</div>
|
|
473
|
-
)}
|
|
474
|
-
</div>
|
|
475
|
-
) : null}
|
|
476
|
-
</div>
|
|
475
|
+
</AccordionContent>
|
|
476
|
+
</AccordionItem>
|
|
477
477
|
)
|
|
478
478
|
})}
|
|
479
|
-
</
|
|
479
|
+
</Accordion>
|
|
480
480
|
)}
|
|
481
481
|
|
|
482
482
|
{/* Pipeline Dialog */}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import Link from 'next/link'
|
|
5
|
-
import { Search, Check, Settings2 } from 'lucide-react'
|
|
5
|
+
import { Search, Check, CheckCheck, Settings2 } from 'lucide-react'
|
|
6
|
+
import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
|
|
6
7
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
7
8
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
8
9
|
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
@@ -419,12 +420,14 @@ export function AssignRoleDialog({
|
|
|
419
420
|
</div>
|
|
420
421
|
|
|
421
422
|
{!availableRoleTypes.length ? (
|
|
422
|
-
<
|
|
423
|
-
|
|
423
|
+
<EmptyState
|
|
424
|
+
size="sm"
|
|
425
|
+
icon={<CheckCheck className="h-8 w-8" aria-hidden="true" />}
|
|
426
|
+
title={t(
|
|
424
427
|
'customers.roles.dialog.noAvailableRoles',
|
|
425
428
|
'All available role types are already assigned.',
|
|
426
429
|
)}
|
|
427
|
-
|
|
430
|
+
/>
|
|
428
431
|
) : null}
|
|
429
432
|
</div>
|
|
430
433
|
) : null}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
-
import { Check, Plus, Search, SlidersHorizontal, Tag, X } from 'lucide-react'
|
|
4
|
+
import { Check, Plus, Search, SearchX, SlidersHorizontal, Tag, X } from 'lucide-react'
|
|
5
|
+
import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
|
|
5
6
|
import { VisuallyHidden } from '@radix-ui/react-visually-hidden'
|
|
6
7
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
7
8
|
import { cn, slugifyTagLabel } from '@open-mercato/shared/lib/utils'
|
|
@@ -1238,13 +1239,17 @@ export function EntityTagsDialog({
|
|
|
1238
1239
|
{t('customers.personTags.loading', 'Loading...')}
|
|
1239
1240
|
</div>
|
|
1240
1241
|
) : (
|
|
1241
|
-
<
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1242
|
+
<EmptyState
|
|
1243
|
+
size="sm"
|
|
1244
|
+
icon={<SearchX className="h-8 w-8" aria-hidden="true" />}
|
|
1245
|
+
title={
|
|
1246
|
+
searchValue.trim().length > 0
|
|
1247
|
+
? t('customers.personTags.emptySearchResults', 'No options match the current search.')
|
|
1248
|
+
: activeCategory.source === 'dictionary'
|
|
1249
|
+
? t('customers.personTags.emptyDictionaryCategory', 'No options are configured for this category yet.')
|
|
1250
|
+
: t('customers.personTags.emptyCategory', 'No items have been added for this category yet.')
|
|
1251
|
+
}
|
|
1252
|
+
/>
|
|
1248
1253
|
)}
|
|
1249
1254
|
|
|
1250
1255
|
{activeCategory.supportsCreate ? (
|