@open-mercato/core 0.5.1-develop.2975.ccbadc8198 → 0.5.1-develop.2996.ce62fd491c
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 +2 -2
- package/dist/generated/entities/sidebar_variant/index.js +25 -0
- package/dist/generated/entities/sidebar_variant/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +1 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +13 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/authUi.js +1 -1
- package/dist/helpers/integration/authUi.js.map +2 -2
- package/dist/modules/audit_logs/services/actionLogService.js +4 -5
- package/dist/modules/audit_logs/services/actionLogService.js.map +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js +224 -35
- package/dist/modules/auth/api/sidebar/preferences/route.js.map +3 -3
- package/dist/modules/auth/api/sidebar/variants/[id]/route.js +161 -0
- package/dist/modules/auth/api/sidebar/variants/[id]/route.js.map +7 -0
- package/dist/modules/auth/api/sidebar/variants/route.js +142 -0
- package/dist/modules/auth/api/sidebar/variants/route.js.map +7 -0
- package/dist/modules/auth/backend/sidebar-customization/page.js +16 -0
- package/dist/modules/auth/backend/sidebar-customization/page.js.map +7 -0
- package/dist/modules/auth/backend/sidebar-customization/page.meta.js +28 -0
- package/dist/modules/auth/backend/sidebar-customization/page.meta.js.map +7 -0
- package/dist/modules/auth/data/entities.js +45 -4
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/data/validators.js +63 -1
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/auth/migrations/Migration20260427081815.js +15 -0
- package/dist/modules/auth/migrations/Migration20260427081815.js.map +7 -0
- package/dist/modules/auth/migrations/Migration20260427124900.js +15 -0
- package/dist/modules/auth/migrations/Migration20260427124900.js.map +7 -0
- package/dist/modules/auth/migrations/Migration20260427143311.js +72 -0
- package/dist/modules/auth/migrations/Migration20260427143311.js.map +7 -0
- package/dist/modules/auth/services/sidebarPreferencesService.js +176 -16
- package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/[id]/page.js +3 -1
- package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +4 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +8 -3
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +3 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/formConfig.js +3 -3
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/customers/lib/displayName.js +12 -0
- package/dist/modules/customers/lib/displayName.js.map +2 -2
- package/dist/modules/entities/cli.js +5 -6
- package/dist/modules/entities/cli.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js +124 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.meta.js +11 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.meta.js.map +7 -0
- package/generated/entities/sidebar_variant/index.ts +11 -0
- package/generated/entities.ids.generated.ts +1 -0
- package/generated/entity-fields-registry.ts +13 -0
- package/package.json +6 -6
- package/src/helpers/integration/authUi.ts +1 -1
- package/src/modules/audit_logs/services/actionLogService.ts +5 -6
- package/src/modules/auth/api/sidebar/preferences/route.ts +266 -34
- package/src/modules/auth/api/sidebar/variants/[id]/route.ts +183 -0
- package/src/modules/auth/api/sidebar/variants/route.ts +157 -0
- package/src/modules/auth/backend/sidebar-customization/page.meta.ts +34 -0
- package/src/modules/auth/backend/sidebar-customization/page.tsx +17 -0
- package/src/modules/auth/data/entities.ts +48 -2
- package/src/modules/auth/data/validators.ts +70 -0
- package/src/modules/auth/migrations/.snapshot-open-mercato.json +790 -71
- package/src/modules/auth/migrations/Migration20260427081815.ts +16 -0
- package/src/modules/auth/migrations/Migration20260427124900.ts +19 -0
- package/src/modules/auth/migrations/Migration20260427143311.ts +83 -0
- package/src/modules/auth/services/sidebarPreferencesService.ts +243 -18
- package/src/modules/customers/backend/customers/companies/[id]/page.tsx +5 -4
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +6 -5
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +13 -9
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +3 -2
- package/src/modules/customers/components/formConfig.tsx +3 -3
- package/src/modules/customers/lib/displayName.ts +21 -0
- package/src/modules/entities/cli.ts +5 -6
- package/src/modules/portal/frontend/[orgSlug]/portal/reset-password/page.meta.ts +9 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/reset-password/page.tsx +168 -0
- package/src/modules/portal/i18n/de.json +20 -0
- package/src/modules/portal/i18n/en.json +20 -0
- package/src/modules/portal/i18n/es.json +20 -0
- package/src/modules/portal/i18n/pl.json +20 -0
|
@@ -17,14 +17,71 @@ const confirmPasswordResetSchema = z.object({
|
|
|
17
17
|
const refreshSessionRequestSchema = z.object({
|
|
18
18
|
refreshToken: z.string().min(1)
|
|
19
19
|
});
|
|
20
|
+
const sidebarPreferencesScopeSchema = z.union([
|
|
21
|
+
z.object({ type: z.literal("user") }),
|
|
22
|
+
z.object({ type: z.literal("role"), roleId: z.string().uuid() })
|
|
23
|
+
]);
|
|
24
|
+
const sidebarVariantSettingsSchema = z.object({
|
|
25
|
+
version: z.number().int().positive().optional(),
|
|
26
|
+
groupOrder: z.array(z.string().min(1)).max(200).optional(),
|
|
27
|
+
groupLabels: z.record(z.string().min(1), z.string().min(1).max(120)).optional(),
|
|
28
|
+
itemLabels: z.record(z.string().min(1), z.string().min(1).max(120)).optional(),
|
|
29
|
+
hiddenItems: z.array(z.string().min(1)).max(500).optional(),
|
|
30
|
+
itemOrder: z.record(z.string().min(1), z.array(z.string().min(1)).max(500)).optional()
|
|
31
|
+
});
|
|
32
|
+
const createSidebarVariantInputSchema = z.object({
|
|
33
|
+
name: z.string().trim().min(1).max(120).optional(),
|
|
34
|
+
settings: sidebarVariantSettingsSchema.optional(),
|
|
35
|
+
isActive: z.boolean().optional()
|
|
36
|
+
});
|
|
37
|
+
const updateSidebarVariantInputSchema = z.object({
|
|
38
|
+
name: z.string().trim().min(1).max(120).optional(),
|
|
39
|
+
settings: sidebarVariantSettingsSchema.optional(),
|
|
40
|
+
isActive: z.boolean().optional()
|
|
41
|
+
});
|
|
42
|
+
const sidebarVariantRecordSchema = z.object({
|
|
43
|
+
id: z.string().uuid(),
|
|
44
|
+
name: z.string(),
|
|
45
|
+
isActive: z.boolean(),
|
|
46
|
+
settings: z.object({
|
|
47
|
+
version: z.number().int().positive(),
|
|
48
|
+
groupOrder: z.array(z.string()),
|
|
49
|
+
groupLabels: z.record(z.string(), z.string()),
|
|
50
|
+
itemLabels: z.record(z.string(), z.string()),
|
|
51
|
+
hiddenItems: z.array(z.string()),
|
|
52
|
+
itemOrder: z.record(z.string(), z.array(z.string()))
|
|
53
|
+
}),
|
|
54
|
+
createdAt: z.string(),
|
|
55
|
+
updatedAt: z.string().nullable()
|
|
56
|
+
});
|
|
20
57
|
const sidebarPreferencesInputSchema = z.object({
|
|
21
58
|
version: z.number().int().positive().optional(),
|
|
22
59
|
groupOrder: z.array(z.string().min(1)).max(200).optional(),
|
|
23
60
|
groupLabels: z.record(z.string().min(1), z.string().min(1).max(120)).optional(),
|
|
24
61
|
itemLabels: z.record(z.string().min(1), z.string().min(1).max(120)).optional(),
|
|
25
62
|
hiddenItems: z.array(z.string().min(1)).max(500).optional(),
|
|
63
|
+
itemOrder: z.record(z.string().min(1), z.array(z.string().min(1)).max(500)).optional(),
|
|
26
64
|
applyToRoles: z.array(z.string().uuid()).optional(),
|
|
27
|
-
clearRoleIds: z.array(z.string().uuid()).optional()
|
|
65
|
+
clearRoleIds: z.array(z.string().uuid()).optional(),
|
|
66
|
+
scope: sidebarPreferencesScopeSchema.optional()
|
|
67
|
+
}).superRefine((value, ctx) => {
|
|
68
|
+
const scopeType = value.scope?.type ?? "user";
|
|
69
|
+
if (scopeType === "role") {
|
|
70
|
+
if ((value.applyToRoles?.length ?? 0) > 0) {
|
|
71
|
+
ctx.addIssue({
|
|
72
|
+
code: z.ZodIssueCode.custom,
|
|
73
|
+
path: ["applyToRoles"],
|
|
74
|
+
message: 'applyToRoles is only valid when scope.type === "user"'
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if ((value.clearRoleIds?.length ?? 0) > 0) {
|
|
78
|
+
ctx.addIssue({
|
|
79
|
+
code: z.ZodIssueCode.custom,
|
|
80
|
+
path: ["clearRoleIds"],
|
|
81
|
+
message: 'clearRoleIds is only valid when scope.type === "user"'
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
28
85
|
});
|
|
29
86
|
const userCreateSchema = z.object({
|
|
30
87
|
email: z.string().email(),
|
|
@@ -42,10 +99,15 @@ const featureCheckRequestSchema = z.object({
|
|
|
42
99
|
}).describe("Batch feature check payload");
|
|
43
100
|
export {
|
|
44
101
|
confirmPasswordResetSchema,
|
|
102
|
+
createSidebarVariantInputSchema,
|
|
45
103
|
featureCheckRequestSchema,
|
|
46
104
|
refreshSessionRequestSchema,
|
|
47
105
|
requestPasswordResetSchema,
|
|
48
106
|
sidebarPreferencesInputSchema,
|
|
107
|
+
sidebarPreferencesScopeSchema,
|
|
108
|
+
sidebarVariantRecordSchema,
|
|
109
|
+
sidebarVariantSettingsSchema,
|
|
110
|
+
updateSidebarVariantInputSchema,
|
|
49
111
|
userCreateSchema,
|
|
50
112
|
userLoginSchema
|
|
51
113
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/auth/data/validators.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolicy'\n\nconst passwordSchema = buildPasswordSchema()\n\n// Core auth validators\nexport const userLoginSchema = z.object({\n email: z.string().email(),\n password: z.string().min(6),\n requireRole: z.string().optional(),\n tenantId: z.string().uuid().optional(),\n})\n\nexport const requestPasswordResetSchema = z.object({\n email: z.string().email(),\n})\n\nexport const confirmPasswordResetSchema = z.object({\n token: z.string().min(10),\n password: passwordSchema,\n})\n\nexport const refreshSessionRequestSchema = z.object({\n refreshToken: z.string().min(1),\n})\n\nexport const sidebarPreferencesInputSchema = z.object({\n version: z.number().int().positive().optional(),\n groupOrder: z.array(z.string().min(1)).max(200).optional(),\n groupLabels: z.record(z.string().min(1), z.string().min(1).max(120)).optional(),\n itemLabels: z.record(z.string().min(1), z.string().min(1).max(120)).optional(),\n hiddenItems: z.array(z.string().min(1)).max(500).optional(),\n applyToRoles: z.array(z.string().uuid()).optional(),\n clearRoleIds: z.array(z.string().uuid()).optional(),\n})\n\n// Optional helpers for CLI or admin forms\nexport const userCreateSchema = z.object({\n email: z.string().email(),\n password: passwordSchema.optional(),\n sendInviteEmail: z.boolean().optional(),\n tenantId: z.string().uuid().optional(),\n organizationId: z.string().uuid(),\n rolesCsv: z.string().optional(),\n}).refine(\n (data) => data.password || data.sendInviteEmail,\n { message: 'Either password or sendInviteEmail is required', path: ['password'] },\n)\n\nexport const featureCheckRequestSchema = z.object({\n features: z.array(z.string().max(128)).max(50).describe('Feature identifiers to check'),\n}).describe('Batch feature check payload')\n\nexport type UserLoginInput = z.infer<typeof userLoginSchema>\nexport type RequestPasswordResetInput = z.infer<typeof requestPasswordResetSchema>\nexport type ConfirmPasswordResetInput = z.infer<typeof confirmPasswordResetSchema>\nexport type RefreshSessionRequestInput = z.infer<typeof refreshSessionRequestSchema>\nexport type SidebarPreferencesInput = z.infer<typeof sidebarPreferencesInputSchema>\nexport type UserCreateInput = z.infer<typeof userCreateSchema>\nexport type FeatureCheckRequestInput = z.infer<typeof featureCheckRequestSchema>\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,2BAA2B;AAEpC,MAAM,iBAAiB,oBAAoB;AAGpC,MAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,OAAO,EAAE,OAAO,EAAE,MAAM;AAAA,EACxB,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC1B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AACvC,CAAC;AAEM,MAAM,6BAA6B,EAAE,OAAO;AAAA,EACjD,OAAO,EAAE,OAAO,EAAE,MAAM;AAC1B,CAAC;AAEM,MAAM,6BAA6B,EAAE,OAAO;AAAA,EACjD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;AAAA,EACxB,UAAU;AACZ,CAAC;AAEM,MAAM,8BAA8B,EAAE,OAAO;AAAA,EAClD,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC;AAChC,CAAC;AAEM,MAAM,gCAAgC,EAAE,OAAO;AAAA,EACpD,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACzD,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,SAAS;AAAA,EAC9E,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,SAAS;AAAA,EAC7E,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC1D,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AAAA,EAClD,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolicy'\n\nconst passwordSchema = buildPasswordSchema()\n\n// Core auth validators\nexport const userLoginSchema = z.object({\n email: z.string().email(),\n password: z.string().min(6),\n requireRole: z.string().optional(),\n tenantId: z.string().uuid().optional(),\n})\n\nexport const requestPasswordResetSchema = z.object({\n email: z.string().email(),\n})\n\nexport const confirmPasswordResetSchema = z.object({\n token: z.string().min(10),\n password: passwordSchema,\n})\n\nexport const refreshSessionRequestSchema = z.object({\n refreshToken: z.string().min(1),\n})\n\nexport const sidebarPreferencesScopeSchema = z.union([\n z.object({ type: z.literal('user') }),\n z.object({ type: z.literal('role'), roleId: z.string().uuid() }),\n])\n\n// Sidebar settings shape shared by both `sidebarPreferencesInputSchema` (which\n// adds role-targeting fields) and the variants API (which uses just the\n// settings + name + isActive). Keeping the field constraints in one place\n// prevents drift between the preferences and variants surfaces.\nexport const sidebarVariantSettingsSchema = z.object({\n version: z.number().int().positive().optional(),\n groupOrder: z.array(z.string().min(1)).max(200).optional(),\n groupLabels: z.record(z.string().min(1), z.string().min(1).max(120)).optional(),\n itemLabels: z.record(z.string().min(1), z.string().min(1).max(120)).optional(),\n hiddenItems: z.array(z.string().min(1)).max(500).optional(),\n itemOrder: z.record(z.string().min(1), z.array(z.string().min(1)).max(500)).optional(),\n})\n\nexport const createSidebarVariantInputSchema = z.object({\n name: z.string().trim().min(1).max(120).optional(),\n settings: sidebarVariantSettingsSchema.optional(),\n isActive: z.boolean().optional(),\n})\n\nexport const updateSidebarVariantInputSchema = z.object({\n name: z.string().trim().min(1).max(120).optional(),\n settings: sidebarVariantSettingsSchema.optional(),\n isActive: z.boolean().optional(),\n})\n\nexport const sidebarVariantRecordSchema = z.object({\n id: z.string().uuid(),\n name: z.string(),\n isActive: z.boolean(),\n settings: z.object({\n version: z.number().int().positive(),\n groupOrder: z.array(z.string()),\n groupLabels: z.record(z.string(), z.string()),\n itemLabels: z.record(z.string(), z.string()),\n hiddenItems: z.array(z.string()),\n itemOrder: z.record(z.string(), z.array(z.string())),\n }),\n createdAt: z.string(),\n updatedAt: z.string().nullable(),\n})\n\nexport const sidebarPreferencesInputSchema = z.object({\n version: z.number().int().positive().optional(),\n groupOrder: z.array(z.string().min(1)).max(200).optional(),\n groupLabels: z.record(z.string().min(1), z.string().min(1).max(120)).optional(),\n itemLabels: z.record(z.string().min(1), z.string().min(1).max(120)).optional(),\n hiddenItems: z.array(z.string().min(1)).max(500).optional(),\n itemOrder: z.record(z.string().min(1), z.array(z.string().min(1)).max(500)).optional(),\n applyToRoles: z.array(z.string().uuid()).optional(),\n clearRoleIds: z.array(z.string().uuid()).optional(),\n scope: sidebarPreferencesScopeSchema.optional(),\n}).superRefine((value, ctx) => {\n const scopeType = value.scope?.type ?? 'user'\n if (scopeType === 'role') {\n if ((value.applyToRoles?.length ?? 0) > 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['applyToRoles'],\n message: 'applyToRoles is only valid when scope.type === \"user\"',\n })\n }\n if ((value.clearRoleIds?.length ?? 0) > 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['clearRoleIds'],\n message: 'clearRoleIds is only valid when scope.type === \"user\"',\n })\n }\n }\n})\n\n// Optional helpers for CLI or admin forms\nexport const userCreateSchema = z.object({\n email: z.string().email(),\n password: passwordSchema.optional(),\n sendInviteEmail: z.boolean().optional(),\n tenantId: z.string().uuid().optional(),\n organizationId: z.string().uuid(),\n rolesCsv: z.string().optional(),\n}).refine(\n (data) => data.password || data.sendInviteEmail,\n { message: 'Either password or sendInviteEmail is required', path: ['password'] },\n)\n\nexport const featureCheckRequestSchema = z.object({\n features: z.array(z.string().max(128)).max(50).describe('Feature identifiers to check'),\n}).describe('Batch feature check payload')\n\nexport type UserLoginInput = z.infer<typeof userLoginSchema>\nexport type RequestPasswordResetInput = z.infer<typeof requestPasswordResetSchema>\nexport type ConfirmPasswordResetInput = z.infer<typeof confirmPasswordResetSchema>\nexport type RefreshSessionRequestInput = z.infer<typeof refreshSessionRequestSchema>\nexport type SidebarPreferencesInput = z.infer<typeof sidebarPreferencesInputSchema>\nexport type SidebarVariantSettingsInput = z.infer<typeof sidebarVariantSettingsSchema>\nexport type CreateSidebarVariantInput = z.infer<typeof createSidebarVariantInputSchema>\nexport type UpdateSidebarVariantInput = z.infer<typeof updateSidebarVariantInputSchema>\nexport type SidebarVariantRecordResponse = z.infer<typeof sidebarVariantRecordSchema>\nexport type UserCreateInput = z.infer<typeof userCreateSchema>\nexport type FeatureCheckRequestInput = z.infer<typeof featureCheckRequestSchema>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260427081815 extends Migration {
|
|
3
|
+
up() {
|
|
4
|
+
this.addSql(`create table "sidebar_variants" ("id" uuid not null default gen_random_uuid(), "user_id" uuid not null, "tenant_id" uuid null, "organization_id" uuid null, "locale" text not null, "name" text not null, "settings_json" jsonb null, "is_active" boolean not null default false, "created_at" timestamptz not null, "updated_at" timestamptz null, "deleted_at" timestamptz null, primary key ("id"));`);
|
|
5
|
+
this.addSql(`alter table "sidebar_variants" add constraint "sidebar_variants_user_id_tenant_id_locale_name_unique" unique ("user_id", "tenant_id", "locale", "name");`);
|
|
6
|
+
this.addSql(`alter table "sidebar_variants" add constraint "sidebar_variants_user_id_foreign" foreign key ("user_id") references "users" ("id");`);
|
|
7
|
+
}
|
|
8
|
+
down() {
|
|
9
|
+
this.addSql(`drop table if exists "sidebar_variants" cascade;`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
Migration20260427081815
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=Migration20260427081815.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/auth/migrations/Migration20260427081815.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260427081815 extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`create table \"sidebar_variants\" (\"id\" uuid not null default gen_random_uuid(), \"user_id\" uuid not null, \"tenant_id\" uuid null, \"organization_id\" uuid null, \"locale\" text not null, \"name\" text not null, \"settings_json\" jsonb null, \"is_active\" boolean not null default false, \"created_at\" timestamptz not null, \"updated_at\" timestamptz null, \"deleted_at\" timestamptz null, primary key (\"id\"));`);\n this.addSql(`alter table \"sidebar_variants\" add constraint \"sidebar_variants_user_id_tenant_id_locale_name_unique\" unique (\"user_id\", \"tenant_id\", \"locale\", \"name\");`);\n\n this.addSql(`alter table \"sidebar_variants\" add constraint \"sidebar_variants_user_id_foreign\" foreign key (\"user_id\") references \"users\" (\"id\");`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`drop table if exists \"sidebar_variants\" cascade;`);\n }\n\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,gCAAgC,UAAU;AAAA,EAE5C,KAA2B;AAClC,SAAK,OAAO,yYAAyY;AACrZ,SAAK,OAAO,0JAA0J;AAEtK,SAAK,OAAO,qIAAqI;AAAA,EACnJ;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,kDAAkD;AAAA,EAChE;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260427124900 extends Migration {
|
|
3
|
+
up() {
|
|
4
|
+
this.addSql(`alter table "sidebar_variants" drop constraint if exists "sidebar_variants_user_id_tenant_id_locale_name_unique";`);
|
|
5
|
+
this.addSql(`create unique index if not exists "sidebar_variants_active_name_unique_idx" on "sidebar_variants" ("user_id", "tenant_id", "locale", "name") where "deleted_at" is null;`);
|
|
6
|
+
}
|
|
7
|
+
down() {
|
|
8
|
+
this.addSql(`drop index if exists "sidebar_variants_active_name_unique_idx";`);
|
|
9
|
+
this.addSql(`alter table "sidebar_variants" add constraint "sidebar_variants_user_id_tenant_id_locale_name_unique" unique ("user_id", "tenant_id", "locale", "name");`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
Migration20260427124900
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=Migration20260427124900.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/auth/migrations/Migration20260427124900.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\n// Replace the full unique constraint on sidebar_variants with a partial unique index\n// scoped to live rows (deleted_at IS NULL). Without this, soft-deleting a variant and\n// then trying to recreate one with the same name throws a duplicate-key error because\n// the regular unique constraint considers tombstoned rows.\nexport class Migration20260427124900 extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`alter table \"sidebar_variants\" drop constraint if exists \"sidebar_variants_user_id_tenant_id_locale_name_unique\";`);\n this.addSql(`create unique index if not exists \"sidebar_variants_active_name_unique_idx\" on \"sidebar_variants\" (\"user_id\", \"tenant_id\", \"locale\", \"name\") where \"deleted_at\" is null;`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`drop index if exists \"sidebar_variants_active_name_unique_idx\";`);\n this.addSql(`alter table \"sidebar_variants\" add constraint \"sidebar_variants_user_id_tenant_id_locale_name_unique\" unique (\"user_id\", \"tenant_id\", \"locale\", \"name\");`);\n }\n\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAMnB,MAAM,gCAAgC,UAAU;AAAA,EAE5C,KAA2B;AAClC,SAAK,OAAO,mHAAmH;AAC/H,SAAK,OAAO,0KAA0K;AAAA,EACxL;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,iEAAiE;AAC7E,SAAK,OAAO,0JAA0J;AAAA,EACxK;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260427143311 extends Migration {
|
|
3
|
+
up() {
|
|
4
|
+
this.addSql(`
|
|
5
|
+
with ranked as (
|
|
6
|
+
select id,
|
|
7
|
+
row_number() over (
|
|
8
|
+
partition by user_id, tenant_id, name
|
|
9
|
+
order by coalesce(updated_at, created_at) desc, created_at desc, id desc
|
|
10
|
+
) as rn
|
|
11
|
+
from sidebar_variants
|
|
12
|
+
where deleted_at is null
|
|
13
|
+
)
|
|
14
|
+
update sidebar_variants
|
|
15
|
+
set deleted_at = now()
|
|
16
|
+
from ranked
|
|
17
|
+
where sidebar_variants.id = ranked.id and ranked.rn > 1;
|
|
18
|
+
`);
|
|
19
|
+
this.addSql(`drop index if exists "sidebar_variants_active_name_unique_idx";`);
|
|
20
|
+
this.addSql(`alter table "sidebar_variants" drop constraint if exists "sidebar_variants_user_id_tenant_id_locale_name_unique";`);
|
|
21
|
+
this.addSql(`create unique index if not exists "sidebar_variants_active_name_unique_idx" on "sidebar_variants" ("user_id", "tenant_id", "name") where "deleted_at" is null;`);
|
|
22
|
+
this.addSql(`
|
|
23
|
+
with ranked as (
|
|
24
|
+
select id,
|
|
25
|
+
row_number() over (
|
|
26
|
+
partition by user_id, tenant_id, organization_id
|
|
27
|
+
order by coalesce(updated_at, created_at) desc, created_at desc, id desc
|
|
28
|
+
) as rn
|
|
29
|
+
from user_sidebar_preferences
|
|
30
|
+
where deleted_at is null
|
|
31
|
+
)
|
|
32
|
+
update user_sidebar_preferences
|
|
33
|
+
set deleted_at = now()
|
|
34
|
+
from ranked
|
|
35
|
+
where user_sidebar_preferences.id = ranked.id and ranked.rn > 1;
|
|
36
|
+
`);
|
|
37
|
+
this.addSql(`alter table "user_sidebar_preferences" drop constraint if exists "user_sidebar_preferences_user_id_tenant_id_organi_35248_unique";`);
|
|
38
|
+
this.addSql(`alter table "user_sidebar_preferences" drop constraint if exists "user_sidebar_preferences_user_id_tenant_id_organi_f3f2f_unique";`);
|
|
39
|
+
this.addSql(`drop index if exists "user_sidebar_preferences_active_unique_idx";`);
|
|
40
|
+
this.addSql(`create unique index if not exists "user_sidebar_preferences_active_unique_idx" on "user_sidebar_preferences" ("user_id", "tenant_id", "organization_id") where "deleted_at" is null;`);
|
|
41
|
+
this.addSql(`
|
|
42
|
+
with ranked as (
|
|
43
|
+
select id,
|
|
44
|
+
row_number() over (
|
|
45
|
+
partition by role_id, tenant_id
|
|
46
|
+
order by coalesce(updated_at, created_at) desc, created_at desc, id desc
|
|
47
|
+
) as rn
|
|
48
|
+
from role_sidebar_preferences
|
|
49
|
+
where deleted_at is null
|
|
50
|
+
)
|
|
51
|
+
update role_sidebar_preferences
|
|
52
|
+
set deleted_at = now()
|
|
53
|
+
from ranked
|
|
54
|
+
where role_sidebar_preferences.id = ranked.id and ranked.rn > 1;
|
|
55
|
+
`);
|
|
56
|
+
this.addSql(`alter table "role_sidebar_preferences" drop constraint if exists "role_sidebar_preferences_role_id_tenant_id_locale_unique";`);
|
|
57
|
+
this.addSql(`drop index if exists "role_sidebar_preferences_active_unique_idx";`);
|
|
58
|
+
this.addSql(`create unique index if not exists "role_sidebar_preferences_active_unique_idx" on "role_sidebar_preferences" ("role_id", "tenant_id") where "deleted_at" is null;`);
|
|
59
|
+
}
|
|
60
|
+
down() {
|
|
61
|
+
this.addSql(`drop index if exists "sidebar_variants_active_name_unique_idx";`);
|
|
62
|
+
this.addSql(`alter table "sidebar_variants" add constraint "sidebar_variants_user_id_tenant_id_locale_name_unique" unique ("user_id", "tenant_id", "locale", "name");`);
|
|
63
|
+
this.addSql(`drop index if exists "user_sidebar_preferences_active_unique_idx";`);
|
|
64
|
+
this.addSql(`alter table "user_sidebar_preferences" add constraint "user_sidebar_preferences_user_id_tenant_id_organi_35248_unique" unique ("user_id", "tenant_id", "organization_id", "locale");`);
|
|
65
|
+
this.addSql(`drop index if exists "role_sidebar_preferences_active_unique_idx";`);
|
|
66
|
+
this.addSql(`alter table "role_sidebar_preferences" add constraint "role_sidebar_preferences_role_id_tenant_id_locale_unique" unique ("role_id", "tenant_id", "locale");`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
Migration20260427143311
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=Migration20260427143311.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/auth/migrations/Migration20260427143311.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\n// Cross-locale sidebar customization. Strip `locale` from uniqueness scope so the same\n// variant/preference applies regardless of the user's active language. Soft-delete any\n// historical duplicates first (keep the most-recently-updated row per scope) so the new\n// constraints can be created without violations.\nexport class Migration20260427143311 extends Migration {\n\n override up(): void | Promise<void> {\n // --- sidebar_variants: collapse (user_id, tenant_id, name) across locales ---------\n this.addSql(`\n with ranked as (\n select id,\n row_number() over (\n partition by user_id, tenant_id, name\n order by coalesce(updated_at, created_at) desc, created_at desc, id desc\n ) as rn\n from sidebar_variants\n where deleted_at is null\n )\n update sidebar_variants\n set deleted_at = now()\n from ranked\n where sidebar_variants.id = ranked.id and ranked.rn > 1;\n `);\n this.addSql(`drop index if exists \"sidebar_variants_active_name_unique_idx\";`);\n this.addSql(`alter table \"sidebar_variants\" drop constraint if exists \"sidebar_variants_user_id_tenant_id_locale_name_unique\";`);\n this.addSql(`create unique index if not exists \"sidebar_variants_active_name_unique_idx\" on \"sidebar_variants\" (\"user_id\", \"tenant_id\", \"name\") where \"deleted_at\" is null;`);\n\n // --- user_sidebar_preferences: collapse (user_id, tenant_id, organization_id) -----\n this.addSql(`\n with ranked as (\n select id,\n row_number() over (\n partition by user_id, tenant_id, organization_id\n order by coalesce(updated_at, created_at) desc, created_at desc, id desc\n ) as rn\n from user_sidebar_preferences\n where deleted_at is null\n )\n update user_sidebar_preferences\n set deleted_at = now()\n from ranked\n where user_sidebar_preferences.id = ranked.id and ranked.rn > 1;\n `);\n this.addSql(`alter table \"user_sidebar_preferences\" drop constraint if exists \"user_sidebar_preferences_user_id_tenant_id_organi_35248_unique\";`);\n this.addSql(`alter table \"user_sidebar_preferences\" drop constraint if exists \"user_sidebar_preferences_user_id_tenant_id_organi_f3f2f_unique\";`);\n this.addSql(`drop index if exists \"user_sidebar_preferences_active_unique_idx\";`);\n this.addSql(`create unique index if not exists \"user_sidebar_preferences_active_unique_idx\" on \"user_sidebar_preferences\" (\"user_id\", \"tenant_id\", \"organization_id\") where \"deleted_at\" is null;`);\n\n // --- role_sidebar_preferences: collapse (role_id, tenant_id) ----------------------\n this.addSql(`\n with ranked as (\n select id,\n row_number() over (\n partition by role_id, tenant_id\n order by coalesce(updated_at, created_at) desc, created_at desc, id desc\n ) as rn\n from role_sidebar_preferences\n where deleted_at is null\n )\n update role_sidebar_preferences\n set deleted_at = now()\n from ranked\n where role_sidebar_preferences.id = ranked.id and ranked.rn > 1;\n `);\n this.addSql(`alter table \"role_sidebar_preferences\" drop constraint if exists \"role_sidebar_preferences_role_id_tenant_id_locale_unique\";`);\n this.addSql(`drop index if exists \"role_sidebar_preferences_active_unique_idx\";`);\n this.addSql(`create unique index if not exists \"role_sidebar_preferences_active_unique_idx\" on \"role_sidebar_preferences\" (\"role_id\", \"tenant_id\") where \"deleted_at\" is null;`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`drop index if exists \"sidebar_variants_active_name_unique_idx\";`);\n this.addSql(`alter table \"sidebar_variants\" add constraint \"sidebar_variants_user_id_tenant_id_locale_name_unique\" unique (\"user_id\", \"tenant_id\", \"locale\", \"name\");`);\n\n this.addSql(`drop index if exists \"user_sidebar_preferences_active_unique_idx\";`);\n this.addSql(`alter table \"user_sidebar_preferences\" add constraint \"user_sidebar_preferences_user_id_tenant_id_organi_35248_unique\" unique (\"user_id\", \"tenant_id\", \"organization_id\", \"locale\");`);\n\n this.addSql(`drop index if exists \"role_sidebar_preferences_active_unique_idx\";`);\n this.addSql(`alter table \"role_sidebar_preferences\" add constraint \"role_sidebar_preferences_role_id_tenant_id_locale_unique\" unique (\"role_id\", \"tenant_id\", \"locale\");`);\n }\n\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAMnB,MAAM,gCAAgC,UAAU;AAAA,EAE5C,KAA2B;AAElC,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAcX;AACD,SAAK,OAAO,iEAAiE;AAC7E,SAAK,OAAO,mHAAmH;AAC/H,SAAK,OAAO,gKAAgK;AAG5K,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAcX;AACD,SAAK,OAAO,oIAAoI;AAChJ,SAAK,OAAO,oIAAoI;AAChJ,SAAK,OAAO,oEAAoE;AAChF,SAAK,OAAO,sLAAsL;AAGlM,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAcX;AACD,SAAK,OAAO,8HAA8H;AAC1I,SAAK,OAAO,oEAAoE;AAChF,SAAK,OAAO,mKAAmK;AAAA,EACjL;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,iEAAiE;AAC7E,SAAK,OAAO,0JAA0J;AAEtK,SAAK,OAAO,oEAAoE;AAChF,SAAK,OAAO,sLAAsL;AAElM,SAAK,OAAO,oEAAoE;AAChF,SAAK,OAAO,6JAA6J;AAAA,EAC3K;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
2
|
+
import { Role, RoleSidebarPreference, SidebarVariant, User, UserSidebarPreference } from "../data/entities.js";
|
|
2
3
|
import {
|
|
3
4
|
SIDEBAR_PREFERENCES_VERSION,
|
|
4
5
|
normalizeSidebarSettings
|
|
5
6
|
} from "@open-mercato/shared/modules/navigation/sidebarPreferences";
|
|
6
7
|
async function loadSidebarPreference(em, scope) {
|
|
7
|
-
const { userId, tenantId, organizationId
|
|
8
|
-
const existing = await
|
|
8
|
+
const { userId, tenantId, organizationId } = normalizeScope(scope);
|
|
9
|
+
const existing = await findOneWithDecryption(
|
|
10
|
+
em,
|
|
11
|
+
UserSidebarPreference,
|
|
12
|
+
{ user: userId, tenantId, organizationId },
|
|
13
|
+
void 0,
|
|
14
|
+
{ tenantId, organizationId }
|
|
15
|
+
);
|
|
9
16
|
return normalizeSidebarSettings(existing?.settingsJson);
|
|
10
17
|
}
|
|
11
18
|
async function saveSidebarPreference(em, scope, input) {
|
|
@@ -14,7 +21,13 @@ async function saveSidebarPreference(em, scope, input) {
|
|
|
14
21
|
version: input?.version ?? SIDEBAR_PREFERENCES_VERSION
|
|
15
22
|
});
|
|
16
23
|
const { userId, tenantId, organizationId, locale } = normalizeScope(scope);
|
|
17
|
-
let pref = await
|
|
24
|
+
let pref = await findOneWithDecryption(
|
|
25
|
+
em,
|
|
26
|
+
UserSidebarPreference,
|
|
27
|
+
{ user: userId, tenantId, organizationId },
|
|
28
|
+
void 0,
|
|
29
|
+
{ tenantId, organizationId }
|
|
30
|
+
);
|
|
18
31
|
if (!pref) {
|
|
19
32
|
pref = em.create(UserSidebarPreference, {
|
|
20
33
|
user: em.getReference(User, userId),
|
|
@@ -34,11 +47,13 @@ async function loadRoleSidebarPreferences(em, options) {
|
|
|
34
47
|
if (!options.roleIds.length) return /* @__PURE__ */ new Map();
|
|
35
48
|
const tenantId = options.tenantId ?? null;
|
|
36
49
|
const tenantFilter = tenantId === null ? null : { $in: [tenantId, null] };
|
|
37
|
-
const prefs = await
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
const prefs = await findWithDecryption(
|
|
51
|
+
em,
|
|
52
|
+
RoleSidebarPreference,
|
|
53
|
+
{ role: { $in: options.roleIds }, tenantId: tenantFilter },
|
|
54
|
+
void 0,
|
|
55
|
+
{ tenantId, organizationId: null }
|
|
56
|
+
);
|
|
42
57
|
const map = /* @__PURE__ */ new Map();
|
|
43
58
|
for (const pref of prefs) {
|
|
44
59
|
const key = pref.role.id;
|
|
@@ -58,11 +73,13 @@ async function loadFirstRoleSidebarPreference(em, options) {
|
|
|
58
73
|
if (!options.roleIds.length) return null;
|
|
59
74
|
const tenantId = options.tenantId ?? null;
|
|
60
75
|
const tenantFilter = tenantId === null ? null : { $in: [tenantId, null] };
|
|
61
|
-
const prefs = await
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
const prefs = await findWithDecryption(
|
|
77
|
+
em,
|
|
78
|
+
RoleSidebarPreference,
|
|
79
|
+
{ role: { $in: options.roleIds }, tenantId: tenantFilter },
|
|
80
|
+
void 0,
|
|
81
|
+
{ tenantId, organizationId: null }
|
|
82
|
+
);
|
|
66
83
|
if (!prefs.length) return null;
|
|
67
84
|
const ordered = options.roleIds.map((id) => {
|
|
68
85
|
if (tenantId !== null) {
|
|
@@ -80,7 +97,13 @@ async function saveRoleSidebarPreference(em, scope, input) {
|
|
|
80
97
|
version: input?.version ?? SIDEBAR_PREFERENCES_VERSION
|
|
81
98
|
});
|
|
82
99
|
const { roleId, tenantId, locale } = normalizeRoleScope(scope);
|
|
83
|
-
let pref = await
|
|
100
|
+
let pref = await findOneWithDecryption(
|
|
101
|
+
em,
|
|
102
|
+
RoleSidebarPreference,
|
|
103
|
+
{ role: roleId, tenantId },
|
|
104
|
+
void 0,
|
|
105
|
+
{ tenantId, organizationId: null }
|
|
106
|
+
);
|
|
84
107
|
if (!pref) {
|
|
85
108
|
pref = em.create(RoleSidebarPreference, {
|
|
86
109
|
role: em.getReference(Role, roleId),
|
|
@@ -156,12 +179,149 @@ function normalizeRoleScope(scope) {
|
|
|
156
179
|
locale: scope.locale
|
|
157
180
|
};
|
|
158
181
|
}
|
|
182
|
+
function toVariantRecord(variant) {
|
|
183
|
+
return {
|
|
184
|
+
id: variant.id,
|
|
185
|
+
name: variant.name,
|
|
186
|
+
isActive: variant.isActive === true,
|
|
187
|
+
settings: normalizeSidebarSettings(variant.settingsJson),
|
|
188
|
+
createdAt: variant.createdAt,
|
|
189
|
+
updatedAt: variant.updatedAt ?? null
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async function listSidebarVariants(em, scope) {
|
|
193
|
+
const { userId, tenantId, organizationId } = normalizeVariantScope(scope);
|
|
194
|
+
const variants = await findWithDecryption(
|
|
195
|
+
em,
|
|
196
|
+
SidebarVariant,
|
|
197
|
+
{ user: userId, tenantId, deletedAt: null },
|
|
198
|
+
{ orderBy: { createdAt: "asc" } },
|
|
199
|
+
{ tenantId, organizationId }
|
|
200
|
+
);
|
|
201
|
+
return variants.map(toVariantRecord);
|
|
202
|
+
}
|
|
203
|
+
async function loadSidebarVariant(em, scope, variantId) {
|
|
204
|
+
const { userId, tenantId, organizationId } = normalizeVariantScope(scope);
|
|
205
|
+
const variant = await findOneWithDecryption(
|
|
206
|
+
em,
|
|
207
|
+
SidebarVariant,
|
|
208
|
+
{ id: variantId, user: userId, tenantId, deletedAt: null },
|
|
209
|
+
void 0,
|
|
210
|
+
{ tenantId, organizationId }
|
|
211
|
+
);
|
|
212
|
+
return variant ? toVariantRecord(variant) : null;
|
|
213
|
+
}
|
|
214
|
+
async function nextVariantAutoName(em, scope, prefix = "My preferences") {
|
|
215
|
+
const variants = await listSidebarVariants(em, scope);
|
|
216
|
+
const usedNumbers = /* @__PURE__ */ new Set();
|
|
217
|
+
for (const variant of variants) {
|
|
218
|
+
if (variant.name === prefix) {
|
|
219
|
+
usedNumbers.add(1);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const escaped = prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
223
|
+
const match = variant.name.match(new RegExp(`^${escaped}\\s+(\\d+)$`));
|
|
224
|
+
if (match) {
|
|
225
|
+
const n = Number.parseInt(match[1], 10);
|
|
226
|
+
if (!Number.isNaN(n)) usedNumbers.add(n);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (!usedNumbers.has(1)) return prefix;
|
|
230
|
+
let next = 2;
|
|
231
|
+
while (usedNumbers.has(next)) next += 1;
|
|
232
|
+
return `${prefix} ${next}`;
|
|
233
|
+
}
|
|
234
|
+
async function createSidebarVariant(em, scope, input) {
|
|
235
|
+
const { userId, tenantId, organizationId, locale } = normalizeVariantScope(scope);
|
|
236
|
+
const finalName = (input.name ?? "").trim() || await nextVariantAutoName(em, scope);
|
|
237
|
+
const settings = normalizeSidebarSettings({
|
|
238
|
+
...input.settings ?? {},
|
|
239
|
+
version: input.settings?.version ?? SIDEBAR_PREFERENCES_VERSION
|
|
240
|
+
});
|
|
241
|
+
if (input.isActive === true) {
|
|
242
|
+
await deactivateAllVariants(em, scope);
|
|
243
|
+
}
|
|
244
|
+
const variant = em.create(SidebarVariant, {
|
|
245
|
+
user: em.getReference(User, userId),
|
|
246
|
+
tenantId,
|
|
247
|
+
organizationId,
|
|
248
|
+
locale,
|
|
249
|
+
name: finalName,
|
|
250
|
+
settingsJson: settings,
|
|
251
|
+
isActive: input.isActive === true,
|
|
252
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
253
|
+
});
|
|
254
|
+
await em.flush();
|
|
255
|
+
return toVariantRecord(variant);
|
|
256
|
+
}
|
|
257
|
+
async function updateSidebarVariant(em, scope, variantId, input) {
|
|
258
|
+
const { userId, tenantId, organizationId } = normalizeVariantScope(scope);
|
|
259
|
+
const variant = await findOneWithDecryption(
|
|
260
|
+
em,
|
|
261
|
+
SidebarVariant,
|
|
262
|
+
{ id: variantId, user: userId, tenantId, deletedAt: null },
|
|
263
|
+
void 0,
|
|
264
|
+
{ tenantId, organizationId }
|
|
265
|
+
);
|
|
266
|
+
if (!variant) return null;
|
|
267
|
+
if (typeof input.name === "string" && input.name.trim().length > 0) {
|
|
268
|
+
variant.name = input.name.trim();
|
|
269
|
+
}
|
|
270
|
+
if (input.settings) {
|
|
271
|
+
variant.settingsJson = normalizeSidebarSettings({
|
|
272
|
+
...input.settings,
|
|
273
|
+
version: input.settings.version ?? SIDEBAR_PREFERENCES_VERSION
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (typeof input.isActive === "boolean") {
|
|
277
|
+
if (input.isActive) {
|
|
278
|
+
await deactivateAllVariants(em, scope, variantId);
|
|
279
|
+
}
|
|
280
|
+
variant.isActive = input.isActive;
|
|
281
|
+
}
|
|
282
|
+
await em.flush();
|
|
283
|
+
return toVariantRecord(variant);
|
|
284
|
+
}
|
|
285
|
+
async function deleteSidebarVariant(em, scope, variantId) {
|
|
286
|
+
const { userId, tenantId, organizationId } = normalizeVariantScope(scope);
|
|
287
|
+
const variant = await findOneWithDecryption(
|
|
288
|
+
em,
|
|
289
|
+
SidebarVariant,
|
|
290
|
+
{ id: variantId, user: userId, tenantId, deletedAt: null },
|
|
291
|
+
void 0,
|
|
292
|
+
{ tenantId, organizationId }
|
|
293
|
+
);
|
|
294
|
+
if (!variant) return false;
|
|
295
|
+
variant.deletedAt = /* @__PURE__ */ new Date();
|
|
296
|
+
variant.isActive = false;
|
|
297
|
+
await em.flush();
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
async function deactivateAllVariants(em, scope, exceptId) {
|
|
301
|
+
const { userId, tenantId } = normalizeVariantScope(scope);
|
|
302
|
+
const where = exceptId ? { user: userId, tenantId, isActive: true, deletedAt: null, id: { $ne: exceptId } } : { user: userId, tenantId, isActive: true, deletedAt: null };
|
|
303
|
+
await em.nativeUpdate(SidebarVariant, where, { isActive: false });
|
|
304
|
+
}
|
|
305
|
+
function normalizeVariantScope(scope) {
|
|
306
|
+
return {
|
|
307
|
+
userId: scope.userId,
|
|
308
|
+
tenantId: scope.tenantId ?? null,
|
|
309
|
+
organizationId: scope.organizationId ?? null,
|
|
310
|
+
locale: scope.locale
|
|
311
|
+
};
|
|
312
|
+
}
|
|
159
313
|
export {
|
|
160
314
|
applySidebarPreference,
|
|
315
|
+
createSidebarVariant,
|
|
316
|
+
deleteSidebarVariant,
|
|
317
|
+
listSidebarVariants,
|
|
161
318
|
loadFirstRoleSidebarPreference,
|
|
162
319
|
loadRoleSidebarPreferences,
|
|
163
320
|
loadSidebarPreference,
|
|
321
|
+
loadSidebarVariant,
|
|
322
|
+
nextVariantAutoName,
|
|
164
323
|
saveRoleSidebarPreference,
|
|
165
|
-
saveSidebarPreference
|
|
324
|
+
saveSidebarPreference,
|
|
325
|
+
updateSidebarVariant
|
|
166
326
|
};
|
|
167
327
|
//# sourceMappingURL=sidebarPreferencesService.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/auth/services/sidebarPreferencesService.ts"],
|
|
4
|
-
"sourcesContent": ["import { EntityManager } from '@mikro-orm/postgresql'\nimport { Role, RoleSidebarPreference, User, UserSidebarPreference } from '../data/entities'\nimport {\n SIDEBAR_PREFERENCES_VERSION,\n SidebarPreferencesSettings,\n normalizeSidebarSettings,\n} from '@open-mercato/shared/modules/navigation/sidebarPreferences'\n\nexport type SidebarPreferenceScope = {\n userId: string\n tenantId?: string | null\n organizationId?: string | null\n locale: string\n}\n\nexport type RoleSidebarPreferenceScope = {\n roleId: string\n tenantId?: string | null\n locale: string\n}\n\nexport type SidebarItemLike<T = Record<string, unknown>> = {\n id?: string\n href: string\n title: string\n defaultTitle: string\n children?: SidebarItemLike<T>[]\n} & T\n\nexport type SidebarGroupLike<T = Record<string, unknown>> = {\n id: string\n name: string\n defaultName: string\n items: SidebarItemLike<T>[]\n weight?: number\n} & T\n\nexport async function loadSidebarPreference(\n em: EntityManager,\n scope: SidebarPreferenceScope,\n): Promise<SidebarPreferencesSettings> {\n const { userId, tenantId, organizationId, locale } = normalizeScope(scope)\n const existing = await em.findOne(UserSidebarPreference, { user: userId, tenantId, organizationId, locale })\n return normalizeSidebarSettings(existing?.settingsJson as SidebarPreferencesSettings | undefined)\n}\n\nexport async function saveSidebarPreference(\n em: EntityManager,\n scope: SidebarPreferenceScope,\n input: SidebarPreferencesSettings,\n): Promise<SidebarPreferencesSettings> {\n const normalized = normalizeSidebarSettings({\n ...input,\n version: input?.version ?? SIDEBAR_PREFERENCES_VERSION,\n })\n const { userId, tenantId, organizationId, locale } = normalizeScope(scope)\n let pref = await em.findOne(UserSidebarPreference, { user: userId, tenantId, organizationId, locale })\n if (!pref) {\n pref = em.create(UserSidebarPreference, {\n user: em.getReference(User, userId),\n tenantId,\n organizationId,\n locale,\n settingsJson: normalized,\n createdAt: new Date(),\n })\n } else {\n pref.settingsJson = normalized\n }\n await em.flush()\n return normalized\n}\n\nexport async function loadRoleSidebarPreferences(\n em: EntityManager,\n options: { roleIds: string[]; tenantId?: string | null; locale: string },\n): Promise<Map<string, SidebarPreferencesSettings>> {\n if (!options.roleIds.length) return new Map()\n const tenantId = options.tenantId ?? null\n const tenantFilter = tenantId === null ? null : { $in: [tenantId, null] }\n const prefs = await em.find(RoleSidebarPreference, {\n role: { $in: options.roleIds },\n tenantId: tenantFilter,\n locale: options.locale,\n })\n const map = new Map<string, SidebarPreferencesSettings>()\n for (const pref of prefs) {\n const key = pref.role.id\n if (tenantId !== null) {\n const existing = map.get(key)\n if (existing && pref.tenantId === null) continue\n if (!existing || pref.tenantId === tenantId) {\n map.set(key, normalizeSidebarSettings(pref.settingsJson as SidebarPreferencesSettings | undefined))\n }\n continue\n }\n map.set(key, normalizeSidebarSettings(pref.settingsJson as SidebarPreferencesSettings | undefined))\n }\n return map\n}\n\nexport async function loadFirstRoleSidebarPreference(\n em: EntityManager,\n options: { roleIds: string[]; tenantId?: string | null; locale: string },\n): Promise<SidebarPreferencesSettings | null> {\n if (!options.roleIds.length) return null\n const tenantId = options.tenantId ?? null\n const tenantFilter = tenantId === null ? null : { $in: [tenantId, null] }\n const prefs = await em.find(RoleSidebarPreference, {\n role: { $in: options.roleIds },\n tenantId: tenantFilter,\n locale: options.locale,\n })\n if (!prefs.length) return null\n const ordered = options.roleIds\n .map((id) => {\n if (tenantId !== null) {\n const specific = prefs.find((pref) => pref.role.id === id && pref.tenantId === tenantId)\n if (specific) return specific\n }\n return prefs.find((pref) => pref.role.id === id && pref.tenantId === null)\n })\n .filter(Boolean) as RoleSidebarPreference[]\n const first = ordered[0] ?? prefs[0]\n return normalizeSidebarSettings(first?.settingsJson as SidebarPreferencesSettings | undefined)\n}\n\nexport async function saveRoleSidebarPreference(\n em: EntityManager,\n scope: RoleSidebarPreferenceScope,\n input: SidebarPreferencesSettings,\n): Promise<SidebarPreferencesSettings> {\n const normalized = normalizeSidebarSettings({\n ...input,\n version: input?.version ?? SIDEBAR_PREFERENCES_VERSION,\n })\n const { roleId, tenantId, locale } = normalizeRoleScope(scope)\n let pref = await em.findOne(RoleSidebarPreference, { role: roleId, tenantId, locale })\n if (!pref) {\n pref = em.create(RoleSidebarPreference, {\n role: em.getReference(Role, roleId),\n tenantId,\n locale,\n settingsJson: normalized,\n createdAt: new Date(),\n })\n } else {\n pref.settingsJson = normalized\n }\n await em.flush()\n return normalized\n}\n\nexport function applySidebarPreference<T extends SidebarGroupLike>(\n groups: T[],\n settings?: SidebarPreferencesSettings | null,\n): T[] {\n const normalized = normalizeSidebarSettings(settings)\n const orderIndex = new Map<string, number>()\n normalized.groupOrder?.forEach((id, idx) => {\n if (!orderIndex.has(id)) orderIndex.set(id, idx)\n })\n const hiddenSet = new Set(normalized.hiddenItems ?? [])\n const resolveItemKey = (item: SidebarItemLike): string => {\n const candidate = item.id?.trim()\n if (candidate && candidate.length > 0) return candidate\n return item.href\n }\n const applyItems = <TI extends SidebarItemLike>(items: TI[]): TI[] => {\n return items.map((item) => {\n const itemKey = resolveItemKey(item)\n const override = normalized.itemLabels?.[itemKey] ?? normalized.itemLabels?.[item.href]\n const nextChildren = item.children ? applyItems(item.children) : undefined\n const hidden = hiddenSet.has(itemKey) || hiddenSet.has(item.href)\n const next = {\n ...item,\n title: override && override.trim().length > 0 ? override.trim() : item.defaultTitle,\n children: nextChildren,\n } as TI & { hidden?: boolean }\n next.hidden = hidden\n return next\n })\n }\n const mapped = groups.map((group) => {\n const override = normalized.groupLabels?.[group.id]\n return {\n ...group,\n name: override && override.trim().length > 0 ? override.trim() : group.defaultName,\n items: applyItems(group.items),\n }\n })\n mapped.sort((a, b) => {\n const ao = orderIndex.has(a.id) ? orderIndex.get(a.id)! : Number.POSITIVE_INFINITY\n const bo = orderIndex.has(b.id) ? orderIndex.get(b.id)! : Number.POSITIVE_INFINITY\n if (ao !== bo) return ao - bo\n const aw = typeof a.weight === 'number' ? a.weight : 10_000\n const bw = typeof b.weight === 'number' ? b.weight : 10_000\n if (aw !== bw) return aw - bw\n return a.defaultName.localeCompare(b.defaultName)\n })\n return mapped\n}\n\nfunction normalizeScope(scope: SidebarPreferenceScope) {\n return {\n userId: scope.userId,\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId ?? null,\n locale: scope.locale,\n }\n}\n\nfunction normalizeRoleScope(scope: RoleSidebarPreferenceScope) {\n return {\n roleId: scope.roleId,\n tenantId: scope.tenantId ?? null,\n locale: scope.locale,\n }\n}\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,MAAM,uBAAuB,MAAM,6BAA6B;
|
|
4
|
+
"sourcesContent": ["import { EntityManager, type FilterQuery } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { Role, RoleSidebarPreference, SidebarVariant, User, UserSidebarPreference } from '../data/entities'\nimport {\n SIDEBAR_PREFERENCES_VERSION,\n SidebarPreferencesSettings,\n normalizeSidebarSettings,\n} from '@open-mercato/shared/modules/navigation/sidebarPreferences'\n\nexport type SidebarPreferenceScope = {\n userId: string\n tenantId?: string | null\n organizationId?: string | null\n locale: string\n}\n\nexport type RoleSidebarPreferenceScope = {\n roleId: string\n tenantId?: string | null\n locale: string\n}\n\nexport type SidebarItemLike<T = Record<string, unknown>> = {\n id?: string\n href: string\n title: string\n defaultTitle: string\n children?: SidebarItemLike<T>[]\n} & T\n\nexport type SidebarGroupLike<T = Record<string, unknown>> = {\n id: string\n name: string\n defaultName: string\n items: SidebarItemLike<T>[]\n weight?: number\n} & T\n\nexport async function loadSidebarPreference(\n em: EntityManager,\n scope: SidebarPreferenceScope,\n): Promise<SidebarPreferencesSettings> {\n // Cross-locale: variants & preferences are scoped per (user, tenant, org) only.\n // The `locale` field on the row is kept for audit / when the row was created.\n const { userId, tenantId, organizationId } = normalizeScope(scope)\n const existing = await findOneWithDecryption(\n em,\n UserSidebarPreference,\n { user: userId, tenantId, organizationId },\n undefined,\n { tenantId, organizationId },\n )\n return normalizeSidebarSettings(existing?.settingsJson as SidebarPreferencesSettings | undefined)\n}\n\nexport async function saveSidebarPreference(\n em: EntityManager,\n scope: SidebarPreferenceScope,\n input: SidebarPreferencesSettings,\n): Promise<SidebarPreferencesSettings> {\n const normalized = normalizeSidebarSettings({\n ...input,\n version: input?.version ?? SIDEBAR_PREFERENCES_VERSION,\n })\n const { userId, tenantId, organizationId, locale } = normalizeScope(scope)\n let pref = await findOneWithDecryption(\n em,\n UserSidebarPreference,\n { user: userId, tenantId, organizationId },\n undefined,\n { tenantId, organizationId },\n )\n if (!pref) {\n pref = em.create(UserSidebarPreference, {\n user: em.getReference(User, userId),\n tenantId,\n organizationId,\n locale,\n settingsJson: normalized,\n createdAt: new Date(),\n })\n } else {\n pref.settingsJson = normalized\n }\n await em.flush()\n return normalized\n}\n\nexport async function loadRoleSidebarPreferences(\n em: EntityManager,\n options: { roleIds: string[]; tenantId?: string | null; locale?: string },\n): Promise<Map<string, SidebarPreferencesSettings>> {\n if (!options.roleIds.length) return new Map()\n const tenantId = options.tenantId ?? null\n const tenantFilter = tenantId === null ? null : { $in: [tenantId, null] }\n const prefs = await findWithDecryption(\n em,\n RoleSidebarPreference,\n { role: { $in: options.roleIds }, tenantId: tenantFilter } as FilterQuery<RoleSidebarPreference>,\n undefined,\n { tenantId, organizationId: null },\n )\n const map = new Map<string, SidebarPreferencesSettings>()\n for (const pref of prefs) {\n const key = pref.role.id\n if (tenantId !== null) {\n const existing = map.get(key)\n if (existing && pref.tenantId === null) continue\n if (!existing || pref.tenantId === tenantId) {\n map.set(key, normalizeSidebarSettings(pref.settingsJson as SidebarPreferencesSettings | undefined))\n }\n continue\n }\n map.set(key, normalizeSidebarSettings(pref.settingsJson as SidebarPreferencesSettings | undefined))\n }\n return map\n}\n\nexport async function loadFirstRoleSidebarPreference(\n em: EntityManager,\n options: { roleIds: string[]; tenantId?: string | null; locale?: string },\n): Promise<SidebarPreferencesSettings | null> {\n if (!options.roleIds.length) return null\n const tenantId = options.tenantId ?? null\n const tenantFilter = tenantId === null ? null : { $in: [tenantId, null] }\n const prefs = await findWithDecryption(\n em,\n RoleSidebarPreference,\n { role: { $in: options.roleIds }, tenantId: tenantFilter } as FilterQuery<RoleSidebarPreference>,\n undefined,\n { tenantId, organizationId: null },\n )\n if (!prefs.length) return null\n const ordered = options.roleIds\n .map((id) => {\n if (tenantId !== null) {\n const specific = prefs.find((pref) => pref.role.id === id && pref.tenantId === tenantId)\n if (specific) return specific\n }\n return prefs.find((pref) => pref.role.id === id && pref.tenantId === null)\n })\n .filter(Boolean) as RoleSidebarPreference[]\n const first = ordered[0] ?? prefs[0]\n return normalizeSidebarSettings(first?.settingsJson as SidebarPreferencesSettings | undefined)\n}\n\nexport async function saveRoleSidebarPreference(\n em: EntityManager,\n scope: RoleSidebarPreferenceScope,\n input: SidebarPreferencesSettings,\n): Promise<SidebarPreferencesSettings> {\n const normalized = normalizeSidebarSettings({\n ...input,\n version: input?.version ?? SIDEBAR_PREFERENCES_VERSION,\n })\n const { roleId, tenantId, locale } = normalizeRoleScope(scope)\n let pref = await findOneWithDecryption(\n em,\n RoleSidebarPreference,\n { role: roleId, tenantId },\n undefined,\n { tenantId, organizationId: null },\n )\n if (!pref) {\n pref = em.create(RoleSidebarPreference, {\n role: em.getReference(Role, roleId),\n tenantId,\n locale,\n settingsJson: normalized,\n createdAt: new Date(),\n })\n } else {\n pref.settingsJson = normalized\n }\n await em.flush()\n return normalized\n}\n\nexport function applySidebarPreference<T extends SidebarGroupLike>(\n groups: T[],\n settings?: SidebarPreferencesSettings | null,\n): T[] {\n const normalized = normalizeSidebarSettings(settings)\n const orderIndex = new Map<string, number>()\n normalized.groupOrder?.forEach((id, idx) => {\n if (!orderIndex.has(id)) orderIndex.set(id, idx)\n })\n const hiddenSet = new Set(normalized.hiddenItems ?? [])\n const resolveItemKey = (item: SidebarItemLike): string => {\n const candidate = item.id?.trim()\n if (candidate && candidate.length > 0) return candidate\n return item.href\n }\n const applyItems = <TI extends SidebarItemLike>(items: TI[]): TI[] => {\n return items.map((item) => {\n const itemKey = resolveItemKey(item)\n const override = normalized.itemLabels?.[itemKey] ?? normalized.itemLabels?.[item.href]\n const nextChildren = item.children ? applyItems(item.children) : undefined\n const hidden = hiddenSet.has(itemKey) || hiddenSet.has(item.href)\n const next = {\n ...item,\n title: override && override.trim().length > 0 ? override.trim() : item.defaultTitle,\n children: nextChildren,\n } as TI & { hidden?: boolean }\n next.hidden = hidden\n return next\n })\n }\n const mapped = groups.map((group) => {\n const override = normalized.groupLabels?.[group.id]\n return {\n ...group,\n name: override && override.trim().length > 0 ? override.trim() : group.defaultName,\n items: applyItems(group.items),\n }\n })\n mapped.sort((a, b) => {\n const ao = orderIndex.has(a.id) ? orderIndex.get(a.id)! : Number.POSITIVE_INFINITY\n const bo = orderIndex.has(b.id) ? orderIndex.get(b.id)! : Number.POSITIVE_INFINITY\n if (ao !== bo) return ao - bo\n const aw = typeof a.weight === 'number' ? a.weight : 10_000\n const bw = typeof b.weight === 'number' ? b.weight : 10_000\n if (aw !== bw) return aw - bw\n return a.defaultName.localeCompare(b.defaultName)\n })\n return mapped\n}\n\nfunction normalizeScope(scope: SidebarPreferenceScope) {\n return {\n userId: scope.userId,\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId ?? null,\n locale: scope.locale,\n }\n}\n\nfunction normalizeRoleScope(scope: RoleSidebarPreferenceScope) {\n return {\n roleId: scope.roleId,\n tenantId: scope.tenantId ?? null,\n locale: scope.locale,\n }\n}\n\n// --- Named variants (per-user library of saved sidebar layouts) ----------------\n\nexport type VariantScope = {\n userId: string\n tenantId?: string | null\n organizationId?: string | null\n locale: string\n}\n\nexport type SidebarVariantRecord = {\n id: string\n name: string\n isActive: boolean\n settings: SidebarPreferencesSettings\n createdAt: Date\n updatedAt?: Date | null\n}\n\nfunction toVariantRecord(variant: SidebarVariant): SidebarVariantRecord {\n return {\n id: variant.id,\n name: variant.name,\n isActive: variant.isActive === true,\n settings: normalizeSidebarSettings(variant.settingsJson as SidebarPreferencesSettings | undefined),\n createdAt: variant.createdAt,\n updatedAt: variant.updatedAt ?? null,\n }\n}\n\nexport async function listSidebarVariants(\n em: EntityManager,\n scope: VariantScope,\n): Promise<SidebarVariantRecord[]> {\n // Cross-locale: variants are scoped per (user, tenant) only.\n const { userId, tenantId, organizationId } = normalizeVariantScope(scope)\n const variants = await findWithDecryption(\n em,\n SidebarVariant,\n { user: userId, tenantId, deletedAt: null },\n { orderBy: { createdAt: 'asc' } },\n { tenantId, organizationId },\n )\n return variants.map(toVariantRecord)\n}\n\nexport async function loadSidebarVariant(\n em: EntityManager,\n scope: VariantScope,\n variantId: string,\n): Promise<SidebarVariantRecord | null> {\n const { userId, tenantId, organizationId } = normalizeVariantScope(scope)\n const variant = await findOneWithDecryption(\n em,\n SidebarVariant,\n { id: variantId, user: userId, tenantId, deletedAt: null },\n undefined,\n { tenantId, organizationId },\n )\n return variant ? toVariantRecord(variant) : null\n}\n\nexport async function nextVariantAutoName(\n em: EntityManager,\n scope: VariantScope,\n prefix = 'My preferences',\n): Promise<string> {\n const variants = await listSidebarVariants(em, scope)\n // Match names like \"My preferences\", \"My preferences 2\", \"My preferences 17\"\n const usedNumbers = new Set<number>()\n for (const variant of variants) {\n if (variant.name === prefix) {\n usedNumbers.add(1)\n continue\n }\n const escaped = prefix.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const match = variant.name.match(new RegExp(`^${escaped}\\\\s+(\\\\d+)$`))\n if (match) {\n const n = Number.parseInt(match[1], 10)\n if (!Number.isNaN(n)) usedNumbers.add(n)\n }\n }\n if (!usedNumbers.has(1)) return prefix\n let next = 2\n while (usedNumbers.has(next)) next += 1\n return `${prefix} ${next}`\n}\n\nexport async function createSidebarVariant(\n em: EntityManager,\n scope: VariantScope,\n input: {\n name?: string | null\n settings?: Partial<SidebarPreferencesSettings> | null\n isActive?: boolean\n },\n): Promise<SidebarVariantRecord> {\n const { userId, tenantId, organizationId, locale } = normalizeVariantScope(scope)\n const finalName = (input.name ?? '').trim() || (await nextVariantAutoName(em, scope))\n const settings = normalizeSidebarSettings({\n ...(input.settings ?? {}),\n version: input.settings?.version ?? SIDEBAR_PREFERENCES_VERSION,\n })\n\n if (input.isActive === true) {\n await deactivateAllVariants(em, scope)\n }\n\n const variant = em.create(SidebarVariant, {\n user: em.getReference(User, userId),\n tenantId,\n organizationId,\n locale,\n name: finalName,\n settingsJson: settings,\n isActive: input.isActive === true,\n createdAt: new Date(),\n })\n await em.flush()\n return toVariantRecord(variant)\n}\n\nexport async function updateSidebarVariant(\n em: EntityManager,\n scope: VariantScope,\n variantId: string,\n input: {\n name?: string\n settings?: Partial<SidebarPreferencesSettings> | null\n isActive?: boolean\n },\n): Promise<SidebarVariantRecord | null> {\n const { userId, tenantId, organizationId } = normalizeVariantScope(scope)\n const variant = await findOneWithDecryption(\n em,\n SidebarVariant,\n { id: variantId, user: userId, tenantId, deletedAt: null },\n undefined,\n { tenantId, organizationId },\n )\n if (!variant) return null\n if (typeof input.name === 'string' && input.name.trim().length > 0) {\n variant.name = input.name.trim()\n }\n if (input.settings) {\n variant.settingsJson = normalizeSidebarSettings({\n ...input.settings,\n version: input.settings.version ?? SIDEBAR_PREFERENCES_VERSION,\n })\n }\n if (typeof input.isActive === 'boolean') {\n if (input.isActive) {\n await deactivateAllVariants(em, scope, variantId)\n }\n variant.isActive = input.isActive\n }\n await em.flush()\n return toVariantRecord(variant)\n}\n\nexport async function deleteSidebarVariant(\n em: EntityManager,\n scope: VariantScope,\n variantId: string,\n): Promise<boolean> {\n const { userId, tenantId, organizationId } = normalizeVariantScope(scope)\n const variant = await findOneWithDecryption(\n em,\n SidebarVariant,\n { id: variantId, user: userId, tenantId, deletedAt: null },\n undefined,\n { tenantId, organizationId },\n )\n if (!variant) return false\n variant.deletedAt = new Date()\n variant.isActive = false\n await em.flush()\n return true\n}\n\nasync function deactivateAllVariants(\n em: EntityManager,\n scope: VariantScope,\n exceptId?: string,\n): Promise<void> {\n const { userId, tenantId } = normalizeVariantScope(scope)\n const where: FilterQuery<SidebarVariant> = exceptId\n ? { user: userId, tenantId, isActive: true, deletedAt: null, id: { $ne: exceptId } }\n : { user: userId, tenantId, isActive: true, deletedAt: null }\n await em.nativeUpdate(SidebarVariant, where, { isActive: false })\n}\n\nfunction normalizeVariantScope(scope: VariantScope) {\n return {\n userId: scope.userId,\n tenantId: scope.tenantId ?? null,\n organizationId: scope.organizationId ?? null,\n locale: scope.locale,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -32,6 +32,7 @@ import { CompanyHighlights } from "../../../../components/detail/CompanyHighligh
|
|
|
32
32
|
import { normalizeCustomFieldSubmitValue } from "../../../../components/detail/customFieldUtils.js";
|
|
33
33
|
import { InlineDictionaryEditor, renderMultilineMarkdownDisplay } from "../../../../components/detail/InlineEditors.js";
|
|
34
34
|
import { formatTemplate } from "../../../../components/detail/utils.js";
|
|
35
|
+
import { coerceDisplayName } from "../../../../lib/displayName.js";
|
|
35
36
|
import { createTranslatorWithFallback } from "@open-mercato/shared/lib/i18n/translate";
|
|
36
37
|
import {
|
|
37
38
|
CompanyPeopleSection
|
|
@@ -67,7 +68,8 @@ function CustomerCompanyDetailPage({ params }) {
|
|
|
67
68
|
const [sectionAction, setSectionAction] = React.useState(null);
|
|
68
69
|
const [isDeleting, setIsDeleting] = React.useState(false);
|
|
69
70
|
const currentCompanyId = data?.company?.id ?? null;
|
|
70
|
-
const
|
|
71
|
+
const companyDisplayName = coerceDisplayName(data?.company?.displayName);
|
|
72
|
+
const companyName = companyDisplayName.trim().length ? companyDisplayName : t("customers.companies.list.deleteFallbackName", "this company");
|
|
71
73
|
const translateCompanyDetail = React.useCallback(
|
|
72
74
|
(key, fallback, params2) => {
|
|
73
75
|
const mappedKey = key.startsWith("customers.people.detail.") ? key.replace("customers.people.detail.", "customers.companies.detail.") : key;
|