@open-mercato/core 0.10.1-develop.7827.1.5c631908e4 → 0.10.1-develop.7833.1.d6cbf0e3ab

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.
@@ -1,4 +1,4 @@
1
- [build:core] found 6227 entry points
1
+ [build:core] found 6230 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 271 entry points
4
4
  [build:core:generated] built successfully
@@ -0,0 +1,17 @@
1
+ import { createRequire } from "node:module";
2
+ import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ async function importFromAppRuntime(specifier) {
5
+ const resolvedPath = resolveAppRuntimeModulePath(specifier);
6
+ return await (resolvedPath ? import(pathToFileURL(resolvedPath).href) : import(specifier));
7
+ }
8
+ function resolveAppRuntimeModulePath(specifier, appRoot = process.env.OM_TEST_APP_ROOT) {
9
+ const standaloneRoot = appRoot?.trim();
10
+ if (!standaloneRoot) return null;
11
+ return createRequire(path.join(path.resolve(standaloneRoot), "package.json")).resolve(specifier);
12
+ }
13
+ export {
14
+ importFromAppRuntime,
15
+ resolveAppRuntimeModulePath
16
+ };
17
+ //# sourceMappingURL=appRuntime.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/helpers/integration/appRuntime.ts"],
4
+ "sourcesContent": ["import { createRequire } from 'node:module'\nimport path from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\n/**\n * Imports a package module from the same copy the app under test runs on.\n *\n * A spec that bootstraps the app in-process (`bootstrapFromAppRoot`) and then calls into its\n * runtime \u2014 the command bus, `withAtomicFlush`, transaction lifetime \u2014 must load every one of those\n * modules from a single copy. Against a standalone app (`OM_TEST_APP_ROOT`) the app's generated\n * registries import `node_modules/@open-mercato/*` inside the app, while the spec's own static\n * imports resolve to the monorepo's packages, so module-local state such as the command registry\n * splits in two: handlers register in one copy and the bus resolves them in the other.\n *\n * Without `OM_TEST_APP_ROOT` this is a plain dynamic import, so the monorepo lane is unchanged.\n */\nexport async function importFromAppRuntime<TModule>(specifier: string): Promise<TModule> {\n const resolvedPath = resolveAppRuntimeModulePath(specifier)\n return (await import(resolvedPath ? pathToFileURL(resolvedPath).href : specifier)) as TModule\n}\n\nexport function resolveAppRuntimeModulePath(specifier: string, appRoot = process.env.OM_TEST_APP_ROOT): string | null {\n const standaloneRoot = appRoot?.trim()\n if (!standaloneRoot) return null\n return createRequire(path.join(path.resolve(standaloneRoot), 'package.json')).resolve(specifier)\n}\n"],
5
+ "mappings": "AAAA,SAAS,qBAAqB;AAC9B,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAc9B,eAAsB,qBAA8B,WAAqC;AACvF,QAAM,eAAe,4BAA4B,SAAS;AAC1D,SAAQ,OAAa,eAAP,OAAsB,cAAc,YAAY,EAAE,QAAlD,OAAyD;AACzE;AAEO,SAAS,4BAA4B,WAAmB,UAAU,QAAQ,IAAI,kBAAiC;AACpH,QAAM,iBAAiB,SAAS,KAAK;AACrC,MAAI,CAAC,eAAgB,QAAO;AAC5B,SAAO,cAAc,KAAK,KAAK,KAAK,QAAQ,cAAc,GAAG,cAAc,CAAC,EAAE,QAAQ,SAAS;AACjG;",
6
+ "names": []
7
+ }
@@ -23,17 +23,18 @@ function appendLine(lines, label, value) {
23
23
  function resolveClaimId(record) {
24
24
  return readString(record, "id", "id");
25
25
  }
26
- function resolvePresenter(record) {
26
+ async function resolvePresenter(record) {
27
+ const { t } = await resolveTranslations();
27
28
  const claimNumber = readString(record, "claim_number", "claimNumber") ?? "Warranty claim";
28
29
  const customerName = readString(record, "customer_name", "customerName");
29
30
  const claimType = readString(record, "claim_type", "claimType");
30
31
  const status = readString(record, "status", "status");
31
- const subtitle = [customerName, claimType].filter((value) => Boolean(value)).join(" \u2014 ");
32
+ const statusLabel = status ? t(`warranty_claims.status.${status}`, status) : null;
33
+ const subtitle = [customerName, claimType, statusLabel].filter((value) => Boolean(value)).join(" \u2014 ");
32
34
  return {
33
35
  title: claimNumber,
34
36
  subtitle: subtitle || void 0,
35
- icon: "shield-check",
36
- badge: status ?? void 0
37
+ icon: "shield-check"
37
38
  };
38
39
  }
39
40
  async function loadClaimLineRows(ctx, claimId) {
@@ -113,7 +114,7 @@ const searchConfig = {
113
114
  const lineRows = claimId ? await loadClaimLineRows(ctx, claimId) : [];
114
115
  appendLineRows(lines, lineRows);
115
116
  if (!lines.length) return null;
116
- const presenter = resolvePresenter(record);
117
+ const presenter = await resolvePresenter(record);
117
118
  const url = buildClaimUrl(record);
118
119
  const links = url ? [{ href: url, label: presenter.title, kind: "primary" }] : [];
119
120
  return {
@@ -127,7 +128,7 @@ const searchConfig = {
127
128
  }
128
129
  };
129
130
  },
130
- formatResult: (ctx) => {
131
+ formatResult: async (ctx) => {
131
132
  assertTenantContext(ctx);
132
133
  return resolvePresenter(ctx.record);
133
134
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/warranty_claims/search.ts"],
4
- "sourcesContent": ["import { SortDir, type QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport type {\n SearchBuildContext,\n SearchIndexSource,\n SearchModuleConfig,\n SearchResultLink,\n SearchResultPresenter,\n} from '@open-mercato/shared/modules/search'\nimport { E } from '#generated/entities.ids.generated'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\n\nconst logger = createLogger('warranty_claims')\n\ntype SearchContext = SearchBuildContext & {\n tenantId: string\n queryEngine?: QueryEngine\n}\n\nfunction assertTenantContext(ctx: SearchBuildContext): asserts ctx is SearchContext {\n if (typeof ctx.tenantId !== 'string' || ctx.tenantId.length === 0) {\n throw new Error('[internal] [search.warranty_claims] Missing tenantId in search build context')\n }\n}\n\nfunction readString(record: Record<string, unknown>, snakeKey: string, camelKey?: string): string | null {\n const value = record[snakeKey] ?? (camelKey ? record[camelKey] : undefined)\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length ? trimmed : null\n}\n\nfunction appendLine(lines: string[], label: string, value: unknown): void {\n if (typeof value !== 'string') return\n const trimmed = value.trim()\n if (!trimmed.length) return\n lines.push(`${label}: ${trimmed}`)\n}\n\nfunction resolveClaimId(record: Record<string, unknown>): string | null {\n return readString(record, 'id', 'id')\n}\n\nfunction resolvePresenter(record: Record<string, unknown>): SearchResultPresenter {\n const claimNumber = readString(record, 'claim_number', 'claimNumber') ?? 'Warranty claim'\n const customerName = readString(record, 'customer_name', 'customerName')\n const claimType = readString(record, 'claim_type', 'claimType')\n const status = readString(record, 'status', 'status')\n const subtitle = [customerName, claimType].filter((value): value is string => Boolean(value)).join(' \u2014 ')\n return {\n title: claimNumber,\n subtitle: subtitle || undefined,\n icon: 'shield-check',\n badge: status ?? undefined,\n }\n}\n\nasync function loadClaimLineRows(ctx: SearchContext, claimId: string): Promise<Array<Record<string, unknown>>> {\n if (!ctx.queryEngine) return []\n try {\n const rows: Array<Record<string, unknown>> = []\n const pageSize = 100\n for (let page = 1; ; page += 1) {\n const result = await ctx.queryEngine.query<Record<string, unknown>>(E.warranty_claims.warranty_claim_line, {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId ?? undefined,\n filters: { claim_id: { $eq: claimId } },\n fields: [\n 'id',\n 'line_no',\n 'sku',\n 'product_name',\n 'serial_number',\n 'lot_number',\n 'fault_code',\n 'warranty_status',\n 'disposition',\n 'line_status',\n ],\n page: { page, pageSize },\n sort: [\n { field: 'line_no', dir: SortDir.Asc },\n { field: 'id', dir: SortDir.Asc },\n ],\n })\n rows.push(...result.items)\n if (result.items.length < pageSize || rows.length >= result.total) break\n }\n return rows\n } catch (err) {\n logger.warn('[search.warranty_claims] Failed to load claim lines', {\n claimId,\n error: err instanceof Error ? err.message : err,\n })\n return []\n }\n}\n\nfunction appendLineRows(lines: string[], lineRows: Array<Record<string, unknown>>): void {\n for (const row of lineRows) {\n appendLine(lines, 'Line SKU', readString(row, 'sku', 'sku') ?? '')\n appendLine(lines, 'Line product', readString(row, 'product_name', 'productName') ?? '')\n appendLine(lines, 'Serial number', readString(row, 'serial_number', 'serialNumber') ?? '')\n appendLine(lines, 'Lot number', readString(row, 'lot_number', 'lotNumber') ?? '')\n appendLine(lines, 'Fault code', readString(row, 'fault_code', 'faultCode') ?? '')\n appendLine(lines, 'Warranty status', readString(row, 'warranty_status', 'warrantyStatus') ?? '')\n appendLine(lines, 'Disposition', readString(row, 'disposition', 'disposition') ?? '')\n appendLine(lines, 'Line status', readString(row, 'line_status', 'lineStatus') ?? '')\n }\n}\n\nfunction buildClaimUrl(record: Record<string, unknown>): string | null {\n const claimId = resolveClaimId(record)\n return claimId ? `/backend/warranty_claims/${claimId}` : null\n}\n\nexport const searchConfig: SearchModuleConfig = {\n entities: [\n {\n entityId: E.warranty_claims.warranty_claim,\n enabled: true,\n priority: 20,\n\n buildSource: async (ctx: SearchBuildContext): Promise<SearchIndexSource | null> => {\n assertTenantContext(ctx)\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Claim number', readString(record, 'claim_number', 'claimNumber') ?? '')\n appendLine(lines, 'Customer', readString(record, 'customer_name', 'customerName') ?? '')\n appendLine(lines, 'Order number', readString(record, 'order_number', 'orderNumber') ?? '')\n appendLine(lines, 'Claim type', readString(record, 'claim_type', 'claimType') ?? '')\n appendLine(lines, 'Status', readString(record, 'status', 'status') ?? '')\n appendLine(lines, 'Vendor', readString(record, 'vendor_name', 'vendorName') ?? '')\n appendLine(lines, 'Vendor reference', readString(record, 'vendor_ref', 'vendorRef') ?? '')\n\n const claimId = resolveClaimId(record)\n const lineRows = claimId ? await loadClaimLineRows(ctx, claimId) : []\n appendLineRows(lines, lineRows)\n if (!lines.length) return null\n\n const presenter = resolvePresenter(record)\n const url = buildClaimUrl(record)\n const links: SearchResultLink[] = url ? [{ href: url, label: presenter.title, kind: 'primary' }] : []\n return {\n text: lines,\n presenter,\n links,\n checksumSource: {\n record,\n customFields: ctx.customFields,\n lineRows,\n },\n }\n },\n\n formatResult: (ctx: SearchBuildContext): SearchResultPresenter | null => {\n assertTenantContext(ctx)\n return resolvePresenter(ctx.record)\n },\n\n resolveUrl: (ctx: SearchBuildContext): string | null => buildClaimUrl(ctx.record),\n\n resolveLinks: async (ctx: SearchBuildContext): Promise<SearchResultLink[] | null> => {\n const url = buildClaimUrl(ctx.record)\n if (!url) return null\n const { t } = await resolveTranslations()\n return [{ href: url, label: t('warranty_claims.search.openClaim', 'Open claim'), kind: 'secondary' }]\n },\n\n fieldPolicy: {\n searchable: [\n 'claim_number',\n 'claim_type',\n 'status',\n 'priority',\n 'customer_name',\n 'vendor_name',\n 'vendor_ref',\n ],\n hashOnly: ['customer_id', 'order_id', 'sales_return_id', 'replacement_order_id', 'source_claim_id'],\n excluded: [\n 'tenant_id',\n 'organization_id',\n 'assignee_user_id',\n 'deleted_at',\n 'notes',\n 'resolution_summary',\n 'contact_email',\n 'fault_description',\n 'inspection_notes',\n 'rejection_reason_code',\n ],\n },\n aclFeatures: ['warranty_claims.claim.view'],\n },\n ],\n}\n\nexport default searchConfig\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { SortDir, type QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport type {\n SearchBuildContext,\n SearchIndexSource,\n SearchModuleConfig,\n SearchResultLink,\n SearchResultPresenter,\n} from '@open-mercato/shared/modules/search'\nimport { E } from '#generated/entities.ids.generated'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\n\nconst logger = createLogger('warranty_claims')\n\ntype SearchContext = SearchBuildContext & {\n tenantId: string\n queryEngine?: QueryEngine\n}\n\nfunction assertTenantContext(ctx: SearchBuildContext): asserts ctx is SearchContext {\n if (typeof ctx.tenantId !== 'string' || ctx.tenantId.length === 0) {\n throw new Error('[internal] [search.warranty_claims] Missing tenantId in search build context')\n }\n}\n\nfunction readString(record: Record<string, unknown>, snakeKey: string, camelKey?: string): string | null {\n const value = record[snakeKey] ?? (camelKey ? record[camelKey] : undefined)\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length ? trimmed : null\n}\n\nfunction appendLine(lines: string[], label: string, value: unknown): void {\n if (typeof value !== 'string') return\n const trimmed = value.trim()\n if (!trimmed.length) return\n lines.push(`${label}: ${trimmed}`)\n}\n\nfunction resolveClaimId(record: Record<string, unknown>): string | null {\n return readString(record, 'id', 'id')\n}\n\nasync function resolvePresenter(record: Record<string, unknown>): Promise<SearchResultPresenter> {\n const { t } = await resolveTranslations()\n const claimNumber = readString(record, 'claim_number', 'claimNumber') ?? 'Warranty claim'\n const customerName = readString(record, 'customer_name', 'customerName')\n const claimType = readString(record, 'claim_type', 'claimType')\n const status = readString(record, 'status', 'status')\n const statusLabel = status ? t(`warranty_claims.status.${status}`, status) : null\n const subtitle = [customerName, claimType, statusLabel]\n .filter((value): value is string => Boolean(value))\n .join(' \u2014 ')\n return {\n title: claimNumber,\n subtitle: subtitle || undefined,\n icon: 'shield-check',\n }\n}\n\nasync function loadClaimLineRows(ctx: SearchContext, claimId: string): Promise<Array<Record<string, unknown>>> {\n if (!ctx.queryEngine) return []\n try {\n const rows: Array<Record<string, unknown>> = []\n const pageSize = 100\n for (let page = 1; ; page += 1) {\n const result = await ctx.queryEngine.query<Record<string, unknown>>(E.warranty_claims.warranty_claim_line, {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId ?? undefined,\n filters: { claim_id: { $eq: claimId } },\n fields: [\n 'id',\n 'line_no',\n 'sku',\n 'product_name',\n 'serial_number',\n 'lot_number',\n 'fault_code',\n 'warranty_status',\n 'disposition',\n 'line_status',\n ],\n page: { page, pageSize },\n sort: [\n { field: 'line_no', dir: SortDir.Asc },\n { field: 'id', dir: SortDir.Asc },\n ],\n })\n rows.push(...result.items)\n if (result.items.length < pageSize || rows.length >= result.total) break\n }\n return rows\n } catch (err) {\n logger.warn('[search.warranty_claims] Failed to load claim lines', {\n claimId,\n error: err instanceof Error ? err.message : err,\n })\n return []\n }\n}\n\nfunction appendLineRows(lines: string[], lineRows: Array<Record<string, unknown>>): void {\n for (const row of lineRows) {\n appendLine(lines, 'Line SKU', readString(row, 'sku', 'sku') ?? '')\n appendLine(lines, 'Line product', readString(row, 'product_name', 'productName') ?? '')\n appendLine(lines, 'Serial number', readString(row, 'serial_number', 'serialNumber') ?? '')\n appendLine(lines, 'Lot number', readString(row, 'lot_number', 'lotNumber') ?? '')\n appendLine(lines, 'Fault code', readString(row, 'fault_code', 'faultCode') ?? '')\n appendLine(lines, 'Warranty status', readString(row, 'warranty_status', 'warrantyStatus') ?? '')\n appendLine(lines, 'Disposition', readString(row, 'disposition', 'disposition') ?? '')\n appendLine(lines, 'Line status', readString(row, 'line_status', 'lineStatus') ?? '')\n }\n}\n\nfunction buildClaimUrl(record: Record<string, unknown>): string | null {\n const claimId = resolveClaimId(record)\n return claimId ? `/backend/warranty_claims/${claimId}` : null\n}\n\nexport const searchConfig: SearchModuleConfig = {\n entities: [\n {\n entityId: E.warranty_claims.warranty_claim,\n enabled: true,\n priority: 20,\n\n buildSource: async (ctx: SearchBuildContext): Promise<SearchIndexSource | null> => {\n assertTenantContext(ctx)\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Claim number', readString(record, 'claim_number', 'claimNumber') ?? '')\n appendLine(lines, 'Customer', readString(record, 'customer_name', 'customerName') ?? '')\n appendLine(lines, 'Order number', readString(record, 'order_number', 'orderNumber') ?? '')\n appendLine(lines, 'Claim type', readString(record, 'claim_type', 'claimType') ?? '')\n appendLine(lines, 'Status', readString(record, 'status', 'status') ?? '')\n appendLine(lines, 'Vendor', readString(record, 'vendor_name', 'vendorName') ?? '')\n appendLine(lines, 'Vendor reference', readString(record, 'vendor_ref', 'vendorRef') ?? '')\n\n const claimId = resolveClaimId(record)\n const lineRows = claimId ? await loadClaimLineRows(ctx, claimId) : []\n appendLineRows(lines, lineRows)\n if (!lines.length) return null\n\n const presenter = await resolvePresenter(record)\n const url = buildClaimUrl(record)\n const links: SearchResultLink[] = url ? [{ href: url, label: presenter.title, kind: 'primary' }] : []\n return {\n text: lines,\n presenter,\n links,\n checksumSource: {\n record,\n customFields: ctx.customFields,\n lineRows,\n },\n }\n },\n\n formatResult: async (ctx: SearchBuildContext): Promise<SearchResultPresenter | null> => {\n assertTenantContext(ctx)\n return resolvePresenter(ctx.record)\n },\n\n resolveUrl: (ctx: SearchBuildContext): string | null => buildClaimUrl(ctx.record),\n\n resolveLinks: async (ctx: SearchBuildContext): Promise<SearchResultLink[] | null> => {\n const url = buildClaimUrl(ctx.record)\n if (!url) return null\n const { t } = await resolveTranslations()\n return [{ href: url, label: t('warranty_claims.search.openClaim', 'Open claim'), kind: 'secondary' }]\n },\n\n fieldPolicy: {\n searchable: [\n 'claim_number',\n 'claim_type',\n 'status',\n 'priority',\n 'customer_name',\n 'vendor_name',\n 'vendor_ref',\n ],\n hashOnly: ['customer_id', 'order_id', 'sales_return_id', 'replacement_order_id', 'source_claim_id'],\n excluded: [\n 'tenant_id',\n 'organization_id',\n 'assignee_user_id',\n 'deleted_at',\n 'notes',\n 'resolution_summary',\n 'contact_email',\n 'fault_description',\n 'inspection_notes',\n 'rejection_reason_code',\n ],\n },\n aclFeatures: ['warranty_claims.claim.view'],\n },\n ],\n}\n\nexport default searchConfig\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.10.1-develop.7827.1.5c631908e4",
3
+ "version": "0.10.1-develop.7833.1.d6cbf0e3ab",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -266,16 +266,16 @@
266
266
  "zod": "^4.6.6"
267
267
  },
268
268
  "peerDependencies": {
269
- "@open-mercato/ai-assistant": "0.10.1-develop.7827.1.5c631908e4",
270
- "@open-mercato/shared": "0.10.1-develop.7827.1.5c631908e4",
271
- "@open-mercato/ui": "0.10.1-develop.7827.1.5c631908e4",
269
+ "@open-mercato/ai-assistant": "0.10.1-develop.7833.1.d6cbf0e3ab",
270
+ "@open-mercato/shared": "0.10.1-develop.7833.1.d6cbf0e3ab",
271
+ "@open-mercato/ui": "0.10.1-develop.7833.1.d6cbf0e3ab",
272
272
  "react": "^19.0.0",
273
273
  "react-dom": "^19.0.0"
274
274
  },
275
275
  "devDependencies": {
276
- "@open-mercato/ai-assistant": "0.10.1-develop.7827.1.5c631908e4",
277
- "@open-mercato/shared": "0.10.1-develop.7827.1.5c631908e4",
278
- "@open-mercato/ui": "0.10.1-develop.7827.1.5c631908e4",
276
+ "@open-mercato/ai-assistant": "0.10.1-develop.7833.1.d6cbf0e3ab",
277
+ "@open-mercato/shared": "0.10.1-develop.7833.1.d6cbf0e3ab",
278
+ "@open-mercato/ui": "0.10.1-develop.7833.1.d6cbf0e3ab",
279
279
  "@testing-library/dom": "^10.4.1",
280
280
  "@testing-library/jest-dom": "^7.0.1",
281
281
  "@testing-library/react": "^16.3.3",
@@ -0,0 +1,26 @@
1
+ import { createRequire } from 'node:module'
2
+ import path from 'node:path'
3
+ import { pathToFileURL } from 'node:url'
4
+
5
+ /**
6
+ * Imports a package module from the same copy the app under test runs on.
7
+ *
8
+ * A spec that bootstraps the app in-process (`bootstrapFromAppRoot`) and then calls into its
9
+ * runtime — the command bus, `withAtomicFlush`, transaction lifetime — must load every one of those
10
+ * modules from a single copy. Against a standalone app (`OM_TEST_APP_ROOT`) the app's generated
11
+ * registries import `node_modules/@open-mercato/*` inside the app, while the spec's own static
12
+ * imports resolve to the monorepo's packages, so module-local state such as the command registry
13
+ * splits in two: handlers register in one copy and the bus resolves them in the other.
14
+ *
15
+ * Without `OM_TEST_APP_ROOT` this is a plain dynamic import, so the monorepo lane is unchanged.
16
+ */
17
+ export async function importFromAppRuntime<TModule>(specifier: string): Promise<TModule> {
18
+ const resolvedPath = resolveAppRuntimeModulePath(specifier)
19
+ return (await import(resolvedPath ? pathToFileURL(resolvedPath).href : specifier)) as TModule
20
+ }
21
+
22
+ export function resolveAppRuntimeModulePath(specifier: string, appRoot = process.env.OM_TEST_APP_ROOT): string | null {
23
+ const standaloneRoot = appRoot?.trim()
24
+ if (!standaloneRoot) return null
25
+ return createRequire(path.join(path.resolve(standaloneRoot), 'package.json')).resolve(specifier)
26
+ }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "notifications.categories.warranty_claims": "Garantieclaims",
3
+ "search.entityType.warranty_claims.warranty_claim": "Garantieclaim",
3
4
  "warranty_claims.ai.apply": "Anwenden",
4
5
  "warranty_claims.ai.assess.attachment": "Anhang",
5
6
  "warranty_claims.ai.assess.attachmentsError": "Positionsanhänge konnten nicht geladen werden.",
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "notifications.categories.warranty_claims": "Warranty claims",
3
+ "search.entityType.warranty_claims.warranty_claim": "Warranty Claim",
3
4
  "warranty_claims.ai.apply": "Apply",
4
5
  "warranty_claims.ai.assess.attachment": "Attachment",
5
6
  "warranty_claims.ai.assess.attachmentsError": "Failed to load line attachments.",
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "notifications.categories.warranty_claims": "Reclamos de garantía",
3
+ "search.entityType.warranty_claims.warranty_claim": "Reclamo de garantía",
3
4
  "warranty_claims.ai.apply": "Aplicar",
4
5
  "warranty_claims.ai.assess.attachment": "Adjunto",
5
6
  "warranty_claims.ai.assess.attachmentsError": "No se pudieron cargar los adjuntos de la línea.",
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "notifications.categories.warranty_claims": "보증 클레임",
3
+ "search.entityType.warranty_claims.warranty_claim": "보증 클레임",
3
4
  "warranty_claims.ai.apply": "적용",
4
5
  "warranty_claims.ai.assess.attachment": "첨부파일",
5
6
  "warranty_claims.ai.assess.attachmentsError": "라인 첨부파일을 불러오지 못했습니다.",
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "notifications.categories.warranty_claims": "Reklamacje gwarancyjne",
3
+ "search.entityType.warranty_claims.warranty_claim": "Reklamacja gwarancyjna",
3
4
  "warranty_claims.ai.apply": "Zastosuj",
4
5
  "warranty_claims.ai.assess.attachment": "Załącznik",
5
6
  "warranty_claims.ai.assess.attachmentsError": "Nie udało się załadować załączników pozycji.",
@@ -41,17 +41,20 @@ function resolveClaimId(record: Record<string, unknown>): string | null {
41
41
  return readString(record, 'id', 'id')
42
42
  }
43
43
 
44
- function resolvePresenter(record: Record<string, unknown>): SearchResultPresenter {
44
+ async function resolvePresenter(record: Record<string, unknown>): Promise<SearchResultPresenter> {
45
+ const { t } = await resolveTranslations()
45
46
  const claimNumber = readString(record, 'claim_number', 'claimNumber') ?? 'Warranty claim'
46
47
  const customerName = readString(record, 'customer_name', 'customerName')
47
48
  const claimType = readString(record, 'claim_type', 'claimType')
48
49
  const status = readString(record, 'status', 'status')
49
- const subtitle = [customerName, claimType].filter((value): value is string => Boolean(value)).join(' — ')
50
+ const statusLabel = status ? t(`warranty_claims.status.${status}`, status) : null
51
+ const subtitle = [customerName, claimType, statusLabel]
52
+ .filter((value): value is string => Boolean(value))
53
+ .join(' — ')
50
54
  return {
51
55
  title: claimNumber,
52
56
  subtitle: subtitle || undefined,
53
57
  icon: 'shield-check',
54
- badge: status ?? undefined,
55
58
  }
56
59
  }
57
60
 
@@ -138,7 +141,7 @@ export const searchConfig: SearchModuleConfig = {
138
141
  appendLineRows(lines, lineRows)
139
142
  if (!lines.length) return null
140
143
 
141
- const presenter = resolvePresenter(record)
144
+ const presenter = await resolvePresenter(record)
142
145
  const url = buildClaimUrl(record)
143
146
  const links: SearchResultLink[] = url ? [{ href: url, label: presenter.title, kind: 'primary' }] : []
144
147
  return {
@@ -153,7 +156,7 @@ export const searchConfig: SearchModuleConfig = {
153
156
  }
154
157
  },
155
158
 
156
- formatResult: (ctx: SearchBuildContext): SearchResultPresenter | null => {
159
+ formatResult: async (ctx: SearchBuildContext): Promise<SearchResultPresenter | null> => {
157
160
  assertTenantContext(ctx)
158
161
  return resolvePresenter(ctx.record)
159
162
  },