@webx-ui/module-recipes 0.2.5 → 0.3.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/index.js CHANGED
@@ -1,1176 +1,19 @@
1
- import { reorderItems as Ue, useAdmin as oe, useDates as Be, useTranslate as se, useErrorText as $e, useEditing as Le, provideRelationOwner as He, provideRecordAddress as Fe, WxScreenHead as Ke, WxEditingAlerts as Je, WxScreen as Ge, WxSaveState as je, WxDate as Qe, WxDrafts as Xe, WxListScreen as Ye, WxRowMenu as Ze, WxAddressNote as et, categoryRoutes as Se } from "@webx-ui/module-admin";
2
- import { provide as tt, inject as at, defineComponent as he, computed as f, ref as _, watch as ne, onMounted as Ne, onBeforeUnmount as ze, openBlock as p, createElementBlock as q, Fragment as ee, createVNode as y, unref as e, withCtx as v, createTextVNode as $, toDisplayString as x, createBlock as k, createCommentVNode as D, createElementVNode as A, renderList as qe, normalizeClass as Te, useTemplateRef as it, nextTick as rt, isRef as st, withKeys as nt, resolveDynamicComponent as lt, mergeProps as ot } from "vue";
3
- import { useRoute as Ae, useRouter as De, onBeforeRouteLeave as ut } from "vue-router";
4
- import { useLocales as ct, localizedValue as dt, confirm as ae, WxSkeleton as ve, WxCard as pt, WxBadge as Z, WxBreadcrumb as vt, WxBreadcrumbItem as Ve, WxActionBar as ht, WxButton as le, toast as S, WxEmpty as Ee, WxText as ft, useModal as yt, WxDialog as mt, WxFormItem as bt, WxInput as Pe, WxSpace as gt, createModal as wt, WxIcon as _e, WxSelect as ke, WxSortableList as xt, WxTooltip as _t, WxAlert as kt } from "@webx-ui/core";
5
- const Ce = "recipes";
6
- function fe(u) {
7
- const l = `${u.apiPath}/${Ce}`, m = (t) => t.data;
8
- return {
9
- list: (t = {}) => {
10
- const o = new URLSearchParams();
11
- return t.q && o.set("q", t.q), t.category != null && o.set("category", String(t.category)), t.nutrient != null && o.set("nutrient", String(t.nutrient)), t.service != null && o.set("service", String(t.service)), t.status && o.set("status", t.status), t.trashed && o.set("trashed", "1"), u.http.get(o.size > 0 ? `${l}?${o}` : l);
12
- },
13
- get: (t) => u.http.get(`${l}/${t}`).then(m),
14
- create: (t) => u.http.post(l, t).then(m),
15
- save: (t, o) => u.http.put(`${l}/${t}`, o).then(m),
16
- discard: (t) => u.http.post(`${l}/${t}/discard`, {}).then(m),
17
- publish: (t, o) => u.http.post(`${l}/${t}/publish`, o ? { revision: o } : {}).then(m),
18
- unpublish: (t) => u.http.post(`${l}/${t}/unpublish`, {}).then(m),
19
- remove: (t) => u.http.delete(`${l}/${t}`).then(() => {
20
- }),
21
- restore: (t) => u.http.post(`${l}/${t}/restore`, {}).then(m),
22
- versions: (t) => u.http.get(`${l}/${t}/versions`).then(m),
23
- restoreVersion: (t, o) => u.http.post(`${l}/${t}/versions/${o}/restore`, {}).then(m),
24
- reorder: (t) => Ue(u, Ce, t, null)
25
- };
26
- }
27
- const Me = /* @__PURE__ */ Symbol("wx-recipe-editor");
28
- function Tt(u) {
29
- tt(Me, u);
30
- }
31
- function $t() {
32
- return at(Me, null);
33
- }
34
- const Rt = {
35
- module: {
36
- group: "Recipes",
37
- recipes: "Recipes",
38
- categories: "Categories",
39
- nutrients: "Rich in"
40
- },
41
- panel: {
42
- new: "New recipe",
43
- "new-title": "New recipe",
44
- "field-title": "Title",
45
- create: "Create",
46
- cancel: "Cancel",
47
- search: "Search by title or address",
48
- empty: "No recipes yet.",
49
- "empty-help": "A recipe is a page of the site with a gallery, ingredients, a method and nutrition, filed under categories.",
50
- "empty-search": "Nothing matches that.",
51
- "empty-bin": "The bin is empty.",
52
- "status-draft": "Draft",
53
- "status-published": "Live",
54
- "status-modified": "Live",
55
- "status-unpublished": "Off the site",
56
- edits: "edits",
57
- "no-address": "No address in this language",
58
- "main-category": "The main category: it goes in the breadcrumbs",
59
- minutes: ":count min",
60
- "hours-minutes": ":hours h :minutes min",
61
- hours: ":hours h",
62
- "view-all": "All",
63
- "view-published": "Live",
64
- "view-draft": "Drafts",
65
- "view-unpublished": "Off the site",
66
- bin: "Bin",
67
- "filter-category": "Category",
68
- "any-category": "Any category",
69
- "filter-nutrient": "Rich in",
70
- "any-nutrient": "Rich in anything",
71
- "filter-service": "Service",
72
- "any-service": "Any service",
73
- "order-all": "Drag to change the order on the site. It is the one order recipes have.",
74
- "order-locked": "Recipes have one order, the same in every category and on every page of the site. Clear the search and the filters to drag it.",
75
- open: "Open",
76
- "open-on-site": "Open on the site",
77
- "copy-address": "Copy the address",
78
- "address-copied": "The address is on the clipboard.",
79
- publish: "Publish",
80
- unpublish: "Take off the site",
81
- delete: "Delete",
82
- restore: "Restore",
83
- "delete-title": "Delete “:title”?",
84
- "delete-text": "It goes to the bin and comes off the site, and its address is free again.",
85
- deleted: "The recipe is in the bin.",
86
- restored: "The recipe is back.",
87
- published: "The recipe is on the site.",
88
- "unpublished-done": "The recipe is off the site.",
89
- "reorder-failed": "The new order was not saved."
90
- },
91
- recipe: {
92
- trail: "Where this recipe sits",
93
- untitled: "Untitled",
94
- save: "Save",
95
- "save-failed": "The recipe was not saved.",
96
- "publish-title": "Put “:title” on the site?",
97
- "publish-text": "It answers at :address from the moment you do, for everyone — with its categories, services and similar recipes as they are chosen now.",
98
- "publish-nowhere": "It has no address in this language yet, so nothing will answer.",
99
- preview: "Preview",
100
- discard: "Discard changes",
101
- discarded: "The recipe is back to what is published.",
102
- "conflict-title": "The recipe changed while you were editing",
103
- "conflict-mine": "Keep mine",
104
- "conflict-theirs": "Take the newer version",
105
- "conflict-theirs-title": "Give up what you wrote?",
106
- "conflict-theirs-text": "The recipe is read again as it now is, and what you have typed since goes.",
107
- leave: "Leave",
108
- "leave-title": "Leave without saving?",
109
- "leave-text": "The recipe could not be saved, and what you wrote is not on the server.",
110
- "live-since": "On the site since :date",
111
- "address-moving": "The address is changing. The old one keeps working and leads to the new one.",
112
- "history-empty": "This recipe has never been published.",
113
- version: "#:number",
114
- "version-live": "On the site",
115
- "source-panel": "From the panel",
116
- "source-mcp": "By an agent",
117
- "source-import": "Imported",
118
- "restore-title": "Restore version :number?",
119
- "restore-text": "It becomes the draft. The site keeps showing what is published until you publish this.",
120
- "restore-version": "Restore",
121
- "restored-version": "Version :number is now the draft."
122
- },
123
- category: {
124
- new: "New category",
125
- empty: "No categories yet.",
126
- "empty-help": "A category is a section of the recipes with a page of its own.",
127
- order: "Drag to change the order of the categories on the site.",
128
- hidden: "Hidden from the site",
129
- "no-address": "No address in this language",
130
- recipes: "Recipes: :count",
131
- "show-recipes": "Show its recipes",
132
- "delete-blocked": "This category still holds recipes. Move them first.",
133
- "delete-text": "It goes to the bin and its page comes off the site. Its recipes stay.",
134
- deleted: "The category is in the bin.",
135
- saved: "The category is saved.",
136
- "field-title": "Title",
137
- "field-slug": "Address",
138
- "address-moving": "The address is changing. The old one keeps working and leads to the new one."
139
- },
140
- nutrient: {
141
- new: "New nutrient",
142
- empty: "Nothing here yet.",
143
- "empty-help": "What a recipe is rich in — iron, fibre, protein. A chip on the recipe and a filter of the catalogue, with no page of its own.",
144
- order: "Drag to change the order of the chips and of the filter on the site.",
145
- hidden: "Hidden from the site",
146
- recipes: "Recipes: :count",
147
- "show-recipes": "Show its recipes",
148
- "delete-blocked": "Recipes are still marked with this. Take it off them first.",
149
- "delete-text": "It goes to the bin and drops out of the filter. The recipes stay.",
150
- deleted: "It is in the bin.",
151
- saved: "Saved.",
152
- "field-title": "Title"
153
- }
154
- }, Ie = /* @__PURE__ */ new WeakSet();
155
- function ye() {
156
- try {
157
- const { i18n: u } = oe();
158
- Ie.has(u) || (u.defaults("webx-recipes", Rt), Ie.add(u));
159
- } catch {
160
- }
161
- }
162
- const Wt = 1200, St = /* @__PURE__ */ he({
163
- __name: "RecipeEditorPage",
164
- props: {
165
- base: { default: "/recipes" }
166
- },
167
- setup(u) {
168
- const l = u, m = oe(), t = fe(m), o = Ae(), E = De(), i = ct(), V = Be();
169
- ye();
170
- const r = se("webx-recipes"), g = se("webx-admin"), R = $e(), P = f(() => Number(o.params.id)), N = f(() => l.base), n = _(null), b = _({}), h = _(""), M = _(null), B = _(null), F = _(!0), O = _(!1), I = _(!1), j = _({}), Q = _(""), z = f(() => m.can("recipes.manage")), T = Le({
171
- entity: "recipes",
172
- id: () => n.value?.id,
173
- values: b,
174
- revision: h,
175
- read: async () => {
176
- const s = await t.get(P.value);
177
- return { values: s.values, revision: s.revision };
178
- },
179
- adopt: (s) => {
180
- Q.value = JSON.stringify(s.values);
181
- },
182
- // Published or restored by somebody else: the badge and the address follow, and the form
183
- // stays as it is.
184
- refresh: () => be(),
185
- restore: async () => {
186
- n.value && await t.restore(n.value.id);
187
- },
188
- canWrite: () => z.value,
189
- busy: () => O.value || G !== void 0,
190
- screen: "recipes.form"
191
- }), K = T.conflict, me = f(() => !z.value || I.value), U = f(() => JSON.stringify(b.value)), C = f(() => Q.value !== "" && U.value !== Q.value), ie = f(() => O.value ? "saving" : C.value ? "unsaved" : "saved"), X = f(() => {
192
- const s = b.value.title;
193
- return dt(s, i.active.value, n.value?.title ?? "");
194
- }), ue = f(() => {
195
- const s = n.value;
196
- return !s || s.status !== "published" && s.status !== "modified" ? "" : r("recipe.live-since", { date: V.short(s.published_at) });
197
- });
198
- He({ type: "recipe", id: f(() => n.value?.id ?? null) }), Fe({
199
- values: b,
200
- prefix: M,
201
- path: f(() => n.value?.path),
202
- moving: () => r("recipe.address-moving")
203
- }), Tt({ recipe: n, canManage: z.value, reload: () => J(!0) });
204
- function ce(s) {
205
- n.value = s.recipe, b.value = s.values, h.value = s.revision, M.value = s.prefix, B.value = s.preview_url, Q.value = JSON.stringify(s.values), T.opened(s.values), K.value = null;
206
- }
207
- async function J(s = !1) {
208
- s || (F.value = !0);
209
- try {
210
- ce(await t.get(P.value));
211
- } catch (w) {
212
- S.danger(R(w)), E.push(N.value);
213
- } finally {
214
- F.value = !1;
215
- }
216
- }
217
- async function be() {
218
- try {
219
- const s = await t.get(P.value);
220
- n.value = s.recipe, M.value = s.prefix, B.value = s.preview_url;
221
- } catch {
222
- }
223
- }
224
- let Y;
225
- function ge() {
226
- !z.value || K.value || !C.value || T.stopped.value || (clearTimeout(Y), Y = setTimeout(() => {
227
- H();
228
- }, Wt));
229
- }
230
- function te() {
231
- !z.value || K.value || !C.value || O.value || T.stopped.value || (clearTimeout(Y), H());
232
- }
233
- let G;
234
- async function H() {
235
- for (; G; ) {
236
- const { carried: W, done: L } = G;
237
- if (await L, !C.value || K.value || U.value === W) return;
238
- }
239
- const s = U.value, w = we();
240
- G = { carried: s, done: w };
241
- try {
242
- await w;
243
- } finally {
244
- G?.done === w && (G = void 0);
245
- }
246
- }
247
- async function we() {
248
- if (!n.value || !z.value) return;
249
- clearTimeout(Y);
250
- const s = U.value, w = b.value;
251
- O.value = !0, j.value = {};
252
- try {
253
- const W = await t.save(n.value.id, { values: w, revision: h.value });
254
- n.value = W.recipe, h.value = W.revision, M.value = W.prefix, B.value = W.preview_url, K.value = null, U.value === s && (Q.value = s), T.opened(w);
255
- } catch (W) {
256
- const L = W;
257
- if (L.status === 409 && L.body?.data) {
258
- const pe = L.body.data;
259
- T.refused({
260
- values: pe.values,
261
- revision: pe.revision,
262
- changed: L.body.changed
263
- }) && (n.value = pe.recipe, H());
264
- } else L.body?.errors ? (j.value = L.body.errors, S.danger(r("recipe.save-failed"))) : await T.failed(W) || S.danger(R(W));
265
- } finally {
266
- O.value = !1;
267
- }
268
- }
269
- async function xe() {
270
- await J(!0);
271
- }
272
- async function a() {
273
- if (!(!n.value || !await ae({
274
- title: g("editor.discard-title"),
275
- message: g("editor.discard-text"),
276
- confirmText: r("recipe.discard"),
277
- // Not «Cancel»: beside «Discard changes» the two read as the same word.
278
- cancelText: g("editor.keep-changes"),
279
- tone: "danger"
280
- }))) {
281
- I.value = !0;
282
- try {
283
- ce(await t.discard(n.value.id)), S.success(r("recipe.discarded"));
284
- } catch (w) {
285
- S.danger(R(w));
286
- } finally {
287
- I.value = !1;
288
- }
289
- }
290
- }
291
- async function d() {
292
- if (C.value && await H(), K.value || C.value || !n.value) return;
293
- const s = await T.beforePublish();
294
- if (!s) return;
295
- const w = n.value, W = w.next_path ?? w.path, L = w.next_path != null && w.path !== null ? ` ${g("editor.publish-moves", { old: `/${w.path}` })}` : "";
296
- if (s.asked || await ae({
297
- title: r("recipe.publish-title", { title: X.value || w.title }),
298
- message: W === null ? r("recipe.publish-nowhere") : r("recipe.publish-text", { address: `/${W}` }) + L,
299
- confirmText: r("panel.publish"),
300
- cancelText: r("panel.cancel")
301
- })) {
302
- I.value = !0;
303
- try {
304
- await t.publish(w.id, s.revision), await J(!0), S.success(r("panel.published"));
305
- } catch (We) {
306
- await T.failed(We) || S.danger(R(We));
307
- } finally {
308
- I.value = !1;
309
- }
310
- }
311
- }
312
- async function c() {
313
- const s = n.value;
314
- if (!(!s || !await ae({
315
- title: g("editor.unpublish-title"),
316
- message: g("editor.unpublish-text"),
317
- confirmText: g("editor.unpublish"),
318
- cancelText: g("editor.keep-published")
319
- }))) {
320
- I.value = !0;
321
- try {
322
- await t.unpublish(s.id), await J(!0), S.success(g("editor.unpublished"));
323
- } catch (W) {
324
- S.danger(R(W));
325
- } finally {
326
- I.value = !1;
327
- }
328
- }
329
- }
330
- function re() {
331
- const s = n.value?.status;
332
- return s === "published" || s === "modified" ? "success" : "default";
333
- }
334
- function de(s) {
335
- C.value && s.preventDefault();
336
- }
337
- ne(U, ge), ne(P, () => {
338
- J();
339
- }), Ne(() => {
340
- J(), window.addEventListener("beforeunload", de);
341
- }), ze(() => {
342
- clearTimeout(Y), window.removeEventListener("beforeunload", de);
343
- }), ut(async () => !C.value || !z.value || (await H(), !C.value) ? !0 : await ae({
344
- title: r("recipe.leave-title"),
345
- message: r("recipe.leave-text"),
346
- confirmText: r("recipe.leave"),
347
- cancelText: r("panel.cancel"),
348
- tone: "danger"
349
- }));
350
- const Oe = f(() => {
351
- const s = [];
352
- return B.value && s.push({ key: "preview", label: r("recipe.preview"), icon: "eye", href: B.value }), n.value?.url && n.value.status !== "draft" && s.push({
353
- key: "site",
354
- label: r("panel.open-on-site"),
355
- icon: "link",
356
- href: n.value.url
357
- }), z.value && (n.value?.status === "published" || n.value?.status === "modified") && s.push({
358
- key: "unpublish",
359
- label: g("editor.unpublish"),
360
- icon: "eye-off",
361
- run: () => {
362
- c();
363
- }
364
- }), n.value?.status === "modified" && s.push({
365
- key: "discard",
366
- label: r("recipe.discard"),
367
- icon: "refresh",
368
- danger: !0,
369
- menu: !0,
370
- run: () => {
371
- a();
372
- }
373
- }), s;
374
- });
375
- return (s, w) => (p(), q("div", {
376
- class: "wx-recipe-editor",
377
- onFocusout: te
378
- }, [
379
- F.value || !n.value ? (p(), q(ee, { key: 0 }, [
380
- y(e(ve), {
381
- class: "wx-recipe-editor__ghost",
382
- title: "",
383
- rows: 1
384
- }),
385
- y(e(pt), null, {
386
- default: v(() => [
387
- y(e(ve), { rows: 8 })
388
- ]),
389
- _: 1
390
- })
391
- ], 64)) : (p(), q(ee, { key: 1 }, [
392
- y(e(Ke), {
393
- divider: "",
394
- back: N.value,
395
- "back-label": e(r)("module.recipes"),
396
- title: X.value || e(r)("recipe.untitled"),
397
- subtitle: ue.value,
398
- actions: Oe.value
399
- }, {
400
- trail: v(() => [
401
- y(e(vt), {
402
- size: "sm",
403
- label: e(r)("recipe.trail")
404
- }, {
405
- default: v(() => [
406
- y(e(Ve), {
407
- as: "router-link",
408
- to: N.value
409
- }, {
410
- default: v(() => [
411
- $(x(e(r)("module.recipes")), 1)
412
- ]),
413
- _: 1
414
- }, 8, ["to"]),
415
- y(e(Ve), { current: "" }, {
416
- default: v(() => [
417
- $(x(X.value || e(r)("recipe.untitled")), 1)
418
- ]),
419
- _: 1
420
- })
421
- ]),
422
- _: 1
423
- }, 8, ["label"])
424
- ]),
425
- "title-after": v(() => [
426
- y(e(Z), {
427
- type: re(),
428
- dot: ""
429
- }, {
430
- default: v(() => [
431
- $(x(e(r)(`panel.status-${n.value.status}`)), 1)
432
- ]),
433
- _: 1
434
- }, 8, ["type"]),
435
- n.value.status === "modified" ? (p(), k(e(Z), {
436
- key: 0,
437
- type: "primary",
438
- round: ""
439
- }, {
440
- default: v(() => [
441
- $(x(e(r)("panel.edits")), 1)
442
- ]),
443
- _: 1
444
- })) : D("", !0)
445
- ]),
446
- _: 1
447
- }, 8, ["back", "back-label", "title", "subtitle", "actions"]),
448
- y(e(Je), {
449
- editing: e(T),
450
- onSave: H,
451
- onTheirs: xe
452
- }, null, 8, ["editing"]),
453
- y(e(Ge), {
454
- modelValue: b.value,
455
- "onUpdate:modelValue": w[0] || (w[0] = (W) => b.value = W),
456
- name: "recipes.form",
457
- errors: j.value,
458
- disabled: me.value
459
- }, null, 8, ["modelValue", "errors", "disabled"]),
460
- z.value ? (p(), k(e(ht), { key: 0 }, {
461
- state: v(() => [
462
- e(T).stopped.value ? D("", !0) : (p(), k(e(je), {
463
- key: 0,
464
- state: ie.value
465
- }, null, 8, ["state"]))
466
- ]),
467
- default: v(() => [
468
- y(e(le), {
469
- variant: "outline",
470
- loading: O.value,
471
- disabled: !C.value || e(T).stopped.value,
472
- title: e(T).blocked.value,
473
- onClick: H
474
- }, {
475
- default: v(() => [
476
- $(x(e(r)("recipe.save")), 1)
477
- ]),
478
- _: 1
479
- }, 8, ["loading", "disabled", "title"]),
480
- y(e(le), {
481
- type: "primary",
482
- loading: I.value,
483
- disabled: e(T).stopped.value || n.value.status === "published" && !C.value,
484
- title: e(T).blocked.value,
485
- onClick: d
486
- }, {
487
- default: v(() => [
488
- $(x(e(r)("panel.publish")), 1)
489
- ]),
490
- _: 1
491
- }, 8, ["loading", "disabled", "title"])
492
- ]),
493
- _: 1
494
- })) : D("", !0)
495
- ], 64))
496
- ], 32));
497
- }
498
- }), Re = (u, l) => {
499
- const m = u.__vccOpts || u;
500
- for (const [t, o] of l)
501
- m[t] = o;
502
- return m;
503
- }, Vt = /* @__PURE__ */ Re(St, [["__scopeId", "data-v-253e5134"]]), Ct = { class: "wx-recipe-history-tab" }, It = { class: "wx-recipe-history" }, Nt = { class: "wx-recipe-history__number" }, zt = { class: "wx-recipe-history__who" }, qt = /* @__PURE__ */ he({
504
- name: "WxRecipeHistory",
505
- __name: "RecipeHistory",
506
- setup(u) {
507
- const l = oe(), m = fe(l), t = $t();
508
- ye();
509
- const o = se("webx-recipes"), E = $e(), i = _(null), V = _(!1), r = f(() => t?.recipe.value ?? null), g = f(() => t?.canManage ?? !1), R = f(
510
- () => r.value?.status === "published" || r.value?.status === "modified" ? i.value?.[0]?.number ?? null : null
511
- );
512
- async function P() {
513
- const n = r.value;
514
- if (n)
515
- try {
516
- i.value = await m.versions(n.id);
517
- } catch (b) {
518
- i.value = [], S.danger(E(b));
519
- }
520
- }
521
- async function N(n) {
522
- const b = r.value;
523
- if (!(!b || !await ae({
524
- title: o("recipe.restore-title", { number: n.number }),
525
- message: o("recipe.restore-text"),
526
- confirmText: o("recipe.restore-version"),
527
- cancelText: o("panel.cancel")
528
- }))) {
529
- V.value = !0;
530
- try {
531
- await m.restoreVersion(b.id, n.number), await t?.reload(), S.success(o("recipe.restored-version", { number: n.number }));
532
- } catch (M) {
533
- S.danger(E(M));
534
- } finally {
535
- V.value = !1;
536
- }
537
- }
538
- }
539
- return ne(
540
- () => [r.value?.id, r.value?.published_at, r.value?.updated_at],
541
- () => {
542
- P();
543
- },
544
- { immediate: !0 }
545
- ), (n, b) => (p(), q("div", Ct, [
546
- A("div", It, [
547
- i.value === null ? (p(), k(e(ve), {
548
- key: 0,
549
- class: "wx-recipe-history__ghost",
550
- rows: 3
551
- })) : i.value.length === 0 ? (p(), k(e(Ee), {
552
- key: 1,
553
- description: e(o)("recipe.history-empty")
554
- }, null, 8, ["description"])) : (p(!0), q(ee, { key: 2 }, qe(i.value, (h) => (p(), q("div", {
555
- key: h.number,
556
- class: Te(["wx-recipe-history__row", { "is-live": h.number === R.value }])
557
- }, [
558
- A("code", Nt, x(e(o)("recipe.version", { number: h.number })), 1),
559
- A("div", zt, [
560
- h.created_at ? (p(), k(e(Qe), {
561
- key: 0,
562
- value: h.created_at,
563
- size: "md",
564
- tone: "default"
565
- }, null, 8, ["value"])) : D("", !0),
566
- y(e(ft), {
567
- size: "sm",
568
- tone: "muted"
569
- }, {
570
- default: v(() => [
571
- $(x(h.author ?? e(o)(`recipe.source-${h.source}`)) + " ", 1),
572
- h.comment ? (p(), q(ee, { key: 0 }, [
573
- $(" · " + x(h.comment), 1)
574
- ], 64)) : D("", !0)
575
- ]),
576
- _: 2
577
- }, 1024)
578
- ]),
579
- h.number === R.value ? (p(), k(e(Z), {
580
- key: 0,
581
- type: "success",
582
- dot: ""
583
- }, {
584
- default: v(() => [
585
- $(x(e(o)("recipe.version-live")), 1)
586
- ]),
587
- _: 1
588
- })) : D("", !0),
589
- g.value && h.number !== R.value ? (p(), k(e(le), {
590
- key: 1,
591
- size: "sm",
592
- variant: "outline",
593
- loading: V.value,
594
- onClick: (M) => N(h)
595
- }, {
596
- default: v(() => [
597
- $(x(e(o)("recipe.restore-version")), 1)
598
- ]),
599
- _: 1
600
- }, 8, ["loading", "onClick"])) : D("", !0)
601
- ], 2))), 128))
602
- ]),
603
- y(e(Xe), {
604
- id: r.value?.id,
605
- entity: "recipes",
606
- screen: "recipes.form",
607
- "can-restore": g.value,
608
- stamp: [r.value?.published_at, r.value?.updated_at],
609
- onRestored: b[0] || (b[0] = (h) => e(t)?.reload())
610
- }, null, 8, ["id", "can-restore", "stamp"])
611
- ]));
612
- }
613
- }), At = /* @__PURE__ */ Re(qt, [["__scopeId", "data-v-23c74649"]]);
614
- function Dt(u, l) {
615
- if (u == null || u <= 0) return "";
616
- const m = Math.floor(u / 60), t = u % 60;
617
- return m === 0 ? l("panel.minutes", { count: t }) : t === 0 ? l("panel.hours", { hours: m }) : l("panel.hours-minutes", { hours: m, minutes: t });
618
- }
619
- const Et = /* @__PURE__ */ he({
620
- __name: "RecipeCreateDialog",
621
- setup(u) {
622
- const { open: l, resolve: m, dismiss: t } = yt(), o = oe(), E = fe(o);
623
- ye();
624
- const i = se("webx-recipes"), V = _(""), r = _(!1), g = _({}), R = it("field");
625
- Ne(async () => {
626
- await rt(), R.value?.querySelector("input")?.focus();
627
- });
628
- async function P() {
629
- if (!(V.value.trim() === "" || r.value)) {
630
- r.value = !0, g.value = {};
631
- try {
632
- m((await E.create({ title: V.value.trim() })).recipe);
633
- } catch (N) {
634
- g.value = N.body?.errors ?? {};
635
- } finally {
636
- r.value = !1;
637
- }
638
- }
639
- }
640
- return (N, n) => (p(), k(e(mt), {
641
- open: e(l),
642
- "onUpdate:open": n[2] || (n[2] = (b) => st(l) ? l.value = b : null),
643
- title: e(i)("panel.new-title"),
644
- width: 440
645
- }, {
646
- footer: v(() => [
647
- y(e(gt), { size: "sm" }, {
648
- default: v(() => [
649
- y(e(le), {
650
- variant: "outline",
651
- onClick: n[1] || (n[1] = (b) => e(t)())
652
- }, {
653
- default: v(() => [
654
- $(x(e(i)("panel.cancel")), 1)
655
- ]),
656
- _: 1
657
- }),
658
- y(e(le), {
659
- type: "primary",
660
- loading: r.value,
661
- disabled: V.value.trim() === "",
662
- onClick: P
663
- }, {
664
- default: v(() => [
665
- $(x(e(i)("panel.create")), 1)
666
- ]),
667
- _: 1
668
- }, 8, ["loading", "disabled"])
669
- ]),
670
- _: 1
671
- })
672
- ]),
673
- default: v(() => [
674
- y(e(bt), {
675
- label: e(i)("panel.field-title"),
676
- error: g.value.title?.[0] ?? g.value.slug?.[0],
677
- required: ""
678
- }, {
679
- default: v(() => [
680
- A("div", {
681
- ref_key: "field",
682
- ref: R
683
- }, [
684
- y(e(Pe), {
685
- modelValue: V.value,
686
- "onUpdate:modelValue": n[0] || (n[0] = (b) => V.value = b),
687
- "aria-label": e(i)("panel.field-title"),
688
- onKeyup: nt(P, ["enter"])
689
- }, null, 8, ["modelValue", "aria-label"])
690
- ], 512)
691
- ]),
692
- _: 1
693
- }, 8, ["label", "error"])
694
- ]),
695
- _: 1
696
- }, 8, ["open", "title"]));
697
- }
698
- }), Pt = { class: "wx-recipes" }, Mt = { class: "wx-recipes__bar" }, Ot = ["src"], Ut = { class: "wx-recipe-row__name" }, Bt = { class: "wx-recipe-row__title" }, Lt = { class: "wx-recipe-row__address" }, Ht = { class: "wx-recipe-row__facts" }, Ft = { class: "wx-recipe-row__chips" }, Kt = { class: "wx-recipe-row__state" }, Jt = {
699
- key: 0,
700
- class: "wx-recipe-row__time"
701
- }, Gt = /* @__PURE__ */ he({
702
- __name: "RecipesPage",
703
- props: {
704
- base: { default: "/recipes" }
705
- },
706
- setup(u) {
707
- const l = u, m = oe(), t = fe(m), o = Ae(), E = De();
708
- ye();
709
- const i = se("webx-recipes"), V = $e(), r = _([]), g = _({ categories: [], nutrients: [], services: null }), R = _(!0), P = wt(Et), N = f(() => m.can("recipes.manage")), n = f(
710
- () => m.state.manifest?.modules.find((a) => a.id === "recipes")?.title ?? i("module.recipes")
711
- ), b = f({
712
- get: () => typeof o.query.view == "string" ? o.query.view : "",
713
- set: (a) => {
714
- E.replace({
715
- query: { ...o.query, view: a === "" ? void 0 : String(a) }
716
- });
717
- }
718
- }), h = f(() => b.value === "trashed");
719
- function M(a) {
720
- const d = o.query[a];
721
- return typeof d == "string" && d !== "" && Number(d) || null;
722
- }
723
- const B = f(() => M("category")), F = f(() => M("nutrient")), O = f(() => M("service")), I = f(() => typeof o.query.q == "string" ? o.query.q : ""), j = _(I.value), Q = f(() => [
724
- { value: "", label: i("panel.view-all") },
725
- { value: "published", label: i("panel.view-published") },
726
- { value: "draft", label: i("panel.view-draft") },
727
- { value: "unpublished", label: i("panel.view-unpublished") },
728
- { value: "trashed", label: i("panel.bin"), icon: "trash" }
729
- ]), z = f(
730
- () => b.value === "" && I.value.trim() === "" && B.value === null && F.value === null && O.value === null
731
- ), T = f(() => N.value && z.value), K = f(() => i(z.value ? "panel.order-all" : "panel.order-locked")), me = f(() => I.value !== "" ? i("panel.empty-search") : h.value ? i("panel.empty-bin") : i("panel.empty"));
732
- async function U() {
733
- R.value = !0;
734
- try {
735
- const a = await t.list({
736
- q: I.value,
737
- category: B.value,
738
- nutrient: F.value,
739
- service: O.value,
740
- status: h.value ? "" : b.value,
741
- trashed: h.value
742
- });
743
- r.value = a.data, g.value = a.filters;
744
- } catch (a) {
745
- S.danger(V(a));
746
- } finally {
747
- R.value = !1;
748
- }
749
- }
750
- ne(
751
- () => [b.value, B.value, F.value, O.value, I.value],
752
- () => {
753
- U();
754
- }
755
- ), U();
756
- let C;
757
- ne(j, (a) => {
758
- clearTimeout(C), C = setTimeout(() => {
759
- E.replace({ query: { ...o.query, q: a.trim() === "" ? void 0 : a } });
760
- }, 300);
761
- }), ze(() => clearTimeout(C));
762
- function ie(a, d) {
763
- E.replace({
764
- query: { ...o.query, [a]: typeof d == "number" ? String(d) : void 0 }
765
- });
766
- }
767
- const X = (a) => (a ?? []).map((d) => ({ value: d.id, label: d.title }));
768
- function ue(a) {
769
- E.push({ path: `${l.base}/${a.id}`, query: { ...o.query } });
770
- }
771
- async function ce() {
772
- const a = await P({});
773
- a && ue(a);
774
- }
775
- async function J() {
776
- try {
777
- await t.reorder(r.value.map((a) => a.id));
778
- } catch (a) {
779
- S.danger(V(a, i("panel.reorder-failed"))), await U();
780
- }
781
- }
782
- function be(a) {
783
- if (!N.value) return [];
784
- if (h.value)
785
- return [
786
- {
787
- key: "restore",
788
- icon: "refresh",
789
- label: i("panel.restore"),
790
- run: () => {
791
- ge(a);
792
- }
793
- }
794
- ];
795
- const d = [
796
- { key: "open", icon: "edit", label: i("panel.open"), run: () => ue(a) }
797
- ];
798
- return a.url && a.status !== "draft" && d.push({
799
- key: "site",
800
- icon: "external-link",
801
- label: i("panel.open-on-site"),
802
- href: a.url
803
- }), a.url && d.push({
804
- key: "copy",
805
- icon: "copy",
806
- label: i("panel.copy-address"),
807
- run: () => {
808
- G(a);
809
- }
810
- }), d.push(
811
- a.status === "published" || a.status === "modified" ? {
812
- key: "unpublish",
813
- icon: "eye-off",
814
- label: i("panel.unpublish"),
815
- run: () => {
816
- te(() => t.unpublish(a.id), i("panel.unpublished-done"));
817
- }
818
- } : {
819
- key: "publish",
820
- icon: "upload",
821
- label: i("panel.publish"),
822
- run: () => {
823
- te(() => t.publish(a.id), i("panel.published"));
824
- }
825
- }
826
- ), d.push({
827
- key: "delete",
828
- icon: "trash",
829
- label: i("panel.delete"),
830
- danger: !0,
831
- run: () => {
832
- Y(a);
833
- }
834
- }), d;
835
- }
836
- async function Y(a) {
837
- await ae({
838
- title: i("panel.delete-title", { title: a.title }),
839
- message: i("panel.delete-text"),
840
- confirmText: i("panel.delete"),
841
- cancelText: i("panel.cancel"),
842
- tone: "danger"
843
- }) && await te(() => t.remove(a.id), i("panel.deleted"));
844
- }
845
- async function ge(a) {
846
- await te(() => t.restore(a.id), i("panel.restored"));
847
- }
848
- async function te(a, d) {
849
- try {
850
- await a(), S.success(d);
851
- } catch (c) {
852
- S.danger(V(c));
853
- } finally {
854
- await U();
855
- }
856
- }
857
- async function G(a) {
858
- if (a.url)
859
- try {
860
- await navigator.clipboard.writeText(a.url), S.success(i("panel.address-copied"));
861
- } catch {
862
- }
863
- }
864
- function H(a) {
865
- return a === "published" || a === "modified" ? "success" : "default";
866
- }
867
- function we(a) {
868
- return h.value ? "—" : a.path === null ? i("panel.no-address") : `/${a.path}`;
869
- }
870
- const xe = f(
871
- () => N.value ? [{ key: "new", label: i("panel.new"), icon: "plus", primary: !0, run: () => {
872
- ce();
873
- } }] : []
874
- );
875
- return (a, d) => (p(), q("div", Pt, [
876
- y(e(Ye), {
877
- view: b.value,
878
- "onUpdate:view": d[5] || (d[5] = (c) => b.value = c),
879
- title: n.value,
880
- views: Q.value,
881
- actions: xe.value,
882
- padding: "sm"
883
- }, {
884
- default: v(() => [
885
- A("div", Mt, [
886
- y(e(Pe), {
887
- modelValue: j.value,
888
- "onUpdate:modelValue": d[0] || (d[0] = (c) => j.value = c),
889
- class: "wx-recipes__search",
890
- size: "sm",
891
- clearable: "",
892
- placeholder: e(i)("panel.search"),
893
- "aria-label": e(i)("panel.search")
894
- }, {
895
- prefix: v(() => [
896
- y(e(_e), {
897
- name: "search",
898
- size: "sm"
899
- })
900
- ]),
901
- _: 1
902
- }, 8, ["modelValue", "placeholder", "aria-label"]),
903
- y(e(ke), {
904
- "model-value": B.value,
905
- options: X(g.value.categories),
906
- placeholder: e(i)("panel.any-category"),
907
- "aria-label": e(i)("panel.filter-category"),
908
- clearable: "",
909
- size: "sm",
910
- "onUpdate:modelValue": d[1] || (d[1] = (c) => ie("category", c))
911
- }, null, 8, ["model-value", "options", "placeholder", "aria-label"]),
912
- y(e(ke), {
913
- "model-value": F.value,
914
- options: X(g.value.nutrients),
915
- placeholder: e(i)("panel.any-nutrient"),
916
- "aria-label": e(i)("panel.filter-nutrient"),
917
- clearable: "",
918
- size: "sm",
919
- "onUpdate:modelValue": d[2] || (d[2] = (c) => ie("nutrient", c))
920
- }, null, 8, ["model-value", "options", "placeholder", "aria-label"]),
921
- g.value.services !== null ? (p(), k(e(ke), {
922
- key: 0,
923
- "model-value": O.value,
924
- options: X(g.value.services),
925
- placeholder: e(i)("panel.any-service"),
926
- "aria-label": e(i)("panel.filter-service"),
927
- clearable: "",
928
- size: "sm",
929
- "onUpdate:modelValue": d[3] || (d[3] = (c) => ie("service", c))
930
- }, null, 8, ["model-value", "options", "placeholder", "aria-label"])) : D("", !0)
931
- ]),
932
- R.value && r.value.length === 0 ? (p(), k(e(ve), {
933
- key: 0,
934
- class: "wx-recipes__loading",
935
- rows: 5
936
- })) : r.value.length === 0 ? (p(), k(e(Ee), {
937
- key: 1,
938
- title: me.value,
939
- description: z.value ? e(i)("panel.empty-help") : void 0
940
- }, null, 8, ["title", "description"])) : (p(), q(ee, { key: 2 }, [
941
- y(e(xt), {
942
- modelValue: r.value,
943
- "onUpdate:modelValue": d[4] || (d[4] = (c) => r.value = c),
944
- class: Te(["wx-recipes__list", { "is-loading": R.value }]),
945
- plain: "",
946
- "item-key": "id",
947
- handle: T.value ? "grip" : "row",
948
- "item-label": (c) => c.title,
949
- disabled: !T.value,
950
- onMove: J
951
- }, {
952
- default: v(({ item: c }) => [
953
- (p(), k(lt(h.value ? "div" : "router-link"), ot({ class: "wx-recipe-row" }, h.value ? {} : { to: { path: `${l.base}/${c.id}`, query: e(o).query } }), {
954
- default: v(() => [
955
- A("span", {
956
- class: Te(["wx-recipe-row__cover", { "is-empty": !c.cover?.thumb }])
957
- }, [
958
- c.cover?.thumb ? (p(), q("img", {
959
- key: 0,
960
- src: c.cover.thumb,
961
- alt: "",
962
- loading: "lazy"
963
- }, null, 8, Ot)) : (p(), k(e(_e), {
964
- key: 1,
965
- name: "image",
966
- size: "sm"
967
- }))
968
- ], 2),
969
- A("span", Ut, [
970
- A("span", Bt, x(c.title), 1),
971
- A("span", Lt, [
972
- $(x(we(c)), 1),
973
- h.value ? D("", !0) : (p(), k(e(et), {
974
- key: 0,
975
- locale: c.address_locale
976
- }, null, 8, ["locale"]))
977
- ])
978
- ]),
979
- A("span", Ht, [
980
- A("span", Ft, [
981
- (p(!0), q(ee, null, qe(c.categories, (re, de) => (p(), q(ee, {
982
- key: re.id
983
- }, [
984
- de === 0 ? (p(), k(e(_t), {
985
- key: 0,
986
- content: e(i)("panel.main-category")
987
- }, {
988
- default: v(() => [
989
- y(e(Z), {
990
- size: "sm",
991
- round: "",
992
- type: "primary"
993
- }, {
994
- default: v(() => [
995
- $(x(re.title), 1)
996
- ]),
997
- _: 2
998
- }, 1024)
999
- ]),
1000
- _: 2
1001
- }, 1032, ["content"])) : (p(), k(e(Z), {
1002
- key: 1,
1003
- size: "sm",
1004
- round: ""
1005
- }, {
1006
- default: v(() => [
1007
- $(x(re.title), 1)
1008
- ]),
1009
- _: 2
1010
- }, 1024))
1011
- ], 64))), 128))
1012
- ]),
1013
- A("span", Kt, [
1014
- c.minutes ? (p(), q("span", Jt, [
1015
- y(e(_e), {
1016
- name: "clock",
1017
- size: "sm"
1018
- }),
1019
- $(x(e(Dt)(c.minutes, e(i))), 1)
1020
- ])) : D("", !0),
1021
- y(e(Z), {
1022
- type: H(c.status),
1023
- dot: "",
1024
- size: "sm"
1025
- }, {
1026
- default: v(() => [
1027
- $(x(e(i)(`panel.status-${c.status}`)), 1)
1028
- ]),
1029
- _: 2
1030
- }, 1032, ["type"]),
1031
- c.status === "modified" ? (p(), k(e(Z), {
1032
- key: 1,
1033
- type: "primary",
1034
- size: "sm",
1035
- round: ""
1036
- }, {
1037
- default: v(() => [
1038
- $(x(e(i)("panel.edits")), 1)
1039
- ]),
1040
- _: 1
1041
- })) : D("", !0)
1042
- ])
1043
- ])
1044
- ]),
1045
- _: 2
1046
- }, 1040))
1047
- ]),
1048
- actions: v(({ item: c }) => [
1049
- y(e(Ze), {
1050
- actions: be(c),
1051
- label: c.title
1052
- }, null, 8, ["actions", "label"])
1053
- ]),
1054
- _: 1
1055
- }, 8, ["modelValue", "class", "handle", "item-label", "disabled"]),
1056
- N.value && !h.value ? (p(), k(e(kt), {
1057
- key: 0,
1058
- class: "wx-recipes__note",
1059
- type: "info",
1060
- description: K.value
1061
- }, null, 8, ["description"])) : D("", !0)
1062
- ], 64))
1063
- ]),
1064
- _: 1
1065
- }, 8, ["view", "title", "views", "actions"])
1066
- ]));
1067
- }
1068
- }), jt = /* @__PURE__ */ Re(Gt, [["__scopeId", "data-v-27e46b1b"]]);
1069
- function Qt(u = "/recipes") {
1070
- return {
1071
- api: "recipes/categories",
1072
- path: `${u}/categories`,
1073
- name: "webx.recipes.categories",
1074
- module: "recipe-categories",
1075
- screen: "recipes.category-form",
1076
- manage: "recipes.categories.manage",
1077
- count: "recipes_count",
1078
- // A category has no order of its own inside it (decision 4): the link shows what is in it.
1079
- items: (l) => ({ path: u, query: { category: String(l) } }),
1080
- words: {
1081
- new: "webx-recipes::category.new",
1082
- empty: "webx-recipes::category.empty",
1083
- "empty-help": "webx-recipes::category.empty-help",
1084
- order: "webx-recipes::category.order",
1085
- hidden: "webx-recipes::category.hidden",
1086
- "no-address": "webx-recipes::category.no-address",
1087
- count: "webx-recipes::category.recipes",
1088
- "show-items": "webx-recipes::category.show-recipes",
1089
- "delete-blocked": "webx-recipes::category.delete-blocked",
1090
- "delete-text": "webx-recipes::category.delete-text",
1091
- deleted: "webx-recipes::category.deleted",
1092
- saved: "webx-recipes::category.saved",
1093
- "field-title": "webx-recipes::category.field-title",
1094
- "field-slug": "webx-recipes::category.field-slug",
1095
- "address-moving": "webx-recipes::category.address-moving"
1096
- }
1097
- };
1098
- }
1099
- function Xt(u = "/recipes") {
1100
- return {
1101
- api: "recipes/nutrients",
1102
- path: `${u}/nutrients`,
1103
- name: "webx.recipes.nutrients",
1104
- module: "recipe-nutrients",
1105
- screen: "recipes.nutrient-form",
1106
- manage: "recipes.categories.manage",
1107
- count: "recipes_count",
1108
- items: (l) => ({ path: u, query: { nutrient: String(l) } }),
1109
- words: {
1110
- new: "webx-recipes::nutrient.new",
1111
- empty: "webx-recipes::nutrient.empty",
1112
- "empty-help": "webx-recipes::nutrient.empty-help",
1113
- order: "webx-recipes::nutrient.order",
1114
- hidden: "webx-recipes::nutrient.hidden",
1115
- count: "webx-recipes::nutrient.recipes",
1116
- "show-items": "webx-recipes::nutrient.show-recipes",
1117
- "delete-blocked": "webx-recipes::nutrient.delete-blocked",
1118
- "delete-text": "webx-recipes::nutrient.delete-text",
1119
- deleted: "webx-recipes::nutrient.deleted",
1120
- saved: "webx-recipes::nutrient.saved",
1121
- "field-title": "webx-recipes::nutrient.field-title"
1122
- }
1123
- };
1124
- }
1125
- function aa(u = {}) {
1126
- const l = u.path ?? "/recipes";
1127
- return [
1128
- {
1129
- id: "recipes",
1130
- path: l,
1131
- routes: [
1132
- { path: l, name: "webx.recipes", component: jt, props: { base: l } },
1133
- {
1134
- path: `${l}/:id(\\d+)`,
1135
- name: "webx.recipes.edit",
1136
- component: Vt,
1137
- props: { base: l }
1138
- }
1139
- ],
1140
- /*
1141
- * The one part of `recipes.form` only this module can draw. Everything else on it is the
1142
- * panel's (`wx-slug`, `wx-categories`, `wx-relations`, `wx-gallery`, `wx-rich-text`).
1143
- */
1144
- types: {
1145
- "wx-recipe-history": { component: At, kind: "display" }
1146
- }
1147
- },
1148
- {
1149
- id: "recipe-categories",
1150
- path: `${l}/categories`,
1151
- routes: Se(Qt(l))
1152
- },
1153
- {
1154
- id: "recipe-nutrients",
1155
- path: `${l}/nutrients`,
1156
- routes: Se(Xt(l))
1157
- }
1158
- ];
1159
- }
1
+ import "vue";
2
+ import { R as a, _ as r, W as p, b as t, d as c, c as o, f as R, p as d, r as g, e as E, g as x, h as W, i as n, j as P } from "./index-rwJ6c36l.js";
1160
3
  export {
1161
- Ce as RECIPES_API,
1162
- Et as WxRecipeCreateDialog,
1163
- Vt as WxRecipeEditorPage,
1164
- At as WxRecipeHistory,
1165
- jt as WxRecipesPage,
1166
- fe as createRecipesApi,
1167
- Dt as formatMinutes,
1168
- Tt as provideRecipeEditor,
1169
- Qt as recipeCategoriesOptions,
1170
- Me as recipeEditorKey,
1171
- Xt as recipeNutrientsOptions,
1172
- aa as recipes,
1173
- Rt as recipesMessages,
1174
- $t as useRecipeEditor
4
+ a as RECIPES_API,
5
+ r as WxRecipeCreateDialog,
6
+ p as WxRecipeEditorPage,
7
+ t as WxRecipeHistory,
8
+ c as WxRecipesPage,
9
+ o as createRecipesApi,
10
+ R as formatMinutes,
11
+ d as provideRecipeEditor,
12
+ g as recipeCategoriesOptions,
13
+ E as recipeEditorKey,
14
+ x as recipeNutrientsOptions,
15
+ W as recipes,
16
+ n as recipesMessages,
17
+ P as useRecipeEditor
1175
18
  };
1176
19
  //# sourceMappingURL=index.js.map