@open-mercato/core 0.6.4-develop.4236.1.9fa6806b34 → 0.6.4-develop.4239.1.4a264a5828
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/dist/modules/business_rules/backend/logs/[id]/page.js +24 -5
- package/dist/modules/business_rules/backend/logs/[id]/page.js.map +2 -2
- package/dist/modules/catalog/api/offers/route.js +15 -5
- package/dist/modules/catalog/api/offers/route.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/[id]/page.js +19 -2
- package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +27 -7
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +27 -7
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/[id]/page.js +29 -8
- package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
- package/dist/modules/progress/acl.js +8 -4
- package/dist/modules/progress/acl.js.map +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.js +24 -6
- package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.js +27 -5
- package/dist/modules/workflows/backend/instances/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.js +25 -6
- package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/business_rules/backend/logs/[id]/page.tsx +32 -7
- package/src/modules/catalog/api/offers/route.ts +20 -5
- package/src/modules/currencies/backend/currencies/[id]/page.tsx +21 -2
- package/src/modules/currencies/i18n/de.json +1 -0
- package/src/modules/currencies/i18n/en.json +1 -0
- package/src/modules/currencies/i18n/es.json +1 -0
- package/src/modules/currencies/i18n/pl.json +1 -0
- package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +34 -11
- package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +34 -11
- package/src/modules/customers/backend/customers/people/[id]/page.tsx +35 -11
- package/src/modules/progress/acl.ts +4 -0
- package/src/modules/workflows/backend/events/[id]/page.tsx +32 -10
- package/src/modules/workflows/backend/instances/[id]/page.tsx +33 -9
- package/src/modules/workflows/backend/tasks/[id]/page.tsx +33 -10
- package/src/modules/workflows/i18n/de.json +1 -0
- package/src/modules/workflows/i18n/en.json +1 -0
- package/src/modules/workflows/i18n/es.json +1 -0
- package/src/modules/workflows/i18n/pl.json +1 -0
|
@@ -94,11 +94,25 @@ export async function decorateOffersWithDetails(
|
|
|
94
94
|
.filter((value): value is string => !!value)
|
|
95
95
|
if (!offerIds.length && !productIds.length) return
|
|
96
96
|
const em = ctx.container.resolve('em') as EntityManager
|
|
97
|
+
const scopeTenantId = ctx.auth?.tenantId ?? null
|
|
98
|
+
if (!scopeTenantId) {
|
|
99
|
+
throw new CrudHttpError(403, '[internal] Missing tenant scope for offer decoration')
|
|
100
|
+
}
|
|
101
|
+
const scopeOrgIds =
|
|
102
|
+
Array.isArray(ctx.organizationIds) && ctx.organizationIds.length
|
|
103
|
+
? Array.from(new Set(ctx.organizationIds))
|
|
104
|
+
: (ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null)
|
|
105
|
+
? [(ctx.selectedOrganizationId ?? ctx.auth?.orgId) as string]
|
|
106
|
+
: []
|
|
107
|
+
const scopeWhere: Record<string, unknown> = { tenantId: scopeTenantId }
|
|
108
|
+
if (scopeOrgIds.length === 1) scopeWhere.organizationId = scopeOrgIds[0]
|
|
109
|
+
else if (scopeOrgIds.length > 1) scopeWhere.organizationId = { $in: scopeOrgIds }
|
|
110
|
+
const scope = { tenantId: scopeTenantId, organizationId: scopeOrgIds.length === 1 ? scopeOrgIds[0] : null }
|
|
97
111
|
const [products, prices, defaultVariants] = await Promise.all([
|
|
98
112
|
productIds.length
|
|
99
113
|
? em.find(
|
|
100
114
|
CatalogProduct,
|
|
101
|
-
{ id: { $in: productIds } },
|
|
115
|
+
{ id: { $in: productIds }, ...scopeWhere },
|
|
102
116
|
{
|
|
103
117
|
fields: ['id', 'title', 'description', 'defaultMediaId', 'defaultMediaUrl', 'sku'],
|
|
104
118
|
},
|
|
@@ -108,15 +122,15 @@ export async function decorateOffersWithDetails(
|
|
|
108
122
|
? findWithDecryption(
|
|
109
123
|
em,
|
|
110
124
|
CatalogProductPrice,
|
|
111
|
-
{ offer: { $in: offerIds } },
|
|
125
|
+
{ offer: { $in: offerIds }, ...scopeWhere },
|
|
112
126
|
{ populate: ['priceKind'] },
|
|
113
|
-
|
|
127
|
+
scope,
|
|
114
128
|
)
|
|
115
129
|
: [],
|
|
116
130
|
productIds.length
|
|
117
131
|
? em.find(
|
|
118
132
|
CatalogProductVariant,
|
|
119
|
-
{ product: { $in: productIds }, isDefault: true },
|
|
133
|
+
{ product: { $in: productIds }, isDefault: true, ...scopeWhere },
|
|
120
134
|
{ fields: ['id', 'product'] },
|
|
121
135
|
)
|
|
122
136
|
: [],
|
|
@@ -227,6 +241,7 @@ export async function decorateOffersWithDetails(
|
|
|
227
241
|
CatalogProductPrice,
|
|
228
242
|
{
|
|
229
243
|
offer: null,
|
|
244
|
+
...scopeWhere,
|
|
230
245
|
$and: [
|
|
231
246
|
{ $or: fallbackTargets },
|
|
232
247
|
channelFilterValues.includes(null)
|
|
@@ -240,7 +255,7 @@ export async function decorateOffersWithDetails(
|
|
|
240
255
|
],
|
|
241
256
|
},
|
|
242
257
|
{ populate: ['priceKind'] },
|
|
243
|
-
|
|
258
|
+
scope,
|
|
244
259
|
)
|
|
245
260
|
: []
|
|
246
261
|
fallbackEntries.forEach((entry) => {
|
|
@@ -12,6 +12,7 @@ import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
|
12
12
|
import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
|
|
13
13
|
import { DataLoader } from '@open-mercato/ui/primitives/DataLoader'
|
|
14
14
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
15
|
+
import { RecordNotFoundState, ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
15
16
|
|
|
16
17
|
type CurrencyData = {
|
|
17
18
|
id: string
|
|
@@ -35,6 +36,7 @@ export default function EditCurrencyPage({ params }: { params?: { id?: string }
|
|
|
35
36
|
const [currency, setCurrency] = React.useState<CurrencyData | null>(null)
|
|
36
37
|
const [loading, setLoading] = React.useState(true)
|
|
37
38
|
const [error, setError] = React.useState<string | null>(null)
|
|
39
|
+
const [isNotFound, setIsNotFound] = React.useState(false)
|
|
38
40
|
|
|
39
41
|
React.useEffect(() => {
|
|
40
42
|
async function loadCurrency() {
|
|
@@ -42,8 +44,10 @@ export default function EditCurrencyPage({ params }: { params?: { id?: string }
|
|
|
42
44
|
const response = await apiCall<{ items: CurrencyData[] }>(`/api/currencies/currencies?id=${params?.id}`)
|
|
43
45
|
if (response.ok && response.result && response.result.items.length > 0) {
|
|
44
46
|
setCurrency(response.result.items[0])
|
|
47
|
+
} else if (!response.ok) {
|
|
48
|
+
setError(t('currencies.form.errors.load'))
|
|
45
49
|
} else {
|
|
46
|
-
|
|
50
|
+
setIsNotFound(true)
|
|
47
51
|
}
|
|
48
52
|
} catch (err) {
|
|
49
53
|
setError(t('currencies.form.errors.load'))
|
|
@@ -163,11 +167,26 @@ export default function EditCurrencyPage({ params }: { params?: { id?: string }
|
|
|
163
167
|
)
|
|
164
168
|
}
|
|
165
169
|
|
|
170
|
+
if (isNotFound) {
|
|
171
|
+
return (
|
|
172
|
+
<Page>
|
|
173
|
+
<PageBody>
|
|
174
|
+
<RecordNotFoundState
|
|
175
|
+
label={t('currencies.form.errors.notFound', 'Currency not found.')}
|
|
176
|
+
backHref="/backend/currencies"
|
|
177
|
+
backLabel={t('currencies.form.actions.backToList', 'Back to currencies')}
|
|
178
|
+
/>
|
|
179
|
+
</PageBody>
|
|
180
|
+
{ConfirmDialogElement}
|
|
181
|
+
</Page>
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
|
|
166
185
|
if (error || !currency) {
|
|
167
186
|
return (
|
|
168
187
|
<Page>
|
|
169
188
|
<PageBody>
|
|
170
|
-
<
|
|
189
|
+
<ErrorMessage label={error ?? t('currencies.form.errors.notFound', 'Currency not found.')} />
|
|
171
190
|
</PageBody>
|
|
172
191
|
{ConfirmDialogElement}
|
|
173
192
|
</Page>
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"currencies.flash.updated": "Währung erfolgreich aktualisiert",
|
|
42
42
|
"currencies.form.action.create": "Währung erstellen",
|
|
43
43
|
"currencies.form.action.save": "Änderungen speichern",
|
|
44
|
+
"currencies.form.actions.backToList": "Zurück zu Währungen",
|
|
44
45
|
"currencies.form.errors.codeFormat": "Währungscode muss genau 3 Großbuchstaben sein (z.B. USD)",
|
|
45
46
|
"currencies.form.errors.delete": "Währung konnte nicht gelöscht werden",
|
|
46
47
|
"currencies.form.errors.load": "Währung konnte nicht geladen werden",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"currencies.flash.updated": "Currency updated successfully",
|
|
42
42
|
"currencies.form.action.create": "Create Currency",
|
|
43
43
|
"currencies.form.action.save": "Save Changes",
|
|
44
|
+
"currencies.form.actions.backToList": "Back to currencies",
|
|
44
45
|
"currencies.form.errors.codeFormat": "Currency code must be exactly 3 uppercase letters (e.g., USD)",
|
|
45
46
|
"currencies.form.errors.delete": "Failed to delete currency",
|
|
46
47
|
"currencies.form.errors.load": "Failed to load currency",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"currencies.flash.updated": "Moneda actualizada correctamente",
|
|
42
42
|
"currencies.form.action.create": "Crear Moneda",
|
|
43
43
|
"currencies.form.action.save": "Guardar Cambios",
|
|
44
|
+
"currencies.form.actions.backToList": "Volver a divisas",
|
|
44
45
|
"currencies.form.errors.codeFormat": "El código de moneda debe ser exactamente 3 letras mayúsculas (ej. USD)",
|
|
45
46
|
"currencies.form.errors.delete": "Error al eliminar moneda",
|
|
46
47
|
"currencies.form.errors.load": "Error al cargar moneda",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"currencies.flash.updated": "Waluta zaktualizowana pomyślnie",
|
|
42
42
|
"currencies.form.action.create": "Utwórz Walutę",
|
|
43
43
|
"currencies.form.action.save": "Zapisz Zmiany",
|
|
44
|
+
"currencies.form.actions.backToList": "Wróć do walut",
|
|
44
45
|
"currencies.form.errors.codeFormat": "Kod waluty musi składać się z dokładnie 3 wielkich liter (np. USD)",
|
|
45
46
|
"currencies.form.errors.delete": "Nie udało się usunąć waluty",
|
|
46
47
|
"currencies.form.errors.load": "Nie udało się załadować waluty",
|
|
@@ -11,6 +11,7 @@ import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
|
11
11
|
import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
12
12
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
13
13
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
14
|
+
import { RecordNotFoundState, ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
14
15
|
|
|
15
16
|
type RoleDetail = {
|
|
16
17
|
id: string
|
|
@@ -146,10 +147,11 @@ export default function CustomerRoleDetailPage({ params }: { params?: { id?: str
|
|
|
146
147
|
const [data, setData] = React.useState<RoleDetail | null>(null)
|
|
147
148
|
const [isLoading, setIsLoading] = React.useState(true)
|
|
148
149
|
const [error, setError] = React.useState<string | null>(null)
|
|
150
|
+
const [isNotFound, setIsNotFound] = React.useState(false)
|
|
149
151
|
|
|
150
152
|
React.useEffect(() => {
|
|
151
153
|
if (!id) {
|
|
152
|
-
|
|
154
|
+
setIsNotFound(true)
|
|
153
155
|
setIsLoading(false)
|
|
154
156
|
return
|
|
155
157
|
}
|
|
@@ -157,6 +159,7 @@ export default function CustomerRoleDetailPage({ params }: { params?: { id?: str
|
|
|
157
159
|
async function load() {
|
|
158
160
|
setIsLoading(true)
|
|
159
161
|
setError(null)
|
|
162
|
+
setIsNotFound(false)
|
|
160
163
|
try {
|
|
161
164
|
const payload = await readApiResultOrThrow<RoleDetail>(
|
|
162
165
|
`/api/customer_accounts/admin/roles/${encodeURIComponent(id!)}`,
|
|
@@ -167,8 +170,12 @@ export default function CustomerRoleDetailPage({ params }: { params?: { id?: str
|
|
|
167
170
|
setData(payload)
|
|
168
171
|
} catch (err) {
|
|
169
172
|
if (cancelled) return
|
|
170
|
-
|
|
171
|
-
|
|
173
|
+
if ((err as { status?: number }).status === 404) {
|
|
174
|
+
setIsNotFound(true)
|
|
175
|
+
} else {
|
|
176
|
+
const message = err instanceof Error ? err.message : t('customer_accounts.admin.roleDetail.error.load', 'Failed to load role')
|
|
177
|
+
setError(message)
|
|
178
|
+
}
|
|
172
179
|
} finally {
|
|
173
180
|
if (!cancelled) setIsLoading(false)
|
|
174
181
|
}
|
|
@@ -300,18 +307,34 @@ export default function CustomerRoleDetailPage({ params }: { params?: { id?: str
|
|
|
300
307
|
)
|
|
301
308
|
}
|
|
302
309
|
|
|
310
|
+
if (isNotFound) {
|
|
311
|
+
return (
|
|
312
|
+
<Page>
|
|
313
|
+
<PageBody>
|
|
314
|
+
<RecordNotFoundState
|
|
315
|
+
label={t('customer_accounts.admin.roleDetail.error.notFound', 'Role not found')}
|
|
316
|
+
backHref="/backend/customer_accounts/roles"
|
|
317
|
+
backLabel={t('customer_accounts.admin.roleDetail.actions.backToList', 'Back to roles')}
|
|
318
|
+
/>
|
|
319
|
+
</PageBody>
|
|
320
|
+
</Page>
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
|
|
303
324
|
if (error || !data) {
|
|
304
325
|
return (
|
|
305
326
|
<Page>
|
|
306
327
|
<PageBody>
|
|
307
|
-
<
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
<
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
328
|
+
<ErrorMessage
|
|
329
|
+
label={error ?? t('customer_accounts.admin.roleDetail.error.notFound', 'Role not found')}
|
|
330
|
+
action={
|
|
331
|
+
<Button asChild variant="outline" size="sm">
|
|
332
|
+
<Link href="/backend/customer_accounts/roles">
|
|
333
|
+
{t('customer_accounts.admin.roleDetail.actions.backToList', 'Back to roles')}
|
|
334
|
+
</Link>
|
|
335
|
+
</Button>
|
|
336
|
+
}
|
|
337
|
+
/>
|
|
315
338
|
</PageBody>
|
|
316
339
|
</Page>
|
|
317
340
|
)
|
|
@@ -16,6 +16,7 @@ import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
|
16
16
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
17
17
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
18
18
|
import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
|
|
19
|
+
import { RecordNotFoundState, ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
19
20
|
|
|
20
21
|
type UserDetail = {
|
|
21
22
|
id: string
|
|
@@ -147,6 +148,7 @@ export default function CustomerUserDetailPage({ params }: { params?: { id?: str
|
|
|
147
148
|
const [data, setData] = React.useState<UserDetail | null>(null)
|
|
148
149
|
const [isLoading, setIsLoading] = React.useState(true)
|
|
149
150
|
const [error, setError] = React.useState<string | null>(null)
|
|
151
|
+
const [isNotFound, setIsNotFound] = React.useState(false)
|
|
150
152
|
const [isSaving, setIsSaving] = React.useState(false)
|
|
151
153
|
const [editActive, setEditActive] = React.useState<boolean | null>(null)
|
|
152
154
|
const [editDisplayName, setEditDisplayName] = React.useState('')
|
|
@@ -186,7 +188,7 @@ export default function CustomerUserDetailPage({ params }: { params?: { id?: str
|
|
|
186
188
|
|
|
187
189
|
React.useEffect(() => {
|
|
188
190
|
if (!id) {
|
|
189
|
-
|
|
191
|
+
setIsNotFound(true)
|
|
190
192
|
setIsLoading(false)
|
|
191
193
|
return
|
|
192
194
|
}
|
|
@@ -194,6 +196,7 @@ export default function CustomerUserDetailPage({ params }: { params?: { id?: str
|
|
|
194
196
|
async function load() {
|
|
195
197
|
setIsLoading(true)
|
|
196
198
|
setError(null)
|
|
199
|
+
setIsNotFound(false)
|
|
197
200
|
try {
|
|
198
201
|
const payload = await readApiResultOrThrow<UserDetail>(
|
|
199
202
|
`/api/customer_accounts/admin/users/${encodeURIComponent(id!)}`,
|
|
@@ -209,8 +212,12 @@ export default function CustomerUserDetailPage({ params }: { params?: { id?: str
|
|
|
209
212
|
setEditCustomerEntityId(payload.customerEntityId)
|
|
210
213
|
} catch (err) {
|
|
211
214
|
if (cancelled) return
|
|
212
|
-
|
|
213
|
-
|
|
215
|
+
if ((err as { status?: number }).status === 404) {
|
|
216
|
+
setIsNotFound(true)
|
|
217
|
+
} else {
|
|
218
|
+
const message = err instanceof Error ? err.message : t('customer_accounts.admin.detail.error.load', 'Failed to load user')
|
|
219
|
+
setError(message)
|
|
220
|
+
}
|
|
214
221
|
} finally {
|
|
215
222
|
if (!cancelled) setIsLoading(false)
|
|
216
223
|
}
|
|
@@ -463,18 +470,34 @@ export default function CustomerUserDetailPage({ params }: { params?: { id?: str
|
|
|
463
470
|
)
|
|
464
471
|
}
|
|
465
472
|
|
|
473
|
+
if (isNotFound) {
|
|
474
|
+
return (
|
|
475
|
+
<Page>
|
|
476
|
+
<PageBody>
|
|
477
|
+
<RecordNotFoundState
|
|
478
|
+
label={t('customer_accounts.admin.detail.error.notFound', 'User not found')}
|
|
479
|
+
backHref="/backend/customer_accounts/users"
|
|
480
|
+
backLabel={t('customer_accounts.admin.detail.actions.backToList', 'Back to list')}
|
|
481
|
+
/>
|
|
482
|
+
</PageBody>
|
|
483
|
+
</Page>
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
|
|
466
487
|
if (error || !data) {
|
|
467
488
|
return (
|
|
468
489
|
<Page>
|
|
469
490
|
<PageBody>
|
|
470
|
-
<
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
<
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
491
|
+
<ErrorMessage
|
|
492
|
+
label={error ?? t('customer_accounts.admin.detail.error.notFound', 'User not found')}
|
|
493
|
+
action={
|
|
494
|
+
<Button asChild variant="outline" size="sm">
|
|
495
|
+
<Link href="/backend/customer_accounts/users">
|
|
496
|
+
{t('customer_accounts.admin.detail.actions.backToList', 'Back to list')}
|
|
497
|
+
</Link>
|
|
498
|
+
</Button>
|
|
499
|
+
}
|
|
500
|
+
/>
|
|
478
501
|
</PageBody>
|
|
479
502
|
</Page>
|
|
480
503
|
)
|
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
NotesSection,
|
|
21
21
|
type CommentSummary,
|
|
22
22
|
type SectionAction,
|
|
23
|
+
RecordNotFoundState,
|
|
24
|
+
ErrorMessage,
|
|
23
25
|
} from '@open-mercato/ui/backend/detail'
|
|
24
26
|
import {
|
|
25
27
|
TagsSection,
|
|
@@ -116,6 +118,7 @@ export default function CustomerPersonDetailPage({ params }: { params?: { id?: s
|
|
|
116
118
|
const [data, setData] = React.useState<PersonOverview | null>(null)
|
|
117
119
|
const [isLoading, setIsLoading] = React.useState(true)
|
|
118
120
|
const [error, setError] = React.useState<string | null>(null)
|
|
121
|
+
const [isNotFound, setIsNotFound] = React.useState(false)
|
|
119
122
|
const [activeTab, setActiveTab] = React.useState<SectionKey>(initialTab)
|
|
120
123
|
const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)
|
|
121
124
|
const [isDeleting, setIsDeleting] = React.useState(false)
|
|
@@ -276,7 +279,7 @@ export default function CustomerPersonDetailPage({ params }: { params?: { id?: s
|
|
|
276
279
|
const initialLoadDoneRef = React.useRef(false)
|
|
277
280
|
const loadData = React.useCallback(async () => {
|
|
278
281
|
if (!id) {
|
|
279
|
-
|
|
282
|
+
setIsNotFound(true)
|
|
280
283
|
setIsLoading(false)
|
|
281
284
|
return
|
|
282
285
|
}
|
|
@@ -284,6 +287,7 @@ export default function CustomerPersonDetailPage({ params }: { params?: { id?: s
|
|
|
284
287
|
setIsLoading(true)
|
|
285
288
|
}
|
|
286
289
|
setError(null)
|
|
290
|
+
setIsNotFound(false)
|
|
287
291
|
try {
|
|
288
292
|
const payload = await readApiResultOrThrow<PersonOverview>(
|
|
289
293
|
`/api/customers/people/${encodeURIComponent(id)}?include=todos`,
|
|
@@ -292,8 +296,12 @@ export default function CustomerPersonDetailPage({ params }: { params?: { id?: s
|
|
|
292
296
|
)
|
|
293
297
|
setData(payload as PersonOverview)
|
|
294
298
|
} catch (err) {
|
|
295
|
-
|
|
296
|
-
|
|
299
|
+
if ((err as { status?: number }).status === 404) {
|
|
300
|
+
setIsNotFound(true)
|
|
301
|
+
} else {
|
|
302
|
+
const message = err instanceof Error ? err.message : t('customers.people.detail.error.load')
|
|
303
|
+
setError(message)
|
|
304
|
+
}
|
|
297
305
|
if (!initialLoadDoneRef.current) setData(null)
|
|
298
306
|
} finally {
|
|
299
307
|
setIsLoading(false)
|
|
@@ -469,18 +477,34 @@ export default function CustomerPersonDetailPage({ params }: { params?: { id?: s
|
|
|
469
477
|
)
|
|
470
478
|
}
|
|
471
479
|
|
|
480
|
+
if (isNotFound) {
|
|
481
|
+
return (
|
|
482
|
+
<Page>
|
|
483
|
+
<PageBody>
|
|
484
|
+
<RecordNotFoundState
|
|
485
|
+
label={t('customers.people.detail.error.notFound', 'Person not found.')}
|
|
486
|
+
backHref="/backend/customers/people"
|
|
487
|
+
backLabel={t('customers.people.detail.actions.backToList', 'Back to people')}
|
|
488
|
+
/>
|
|
489
|
+
</PageBody>
|
|
490
|
+
</Page>
|
|
491
|
+
)
|
|
492
|
+
}
|
|
493
|
+
|
|
472
494
|
if (error || !data || !personId) {
|
|
473
495
|
return (
|
|
474
496
|
<Page>
|
|
475
497
|
<PageBody>
|
|
476
|
-
<
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
<
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
498
|
+
<ErrorMessage
|
|
499
|
+
label={error ?? t('customers.people.detail.error.notFound', 'Person not found.')}
|
|
500
|
+
action={
|
|
501
|
+
<Button asChild variant="outline" size="sm">
|
|
502
|
+
<Link href="/backend/customers/people">
|
|
503
|
+
{t('customers.people.detail.actions.backToList', 'Back to people')}
|
|
504
|
+
</Link>
|
|
505
|
+
</Button>
|
|
506
|
+
}
|
|
507
|
+
/>
|
|
484
508
|
</PageBody>
|
|
485
509
|
</Page>
|
|
486
510
|
)
|
|
@@ -8,21 +8,25 @@ export const features = [
|
|
|
8
8
|
id: 'progress.create',
|
|
9
9
|
title: 'Create progress jobs',
|
|
10
10
|
module: 'progress',
|
|
11
|
+
dependsOn: ['progress.view'],
|
|
11
12
|
},
|
|
12
13
|
{
|
|
13
14
|
id: 'progress.update',
|
|
14
15
|
title: 'Update progress jobs',
|
|
15
16
|
module: 'progress',
|
|
17
|
+
dependsOn: ['progress.view'],
|
|
16
18
|
},
|
|
17
19
|
{
|
|
18
20
|
id: 'progress.cancel',
|
|
19
21
|
title: 'Cancel progress jobs',
|
|
20
22
|
module: 'progress',
|
|
23
|
+
dependsOn: ['progress.view'],
|
|
21
24
|
},
|
|
22
25
|
{
|
|
23
26
|
id: 'progress.manage',
|
|
24
27
|
title: 'Manage all progress jobs',
|
|
25
28
|
module: 'progress',
|
|
29
|
+
dependsOn: ['progress.view'],
|
|
26
30
|
},
|
|
27
31
|
]
|
|
28
32
|
|
|
@@ -11,6 +11,7 @@ import { Button } from '@open-mercato/ui/primitives/button'
|
|
|
11
11
|
import { FormHeader } from '@open-mercato/ui/backend/forms'
|
|
12
12
|
import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
13
13
|
import { JsonDisplay } from '@open-mercato/ui/backend/JsonDisplay'
|
|
14
|
+
import { RecordNotFoundState, ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
14
15
|
|
|
15
16
|
type WorkflowEvent = {
|
|
16
17
|
id: string
|
|
@@ -55,8 +56,13 @@ export default function WorkflowEventDetailPage() {
|
|
|
55
56
|
const response = await apiFetch(`/api/workflows/events/${eventId}`)
|
|
56
57
|
|
|
57
58
|
if (!response.ok) {
|
|
58
|
-
const
|
|
59
|
-
|
|
59
|
+
const httpErr = new Error(
|
|
60
|
+
response.status === 404
|
|
61
|
+
? t('workflows.events.notFound', 'Event not found.')
|
|
62
|
+
: t('workflows.events.messages.loadFailed')
|
|
63
|
+
) as Error & { status: number }
|
|
64
|
+
httpErr.status = response.status
|
|
65
|
+
throw httpErr
|
|
60
66
|
}
|
|
61
67
|
const result = await response.json()
|
|
62
68
|
return result as WorkflowEvent
|
|
@@ -77,18 +83,34 @@ export default function WorkflowEventDetailPage() {
|
|
|
77
83
|
)
|
|
78
84
|
}
|
|
79
85
|
|
|
86
|
+
const isNotFound = !isLoading && (error as (Error & { status?: number }) | null)?.status === 404
|
|
87
|
+
|
|
88
|
+
if (isNotFound) {
|
|
89
|
+
return (
|
|
90
|
+
<Page>
|
|
91
|
+
<PageBody>
|
|
92
|
+
<RecordNotFoundState
|
|
93
|
+
label={t('workflows.events.notFound', 'Event not found.')}
|
|
94
|
+
backHref="/backend/events"
|
|
95
|
+
backLabel={t('workflows.events.backToList', 'Back to Events')}
|
|
96
|
+
/>
|
|
97
|
+
</PageBody>
|
|
98
|
+
</Page>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
80
102
|
if (error || !event) {
|
|
81
103
|
return (
|
|
82
104
|
<Page>
|
|
83
105
|
<PageBody>
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
{t('workflows.events.backToList')}
|
|
89
|
-
</
|
|
90
|
-
|
|
91
|
-
|
|
106
|
+
<ErrorMessage
|
|
107
|
+
label={(error as Error | null)?.message ?? t('workflows.events.messages.loadFailed')}
|
|
108
|
+
action={
|
|
109
|
+
<Button asChild variant="outline" size="sm">
|
|
110
|
+
<Link href="/backend/events">{t('workflows.events.backToList', 'Back to Events')}</Link>
|
|
111
|
+
</Button>
|
|
112
|
+
}
|
|
113
|
+
/>
|
|
92
114
|
</PageBody>
|
|
93
115
|
</Page>
|
|
94
116
|
)
|
|
@@ -19,6 +19,7 @@ import { MobileInstanceOverview } from '../../../components/mobile/MobileInstanc
|
|
|
19
19
|
import { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'
|
|
20
20
|
import { definitionToGraph } from '../../../lib/graph-utils'
|
|
21
21
|
import { Node } from '@xyflow/react'
|
|
22
|
+
import { RecordNotFoundState, ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
22
23
|
|
|
23
24
|
export default function WorkflowInstanceDetailPage({ params }: { params?: { id?: string } }) {
|
|
24
25
|
const id = params?.id
|
|
@@ -32,7 +33,13 @@ export default function WorkflowInstanceDetailPage({ params }: { params?: { id?:
|
|
|
32
33
|
queryFn: async () => {
|
|
33
34
|
const response = await apiFetch(`/api/workflows/instances/${id}`)
|
|
34
35
|
if (!response.ok) {
|
|
35
|
-
|
|
36
|
+
const httpErr = new Error(
|
|
37
|
+
response.status === 404
|
|
38
|
+
? t('workflows.instances.detail.notFound', 'Workflow instance not found.')
|
|
39
|
+
: t('workflows.instances.loadFailed', 'Failed to load workflow instance.')
|
|
40
|
+
) as Error & { status: number }
|
|
41
|
+
httpErr.status = response.status
|
|
42
|
+
throw httpErr
|
|
36
43
|
}
|
|
37
44
|
const data = await response.json()
|
|
38
45
|
return data.data as WorkflowInstance
|
|
@@ -368,18 +375,35 @@ export default function WorkflowInstanceDetailPage({ params }: { params?: { id?:
|
|
|
368
375
|
)
|
|
369
376
|
}
|
|
370
377
|
|
|
378
|
+
const isNotFound = !isLoading && (error as (Error & { status?: number }) | null)?.status === 404
|
|
379
|
+
|
|
380
|
+
if (isNotFound) {
|
|
381
|
+
return (
|
|
382
|
+
<Page>
|
|
383
|
+
<PageBody>
|
|
384
|
+
<RecordNotFoundState
|
|
385
|
+
label={t('workflows.instances.detail.notFound', 'Workflow instance not found.')}
|
|
386
|
+
backHref="/backend/instances"
|
|
387
|
+
backLabel={t('workflows.instances.actions.backToList', 'Back to instances')}
|
|
388
|
+
/>
|
|
389
|
+
</PageBody>
|
|
390
|
+
{ConfirmDialogElement}
|
|
391
|
+
</Page>
|
|
392
|
+
)
|
|
393
|
+
}
|
|
394
|
+
|
|
371
395
|
if (error || !instance) {
|
|
372
396
|
return (
|
|
373
397
|
<Page>
|
|
374
398
|
<PageBody>
|
|
375
|
-
<
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
<
|
|
379
|
-
{t('workflows.instances.actions.backToList'
|
|
380
|
-
</
|
|
381
|
-
|
|
382
|
-
|
|
399
|
+
<ErrorMessage
|
|
400
|
+
label={(error as Error | null)?.message ?? t('workflows.instances.loadFailed', 'Failed to load workflow instance.')}
|
|
401
|
+
action={
|
|
402
|
+
<Button asChild variant="outline" size="sm">
|
|
403
|
+
<Link href="/backend/instances">{t('workflows.instances.actions.backToList', 'Back to instances')}</Link>
|
|
404
|
+
</Button>
|
|
405
|
+
}
|
|
406
|
+
/>
|
|
383
407
|
</PageBody>
|
|
384
408
|
{ConfirmDialogElement}
|
|
385
409
|
</Page>
|
|
@@ -25,6 +25,7 @@ import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
|
25
25
|
import { MobileTaskForm } from '../../../components/mobile/MobileTaskForm'
|
|
26
26
|
import { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'
|
|
27
27
|
import type { UserTaskResponse, UserTaskStatus } from '../../../data/types'
|
|
28
|
+
import { RecordNotFoundState, ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
28
29
|
|
|
29
30
|
export default function UserTaskDetailPage({ params }: { params: { id: string } }) {
|
|
30
31
|
const router = useRouter()
|
|
@@ -44,12 +45,16 @@ export default function UserTaskDetailPage({ params }: { params: { id: string }
|
|
|
44
45
|
const result = await apiCall<{ data: UserTaskResponse }>(
|
|
45
46
|
`/api/workflows/tasks/${params.id}`
|
|
46
47
|
)
|
|
47
|
-
|
|
48
48
|
if (!result.ok) {
|
|
49
|
-
|
|
49
|
+
const httpErr = new Error(
|
|
50
|
+
result.status === 404
|
|
51
|
+
? t('workflows.tasks.detail.notFound', 'Task not found')
|
|
52
|
+
: t('workflows.tasks.detail.loadFailed', 'Failed to load task')
|
|
53
|
+
) as Error & { status: number }
|
|
54
|
+
httpErr.status = result.status
|
|
55
|
+
throw httpErr
|
|
50
56
|
}
|
|
51
|
-
|
|
52
|
-
return result.result?.data || null
|
|
57
|
+
return result.result?.data ?? null
|
|
53
58
|
},
|
|
54
59
|
})
|
|
55
60
|
|
|
@@ -347,16 +352,34 @@ export default function UserTaskDetailPage({ params }: { params: { id: string }
|
|
|
347
352
|
)
|
|
348
353
|
}
|
|
349
354
|
|
|
355
|
+
const isNotFound = !isLoading && (error as (Error & { status?: number }) | null)?.status === 404
|
|
356
|
+
|
|
357
|
+
if (isNotFound) {
|
|
358
|
+
return (
|
|
359
|
+
<Page>
|
|
360
|
+
<PageBody>
|
|
361
|
+
<RecordNotFoundState
|
|
362
|
+
label={t('workflows.tasks.detail.notFound', 'Task not found')}
|
|
363
|
+
backHref="/backend/tasks"
|
|
364
|
+
backLabel={t('workflows.tasks.detail.backToList', 'Back to Tasks')}
|
|
365
|
+
/>
|
|
366
|
+
</PageBody>
|
|
367
|
+
</Page>
|
|
368
|
+
)
|
|
369
|
+
}
|
|
370
|
+
|
|
350
371
|
if (error || !task) {
|
|
351
372
|
return (
|
|
352
373
|
<Page>
|
|
353
374
|
<PageBody>
|
|
354
|
-
<
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
375
|
+
<ErrorMessage
|
|
376
|
+
label={(error as Error | null)?.message ?? t('workflows.tasks.detail.loadFailed', 'Failed to load task')}
|
|
377
|
+
action={
|
|
378
|
+
<Button asChild variant="outline" size="sm">
|
|
379
|
+
<Link href="/backend/tasks">{t('workflows.tasks.detail.backToList', 'Back to Tasks')}</Link>
|
|
380
|
+
</Button>
|
|
381
|
+
}
|
|
382
|
+
/>
|
|
360
383
|
</PageBody>
|
|
361
384
|
</Page>
|
|
362
385
|
)
|