@webx-ui/module-catalog-properties 0.1.13 → 0.2.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/PropertiesPage-B1jxbWZh.js +370 -0
- package/dist/PropertiesPage-B1jxbWZh.js.map +1 -0
- package/dist/PropertyEditorPage-C0o03q-A.js +198 -0
- package/dist/PropertyEditorPage-C0o03q-A.js.map +1 -0
- package/dist/index-B2Yl11Yn.js +1875 -0
- package/dist/index-B2Yl11Yn.js.map +1 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.js +22 -2415
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { defineComponent as L, ref as v, useTemplateRef as ie, computed as c, onMounted as ue, nextTick as pe, openBlock as y, createBlock as h, unref as e, isRef as de, withCtx as n, createVNode as s, createElementVNode as U, withKeys as ce, createTextVNode as S, toDisplayString as A, watch as ve, createElementBlock as O, Fragment as R, resolveDynamicComponent as me, renderList as fe, createCommentVNode as ye } from "vue";
|
|
2
|
+
import { useRouter as ge } from "vue-router";
|
|
3
|
+
import { useModal as be, WxDialog as we, WxFormItem as B, WxInput as xe, WxRadioGroup as _e, WxSpace as he, WxButton as D, createModal as ke, WxSelect as G, WxSkeleton as Ve, WxEmpty as We, WxSortableList as $e, WxText as z, WxTooltip as Ce, WxIcon as Pe, WxAlert as Se, toast as $, confirm as Ae } from "@webx-ui/core";
|
|
4
|
+
import { useAdmin as F, useTranslate as j, createCategoriesApi as Te, useErrorText as Me, WxListScreen as Ue, WxRowMenu as Ne } from "@webx-ui/module-admin";
|
|
5
|
+
import { c as q, N as K, G as ze, u as Ee, p as M, w as I } from "./index-B2Yl11Yn.js";
|
|
6
|
+
const Oe = /* @__PURE__ */ L({
|
|
7
|
+
name: "WxCatalogPropertyCreateDialog",
|
|
8
|
+
__name: "PropertyCreateDialog",
|
|
9
|
+
setup(E) {
|
|
10
|
+
const { open: C, resolve: b, dismiss: k } = be(), N = q(F()), p = j(K), t = v(""), g = v("select"), m = v(!1), d = v({}), w = ie("field"), x = c(
|
|
11
|
+
() => ["select", "number", "text", "bool"].map((i) => ({
|
|
12
|
+
value: i,
|
|
13
|
+
label: p(`property.types.${i}`)
|
|
14
|
+
}))
|
|
15
|
+
);
|
|
16
|
+
ue(async () => {
|
|
17
|
+
await pe(), w.value?.querySelector("input")?.focus();
|
|
18
|
+
});
|
|
19
|
+
function _(i) {
|
|
20
|
+
const r = Object.keys(d.value).find((u) => u === i || u.startsWith(`${i}.`));
|
|
21
|
+
return r === void 0 ? void 0 : d.value[r]?.[0];
|
|
22
|
+
}
|
|
23
|
+
async function V() {
|
|
24
|
+
if (!(t.value.trim() === "" || m.value)) {
|
|
25
|
+
m.value = !0, d.value = {};
|
|
26
|
+
try {
|
|
27
|
+
b(await N.create({ title: t.value.trim(), type: g.value }));
|
|
28
|
+
} catch (i) {
|
|
29
|
+
d.value = i.body?.errors ?? {};
|
|
30
|
+
} finally {
|
|
31
|
+
m.value = !1;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return (i, r) => (y(), h(e(we), {
|
|
36
|
+
open: e(C),
|
|
37
|
+
"onUpdate:open": r[3] || (r[3] = (u) => de(C) ? C.value = u : null),
|
|
38
|
+
title: e(p)("panel.new"),
|
|
39
|
+
width: 440
|
|
40
|
+
}, {
|
|
41
|
+
footer: n(() => [
|
|
42
|
+
s(e(he), { size: "sm" }, {
|
|
43
|
+
default: n(() => [
|
|
44
|
+
s(e(D), {
|
|
45
|
+
variant: "outline",
|
|
46
|
+
onClick: r[2] || (r[2] = (u) => e(k)())
|
|
47
|
+
}, {
|
|
48
|
+
default: n(() => [
|
|
49
|
+
S(A(e(p)("panel.cancel")), 1)
|
|
50
|
+
]),
|
|
51
|
+
_: 1
|
|
52
|
+
}),
|
|
53
|
+
s(e(D), {
|
|
54
|
+
type: "primary",
|
|
55
|
+
loading: m.value,
|
|
56
|
+
disabled: t.value.trim() === "",
|
|
57
|
+
onClick: V
|
|
58
|
+
}, {
|
|
59
|
+
default: n(() => [
|
|
60
|
+
S(A(e(p)("panel.create")), 1)
|
|
61
|
+
]),
|
|
62
|
+
_: 1
|
|
63
|
+
}, 8, ["loading", "disabled"])
|
|
64
|
+
]),
|
|
65
|
+
_: 1
|
|
66
|
+
})
|
|
67
|
+
]),
|
|
68
|
+
default: n(() => [
|
|
69
|
+
s(e(B), {
|
|
70
|
+
label: e(p)("property.title"),
|
|
71
|
+
error: _("title") ?? _("code"),
|
|
72
|
+
required: ""
|
|
73
|
+
}, {
|
|
74
|
+
default: n(() => [
|
|
75
|
+
U("div", {
|
|
76
|
+
ref_key: "field",
|
|
77
|
+
ref: w
|
|
78
|
+
}, [
|
|
79
|
+
s(e(xe), {
|
|
80
|
+
modelValue: t.value,
|
|
81
|
+
"onUpdate:modelValue": r[0] || (r[0] = (u) => t.value = u),
|
|
82
|
+
"aria-label": e(p)("property.title"),
|
|
83
|
+
onKeyup: ce(V, ["enter"])
|
|
84
|
+
}, null, 8, ["modelValue", "aria-label"])
|
|
85
|
+
], 512)
|
|
86
|
+
]),
|
|
87
|
+
_: 1
|
|
88
|
+
}, 8, ["label", "error"]),
|
|
89
|
+
s(e(B), {
|
|
90
|
+
label: e(p)("property.type"),
|
|
91
|
+
help: e(p)("property.type-help"),
|
|
92
|
+
error: _("type")
|
|
93
|
+
}, {
|
|
94
|
+
default: n(() => [
|
|
95
|
+
s(e(_e), {
|
|
96
|
+
modelValue: g.value,
|
|
97
|
+
"onUpdate:modelValue": r[1] || (r[1] = (u) => g.value = u),
|
|
98
|
+
options: x.value
|
|
99
|
+
}, null, 8, ["modelValue", "options"])
|
|
100
|
+
]),
|
|
101
|
+
_: 1
|
|
102
|
+
}, 8, ["label", "help", "error"])
|
|
103
|
+
]),
|
|
104
|
+
_: 1
|
|
105
|
+
}, 8, ["open", "title"]));
|
|
106
|
+
}
|
|
107
|
+
}), Re = { class: "wx-catalog-properties__filters" }, Be = { class: "wx-catalog-property-row__name" }, De = { class: "wx-catalog-property-row__flags" }, qe = /* @__PURE__ */ L({
|
|
108
|
+
name: "WxCatalogPropertiesPage",
|
|
109
|
+
__name: "PropertiesPage",
|
|
110
|
+
props: {
|
|
111
|
+
base: { default: "/catalog" }
|
|
112
|
+
},
|
|
113
|
+
setup(E) {
|
|
114
|
+
const C = E, b = F(), k = q(b), N = Te(b, ze), p = ge();
|
|
115
|
+
Ee();
|
|
116
|
+
const t = j(K), g = Me(), m = c(() => `${C.base}/properties`), d = c(() => b.i18n.state.locale), w = c(() => b.can("catalog.manage")), x = v([]), _ = v(!0), V = v("all"), i = v(null), r = v(null), u = v([]), f = c(() => V.value === "deleted"), T = c(() => i.value !== null || r.value !== null), H = c(
|
|
117
|
+
() => b.state.manifest?.modules.find((a) => a.id === "catalog-properties")?.title ?? t("module.title")
|
|
118
|
+
), J = c(() => [
|
|
119
|
+
{ value: "all", label: t("panel.view-all") },
|
|
120
|
+
{ value: "deleted", label: t("panel.view-deleted") }
|
|
121
|
+
]), Q = c(
|
|
122
|
+
() => ["select", "number", "text", "bool"].map((a) => ({
|
|
123
|
+
value: a,
|
|
124
|
+
label: t(`property.types.${a}`)
|
|
125
|
+
}))
|
|
126
|
+
), X = ke(Oe), Y = c(() => {
|
|
127
|
+
const a = [
|
|
128
|
+
{
|
|
129
|
+
key: "groups",
|
|
130
|
+
label: t("panel.groups"),
|
|
131
|
+
icon: "folder",
|
|
132
|
+
run: () => {
|
|
133
|
+
p.push(`${m.value}/groups`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
];
|
|
137
|
+
return w.value && a.push({ key: "new", label: t("panel.new"), icon: "plus", primary: !0, run: le }), a;
|
|
138
|
+
}), Z = [
|
|
139
|
+
{ flag: "is_filterable", icon: "filter" },
|
|
140
|
+
{ flag: "is_searchable", icon: "search" },
|
|
141
|
+
{ flag: "in_card", icon: "grid" },
|
|
142
|
+
{ flag: "on_page", icon: "file-text" },
|
|
143
|
+
{ flag: "in_list", icon: "list" }
|
|
144
|
+
];
|
|
145
|
+
function ee(a) {
|
|
146
|
+
return Z.filter(({ flag: o }) => a[o] === !0).map(({ flag: o, icon: l }) => ({
|
|
147
|
+
icon: l,
|
|
148
|
+
label: t(`property.${String(o)}`)
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
async function P() {
|
|
152
|
+
_.value = !0;
|
|
153
|
+
try {
|
|
154
|
+
const a = await k.list({
|
|
155
|
+
trashed: f.value,
|
|
156
|
+
type: i.value ?? void 0,
|
|
157
|
+
group: r.value
|
|
158
|
+
});
|
|
159
|
+
x.value = a.data;
|
|
160
|
+
} catch (a) {
|
|
161
|
+
$.danger(g(a));
|
|
162
|
+
} finally {
|
|
163
|
+
_.value = !1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function ae() {
|
|
167
|
+
try {
|
|
168
|
+
u.value = (await N.list()).data.map((a) => ({ value: a.id, label: a.name }));
|
|
169
|
+
} catch {
|
|
170
|
+
u.value = [];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
ve([f, i, r], () => {
|
|
174
|
+
P();
|
|
175
|
+
}), P(), ae();
|
|
176
|
+
function te(a) {
|
|
177
|
+
p.push(`${m.value}/${a.id}`);
|
|
178
|
+
}
|
|
179
|
+
async function le() {
|
|
180
|
+
const a = await X({});
|
|
181
|
+
a !== void 0 && p.push(`${m.value}/${a.property.id}`);
|
|
182
|
+
}
|
|
183
|
+
async function oe(a) {
|
|
184
|
+
const o = M(a, d.value);
|
|
185
|
+
if (await Ae({
|
|
186
|
+
title: t("panel.delete-title", { name: o }),
|
|
187
|
+
message: t("panel.delete-text"),
|
|
188
|
+
confirmText: t("panel.delete"),
|
|
189
|
+
cancelText: t("panel.cancel"),
|
|
190
|
+
tone: "danger"
|
|
191
|
+
}))
|
|
192
|
+
try {
|
|
193
|
+
await k.remove(a.id), $.success(t("panel.deleted")), await P();
|
|
194
|
+
} catch (W) {
|
|
195
|
+
$.danger(g(W));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async function re(a) {
|
|
199
|
+
try {
|
|
200
|
+
await k.restore(a.id), $.success(t("panel.restored")), await P();
|
|
201
|
+
} catch (o) {
|
|
202
|
+
$.danger(g(o));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function ne(a) {
|
|
206
|
+
if (f.value)
|
|
207
|
+
return w.value ? [
|
|
208
|
+
{
|
|
209
|
+
key: "restore",
|
|
210
|
+
label: t("panel.restore"),
|
|
211
|
+
icon: "refresh",
|
|
212
|
+
run: () => {
|
|
213
|
+
re(a);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
] : [];
|
|
217
|
+
const o = [
|
|
218
|
+
{ key: "edit", label: t("panel.edit"), icon: "edit", run: () => te(a) }
|
|
219
|
+
];
|
|
220
|
+
return w.value && o.push({
|
|
221
|
+
key: "delete",
|
|
222
|
+
label: t("panel.delete"),
|
|
223
|
+
icon: "trash",
|
|
224
|
+
danger: !0,
|
|
225
|
+
run: () => {
|
|
226
|
+
oe(a);
|
|
227
|
+
}
|
|
228
|
+
}), o;
|
|
229
|
+
}
|
|
230
|
+
async function se() {
|
|
231
|
+
try {
|
|
232
|
+
await k.reorder(x.value.map((a) => a.id));
|
|
233
|
+
} catch (a) {
|
|
234
|
+
$.danger(g(a, t("panel.reorder-failed"))), await P();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return (a, o) => (y(), h(e(Ue), {
|
|
238
|
+
view: V.value,
|
|
239
|
+
"onUpdate:view": o[3] || (o[3] = (l) => V.value = l),
|
|
240
|
+
class: "wx-catalog-properties",
|
|
241
|
+
title: H.value,
|
|
242
|
+
views: J.value,
|
|
243
|
+
actions: Y.value,
|
|
244
|
+
padding: "sm"
|
|
245
|
+
}, {
|
|
246
|
+
default: n(() => [
|
|
247
|
+
U("div", Re, [
|
|
248
|
+
s(e(G), {
|
|
249
|
+
modelValue: i.value,
|
|
250
|
+
"onUpdate:modelValue": o[0] || (o[0] = (l) => i.value = l),
|
|
251
|
+
options: Q.value,
|
|
252
|
+
placeholder: e(t)("panel.type-any"),
|
|
253
|
+
"aria-label": e(t)("property.type"),
|
|
254
|
+
clearable: "",
|
|
255
|
+
size: "sm"
|
|
256
|
+
}, null, 8, ["modelValue", "options", "placeholder", "aria-label"]),
|
|
257
|
+
s(e(G), {
|
|
258
|
+
modelValue: r.value,
|
|
259
|
+
"onUpdate:modelValue": o[1] || (o[1] = (l) => r.value = l),
|
|
260
|
+
options: u.value,
|
|
261
|
+
placeholder: e(t)("panel.group-any"),
|
|
262
|
+
"aria-label": e(t)("property.group_id"),
|
|
263
|
+
clearable: "",
|
|
264
|
+
filterable: "",
|
|
265
|
+
size: "sm"
|
|
266
|
+
}, null, 8, ["modelValue", "options", "placeholder", "aria-label"])
|
|
267
|
+
]),
|
|
268
|
+
_.value ? (y(), h(e(Ve), {
|
|
269
|
+
key: 0,
|
|
270
|
+
class: "wx-catalog-properties__loading",
|
|
271
|
+
rows: 5
|
|
272
|
+
})) : x.value.length === 0 ? (y(), h(e(We), {
|
|
273
|
+
key: 1,
|
|
274
|
+
title: T.value || f.value ? e(t)("panel.empty-found") : e(t)("panel.empty"),
|
|
275
|
+
description: T.value || f.value ? void 0 : e(t)("panel.empty-help")
|
|
276
|
+
}, null, 8, ["title", "description"])) : (y(), O(R, { key: 2 }, [
|
|
277
|
+
s(e($e), {
|
|
278
|
+
modelValue: x.value,
|
|
279
|
+
"onUpdate:modelValue": o[2] || (o[2] = (l) => x.value = l),
|
|
280
|
+
class: "wx-catalog-properties__list",
|
|
281
|
+
plain: "",
|
|
282
|
+
"item-key": "id",
|
|
283
|
+
"item-label": (l) => e(M)(l, d.value),
|
|
284
|
+
disabled: !w.value || T.value || f.value,
|
|
285
|
+
onMove: se
|
|
286
|
+
}, {
|
|
287
|
+
default: n(({ item: l }) => [
|
|
288
|
+
(y(), h(me(f.value ? "div" : "router-link"), {
|
|
289
|
+
class: "wx-catalog-property-row",
|
|
290
|
+
to: f.value ? void 0 : `${m.value}/${l.id}`
|
|
291
|
+
}, {
|
|
292
|
+
default: n(() => [
|
|
293
|
+
U("span", Be, [
|
|
294
|
+
s(e(z), {
|
|
295
|
+
truncate: "",
|
|
296
|
+
weight: "medium"
|
|
297
|
+
}, {
|
|
298
|
+
default: n(() => [
|
|
299
|
+
S(A(e(M)(l, d.value)), 1)
|
|
300
|
+
]),
|
|
301
|
+
_: 2
|
|
302
|
+
}, 1024),
|
|
303
|
+
U("span", De, [
|
|
304
|
+
(y(!0), O(R, null, fe(ee(l), (W) => (y(), h(e(Ce), {
|
|
305
|
+
key: W.icon,
|
|
306
|
+
content: W.label
|
|
307
|
+
}, {
|
|
308
|
+
default: n(() => [
|
|
309
|
+
s(e(Pe), {
|
|
310
|
+
name: W.icon,
|
|
311
|
+
size: "sm",
|
|
312
|
+
label: W.label
|
|
313
|
+
}, null, 8, ["name", "label"])
|
|
314
|
+
]),
|
|
315
|
+
_: 2
|
|
316
|
+
}, 1032, ["content"]))), 128))
|
|
317
|
+
])
|
|
318
|
+
]),
|
|
319
|
+
s(e(z), {
|
|
320
|
+
size: "sm",
|
|
321
|
+
tone: "muted",
|
|
322
|
+
truncate: ""
|
|
323
|
+
}, {
|
|
324
|
+
default: n(() => [
|
|
325
|
+
S(A([
|
|
326
|
+
e(I)(l.code, d.value),
|
|
327
|
+
e(t)(`property.types.${l.type}`),
|
|
328
|
+
l.group ? e(I)(l.group.title, d.value) : null
|
|
329
|
+
].filter(Boolean).join(" · ")), 1)
|
|
330
|
+
]),
|
|
331
|
+
_: 2
|
|
332
|
+
}, 1024)
|
|
333
|
+
]),
|
|
334
|
+
_: 2
|
|
335
|
+
}, 1032, ["to"]))
|
|
336
|
+
]),
|
|
337
|
+
actions: n(({ item: l }) => [
|
|
338
|
+
s(e(z), {
|
|
339
|
+
class: "wx-catalog-property-row__count",
|
|
340
|
+
size: "sm",
|
|
341
|
+
tone: "muted"
|
|
342
|
+
}, {
|
|
343
|
+
default: n(() => [
|
|
344
|
+
S(A(e(t)("panel.count", { count: l.products_count })), 1)
|
|
345
|
+
]),
|
|
346
|
+
_: 2
|
|
347
|
+
}, 1024),
|
|
348
|
+
s(e(Ne), {
|
|
349
|
+
actions: ne(l),
|
|
350
|
+
label: e(M)(l, d.value)
|
|
351
|
+
}, null, 8, ["actions", "label"])
|
|
352
|
+
]),
|
|
353
|
+
_: 1
|
|
354
|
+
}, 8, ["modelValue", "item-label", "disabled"]),
|
|
355
|
+
f.value ? ye("", !0) : (y(), h(e(Se), {
|
|
356
|
+
key: 0,
|
|
357
|
+
class: "wx-catalog-properties__note",
|
|
358
|
+
type: "info",
|
|
359
|
+
description: T.value ? e(t)("panel.order-narrowed") : e(t)("panel.order")
|
|
360
|
+
}, null, 8, ["description"]))
|
|
361
|
+
], 64))
|
|
362
|
+
]),
|
|
363
|
+
_: 1
|
|
364
|
+
}, 8, ["view", "title", "views", "actions"]));
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
export {
|
|
368
|
+
qe as default
|
|
369
|
+
};
|
|
370
|
+
//# sourceMappingURL=PropertiesPage-B1jxbWZh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PropertiesPage-B1jxbWZh.js","sources":["../src/PropertyCreateDialog.vue","../src/PropertiesPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue'\nimport {\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxRadioGroup,\n WxSpace,\n} from '@webx-ui/core'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport { createPropertiesApi } from './api'\nimport { NAMESPACE } from './i18n'\nimport type { PropertyDetail, PropertyType } from './types'\n\n/**\n * A new property: its name and its type, and nothing else.\n *\n * The type is asked here because it is asked once (decision 1): the values of the products take\n * its shape, and the page that opens next shows only the fields the type has. The code is made of\n * the name on the server, per language.\n */\ndefineOptions({ name: 'WxCatalogPropertyCreateDialog' })\n\nconst { open, resolve, dismiss } = useModal<PropertyDetail>()\n\nconst api = createPropertiesApi(useAdmin())\nconst t = useTranslate(NAMESPACE)\n\nconst title = ref('')\nconst type = ref<PropertyType>('select')\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\nconst field = useTemplateRef<HTMLElement>('field')\n\nconst types = computed(() =>\n (['select', 'number', 'text', 'bool'] as const).map((value) => ({\n value,\n label: t(`property.types.${value}`),\n })),\n)\n\nonMounted(async () => {\n await nextTick()\n field.value?.querySelector('input')?.focus()\n})\n\nfunction errorOf(name: string): string | undefined {\n const key = Object.keys(errors.value).find((one) => one === name || one.startsWith(`${name}.`))\n\n return key === undefined ? undefined : errors.value[key]?.[0]\n}\n\nasync function submit(): Promise<void> {\n if (title.value.trim() === '' || saving.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n resolve(await api.create({ title: title.value.trim(), type: type.value }))\n } catch (error) {\n // The code is what can be refused here — taken by another property or a facet of the\n // catalogue — and it is made of the name, so the refusal goes under the name.\n errors.value = (error as { body?: { errors?: Record<string, string[]> } }).body?.errors ?? {}\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('panel.new')\" :width=\"440\">\n <wx-form-item\n :label=\"t('property.title')\"\n :error=\"errorOf('title') ?? errorOf('code')\"\n required\n >\n <div ref=\"field\">\n <wx-input v-model=\"title\" :aria-label=\"t('property.title')\" @keyup.enter=\"submit\" />\n </div>\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('property.type')\"\n :help=\"t('property.type-help')\"\n :error=\"errorOf('type')\"\n >\n <wx-radio-group v-model=\"type\" :options=\"types\" />\n </wx-form-item>\n\n <template #footer>\n <wx-space size=\"sm\">\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('panel.cancel') }}</wx-button>\n <wx-button type=\"primary\" :loading=\"saving\" :disabled=\"title.trim() === ''\" @click=\"submit\">\n {{ t('panel.create') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { useRouter } from 'vue-router'\nimport {\n confirm,\n createModal,\n toast,\n WxAlert,\n WxEmpty,\n WxIcon,\n WxSelect,\n WxSkeleton,\n WxSortableList,\n WxText,\n WxTooltip,\n type IconName,\n type SelectOption,\n type TabItem,\n type TabValue,\n} from '@webx-ui/core'\nimport {\n createCategoriesApi,\n useAdmin,\n useErrorText,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport { createPropertiesApi, GROUPS_API } from './api'\nimport { propertyName, wordsIn } from './format'\nimport { NAMESPACE, useCatalogPropertiesMessages } from './i18n'\nimport PropertyCreateDialog from './PropertyCreateDialog.vue'\nimport type { PropertyDetail, PropertyRow, PropertyType } from './types'\n\n/**\n * The properties of the catalogue (§7.1 of the properties spec): every one on a line — name,\n * code, type, group, what it is used for as icons, how many products hold it — in the order they\n * are dragged into, which is the order of the filter's blocks where a category says nothing else.\n *\n * All of them at once rather than a page: a shop has tens of properties, a large one a couple of\n * hundred, and an order is dragged on the whole list or not at all. So the order is dragged only\n * while nothing narrows the list; a type or a group chosen, it is a list to find one in.\n *\n * The groups of the card are the button in the head — the panel's shared category screens.\n */\ndefineOptions({ name: 'WxCatalogPropertiesPage' })\n\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\nconst admin = useAdmin()\nconst api = createPropertiesApi(admin)\nconst groupsApi = createCategoriesApi(admin, GROUPS_API)\nconst router = useRouter()\nuseCatalogPropertiesMessages()\n\nconst t = useTranslate(NAMESPACE)\nconst message = useErrorText()\n\nconst path = computed(() => `${props.base}/properties`)\nconst locale = computed(() => admin.i18n.state.locale)\nconst canManage = computed(() => admin.can('catalog.manage'))\n\nconst rows = ref<PropertyRow[]>([])\nconst loading = ref(true)\nconst view = ref<TabValue | undefined>('all')\nconst type = ref<PropertyType | null>(null)\nconst group = ref<number | null>(null)\nconst groups = ref<SelectOption[]>([])\n\nconst trashed = computed(() => view.value === 'deleted')\nconst narrowed = computed(() => type.value !== null || group.value !== null)\n\nconst title = computed(\n () =>\n admin.state.manifest?.modules.find((module) => module.id === 'catalog-properties')?.title ??\n t('module.title'),\n)\n\nconst views = computed<TabItem[]>(() => [\n { value: 'all', label: t('panel.view-all') },\n { value: 'deleted', label: t('panel.view-deleted') },\n])\n\nconst typeOptions = computed<SelectOption[]>(() =>\n (['select', 'number', 'text', 'bool'] as const).map((value) => ({\n value,\n label: t(`property.types.${value}`),\n })),\n)\n\nconst create = createModal<PropertyDetail>(PropertyCreateDialog)\n\nconst actions = computed<ScreenAction[]>(() => {\n const list: ScreenAction[] = [\n {\n key: 'groups',\n label: t('panel.groups'),\n icon: 'folder',\n run: () => void router.push(`${path.value}/groups`),\n },\n ]\n\n if (canManage.value) {\n list.push({ key: 'new', label: t('panel.new'), icon: 'plus', primary: true, run: add })\n }\n\n return list\n})\n\n/** What a property is used for, as the icons of its line — the ones it has, each with its word. */\nconst FLAGS: { flag: keyof PropertyRow; icon: IconName }[] = [\n { flag: 'is_filterable', icon: 'filter' },\n { flag: 'is_searchable', icon: 'search' },\n { flag: 'in_card', icon: 'grid' },\n { flag: 'on_page', icon: 'file-text' },\n { flag: 'in_list', icon: 'list' },\n]\n\nfunction flagsOf(row: PropertyRow) {\n return FLAGS.filter(({ flag }) => row[flag] === true).map(({ flag, icon }) => ({\n icon,\n label: t(`property.${String(flag)}`),\n }))\n}\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n const page = await api.list({\n trashed: trashed.value,\n type: type.value ?? undefined,\n group: group.value,\n })\n rows.value = page.data\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nasync function loadGroups(): Promise<void> {\n try {\n groups.value = (await groupsApi.list()).data.map((row) => ({ value: row.id, label: row.name }))\n } catch {\n // The filter by group is a convenience; without its list the rest of the screen still works.\n groups.value = []\n }\n}\n\nwatch([trashed, type, group], () => void load())\nvoid load()\nvoid loadGroups()\n\nfunction open(row: PropertyRow): void {\n void router.push(`${path.value}/${row.id}`)\n}\n\n/** A name and a type in a dialog, and then the page: the type is chosen once (decision 1). */\nasync function add(): Promise<void> {\n const made = await create({})\n\n if (made !== undefined) void router.push(`${path.value}/${made.property.id}`)\n}\n\nasync function remove(row: PropertyRow): Promise<void> {\n const name = propertyName(row, locale.value)\n const agreed = await confirm({\n title: t('panel.delete-title', { name }),\n message: t('panel.delete-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.remove(row.id)\n toast.success(t('panel.deleted'))\n await load()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nasync function restore(row: PropertyRow): Promise<void> {\n try {\n await api.restore(row.id)\n toast.success(t('panel.restored'))\n await load()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction menuOf(row: PropertyRow): RowAction[] {\n if (trashed.value) {\n return canManage.value\n ? [\n {\n key: 'restore',\n label: t('panel.restore'),\n icon: 'refresh',\n run: () => void restore(row),\n },\n ]\n : []\n }\n\n const items: RowAction[] = [\n { key: 'edit', label: t('panel.edit'), icon: 'edit', run: () => open(row) },\n ]\n\n if (canManage.value) {\n items.push({\n key: 'delete',\n label: t('panel.delete'),\n icon: 'trash',\n danger: true,\n run: () => void remove(row),\n })\n }\n\n return items\n}\n\n/** The whole order, every time; a failure puts the list back as the server has it. */\nasync function reorder(): Promise<void> {\n try {\n await api.reorder(rows.value.map((row) => row.id))\n } catch (error) {\n toast.danger(message(error, t('panel.reorder-failed')))\n await load()\n }\n}\n</script>\n\n<template>\n <wx-list-screen\n v-model:view=\"view\"\n class=\"wx-catalog-properties\"\n :title=\"title\"\n :views=\"views\"\n :actions=\"actions\"\n padding=\"sm\"\n >\n <div class=\"wx-catalog-properties__filters\">\n <wx-select\n v-model=\"type\"\n :options=\"typeOptions\"\n :placeholder=\"t('panel.type-any')\"\n :aria-label=\"t('property.type')\"\n clearable\n size=\"sm\"\n />\n <wx-select\n v-model=\"group\"\n :options=\"groups\"\n :placeholder=\"t('panel.group-any')\"\n :aria-label=\"t('property.group_id')\"\n clearable\n filterable\n size=\"sm\"\n />\n </div>\n\n <wx-skeleton v-if=\"loading\" class=\"wx-catalog-properties__loading\" :rows=\"5\" />\n\n <wx-empty\n v-else-if=\"rows.length === 0\"\n :title=\"narrowed || trashed ? t('panel.empty-found') : t('panel.empty')\"\n :description=\"narrowed || trashed ? undefined : t('panel.empty-help')\"\n />\n\n <template v-else>\n <wx-sortable-list\n v-model=\"rows\"\n class=\"wx-catalog-properties__list\"\n plain\n item-key=\"id\"\n :item-label=\"(item: PropertyRow) => propertyName(item, locale)\"\n :disabled=\"!canManage || narrowed || trashed\"\n @move=\"reorder\"\n >\n <template #default=\"{ item }\">\n <component\n :is=\"trashed ? 'div' : 'router-link'\"\n class=\"wx-catalog-property-row\"\n :to=\"trashed ? undefined : `${path}/${(item as PropertyRow).id}`\"\n >\n <span class=\"wx-catalog-property-row__name\">\n <wx-text truncate weight=\"medium\">{{\n propertyName(item as PropertyRow, locale)\n }}</wx-text>\n <span class=\"wx-catalog-property-row__flags\">\n <wx-tooltip\n v-for=\"flag in flagsOf(item as PropertyRow)\"\n :key=\"flag.icon\"\n :content=\"flag.label\"\n >\n <wx-icon :name=\"flag.icon\" size=\"sm\" :label=\"flag.label\" />\n </wx-tooltip>\n </span>\n </span>\n <wx-text size=\"sm\" tone=\"muted\" truncate>\n {{\n [\n wordsIn((item as PropertyRow).code, locale),\n t(`property.types.${(item as PropertyRow).type}`),\n (item as PropertyRow).group\n ? wordsIn((item as PropertyRow).group!.title, locale)\n : null,\n ]\n .filter(Boolean)\n .join(' · ')\n }}\n </wx-text>\n </component>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-text class=\"wx-catalog-property-row__count\" size=\"sm\" tone=\"muted\">\n {{ t('panel.count', { count: (item as PropertyRow).products_count }) }}\n </wx-text>\n <wx-row-menu\n :actions=\"menuOf(item as PropertyRow)\"\n :label=\"propertyName(item as PropertyRow, locale)\"\n />\n </template>\n </wx-sortable-list>\n\n <wx-alert\n v-if=\"!trashed\"\n class=\"wx-catalog-properties__note\"\n type=\"info\"\n :description=\"narrowed ? t('panel.order-narrowed') : t('panel.order')\"\n />\n </template>\n </wx-list-screen>\n</template>\n\n<style>\n.wx-catalog-properties__filters {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-8);\n padding: var(--wx-space-8);\n}\n\n.wx-catalog-properties__filters > * {\n flex: 0 1 220px;\n min-width: 0;\n}\n\n.wx-catalog-properties__loading {\n padding: var(--wx-space-8);\n}\n\n.wx-catalog-properties__list.is-plain .wx-sortable-list__row {\n padding-inline: var(--wx-space-8);\n}\n\n.wx-catalog-properties__list .wx-sortable-list__row:hover {\n background: var(--wx-bg-subtle);\n border-radius: var(--wx-radius-sm);\n}\n\n.wx-catalog-properties__note {\n margin-block-start: var(--wx-space-8);\n}\n\n.wx-catalog-property-row {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-2);\n align-items: flex-start;\n flex: 1 1 auto;\n min-width: 0;\n color: inherit;\n text-decoration: none;\n}\n\n.wx-catalog-property-row__name {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n min-width: 0;\n max-width: 100%;\n}\n\n.wx-catalog-property-row__flags {\n display: inline-flex;\n flex: none;\n gap: var(--wx-space-4);\n color: var(--wx-text-muted);\n}\n\n.wx-catalog-property-row__count {\n flex: none;\n font-variant-numeric: tabular-nums;\n}\n</style>\n"],"names":["open","resolve","dismiss","useModal","api","createPropertiesApi","useAdmin","t","useTranslate","NAMESPACE","title","ref","type","saving","errors","field","useTemplateRef","types","computed","value","onMounted","nextTick","errorOf","name","key","one","submit","error","_createBlock","_unref","WxDialog","$event","_createVNode","WxSpace","WxButton","WxFormItem","_createElementVNode","WxInput","WxRadioGroup","props","__props","admin","groupsApi","createCategoriesApi","GROUPS_API","router","useRouter","useCatalogPropertiesMessages","message","useErrorText","path","locale","canManage","rows","loading","view","group","groups","trashed","narrowed","module","views","typeOptions","create","createModal","PropertyCreateDialog","actions","list","add","FLAGS","flagsOf","row","flag","icon","load","page","toast","loadGroups","watch","made","remove","propertyName","confirm","restore","menuOf","items","reorder","WxListScreen","_hoisted_1","WxSelect","WxSkeleton","WxEmpty","_createElementBlock","_Fragment","WxSortableList","item","_withCtx","_openBlock","_resolveDynamicComponent","_hoisted_2","WxText","_hoisted_3","_renderList","WxTooltip","WxIcon","wordsIn","WxRowMenu","WxAlert"],"mappings":"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"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { defineComponent as Q, computed as r, ref as i, watch as X, onMounted as Y, onBeforeUnmount as Z, openBlock as g, createElementBlock as h, Fragment as I, createVNode as o, unref as t, withCtx as n, createSlots as ee, createTextVNode as b, toDisplayString as x, createBlock as ae, createCommentVNode as te } from "vue";
|
|
2
|
+
import { useRoute as le, useRouter as re, onBeforeRouteLeave as oe } from "vue-router";
|
|
3
|
+
import { useLocales as ne, localizedValue as se, confirm as O, WxSkeleton as R, WxCard as ue, WxBreadcrumb as ie, WxBreadcrumbItem as z, WxBadge as ce, WxActionBar as de, WxButton as ve, toast as d } from "@webx-ui/core";
|
|
4
|
+
import { useAdmin as pe, useTranslate as fe, useErrorText as me, provideHistorySubject as ye, useBodyKeys as ge, WxScreenHead as be, WxScreen as xe, WxSaveState as we } from "@webx-ui/module-admin";
|
|
5
|
+
import { c as _e, u as ke, N as Be, p as Se, w as We, a as he, _ as Ee } from "./index-B2Yl11Yn.js";
|
|
6
|
+
const Ce = /* @__PURE__ */ Q({
|
|
7
|
+
name: "WxCatalogPropertyEditor",
|
|
8
|
+
__name: "PropertyEditorPage",
|
|
9
|
+
props: {
|
|
10
|
+
base: { default: "/catalog" }
|
|
11
|
+
},
|
|
12
|
+
setup(H) {
|
|
13
|
+
const J = H, f = pe(), w = _e(f), U = le(), E = re(), C = ne();
|
|
14
|
+
ke();
|
|
15
|
+
const a = fe(Be), _ = me(), N = r(() => Number(U.params.id)), m = r(() => `${J.base}/properties`), l = i(null), u = i({}), P = i([]), y = i(""), k = i(!0), c = i(!1), B = i({}), v = r(() => f.can("catalog.manage")), T = r(() => !v.value || c.value), V = r(() => JSON.stringify(u.value)), p = r(() => y.value !== "" && V.value !== y.value), $ = r(() => c.value ? "saving" : p.value ? "unsaved" : "saved"), A = r(
|
|
16
|
+
() => f.state.manifest?.modules.find((e) => e.id === "catalog-properties")?.title ?? a("module.title")
|
|
17
|
+
), S = r(() => {
|
|
18
|
+
const e = u.value.title, s = l.value ? Se(l.value, f.i18n.state.locale) : "";
|
|
19
|
+
return se(e, C.active.value, s) || a("panel.untitled");
|
|
20
|
+
}), F = r(() => {
|
|
21
|
+
const e = l.value;
|
|
22
|
+
if (e)
|
|
23
|
+
return [We(e.code, C.active.value), a(`property.types.${e.type}`)].filter(Boolean).join(" · ");
|
|
24
|
+
});
|
|
25
|
+
he({ property: l, values: u, intervals: P, locked: T }), ye({ id: r(() => l.value?.id ?? null) });
|
|
26
|
+
function L(e) {
|
|
27
|
+
l.value = e.property, u.value = e.values, P.value = e.intervals, y.value = JSON.stringify(e.values);
|
|
28
|
+
}
|
|
29
|
+
async function K() {
|
|
30
|
+
k.value = !0;
|
|
31
|
+
try {
|
|
32
|
+
L(await w.get(N.value));
|
|
33
|
+
} catch (e) {
|
|
34
|
+
d.danger(_(e)), E.push(m.value);
|
|
35
|
+
} finally {
|
|
36
|
+
k.value = !1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function M() {
|
|
40
|
+
if (!(!l.value || c.value || !v.value)) {
|
|
41
|
+
c.value = !0, B.value = {};
|
|
42
|
+
try {
|
|
43
|
+
L(await w.save(l.value.id, u.value)), d.success(a("panel.saved"));
|
|
44
|
+
} catch (e) {
|
|
45
|
+
const s = e.body;
|
|
46
|
+
s?.errors && Object.keys(s.errors).length > 0 ? (B.value = s.errors, d.danger(a("panel.save-failed"))) : d.danger(_(e));
|
|
47
|
+
} finally {
|
|
48
|
+
c.value = !1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function G() {
|
|
53
|
+
const e = l.value;
|
|
54
|
+
if (!(!e || !await O({
|
|
55
|
+
title: a("panel.delete-title", { name: S.value }),
|
|
56
|
+
message: a("panel.delete-text"),
|
|
57
|
+
confirmText: a("panel.delete"),
|
|
58
|
+
cancelText: a("panel.cancel"),
|
|
59
|
+
tone: "danger"
|
|
60
|
+
})))
|
|
61
|
+
try {
|
|
62
|
+
await w.remove(e.id), d.success(a("panel.deleted")), y.value = V.value, E.push(m.value);
|
|
63
|
+
} catch (W) {
|
|
64
|
+
d.danger(_(W));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function j(e) {
|
|
68
|
+
p.value && e.preventDefault();
|
|
69
|
+
}
|
|
70
|
+
function D(e) {
|
|
71
|
+
(e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "s" && (e.preventDefault(), p.value && M());
|
|
72
|
+
}
|
|
73
|
+
ge(D), X(N, () => {
|
|
74
|
+
K();
|
|
75
|
+
}), Y(() => {
|
|
76
|
+
K(), window.addEventListener("beforeunload", j);
|
|
77
|
+
}), Z(() => {
|
|
78
|
+
window.removeEventListener("beforeunload", j);
|
|
79
|
+
}), oe(async () => !p.value || !v.value ? !0 : await O({
|
|
80
|
+
title: a("panel.leave-title"),
|
|
81
|
+
message: a("panel.leave-text"),
|
|
82
|
+
confirmText: a("panel.leave"),
|
|
83
|
+
cancelText: a("panel.cancel"),
|
|
84
|
+
tone: "danger"
|
|
85
|
+
}));
|
|
86
|
+
const q = r(
|
|
87
|
+
() => l.value && v.value && l.value.deleted_at === null ? [
|
|
88
|
+
{
|
|
89
|
+
key: "delete",
|
|
90
|
+
label: a("panel.delete"),
|
|
91
|
+
icon: "trash",
|
|
92
|
+
danger: !0,
|
|
93
|
+
menu: !0,
|
|
94
|
+
run: () => {
|
|
95
|
+
G();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
] : []
|
|
99
|
+
);
|
|
100
|
+
return (e, s) => (g(), h("div", {
|
|
101
|
+
class: "wx-catalog-property-editor",
|
|
102
|
+
onKeydown: D
|
|
103
|
+
}, [
|
|
104
|
+
k.value || !l.value ? (g(), h(I, { key: 0 }, [
|
|
105
|
+
o(t(R), {
|
|
106
|
+
class: "wx-catalog-property-editor__ghost",
|
|
107
|
+
title: "",
|
|
108
|
+
rows: 1
|
|
109
|
+
}),
|
|
110
|
+
o(t(ue), null, {
|
|
111
|
+
default: n(() => [
|
|
112
|
+
o(t(R), { rows: 6 })
|
|
113
|
+
]),
|
|
114
|
+
_: 1
|
|
115
|
+
})
|
|
116
|
+
], 64)) : (g(), h(I, { key: 1 }, [
|
|
117
|
+
o(t(be), {
|
|
118
|
+
divider: "",
|
|
119
|
+
back: m.value,
|
|
120
|
+
"back-label": A.value,
|
|
121
|
+
title: S.value,
|
|
122
|
+
subtitle: F.value,
|
|
123
|
+
actions: q.value
|
|
124
|
+
}, ee({
|
|
125
|
+
trail: n(() => [
|
|
126
|
+
o(t(ie), {
|
|
127
|
+
size: "sm",
|
|
128
|
+
label: t(a)("panel.trail")
|
|
129
|
+
}, {
|
|
130
|
+
default: n(() => [
|
|
131
|
+
o(t(z), {
|
|
132
|
+
as: "router-link",
|
|
133
|
+
to: m.value
|
|
134
|
+
}, {
|
|
135
|
+
default: n(() => [
|
|
136
|
+
b(x(A.value), 1)
|
|
137
|
+
]),
|
|
138
|
+
_: 1
|
|
139
|
+
}, 8, ["to"]),
|
|
140
|
+
o(t(z), { current: "" }, {
|
|
141
|
+
default: n(() => [
|
|
142
|
+
b(x(S.value), 1)
|
|
143
|
+
]),
|
|
144
|
+
_: 1
|
|
145
|
+
})
|
|
146
|
+
]),
|
|
147
|
+
_: 1
|
|
148
|
+
}, 8, ["label"])
|
|
149
|
+
]),
|
|
150
|
+
_: 2
|
|
151
|
+
}, [
|
|
152
|
+
l.value.deleted_at !== null ? {
|
|
153
|
+
name: "title-after",
|
|
154
|
+
fn: n(() => [
|
|
155
|
+
o(t(ce), { dot: "" }, {
|
|
156
|
+
default: n(() => [
|
|
157
|
+
b(x(t(a)("panel.in-bin")), 1)
|
|
158
|
+
]),
|
|
159
|
+
_: 1
|
|
160
|
+
})
|
|
161
|
+
]),
|
|
162
|
+
key: "0"
|
|
163
|
+
} : void 0
|
|
164
|
+
]), 1032, ["back", "back-label", "title", "subtitle", "actions"]),
|
|
165
|
+
o(t(xe), {
|
|
166
|
+
modelValue: u.value,
|
|
167
|
+
"onUpdate:modelValue": s[0] || (s[0] = (W) => u.value = W),
|
|
168
|
+
name: "catalog.property-form",
|
|
169
|
+
errors: B.value,
|
|
170
|
+
disabled: T.value
|
|
171
|
+
}, null, 8, ["modelValue", "errors", "disabled"]),
|
|
172
|
+
v.value ? (g(), ae(t(de), { key: 0 }, {
|
|
173
|
+
state: n(() => [
|
|
174
|
+
o(t(we), { state: $.value }, null, 8, ["state"])
|
|
175
|
+
]),
|
|
176
|
+
default: n(() => [
|
|
177
|
+
o(t(ve), {
|
|
178
|
+
type: "primary",
|
|
179
|
+
loading: c.value,
|
|
180
|
+
disabled: !p.value,
|
|
181
|
+
onClick: M
|
|
182
|
+
}, {
|
|
183
|
+
default: n(() => [
|
|
184
|
+
b(x(t(a)("panel.save")), 1)
|
|
185
|
+
]),
|
|
186
|
+
_: 1
|
|
187
|
+
}, 8, ["loading", "disabled"])
|
|
188
|
+
]),
|
|
189
|
+
_: 1
|
|
190
|
+
})) : te("", !0)
|
|
191
|
+
], 64))
|
|
192
|
+
], 32));
|
|
193
|
+
}
|
|
194
|
+
}), Le = /* @__PURE__ */ Ee(Ce, [["__scopeId", "data-v-256c406b"]]);
|
|
195
|
+
export {
|
|
196
|
+
Le as default
|
|
197
|
+
};
|
|
198
|
+
//# sourceMappingURL=PropertyEditorPage-C0o03q-A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PropertyEditorPage-C0o03q-A.js","sources":["../src/PropertyEditorPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'\nimport {\n confirm,\n localizedValue,\n toast,\n useLocales,\n WxActionBar,\n WxBadge,\n WxBreadcrumb,\n WxBreadcrumbItem,\n WxButton,\n WxCard,\n WxSkeleton,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport {\n provideHistorySubject,\n useAdmin,\n useBodyKeys,\n useErrorText,\n useTranslate,\n WxSaveState,\n WxScreen,\n WxScreenHead,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport { createPropertiesApi } from './api'\nimport { providePropertyEditor } from './editor'\nimport { propertyName, wordsIn } from './format'\nimport { NAMESPACE, useCatalogPropertiesMessages } from './i18n'\nimport type { PropertyDetail, PropertyInterval, PropertyRow } from './types'\n\n/**\n * One property: a head, `catalog.property-form` under it, and one button (§7.1).\n *\n * The button saves «Main» — the property's own fields. «Values» and «Intervals» are records of\n * their own and save themselves, as the gallery of a product does: a reference book of three\n * hundred colours is not a value of a form, and a value merged away cannot wait for a button.\n */\ndefineOptions({ name: 'WxCatalogPropertyEditor' })\n\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\nconst admin = useAdmin()\nconst api = createPropertiesApi(admin)\nconst route = useRoute()\nconst router = useRouter()\nconst locales = useLocales()\nuseCatalogPropertiesMessages()\n\nconst t = useTranslate(NAMESPACE)\nconst message = useErrorText()\n\nconst id = computed(() => Number(route.params.id))\nconst list = computed(() => `${props.base}/properties`)\n\nconst property = ref<PropertyRow | null>(null)\nconst values = ref<ScreenModel>({})\nconst intervals = ref<PropertyInterval[]>([])\nconst snapshot = ref('')\n\nconst loading = ref(true)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst canManage = computed(() => admin.can('catalog.manage'))\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 admin.state.manifest?.modules.find((module) => module.id === 'catalog-properties')?.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.title as LocalizedValue | string | null | undefined\n const saved = property.value ? propertyName(property.value, admin.i18n.state.locale) : ''\n\n return localizedValue(written, locales.active.value, saved) || t('panel.untitled')\n})\n\nconst subtitle = computed(() => {\n const row = property.value\n\n if (!row) return undefined\n\n return [wordsIn(row.code, locales.active.value), t(`property.types.${row.type}`)]\n .filter(Boolean)\n .join(' · ')\n})\n\nprovidePropertyEditor({ property, values, intervals, locked })\nprovideHistorySubject({ id: computed(() => property.value?.id ?? null) })\n\nfunction take(detail: PropertyDetail): void {\n property.value = detail.property\n values.value = detail.values\n intervals.value = detail.intervals\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.get(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 (!property.value || saving.value || !canManage.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n take(await api.save(property.value.id, values.value))\n toast.success(t('panel.saved'))\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors && Object.keys(body.errors).length > 0) {\n errors.value = body.errors\n toast.danger(t('panel.save-failed'))\n } else {\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n\nasync function remove(): Promise<void> {\n const row = property.value\n\n if (!row) return\n\n const agreed = await confirm({\n title: t('panel.delete-title', { name: title.value }),\n message: t('panel.delete-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.remove(row.id)\n toast.success(t('panel.deleted'))\n snapshot.value = current.value\n void router.push(list.value)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction leaveGuard(event: BeforeUnloadEvent): void {\n if (dirty.value) event.preventDefault()\n}\n\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 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\nconst actions = computed<ScreenAction[]>(() =>\n property.value && canManage.value && property.value.deleted_at === null\n ? [\n {\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)\n</script>\n\n<template>\n <div class=\"wx-catalog-property-editor\" @keydown=\"onKeydown\">\n <template v-if=\"loading || !property\">\n <wx-skeleton class=\"wx-catalog-property-editor__ghost\" title :rows=\"1\" />\n <wx-card><wx-skeleton :rows=\"6\" /></wx-card>\n </template>\n\n <template v-else>\n <wx-screen-head\n divider\n :back=\"list\"\n :back-label=\"section\"\n :title=\"title\"\n :subtitle=\"subtitle\"\n :actions=\"actions\"\n >\n <template #trail>\n <wx-breadcrumb size=\"sm\" :label=\"t('panel.trail')\">\n <wx-breadcrumb-item :as=\"'router-link'\" :to=\"list\">{{ section }}</wx-breadcrumb-item>\n <wx-breadcrumb-item current>{{ title }}</wx-breadcrumb-item>\n </wx-breadcrumb>\n </template>\n\n <template v-if=\"property.deleted_at !== null\" #title-after>\n <wx-badge dot>{{ t('panel.in-bin') }}</wx-badge>\n </template>\n </wx-screen-head>\n\n <wx-screen\n v-model=\"values\"\n name=\"catalog.property-form\"\n :errors=\"errors\"\n :disabled=\"locked\"\n />\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-property-editor {\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-property-editor__ghost {\n max-width: 420px;\n}\n</style>\n"],"names":["props","__props","admin","useAdmin","api","createPropertiesApi","route","useRoute","router","useRouter","locales","useLocales","useCatalogPropertiesMessages","t","useTranslate","NAMESPACE","message","useErrorText","id","computed","list","property","ref","values","intervals","snapshot","loading","saving","errors","canManage","locked","current","dirty","state","section","module","title","written","saved","propertyName","localizedValue","subtitle","row","wordsIn","providePropertyEditor","provideHistorySubject","take","detail","load","error","toast","save","body","remove","confirm","leaveGuard","event","onKeydown","useBodyKeys","watch","onMounted","onBeforeUnmount","onBeforeRouteLeave","actions","_createElementBlock","_Fragment","_createVNode","_unref","WxSkeleton","WxCard","WxScreenHead","WxBreadcrumb","WxBreadcrumbItem","WxBadge","WxScreen","$event","_createBlock","WxActionBar","WxSaveState","WxButton"],"mappings":";;;;;;;;;;;;AA4CA,UAAMA,IAAQC,GAERC,IAAQC,GAAA,GACRC,IAAMC,GAAoBH,CAAK,GAC/BI,IAAQC,GAAA,GACRC,IAASC,GAAA,GACTC,IAAUC,GAAA;AAChB,IAAAC,GAAA;AAEA,UAAMC,IAAIC,GAAaC,EAAS,GAC1BC,IAAUC,GAAA,GAEVC,IAAKC,EAAS,MAAM,OAAOb,EAAM,OAAO,EAAE,CAAC,GAC3Cc,IAAOD,EAAS,MAAM,GAAGnB,EAAM,IAAI,aAAa,GAEhDqB,IAAWC,EAAwB,IAAI,GACvCC,IAASD,EAAiB,EAAE,GAC5BE,IAAYF,EAAwB,EAAE,GACtCG,IAAWH,EAAI,EAAE,GAEjBI,IAAUJ,EAAI,EAAI,GAClBK,IAASL,EAAI,EAAK,GAClBM,IAASN,EAA8B,EAAE,GAEzCO,IAAYV,EAAS,MAAMjB,EAAM,IAAI,gBAAgB,CAAC,GACtD4B,IAASX,EAAS,MAAM,CAACU,EAAU,SAASF,EAAO,KAAK,GAExDI,IAAUZ,EAAS,MAAM,KAAK,UAAUI,EAAO,KAAK,CAAC,GACrDS,IAAQb,EAAS,MAAMM,EAAS,UAAU,MAAMM,EAAQ,UAAUN,EAAS,KAAK,GAEhFQ,IAAQd,EAAyC,MACjDQ,EAAO,QAAc,WAElBK,EAAM,QAAQ,YAAY,OAClC,GAEKE,IAAUf;AAAA,MACd,MACEjB,EAAM,MAAM,UAAU,QAAQ,KAAK,CAACiC,MAAWA,EAAO,OAAO,oBAAoB,GAAG,SACpFtB,EAAE,cAAc;AAAA,IAAA,GAIduB,IAAQjB,EAAS,MAAM;AAC3B,YAAMkB,IAAUd,EAAO,MAAM,OACvBe,IAAQjB,EAAS,QAAQkB,GAAalB,EAAS,OAAOnB,EAAM,KAAK,MAAM,MAAM,IAAI;AAEvF,aAAOsC,GAAeH,GAAS3B,EAAQ,OAAO,OAAO4B,CAAK,KAAKzB,EAAE,gBAAgB;AAAA,IACnF,CAAC,GAEK4B,IAAWtB,EAAS,MAAM;AAC9B,YAAMuB,IAAMrB,EAAS;AAErB,UAAKqB;AAEL,eAAO,CAACC,GAAQD,EAAI,MAAMhC,EAAQ,OAAO,KAAK,GAAGG,EAAE,kBAAkB6B,EAAI,IAAI,EAAE,CAAC,EAC7E,OAAO,OAAO,EACd,KAAK,KAAK;AAAA,IACf,CAAC;AAED,IAAAE,GAAsB,EAAE,UAAAvB,GAAU,QAAAE,GAAQ,WAAAC,GAAW,QAAAM,GAAQ,GAC7De,GAAsB,EAAE,IAAI1B,EAAS,MAAME,EAAS,OAAO,MAAM,IAAI,GAAG;AAExE,aAASyB,EAAKC,GAA8B;AAC1C,MAAA1B,EAAS,QAAQ0B,EAAO,UACxBxB,EAAO,QAAQwB,EAAO,QACtBvB,EAAU,QAAQuB,EAAO,WACzBtB,EAAS,QAAQ,KAAK,UAAUsB,EAAO,MAAM;AAAA,IAC/C;AAEA,mBAAeC,IAAsB;AACnC,MAAAtB,EAAQ,QAAQ;AAEhB,UAAI;AACF,QAAAoB,EAAK,MAAM1C,EAAI,IAAIc,EAAG,KAAK,CAAC;AAAA,MAC9B,SAAS+B,GAAO;AACd,QAAAC,EAAM,OAAOlC,EAAQiC,CAAK,CAAC,GACtBzC,EAAO,KAAKY,EAAK,KAAK;AAAA,MAC7B,UAAA;AACE,QAAAM,EAAQ,QAAQ;AAAA,MAClB;AAAA,IACF;AAEA,mBAAeyB,IAAsB;AACnC,UAAI,GAAC9B,EAAS,SAASM,EAAO,SAAS,CAACE,EAAU,QAElD;AAAA,QAAAF,EAAO,QAAQ,IACfC,EAAO,QAAQ,CAAA;AAEf,YAAI;AACF,UAAAkB,EAAK,MAAM1C,EAAI,KAAKiB,EAAS,MAAM,IAAIE,EAAO,KAAK,CAAC,GACpD2B,EAAM,QAAQrC,EAAE,aAAa,CAAC;AAAA,QAChC,SAASoC,GAAO;AACd,gBAAMG,IAAQH,EAA2D;AAEzE,UAAIG,GAAM,UAAU,OAAO,KAAKA,EAAK,MAAM,EAAE,SAAS,KACpDxB,EAAO,QAAQwB,EAAK,QACpBF,EAAM,OAAOrC,EAAE,mBAAmB,CAAC,KAEnCqC,EAAM,OAAOlC,EAAQiC,CAAK,CAAC;AAAA,QAE/B,UAAA;AACE,UAAAtB,EAAO,QAAQ;AAAA,QACjB;AAAA;AAAA,IACF;AAEA,mBAAe0B,IAAwB;AACrC,YAAMX,IAAMrB,EAAS;AAYrB,UAVI,GAACqB,KAUD,CARW,MAAMY,EAAQ;AAAA,QAC3B,OAAOzC,EAAE,sBAAsB,EAAE,MAAMuB,EAAM,OAAO;AAAA,QACpD,SAASvB,EAAE,mBAAmB;AAAA,QAC9B,aAAaA,EAAE,cAAc;AAAA,QAC7B,YAAYA,EAAE,cAAc;AAAA,QAC5B,MAAM;AAAA,MAAA,CACP;AAID,YAAI;AACF,gBAAMT,EAAI,OAAOsC,EAAI,EAAE,GACvBQ,EAAM,QAAQrC,EAAE,eAAe,CAAC,GAChCY,EAAS,QAAQM,EAAQ,OACpBvB,EAAO,KAAKY,EAAK,KAAK;AAAA,QAC7B,SAAS6B,GAAO;AACd,UAAAC,EAAM,OAAOlC,EAAQiC,CAAK,CAAC;AAAA,QAC7B;AAAA,IACF;AAEA,aAASM,EAAWC,GAAgC;AAClD,MAAIxB,EAAM,SAAOwB,EAAM,eAAA;AAAA,IACzB;AAEA,aAASC,EAAUD,GAA4B;AAC7C,OAAKA,EAAM,WAAWA,EAAM,YAAYA,EAAM,IAAI,YAAA,MAAkB,QAClEA,EAAM,eAAA,GACFxB,EAAM,SAAYmB,EAAA;AAAA,IAE1B;AAEA,IAAAO,GAAYD,CAAS,GAErBE,EAAMzC,GAAI;AAAM,MAAK8B;KAAM,GAE3BY,EAAU,MAAM;AACd,MAAKZ,EAAA,GACL,OAAO,iBAAiB,gBAAgBO,CAAU;AAAA,IACpD,CAAC,GAEDM,EAAgB,MAAM;AACpB,aAAO,oBAAoB,gBAAgBN,CAAU;AAAA,IACvD,CAAC,GAEDO,GAAmB,YACb,CAAC9B,EAAM,SAAS,CAACH,EAAU,QAAc,KAEtC,MAAMyB,EAAQ;AAAA,MACnB,OAAOzC,EAAE,mBAAmB;AAAA,MAC5B,SAASA,EAAE,kBAAkB;AAAA,MAC7B,aAAaA,EAAE,aAAa;AAAA,MAC5B,YAAYA,EAAE,cAAc;AAAA,MAC5B,MAAM;AAAA,IAAA,CACP,CACF;AAED,UAAMkD,IAAU5C;AAAA,MAAyB,MACvCE,EAAS,SAASQ,EAAU,SAASR,EAAS,MAAM,eAAe,OAC/D;AAAA,QACE;AAAA,UACE,KAAK;AAAA,UACL,OAAOR,EAAE,cAAc;AAAA,UACvB,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,KAAK,MAAA;AAAM,YAAKwC,EAAA;AAAA;AAAA,QAAO;AAAA,MACzB,IAEF,CAAA;AAAA,IAAC;2BAKLW,EA4CM,OAAA;AAAA,MA5CD,OAAM;AAAA,MAA8B,WAAAP;AAAA,IAAA;MACvB/B,EAAA,UAAYL,EAAA,cAA5B2C,EAGWC,GAAA,EAAA,KAAA,EAAA,GAAA;AAAA,QAFTC,EAAyEC,EAAAC,CAAA,GAAA;AAAA,UAA5D,OAAM;AAAA,UAAoC,OAAA;AAAA,UAAO,MAAM;AAAA,QAAA;QACpEF,EAA4CC,EAAAE,EAAA,GAAA,MAAA;AAAA,qBAAnC,MAAyB;AAAA,YAAzBH,EAAyBC,EAAAC,CAAA,GAAA,EAAX,MAAM,GAAC;AAAA,UAAA;;;sBAGhCJ,EAqCWC,GAAA,EAAA,KAAA,KAAA;AAAA,QApCTC,EAkBiBC,EAAAG,EAAA,GAAA;AAAA,UAjBf,SAAA;AAAA,UACC,MAAMlD,EAAA;AAAA,UACN,cAAYc,EAAA;AAAA,UACZ,OAAOE,EAAA;AAAA,UACP,UAAUK,EAAA;AAAA,UACV,SAASsB,EAAA;AAAA,QAAA;UAEC,SACT,MAGgB;AAAA,YAHhBG,EAGgBC,EAAAI,EAAA,GAAA;AAAA,cAHD,MAAK;AAAA,cAAM,OAAOJ,EAAAtD,CAAA,EAAC,aAAA;AAAA,YAAA;yBAChC,MAAqF;AAAA,gBAArFqD,EAAqFC,EAAAK,CAAA,GAAA;AAAA,kBAAhE,IAAI;AAAA,kBAAgB,IAAIpD,EAAA;AAAA,gBAAA;6BAAM,MAAa;AAAA,wBAAVc,EAAA,KAAO,GAAA,CAAA;AAAA,kBAAA;;;gBAC7DgC,EAA4DC,EAAAK,CAAA,GAAA,EAAxC,SAAA,MAAO;AAAA,6BAAC,MAAW;AAAA,wBAARpC,EAAA,KAAK,GAAA,CAAA;AAAA,kBAAA;;;;;;;;;UAIxBf,EAAA,MAAS,eAAU;kBAAY;AAAA,kBAC7C,MAAgD;AAAA,cAAhD6C,EAAgDC,EAAAM,EAAA,GAAA,EAAtC,KAAA,MAAG;AAAA,2BAAC,MAAuB;AAAA,sBAApBN,EAAAtD,CAAA,EAAC,cAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;;;;;QAItBqD,EAKEC,EAAAO,EAAA,GAAA;AAAA,sBAJSnD,EAAA;AAAA,wDAAAA,EAAM,QAAAoD;AAAA,UACf,MAAK;AAAA,UACJ,QAAQ/C,EAAA;AAAA,UACR,UAAUE,EAAA;AAAA,QAAA;QAGQD,EAAA,cAArB+C,GAQgBT,EAAAU,EAAA,GAAA,EAAA,KAAA,KAAA;AAAA,UAPH,SACT,MAAgC;AAAA,YAAhCX,EAAgCC,EAAAW,EAAA,GAAA,EAAhB,OAAO7C,EAAA,SAAK,MAAA,GAAA,CAAA,OAAA,CAAA;AAAA,UAAA;qBAG9B,MAEY;AAAA,YAFZiC,EAEYC,EAAAY,EAAA,GAAA;AAAA,cAFD,MAAK;AAAA,cAAW,SAASpD,EAAA;AAAA,cAAS,WAAWK,EAAA;AAAA,cAAQ,SAAOmB;AAAA,YAAA;yBACrE,MAAqB;AAAA,oBAAlBgB,EAAAtD,CAAA,EAAC,YAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;;;;;;;;"}
|