@open-mercato/core 0.6.4-develop.4360.1.568bf6e32b → 0.6.4-develop.4368.1.2f7e9a7002
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/catalog/backend/catalog/categories/[id]/edit/page.js +66 -44
- package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +124 -103
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +26 -4
- package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js +24 -4
- package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +35 -6
- package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +3 -3
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +67 -47
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +29 -6
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +29 -6
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +33 -4
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +33 -4
- package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +37 -8
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +3 -3
- package/dist/modules/workflows/backend/definitions/[id]/page.js +24 -6
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +39 -8
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +38 -6
- package/src/modules/catalog/i18n/de.json +2 -0
- package/src/modules/catalog/i18n/en.json +2 -0
- package/src/modules/catalog/i18n/es.json +2 -0
- package/src/modules/catalog/i18n/pl.json +2 -0
- package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +30 -4
- package/src/modules/directory/backend/directory/tenants/[id]/edit/page.tsx +28 -6
- package/src/modules/directory/i18n/de.json +2 -0
- package/src/modules/directory/i18n/en.json +2 -0
- package/src/modules/directory/i18n/es.json +2 -0
- package/src/modules/directory/i18n/pl.json +2 -0
- package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +44 -4
- package/src/modules/planner/i18n/de.json +1 -0
- package/src/modules/planner/i18n/en.json +1 -0
- package/src/modules/planner/i18n/es.json +1 -0
- package/src/modules/planner/i18n/pl.json +1 -0
- package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +36 -7
- package/src/modules/sales/i18n/de.json +1 -0
- package/src/modules/sales/i18n/en.json +1 -0
- package/src/modules/sales/i18n/es.json +1 -0
- package/src/modules/sales/i18n/pl.json +1 -0
- package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +33 -6
- package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +33 -6
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +44 -4
- package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +44 -4
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +44 -4
- package/src/modules/staff/i18n/de.json +5 -0
- package/src/modules/staff/i18n/en.json +5 -0
- package/src/modules/staff/i18n/es.json +5 -0
- package/src/modules/staff/i18n/pl.json +5 -0
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +26 -3
- 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
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"catalog.categories.flash.updated": "Category updated",
|
|
41
41
|
"catalog.categories.form.action.create": "Create",
|
|
42
42
|
"catalog.categories.form.action.save": "Save",
|
|
43
|
+
"catalog.categories.form.actions.backToList": "Back to categories",
|
|
43
44
|
"catalog.categories.form.createTitle": "Create category",
|
|
44
45
|
"catalog.categories.form.editTitle": "Edit category",
|
|
45
46
|
"catalog.categories.form.errors.delete": "Failed to delete category",
|
|
@@ -559,6 +560,7 @@
|
|
|
559
560
|
"catalog.stats.tags": "Tags",
|
|
560
561
|
"catalog.stats.title": "Catalog overview",
|
|
561
562
|
"catalog.variants.errors.skuExists": "SKU already in use.",
|
|
563
|
+
"catalog.variants.form.actions.backToProduct": "Back to product variants",
|
|
562
564
|
"catalog.variants.form.barcodeLabel": "Barcode",
|
|
563
565
|
"catalog.variants.form.barcodePlaceholder": "EAN, UPC, etc.",
|
|
564
566
|
"catalog.variants.form.createAction": "Create variant",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"catalog.categories.flash.updated": "Categoría actualizada",
|
|
41
41
|
"catalog.categories.form.action.create": "Crear",
|
|
42
42
|
"catalog.categories.form.action.save": "Guardar",
|
|
43
|
+
"catalog.categories.form.actions.backToList": "Volver a categorías",
|
|
43
44
|
"catalog.categories.form.createTitle": "Crear categoría",
|
|
44
45
|
"catalog.categories.form.editTitle": "Editar categoría",
|
|
45
46
|
"catalog.categories.form.errors.delete": "No se pudo eliminar la categoría",
|
|
@@ -559,6 +560,7 @@
|
|
|
559
560
|
"catalog.stats.tags": "Etiquetas",
|
|
560
561
|
"catalog.stats.title": "Resumen del catálogo",
|
|
561
562
|
"catalog.variants.errors.skuExists": "El SKU ya está en uso.",
|
|
563
|
+
"catalog.variants.form.actions.backToProduct": "Volver a variantes del producto",
|
|
562
564
|
"catalog.variants.form.barcodeLabel": "Código de barras",
|
|
563
565
|
"catalog.variants.form.barcodePlaceholder": "EAN, UPC, etc.",
|
|
564
566
|
"catalog.variants.form.createAction": "Crear variante",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"catalog.categories.flash.updated": "Kategoria zaktualizowana",
|
|
41
41
|
"catalog.categories.form.action.create": "Utwórz",
|
|
42
42
|
"catalog.categories.form.action.save": "Zapisz",
|
|
43
|
+
"catalog.categories.form.actions.backToList": "Powrót do kategorii",
|
|
43
44
|
"catalog.categories.form.createTitle": "Utwórz kategorię",
|
|
44
45
|
"catalog.categories.form.editTitle": "Edytuj kategorię",
|
|
45
46
|
"catalog.categories.form.errors.delete": "Nie udało się usunąć kategorii",
|
|
@@ -559,6 +560,7 @@
|
|
|
559
560
|
"catalog.stats.tags": "Tagi",
|
|
560
561
|
"catalog.stats.title": "Przegląd katalogu",
|
|
561
562
|
"catalog.variants.errors.skuExists": "SKU jest już używany.",
|
|
563
|
+
"catalog.variants.form.actions.backToProduct": "Powrót do wariantów produktu",
|
|
562
564
|
"catalog.variants.form.barcodeLabel": "Kod kreskowy",
|
|
563
565
|
"catalog.variants.form.barcodePlaceholder": "EAN, UPC itp.",
|
|
564
566
|
"catalog.variants.form.createAction": "Utwórz wariant",
|
|
@@ -16,6 +16,7 @@ import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
|
|
|
16
16
|
import { deleteCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
17
17
|
import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
|
|
18
18
|
import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
|
|
19
|
+
import { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'
|
|
19
20
|
|
|
20
21
|
type TreeResponse = {
|
|
21
22
|
items: OrganizationTreeNode[]
|
|
@@ -48,6 +49,7 @@ export default function EditOrganizationPage({ params }: { params?: { id?: strin
|
|
|
48
49
|
const [tenantId, setTenantId] = React.useState<string | null>(null)
|
|
49
50
|
const [loading, setLoading] = React.useState(true)
|
|
50
51
|
const [error, setError] = React.useState<string | null>(null)
|
|
52
|
+
const [isNotFound, setIsNotFound] = React.useState(false)
|
|
51
53
|
const [parentTree, setParentTree] = React.useState<OrganizationTreeNode[]>([])
|
|
52
54
|
const [childSummary, setChildSummary] = React.useState<OrganizationTreeOption[]>([])
|
|
53
55
|
const [originalChildIds, setOriginalChildIds] = React.useState<string[]>([])
|
|
@@ -107,13 +109,23 @@ export default function EditOrganizationPage({ params }: { params?: { id?: strin
|
|
|
107
109
|
async function load() {
|
|
108
110
|
setLoading(true)
|
|
109
111
|
setError(null)
|
|
112
|
+
setIsNotFound(false)
|
|
110
113
|
try {
|
|
111
|
-
const { ok, result } = await apiCall<OrganizationResponse>(
|
|
114
|
+
const { ok, status, result } = await apiCall<OrganizationResponse>(
|
|
112
115
|
`/api/directory/organizations?view=manage&ids=${currentOrgId}&status=all&includeInactive=true&page=1&pageSize=1`,
|
|
113
116
|
)
|
|
114
|
-
if (!ok)
|
|
117
|
+
if (!ok) {
|
|
118
|
+
if (status === 404) {
|
|
119
|
+
if (!cancelled) setIsNotFound(true)
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
throw new Error(t('directory.organizations.form.errors.load', 'Failed to load organization'))
|
|
123
|
+
}
|
|
115
124
|
const record = Array.isArray(result?.items) ? result.items?.[0] : undefined
|
|
116
|
-
if (!record)
|
|
125
|
+
if (!record) {
|
|
126
|
+
if (!cancelled) setIsNotFound(true)
|
|
127
|
+
return
|
|
128
|
+
}
|
|
117
129
|
const resolvedTenantId = record.tenantId || null
|
|
118
130
|
setTenantId(resolvedTenantId)
|
|
119
131
|
const baseTree = await loadParentTree(resolvedTenantId, record.descendantIds ?? [])
|
|
@@ -263,11 +275,25 @@ export default function EditOrganizationPage({ params }: { params?: { id?: strin
|
|
|
263
275
|
)
|
|
264
276
|
}
|
|
265
277
|
|
|
278
|
+
if (isNotFound) {
|
|
279
|
+
return (
|
|
280
|
+
<Page>
|
|
281
|
+
<PageBody>
|
|
282
|
+
<RecordNotFoundState
|
|
283
|
+
label={t('directory.organizations.form.errors.notFound', 'Organization not found')}
|
|
284
|
+
backHref="/backend/directory/organizations"
|
|
285
|
+
backLabel={t('directory.organizations.form.actions.backToList', 'Back to organizations')}
|
|
286
|
+
/>
|
|
287
|
+
</PageBody>
|
|
288
|
+
</Page>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
|
|
266
292
|
if (error && !loading && !initialValues) {
|
|
267
293
|
return (
|
|
268
294
|
<Page>
|
|
269
295
|
<PageBody>
|
|
270
|
-
<
|
|
296
|
+
<ErrorMessage label={error} />
|
|
271
297
|
</PageBody>
|
|
272
298
|
</Page>
|
|
273
299
|
)
|
|
@@ -7,6 +7,7 @@ import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customF
|
|
|
7
7
|
import { updateCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
8
8
|
import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
|
|
9
9
|
import { readApiResultOrThrow, apiCall } from '@open-mercato/ui/backend/utils/apiCall'
|
|
10
|
+
import { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'
|
|
10
11
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
11
12
|
import { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'
|
|
12
13
|
|
|
@@ -21,6 +22,7 @@ export default function EditTenantPage({ params }: { params?: { id?: string } })
|
|
|
21
22
|
const [initial, setInitial] = React.useState<TenantFormValues | null>(null)
|
|
22
23
|
const [loading, setLoading] = React.useState(true)
|
|
23
24
|
const [error, setError] = React.useState<string | null>(null)
|
|
25
|
+
const [isNotFound, setIsNotFound] = React.useState(false)
|
|
24
26
|
const t = useT()
|
|
25
27
|
const fields = React.useMemo<CrudField[]>(() => [
|
|
26
28
|
{ id: 'name', label: t('directory.tenants.form.fields.name', 'Name'), type: 'text', required: true },
|
|
@@ -37,6 +39,7 @@ export default function EditTenantPage({ params }: { params?: { id?: string } })
|
|
|
37
39
|
if (!tenantId) return
|
|
38
40
|
setLoading(true)
|
|
39
41
|
setError(null)
|
|
42
|
+
setIsNotFound(false)
|
|
40
43
|
try {
|
|
41
44
|
const data = await readApiResultOrThrow<{ items?: Record<string, unknown>[] }>(
|
|
42
45
|
`/api/directory/tenants?id=${encodeURIComponent(tenantId)}`,
|
|
@@ -45,7 +48,10 @@ export default function EditTenantPage({ params }: { params?: { id?: string } })
|
|
|
45
48
|
)
|
|
46
49
|
const rows = Array.isArray(data?.items) ? data.items : []
|
|
47
50
|
const row = rows[0]
|
|
48
|
-
if (!row)
|
|
51
|
+
if (!row) {
|
|
52
|
+
if (!cancelled) setIsNotFound(true)
|
|
53
|
+
return
|
|
54
|
+
}
|
|
49
55
|
const cfValues = extractCustomFieldEntries(row as Record<string, unknown>)
|
|
50
56
|
const values: TenantFormValues = {
|
|
51
57
|
id: String(row.id),
|
|
@@ -56,8 +62,12 @@ export default function EditTenantPage({ params }: { params?: { id?: string } })
|
|
|
56
62
|
if (!cancelled) setInitial(values)
|
|
57
63
|
} catch (err) {
|
|
58
64
|
if (!cancelled) {
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
if ((err as { status?: number }).status === 404) {
|
|
66
|
+
setIsNotFound(true)
|
|
67
|
+
} else {
|
|
68
|
+
const message = err instanceof Error ? err.message : t('directory.tenants.form.errors.load', 'Failed to load tenant')
|
|
69
|
+
setError(message)
|
|
70
|
+
}
|
|
61
71
|
}
|
|
62
72
|
} finally {
|
|
63
73
|
if (!cancelled) setLoading(false)
|
|
@@ -69,13 +79,25 @@ export default function EditTenantPage({ params }: { params?: { id?: string } })
|
|
|
69
79
|
|
|
70
80
|
if (!tenantId) return null
|
|
71
81
|
|
|
82
|
+
if (isNotFound) {
|
|
83
|
+
return (
|
|
84
|
+
<Page>
|
|
85
|
+
<PageBody>
|
|
86
|
+
<RecordNotFoundState
|
|
87
|
+
label={t('directory.tenants.form.errors.notFound', 'Tenant not found')}
|
|
88
|
+
backHref="/backend/directory/tenants"
|
|
89
|
+
backLabel={t('directory.tenants.form.actions.backToList', 'Back to tenants')}
|
|
90
|
+
/>
|
|
91
|
+
</PageBody>
|
|
92
|
+
</Page>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
72
96
|
if (error && !loading && !initial) {
|
|
73
97
|
return (
|
|
74
98
|
<Page>
|
|
75
99
|
<PageBody>
|
|
76
|
-
<
|
|
77
|
-
{error}
|
|
78
|
-
</div>
|
|
100
|
+
<ErrorMessage label={error} />
|
|
79
101
|
</PageBody>
|
|
80
102
|
</Page>
|
|
81
103
|
)
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"directory.organizations.flash.updated": "Organisation aktualisiert",
|
|
20
20
|
"directory.organizations.form.action.create": "Erstellen",
|
|
21
21
|
"directory.organizations.form.action.save": "Speichern",
|
|
22
|
+
"directory.organizations.form.actions.backToList": "Zurück zu Organisationen",
|
|
22
23
|
"directory.organizations.form.children.empty": "Keine direkten Unterorganisationen zugewiesen.",
|
|
23
24
|
"directory.organizations.form.errors.load": "Organisation konnte nicht geladen werden",
|
|
24
25
|
"directory.organizations.form.errors.missingId": "Organisationskennung fehlt.",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"directory.organizations.list.filters.status": "Status",
|
|
56
57
|
"directory.organizations.list.searchPlaceholder": "Organisationen durchsuchen",
|
|
57
58
|
"directory.organizations.list.title": "Organisationen",
|
|
59
|
+
"directory.tenants.form.actions.backToList": "Zurück zu Mandanten",
|
|
58
60
|
"directory.tenants.form.errors.delete": "Mandant konnte nicht gelöscht werden",
|
|
59
61
|
"directory.tenants.form.errors.load": "Mandant konnte nicht geladen werden",
|
|
60
62
|
"directory.tenants.form.errors.notFound": "Mandant nicht gefunden",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"directory.organizations.flash.updated": "Organization updated",
|
|
20
20
|
"directory.organizations.form.action.create": "Create",
|
|
21
21
|
"directory.organizations.form.action.save": "Save",
|
|
22
|
+
"directory.organizations.form.actions.backToList": "Back to organizations",
|
|
22
23
|
"directory.organizations.form.children.empty": "No direct children assigned.",
|
|
23
24
|
"directory.organizations.form.errors.load": "Failed to load organization",
|
|
24
25
|
"directory.organizations.form.errors.missingId": "Organization identifier is missing.",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"directory.organizations.list.filters.status": "Status",
|
|
56
57
|
"directory.organizations.list.searchPlaceholder": "Search organizations",
|
|
57
58
|
"directory.organizations.list.title": "Organizations",
|
|
59
|
+
"directory.tenants.form.actions.backToList": "Back to tenants",
|
|
58
60
|
"directory.tenants.form.errors.delete": "Failed to delete tenant",
|
|
59
61
|
"directory.tenants.form.errors.load": "Failed to load tenant",
|
|
60
62
|
"directory.tenants.form.errors.notFound": "Tenant not found",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"directory.organizations.flash.updated": "Organización actualizada",
|
|
20
20
|
"directory.organizations.form.action.create": "Crear",
|
|
21
21
|
"directory.organizations.form.action.save": "Guardar",
|
|
22
|
+
"directory.organizations.form.actions.backToList": "Volver a organizaciones",
|
|
22
23
|
"directory.organizations.form.children.empty": "Sin hijos asignados.",
|
|
23
24
|
"directory.organizations.form.errors.load": "No se pudo cargar la organización",
|
|
24
25
|
"directory.organizations.form.errors.missingId": "Falta el identificador de la organización.",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"directory.organizations.list.filters.status": "Estado",
|
|
56
57
|
"directory.organizations.list.searchPlaceholder": "Buscar organizaciones",
|
|
57
58
|
"directory.organizations.list.title": "Organizaciones",
|
|
59
|
+
"directory.tenants.form.actions.backToList": "Volver a inquilinos",
|
|
58
60
|
"directory.tenants.form.errors.delete": "No se pudo eliminar el inquilino",
|
|
59
61
|
"directory.tenants.form.errors.load": "No se pudo cargar el inquilino",
|
|
60
62
|
"directory.tenants.form.errors.notFound": "Inquilino no encontrado",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"directory.organizations.flash.updated": "Organizacja zaktualizowana",
|
|
20
20
|
"directory.organizations.form.action.create": "Utwórz",
|
|
21
21
|
"directory.organizations.form.action.save": "Zapisz",
|
|
22
|
+
"directory.organizations.form.actions.backToList": "Powrót do organizacji",
|
|
22
23
|
"directory.organizations.form.children.empty": "Brak przypisanych organizacji podrzędnych.",
|
|
23
24
|
"directory.organizations.form.errors.load": "Nie udało się wczytać organizacji",
|
|
24
25
|
"directory.organizations.form.errors.missingId": "Brakuje identyfikatora organizacji.",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"directory.organizations.list.filters.status": "Status",
|
|
56
57
|
"directory.organizations.list.searchPlaceholder": "Szukaj organizacji",
|
|
57
58
|
"directory.organizations.list.title": "Organizacje",
|
|
59
|
+
"directory.tenants.form.actions.backToList": "Powrót do najemców",
|
|
58
60
|
"directory.tenants.form.errors.delete": "Nie udało się usunąć najemcy",
|
|
59
61
|
"directory.tenants.form.errors.load": "Nie udało się wczytać najemcy",
|
|
60
62
|
"directory.tenants.form.errors.notFound": "Nie znaleziono najemcy",
|
|
@@ -4,6 +4,7 @@ import * as React from 'react'
|
|
|
4
4
|
import { useRouter } from 'next/navigation'
|
|
5
5
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
6
6
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
7
|
+
import { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'
|
|
7
8
|
import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
8
9
|
import { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
9
10
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
@@ -40,6 +41,8 @@ export default function PlannerAvailabilityRuleSetDetailPage({ params }: { param
|
|
|
40
41
|
const translate = useT()
|
|
41
42
|
const router = useRouter()
|
|
42
43
|
const [initialValues, setInitialValues] = React.useState<AvailabilityRuleSetFormValues | null>(null)
|
|
44
|
+
const [error, setError] = React.useState<string | null>(null)
|
|
45
|
+
const [isNotFound, setIsNotFound] = React.useState(false)
|
|
43
46
|
const [activeTab, setActiveTab] = React.useState<'details' | 'availability'>('details')
|
|
44
47
|
|
|
45
48
|
React.useEffect(() => {
|
|
@@ -47,6 +50,10 @@ export default function PlannerAvailabilityRuleSetDetailPage({ params }: { param
|
|
|
47
50
|
const rulesetIdValue = rulesetId
|
|
48
51
|
let cancelled = false
|
|
49
52
|
async function loadRuleSet() {
|
|
53
|
+
if (!cancelled) {
|
|
54
|
+
setError(null)
|
|
55
|
+
setIsNotFound(false)
|
|
56
|
+
}
|
|
50
57
|
try {
|
|
51
58
|
const params = new URLSearchParams({ page: '1', pageSize: '1', ids: rulesetIdValue })
|
|
52
59
|
const payload = await readApiResultOrThrow<RuleSetResponse>(
|
|
@@ -55,7 +62,10 @@ export default function PlannerAvailabilityRuleSetDetailPage({ params }: { param
|
|
|
55
62
|
{ errorMessage: translate('planner.availabilityRuleSets.form.errors.load', 'Failed to load schedule.') },
|
|
56
63
|
)
|
|
57
64
|
const record = Array.isArray(payload.items) ? payload.items[0] : null
|
|
58
|
-
if (!record)
|
|
65
|
+
if (!record) {
|
|
66
|
+
if (!cancelled) setIsNotFound(true)
|
|
67
|
+
return
|
|
68
|
+
}
|
|
59
69
|
const customFields = extractCustomFieldEntries(record)
|
|
60
70
|
if (!cancelled) {
|
|
61
71
|
setInitialValues({
|
|
@@ -66,9 +76,15 @@ export default function PlannerAvailabilityRuleSetDetailPage({ params }: { param
|
|
|
66
76
|
...customFields,
|
|
67
77
|
})
|
|
68
78
|
}
|
|
69
|
-
} catch (
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
} catch (err) {
|
|
80
|
+
if (!cancelled) {
|
|
81
|
+
if ((err as { status?: number }).status === 404) {
|
|
82
|
+
setIsNotFound(true)
|
|
83
|
+
} else {
|
|
84
|
+
const message = err instanceof Error ? err.message : translate('planner.availabilityRuleSets.form.errors.load', 'Failed to load schedule.')
|
|
85
|
+
setError(message)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
72
88
|
}
|
|
73
89
|
}
|
|
74
90
|
loadRuleSet()
|
|
@@ -150,6 +166,30 @@ export default function PlannerAvailabilityRuleSetDetailPage({ params }: { param
|
|
|
150
166
|
|
|
151
167
|
const resolvedInitialValues = initialValues ?? {}
|
|
152
168
|
|
|
169
|
+
if (isNotFound) {
|
|
170
|
+
return (
|
|
171
|
+
<Page>
|
|
172
|
+
<PageBody>
|
|
173
|
+
<RecordNotFoundState
|
|
174
|
+
label={translate('planner.availabilityRuleSets.form.errors.notFound', 'Schedule not found.')}
|
|
175
|
+
backHref="/backend/planner/availability-rulesets"
|
|
176
|
+
backLabel={translate('planner.availabilityRuleSets.actions.backToList', 'Back to schedules')}
|
|
177
|
+
/>
|
|
178
|
+
</PageBody>
|
|
179
|
+
</Page>
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (error && !initialValues) {
|
|
184
|
+
return (
|
|
185
|
+
<Page>
|
|
186
|
+
<PageBody>
|
|
187
|
+
<ErrorMessage label={error} />
|
|
188
|
+
</PageBody>
|
|
189
|
+
</Page>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
153
193
|
return (
|
|
154
194
|
<Page>
|
|
155
195
|
<PageBody>
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"planner.availability.errors.updateDateSpecific": "Datumsspezifische Verfügbarkeit konnte nicht gespeichert werden.",
|
|
101
101
|
"planner.availability.errors.updateWeekly": "Wöchentliche Verfügbarkeit konnte nicht gespeichert werden.",
|
|
102
102
|
"planner.availabilityRuleSets.actions.add": "Neuer Zeitplan",
|
|
103
|
+
"planner.availabilityRuleSets.actions.backToList": "Zurück zu Zeitplänen",
|
|
103
104
|
"planner.availabilityRuleSets.actions.delete": "Löschen",
|
|
104
105
|
"planner.availabilityRuleSets.actions.deleteConfirm": "Zeitplan \"{{name}}\" löschen?",
|
|
105
106
|
"planner.availabilityRuleSets.actions.edit": "Bearbeiten",
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"planner.availability.errors.updateDateSpecific": "Failed to save date-specific availability.",
|
|
101
101
|
"planner.availability.errors.updateWeekly": "Failed to save weekly availability.",
|
|
102
102
|
"planner.availabilityRuleSets.actions.add": "New schedule",
|
|
103
|
+
"planner.availabilityRuleSets.actions.backToList": "Back to schedules",
|
|
103
104
|
"planner.availabilityRuleSets.actions.delete": "Delete",
|
|
104
105
|
"planner.availabilityRuleSets.actions.deleteConfirm": "Delete schedule \"{{name}}\"?",
|
|
105
106
|
"planner.availabilityRuleSets.actions.edit": "Edit",
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"planner.availability.errors.updateDateSpecific": "No se pudo guardar la disponibilidad por fecha.",
|
|
101
101
|
"planner.availability.errors.updateWeekly": "No se pudo guardar la disponibilidad semanal.",
|
|
102
102
|
"planner.availabilityRuleSets.actions.add": "Nuevo horario",
|
|
103
|
+
"planner.availabilityRuleSets.actions.backToList": "Volver a horarios",
|
|
103
104
|
"planner.availabilityRuleSets.actions.delete": "Eliminar",
|
|
104
105
|
"planner.availabilityRuleSets.actions.deleteConfirm": "¿Eliminar el horario \"{{name}}\"?",
|
|
105
106
|
"planner.availabilityRuleSets.actions.edit": "Editar",
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"planner.availability.errors.updateDateSpecific": "Nie udało się zapisać dostępności dla dat.",
|
|
101
101
|
"planner.availability.errors.updateWeekly": "Nie udało się zapisać tygodniowej dostępności.",
|
|
102
102
|
"planner.availabilityRuleSets.actions.add": "Nowy harmonogram",
|
|
103
|
+
"planner.availabilityRuleSets.actions.backToList": "Powrót do harmonogramów",
|
|
103
104
|
"planner.availabilityRuleSets.actions.delete": "Usuń",
|
|
104
105
|
"planner.availabilityRuleSets.actions.deleteConfirm": "Usunąć harmonogram \"{{name}}\"?",
|
|
105
106
|
"planner.availabilityRuleSets.actions.edit": "Edytuj",
|
|
@@ -4,6 +4,7 @@ import * as React from 'react'
|
|
|
4
4
|
import { useRouter, useSearchParams } from 'next/navigation'
|
|
5
5
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
6
6
|
import { CrudForm } from '@open-mercato/ui/backend/CrudForm'
|
|
7
|
+
import { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'
|
|
7
8
|
import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
|
|
8
9
|
import { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
9
10
|
import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
@@ -28,6 +29,7 @@ export default function EditChannelPage({ params }: { params?: { channelId?: str
|
|
|
28
29
|
const [initialValues, setInitialValues] = React.useState<ChannelFormValues | null>(null)
|
|
29
30
|
const [loading, setLoading] = React.useState(true)
|
|
30
31
|
const [error, setError] = React.useState<string | null>(null)
|
|
32
|
+
const [isNotFound, setIsNotFound] = React.useState(false)
|
|
31
33
|
const [activeTab, setActiveTab] = React.useState<'settings' | 'offers'>('settings')
|
|
32
34
|
|
|
33
35
|
React.useEffect(() => {
|
|
@@ -45,6 +47,7 @@ export default function EditChannelPage({ params }: { params?: { channelId?: str
|
|
|
45
47
|
async function loadChannel() {
|
|
46
48
|
setLoading(true)
|
|
47
49
|
setError(null)
|
|
50
|
+
setIsNotFound(false)
|
|
48
51
|
try {
|
|
49
52
|
const payload = await readApiResultOrThrow<ChannelApiResponse>(
|
|
50
53
|
`/api/sales/channels?id=${encodeURIComponent(channelId)}&pageSize=1`,
|
|
@@ -53,14 +56,21 @@ export default function EditChannelPage({ params }: { params?: { channelId?: str
|
|
|
53
56
|
)
|
|
54
57
|
const item = Array.isArray(payload.items) ? payload.items[0] : null
|
|
55
58
|
if (!item) {
|
|
56
|
-
|
|
59
|
+
if (!cancelled) setIsNotFound(true)
|
|
60
|
+
return
|
|
57
61
|
}
|
|
58
62
|
if (!cancelled) {
|
|
59
63
|
setInitialValues(mapChannelToFormValues(item))
|
|
60
64
|
}
|
|
61
65
|
} catch (err) {
|
|
62
66
|
console.error('sales.channels.load', err)
|
|
63
|
-
if (!cancelled)
|
|
67
|
+
if (!cancelled) {
|
|
68
|
+
if ((err as { status?: number }).status === 404) {
|
|
69
|
+
setIsNotFound(true)
|
|
70
|
+
} else {
|
|
71
|
+
setError(t('sales.channels.form.errors.load', 'Failed to load channel.'))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
64
74
|
} finally {
|
|
65
75
|
if (!cancelled) setLoading(false)
|
|
66
76
|
}
|
|
@@ -116,14 +126,33 @@ export default function EditChannelPage({ params }: { params?: { channelId?: str
|
|
|
116
126
|
</div>
|
|
117
127
|
), [tabButton, t])
|
|
118
128
|
|
|
129
|
+
if (isNotFound) {
|
|
130
|
+
return (
|
|
131
|
+
<Page>
|
|
132
|
+
<PageBody>
|
|
133
|
+
<RecordNotFoundState
|
|
134
|
+
label={t('sales.channels.form.errors.notFound', 'Channel not found.')}
|
|
135
|
+
backHref="/backend/sales/channels"
|
|
136
|
+
backLabel={t('sales.channels.actions.backToList', 'Back to channels')}
|
|
137
|
+
/>
|
|
138
|
+
</PageBody>
|
|
139
|
+
</Page>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (error && !loading && !initialValues) {
|
|
144
|
+
return (
|
|
145
|
+
<Page>
|
|
146
|
+
<PageBody>
|
|
147
|
+
<ErrorMessage label={error} />
|
|
148
|
+
</PageBody>
|
|
149
|
+
</Page>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
119
153
|
return (
|
|
120
154
|
<Page>
|
|
121
155
|
<PageBody>
|
|
122
|
-
{error ? (
|
|
123
|
-
<div className="mb-4 rounded border border-destructive/40 bg-destructive/10 px-4 py-3 text-sm text-destructive">
|
|
124
|
-
{error}
|
|
125
|
-
</div>
|
|
126
|
-
) : null}
|
|
127
156
|
{activeTab === 'settings' ? (
|
|
128
157
|
<CrudForm<ChannelFormValues>
|
|
129
158
|
title={t('sales.channels.form.editTitle', 'Edit channel')}
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"sales.audit.tax-rates.create": "Create tax rate",
|
|
53
53
|
"sales.audit.tax-rates.delete": "Delete tax rate",
|
|
54
54
|
"sales.audit.tax-rates.update": "Update tax rate",
|
|
55
|
+
"sales.channels.actions.backToList": "Zurück zu Kanälen",
|
|
55
56
|
"sales.channels.actions.create": "Kanal hinzufügen",
|
|
56
57
|
"sales.channels.create": "Vertriebskanal erstellen",
|
|
57
58
|
"sales.channels.form.address1": "Adresszeile 1",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"sales.audit.tax-rates.create": "Create tax rate",
|
|
53
53
|
"sales.audit.tax-rates.delete": "Delete tax rate",
|
|
54
54
|
"sales.audit.tax-rates.update": "Update tax rate",
|
|
55
|
+
"sales.channels.actions.backToList": "Back to channels",
|
|
55
56
|
"sales.channels.actions.create": "Add channel",
|
|
56
57
|
"sales.channels.create": "Create sales channel",
|
|
57
58
|
"sales.channels.form.address1": "Address line 1",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"sales.audit.tax-rates.create": "Create tax rate",
|
|
53
53
|
"sales.audit.tax-rates.delete": "Delete tax rate",
|
|
54
54
|
"sales.audit.tax-rates.update": "Update tax rate",
|
|
55
|
+
"sales.channels.actions.backToList": "Volver a canales",
|
|
55
56
|
"sales.channels.actions.create": "Añadir canal",
|
|
56
57
|
"sales.channels.create": "Crear canal de ventas",
|
|
57
58
|
"sales.channels.form.address1": "Línea de dirección 1",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"sales.audit.tax-rates.create": "Create tax rate",
|
|
53
53
|
"sales.audit.tax-rates.delete": "Delete tax rate",
|
|
54
54
|
"sales.audit.tax-rates.update": "Update tax rate",
|
|
55
|
+
"sales.channels.actions.backToList": "Powrót do kanałów",
|
|
55
56
|
"sales.channels.actions.create": "Dodaj kanał",
|
|
56
57
|
"sales.channels.create": "Utwórz kanał sprzedaży",
|
|
57
58
|
"sales.channels.form.address1": "Adres linia 1",
|
|
@@ -6,7 +6,7 @@ import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
|
6
6
|
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
7
7
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
8
8
|
import { Textarea } from '@open-mercato/ui/primitives/textarea'
|
|
9
|
-
import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
9
|
+
import { LoadingMessage, ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'
|
|
10
10
|
import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
|
|
11
11
|
import { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
12
12
|
import { updateCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
@@ -21,12 +21,13 @@ export default function StaffLeaveRequestDetailPage({ params }: { params?: { id?
|
|
|
21
21
|
const router = useRouter()
|
|
22
22
|
const [isLoading, setIsLoading] = React.useState(true)
|
|
23
23
|
const [error, setError] = React.useState<string | null>(null)
|
|
24
|
+
const [isNotFound, setIsNotFound] = React.useState(false)
|
|
24
25
|
const [record, setRecord] = React.useState<NormalizedLeaveRequest | null>(null)
|
|
25
26
|
const [decisionComment, setDecisionComment] = React.useState('')
|
|
26
27
|
|
|
27
28
|
React.useEffect(() => {
|
|
28
29
|
if (!id) {
|
|
29
|
-
|
|
30
|
+
setIsNotFound(true)
|
|
30
31
|
setIsLoading(false)
|
|
31
32
|
return
|
|
32
33
|
}
|
|
@@ -35,6 +36,7 @@ export default function StaffLeaveRequestDetailPage({ params }: { params?: { id?
|
|
|
35
36
|
async function load() {
|
|
36
37
|
setIsLoading(true)
|
|
37
38
|
setError(null)
|
|
39
|
+
setIsNotFound(false)
|
|
38
40
|
try {
|
|
39
41
|
const params = new URLSearchParams({ page: '1', pageSize: '1', ids: requestId })
|
|
40
42
|
const payload = await readApiResultOrThrow<LeaveRequestsResponse>(
|
|
@@ -43,7 +45,13 @@ export default function StaffLeaveRequestDetailPage({ params }: { params?: { id?
|
|
|
43
45
|
{ errorMessage: t('staff.leaveRequests.errors.load', 'Failed to load leave request.') },
|
|
44
46
|
)
|
|
45
47
|
const entry = Array.isArray(payload.items) ? payload.items[0] : null
|
|
46
|
-
if (!entry)
|
|
48
|
+
if (!entry) {
|
|
49
|
+
if (!cancelled) {
|
|
50
|
+
setIsNotFound(true)
|
|
51
|
+
setRecord(null)
|
|
52
|
+
}
|
|
53
|
+
return
|
|
54
|
+
}
|
|
47
55
|
if (!cancelled) {
|
|
48
56
|
const normalized = normalizeLeaveRequest(entry)
|
|
49
57
|
setRecord(normalized)
|
|
@@ -51,9 +59,14 @@ export default function StaffLeaveRequestDetailPage({ params }: { params?: { id?
|
|
|
51
59
|
}
|
|
52
60
|
} catch (err) {
|
|
53
61
|
if (!cancelled) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
if ((err as { status?: number }).status === 404) {
|
|
63
|
+
setIsNotFound(true)
|
|
64
|
+
setRecord(null)
|
|
65
|
+
} else {
|
|
66
|
+
const message = err instanceof Error ? err.message : t('staff.leaveRequests.errors.load', 'Failed to load leave request.')
|
|
67
|
+
setError(message)
|
|
68
|
+
setRecord(null)
|
|
69
|
+
}
|
|
57
70
|
}
|
|
58
71
|
} finally {
|
|
59
72
|
if (!cancelled) setIsLoading(false)
|
|
@@ -115,6 +128,20 @@ const handleSubmit = React.useCallback(async (values: LeaveRequestFormValues) =>
|
|
|
115
128
|
)
|
|
116
129
|
}
|
|
117
130
|
|
|
131
|
+
if (isNotFound) {
|
|
132
|
+
return (
|
|
133
|
+
<Page>
|
|
134
|
+
<PageBody>
|
|
135
|
+
<RecordNotFoundState
|
|
136
|
+
label={t('staff.leaveRequests.errors.notFound', 'Leave request not found.')}
|
|
137
|
+
backHref="/backend/staff/leave-requests"
|
|
138
|
+
backLabel={t('staff.leaveRequests.actions.backToList', 'Back to leave requests')}
|
|
139
|
+
/>
|
|
140
|
+
</PageBody>
|
|
141
|
+
</Page>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
118
145
|
if (error || !record) {
|
|
119
146
|
return (
|
|
120
147
|
<Page>
|