@webx-ui/module-catalog 0.8.9 → 0.9.0
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/CategoriesPage-B6GBTzvN.js +279 -0
- package/dist/CategoriesPage-B6GBTzvN.js.map +1 -0
- package/dist/CategoryEditorPage-y6EFmOof.js +232 -0
- package/dist/CategoryEditorPage-y6EFmOof.js.map +1 -0
- package/dist/DeletedPage-TGTyQbzn.js +214 -0
- package/dist/DeletedPage-TGTyQbzn.js.map +1 -0
- package/dist/ExchangeImportPage-C6cSg7Cr.js +603 -0
- package/dist/ExchangeImportPage-C6cSg7Cr.js.map +1 -0
- package/dist/ExchangePage-9RrRcSiS.js +544 -0
- package/dist/ExchangePage-9RrRcSiS.js.map +1 -0
- package/dist/ExchangeProfilePage-BUnh9_D7.js +321 -0
- package/dist/ExchangeProfilePage-BUnh9_D7.js.map +1 -0
- package/dist/ExchangeProfilesPage-T6G3HHD8.js +180 -0
- package/dist/ExchangeProfilesPage-T6G3HHD8.js.map +1 -0
- package/dist/ProductEditorPage-Bfp6ZV3d.js +262 -0
- package/dist/ProductEditorPage-Bfp6ZV3d.js.map +1 -0
- package/dist/ProductsPage-BBmw9W4m.js +963 -0
- package/dist/ProductsPage-BBmw9W4m.js.map +1 -0
- package/dist/index-BTEbHnJ5.js +2571 -0
- package/dist/index-BTEbHnJ5.js.map +1 -0
- package/dist/index.d.ts +67 -9
- package/dist/index.js +46 -6056
- package/dist/index.js.map +1 -1
- package/dist/paths-DvfRnXtl.js +15 -0
- package/dist/paths-DvfRnXtl.js.map +1 -0
- package/dist/style.css +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExchangeProfilesPage-T6G3HHD8.js","sources":["../src/ExchangeProfilesPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\nimport { RouterLink, useRouter } from 'vue-router'\nimport {\n rowMenuWidth,\n useAdmin,\n useErrorText,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n toast,\n WxBadge,\n WxIcon,\n WxLink,\n WxTable,\n WxText,\n type TableColumn,\n} from '@webx-ui/core'\nimport { createExchangeApi, type ExchangeProfile } from './exchange'\nimport { useCatalogMessages } from './i18n'\n\n/**\n * The saved profiles of the exchange (§8.1): a name, a direction, a format, and what a file of\n * that kind looks like — so the supplier's price list of every Monday is one step, not three.\n * A row opens the form; an import profile also starts the wizard with itself filled in.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\nconst context = useAdmin()\nconst api = createExchangeApi(context)\nconst router = useRouter()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\nconst message = useErrorText()\n\nconst rows = ref<ExchangeProfile[] | null>(null)\nconst canManage = computed(() => context.can('catalog.manage'))\n\nasync function load(): Promise<void> {\n try {\n rows.value = await api.profiles()\n } catch (error) {\n toast.danger(message(error))\n rows.value = []\n }\n}\n\nonMounted(load)\n\nconst columns = computed<TableColumn<ExchangeProfile>[]>(() => [\n { key: 'name', label: t('panel.exchange-profile-name'), minWidth: 200 },\n { key: 'direction', label: t('panel.exchange-profile-direction'), width: 150, hideBelow: 560 },\n { key: 'format', label: t('panel.exchange-format'), width: 100, hideBelow: 480 },\n { key: 'last_run_id', label: t('panel.exchange-profile-last-run'), width: 150, hideBelow: 720 },\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right' },\n])\n\nconst actions = computed<ScreenAction[]>(() =>\n canManage.value\n ? [\n {\n key: 'new-import',\n label: t('panel.exchange-new-import-profile'),\n icon: 'plus',\n primary: true,\n run: () =>\n void router.push({\n path: `${props.base}/exchange/profiles/new`,\n query: { direction: 'import' },\n }),\n },\n {\n key: 'new-export',\n label: t('panel.exchange-new-export-profile'),\n icon: 'plus',\n run: () =>\n void router.push({\n path: `${props.base}/exchange/profiles/new`,\n query: { direction: 'export' },\n }),\n },\n ]\n : [],\n)\n\nfunction edit(row: ExchangeProfile): void {\n void router.push(`${props.base}/exchange/profiles/${row.id}`)\n}\n\nasync function remove(row: ExchangeProfile): Promise<void> {\n const sure = await confirm({\n title: t('panel.exchange-profile-delete-title', { name: row.name }),\n message: t('panel.exchange-profile-delete-text'),\n confirmText: t('panel.exchange-delete'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!sure) return\n\n try {\n await api.removeProfile(row.id)\n toast.success(t('panel.exchange-profile-deleted'))\n await load()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction actionsFor(row: ExchangeProfile): RowAction[] {\n const list: RowAction[] = []\n\n if (row.direction === 'import' && canManage.value) {\n list.push({\n key: 'run',\n icon: 'upload',\n label: t('panel.exchange-profile-run'),\n run: () =>\n void router.push({\n path: `${props.base}/exchange/import`,\n query: { profile: String(row.id) },\n }),\n })\n }\n\n list.push({\n key: 'edit',\n icon: 'edit',\n label: t('panel.exchange-profile-edit'),\n run: () => edit(row),\n })\n\n if (canManage.value) {\n list.push({\n key: 'delete',\n icon: 'trash',\n label: t('panel.exchange-delete'),\n danger: true,\n run: () => void remove(row),\n })\n }\n\n return list\n}\n</script>\n\n<template>\n <div class=\"wx-catalog-profiles\">\n <wx-list-screen\n :title=\"t('panel.exchange-profiles-title')\"\n :subtitle=\"t('panel.exchange-profiles-help')\"\n :back=\"`${props.base}/exchange`\"\n :back-label=\"t('panel.exchange-title')\"\n :actions=\"actions\"\n >\n <wx-table\n :data=\"rows ?? []\"\n :columns=\"columns\"\n row-key=\"id\"\n flush\n layout=\"fixed\"\n :loading=\"rows === null\"\n :empty-text=\"t('panel.exchange-profiles-empty')\"\n :aria-label=\"t('panel.exchange-profiles-title')\"\n @row-click=\"edit\"\n >\n <template #cell-name=\"{ row }\">\n <span class=\"wx-catalog-profiles__name\">{{ row.name }}</span>\n </template>\n <template #cell-direction=\"{ row }\">\n <span class=\"wx-catalog-profiles__direction\">\n <wx-icon :name=\"row.direction === 'import' ? 'upload' : 'download'\" size=\"sm\" />\n {{ t(`panel.exchange-${row.direction}`) }}\n </span>\n </template>\n <template #cell-format=\"{ row }\">\n <wx-badge size=\"sm\">{{ row.format.toUpperCase() }}</wx-badge>\n </template>\n <template #cell-last_run_id=\"{ row }\">\n <wx-link\n v-if=\"row.last_run_id !== null\"\n :as=\"RouterLink\"\n :to=\"{ path: `${props.base}/exchange`, query: { run: String(row.last_run_id) } }\"\n @click.stop\n >\n #{{ row.last_run_id }}\n </wx-link>\n <wx-text v-else size=\"sm\" tone=\"muted\">—</wx-text>\n </template>\n <template #cell-actions=\"{ row }\">\n <wx-row-menu :actions=\"actionsFor(row)\" :label=\"row.name\" />\n </template>\n </wx-table>\n </wx-list-screen>\n </div>\n</template>\n\n<style scoped>\n.wx-catalog-profiles {\n min-width: 0;\n}\n\n.wx-catalog-profiles__name {\n display: block;\n font-weight: var(--wx-font-weight-medium);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-catalog-profiles__direction {\n display: inline-flex;\n align-items: center;\n gap: var(--wx-space-6);\n}\n</style>\n"],"names":["props","__props","context","useAdmin","api","createExchangeApi","router","useRouter","useCatalogMessages","t","useTranslate","message","useErrorText","rows","ref","canManage","computed","load","error","toast","onMounted","columns","rowMenuWidth","actions","edit","row","remove","confirm","actionsFor","list","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","WxListScreen","WxTable","_withCtx","_createElementVNode","_hoisted_2","_toDisplayString","_hoisted_3","WxIcon","_createTextVNode","WxBadge","_createBlock","WxLink","RouterLink","WxText","_cache","WxRowMenu"],"mappings":"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"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { defineComponent as ne, computed as o, ref as i, watch as oe, onMounted as re, onBeforeUnmount as ue, openBlock as c, createElementBlock as $, Fragment as G, createVNode as u, unref as t, withCtx as r, createTextVNode as d, toDisplayString as v, createBlock as k, createCommentVNode as P } from "vue";
|
|
2
|
+
import { useRoute as se, useRouter as ie, onBeforeRouteLeave as ce } from "vue-router";
|
|
3
|
+
import { useAdmin as de, useTranslate as ve, useErrorText as pe, provideHistorySubject as fe, provideRecordAddress as me, useBodyKeys as ge, WxScreenHead as ye, WxScreen as be, WxSaveState as ke } from "@webx-ui/module-admin";
|
|
4
|
+
import { useLocales as xe, localizedValue as we, confirm as V, WxSkeleton as H, WxCard as _e, WxBadge as I, WxBreadcrumb as he, WxBreadcrumbItem as L, WxAlert as We, WxButton as J, WxActionBar as Be, toast as m } from "@webx-ui/core";
|
|
5
|
+
import { c as Ce, a as Se, e as Ee, p as Te, _ as $e } from "./index-BTEbHnJ5.js";
|
|
6
|
+
import { l as Pe } from "./paths-DvfRnXtl.js";
|
|
7
|
+
const Ve = /* @__PURE__ */ ne({
|
|
8
|
+
name: "WxCatalogProductEditor",
|
|
9
|
+
__name: "ProductEditorPage",
|
|
10
|
+
props: {
|
|
11
|
+
base: { default: "/catalog" }
|
|
12
|
+
},
|
|
13
|
+
setup(U) {
|
|
14
|
+
const W = U, g = de(), B = Ce(g), F = se(), C = ie(), Q = xe();
|
|
15
|
+
Se();
|
|
16
|
+
const a = ve("webx-catalog"), S = pe(), N = o(() => Number(F.params.id)), x = o(() => `${W.base}/products`), n = i(null), s = i({}), E = i([]), w = i(""), T = i(!0), p = i(!1), _ = i({}), f = i(null), h = o(() => g.can("catalog.manage")), X = o(() => g.can("catalog.delete")), A = o(() => !h.value || p.value), R = o(() => JSON.stringify(s.value)), y = o(() => w.value !== "" && R.value !== w.value), Y = o(() => p.value ? "saving" : y.value ? "unsaved" : "saved"), Z = o(
|
|
17
|
+
() => g.state.manifest?.modules.find((e) => e.id === "catalog")?.title ?? a("module.title")
|
|
18
|
+
), ee = o(() => {
|
|
19
|
+
const e = s.value.name;
|
|
20
|
+
return we(e, Q.active.value, n.value?.name ?? "") || a("panel.untitled");
|
|
21
|
+
}), ae = o(() => {
|
|
22
|
+
const e = n.value;
|
|
23
|
+
if (e)
|
|
24
|
+
return e.is_published && !e.visible ? a("panel.invisible") : e.sku ?? void 0;
|
|
25
|
+
}), D = Ee({
|
|
26
|
+
admin: g,
|
|
27
|
+
product: o(() => n.value?.id ?? null),
|
|
28
|
+
images: E
|
|
29
|
+
});
|
|
30
|
+
Te({ product: n, images: E, locked: A, video: D, values: s }), fe({ id: o(() => n.value?.id ?? null) }), me({
|
|
31
|
+
values: s,
|
|
32
|
+
prefix: i(""),
|
|
33
|
+
path: o(() => Pe(n.value?.url)?.replace(new RegExp(`-${n.value?.id}$`), "") ?? null),
|
|
34
|
+
moving: () => a("panel.address-moving")
|
|
35
|
+
});
|
|
36
|
+
function K(e) {
|
|
37
|
+
n.value = e.product, s.value = e.values, E.value = e.images, w.value = JSON.stringify(e.values);
|
|
38
|
+
}
|
|
39
|
+
async function z() {
|
|
40
|
+
T.value = !0;
|
|
41
|
+
try {
|
|
42
|
+
K(await B.product(N.value));
|
|
43
|
+
} catch (e) {
|
|
44
|
+
m.danger(S(e)), C.push(x.value);
|
|
45
|
+
} finally {
|
|
46
|
+
T.value = !1;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function M() {
|
|
50
|
+
if (!(!n.value || p.value || !h.value)) {
|
|
51
|
+
p.value = !0, _.value = {}, f.value = null;
|
|
52
|
+
try {
|
|
53
|
+
K(await B.saveProduct(n.value.id, s.value)), m.success(a("panel.saved"));
|
|
54
|
+
} catch (e) {
|
|
55
|
+
const l = e.body;
|
|
56
|
+
l?.errors && Object.keys(l.errors).length > 0 ? (_.value = l.errors, f.value = l.meta?.taken_by ?? null, m.danger(a("panel.save-failed"))) : m.danger(S(e));
|
|
57
|
+
} finally {
|
|
58
|
+
p.value = !1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const O = o(() => {
|
|
63
|
+
const e = f.value;
|
|
64
|
+
if (!e) return null;
|
|
65
|
+
if (e.deleted) {
|
|
66
|
+
const l = typeof s.value.sku == "string" ? s.value.sku : "";
|
|
67
|
+
return { path: `${W.base}/deleted`, query: l ? { q: l } : {} };
|
|
68
|
+
}
|
|
69
|
+
return { path: `${W.base}/products/${e.id}` };
|
|
70
|
+
});
|
|
71
|
+
async function te() {
|
|
72
|
+
const e = n.value;
|
|
73
|
+
if (!(!e || !await V({
|
|
74
|
+
title: a("panel.delete-product-title", { name: e.name }),
|
|
75
|
+
message: a("panel.delete-product-text"),
|
|
76
|
+
confirmText: a("panel.delete"),
|
|
77
|
+
cancelText: a("panel.cancel"),
|
|
78
|
+
tone: "danger"
|
|
79
|
+
})))
|
|
80
|
+
try {
|
|
81
|
+
await B.removeProduct(e.id), m.success(a("panel.product-deleted")), w.value = R.value, C.push(x.value);
|
|
82
|
+
} catch (b) {
|
|
83
|
+
m.danger(S(b));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function j(e) {
|
|
87
|
+
y.value && e.preventDefault();
|
|
88
|
+
}
|
|
89
|
+
function q(e) {
|
|
90
|
+
(e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "s" && (e.preventDefault(), y.value && M());
|
|
91
|
+
}
|
|
92
|
+
ge(q), oe(N, () => {
|
|
93
|
+
z();
|
|
94
|
+
}), re(() => {
|
|
95
|
+
z(), window.addEventListener("beforeunload", j);
|
|
96
|
+
}), ue(() => {
|
|
97
|
+
window.removeEventListener("beforeunload", j);
|
|
98
|
+
}), ce(async () => D.busy.value && !await V({
|
|
99
|
+
title: a("panel.video-leave-title"),
|
|
100
|
+
message: a("panel.video-leave-text"),
|
|
101
|
+
confirmText: a("panel.leave"),
|
|
102
|
+
cancelText: a("panel.cancel"),
|
|
103
|
+
tone: "danger"
|
|
104
|
+
}) ? !1 : !y.value || !h.value ? !0 : await V({
|
|
105
|
+
title: a("panel.leave-title"),
|
|
106
|
+
message: a("panel.leave-text"),
|
|
107
|
+
confirmText: a("panel.leave"),
|
|
108
|
+
cancelText: a("panel.cancel"),
|
|
109
|
+
tone: "danger"
|
|
110
|
+
}));
|
|
111
|
+
const le = o(() => {
|
|
112
|
+
const e = n.value, l = [];
|
|
113
|
+
return e && (e.url && l.push({ key: "site", label: a("panel.open-on-site"), icon: "link", href: e.url }), X.value && l.push({
|
|
114
|
+
key: "delete",
|
|
115
|
+
label: a("panel.delete"),
|
|
116
|
+
icon: "trash",
|
|
117
|
+
danger: !0,
|
|
118
|
+
menu: !0,
|
|
119
|
+
run: () => {
|
|
120
|
+
te();
|
|
121
|
+
}
|
|
122
|
+
})), l;
|
|
123
|
+
});
|
|
124
|
+
return (e, l) => (c(), $("div", {
|
|
125
|
+
class: "wx-catalog-product",
|
|
126
|
+
onKeydown: q
|
|
127
|
+
}, [
|
|
128
|
+
T.value || !n.value ? (c(), $(G, { key: 0 }, [
|
|
129
|
+
u(t(H), {
|
|
130
|
+
class: "wx-catalog-product__ghost",
|
|
131
|
+
title: "",
|
|
132
|
+
rows: 1
|
|
133
|
+
}),
|
|
134
|
+
u(t(_e), null, {
|
|
135
|
+
default: r(() => [
|
|
136
|
+
u(t(H), { rows: 8 })
|
|
137
|
+
]),
|
|
138
|
+
_: 1
|
|
139
|
+
})
|
|
140
|
+
], 64)) : (c(), $(G, { key: 1 }, [
|
|
141
|
+
u(t(ye), {
|
|
142
|
+
divider: "",
|
|
143
|
+
back: x.value,
|
|
144
|
+
"back-label": t(a)("module.products"),
|
|
145
|
+
title: ee.value,
|
|
146
|
+
subtitle: ae.value,
|
|
147
|
+
actions: le.value
|
|
148
|
+
}, {
|
|
149
|
+
trail: r(() => [
|
|
150
|
+
u(t(he), {
|
|
151
|
+
size: "sm",
|
|
152
|
+
label: t(a)("panel.product-trail")
|
|
153
|
+
}, {
|
|
154
|
+
default: r(() => [
|
|
155
|
+
u(t(L), {
|
|
156
|
+
as: "router-link",
|
|
157
|
+
to: x.value
|
|
158
|
+
}, {
|
|
159
|
+
default: r(() => [
|
|
160
|
+
d(v(Z.value), 1)
|
|
161
|
+
]),
|
|
162
|
+
_: 1
|
|
163
|
+
}, 8, ["to"]),
|
|
164
|
+
n.value.category ? (c(), k(t(L), {
|
|
165
|
+
key: 0,
|
|
166
|
+
current: ""
|
|
167
|
+
}, {
|
|
168
|
+
default: r(() => [
|
|
169
|
+
d(v(n.value.category.name), 1)
|
|
170
|
+
]),
|
|
171
|
+
_: 1
|
|
172
|
+
})) : (c(), k(t(L), {
|
|
173
|
+
key: 1,
|
|
174
|
+
current: ""
|
|
175
|
+
}, {
|
|
176
|
+
default: r(() => [
|
|
177
|
+
d(v(t(a)("product.no-category")), 1)
|
|
178
|
+
]),
|
|
179
|
+
_: 1
|
|
180
|
+
}))
|
|
181
|
+
]),
|
|
182
|
+
_: 1
|
|
183
|
+
}, 8, ["label"])
|
|
184
|
+
]),
|
|
185
|
+
"title-after": r(() => [
|
|
186
|
+
u(t(I), {
|
|
187
|
+
type: n.value.is_published ? n.value.visible ? "success" : "warning" : "default",
|
|
188
|
+
dot: ""
|
|
189
|
+
}, {
|
|
190
|
+
default: r(() => [
|
|
191
|
+
d(v(t(a)(`states.${n.value.state}`)), 1)
|
|
192
|
+
]),
|
|
193
|
+
_: 1
|
|
194
|
+
}, 8, ["type"]),
|
|
195
|
+
n.value.category ? P("", !0) : (c(), k(t(I), {
|
|
196
|
+
key: 0,
|
|
197
|
+
type: "warning",
|
|
198
|
+
round: ""
|
|
199
|
+
}, {
|
|
200
|
+
default: r(() => [
|
|
201
|
+
d(v(t(a)("product.no-category")), 1)
|
|
202
|
+
]),
|
|
203
|
+
_: 1
|
|
204
|
+
}))
|
|
205
|
+
]),
|
|
206
|
+
_: 1
|
|
207
|
+
}, 8, ["back", "back-label", "title", "subtitle", "actions"]),
|
|
208
|
+
f.value && O.value ? (c(), k(t(We), {
|
|
209
|
+
key: 0,
|
|
210
|
+
type: "warning",
|
|
211
|
+
description: _.value.sku?.[0] ?? "",
|
|
212
|
+
closable: "",
|
|
213
|
+
onClose: l[1] || (l[1] = (b) => f.value = null)
|
|
214
|
+
}, {
|
|
215
|
+
actions: r(() => [
|
|
216
|
+
u(t(J), {
|
|
217
|
+
size: "sm",
|
|
218
|
+
variant: "outline",
|
|
219
|
+
onClick: l[0] || (l[0] = (b) => t(C).push(O.value))
|
|
220
|
+
}, {
|
|
221
|
+
default: r(() => [
|
|
222
|
+
d(v(f.value.deleted ? t(a)("panel.sku-open-deleted") : t(a)("panel.sku-open")), 1)
|
|
223
|
+
]),
|
|
224
|
+
_: 1
|
|
225
|
+
})
|
|
226
|
+
]),
|
|
227
|
+
_: 1
|
|
228
|
+
}, 8, ["description"])) : P("", !0),
|
|
229
|
+
u(t(be), {
|
|
230
|
+
modelValue: s.value,
|
|
231
|
+
"onUpdate:modelValue": l[2] || (l[2] = (b) => s.value = b),
|
|
232
|
+
name: "catalog.product-form",
|
|
233
|
+
errors: _.value,
|
|
234
|
+
disabled: A.value
|
|
235
|
+
}, null, 8, ["modelValue", "errors", "disabled"]),
|
|
236
|
+
h.value ? (c(), k(t(Be), { key: 1 }, {
|
|
237
|
+
state: r(() => [
|
|
238
|
+
u(t(ke), { state: Y.value }, null, 8, ["state"])
|
|
239
|
+
]),
|
|
240
|
+
default: r(() => [
|
|
241
|
+
u(t(J), {
|
|
242
|
+
type: "primary",
|
|
243
|
+
loading: p.value,
|
|
244
|
+
disabled: !y.value,
|
|
245
|
+
onClick: M
|
|
246
|
+
}, {
|
|
247
|
+
default: r(() => [
|
|
248
|
+
d(v(t(a)("panel.save")), 1)
|
|
249
|
+
]),
|
|
250
|
+
_: 1
|
|
251
|
+
}, 8, ["loading", "disabled"])
|
|
252
|
+
]),
|
|
253
|
+
_: 1
|
|
254
|
+
})) : P("", !0)
|
|
255
|
+
], 64))
|
|
256
|
+
], 32));
|
|
257
|
+
}
|
|
258
|
+
}), ze = /* @__PURE__ */ $e(Ve, [["__scopeId", "data-v-9da5d427"]]);
|
|
259
|
+
export {
|
|
260
|
+
ze as default
|
|
261
|
+
};
|
|
262
|
+
//# sourceMappingURL=ProductEditorPage-Bfp6ZV3d.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductEditorPage-Bfp6ZV3d.js","sources":["../src/ProductEditorPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'\nimport {\n provideHistorySubject,\n provideRecordAddress,\n useAdmin,\n useBodyKeys,\n useErrorText,\n useTranslate,\n WxSaveState,\n WxScreen,\n WxScreenHead,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n localizedValue,\n toast,\n useLocales,\n WxActionBar,\n WxAlert,\n WxBadge,\n WxBreadcrumb,\n WxBreadcrumbItem,\n WxButton,\n WxCard,\n WxSkeleton,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport { createCatalogApi } from './api'\nimport { provideProductEditor } from './editor'\nimport { createGalleryVideo } from './galleryVideo'\nimport { useCatalogMessages } from './i18n'\nimport { lastSegment } from './paths'\nimport type { ProductDetail, ProductImage, ProductRow, SkuHolder } from './types'\n\n/**\n * One product: a head, `catalog.product-form` under it, and one button (§11.1).\n *\n * One explicit save, as a category has: a product has no draft (decision 14), so what is saved is\n * on the site at once — a price that changes between two keystrokes is not something to do behind\n * the editor's back. The gallery is the exception and saves itself: its pictures are records of\n * their own, not values of this form (`GalleryField.vue`).\n *\n * The article number is unique across deleted products too (decision 5), so a refusal names the\n * product holding it — and this page turns that into a way to it, which a line under the field\n * cannot be.\n */\ndefineOptions({ name: 'WxCatalogProductEditor' })\n\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst route = useRoute()\nconst router = useRouter()\nconst locales = useLocales()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst id = computed(() => Number(route.params.id))\nconst list = computed(() => `${props.base}/products`)\n\nconst product = ref<ProductRow | null>(null)\nconst values = ref<ScreenModel>({})\nconst images = ref<ProductImage[]>([])\nconst snapshot = ref('')\n\nconst loading = ref(true)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\nconst holder = ref<SkuHolder | null>(null)\n\nconst canManage = computed(() => context.can('catalog.manage'))\nconst canDelete = computed(() => context.can('catalog.delete'))\nconst locked = computed(() => !canManage.value || saving.value)\n\nconst current = computed(() => JSON.stringify(values.value))\nconst dirty = computed(() => snapshot.value !== '' && current.value !== snapshot.value)\n\nconst state = computed<'saving' | 'unsaved' | 'saved'>(() => {\n if (saving.value) return 'saving'\n\n return dirty.value ? 'unsaved' : 'saved'\n})\n\nconst section = computed(\n () =>\n context.state.manifest?.modules.find((module) => module.id === 'catalog')?.title ??\n t('module.title'),\n)\n\n/** The name follows the field rather than the answer: a rename shows at the top as it is typed. */\nconst title = computed(() => {\n const written = values.value.name as LocalizedValue | string | null | undefined\n\n return (\n localizedValue(written, locales.active.value, product.value?.name ?? '') || t('panel.untitled')\n )\n})\n\n/**\n * What the site does with it, under the name. Published but seen by nobody is the one state worth\n * a sentence: every other is what the badge beside the name already says.\n */\nconst subtitle = computed(() => {\n const row = product.value\n\n if (!row) return undefined\n\n if (row.is_published && !row.visible) return t('panel.invisible')\n\n return row.sku ?? undefined\n})\n\n/* The uploads of video files outlive the gallery's tab, so they are kept here (galleryVideo.ts). */\nconst video = createGalleryVideo({\n admin: context,\n product: computed(() => product.value?.id ?? null),\n images,\n})\n\nprovideProductEditor({ product, images, locked, video, values })\nprovideHistorySubject({ id: computed(() => product.value?.id ?? null) })\n\n/*\n * The address field is the panel's shared one (`wx-slug`). A product answers at `/{slug}-{id}`\n * (§4), so what is compared with the slug being typed is the registry's address without its\n * number — otherwise every product would look like one whose address is about to move.\n */\nprovideRecordAddress({\n values,\n prefix: ref(''),\n path: computed(() => {\n const segment = lastSegment(product.value?.url)\n\n return segment?.replace(new RegExp(`-${product.value?.id}$`), '') ?? null\n }),\n moving: () => t('panel.address-moving'),\n})\n\nfunction take(detail: ProductDetail): void {\n product.value = detail.product\n values.value = detail.values\n images.value = detail.images\n snapshot.value = JSON.stringify(detail.values)\n}\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n take(await api.product(id.value))\n } catch (error) {\n toast.danger(message(error))\n void router.push(list.value)\n } finally {\n loading.value = false\n }\n}\n\nasync function save(): Promise<void> {\n if (!product.value || saving.value || !canManage.value) return\n\n saving.value = true\n errors.value = {}\n holder.value = null\n\n try {\n // The gallery is not sent: it saves itself, and the answer carries it as the server has it.\n take(await api.saveProduct(product.value.id, values.value))\n toast.success(t('panel.saved'))\n } catch (error) {\n const body = (\n error as {\n body?: { errors?: Record<string, string[]>; meta?: { taken_by?: SkuHolder } }\n }\n ).body\n\n if (body?.errors && Object.keys(body.errors).length > 0) {\n errors.value = body.errors\n holder.value = body.meta?.taken_by ?? null\n toast.danger(t('panel.save-failed'))\n } else {\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n\n/**\n * Where the product holding the article number is opened: its editor, or «Deleted» searched by\n * that number — a deleted product has no editor, and the only way back to it is a restore.\n */\nconst holderLink = computed(() => {\n const found = holder.value\n\n if (!found) return null\n\n if (found.deleted) {\n const sku = typeof values.value.sku === 'string' ? values.value.sku : ''\n\n return { path: `${props.base}/deleted`, query: sku ? { q: sku } : {} }\n }\n\n return { path: `${props.base}/products/${found.id}` }\n})\n\nasync function remove(): Promise<void> {\n const row = product.value\n\n if (!row) return\n\n const agreed = await confirm({\n title: t('panel.delete-product-title', { name: row.name }),\n message: t('panel.delete-product-text'),\n confirmText: t('panel.delete'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removeProduct(row.id)\n toast.success(t('panel.product-deleted'))\n // Nothing left to protect: the product is in «Deleted», and asking about its unsaved edits on\n // the way out would be asking about a page that is gone.\n snapshot.value = current.value\n void router.push(list.value)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/* The browser's own guard: it cannot wait for a request, so all it can do is ask. */\nfunction leaveGuard(event: BeforeUnloadEvent): void {\n if (dirty.value) event.preventDefault()\n}\n\n/* Ctrl+S saves, as it does everywhere else that has a save button. */\nfunction onKeydown(event: KeyboardEvent): void {\n if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {\n event.preventDefault()\n if (dirty.value) void save()\n }\n}\n\nuseBodyKeys(onKeydown)\n\nwatch(id, () => void load())\n\nonMounted(() => {\n void load()\n window.addEventListener('beforeunload', leaveGuard)\n})\n\nonBeforeUnmount(() => {\n window.removeEventListener('beforeunload', leaveGuard)\n})\n\nonBeforeRouteLeave(async () => {\n // A video going up pauses when the page goes; choosing the same file again continues it.\n if (video.busy.value) {\n const leave = await confirm({\n title: t('panel.video-leave-title'),\n message: t('panel.video-leave-text'),\n confirmText: t('panel.leave'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!leave) return false\n }\n\n if (!dirty.value || !canManage.value) return true\n\n return await confirm({\n title: t('panel.leave-title'),\n message: t('panel.leave-text'),\n confirmText: t('panel.leave'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n})\n\n/*\n * What leads away from the product. \"Open on the site\" is there for an unpublished one too: it\n * answers with the trimmed page (§5), and that page is what a visitor with an old link sees.\n */\nconst actions = computed<ScreenAction[]>(() => {\n const row = product.value\n const leads: ScreenAction[] = []\n\n if (!row) return leads\n\n if (row.url) {\n leads.push({ key: 'site', label: t('panel.open-on-site'), icon: 'link', href: row.url })\n }\n\n if (canDelete.value) {\n leads.push({\n key: 'delete',\n label: t('panel.delete'),\n icon: 'trash',\n danger: true,\n menu: true,\n run: () => void remove(),\n })\n }\n\n return leads\n})\n</script>\n\n<template>\n <div class=\"wx-catalog-product\" @keydown=\"onKeydown\">\n <template v-if=\"loading || !product\">\n <!-- Shaped like the screen it stands in for: the head, and a card for what is coming. -->\n <wx-skeleton class=\"wx-catalog-product__ghost\" title :rows=\"1\" />\n <wx-card><wx-skeleton :rows=\"8\" /></wx-card>\n </template>\n\n <template v-else>\n <wx-screen-head\n divider\n :back=\"list\"\n :back-label=\"t('module.products')\"\n :title=\"title\"\n :subtitle=\"subtitle\"\n :actions=\"actions\"\n >\n <template #trail>\n <wx-breadcrumb size=\"sm\" :label=\"t('panel.product-trail')\">\n <wx-breadcrumb-item :as=\"'router-link'\" :to=\"list\">{{ section }}</wx-breadcrumb-item>\n <wx-breadcrumb-item v-if=\"product.category\" current>\n {{ product.category.name }}\n </wx-breadcrumb-item>\n <wx-breadcrumb-item v-else current>{{ t('product.no-category') }}</wx-breadcrumb-item>\n </wx-breadcrumb>\n </template>\n\n <template #title-after>\n <wx-badge\n :type=\"product.is_published ? (product.visible ? 'success' : 'warning') : 'default'\"\n dot\n >\n {{ t(`states.${product.state}`) }}\n </wx-badge>\n <wx-badge v-if=\"!product.category\" type=\"warning\" round>\n {{ t('product.no-category') }}\n </wx-badge>\n </template>\n </wx-screen-head>\n\n <!-- The article number is somebody else's: say whose, and take the reader there. Kept above\n the tabs, because the field it is about may be on a tab nobody is looking at. -->\n <wx-alert\n v-if=\"holder && holderLink\"\n type=\"warning\"\n :description=\"errors.sku?.[0] ?? ''\"\n closable\n @close=\"holder = null\"\n >\n <template #actions>\n <wx-button size=\"sm\" variant=\"outline\" @click=\"router.push(holderLink)\">\n {{ holder.deleted ? t('panel.sku-open-deleted') : t('panel.sku-open') }}\n </wx-button>\n </template>\n </wx-alert>\n\n <wx-screen v-model=\"values\" name=\"catalog.product-form\" :errors=\"errors\" :disabled=\"locked\" />\n\n <wx-action-bar v-if=\"canManage\">\n <template #state>\n <wx-save-state :state=\"state\" />\n </template>\n\n <wx-button type=\"primary\" :loading=\"saving\" :disabled=\"!dirty\" @click=\"save\">\n {{ t('panel.save') }}\n </wx-button>\n </wx-action-bar>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-catalog-product {\n display: flex;\n flex-direction: column;\n gap: var(--wx-gap, var(--wx-space-16));\n container-type: inline-size;\n}\n\n.wx-catalog-product__ghost {\n max-width: 420px;\n}\n</style>\n"],"names":["props","__props","context","useAdmin","api","createCatalogApi","route","useRoute","router","useRouter","locales","useLocales","useCatalogMessages","t","useTranslate","message","useErrorText","id","computed","list","product","ref","values","images","snapshot","loading","saving","errors","holder","canManage","canDelete","locked","current","dirty","state","section","module","title","written","localizedValue","subtitle","row","video","createGalleryVideo","provideProductEditor","provideHistorySubject","provideRecordAddress","lastSegment","take","detail","load","error","toast","save","body","holderLink","found","sku","remove","confirm","leaveGuard","event","onKeydown","useBodyKeys","watch","onMounted","onBeforeUnmount","onBeforeRouteLeave","actions","leads","_createElementBlock","_Fragment","_createVNode","_unref","WxSkeleton","WxCard","WxScreenHead","WxBreadcrumb","WxBreadcrumbItem","_createBlock","WxBadge","WxAlert","WxButton","_cache","$event","_createTextVNode","_toDisplayString","WxScreen","WxActionBar","WxSaveState"],"mappings":"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"}
|