@webx-ui/module-catalog 0.4.0 → 0.5.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/editor.d.ts +7 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +4 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/api.ts","../src/errors.ts","../src/messages.ts","../src/i18n.ts","../src/store.ts","../src/CategoryCreateDialog.vue","../src/paths.ts","../src/CategoriesPage.vue","../src/editor.ts","../src/CategoryEditorPage.vue","../src/CategoryField.vue","../src/DeletedPage.vue","../src/FacetsField.vue","../src/video.ts","../src/galleryVideo.ts","../src/VideoLinkDialog.vue","../src/VideoProgress.vue","../src/GalleryField.vue","../src/ProductEditorPage.vue","../src/BulkParamsDialog.vue","../src/BulkBar.vue","../src/tones.ts","../src/ColumnValue.vue","../src/ProductCreateDialog.vue","../src/filters.ts","../src/ProductsPage.vue","../src/ToneField.vue","../src/module.ts","../src/dictionaries.ts"],"sourcesContent":["import { readCookie, type AdminContext } from '@webx-ui/module-admin'\nimport type { Paginated } from '@webx-ui/core'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport type {\n BulkActionInfo,\n BulkRun,\n BulkSelection,\n CategoryDetail,\n CategoryNode,\n CategoryRow,\n DeletedCategory,\n DeletedKind,\n DeletedProduct,\n FacetInfo,\n FacetRegistryAnswer,\n SortInfo,\n ProductDetail,\n ProductImage,\n ProductQuery,\n QueuedVideo,\n ProductRow,\n ProductsPage,\n} from './types'\n\nexport interface CatalogApi {\n products(query?: ProductQuery): Promise<ProductsPage>\n product(id: number): Promise<ProductDetail>\n /** The values of `catalog.product-form`; a refused field comes back as a 422 under its name. */\n createProduct(values: ScreenModel): Promise<ProductDetail>\n saveProduct(id: number, values: ScreenModel): Promise<ProductDetail>\n removeProduct(id: number): Promise<void>\n restoreProduct(id: number): Promise<ProductRow>\n\n /** A file off the disk, with how far it has got; or an address the server fetches itself. */\n addImage(\n id: number,\n source: File | string,\n onProgress?: (percent: number) => void,\n ): Promise<ProductImage>\n /** The whole gallery in its order, with every picture's words (§11.2). */\n saveImages(\n id: number,\n images: Pick<ProductImage, 'id' | 'alt' | 'title'>[],\n ): Promise<ProductImage[]>\n removeImage(id: number, image: number): Promise<void>\n /**\n * An address the server fetches: a picture or a YouTube video comes back as the new row, a\n * direct link to a video file as `QueuedVideo` — the row appears once the queue has it.\n */\n fetchImage(id: number, url: string): Promise<ProductImage | QueuedVideo>\n /** A finished chunked upload (`upload` is its id) or an address, onto a picture of the gallery. */\n attachVideo(\n id: number,\n image: number,\n source: { upload: string; duration?: number | null } | { url: string },\n ): Promise<ProductImage | QueuedVideo>\n /** The file, if it is one, is deleted at once. */\n detachVideo(id: number, image: number): Promise<ProductImage>\n\n categories(): Promise<CategoryNode[]>\n category(id: number): Promise<CategoryDetail>\n createCategory(values: ScreenModel, parent: number | null): Promise<CategoryDetail>\n saveCategory(id: number, values: ScreenModel): Promise<CategoryDetail>\n /** Under `parent` (null — the top), before `before` (null — last). Answers with the whole tree. */\n moveCategory(id: number, parent: number | null, before: number | null): Promise<CategoryNode[]>\n removeCategory(id: number): Promise<void>\n restoreCategory(id: number): Promise<CategoryRow>\n\n /** The registry of facets, with the sorts the list is offered. */\n facets(): Promise<FacetRegistryAnswer>\n\n /** The bulk actions this administrator may start, with what each asks for (§11.4). */\n bulkActions(): Promise<BulkActionInfo[]>\n /** Done at once when small (`id` null in the answer), queued otherwise. */\n startBulk(\n action: string,\n params: Record<string, unknown>,\n selection: BulkSelection,\n ): Promise<BulkRun>\n bulkRun(id: number): Promise<BulkRun>\n\n deleted(\n kind: 'products',\n query?: { q?: string; page?: number; per_page?: number },\n ): Promise<Paginated<DeletedProduct>>\n deleted(\n kind: 'categories',\n query?: { q?: string; page?: number; per_page?: number },\n ): Promise<Paginated<DeletedCategory>>\n deleted(\n kind: DeletedKind,\n query?: { q?: string; page?: number; per_page?: number },\n ): Promise<Paginated<DeletedProduct | DeletedCategory>>\n}\n\n/** The path of the catalogue under the panel's API. */\nexport const CATALOG_API = 'catalog'\n\n/**\n * The list's query as Laravel reads nested parameters: `facets[brand][]=3`,\n * `facets[price][min]=100`. Every facet's choice is a list, a toggle's too — on is\n * `facets[in-stock][]=1`, the value the storefront's own links send; a bare `facets[in-stock]=1`\n * is refused. Empty choices are left out, so a filter that was opened and closed again asks for\n * nothing.\n */\nexport function productSearch(query: ProductQuery): URLSearchParams {\n const search = new URLSearchParams()\n\n if (query.q) search.set('q', query.q)\n if (query.state) search.set('state', query.state)\n if (query.sort && query.sort !== 'default') search.set('sort', query.sort)\n if (query.page && query.page > 1) search.set('page', String(query.page))\n if (query.per_page) search.set('per_page', String(query.per_page))\n\n for (const [key, choice] of Object.entries(query.facets ?? {})) {\n if (choice === true) {\n search.set(`facets[${key}][]`, '1')\n } else if (Array.isArray(choice)) {\n for (const value of choice) search.append(`facets[${key}][]`, String(value))\n } else {\n if (choice.min != null) search.set(`facets[${key}][min]`, String(choice.min))\n if (choice.max != null) search.set(`facets[${key}][max]`, String(choice.max))\n }\n }\n\n return search\n}\n\n/**\n * The upload is the one request that is not JSON, and the one worth a progress bar: a phone\n * sending a twelve-megapixel photo over a shop's wifi takes long enough for \"is it doing\n * anything?\". XHR for that, as `module-media` does it.\n */\nfunction upload<T>(admin: AdminContext, url: string, file: File, onProgress?: (p: number) => void) {\n const body = new FormData()\n body.append('file', file)\n\n return new Promise<T>((resolve, reject) => {\n const request = new XMLHttpRequest()\n\n request.open('POST', url)\n request.responseType = 'json'\n request.withCredentials = true\n request.setRequestHeader('Accept', 'application/json')\n request.setRequestHeader('X-Requested-With', 'XMLHttpRequest')\n // The panel's own client sends the language on every request; this one is not that client,\n // and without it a refusal comes back in English under a Russian panel.\n request.setRequestHeader('X-Webx-Locale', admin.i18n.state.locale)\n\n const token = readCookie('XSRF-TOKEN')\n if (token) request.setRequestHeader('X-XSRF-TOKEN', token)\n\n request.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) onProgress?.(Math.round((event.loaded / event.total) * 100))\n })\n\n request.addEventListener('load', () => {\n const answer = request.response as { data?: T; message?: string } | null\n\n if (request.status >= 200 && request.status < 300 && answer?.data) {\n resolve(answer.data)\n\n return\n }\n\n reject(\n Object.assign(new Error(answer?.message ?? 'Upload failed'), {\n status: request.status,\n body: answer,\n }),\n )\n })\n\n request.addEventListener('error', () => reject(new Error('Upload failed')))\n request.send(body)\n })\n}\n\nexport function createCatalogApi(admin: AdminContext): CatalogApi {\n const base = `${admin.apiPath}/${CATALOG_API}`\n const data = <T>(body: { data: T }): T => body.data\n const withQuery = (path: string, search: URLSearchParams) =>\n search.size > 0 ? `${path}?${search}` : path\n\n return {\n products: (query = {}) =>\n admin.http.get<ProductsPage>(withQuery(`${base}/products`, productSearch(query))),\n product: (id) => admin.http.get<{ data: ProductDetail }>(`${base}/products/${id}`).then(data),\n createProduct: (values) =>\n admin.http.post<{ data: ProductDetail }>(`${base}/products`, { values }).then(data),\n saveProduct: (id, values) =>\n admin.http.put<{ data: ProductDetail }>(`${base}/products/${id}`, { values }).then(data),\n removeProduct: (id) => admin.http.delete<void>(`${base}/products/${id}`).then(() => undefined),\n restoreProduct: (id) =>\n admin.http.post<{ data: ProductRow }>(`${base}/products/${id}/restore`, {}).then(data),\n\n addImage: (id, source, onProgress) =>\n typeof source === 'string'\n ? admin.http\n .post<{ data: ProductImage }>(`${base}/products/${id}/images`, { url: source })\n .then(data)\n : upload<ProductImage>(admin, `${base}/products/${id}/images`, source, onProgress),\n saveImages: (id, images) =>\n admin.http\n .put<{ data: ProductImage[] }>(`${base}/products/${id}/images`, {\n images: images.map(({ id: image, alt, title }) => ({ id: image, alt, title })),\n })\n .then(data),\n removeImage: (id, image) =>\n admin.http.delete<void>(`${base}/products/${id}/images/${image}`).then(() => undefined),\n fetchImage: (id, url) =>\n admin.http\n .post<{ data: ProductImage | QueuedVideo }>(`${base}/products/${id}/images`, { url })\n .then(data),\n attachVideo: (id, image, source) =>\n admin.http\n .post<{\n data: ProductImage | QueuedVideo\n }>(`${base}/products/${id}/images/${image}/video`, source)\n .then(data),\n detachVideo: (id, image) =>\n admin.http\n .delete<{ data: ProductImage }>(`${base}/products/${id}/images/${image}/video`)\n .then(data),\n\n categories: () => admin.http.get<{ data: CategoryNode[] }>(`${base}/categories`).then(data),\n category: (id) =>\n admin.http.get<{ data: CategoryDetail }>(`${base}/categories/${id}`).then(data),\n createCategory: (values, parent) =>\n admin.http\n .post<{ data: CategoryDetail }>(`${base}/categories`, { values, parent_id: parent })\n .then(data),\n saveCategory: (id, values) =>\n admin.http.put<{ data: CategoryDetail }>(`${base}/categories/${id}`, { values }).then(data),\n moveCategory: (id, parent, before) =>\n admin.http\n .post<{ data: CategoryNode[] }>(`${base}/categories/${id}/move`, {\n parent_id: parent,\n before_id: before,\n })\n .then(data),\n removeCategory: (id) =>\n admin.http.delete<void>(`${base}/categories/${id}`).then(() => undefined),\n restoreCategory: (id) =>\n admin.http.post<{ data: CategoryRow }>(`${base}/categories/${id}/restore`, {}).then(data),\n\n facets: () =>\n admin.http\n .get<{ data: FacetInfo[]; meta?: { sorts?: SortInfo[] } }>(`${base}/facets`)\n .then((body) => ({ facets: body.data, sorts: body.meta?.sorts ?? [] })),\n\n bulkActions: () => admin.http.get<{ data: BulkActionInfo[] }>(`${base}/bulk`).then(data),\n startBulk: (action, params, selection) =>\n admin.http.post<{ data: BulkRun }>(`${base}/bulk`, { action, params, selection }).then(data),\n bulkRun: (id) => admin.http.get<{ data: BulkRun }>(`${base}/bulk/${id}`).then(data),\n\n deleted: ((kind: DeletedKind, query: { q?: string; page?: number; per_page?: number } = {}) => {\n const search = new URLSearchParams({ type: kind })\n\n if (query.q) search.set('q', query.q)\n if (query.page && query.page > 1) search.set('page', String(query.page))\n if (query.per_page) search.set('per_page', String(query.per_page))\n\n return admin.http.get(`${base}/deleted?${search}`)\n }) as CatalogApi['deleted'],\n }\n}\n","/**\n * The first refusal of a field, whether the server named the field (`name`) or one language of it\n * (`name.en`). Matched on the name and a dot, not on the first dot of the key: names with a dot in\n * them exist (`stock.status`), and `name.en` must not be read as the field `name.en`.\n */\nexport function firstError(errors: Record<string, string[]>, field: string): string | undefined {\n const own = errors[field]?.[0]\n\n if (own !== undefined) return own\n\n const key = Object.keys(errors).find((name) => name.startsWith(`${field}.`))\n\n return key === undefined ? undefined : errors[key]?.[0]\n}\n","import type { Messages } from '@webx-ui/module-admin'\n\n/**\n * The English this package speaks on its own, before the server's dictionary arrives, or without\n * one. The server ships the same lines under `webx-catalog::` and overrides these;\n * `messages.test.ts` keeps the two sets of keys equal.\n *\n * `module`, `states`, `product`, `category` and `units` are the server's groups, which the panel\n * reads too — a column called \"Article number\" in the list and \"Article number\" on the form\n * are one word. `panel` is what only the panel says.\n */\nexport const catalogMessages: Record<string, Messages> = {\n module: {\n title: 'Products',\n group: 'Catalog',\n products: 'Products',\n categories: 'Categories',\n dictionaries: 'Dictionaries',\n deleted: 'Deleted',\n product: 'Product',\n category: 'Category',\n },\n states: {\n published: 'Published',\n unpublished: 'Unpublished',\n deleted: 'Deleted',\n },\n product: {\n name: 'Name',\n slug: 'Address',\n sku: 'Article number',\n barcode: 'Barcode',\n summary: 'Summary',\n description: 'Description',\n category_id: 'Main category',\n category: 'Main category',\n categories: 'Additional categories',\n price: 'Price',\n old_price: 'Old price',\n unit: 'Unit',\n priority: 'Priority',\n is_published: 'Published',\n images: 'Pictures',\n 'no-category': 'No category',\n },\n category: {\n name: 'Name',\n slug: 'Address',\n description: 'Text',\n cover: 'Picture',\n cover_id: 'Picture',\n is_published: 'Published',\n parent: 'Parent category',\n facets: 'Filters',\n },\n units: {\n pcs: 'pcs',\n kg: 'kg',\n g: 'g',\n m: 'm',\n m2: 'm²',\n m3: 'm³',\n l: 'l',\n pack: 'pack',\n set: 'set',\n },\n panel: {\n create: 'Create',\n cancel: 'Cancel',\n save: 'Save',\n saved: 'Saved.',\n 'save-failed': 'Not saved: what is wrong is said under the fields in red.',\n untitled: 'Untitled',\n open: 'Open',\n 'open-on-site': 'Open on the site',\n publish: 'Publish',\n unpublish: 'Take off the site',\n delete: 'Delete',\n restore: 'Restore',\n 'leave-title': 'Leave without saving?',\n 'leave-text': 'What you changed is not on the server.',\n leave: 'Leave',\n 'address-moving':\n 'The address is changing. The old one keeps working and leads to the new one.',\n\n 'new-product': 'New product',\n 'search-products': 'Search by name or article number',\n 'empty-products': 'No products yet.',\n 'empty-products-help': 'A product lives in one main category and any number of others.',\n 'empty-search': 'Nothing matches that.',\n 'view-all': 'All',\n 'view-published': 'Published',\n 'view-unpublished': 'Unpublished',\n 'view-no-category': 'No category',\n 'column-product': 'Product',\n 'column-state': 'State',\n 'column-updated': 'Changed',\n invisible: 'Published, but no category of it is on the site, so nobody sees it.',\n sort: 'Order',\n 'sort-default': 'By priority, then newest',\n 'range-from': 'From',\n 'range-to': 'To',\n any: 'Any',\n selected: 'Selected: :count',\n 'select-clear': 'Clear the selection',\n 'bulk-actions': 'Actions',\n 'select-all-found': 'Select everything found: :count',\n 'selected-all': 'Selected everything found: :count',\n 'bulk-apply': 'Apply',\n 'bulk-param-empty': 'Nothing — takes it off',\n 'bulk-confirm-title': 'Apply «:action»?',\n 'bulk-confirm-text': 'Products it applies to: :count',\n 'bulk-running': 'Running in the background. Done :done of :total',\n 'bulk-done': 'Done. Products: :count',\n 'bulk-refused': 'Refused: :count',\n 'bulk-stopped': 'The run stopped. Done so far: :count',\n 'bulk-errors': 'What was refused',\n 'bulk-close': 'Close',\n 'bulk-nothing': 'None of the selected products fit this action.',\n 'product-trail': 'Where this product sits',\n 'delete-product-title': 'Delete “:name”?',\n 'delete-product-text':\n 'It goes to «Deleted» and comes off the site. Its address leads to its category from then on.',\n 'product-deleted': 'The product is in «Deleted».',\n 'product-restored': 'The product is back.',\n 'product-restored-bare':\n 'The product is back without a category and unpublished: its category is deleted.',\n 'product-published': 'The product is on the site.',\n 'product-unpublished': 'The product is off the site.',\n 'sku-open': 'Open that product',\n 'sku-open-deleted': 'Find it in «Deleted»',\n\n 'gallery-add': 'Add pictures',\n 'gallery-hint': 'JPEG, PNG, WebP or GIF. The first picture is the main one.',\n 'gallery-url': 'Upload from an address',\n 'gallery-url-add': 'Fetch',\n 'gallery-empty': 'No pictures yet.',\n 'gallery-main': 'Main',\n 'gallery-alt': 'Alternative text',\n 'gallery-alt-help': 'What the picture shows, for search engines and screen readers.',\n 'gallery-title': 'Title',\n 'gallery-order': 'Drag to change the order. The first picture is the main one.',\n 'gallery-failed': 'The picture was not added.',\n 'gallery-remove-title': 'Delete this picture?',\n 'gallery-remove-text': 'The file is deleted from the disk at once.',\n 'gallery-removed': 'The picture is deleted.',\n 'gallery-reorder-failed': 'The new order was not saved.',\n 'gallery-captions-failed': 'The captions were not saved.',\n 'gallery-locked': 'Pictures are added to a product you may edit.',\n 'gallery-add-video': 'Add pictures or videos',\n 'gallery-hint-video':\n 'JPEG, PNG, WebP or GIF pictures, MP4 or WebM videos. The first picture is the main one.',\n 'gallery-wrong-type': 'Only JPEG, PNG, WebP and GIF pictures go into the gallery.',\n 'gallery-wrong-type-video':\n 'Only JPEG, PNG, WebP and GIF pictures and MP4 and WebM videos go into the gallery.',\n 'gallery-url-hint': 'The address of a JPEG, PNG, WebP or GIF picture.',\n 'gallery-url-hint-video':\n 'The address of a picture, a YouTube link, or a direct link to an MP4 or WebM file.',\n 'video-badge': 'Has a video',\n 'video-open': 'Open the video',\n 'video-attach': 'Attach',\n 'video-attach-file': 'Attach a video file…',\n 'video-attach-link': 'Attach a YouTube link…',\n 'video-attached': 'The video is attached.',\n 'video-remove': 'Remove the video',\n 'video-remove-title': 'Remove the video?',\n 'video-remove-file-text': 'The video file is deleted from the disk at once. The picture stays.',\n 'video-remove-link-text': 'The link to the video is dropped. The picture stays.',\n 'video-removed': 'The video is removed.',\n 'video-link-title': 'Attach a video by its link',\n 'video-link': 'Link',\n 'video-link-help': 'A YouTube link, or a direct link to an MP4 or WebM file.',\n 'video-link-wrong': 'This is neither a YouTube link nor a link to an MP4 or WebM file.',\n 'video-queued':\n 'The video is downloading on the server. It appears in the gallery when it is done — open the product again then.',\n 'video-wrong-type': 'Only MP4 and WebM videos.',\n 'video-too-large': 'The video is larger than :max.',\n 'video-unreadable':\n 'The browser cannot open this video, so there is no frame for a cover. Add a picture and attach the video to it.',\n 'video-placeholder':\n 'A plain cover. For a frame of the video: add a picture, attach the same link to it and delete this row.',\n 'video-waiting': 'Waits for the video before it.',\n 'video-poster': 'Taking a frame for the cover…',\n 'video-attaching': 'Attaching the video…',\n 'video-uploading': 'Uploading :name',\n 'video-sent': ':sent of :total',\n 'video-left': 'Time left: :time',\n 'video-paused': 'Paused',\n 'video-offline': 'Offline — continues when the connection is back',\n 'video-pause': 'Pause',\n 'video-resume': 'Resume',\n 'video-retry': 'Try again',\n 'video-dismiss': 'Dismiss',\n 'video-unfinished':\n 'The upload of :name did not finish: :sent of :total. Choose the same file to continue.',\n 'video-choose-again': 'Choose the file…',\n 'video-forget': 'Forget',\n 'video-other-file': 'That is another file. Choose :name (:size) to continue.',\n 'video-leave-title': 'Leave during the upload?',\n 'video-leave-text':\n 'The video stops going up. Choose the same file on this product again to continue where it stopped.',\n\n 'new-category': 'New category',\n 'add-child': 'Add a subcategory',\n 'search-categories': 'Search by name or address',\n 'empty-categories': 'No categories yet.',\n 'empty-categories-help':\n 'Categories are the shelves of the catalogue; each has an address of its own at the root of the site.',\n 'column-products': 'Products',\n 'category-hidden': 'Hidden: a category above it is unpublished.',\n 'category-order': 'Drag to move a category. Its address stays the same.',\n 'category-moved': 'The category is moved.',\n 'category-trail': 'Where this category sits',\n 'field-parent': 'Inside',\n 'parent-root': 'At the top level',\n 'show-products': 'Show its products',\n 'delete-category-title': 'Delete “:name”?',\n 'delete-category-text': 'It goes to «Deleted», and its address answers that it is gone.',\n 'category-deleted': 'The category is in «Deleted».',\n 'category-restored': 'The category is back.',\n 'category-published': 'The category is on the site.',\n 'category-unpublished': 'The category and everything under it is off the site.',\n 'pick-category': 'Choose a category',\n 'pick-categories': 'Choose categories',\n 'category-gone': 'deleted',\n\n 'facets-own': 'Own setting',\n 'facets-own-help': 'Which filters this category shows, and in which order.',\n 'facets-inherited': 'As in “:name”',\n 'facets-default': 'All filters, in their default order',\n 'facets-inherited-help':\n 'Switch it on to choose for this category. Categories under it take the setting unless they have their own.',\n 'facets-empty': 'No filters are installed.',\n 'facets-failed': 'The list of filters did not load.',\n 'facets-shown': 'Shown',\n 'facets-unknown': 'Its module is not installed',\n\n 'deleted-search': 'Search by name',\n 'deleted-empty': 'Nothing is deleted.',\n 'deleted-help': 'A deleted product keeps its pictures and comes back under its old address.',\n 'column-deleted': 'Deleted',\n 'column-parent': 'Was inside',\n 'restore-bare': 'Comes back without a category',\n },\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { catalogMessages } from './messages'\n\nconst seeded = new WeakSet<object>()\n\n/** Puts this package's English under the panel's dictionary, once per panel. */\nexport function useCatalogMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-catalog', catalogMessages)\n seeded.add(i18n)\n }\n } catch {\n // Outside a panel there is no dictionary to seed, and `useTranslate` falls back on its own.\n }\n}\n","import { computed, ref, type ComputedRef, type Ref } from 'vue'\nimport type { AdminContext } from '@webx-ui/module-admin'\nimport { createCatalogApi } from './api'\nimport type { CategoryNode, FacetInfo, FacetKind, FacetRegistryAnswer, SortInfo } from './types'\n\n/**\n * What several screens of the catalogue read and nobody should ask for twice: the tree of\n * categories and the registry of facets.\n *\n * The tree is read by the list's filter, by both category pickers of the product form and by the\n * tree screen; one panel, one copy, dropped whenever a category is created, saved, moved, deleted\n * or restored — the next reader asks again. Kept per panel rather than per module instance, so a\n * test that builds a panel of its own starts from nothing.\n */\ninterface Shelf<T> {\n value: Ref<T | null>\n pending: Promise<T> | null\n}\n\ninterface CatalogStore {\n tree: Shelf<CategoryNode[]>\n facets: Shelf<FacetRegistryAnswer>\n}\n\nconst stores = new WeakMap<AdminContext, CatalogStore>()\n\nfunction storeOf(admin: AdminContext): CatalogStore {\n let store = stores.get(admin)\n\n if (!store) {\n store = {\n tree: { value: ref(null), pending: null },\n facets: { value: ref(null), pending: null },\n }\n stores.set(admin, store)\n }\n\n return store\n}\n\nfunction fetchInto<T>(shelf: Shelf<T>, fetch: () => Promise<T>, force: boolean): Promise<T> {\n if (!force && shelf.value.value !== null) return Promise.resolve(shelf.value.value)\n if (!force && shelf.pending) return shelf.pending\n\n const pending = fetch()\n .then((value) => {\n if (shelf.pending === pending) shelf.value.value = value\n\n return value\n })\n .finally(() => {\n if (shelf.pending === pending) shelf.pending = null\n })\n\n shelf.pending = pending\n\n return pending\n}\n\nexport interface CategoryTree {\n /** `null` until the first answer. */\n nodes: Ref<CategoryNode[] | null>\n load(force?: boolean): Promise<CategoryNode[]>\n /** Put in what the server answered with — a move answers with the whole tree. */\n take(nodes: CategoryNode[]): void\n /** Forget it; the next reader asks again. */\n drop(): void\n}\n\nexport function useCategoryTree(admin: AdminContext): CategoryTree {\n const shelf = storeOf(admin).tree\n const api = createCatalogApi(admin)\n\n return {\n nodes: shelf.value,\n load: (force = false) => fetchInto(shelf, () => api.categories(), force),\n take: (nodes) => {\n shelf.pending = null\n shelf.value.value = nodes\n },\n drop: () => {\n shelf.pending = null\n shelf.value.value = null\n },\n }\n}\n\nexport interface FacetRegistry {\n facets: ComputedRef<FacetInfo[] | null>\n /** The orders the list offers, in the registry's order; empty until the answer. */\n sorts: ComputedRef<SortInfo[]>\n load(): Promise<FacetRegistryAnswer>\n}\n\n/**\n * The facets every category can show (§7.1) and the sorts of the list (§7.2) — one answer, since\n * the list needs both before it can draw its filter. The kind is lowercased on the way in: a panel\n * comparing with 'range' should not care how the server cased its enum.\n */\nexport function useFacetRegistry(admin: AdminContext): FacetRegistry {\n const shelf = storeOf(admin).facets\n const api = createCatalogApi(admin)\n\n return {\n facets: computed(() => shelf.value.value?.facets ?? null),\n sorts: computed(() => shelf.value.value?.sorts ?? []),\n load: () =>\n fetchInto(\n shelf,\n () =>\n api.facets().then((answer) => ({\n sorts: answer.sorts,\n facets: answer.facets.map((facet) => ({\n ...facet,\n kind: String(facet.kind).toLowerCase() as FacetKind,\n })),\n })),\n false,\n ),\n }\n}\n\n/** Every node of the tree, depth first, as the server ordered them. */\nexport function flatten(nodes: CategoryNode[]): CategoryNode[] {\n return nodes.flatMap((node) => [node, ...flatten(node.children ?? [])])\n}\n\n/** A node and the chain of its parents, the node first. Empty when it is not in the tree. */\nexport function ancestry(nodes: CategoryNode[], id: number): CategoryNode[] {\n const byId = new Map(flatten(nodes).map((node) => [node.id, node]))\n const chain: CategoryNode[] = []\n let current = byId.get(id)\n\n while (current) {\n chain.push(current)\n current = current.parent_id === null ? undefined : byId.get(current.parent_id)\n }\n\n return chain\n}\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport {\n useLocales,\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSpace,\n WxTreeSelect,\n type TreeSelectValue,\n} from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { firstError } from './errors'\nimport { useCatalogMessages } from './i18n'\nimport { useCategoryTree } from './store'\nimport type { CategoryDetail } from './types'\n\n/**\n * A new category: its name and where it hangs. Created unpublished — a shelf nobody filled is\n * not one to show visitors — at the end of its parent's children; the tree is where it is dragged\n * into place. The address is made of the name, and a taken one is refused under the name.\n */\nconst props = withDefaults(defineProps<{ parent?: number | null }>(), { parent: null })\n\nconst { open, resolve, dismiss } = useModal<CategoryDetail>()\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst tree = useCategoryTree(context)\nconst locales = useLocales()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n\nconst name = ref('')\nconst parent = ref<TreeSelectValue>(props.parent)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\nconst field = useTemplateRef<HTMLElement>('field')\n\nconst nodes = computed(() => tree.nodes.value ?? [])\n\nonMounted(async () => {\n void tree.load().catch(() => undefined)\n await nextTick()\n field.value?.querySelector('input')?.focus()\n})\n\nasync function submit(): Promise<void> {\n if (name.value.trim() === '' || saving.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n const under = typeof parent.value === 'number' ? parent.value : null\n\n resolve(\n await api.createCategory(\n { name: { [locales.active.value]: name.value.trim() }, is_published: false },\n under,\n ),\n )\n } catch (error) {\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-category')\" :width=\"480\">\n <wx-form-item\n :label=\"t('category.name')\"\n :error=\"firstError(errors, 'name') ?? firstError(errors, 'slug')\"\n required\n >\n <div ref=\"field\">\n <wx-input v-model=\"name\" :aria-label=\"t('category.name')\" @keyup.enter=\"submit\" />\n </div>\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.field-parent')\" :error=\"firstError(errors, 'parent_id')\">\n <wx-tree-select\n v-model=\"parent\"\n :nodes=\"nodes\"\n node-key=\"id\"\n label-key=\"name\"\n children-key=\"children\"\n show-path\n separator=\" / \"\n filterable\n clearable\n default-expand-all\n teleport\n :placeholder=\"t('panel.parent-root')\"\n />\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=\"name.trim() === ''\" @click=\"submit\">\n {{ t('panel.create') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","/**\n * The last segment of an address on the site, without slashes: `/laptops/` → `laptops`,\n * `https://shop.test/en/lamp-12` → `lamp-12`. The panel compares it with the slug being typed;\n * the language prefix and the host in front of it are not part of what the slug field edits.\n */\nexport function lastSegment(url: string | null | undefined): string | null {\n if (!url) return null\n\n let path: string\n\n try {\n path = new URL(url, 'http://panel.invalid').pathname\n } catch {\n return null\n }\n\n const segments = path.split('/').filter((segment) => segment !== '')\n\n return segments.length > 0 ? decodeURIComponent(segments[segments.length - 1]!) : null\n}\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\nimport { 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 createModal,\n toast,\n WxBadge,\n WxTable,\n WxText,\n type TableColumn,\n type TableNodeDropEvent,\n type TableState,\n type TableTreeOptions,\n} from '@webx-ui/core'\nimport CategoryCreateDialog from './CategoryCreateDialog.vue'\nimport { createCatalogApi } from './api'\nimport { useCatalogMessages } from './i18n'\nimport { lastSegment } from './paths'\nimport { flatten, useCategoryTree } from './store'\nimport type { CategoryDetail, CategoryNode } from './types'\n\n/**\n * The shelves of the catalogue: the whole tree at once, with how many products each holds\n * (§6.1), dragged into place.\n *\n * Whole, not a level at a time as the pages are: a catalogue has dozens of categories, not\n * thousands, and the counts are one query for all of them on the server. A drag moves a category\n * and everything under it; the addresses do not move with it — they are flat (decision 23) — so\n * no drop is ever questioned, unlike a page's.\n *\n * Publication is here as well as on the form, because taking a branch off the site is a thing\n * done to the tree: an unpublished category hides everything below it (§6.3), and the rows under\n * it say so.\n *\n * A search puts the tree away and lists the matches flat — a branch drawn for one match deep\n * inside it tells the reader nothing they asked about.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst tree = useCategoryTree(context)\nconst router = useRouter()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\nconst message = useErrorText()\n\nconst rows = ref<CategoryNode[]>([])\nconst loading = ref(true)\n/*\n * The tree opens every branch on its first render, and only then — so the table is mounted once\n * there is a tree to open, not on the empty one it would otherwise start from.\n */\nconst loaded = ref(false)\nconst search = ref('')\n\nconst create = createModal<CategoryDetail, { parent: number | null }>(CategoryCreateDialog)\n\nconst canManage = computed(() => context.can('catalog.manage'))\nconst canDelete = computed(() => context.can('catalog.delete'))\nconst asTree = computed(() => search.value.trim() === '')\n\nconst matches = computed(() => {\n const term = search.value.trim().toLowerCase()\n\n return flatten(rows.value)\n .filter(\n (node) =>\n node.name.toLowerCase().includes(term) || (node.slug ?? '').toLowerCase().includes(term),\n )\n .map((node) => ({ ...node, children: [] }))\n})\n\nconst data = computed(() => (asTree.value ? rows.value : matches.value))\n\nconst columns = computed<TableColumn<CategoryNode>[]>(() => [\n { key: 'name', label: t('category.name'), minWidth: 200 },\n { key: 'slug', label: t('category.slug'), width: 200, hideBelow: 620 },\n { key: 'products_count', label: t('panel.column-products'), width: 110, align: 'right' },\n { key: 'state', label: t('panel.column-state'), width: 150, hideBelow: 760 },\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right' },\n])\n\nconst treeOptions = computed<TableTreeOptions<CategoryNode> | undefined>(() =>\n asTree.value\n ? {\n childrenKey: 'children',\n defaultExpandAll: true,\n draggable: canManage.value,\n allowDrop: (drag, drop) => drag.id !== drop.id,\n }\n : undefined,\n)\n\n/** A copy: the table rearranges what it is given, and the cache is every picker's. */\nfunction show(nodes: CategoryNode[]): void {\n rows.value = nodes.map(plain)\n}\n\nfunction plain(node: CategoryNode): CategoryNode {\n return { ...node, children: (node.children ?? []).map(plain) }\n}\n\nasync function load(force = true): Promise<void> {\n loading.value = true\n\n try {\n show(await tree.load(force))\n loaded.value = true\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nfunction onState(state: TableState): void {\n search.value = state.search\n}\n\nfunction open(node: CategoryNode): void {\n void router.push(`${props.base}/categories/${node.id}`)\n}\n\nasync function add(parent: CategoryNode | null): Promise<void> {\n const detail = await create({ parent: parent?.id ?? null })\n\n if (!detail) return\n\n tree.drop()\n void router.push(`${props.base}/categories/${detail.category.id}`)\n}\n\n/**\n * The table has already put the row where it was dropped; the server is told the same thing in\n * its own terms — under which parent, before which sibling — and answers with the whole tree,\n * which is drawn as it says rather than as the drop left it.\n */\nasync function dropped(event: TableNodeDropEvent<CategoryNode>): Promise<void> {\n const parent = event.parent?.id ?? null\n const siblings = event.parent ? (event.parent.children ?? []) : rows.value\n const before = siblings[event.index + 1]?.id ?? null\n\n try {\n const answer = await api.moveCategory(event.row.id, parent, before)\n\n tree.take(answer)\n show(answer)\n toast.success(t('panel.category-moved'))\n } catch (error) {\n toast.danger(message(error))\n await load()\n }\n}\n\nasync function publish(node: CategoryNode, on: boolean): Promise<void> {\n try {\n await api.saveCategory(node.id, { is_published: on })\n toast.success(on ? t('panel.category-published') : t('panel.category-unpublished'))\n await load()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nasync function remove(node: CategoryNode): Promise<void> {\n const agreed = await confirm({\n title: t('panel.delete-category-title', { name: node.name }),\n message: t('panel.delete-category-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.removeCategory(node.id)\n toast.success(t('panel.category-deleted'))\n await load()\n } catch (error) {\n // Not empty: the server's sentence says how many products or subcategories are in the way.\n toast.danger(message(error))\n }\n}\n\nfunction productsOf(node: CategoryNode) {\n return { path: `${props.base}/products`, query: { 'f.category': String(node.id) } }\n}\n\nfunction actionsFor(node: CategoryNode): RowAction[] {\n const actions: RowAction[] = [\n { key: 'open', icon: 'edit', label: t('panel.open'), run: () => open(node) },\n {\n key: 'products',\n icon: 'list',\n label: t('panel.show-products'),\n run: () => void router.push(productsOf(node)),\n },\n ]\n\n if (node.url && node.visible) {\n actions.push({ key: 'site', icon: 'link', label: t('panel.open-on-site'), href: node.url })\n }\n\n if (canManage.value) {\n actions.push(\n { key: 'add', icon: 'plus', label: t('panel.add-child'), run: () => void add(node) },\n node.is_published\n ? {\n key: 'unpublish',\n icon: 'eye-off',\n label: t('panel.unpublish'),\n run: () => void publish(node, false),\n }\n : {\n key: 'publish',\n icon: 'eye',\n label: t('panel.publish'),\n run: () => void publish(node, true),\n },\n )\n }\n\n if (canDelete.value) {\n actions.push({\n key: 'delete',\n icon: 'trash',\n label: t('panel.delete'),\n danger: true,\n run: () => void remove(node),\n })\n }\n\n return actions\n}\n\nonMounted(() => void load(false))\n\nconst actions = computed<ScreenAction[]>(() => {\n const leads: ScreenAction[] = []\n\n if (canManage.value) {\n leads.push({\n key: 'new',\n label: t('panel.new-category'),\n icon: 'plus',\n primary: true,\n run: () => void add(null),\n })\n }\n\n if (canDelete.value) {\n leads.push({\n key: 'deleted',\n label: t('module.deleted'),\n icon: 'trash',\n menu: true,\n run: () => void router.push({ path: `${props.base}/deleted`, query: { type: 'categories' } }),\n })\n }\n\n return leads\n})\n</script>\n\n<template>\n <div class=\"wx-catalog-categories\">\n <wx-list-screen\n :title=\"t('module.categories')\"\n :subtitle=\"canManage && rows.length > 0 ? t('panel.category-order') : undefined\"\n :actions=\"actions\"\n >\n <wx-table\n :key=\"loaded ? 'tree' : 'waiting'\"\n :data=\"data\"\n :columns=\"columns\"\n row-key=\"id\"\n :tree=\"treeOptions\"\n searchable\n clickable\n hover\n flush\n layout=\"fixed\"\n :loading=\"loading\"\n :cards-below=\"0\"\n :search-placeholder=\"t('panel.search-categories')\"\n :empty-text=\"search ? t('panel.empty-search') : t('panel.empty-categories')\"\n :aria-label=\"t('module.categories')\"\n @row-click=\"open\"\n @state-change=\"onState\"\n @node-drop=\"dropped\"\n >\n <template #cell-name=\"{ row }\">\n <span class=\"wx-catalog-categories__name\" :class=\"{ 'is-hidden': !row.visible }\">\n {{ row.name }}\n </span>\n </template>\n\n <template #cell-slug=\"{ row }\">\n <wx-text v-if=\"row.slug\" mono size=\"sm\" tone=\"muted\" truncate>\n /{{ lastSegment(row.url) ?? row.slug }}/\n </wx-text>\n </template>\n\n <template #cell-products_count=\"{ row }\">\n <router-link\n v-if=\"row.products_count > 0\"\n class=\"wx-catalog-categories__count\"\n :to=\"productsOf(row)\"\n @click.stop\n >\n {{ row.products_count }}\n </router-link>\n <wx-text v-else size=\"sm\" tone=\"muted\">0</wx-text>\n </template>\n\n <template #cell-state=\"{ row }\">\n <wx-badge v-if=\"!row.is_published\" dot size=\"sm\">{{ t('states.unpublished') }}</wx-badge>\n <wx-badge\n v-else-if=\"!row.visible\"\n type=\"warning\"\n dot\n size=\"sm\"\n :title=\"t('panel.category-hidden')\"\n >\n {{ t('states.published') }}\n </wx-badge>\n <wx-badge v-else type=\"success\" dot size=\"sm\">{{ t('states.published') }}</wx-badge>\n </template>\n\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-categories {\n min-width: 0;\n}\n\n.wx-catalog-categories__name {\n font-weight: var(--wx-font-weight-medium);\n}\n\n/* Hidden by a parent: still here to be arranged, but plainly not what a visitor sees. */\n.wx-catalog-categories__name.is-hidden {\n color: var(--wx-text-muted);\n}\n\n.wx-catalog-categories__count {\n color: var(--wx-color-primary);\n font-variant-numeric: tabular-nums;\n text-decoration: none;\n}\n\n.wx-catalog-categories__count:hover {\n text-decoration: underline;\n}\n</style>\n","import { inject, provide, type InjectionKey, type Ref } from 'vue'\nimport type { GalleryVideo } from './galleryVideo'\nimport type { CategoryRow, ProductImage, ProductRow } from './types'\n\n/**\n * What the product editor knows and the nodes of `catalog.product-form` do not.\n *\n * The gallery needs it most: its pictures are not a value of the form but records of their own,\n * added, ordered and captioned by requests of their own (§11.2), so the node has to know which\n * product it is attached to and whether this administrator may write.\n */\nexport interface ProductEditorContext {\n /** The product as the server last answered it, or `null` while the first request is out. */\n product: Ref<ProductRow | null>\n /** The gallery in its order. The editor reads it with the product; the node keeps it. */\n images: Ref<ProductImage[]>\n /** Closed for writing: no permission, or a save in flight. */\n locked: Ref<boolean>\n /**\n * Video files on their way into the gallery. Kept up here rather than in the gallery field, whose\n * tab is taken down when another is opened — and an upload with it. The field makes its own\n * when an editor leaves it out.\n */\n video?: GalleryVideo\n}\n\nexport const productEditorKey: InjectionKey<ProductEditorContext> = Symbol('wx-catalog-product')\n\nexport function provideProductEditor(editor: ProductEditorContext): void {\n provide(productEditorKey, editor)\n}\n\n/** The editor above this node, or `null` outside one — a demo or a test draws a placeholder. */\nexport function useProductEditor(): ProductEditorContext | null {\n return inject(productEditorKey, null)\n}\n\n/**\n * What the category editor knows and `wx-catalog-facets` needs: which category it is, so that the\n * field can say whose setting it inherits — the parent's, the grandparent's, or nobody's (§6.2).\n */\nexport interface CategoryEditorContext {\n category: Ref<CategoryRow | null>\n /** Closed for writing — what the screen's form says to its own controls, for the ones it cannot. */\n locked: Ref<boolean>\n /**\n * The server's answer to \"whose facet setting does it show\" (`facets_from`): the ancestor, `null`\n * for its own or the default, `undefined` when the server did not say.\n */\n facetsFrom: Ref<{ id: number; name: string } | null | undefined>\n}\n\nexport const categoryEditorKey: InjectionKey<CategoryEditorContext> = Symbol('wx-catalog-category')\n\nexport function provideCatalogCategoryEditor(editor: CategoryEditorContext): void {\n provide(categoryEditorKey, editor)\n}\n\nexport function useCatalogCategoryEditor(): CategoryEditorContext | null {\n return inject(categoryEditorKey, null)\n}\n","<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 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 { provideCatalogCategoryEditor } from './editor'\nimport { useCatalogMessages } from './i18n'\nimport { lastSegment } from './paths'\nimport { ancestry, useCategoryTree } from './store'\nimport type { CategoryDetail, CategoryRow } from './types'\n\n/**\n * One category of the catalogue: a head, `catalog.category-form` under it, and one button.\n *\n * The shared category editor of the panel (`WxCategoryEditorPage`) is for flat lists with a\n * prefix; this one sits in a tree, at the root of the site (§4), and its trail is the branch it\n * hangs from. The «Filters» tab reads which category this is from here, to say whose setting it\n * inherits (§6.2).\n */\ndefineOptions({ name: 'WxCatalogCategoryEditor' })\n\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst tree = useCategoryTree(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}/categories`)\n\nconst category = ref<CategoryRow | null>(null)\nconst facetsFrom = ref<CategoryDetail['facets_from']>(undefined)\nconst values = ref<ScreenModel>({})\nconst snapshot = ref('')\n\nconst loading = ref(true)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\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\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, category.value?.name ?? '') || t('panel.untitled')\n )\n})\n\n/** Where it answers, as a reader would type it: `/laptops/`. */\nconst address = computed(() => {\n const segment = lastSegment(category.value?.url)\n\n return segment === null ? undefined : `/${segment}/`\n})\n\n/** The categories above this one, from the top down. */\nconst parents = computed(() =>\n category.value\n ? ancestry(tree.nodes.value ?? [], category.value.id)\n .slice(1)\n .reverse()\n : [],\n)\n\nprovideCatalogCategoryEditor({ category, locked, facetsFrom })\nprovideHistorySubject({ id: computed(() => category.value?.id ?? null) })\n\n/* The address of a category is flat at the root of the site at any depth (decision 23). */\nprovideRecordAddress({\n values,\n prefix: ref(''),\n path: computed(() => lastSegment(category.value?.url)),\n moving: () => t('panel.address-moving'),\n})\n\nfunction take(detail: CategoryDetail): void {\n category.value = detail.category\n facetsFrom.value = detail.facets_from\n values.value = detail.values\n snapshot.value = JSON.stringify(detail.values)\n}\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n const [detail] = await Promise.all([api.category(id.value), tree.load().catch(() => [])])\n\n take(detail)\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 (!category.value || saving.value || !canManage.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n take(await api.saveCategory(category.value.id, values.value))\n // A name, a publication or an address: every picker of the catalogue shows one of them.\n tree.drop()\n void tree.load().catch(() => undefined)\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 = category.value\n\n if (!row) return\n\n const agreed = await confirm({\n title: t('panel.delete-category-title', { name: row.name }),\n message: t('panel.delete-category-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.removeCategory(row.id)\n tree.drop()\n toast.success(t('panel.category-deleted'))\n snapshot.value = current.value\n void router.push(list.value)\n } catch (error) {\n // \"Not empty: products in it: 12\" — the server's sentence is the one worth reading here.\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\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 const row = category.value\n const leads: ScreenAction[] = []\n\n if (!row) return leads\n\n // A hidden category answers 404; a link to that would be a link to an error page.\n if (row.url && row.visible) {\n leads.push({ key: 'site', label: t('panel.open-on-site'), icon: 'link', href: row.url })\n }\n\n leads.push({\n key: 'products',\n label: t('panel.show-products'),\n icon: 'list',\n menu: true,\n run: () =>\n void router.push({\n path: `${props.base}/products`,\n query: { 'f.category': String(row.id) },\n }),\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-category-editor\" @keydown=\"onKeydown\">\n <template v-if=\"loading || !category\">\n <wx-skeleton class=\"wx-catalog-category-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=\"t('module.categories')\"\n :title=\"title\"\n :subtitle=\"address\"\n :actions=\"actions\"\n >\n <template #trail>\n <wx-breadcrumb size=\"sm\" :label=\"t('panel.category-trail')\">\n <wx-breadcrumb-item :as=\"'router-link'\" :to=\"list\">\n {{ t('module.categories') }}\n </wx-breadcrumb-item>\n <wx-breadcrumb-item\n v-for=\"parent in parents\"\n :key=\"parent.id\"\n :as=\"'router-link'\"\n :to=\"`${list}/${parent.id}`\"\n >\n {{ parent.name }}\n </wx-breadcrumb-item>\n <wx-breadcrumb-item current>{{ title }}</wx-breadcrumb-item>\n </wx-breadcrumb>\n </template>\n\n <template #title-after>\n <wx-badge v-if=\"!category.is_published\" dot>{{ t('states.unpublished') }}</wx-badge>\n <wx-badge v-else-if=\"!category.visible\" type=\"warning\" dot>\n {{ t('panel.category-hidden') }}\n </wx-badge>\n </template>\n </wx-screen-head>\n\n <wx-screen\n v-model=\"values\"\n name=\"catalog.category-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-category-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-category-editor__ghost {\n max-width: 420px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted } from 'vue'\nimport { useAdmin, useRecordAddress, useTranslate } from '@webx-ui/module-admin'\nimport { WxTreeSelect, type TreeSelectValue } from '@webx-ui/core'\nimport { useCatalogMessages } from './i18n'\nimport { useCategoryTree } from './store'\nimport type { CategoryNode } from './types'\n\n/**\n * `wx-catalog-category`: a category of the catalogue, or several with `props.multiple`.\n *\n * A tree and not a flat list, because the tree is how anybody finds a shelf — \"Laptops\" means\n * nothing until it is seen under \"Computers\" — and a single choice shows its whole path once\n * picked, for the same reason.\n *\n * The additional categories cannot include the main one (decision 2): the server would drop it\n * anyway, and a picker that lets somebody tick what will silently not be saved is a picker that\n * lies. The main one is read off the form the editor hosts.\n *\n * Nothing but `multiple` is declared: a wrapper that declared what `WxTreeSelect` takes would hand\n * it a `false` where the screen asked for nothing (CLAUDE.md §4).\n */\ndefineOptions({ name: 'WxCatalogCategoryField', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<{ multiple?: boolean }>(), { multiple: false })\n\nconst value = defineModel<number | number[] | null>({ default: null })\n\nconst admin = useAdmin()\nconst tree = useCategoryTree(admin)\nconst address = useRecordAddress()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n\ntype Choice = CategoryNode & { locked?: boolean }\n\n/** The main category, for the list of the others to leave out. */\nconst main = computed(() => {\n const chosen = address?.values.value.category_id\n\n return typeof chosen === 'number' ? chosen : null\n})\n\nconst nodes = computed<Choice[]>(() => {\n const mark = (list: CategoryNode[]): Choice[] =>\n list.map((node) => ({\n ...node,\n locked: props.multiple && node.id === main.value,\n children: mark(node.children ?? []),\n }))\n\n return mark(tree.nodes.value ?? [])\n})\n\nconst model = computed<TreeSelectValue>({\n get: () => {\n if (props.multiple) return Array.isArray(value.value) ? value.value : []\n\n return typeof value.value === 'number' ? value.value : null\n },\n set: (next) => {\n if (props.multiple) {\n const ids = (Array.isArray(next) ? next : []).map(Number)\n value.value = ids.filter((id) => id !== main.value)\n\n return\n }\n\n value.value = next === null || next === undefined || Array.isArray(next) ? null : Number(next)\n },\n})\n\nonMounted(() => {\n // A tree that did not arrive leaves the chosen ids where they are, drawn by number; the form\n // still saves what it opened with.\n void tree.load().catch(() => undefined)\n})\n</script>\n\n<template>\n <wx-tree-select\n v-bind=\"$attrs\"\n v-model=\"model\"\n class=\"wx-catalog-category\"\n :nodes=\"nodes\"\n :multiple=\"props.multiple\"\n :check-strictly=\"props.multiple\"\n node-key=\"id\"\n label-key=\"name\"\n children-key=\"children\"\n disabled-key=\"locked\"\n :show-path=\"!props.multiple\"\n separator=\" / \"\n filterable\n clearable\n default-expand-all\n teleport\n :placeholder=\"props.multiple ? t('panel.pick-categories') : t('panel.pick-category')\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport {\n rowMenuWidth,\n useAdmin,\n useErrorText,\n useTranslate,\n WxDate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport {\n toast,\n WxBadge,\n WxTable,\n WxText,\n type Paginated,\n type TabItem,\n type TableColumn,\n type TableState,\n type TabValue,\n} from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { useCatalogMessages } from './i18n'\nimport { useCategoryTree } from './store'\nimport type { CategoryRef, DeletedCategory, DeletedKind, DeletedProduct } from './types'\n\n/**\n * «Deleted» (§5, §6.3): what was deleted, and the one thing that can be done to it — bring it\n * back. There is no \"delete for ever\": orders will point at products, and their links have to\n * keep answering. No filters either; a search finds what somebody is looking for, and the list\n * is newest first, which is where a regret usually is.\n *\n * Products and categories are two tabs of one screen rather than two screens, because the\n * question a reader comes with is \"where did it go\", not which kind of thing it was.\n *\n * A row says what a restore will do to it before it is done: a product whose category is\n * deleted too comes back without one and unpublished, and that is worth knowing first.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst tree = useCategoryTree(context)\nconst route = useRoute()\nconst router = useRouter()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\nconst message = useErrorText()\n\ntype Row = DeletedProduct | DeletedCategory\n\nconst page = ref<Paginated<Row> | null>(null)\nconst loading = ref(true)\nconst search = ref(typeof route.query.q === 'string' ? route.query.q : '')\n\nconst canRestore = computed(() => context.can('catalog.delete'))\n\nconst kind = computed<TabValue>({\n get: () => (route.query.type === 'categories' ? 'categories' : 'products'),\n set: (value) => {\n // The table is keyed by the tab and mounts afresh, which asks for its first page itself.\n search.value = ''\n page.value = null\n void router.replace({\n query: { type: value === 'categories' ? 'categories' : undefined },\n })\n },\n})\n\nconst views = computed<TabItem[]>(() => [\n { value: 'products', label: t('module.products') },\n { value: 'categories', label: t('module.categories') },\n])\n\nconst columns = computed<TableColumn<Row>[]>(() =>\n kind.value === 'categories'\n ? [\n { key: 'name', label: t('category.name'), minWidth: 200 },\n { key: 'parent', label: t('panel.column-parent'), width: 200, hideBelow: 640 },\n { key: 'deleted_at', label: t('panel.column-deleted'), width: 150 },\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right' },\n ]\n : [\n { key: 'name', label: t('panel.column-product'), minWidth: 200 },\n { key: 'category', label: t('product.category'), width: 220, hideBelow: 640 },\n { key: 'deleted_at', label: t('panel.column-deleted'), width: 150 },\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right' },\n ],\n)\n\nlet asked = 0\n\nasync function load(state?: TableState): Promise<void> {\n const mine = ++asked\n\n loading.value = true\n\n try {\n const answer = await api.deleted(kind.value as DeletedKind, {\n q: state?.search ?? search.value,\n page: state?.page,\n per_page: state?.perPage,\n })\n\n if (mine === asked) page.value = answer\n } catch (error) {\n toast.danger(message(error))\n } finally {\n if (mine === asked) loading.value = false\n }\n}\n\nfunction onState(state: TableState): void {\n void router.replace({\n query: { ...route.query, q: state.search === '' ? undefined : state.search },\n })\n void load(state)\n}\n\nasync function restore(row: Row): Promise<void> {\n try {\n if (kind.value === 'categories') {\n await api.restoreCategory(row.id)\n tree.drop()\n toast.success(t('panel.category-restored'))\n } else {\n const product = await api.restoreProduct(row.id)\n const bare = product.category === null && (row as DeletedProduct).category !== null\n\n toast.success(bare ? t('panel.product-restored-bare') : t('panel.product-restored'))\n }\n\n await load()\n } catch (error) {\n // A category whose address was taken while it was away is refused, and the server says by whom.\n toast.danger(message(error))\n }\n}\n\n/** A product's category or a category's parent, as the row names it. */\nfunction refOf(row: Record<string, unknown>, key: 'category' | 'parent'): CategoryRef | null {\n const found = row[key]\n\n return found && typeof found === 'object' ? (found as CategoryRef) : null\n}\n\nfunction actionsFor(row: Row): RowAction[] {\n const actions: RowAction[] = []\n\n if (canRestore.value) {\n actions.push({\n key: 'restore',\n icon: 'refresh',\n label: t('panel.restore'),\n run: () => void restore(row),\n })\n }\n\n return actions\n}\n</script>\n\n<template>\n <div class=\"wx-catalog-deleted\">\n <wx-list-screen\n v-model:view=\"kind\"\n :title=\"t('module.deleted')\"\n :subtitle=\"t('panel.deleted-help')\"\n :back=\"`${props.base}/products`\"\n :back-label=\"t('module.products')\"\n :views=\"views\"\n >\n <wx-table\n :key=\"String(kind)\"\n v-model:search=\"search\"\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n :clickable=\"false\"\n :hover=\"false\"\n flush\n layout=\"fixed\"\n :loading=\"loading\"\n :search-placeholder=\"t('panel.deleted-search')\"\n :empty-text=\"search ? t('panel.empty-search') : t('panel.deleted-empty')\"\n :aria-label=\"t('module.deleted')\"\n @state-change=\"onState\"\n >\n <template #cell-name=\"{ row }\">\n <div class=\"wx-catalog-deleted__name\">\n <span class=\"wx-catalog-deleted__title\">{{ row.name }}</span>\n <span v-if=\"'sku' in row && row.sku\" class=\"wx-catalog-deleted__code\">{{\n row.sku\n }}</span>\n <span v-else-if=\"'slug' in row && row.slug\" class=\"wx-catalog-deleted__code\">\n /{{ row.slug }}/\n </span>\n </div>\n </template>\n\n <template #cell-category=\"{ row }\">\n <wx-text v-if=\"!refOf(row, 'category')\" size=\"sm\" tone=\"muted\">\n {{ t('product.no-category') }}\n </wx-text>\n <span v-else class=\"wx-catalog-deleted__ref\">\n <wx-text\n size=\"sm\"\n truncate\n :tone=\"refOf(row, 'category')?.deleted ? 'muted' : undefined\"\n >\n {{ refOf(row, 'category')?.name }}\n </wx-text>\n <wx-badge v-if=\"refOf(row, 'category')?.deleted\" type=\"warning\" size=\"sm\" round>\n {{ t('panel.restore-bare') }}\n </wx-badge>\n </span>\n </template>\n\n <template #cell-parent=\"{ row }\">\n <wx-text v-if=\"!refOf(row, 'parent')\" size=\"sm\" tone=\"muted\">\n {{ t('panel.parent-root') }}\n </wx-text>\n <wx-text\n v-else\n size=\"sm\"\n truncate\n :tone=\"refOf(row, 'parent')?.deleted ? 'muted' : undefined\"\n >\n {{ refOf(row, 'parent')?.name }}\n <template v-if=\"refOf(row, 'parent')?.deleted\">\n ({{ t('panel.category-gone') }})</template\n >\n </wx-text>\n </template>\n\n <template #cell-deleted_at=\"{ row }\">\n <wx-date :value=\"row.deleted_at\" compact />\n </template>\n\n <template #cell-actions=\"{ row }\">\n <wx-row-menu v-if=\"canRestore\" :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-deleted {\n min-width: 0;\n}\n\n.wx-catalog-deleted__name {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.wx-catalog-deleted__title {\n font-weight: var(--wx-font-weight-medium);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-catalog-deleted__code {\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-sm);\n color: var(--wx-text-muted);\n}\n\n.wx-catalog-deleted__ref {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-4) var(--wx-space-8);\n min-width: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport { WxAlert, WxSkeleton, WxSortableList, WxSwitch, WxText } from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { useCatalogCategoryEditor } from './editor'\nimport { useCatalogMessages } from './i18n'\nimport { useFacetRegistry } from './store'\nimport type { FacetInfo, FacetSetting } from './types'\n\n/**\n * `wx-catalog-facets`: which filters a category shows and in which order (§6.2).\n *\n * The value is `null` — the category takes the setting of its nearest ancestor that has one, or\n * every facet in the registry's order — or its own list. Off, the field says where the setting\n * comes from, because \"inherited\" alone leaves the reader to walk up the tree to find out what\n * that means. On, it is the list itself: dragged into order, each facet shown or not.\n *\n * Switching it on starts from what was inherited, not from a blank list: the reader wanted to\n * change one thing about the setting they were looking at.\n *\n * A facet registered after the setting was written is not in it, and is shown here switched off\n * — a new module does not get to change somebody else's filters on its own (§6.2). A key whose\n * module has gone is kept as it is: putting the module back brings the setting back with it.\n */\ndefineOptions({ name: 'WxCatalogFacetsField' })\n\nconst value = defineModel<FacetSetting[] | null>({ default: null })\n\nconst admin = useAdmin()\nconst api = createCatalogApi(admin)\nconst registry = useFacetRegistry(admin)\nconst editor = useCatalogCategoryEditor()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n\nconst loading = ref(true)\nconst failed = ref(false)\n/** The nearest ancestor with a setting of its own, or `null` — the registry's order. */\nconst source = ref<{ name: string; rows: FacetSetting[] } | null>(null)\n\nconst facets = computed<FacetInfo[]>(() => registry.facets.value ?? [])\nconst labels = computed(() => new Map(facets.value.map((facet) => [facet.key, facet.label])))\n\nconst own = computed(() => Array.isArray(value.value))\nconst locked = computed(() => editor?.locked.value ?? false)\n\ninterface Row extends FacetSetting {\n label: string\n known: boolean\n}\n\n/** The setting with every facet the registry has that it does not, switched off, at the end. */\nfunction complete(rows: FacetSetting[]): FacetSetting[] {\n const named = new Set(rows.map((row) => row.key))\n\n return [\n ...rows.map((row) => ({ key: row.key, visible: row.visible })),\n ...facets.value\n .filter((facet) => !named.has(facet.key))\n .map((facet) => ({ key: facet.key, visible: false })),\n ]\n}\n\nconst inherited = computed<FacetSetting[]>(() =>\n source.value === null\n ? facets.value.map((facet) => ({ key: facet.key, visible: true }))\n : complete(source.value.rows),\n)\n\nconst rows = computed<Row[]>({\n get: () =>\n complete(value.value ?? []).map((row) => ({\n ...row,\n label: labels.value.get(row.key) ?? row.key,\n known: labels.value.has(row.key),\n })),\n set: (next) => {\n value.value = next.map((row) => ({ key: row.key, visible: row.visible }))\n },\n})\n\n/** What a visitor of the category sees while it inherits: the names, in order. */\nconst inheritedNames = computed(() =>\n inherited.value\n .filter((row) => row.visible && labels.value.has(row.key))\n .map((row) => labels.value.get(row.key))\n .join(', '),\n)\n\nfunction toggleOwn(on: boolean | string | number | null): void {\n value.value = on ? inherited.value.map((row) => ({ ...row })) : null\n}\n\nfunction show(key: string, visible: boolean | string | number | null): void {\n rows.value = rows.value.map((row) =>\n row.key === key ? { ...row, visible: Boolean(visible) } : row,\n )\n}\n\n/**\n * Whose setting this is. The server names the ancestor (`facets_from`), and one request reads\n * its rows — the setting is shown, not only named. A server that does not say is walked up the\n * tree from the parent until somebody has a setting of their own, one request per level.\n */\nasync function findSource(parent: number | null): Promise<void> {\n const from = editor?.facetsFrom.value\n\n source.value = null\n\n if (from === null) return\n\n if (from !== undefined) {\n const detail = await api.category(from.id)\n\n if (Array.isArray(detail.values.facets)) {\n source.value = { name: from.name, rows: detail.values.facets as FacetSetting[] }\n }\n\n return\n }\n\n let next = parent\n\n while (next !== null) {\n const detail = await api.category(next)\n const setting = detail.values.facets\n\n if (Array.isArray(setting)) {\n source.value = { name: detail.category.name, rows: setting as FacetSetting[] }\n\n return\n }\n\n next = detail.category.parent_id\n }\n}\n\nasync function load(): Promise<void> {\n loading.value = true\n failed.value = false\n\n try {\n await Promise.all([registry.load(), findSource(editor?.category.value?.parent_id ?? null)])\n } catch {\n failed.value = true\n } finally {\n loading.value = false\n }\n}\n\n/* A move or a save changes whose setting it is; the editor hands both over with the answer. */\nwatch(\n () => [editor?.category.value?.parent_id, JSON.stringify(editor?.facetsFrom.value)],\n (now, before) => {\n if (now[0] !== before[0] || now[1] !== before[1]) void load()\n },\n)\n\nonMounted(load)\n</script>\n\n<template>\n <div class=\"wx-catalog-facets\">\n <wx-skeleton v-if=\"loading\" :rows=\"3\" />\n\n <wx-alert\n v-else-if=\"failed\"\n type=\"warning\"\n variant=\"soft\"\n :description=\"t('panel.facets-failed')\"\n />\n\n <template v-else>\n <div class=\"wx-catalog-facets__own\">\n <wx-switch\n :model-value=\"own\"\n :disabled=\"locked\"\n :label=\"t('panel.facets-own')\"\n @update:model-value=\"toggleOwn\"\n />\n <wx-text size=\"sm\" tone=\"muted\">\n {{ own ? t('panel.facets-own-help') : t('panel.facets-inherited-help') }}\n </wx-text>\n </div>\n\n <div v-if=\"!own\" class=\"wx-catalog-facets__inherited\">\n <wx-text weight=\"medium\">\n {{\n source ? t('panel.facets-inherited', { name: source.name }) : t('panel.facets-default')\n }}\n </wx-text>\n <wx-text v-if=\"inheritedNames\" size=\"sm\" tone=\"muted\">{{ inheritedNames }}</wx-text>\n <wx-text v-else-if=\"facets.length === 0\" size=\"sm\" tone=\"muted\">\n {{ t('panel.facets-empty') }}\n </wx-text>\n </div>\n\n <wx-sortable-list\n v-else\n v-model=\"rows\"\n size=\"sm\"\n item-key=\"key\"\n item-label=\"label\"\n :disabled=\"locked\"\n :empty-text=\"t('panel.facets-empty')\"\n >\n <template #default=\"{ item }\">\n <span class=\"wx-catalog-facets__name\" :class=\"{ 'is-off': !(item as Row).visible }\">\n {{ (item as Row).label }}\n </span>\n <wx-text v-if=\"!(item as Row).known\" size=\"sm\" tone=\"muted\">\n {{ t('panel.facets-unknown') }}\n </wx-text>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-switch\n size=\"sm\"\n :model-value=\"(item as Row).visible\"\n :disabled=\"locked || !(item as Row).known\"\n :aria-label=\"`${t('panel.facets-shown')}: ${(item as Row).label}`\"\n @update:model-value=\"(on) => show((item as Row).key, on)\"\n />\n </template>\n </wx-sortable-list>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-catalog-facets {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n}\n\n.wx-catalog-facets__own,\n.wx-catalog-facets__inherited {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-4);\n}\n\n.wx-catalog-facets :deep(.wx-sortable-list__content) {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n min-width: 0;\n}\n\n.wx-catalog-facets__name {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Hidden facets stay in the list — their place is part of the setting — but read as switched off. */\n.wx-catalog-facets__name.is-off {\n color: var(--wx-text-muted);\n}\n</style>\n","import type { ProductImage } from './types'\n\n/**\n * The browser half of videos in the gallery (§1.6, §7 of the video spec): the frame that becomes\n * the poster, and the checks made before a byte is sent.\n */\n\n/** What the panel learns about a video file by opening it in the browser. */\nexport interface VideoProbe {\n /** The frame, as a JPEG; `null` when only the length was asked for. */\n frame: Blob | null\n /** Whole seconds, or `null` when the file does not say. */\n duration: number | null\n width: number\n height: number\n}\n\n/**\n * The pieces of the DOM the probe needs, handed in so that a test can stand them up: jsdom does\n * not decode video, so a probe against a real `<video>` there never gets past loading.\n */\nexport interface ProbeEnvironment {\n createVideo(): HTMLVideoElement\n createCanvas(): HTMLCanvasElement\n createUrl(file: Blob): string\n revokeUrl(url: string): void\n}\n\nexport interface ProbeOptions {\n /** Take a frame as well as the length. */\n frame?: boolean\n /** How long to wait for the browser to open the file before giving up on it. */\n timeout?: number\n environment?: ProbeEnvironment\n}\n\n/** The browser could not open the file — mostly a codec it does not play. */\nexport class VideoUnreadable extends Error {\n constructor(reason: string) {\n super(reason)\n this.name = 'VideoUnreadable'\n }\n}\n\nconst browser: ProbeEnvironment = {\n createVideo: () => document.createElement('video'),\n createCanvas: () => document.createElement('canvas'),\n createUrl: (file) => URL.createObjectURL(file),\n revokeUrl: (url) => URL.revokeObjectURL(url),\n}\n\n/** Where the frame is taken: a little in, as the very first frame is so often black. */\nexport function frameTime(duration: number): number {\n if (!Number.isFinite(duration) || duration <= 0) return 0\n\n return Math.min(1, duration / 10)\n}\n\n/**\n * Open a video file in the browser and read its length and, if asked, one frame of it.\n *\n * Read off the local file, before the upload, so a poster does not wait on gigabytes going up\n * (decision 6). Rejects with `VideoUnreadable` when the browser cannot open the file, which is\n * what the panel then says in words: add a picture and attach the video to it.\n */\nexport function probeVideo(file: Blob, options: ProbeOptions = {}): Promise<VideoProbe> {\n const environment = options.environment ?? browser\n const timeout = options.timeout ?? 15000\n const video = environment.createVideo()\n const url = environment.createUrl(file)\n\n video.muted = true\n video.playsInline = true\n video.preload = 'auto'\n\n return new Promise<VideoProbe>((resolve, reject) => {\n let settled = false\n const timer = setTimeout(() => fail('The browser took too long to open the video.'), timeout)\n\n function done(): void {\n settled = true\n clearTimeout(timer)\n video.removeAttribute('src')\n video.load?.()\n environment.revokeUrl(url)\n }\n\n function fail(reason: string): void {\n if (settled) return\n\n done()\n reject(new VideoUnreadable(reason))\n }\n\n function duration(): number | null {\n return Number.isFinite(video.duration) && video.duration > 0\n ? Math.round(video.duration)\n : null\n }\n\n video.addEventListener('error', () => fail('The browser cannot play this video.'))\n\n video.addEventListener('loadedmetadata', () => {\n if (settled) return\n\n if (!options.frame) {\n done()\n resolve({\n frame: null,\n duration: duration(),\n width: video.videoWidth,\n height: video.videoHeight,\n })\n\n return\n }\n\n // Sound only, or a codec that gives a length and no picture.\n if (video.videoWidth === 0 || video.videoHeight === 0) {\n fail('The video has no picture the browser can show.')\n\n return\n }\n\n video.currentTime = frameTime(video.duration)\n })\n\n video.addEventListener('seeked', () => {\n if (settled) return\n\n const canvas = environment.createCanvas()\n canvas.width = video.videoWidth\n canvas.height = video.videoHeight\n\n const context = canvas.getContext('2d')\n\n if (context === null) {\n fail('The browser gave no canvas to draw the frame on.')\n\n return\n }\n\n context.drawImage(video, 0, 0, canvas.width, canvas.height)\n\n const { videoWidth: width, videoHeight: height } = video\n const length = duration()\n\n canvas.toBlob(\n (blob) => {\n if (settled) return\n\n if (blob === null) {\n fail('The frame could not be turned into a picture.')\n\n return\n }\n\n done()\n resolve({ frame: blob, duration: length, width, height })\n },\n 'image/jpeg',\n 0.85,\n )\n })\n\n video.src = url\n })\n}\n\n/** The name of the poster: the video's own, as a JPEG. */\nexport function posterName(file: Pick<File, 'name'>): string {\n const base = file.name.replace(/\\.[^.]+$/, '')\n\n return `${base === '' ? 'video' : base}.jpg`\n}\n\nconst YOUTUBE_HOSTS = new Set([\n 'youtube.com',\n 'www.youtube.com',\n 'm.youtube.com',\n 'music.youtube.com',\n 'youtu.be',\n 'www.youtube-nocookie.com',\n 'youtube-nocookie.com',\n])\n\nconst YOUTUBE_ID = /^[A-Za-z0-9_-]{11}$/\n\n/**\n * The id of a YouTube video in any of the forms the server takes (§3), or `null`. The server\n * decides in the end; this only says what the field should call the address.\n */\nexport function youTubeId(address: string): string | null {\n let url: URL\n\n try {\n url = new URL(address.trim())\n } catch {\n return null\n }\n\n if (!/^https?:$/.test(url.protocol) || !YOUTUBE_HOSTS.has(url.hostname.toLowerCase())) {\n return null\n }\n\n if (url.hostname.toLowerCase() === 'youtu.be') {\n const id = url.pathname.split('/')[1] ?? ''\n\n return YOUTUBE_ID.test(id) ? id : null\n }\n\n const watched = url.pathname === '/watch' ? url.searchParams.get('v') : null\n\n if (watched !== null) return YOUTUBE_ID.test(watched) ? watched : null\n\n const [, kind, id] = url.pathname.split('/')\n\n return ['shorts', 'embed', 'live'].includes(kind ?? '') && YOUTUBE_ID.test(id ?? '')\n ? (id ?? null)\n : null\n}\n\n/** A direct link to an MP4 or WebM file, judged by its name — the server checks the answer. */\nexport function isVideoFileLink(address: string): boolean {\n try {\n const url = new URL(address.trim())\n\n return /^https?:$/.test(url.protocol) && /\\.(mp4|webm)$/i.test(url.pathname)\n } catch {\n return false\n }\n}\n\n/** Anything the gallery treats as a video rather than a picture. */\nexport function isVideoLink(address: string): boolean {\n return youTubeId(address) !== null || isVideoFileLink(address)\n}\n\n/** A file the gallery may take as a video, by the limits the screen gave it. */\nexport function videoRefusal(\n file: Pick<File, 'type' | 'size'>,\n types: readonly string[],\n maxBytes: number,\n): 'type' | 'size' | null {\n if (!types.includes(file.type)) return 'type'\n if (file.size > maxBytes) return 'size'\n\n return null\n}\n\n/**\n * The poster the server puts on a video that came by a direct link without a picture: a plain\n * dark PNG, 1280×720 (a single pixel where the server has no GD). Recognised by its shape, as the\n * resource carries no mark of it; a panel-made poster is a JPEG, so a mistake here needs a PNG of\n * exactly that size with a file video on it, and then the hint only says what may be done anyway.\n */\nexport function hasPlaceholderPoster(image: ProductImage): boolean {\n if (image.video?.provider !== 'file' || !/\\.png$/i.test(image.path)) return false\n\n return (image.width === 1280 && image.height === 720) || (image.width === 1 && image.height === 1)\n}\n\n/** Bytes as the panel says them: \"840 KB\", \"1.2 GB\". */\nexport function formatBytes(bytes: number, locale?: string): string {\n const units = ['B', 'KB', 'MB', 'GB', 'TB']\n let value = Math.max(0, bytes)\n let unit = 0\n\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024\n unit += 1\n }\n\n const figure = new Intl.NumberFormat(locale, {\n maximumFractionDigits: value >= 100 || unit === 0 ? 0 : 1,\n }).format(value)\n\n return `${figure} ${units[unit]}`\n}\n\n/** A length of time as a clock: \"0:42\", \"12:05\", \"1:02:03\". */\nexport function formatClock(seconds: number): string {\n const whole = Math.max(0, Math.round(seconds))\n const hours = Math.floor(whole / 3600)\n const minutes = Math.floor((whole % 3600) / 60)\n const rest = String(whole % 60).padStart(2, '0')\n\n return hours > 0 ? `${hours}:${String(minutes).padStart(2, '0')}:${rest}` : `${minutes}:${rest}`\n}\n","import {\n computed,\n getCurrentScope,\n onScopeDispose,\n readonly,\n ref,\n type ComputedRef,\n type Ref,\n} from 'vue'\nimport {\n forgetUnfinishedUpload,\n unfinishedUploads,\n uploadFingerprint,\n useAdmin,\n useChunkedUpload,\n useErrorText,\n useTranslate,\n type AdminContext,\n type ChunkedUpload,\n type UnfinishedUpload,\n} from '@webx-ui/module-admin'\nimport { toast } from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport type { ProductImage } from './types'\nimport { posterName, probeVideo, VideoUnreadable, type ProbeOptions } from './video'\n\n/**\n * Video files going into the gallery (§7 of the video spec): the frame, the picture, the file a\n * piece at a time, and the request that puts the two together.\n *\n * Kept by the product editor rather than by the gallery field. The field lives on a tab, and a tab\n * nobody is looking at is taken down — with the chunked upload inside it, which pauses when its\n * scope dies. Up here the upload carries on while the rest of the form is being filled in.\n *\n * One file at a time, as there is one chunked upload: several at once share one connection\n * anyway, and one bar that moves says more than three that crawl.\n */\n\nexport const VIDEO_PURPOSE = 'catalog.video'\n\nexport type VideoJobStage = 'waiting' | 'poster' | 'uploading' | 'attaching' | 'failed'\n\nexport interface VideoJob {\n key: number\n file: File\n /** The product it goes to, as it was when the file was chosen. */\n product: number\n /** The picture it goes onto; `null` until the frame has been uploaded as one. */\n image: number | null\n /** The job made that picture itself — a cancel takes it away again. */\n created: boolean\n /** The frame, until the picture is on the server and the row shows its own. */\n poster: string | null\n duration: number | null\n stage: VideoJobStage\n /** Why it stopped, in words; only in `failed`. */\n error: string | null\n /** The browser could not open the file, so there is no frame to make a picture of. */\n unreadable: boolean\n}\n\n/** An upload this browser left unfinished for this product, with the picture it was for. */\nexport interface UnfinishedVideo extends UnfinishedUpload {\n image: number | null\n}\n\nexport interface GalleryVideo {\n readonly jobs: Readonly<Ref<readonly VideoJob[]>>\n /** The one upload that moves — its state, progress, speed and time left. */\n readonly upload: ChunkedUpload\n /** Something is on its way to the server; leaving now loses the rest of it. */\n readonly busy: ComputedRef<boolean>\n /** What a reload interrupted on this product, to be continued by choosing the file again. */\n readonly unfinished: ComputedRef<UnfinishedVideo[]>\n /** Onto `image`, or a new row made of its own frame when there is none. */\n add(file: File, image?: number | null): void\n pause(): void\n resume(): void\n /** Try a failed one again: a file the server has part of continues from there. */\n retry(job: VideoJob): void\n cancel(job: VideoJob): Promise<void>\n /** Take a failed one off the list. */\n dismiss(job: VideoJob): void\n /** Stop offering to continue an upload. */\n forget(entry: UnfinishedVideo): void\n}\n\nexport interface GalleryVideoOptions {\n product: Ref<number | null>\n images: Ref<ProductImage[]>\n admin?: AdminContext\n storage?: Storage | null\n probe?: ProbeOptions\n}\n\nconst TARGETS_KEY = 'webx:catalog-video-targets'\n\ntype Targets = Record<string, { product: number; image: number | null }>\n\nexport function createGalleryVideo(options: GalleryVideoOptions): GalleryVideo {\n const admin = options.admin ?? useAdmin()\n const api = createCatalogApi(admin)\n const storage = options.storage === undefined ? defaultStorage() : options.storage\n const upload = useChunkedUpload({ admin, storage })\n const t = useTranslate('webx-catalog')\n const message = useErrorText()\n\n const jobs = ref<VideoJob[]>([])\n // Bumped whenever storage changes under the list of unfinished uploads.\n const stored = ref(0)\n let next = 1\n let running = false\n // The job whose file the chunked upload holds, which is the only one a cancel can stop.\n let uploading: number | null = null\n\n const busy = computed(() => jobs.value.some((job) => job.stage !== 'failed'))\n\n const unfinished = computed<UnfinishedVideo[]>(() => {\n void stored.value\n const product = options.product.value\n const targets = readTargets(storage)\n const going = new Set(jobs.value.map((job) => uploadFingerprint(job.file)))\n\n if (product === null) return []\n\n return unfinishedUploads(VIDEO_PURPOSE, storage)\n .filter((entry) => targets[entry.fingerprint]?.product === product)\n .filter((entry) => !going.has(entry.fingerprint))\n .map((entry) => ({ ...entry, image: targets[entry.fingerprint]?.image ?? null }))\n })\n\n function patch(key: number, change: Partial<VideoJob>): void {\n jobs.value = jobs.value.map((job) => (job.key === key ? { ...job, ...change } : job))\n }\n\n function find(key: number): VideoJob | undefined {\n return jobs.value.find((job) => job.key === key)\n }\n\n function setTarget(file: File, product: number, image: number | null): void {\n writeTargets(storage, {\n ...readTargets(storage),\n [uploadFingerprint(file)]: { product, image },\n })\n stored.value += 1\n }\n\n function dropTarget(file: File): void {\n const targets = readTargets(storage)\n delete targets[uploadFingerprint(file)]\n writeTargets(storage, targets)\n stored.value += 1\n }\n\n function replace(image: ProductImage): void {\n options.images.value = options.images.value.map((one) => (one.id === image.id ? image : one))\n }\n\n function add(file: File, image: number | null = null): void {\n const product = options.product.value\n\n if (product === null) return\n\n jobs.value = [\n ...jobs.value,\n {\n key: next++,\n file,\n product,\n image,\n created: false,\n poster: null,\n duration: null,\n stage: 'waiting',\n error: null,\n unreadable: false,\n },\n ]\n\n void pump()\n }\n\n /** The jobs one after another, until none is waiting. */\n async function pump(): Promise<void> {\n if (running) return\n\n running = true\n\n try {\n let job = jobs.value.find((one) => one.stage === 'waiting')\n\n while (job !== undefined) {\n await work(job.key)\n job = jobs.value.find((one) => one.stage === 'waiting')\n }\n } finally {\n running = false\n }\n }\n\n async function work(key: number): Promise<void> {\n let job = find(key)\n\n if (job === undefined) return\n\n try {\n if (job.image === null) {\n patch(key, { stage: 'poster' })\n\n let probe\n\n try {\n probe = await probeVideo(job.file, { ...options.probe, frame: true })\n } catch (failure) {\n if (!(failure instanceof VideoUnreadable)) throw failure\n\n patch(key, { stage: 'failed', unreadable: true, error: t('panel.video-unreadable') })\n\n return\n }\n\n if (find(key) === undefined) return\n\n const poster = URL.createObjectURL(probe.frame!)\n patch(key, { poster, duration: probe.duration })\n\n try {\n const picture = await api.addImage(\n job.product,\n new File([probe.frame!], posterName(job.file), { type: 'image/jpeg' }),\n )\n\n // Cancelled while the picture was going up: it is nobody's now.\n if (find(key) === undefined) {\n await api.removeImage(job.product, picture.id).catch(() => undefined)\n\n return\n }\n\n if (options.product.value === job.product) {\n options.images.value = [...options.images.value, picture]\n }\n\n patch(key, { image: picture.id, created: true })\n } finally {\n URL.revokeObjectURL(poster)\n patch(key, { poster: null })\n }\n } else if (job.duration === null) {\n // The length is all a picture that is already there needs; a file the browser cannot\n // open still goes up — the server judges it by what is in it.\n const probe = await probeVideo(job.file, options.probe).catch(() => null)\n\n patch(key, { duration: probe?.duration ?? null })\n }\n\n job = find(key)\n\n if (job === undefined || job.image === null) return\n\n patch(key, { stage: 'uploading', error: null })\n setTarget(job.file, job.product, job.image)\n\n uploading = key\n const id = await upload.start(job.file, VIDEO_PURPOSE)\n\n // Cancelled: `cancel` has already tidied up after it.\n if (id === null || find(key) === undefined) return\n\n dropTarget(job.file)\n patch(key, { stage: 'attaching' })\n\n const answer = await api.attachVideo(job.product, job.image, {\n upload: id,\n duration: job.duration,\n })\n\n if ('queued' in answer) return\n\n if (options.product.value === job.product) replace(answer)\n\n jobs.value = jobs.value.filter((one) => one.key !== key)\n toast.success(t('panel.video-attached'))\n } catch (failure) {\n if (find(key) !== undefined) {\n patch(key, { stage: 'failed', error: message(failure) })\n }\n }\n }\n\n function retry(job: VideoJob): void {\n if (find(job.key)?.stage !== 'failed') return\n\n patch(job.key, { stage: 'waiting', error: null, unreadable: false })\n void pump()\n }\n\n async function cancel(job: VideoJob): Promise<void> {\n const current = find(job.key)\n\n if (current === undefined) return\n\n jobs.value = jobs.value.filter((one) => one.key !== job.key)\n dropTarget(current.file)\n\n if (uploading === current.key) {\n uploading = null\n await upload.cancel()\n }\n\n // The picture was made of the video's own frame for this video; without it, it is not\n // something anybody chose to put in the gallery.\n if (current.created && current.image !== null) {\n try {\n await api.removeImage(current.product, current.image)\n\n if (options.product.value === current.product) {\n options.images.value = options.images.value.filter((one) => one.id !== current.image)\n }\n } catch (failure) {\n toast.danger(message(failure))\n }\n }\n }\n\n function dismiss(job: VideoJob): void {\n jobs.value = jobs.value.filter((one) => one.key !== job.key)\n }\n\n function forget(entry: UnfinishedVideo): void {\n forgetUnfinishedUpload(entry.fingerprint, VIDEO_PURPOSE, storage)\n\n const targets = readTargets(storage)\n delete targets[entry.fingerprint]\n writeTargets(storage, targets)\n stored.value += 1\n }\n\n /* The browser's own question on the way out: it cannot wait, so all it can do is ask. */\n function guard(event: BeforeUnloadEvent): void {\n if (busy.value) event.preventDefault()\n }\n\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', guard)\n }\n\n if (getCurrentScope() !== undefined) {\n onScopeDispose(() => {\n if (typeof window !== 'undefined') window.removeEventListener('beforeunload', guard)\n })\n }\n\n return {\n jobs: readonly(jobs) as Readonly<Ref<readonly VideoJob[]>>,\n upload,\n busy,\n unfinished,\n add,\n pause: () => upload.pause(),\n resume: () => void upload.resume(),\n retry,\n cancel,\n dismiss,\n forget,\n }\n}\n\nfunction defaultStorage(): Storage | null {\n try {\n return typeof localStorage === 'undefined' ? null : localStorage\n } catch {\n return null\n }\n}\n\nfunction readTargets(storage: Storage | null): Targets {\n if (storage === null) return {}\n\n try {\n const parsed: unknown = JSON.parse(storage.getItem(TARGETS_KEY) ?? '{}')\n\n return parsed !== null && typeof parsed === 'object' ? (parsed as Targets) : {}\n } catch {\n return {}\n }\n}\n\nfunction writeTargets(storage: Storage | null, targets: Targets): void {\n if (storage === null) return\n\n try {\n if (Object.keys(targets).length === 0) {\n storage.removeItem(TARGETS_KEY)\n } else {\n storage.setItem(TARGETS_KEY, JSON.stringify(targets))\n }\n } catch {\n // Refused or full: offering to continue after a reload is a courtesy.\n }\n}\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport { useModal, WxButton, WxDialog, WxFormItem, WxInput, WxSpace } from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { firstError } from './errors'\nimport { useCatalogMessages } from './i18n'\nimport type { ProductImage, QueuedVideo } from './types'\nimport { isVideoLink } from './video'\n\n/**\n * A video by its address, onto a picture of the gallery: a YouTube link, or a direct link to an\n * MP4 or WebM file the server then downloads in its queue (§5 of the video spec).\n */\nconst props = defineProps<{ product: number; image: number }>()\n\nconst { open, resolve, dismiss } = useModal<ProductImage | QueuedVideo>()\n\nconst api = createCatalogApi(useAdmin())\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\nconst message = useErrorText()\n\nconst address = ref('')\nconst saving = ref(false)\nconst error = ref<string | null>(null)\nconst field = useTemplateRef<HTMLElement>('field')\n\nconst ready = computed(() => isVideoLink(address.value))\n\nonMounted(async () => {\n await nextTick()\n field.value?.querySelector('input')?.focus()\n})\n\nasync function submit(): Promise<void> {\n if (saving.value) return\n\n if (!ready.value) {\n error.value = t('panel.video-link-wrong')\n\n return\n }\n\n saving.value = true\n error.value = null\n\n try {\n resolve(await api.attachVideo(props.product, props.image, { url: address.value.trim() }))\n } catch (failure) {\n const errors = (failure as { body?: { errors?: Record<string, string[]> } }).body?.errors ?? {}\n\n error.value = firstError(errors, 'url') ?? message(failure)\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('panel.video-link-title')\" :width=\"480\">\n <wx-form-item\n :label=\"t('panel.video-link')\"\n :help=\"t('panel.video-link-help')\"\n :error=\"error ?? undefined\"\n >\n <div ref=\"field\">\n <wx-input\n v-model=\"address\"\n type=\"url\"\n placeholder=\"https://www.youtube.com/watch?v=\"\n :aria-label=\"t('panel.video-link')\"\n @keydown.enter.prevent=\"submit\"\n />\n </div>\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\n type=\"primary\"\n :loading=\"saving\"\n :disabled=\"address.trim() === ''\"\n @click=\"submit\"\n >\n {{ t('panel.video-attach') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { useTranslate } from '@webx-ui/module-admin'\nimport { WxButton, WxProgress, WxText, useLocales } from '@webx-ui/core'\nimport type { GalleryVideo, VideoJob } from './galleryVideo'\nimport { formatBytes, formatClock } from './video'\n\n/**\n * One video on its way into the gallery: what it is doing, and the bar while the file goes up —\n * the share, the speed and the time left, with pause and cancel beside it.\n */\nconst props = defineProps<{ job: VideoJob; video: GalleryVideo }>()\n\nconst t = useTranslate('webx-catalog')\nconst locales = useLocales()\n\nconst upload = computed(() => props.video.upload)\nconst state = computed(() => upload.value.state.value)\nconst percent = computed(() => Math.floor(upload.value.progress.value * 100))\n\nconst figures = computed(() => {\n const locale = locales.active.value\n const parts = [\n t('panel.video-sent', {\n sent: formatBytes(upload.value.uploaded.value, locale),\n total: formatBytes(upload.value.total.value, locale),\n }),\n ]\n\n if (state.value === 'paused') {\n parts.push(t('panel.video-paused'))\n } else if (state.value === 'offline') {\n parts.push(t('panel.video-offline'))\n } else {\n if (upload.value.speed.value > 0) {\n parts.push(`${formatBytes(upload.value.speed.value, locale)}/s`)\n }\n\n const left = upload.value.remaining.value\n\n if (left !== null) parts.push(t('panel.video-left', { time: formatClock(left) }))\n }\n\n return parts\n})\n\nconst stage = computed(() => {\n switch (props.job.stage) {\n case 'waiting':\n return t('panel.video-waiting')\n case 'poster':\n return t('panel.video-poster')\n case 'attaching':\n return t('panel.video-attaching')\n default:\n return ''\n }\n})\n</script>\n\n<template>\n <div class=\"wx-catalog-video-progress\" :class=\"{ 'is-failed': job.stage === 'failed' }\">\n <template v-if=\"job.stage === 'uploading'\">\n <wx-progress\n :value=\"percent\"\n :max=\"100\"\n size=\"sm\"\n show-value\n :status=\"state === 'failed' ? 'danger' : 'default'\"\n :aria-label=\"t('panel.video-uploading', { name: job.file.name })\"\n />\n <div class=\"wx-catalog-video-progress__line\">\n <wx-text size=\"sm\" tone=\"muted\" class=\"wx-catalog-video-progress__figures\">\n <!-- The dot goes with the figure after it, so a line never ends on one. -->\n <template v-for=\"(part, index) in figures\" :key=\"index\">\n {{ ' '\n }}<span class=\"wx-catalog-video-progress__figure\">{{\n index > 0 ? `· ${part}` : part\n }}</span>\n </template>\n </wx-text>\n <div class=\"wx-catalog-video-progress__buttons\">\n <wx-button v-if=\"state === 'uploading'\" size=\"sm\" variant=\"text\" @click=\"video.pause()\">\n {{ t('panel.video-pause') }}\n </wx-button>\n <wx-button\n v-else-if=\"state === 'paused' || state === 'offline'\"\n size=\"sm\"\n variant=\"text\"\n @click=\"video.resume()\"\n >\n {{ t('panel.video-resume') }}\n </wx-button>\n <wx-button size=\"sm\" variant=\"text\" type=\"danger\" @click=\"video.cancel(job)\">\n {{ t('panel.cancel') }}\n </wx-button>\n </div>\n </div>\n </template>\n\n <template v-else-if=\"job.stage === 'failed'\">\n <wx-text size=\"sm\" tone=\"danger\" role=\"alert\">{{ job.error }}</wx-text>\n <div class=\"wx-catalog-video-progress__buttons\">\n <wx-button v-if=\"!job.unreadable\" size=\"sm\" variant=\"text\" @click=\"video.retry(job)\">\n {{ t('panel.video-retry') }}\n </wx-button>\n <wx-button v-if=\"job.unreadable\" size=\"sm\" variant=\"text\" @click=\"video.dismiss(job)\">\n {{ t('panel.video-dismiss') }}\n </wx-button>\n <wx-button v-else size=\"sm\" variant=\"text\" type=\"danger\" @click=\"video.cancel(job)\">\n {{ t('panel.cancel') }}\n </wx-button>\n </div>\n </template>\n\n <template v-else>\n <wx-progress indeterminate size=\"sm\" :aria-label=\"stage\" />\n <div class=\"wx-catalog-video-progress__line\">\n <wx-text size=\"sm\" tone=\"muted\">{{ stage }}</wx-text>\n <div class=\"wx-catalog-video-progress__buttons\">\n <wx-button size=\"sm\" variant=\"text\" type=\"danger\" @click=\"video.cancel(job)\">\n {{ t('panel.cancel') }}\n </wx-button>\n </div>\n </div>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-catalog-video-progress {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-4);\n min-width: 0;\n}\n\n.wx-catalog-video-progress__line {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--wx-space-4) var(--wx-space-8);\n min-width: 0;\n}\n\n.wx-catalog-video-progress__figures {\n font-variant-numeric: tabular-nums;\n min-width: 0;\n}\n\n/* A figure never breaks in two; the line breaks between them. */\n.wx-catalog-video-progress__figure {\n white-space: nowrap;\n}\n\n.wx-catalog-video-progress__buttons {\n display: flex;\n gap: var(--wx-space-4);\n margin-inline-start: auto;\n}\n\n.wx-catalog-video-progress.is-failed {\n flex-direction: row;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--wx-space-4) var(--wx-space-8);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, useTemplateRef } from 'vue'\nimport {\n uploadFingerprint,\n useAdmin,\n useErrorText,\n useTranslate,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n toast,\n useLocales,\n WxAlert,\n WxBadge,\n WxButton,\n WxCard,\n WxInput,\n WxSortableList,\n WxText,\n WxUpload,\n type LocalizedValue,\n type UploadFile,\n} from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { useProductEditor } from './editor'\nimport { createGalleryVideo, type GalleryVideo, type UnfinishedVideo } from './galleryVideo'\nimport { useCatalogMessages } from './i18n'\nimport type { ProductImage, QueuedVideo } from './types'\nimport { formatBytes, formatClock, hasPlaceholderPoster, videoRefusal } from './video'\nimport VideoLinkDialog from './VideoLinkDialog.vue'\nimport VideoProgress from './VideoProgress.vue'\n\n/**\n * `wx-catalog-gallery`: the pictures of the product being edited (§10.4, §11.1), and the videos\n * attached to them (the video spec, §7).\n *\n * Not a value of the form. A picture is a record of its own with files on the disk, so adding one\n * is a request the moment it is chosen, deleting one deletes its files at once, and the order and\n * the captions are one request for the whole gallery — the editor's \"Save\" has nothing to do with\n * any of it. That is also why this node has no `name`: there is nothing for the form to carry.\n *\n * The captions save themselves a moment after the last keystroke, like a draft does elsewhere in\n * the panel; a reorder saves on the drop. Both send the whole gallery in its order, which is what\n * the server takes (`PUT …/images`).\n *\n * A video is never a row of its own: it hangs on a picture, which is its poster. A file dropped\n * here becomes a picture of its own frame first, then goes up a piece at a time; the uploads are\n * the editor's (`galleryVideo.ts`), so they carry on while another tab of the form is open.\n */\ndefineOptions({ name: 'WxCatalogGallery' })\n\nconst props = withDefaults(\n defineProps<{\n /** Videos on this site (`webx-catalog.fields.video`); the server's screen patch says. */\n video?: boolean\n /** What a video file may be, checked before a byte of it is sent. */\n videoTypes?: string[]\n /** How large, in bytes. */\n videoMaxBytes?: number\n }>(),\n {\n // Off unless the server says so: a server without videos would refuse the file at the end.\n video: false,\n videoTypes: () => ['video/mp4', 'video/webm'],\n videoMaxBytes: 2048 * 1048576,\n },\n)\n\n/** How long after the last keystroke the captions go to the server. */\nconst PAUSE = 800\n\nconst PICTURES = 'image/jpeg,image/png,image/webp,image/gif'\n\nconst admin = useAdmin()\nconst api = createCatalogApi(admin)\nconst editor = useProductEditor()\nconst locales = useLocales()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\nconst message = useErrorText()\n\nconst uploads = ref<UploadFile[]>([])\nconst address = ref('')\nconst fetching = ref(false)\n\nconst productId = computed(() => editor?.product.value?.id ?? null)\nconst locked = computed(() => editor === null || editor.locked.value)\n\n/** Writable, because that is how `WxSortableList` hands a reorder back. */\nconst images = computed<ProductImage[]>({\n get: () => editor?.images.value ?? [],\n set: (next) => {\n if (editor) editor.images.value = next\n },\n})\n\n/*\n * The editor's, so that an upload outlives this tab; a gallery under an editor that keeps none\n * makes its own. Never used while videos are off, so no upload can start then.\n */\nconst uploader: GalleryVideo | null =\n editor === null ? null : (editor.video ?? createGalleryVideo({ product: productId, images }))\n\nconst videos = computed(() => props.video && uploader !== null)\nconst accept = computed(() => (videos.value ? [PICTURES, ...props.videoTypes].join(',') : PICTURES))\n\nconst jobs = computed(() => uploader?.jobs.value ?? [])\n/* The ones that have no row yet: the frame is being taken, or the browser could not open the file. */\nconst pending = computed(() => jobs.value.filter((job) => job.image === null))\nconst unfinished = computed(() => (videos.value && !locked.value ? uploader!.unfinished.value : []))\n\nconst openLinkDialog = createModal<ProductImage | QueuedVideo, { product: number; image: number }>(\n VideoLinkDialog,\n)\n\n/* What was sent last, so an answer that arrives after further typing does not undo it. */\nlet timer: ReturnType<typeof setTimeout> | undefined\nlet sent = ''\n\nfunction captions(list: ProductImage[]): string {\n return JSON.stringify(list.map((image) => [image.id, image.alt, image.title]))\n}\n\nasync function saveGallery(failure: string): Promise<void> {\n const id = productId.value\n\n if (id === null) return\n\n clearTimeout(timer)\n const carrying = captions(images.value)\n sent = carrying\n\n try {\n const answer = await api.saveImages(id, images.value)\n\n // Typed on while the request was out: keep what is on screen; the next pause sends it.\n // The videos of rows still uploading are not the server's to overwrite either way.\n if (captions(images.value) === carrying) images.value = answer\n } catch (error) {\n toast.danger(`${failure} ${message(error)}`)\n }\n}\n\nfunction caption(image: ProductImage, field: 'alt' | 'title', text: unknown): void {\n images.value = images.value.map((one) =>\n one.id === image.id ? { ...one, [field]: (text ?? {}) as LocalizedValue } : one,\n )\n\n clearTimeout(timer)\n timer = setTimeout(() => void saveGallery(t('panel.gallery-captions-failed')), PAUSE)\n}\n\nfunction moved(): void {\n void saveGallery(t('panel.gallery-reorder-failed'))\n}\n\n/** A video file the screen's limits allow, said in words when they do not. */\nfunction acceptable(file: File): boolean {\n const refusal = videoRefusal(file, props.videoTypes, props.videoMaxBytes)\n\n if (refusal === 'type') toast.danger(t('panel.video-wrong-type'))\n if (refusal === 'size') {\n toast.danger(\n t('panel.video-too-large', { max: formatBytes(props.videoMaxBytes, locales.active.value) }),\n )\n }\n\n return refusal === null\n}\n\nfunction isVideo(file: File): boolean {\n return file.type.startsWith('video/')\n}\n\n/**\n * One file after another rather than all at once: the server numbers them in the order they\n * arrive, and a gallery whose order is whichever upload finished first is not the order anybody\n * chose them in. Videos go their own way — to the editor's queue, which works one at a time.\n */\nasync function add(files: UploadFile[]): Promise<void> {\n const id = productId.value\n\n if (id === null) return\n\n for (const file of files) {\n if (!file.raw) continue\n\n if (isVideo(file.raw)) {\n uploads.value = uploads.value.filter((one) => one.id !== file.id)\n\n if (videos.value && acceptable(file.raw)) uploader!.add(file.raw)\n\n continue\n }\n\n const mark = (patch: Partial<UploadFile>) => {\n uploads.value = uploads.value.map((one) => (one.id === file.id ? { ...one, ...patch } : one))\n }\n\n mark({ status: 'uploading', progress: 0 })\n\n try {\n const image = await api.addImage(id, file.raw, (progress) => mark({ progress }))\n\n images.value = [...images.value, image]\n uploads.value = uploads.value.filter((one) => one.id !== file.id)\n } catch (error) {\n mark({ status: 'error', error: message(error) })\n }\n }\n}\n\nfunction rejected(_file: File, reason: string): void {\n if (reason === 'type') {\n toast.danger(videos.value ? t('panel.gallery-wrong-type-video') : t('panel.gallery-wrong-type'))\n }\n}\n\nfunction queued(): void {\n toast.info(t('panel.video-queued'))\n}\n\nasync function fetchAddress(): Promise<void> {\n const id = productId.value\n const url = address.value.trim()\n\n if (id === null || url === '') return\n\n fetching.value = true\n\n try {\n const answer = await api.fetchImage(id, url)\n\n if ('queued' in answer) {\n queued()\n } else {\n images.value = [...images.value, answer]\n }\n\n address.value = ''\n } catch (error) {\n toast.danger(`${t('panel.gallery-failed')} ${message(error)}`)\n } finally {\n fetching.value = false\n }\n}\n\nasync function remove(image: ProductImage): Promise<void> {\n const id = productId.value\n\n if (id === null) return\n\n const agreed = await confirm({\n title: t('panel.gallery-remove-title'),\n message: t('panel.gallery-remove-text'),\n confirmText: t('panel.delete'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n // A video still going onto it stops first; one that made this picture takes it away itself.\n const job = jobs.value.find((one) => one.image === image.id)\n\n if (job !== undefined) await uploader!.cancel(job)\n if (!images.value.some((one) => one.id === image.id)) return\n\n try {\n await api.removeImage(id, image.id)\n images.value = images.value.filter((one) => one.id !== image.id)\n toast.success(t('panel.gallery-removed'))\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/* ------------------------------------------------------------------------ videos ----- */\n\nconst picker = useTemplateRef<HTMLInputElement>('picker')\n/* The row «Attach a video file…» was chosen on; `null` for continuing an unfinished upload. */\nlet pickFor: { image: number | null; resume?: UnfinishedVideo } | null = null\n\nfunction pickFile(image: number | null, resume?: UnfinishedVideo): void {\n pickFor = { image, resume }\n\n if (picker.value) {\n picker.value.value = ''\n picker.value.click()\n }\n}\n\nfunction picked(event: Event): void {\n const file = (event.target as HTMLInputElement).files?.[0]\n const target = pickFor\n pickFor = null\n\n if (!file || target === null || uploader === null) return\n\n if (target.resume !== undefined) {\n if (uploadFingerprint(file) !== target.resume.fingerprint) {\n toast.danger(\n t('panel.video-other-file', {\n name: target.resume.name,\n size: formatBytes(target.resume.size, locales.active.value),\n }),\n )\n\n return\n }\n\n // Onto the picture it was for, while that is still here; a row of its own otherwise.\n const image = images.value.some((one) => one.id === target.resume!.image)\n ? target.resume.image\n : null\n\n if (acceptable(file)) uploader.add(file, image)\n\n return\n }\n\n if (acceptable(file)) uploader.add(file, target.image)\n}\n\nasync function attachLink(image: ProductImage): Promise<void> {\n const id = productId.value\n\n if (id === null) return\n\n const answer = await openLinkDialog({ product: id, image: image.id })\n\n if (answer === undefined) return\n\n if ('queued' in answer) {\n queued()\n } else {\n images.value = images.value.map((one) => (one.id === answer.id ? answer : one))\n toast.success(t('panel.video-attached'))\n }\n}\n\nasync function detach(image: ProductImage): Promise<void> {\n const id = productId.value\n\n if (id === null) return\n\n const agreed = await confirm({\n title: t('panel.video-remove-title'),\n message:\n image.video?.provider === 'file'\n ? t('panel.video-remove-file-text')\n : t('panel.video-remove-link-text'),\n confirmText: t('panel.video-remove'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n const answer = await api.detachVideo(id, image.id)\n\n images.value = images.value.map((one) => (one.id === answer.id ? answer : one))\n toast.success(t('panel.video-removed'))\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction actions(image: ProductImage): RowAction[] {\n const list: RowAction[] = []\n const going = jobs.value.some((job) => job.image === image.id)\n\n if (videos.value && !going) {\n if (image.video) {\n list.push({\n key: 'detach',\n label: t('panel.video-remove'),\n icon: 'close',\n // Not red: the picture stays, and the question that follows says what goes.\n run: () => void detach(image),\n })\n } else {\n list.push(\n {\n key: 'file',\n label: t('panel.video-attach-file'),\n icon: 'upload',\n run: () => pickFile(image.id),\n },\n {\n key: 'link',\n label: t('panel.video-attach-link'),\n icon: 'link',\n run: () => void attachLink(image),\n },\n )\n }\n }\n\n list.push({\n key: 'delete',\n label: t('panel.delete'),\n icon: 'trash',\n danger: true,\n run: () => void remove(image),\n })\n\n return list\n}\n\n/* The text form of the triangle: Windows draws the bare character as a coloured emoji. */\nconst PLAY = '\\u25B6\\uFE0E'\n\nfunction videoLabel(image: ProductImage): string {\n const duration = image.video?.duration\n\n return duration ? `${PLAY} ${formatClock(duration)}` : PLAY\n}\n\nfunction jobOf(image: ProductImage) {\n return jobs.value.find((job) => job.image === image.id)\n}\n\nfunction unfinishedText(entry: UnfinishedVideo): string {\n const locale = locales.active.value\n\n return t('panel.video-unfinished', {\n name: entry.name,\n sent: formatBytes(entry.offset, locale),\n total: formatBytes(entry.size, locale),\n })\n}\n\n/* Leaving with a pause still running: send it rather than drop the last words typed. */\nonBeforeUnmount(() => {\n if (timer !== undefined && captions(images.value) !== sent) void saveGallery('')\n clearTimeout(timer)\n})\n</script>\n\n<template>\n <div class=\"wx-catalog-gallery\">\n <wx-alert v-if=\"!editor\" type=\"info\" variant=\"soft\" :description=\"t('panel.gallery-empty')\" />\n\n <template v-else>\n <wx-alert\n v-if=\"locked && images.length === 0\"\n type=\"info\"\n variant=\"soft\"\n :description=\"t('panel.gallery-locked')\"\n />\n\n <wx-alert\n v-for=\"entry in unfinished\"\n :key=\"entry.fingerprint\"\n type=\"warning\"\n variant=\"soft\"\n :description=\"unfinishedText(entry)\"\n >\n <template #actions>\n <wx-button size=\"sm\" variant=\"outline\" @click=\"pickFile(null, entry)\">\n {{ t('panel.video-choose-again') }}\n </wx-button>\n <wx-button size=\"sm\" variant=\"text\" @click=\"uploader!.forget(entry)\">\n {{ t('panel.video-forget') }}\n </wx-button>\n </template>\n </wx-alert>\n\n <wx-sortable-list\n v-if=\"images.length > 0\"\n v-model=\"images\"\n class=\"wx-catalog-gallery__list\"\n item-key=\"id\"\n :item-label=\"(image: ProductImage) => image.path.split('/').pop() ?? String(image.id)\"\n :disabled=\"locked\"\n :title=\"t('panel.gallery-order')\"\n @move=\"moved\"\n >\n <template #default=\"{ item, index }\">\n <div class=\"wx-catalog-gallery__item\">\n <!-- A row with a video opens the video: the picture is only its poster. -->\n <a\n class=\"wx-catalog-gallery__thumb\"\n :href=\"(item as ProductImage).video?.url ?? (item as ProductImage).url\"\n target=\"_blank\"\n rel=\"noopener\"\n :title=\"(item as ProductImage).video ? t('panel.video-open') : undefined\"\n >\n <img :src=\"(item as ProductImage).thumb ?? (item as ProductImage).url\" alt=\"\" />\n <wx-badge\n v-if=\"index === 0\"\n class=\"wx-catalog-gallery__main\"\n type=\"primary\"\n size=\"sm\"\n >\n {{ t('panel.gallery-main') }}\n </wx-badge>\n <span\n v-if=\"(item as ProductImage).video\"\n class=\"wx-catalog-gallery__play\"\n :aria-label=\"t('panel.video-badge')\"\n >\n {{ videoLabel(item as ProductImage) }}\n </span>\n <!-- On the picture rather than under the fields: a line of its own made every row\n taller and left the fields hanging off-centre. -->\n <span\n v-if=\"(item as ProductImage).width && (item as ProductImage).height\"\n class=\"wx-catalog-gallery__size\"\n >\n {{ (item as ProductImage).width }}×{{ (item as ProductImage).height }}\n </span>\n </a>\n\n <div class=\"wx-catalog-gallery__body\">\n <!-- No label above each field: the placeholder says which is which, and a row a\n label taller is a list where three pictures fill the screen. -->\n <div class=\"wx-catalog-gallery__words\">\n <wx-input\n :model-value=\"(item as ProductImage).alt\"\n localized\n size=\"sm\"\n :placeholder=\"t('panel.gallery-alt')\"\n :aria-label=\"t('panel.gallery-alt')\"\n :title=\"t('panel.gallery-alt-help')\"\n :disabled=\"locked\"\n @update:model-value=\"(text) => caption(item as ProductImage, 'alt', text)\"\n />\n <wx-input\n :model-value=\"(item as ProductImage).title\"\n localized\n size=\"sm\"\n :placeholder=\"t('panel.gallery-title')\"\n :aria-label=\"t('panel.gallery-title')\"\n :disabled=\"locked\"\n @update:model-value=\"(text) => caption(item as ProductImage, 'title', text)\"\n />\n </div>\n\n <video-progress\n v-if=\"uploader && jobOf(item as ProductImage)\"\n :job=\"jobOf(item as ProductImage)!\"\n :video=\"uploader\"\n />\n\n <wx-text\n v-else-if=\"videos && hasPlaceholderPoster(item as ProductImage)\"\n size=\"sm\"\n tone=\"muted\"\n >\n {{ t('panel.video-placeholder') }}\n </wx-text>\n </div>\n </div>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-row-menu\n v-if=\"!locked\"\n :actions=\"actions(item as ProductImage)\"\n :label=\"(item as ProductImage).path.split('/').pop()\"\n />\n </template>\n </wx-sortable-list>\n\n <wx-text v-else-if=\"!locked && pending.length === 0\" tone=\"muted\">\n {{ t('panel.gallery-empty') }}\n </wx-text>\n\n <!-- A video dropped without a picture, before its frame is a row of the gallery. -->\n <ul v-if=\"uploader && pending.length > 0\" class=\"wx-catalog-gallery__pending\">\n <li v-for=\"job in pending\" :key=\"job.key\" class=\"wx-catalog-gallery__item\">\n <span class=\"wx-catalog-gallery__thumb\">\n <img v-if=\"job.poster\" :src=\"job.poster\" alt=\"\" />\n <span class=\"wx-catalog-gallery__play\">{{ PLAY }}</span>\n </span>\n <div class=\"wx-catalog-gallery__body\">\n <wx-text size=\"sm\" class=\"wx-catalog-gallery__name\">{{ job.file.name }}</wx-text>\n <video-progress :job=\"job\" :video=\"uploader\" />\n </div>\n </li>\n </ul>\n\n <template v-if=\"!locked\">\n <wx-upload\n v-model=\"uploads\"\n :accept=\"accept\"\n multiple\n :button-text=\"videos ? t('panel.gallery-add-video') : t('panel.gallery-add')\"\n :hint=\"videos ? t('panel.gallery-hint-video') : t('panel.gallery-hint')\"\n @add=\"add\"\n @reject=\"rejected\"\n />\n\n <input\n v-if=\"videos\"\n ref=\"picker\"\n type=\"file\"\n class=\"wx-catalog-gallery__picker\"\n :accept=\"videoTypes.join(',')\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n @change=\"picked\"\n />\n\n <!-- A card of its own: a way in that is rarely taken, kept out of the way of the one that is.\n Not a <form>: the gallery stands inside the screen's own, and Enter here must fetch\n the picture rather than submit the product. -->\n <wx-card :title=\"t('panel.gallery-url')\">\n <div class=\"wx-catalog-gallery__address\">\n <wx-input\n v-model=\"address\"\n type=\"url\"\n size=\"sm\"\n placeholder=\"https://\"\n :aria-label=\"t('panel.gallery-url')\"\n @keydown.enter.prevent=\"fetchAddress\"\n />\n <wx-button\n size=\"sm\"\n variant=\"outline\"\n :loading=\"fetching\"\n :disabled=\"address.trim() === ''\"\n @click=\"fetchAddress\"\n >\n {{ t('panel.gallery-url-add') }}\n </wx-button>\n </div>\n <wx-text size=\"sm\" tone=\"muted\" class=\"wx-catalog-gallery__address-hint\">\n {{ videos ? t('panel.gallery-url-hint-video') : t('panel.gallery-url-hint') }}\n </wx-text>\n </wx-card>\n </template>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-catalog-gallery {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-16);\n container-type: inline-size;\n}\n\n.wx-catalog-gallery :deep(.wx-sortable-list__content) {\n min-width: 0;\n}\n\n.wx-catalog-gallery__item {\n display: grid;\n grid-template-columns: 72px minmax(0, 1fr);\n gap: var(--wx-space-12);\n align-items: center;\n}\n\n.wx-catalog-gallery__thumb {\n position: relative;\n display: block;\n width: 72px;\n aspect-ratio: 1;\n overflow: clip;\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-subtle);\n border: 1px solid var(--wx-border-default);\n}\n\n.wx-catalog-gallery__thumb img {\n width: 100%;\n height: 100%;\n min-width: 0;\n object-fit: contain;\n display: block;\n}\n\n.wx-catalog-gallery__main {\n position: absolute;\n inset-inline-start: var(--wx-space-2);\n inset-block-start: var(--wx-space-2);\n}\n\n/*\n * The dim behind a dialog and a fixed white, not the inverse text: that one turns dark in the dark\n * theme, and the strip is dark in both — it has to read on whatever the picture is.\n */\n.wx-catalog-gallery__size,\n.wx-catalog-gallery__play {\n position: absolute;\n background: var(--wx-bg-overlay);\n color: var(--wx-color-white);\n font-size: var(--wx-font-size-xs);\n line-height: 1.2;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n}\n\n.wx-catalog-gallery__size {\n inset-inline: 0;\n inset-block-end: 0;\n padding-block: var(--wx-space-2);\n text-align: center;\n}\n\n/*\n * In the middle, where every video's poster has it: a corner beside «Main» has no room left on a\n * 72-pixel picture once the word is in a longer language.\n */\n.wx-catalog-gallery__play {\n inset-block-start: 50%;\n inset-inline-start: 50%;\n translate: -50% -50%;\n padding: var(--wx-space-2) var(--wx-space-4);\n border-radius: var(--wx-radius-xs);\n}\n\n.wx-catalog-gallery__body {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n min-width: 0;\n}\n\n/* Two fields side by side while there is room for two readable lines of text, stacked below. */\n.wx-catalog-gallery__words {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--wx-space-8);\n}\n\n@container (min-width: 640px) {\n .wx-catalog-gallery__words {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n\n/* A phone: the picture above its words rather than a narrow column beside them. */\n@container (max-width: 420px) {\n .wx-catalog-gallery__item {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n.wx-catalog-gallery__pending {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n margin: 0;\n padding: var(--wx-space-8) var(--wx-space-12);\n list-style: none;\n border: 1px dashed var(--wx-border-default);\n border-radius: var(--wx-radius-md);\n}\n\n.wx-catalog-gallery__name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-catalog-gallery__picker {\n display: none;\n}\n\n.wx-catalog-gallery__address {\n display: flex;\n gap: var(--wx-space-8);\n align-items: center;\n max-width: 560px;\n}\n\n.wx-catalog-gallery__address > :first-child {\n flex: 1;\n min-width: 0;\n}\n\n.wx-catalog-gallery__address-hint {\n display: block;\n margin-block-start: var(--wx-space-8);\n}\n</style>\n","<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 })\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","<script setup lang=\"ts\">\nimport { computed, onMounted, reactive } from 'vue'\nimport { useAdmin, useTranslate, WxSourceSelect } from '@webx-ui/module-admin'\nimport {\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSelect,\n WxSpace,\n WxText,\n WxTreeSelect,\n type SelectOption,\n type TreeSelectValue,\n} from '@webx-ui/core'\nimport { useCatalogMessages } from './i18n'\nimport { useCategoryTree } from './store'\nimport type { BulkActionInfo, BulkParam } from './types'\n\n/**\n * What a bulk action asks for before it runs — a category, a label — and the last word before it\n * does: how many products it is about to touch. The fields are drawn from what the server said\n * about the action, so a satellite's action gets its dialog without this file knowing it: a\n * param with a `source` is a choice out of that reference book (a label, a brand), and one that is\n * not required says what choosing nothing does — for `set-brand`, take the brand off.\n */\nconst props = defineProps<{ action: BulkActionInfo; count: number }>()\n\nconst { open, resolve, dismiss } = useModal<Record<string, unknown>>()\n\nconst context = useAdmin()\nconst tree = useCategoryTree(context)\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n\nconst values = reactive<Record<string, unknown>>({})\n\nconst ready = computed(() =>\n props.action.params.every(\n (param) =>\n !param.rules?.includes('required') ||\n (values[param.name] !== undefined &&\n values[param.name] !== null &&\n values[param.name] !== ''),\n ),\n)\n\nonMounted(() => {\n if (props.action.params.some((param) => param.type === 'category')) {\n void tree.load().catch(() => undefined)\n }\n})\n\nfunction optionsOf(param: BulkParam): SelectOption[] {\n return Array.isArray(param.values)\n ? param.values.map((one) =>\n typeof one === 'object'\n ? { value: one.value, label: one.label }\n : { value: one, label: String(one) },\n )\n : []\n}\n\nfunction required(param: BulkParam): boolean {\n return param.rules?.includes('required') ?? false\n}\n\nfunction onCategory(param: BulkParam, value: TreeSelectValue): void {\n values[param.name] = Array.isArray(value) ? (value[0] ?? null) : value\n}\n\nfunction apply(): void {\n if (ready.value) resolve({ ...values })\n}\n</script>\n\n<template>\n <wx-dialog\n v-model:open=\"open\"\n :title=\"t('panel.bulk-confirm-title', { action: action.label })\"\n :width=\"440\"\n >\n <wx-text tone=\"muted\" size=\"sm\">{{ t('panel.bulk-confirm-text', { count }) }}</wx-text>\n\n <wx-form-item\n v-for=\"param in action.params\"\n :key=\"param.name\"\n :label=\"param.label\"\n :required=\"required(param)\"\n class=\"wx-catalog-bulk-params__field\"\n >\n <wx-tree-select\n v-if=\"param.type === 'category'\"\n :model-value=\"(values[param.name] as number | null | undefined) ?? null\"\n :nodes=\"tree.nodes.value ?? []\"\n node-key=\"id\"\n label-key=\"name\"\n children-key=\"children\"\n check-strictly\n filterable\n :filter-placeholder=\"t('panel.search-categories')\"\n :aria-label=\"param.label\"\n @update:model-value=\"(value: TreeSelectValue) => onCategory(param, value)\"\n />\n <wx-source-select\n v-else-if=\"param.source\"\n :model-value=\"(values[param.name] as number | null | undefined) ?? null\"\n :source=\"param.source\"\n filterable\n :clearable=\"!required(param)\"\n :placeholder=\"required(param) ? undefined : t('panel.bulk-param-empty')\"\n :aria-label=\"param.label\"\n @update:model-value=\"(value: unknown) => (values[param.name] = value)\"\n />\n <wx-select\n v-else-if=\"Array.isArray(param.values)\"\n :model-value=\"(values[param.name] as string | number | undefined) ?? null\"\n :options=\"optionsOf(param)\"\n :aria-label=\"param.label\"\n @update:model-value=\"(value: unknown) => (values[param.name] = value)\"\n />\n <wx-input v-else v-model=\"values[param.name] as string\" :aria-label=\"param.label\" />\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\n :type=\"action.permission === 'catalog.delete' && !action.trashed ? 'danger' : 'primary'\"\n :disabled=\"!ready\"\n @click=\"apply\"\n >\n {{ t('panel.bulk-apply') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-catalog-bulk-params__field {\n margin-top: var(--wx-space-16);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n createModal,\n toast,\n WxButton,\n WxDropdown,\n WxDropdownItem,\n WxIcon,\n WxProgress,\n WxText,\n} from '@webx-ui/core'\nimport BulkParamsDialog from './BulkParamsDialog.vue'\nimport { createCatalogApi } from './api'\nimport { useCatalogMessages } from './i18n'\nimport type { BulkActionInfo, BulkRun, BulkSelection } from './types'\n\n/**\n * The actions over what is picked in the list (§11.4), and the run they start.\n *\n * Which actions there are is the server's answer — the core's and every satellite's, only the\n * ones this administrator may start. A small selection is done inside the request and answered at\n * once; a large one is a run in the background, polled here with its progress, and its refusals\n * are listed by product when it ends. The list reloads either way, and `finished` carries the ids\n * that refused — what the list leaves picked, so they can be put right and tried again.\n */\nconst props = defineProps<{ count: number; selection: BulkSelection }>()\nconst emit = defineEmits<{ finished: [refused: number[]] }>()\n\n/** Often enough to look alive, rarely enough that a run of forty thousand is not a request storm. */\nconst POLL = 1500\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\nconst message = useErrorText()\n\nconst params = createModal<Record<string, unknown>, { action: BulkActionInfo; count: number }>(\n BulkParamsDialog,\n)\n\nconst actions = ref<BulkActionInfo[]>([])\nconst run = ref<BulkRun | null>(null)\nconst starting = ref(false)\nlet timer: ReturnType<typeof setTimeout> | undefined\n\nconst running = computed(\n () => run.value !== null && (run.value.status === 'queued' || run.value.status === 'running'),\n)\nconst refused = computed(() =>\n run.value !== null && !running.value && run.value.failed > 0 ? run.value : null,\n)\n\nonMounted(async () => {\n try {\n actions.value = await api.bulkActions()\n } catch {\n actions.value = []\n }\n})\n\nonBeforeUnmount(() => clearTimeout(timer))\n\n/** An action that asks for something, or deletes, is said once more with the number in it. */\nfunction asks(action: BulkActionInfo): boolean {\n return action.params.length > 0 || (action.permission === 'catalog.delete' && !action.trashed)\n}\n\nasync function start(action: BulkActionInfo): Promise<void> {\n let given: Record<string, unknown> = {}\n\n if (asks(action)) {\n const answer = await params({ action, count: props.count })\n\n if (!answer) return\n given = answer\n }\n\n starting.value = true\n\n try {\n follow(await api.startBulk(action.key, given, props.selection))\n } catch (error) {\n toast.danger(message(error))\n } finally {\n starting.value = false\n }\n}\n\nfunction follow(answer: BulkRun): void {\n run.value = answer\n\n if (answer.status === 'queued' || answer.status === 'running') {\n timer = setTimeout(() => void poll(answer.id!), POLL)\n\n return\n }\n\n finish(answer)\n}\n\nasync function poll(id: number): Promise<void> {\n try {\n follow(await api.bulkRun(id))\n } catch {\n // A missed answer is not the end of the run: ask again.\n timer = setTimeout(() => void poll(id), POLL * 2)\n }\n}\n\nfunction finish(answer: BulkRun): void {\n if (answer.total === 0) toast.warning(t('panel.bulk-nothing'))\n else if (answer.status === 'failed') toast.danger(t('panel.bulk-stopped', { count: answer.done }))\n else if (answer.failed === 0) toast.success(t('panel.bulk-done', { count: answer.done }))\n\n if (answer.failed === 0 && answer.status !== 'failed') run.value = null\n\n emit(\n 'finished',\n answer.errors.map((error) => error.id),\n )\n}\n</script>\n\n<template>\n <div class=\"wx-catalog-bulk\">\n <wx-dropdown v-if=\"actions.length > 0 && !running\" align=\"start\">\n <template #trigger>\n <wx-button size=\"sm\" variant=\"outline\" :loading=\"starting\">\n {{ t('panel.bulk-actions') }}\n <wx-icon name=\"chevron-down\" size=\"sm\" />\n </wx-button>\n </template>\n\n <wx-dropdown-item\n v-for=\"action in actions\"\n :key=\"action.key\"\n :tone=\"action.permission === 'catalog.delete' && !action.trashed ? 'danger' : 'default'\"\n @click=\"start(action)\"\n >\n {{ action.label }}\n </wx-dropdown-item>\n </wx-dropdown>\n\n <div v-if=\"running && run\" class=\"wx-catalog-bulk__progress\" role=\"status\">\n <wx-progress\n :value=\"run.done + run.failed\"\n :max=\"Math.max(1, run.total)\"\n size=\"sm\"\n :aria-label=\"run.label\"\n />\n <wx-text size=\"sm\" tone=\"muted\">\n {{ run.label }} ·\n {{ t('panel.bulk-running', { done: run.done + run.failed, total: run.total }) }}\n </wx-text>\n </div>\n\n <div v-if=\"refused\" class=\"wx-catalog-bulk__refused\">\n <wx-text size=\"sm\" weight=\"medium\">\n {{ refused.label }} · {{ t('panel.bulk-done', { count: refused.done }) }} ·\n {{ t('panel.bulk-refused', { count: refused.failed }) }}\n </wx-text>\n <details class=\"wx-catalog-bulk__errors\">\n <summary>{{ t('panel.bulk-errors') }}</summary>\n <ul>\n <li v-for=\"error in refused.errors\" :key=\"error.id\">\n <strong>{{ error.name }}</strong> — {{ error.message }}\n </li>\n </ul>\n </details>\n <wx-button size=\"sm\" variant=\"text\" @click=\"run = null\">{{\n t('panel.bulk-close')\n }}</wx-button>\n </div>\n </div>\n</template>\n\n<style scoped>\n/* No box of its own: the button, the progress and the refusals stand in the selection strip\n around it, which already wraps — a box here would push the button onto a line of its own. */\n.wx-catalog-bulk {\n display: contents;\n}\n\n.wx-catalog-bulk__progress {\n display: grid;\n gap: var(--wx-space-4);\n flex: 1 1 240px;\n min-width: 0;\n}\n\n.wx-catalog-bulk__refused {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--wx-space-8);\n flex-basis: 100%;\n min-width: 0;\n}\n\n.wx-catalog-bulk__errors {\n flex-basis: 100%;\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-catalog-bulk__errors summary {\n cursor: pointer;\n color: var(--wx-text-muted);\n}\n\n.wx-catalog-bulk__errors ul {\n margin: var(--wx-space-8) 0 0;\n padding-inline-start: var(--wx-space-18);\n max-height: 200px;\n overflow: auto;\n}\n</style>\n","import type { BadgeType } from '@webx-ui/core'\n\n/**\n * The six tones a record of a reference book is painted in (labels, stock statuses) — the\n * server's `Dictionary::TONES`. A tone and not a colour: the site puts it into a class, the panel\n * into a tag of the same tone, and nobody writes a hex anywhere.\n */\nexport const TONES = ['neutral', 'primary', 'success', 'warning', 'danger', 'info'] as const\n\nexport type Tone = (typeof TONES)[number]\n\n/** The tag the panel draws a tone as. `neutral` is the core's `default`; the rest match by name. */\nexport function toneBadge(tone: unknown): BadgeType {\n return typeof tone === 'string' &&\n tone !== 'neutral' &&\n (TONES as readonly string[]).includes(tone)\n ? (tone as BadgeType)\n : 'default'\n}\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { WxBadge, WxText } from '@webx-ui/core'\nimport { toneBadge } from './tones'\nimport type { ColumnValue } from './types'\n\n/**\n * One satellite's value in a row of the list (`ProductColumns`, §7.4) — drawn by its shape, so\n * that a satellite gets a column without this package knowing it:\n *\n * - words or a number — as they are;\n * - a record `{ name }` — its name, muted when `visible: false` (a brand taken off the site stays\n * on its products, and the list is where somebody sees that it does);\n * - a record with a tone, `{ name, color }` — a tag of that tone (a stock status);\n * - a list of those — tags side by side (labels), in the order the server gave.\n */\ndefineOptions({ name: 'WxCatalogColumnValue' })\n\nconst props = defineProps<{ value: ColumnValue | undefined }>()\n\ninterface Piece {\n key: string\n name: string\n tone: string | null\n muted: boolean\n}\n\nfunction piece(value: unknown, index: number): Piece | null {\n if (value === null || value === undefined || value === '') return null\n\n if (typeof value === 'object' && !Array.isArray(value) && 'name' in value) {\n const record = value as { id?: unknown; name?: unknown; color?: unknown; visible?: unknown }\n\n return {\n key: String(record.id ?? index),\n name: String(record.name ?? ''),\n tone: typeof record.color === 'string' ? record.color : null,\n muted: record.visible === false,\n }\n }\n\n if (typeof value === 'boolean') {\n return { key: String(index), name: value ? '✓' : '—', tone: null, muted: !value }\n }\n\n return { key: String(index), name: String(value), tone: null, muted: false }\n}\n\nconst pieces = computed<Piece[]>(() =>\n (Array.isArray(props.value) ? props.value : [props.value])\n .map((one, index) => piece(one, index))\n .filter((one): one is Piece => one !== null),\n)\n</script>\n\n<template>\n <span v-if=\"pieces.length === 0\" class=\"wx-catalog-column-value is-empty\">—</span>\n <span v-else class=\"wx-catalog-column-value\">\n <template v-for=\"one in pieces\" :key=\"one.key\">\n <wx-badge v-if=\"one.tone !== null\" :type=\"toneBadge(one.tone)\" size=\"sm\">\n {{ one.name }}\n </wx-badge>\n <wx-text v-else size=\"sm\" truncate :tone=\"one.muted ? 'muted' : undefined\">\n {{ one.name }}\n </wx-text>\n </template>\n </span>\n</template>\n\n<style scoped>\n.wx-catalog-column-value {\n display: inline-flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-4);\n min-width: 0;\n max-width: 100%;\n}\n\n.wx-catalog-column-value.is-empty {\n color: var(--wx-text-muted);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, ref, useTemplateRef } from 'vue'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport {\n useLocales,\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSpace,\n} from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { useCatalogMessages } from './i18n'\nimport { firstError } from './errors'\nimport type { ProductDetail } from './types'\n\n/**\n * A new product: what it is called, and nothing else. It is created unpublished and without a\n * category — the form is where there is room for those, and a product cannot be published before\n * it has a main category anyway (decision 3). The address is made of the name.\n */\nconst { open, resolve, dismiss } = useModal<ProductDetail>()\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst locales = useLocales()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n\nconst name = ref('')\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\nconst field = useTemplateRef<HTMLElement>('field')\n\nonMounted(async () => {\n await nextTick()\n field.value?.querySelector('input')?.focus()\n})\n\nasync function submit(): Promise<void> {\n if (name.value.trim() === '' || saving.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n resolve(\n await api.createProduct({\n name: { [locales.active.value]: name.value.trim() },\n is_published: false,\n }),\n )\n } catch (error) {\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-product')\" :width=\"440\">\n <wx-form-item\n :label=\"t('product.name')\"\n :error=\"firstError(errors, 'name') ?? firstError(errors, 'slug')\"\n required\n >\n <div ref=\"field\">\n <wx-input v-model=\"name\" :aria-label=\"t('product.name')\" @keyup.enter=\"submit\" />\n </div>\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=\"name.trim() === ''\" @click=\"submit\">\n {{ t('panel.create') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","import type { LocationQuery } from 'vue-router'\nimport type { FacetChoice, FacetInfo } from './types'\n\n/**\n * The list's facet filters live in the address, one parameter per facet — `f.<key>` — so that\n * coming back from a product lands on the same filter, and a filtered list can be linked to\n * (the tree's \"Show its products\" does exactly that):\n *\n * f.category=12,14 values of a terms or tree facet\n * f.price=100-500 a range; either end may be empty\n * f.in-stock=1 a toggle\n *\n * The server's own format is another (`facets[key][]=…`, see `productSearch()`); this one is for\n * a person reading the address bar.\n */\nexport const FACET_PREFIX = 'f.'\n\nexport function readFacets(query: LocationQuery, facets: FacetInfo[]): Record<string, FacetChoice> {\n const chosen: Record<string, FacetChoice> = {}\n\n for (const facet of facets) {\n const raw = query[`${FACET_PREFIX}${facet.key}`]\n const text = typeof raw === 'string' ? raw.trim() : ''\n\n if (text === '') continue\n\n if (facet.kind === 'toggle') {\n if (text === '1') chosen[facet.key] = true\n } else if (facet.kind === 'range') {\n const [min, max] = text.split('-', 2).map((end) => (end === '' ? null : Number(end)))\n const range = {\n min: Number.isFinite(min) ? min : null,\n max: Number.isFinite(max) ? max : null,\n }\n\n if (range.min !== null || range.max !== null) chosen[facet.key] = range\n } else {\n const values = text.split(',').filter((value) => value !== '')\n\n if (values.length > 0) chosen[facet.key] = values\n }\n }\n\n return chosen\n}\n\n/** One facet's choice as its parameter; `undefined` takes the parameter away. */\nexport function writeFacet(choice: FacetChoice | null | undefined): string | undefined {\n if (choice === null || choice === undefined) return undefined\n if (choice === true) return '1'\n\n if (Array.isArray(choice)) return choice.length > 0 ? choice.join(',') : undefined\n\n const min = choice.min ?? null\n const max = choice.max ?? null\n\n return min === null && max === null ? undefined : `${min ?? ''}-${max ?? ''}`\n}\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, useTemplateRef, watch } from 'vue'\nimport { useRoute, useRouter, type LocationQueryRaw } from 'vue-router'\nimport {\n rowMenuWidth,\n useAdmin,\n useErrorText,\n useTranslate,\n WxDate,\n WxFilterChips,\n WxListScreen,\n WxRowMenu,\n type AppliedFilter,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n toast,\n useElementWidth,\n WxBadge,\n WxButton,\n WxEntityCard,\n WxFormItem,\n WxIcon,\n WxInputNumber,\n WxSelect,\n WxSwitch,\n WxTable,\n WxText,\n WxTooltip,\n WxTreeSelect,\n type RowKey,\n type SelectOption,\n type TabItem,\n type TableColumn,\n type TableState,\n type TabValue,\n type TreeSelectValue,\n} from '@webx-ui/core'\nimport BulkBar from './BulkBar.vue'\nimport ColumnValue from './ColumnValue.vue'\nimport ProductCreateDialog from './ProductCreateDialog.vue'\nimport { createCatalogApi } from './api'\nimport { FACET_PREFIX, readFacets, writeFacet } from './filters'\nimport { useCatalogMessages } from './i18n'\nimport { flatten, useCategoryTree, useFacetRegistry } from './store'\nimport type {\n BulkSelection,\n FacetChoice,\n FacetInfo,\n ProductDetail,\n ProductQuery,\n ProductRow,\n ProductSort,\n SortInfo,\n ProductsPage,\n} from './types'\n\n/**\n * The products of the catalogue (§11.1): a page of rows, the views of decision 3 over it, the\n * facets as filters behind the funnel, and a checkbox per row.\n *\n * The checkboxes are for the bulk actions (§11.4): a selection outlives the page it was made on,\n * which is what \"select these twelve across three pages\" needs, and \"everything found\" picks the\n * filter itself rather than its ids. The actions and their progress are `BulkBar`'s.\n *\n * What the list shows beyond the core's columns is the satellites' (`ProductColumns`, §7.4): the\n * server says which, and each row carries its values under their keys.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\n/** Where the row stops being a row. Below it the table draws cards and this screen draws one. */\nconst CARDS = 640\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst tree = useCategoryTree(context)\nconst registry = useFacetRegistry(context)\nconst route = useRoute()\nconst router = useRouter()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n/* The words a list needs as soon as it has filters belong to the panel, not to the catalogue. */\nconst admin = useTranslate('webx-admin')\nconst message = useErrorText()\n\nconst root = useTemplateRef<HTMLElement>('root')\nconst width = useElementWidth(root)\n\nconst page = ref<ProductsPage | null>(null)\nconst loading = ref(true)\nconst selected = ref<RowKey[]>([])\n/** The whole of what the filter finds is picked, not only the ticked rows. */\nconst everything = ref(false)\n/* The table's own search and page, started from the address: coming back lands where it left. */\nconst search = ref(typeof route.query.q === 'string' ? route.query.q : '')\nconst pageNumber = ref(Number(route.query.page ?? 1) || 1)\n/* The page size the table last asked for: a reload after a filter has to page the same way. */\nlet perPage: number | undefined\n\nconst create = createModal<ProductDetail, Record<string, never>>(ProductCreateDialog)\n\nconst canManage = computed(() => context.can('catalog.manage'))\nconst canDelete = computed(() => context.can('catalog.delete'))\n\nconst title = computed(() => t('module.products'))\n\n/** Everything the list is looking at lives in the address — coming back lands in the same place. */\nconst view = computed<TabValue>({\n get: () => (typeof route.query.view === 'string' ? route.query.view : ''),\n set: (value) => {\n void router.replace({\n query: { ...route.query, view: value === '' ? undefined : String(value), page: undefined },\n })\n },\n})\n\n/**\n * The orders are the registry's (§7.2): a satellite adds its own, and a site without prices has\n * none by price — a sort the server does not know is a 422, so nothing is offered or asked for\n * that it did not list. Until it answers, the default order is the one there is.\n */\nconst sorts = computed<SortInfo[]>(() =>\n registry.sorts.value.length > 0\n ? registry.sorts.value\n : [{ key: 'default', label: t('panel.sort-default') }],\n)\n\nconst sort = computed<ProductSort>(() => {\n const asked = route.query.sort\n\n return typeof asked === 'string' && registry.sorts.value.some((one) => one.key === asked)\n ? asked\n : 'default'\n})\n\nconst facets = computed<FacetInfo[]>(() => registry.facets.value ?? [])\nconst chosen = computed(() => readFacets(route.query, facets.value))\n\n/** The counter of decision 3 rides on its own tab, where somebody looking for those products looks. */\nconst views = computed<TabItem[]>(() => {\n const missing = page.value?.counts.no_category ?? 0\n\n return [\n { value: '', label: t('panel.view-all') },\n { value: 'published', label: t('panel.view-published') },\n { value: 'unpublished', label: t('panel.view-unpublished') },\n {\n value: 'no-category',\n label: t('panel.view-no-category'),\n badge: missing > 0 ? missing : undefined,\n },\n ]\n})\n\n/** Absent from every row when the site switched prices off — then the column is not drawn. */\nconst priced = computed(() => (page.value?.data ?? []).some((row) => 'price' in row))\nconst extra = computed(() => page.value?.columns ?? [])\n\nconst asCards = computed(() => width.value > 0 && width.value < CARDS)\n\nconst categoryNames = computed(\n () => new Map(flatten(tree.nodes.value ?? []).map((node) => [String(node.id), node.name])),\n)\n\n/**\n * Every column but the name carries a width and the table is laid out `fixed`, so the ones that\n * drop below a breakpoint give their room to the name. They go in the order they can be spared:\n * when it changed, then the satellites', then the category — what is left is the picture, the\n * name and the state, which is what identifies a product and what somebody is looking for.\n */\nconst EXTRA = 140\nconst CATEGORY_FROM = 860\n\n/*\n * The width each satellite's column is drawn from on: one after another after the category's, so\n * a third satellite drops first rather than taking its room out of the name — the table is laid out\n * `fixed`, and there the name's `minWidth` gives way to the widths around it (three satellites at\n * 1080px left it 59px). «Updated» goes before any of them.\n */\nconst extraFrom = (index: number) => CATEGORY_FROM + EXTRA * (index + 1)\n\nconst columns = computed<TableColumn<ProductRow>[]>(() => {\n if (asCards.value) {\n // The card is the row: a tap opens the product, and a line of its own for the `···` would make\n // every card half as tall again for a menu whose first item is that same tap.\n return [{ key: 'card', label: '' }]\n }\n\n return [\n { key: 'image', label: '', width: 64 },\n { key: 'name', label: t('panel.column-product'), minWidth: 200 },\n { key: 'category', label: t('product.category'), width: 180, hideBelow: CATEGORY_FROM },\n ...(priced.value\n ? [{ key: 'price', label: t('product.price'), width: 150, align: 'right' as const }]\n : []),\n ...extra.value.map((column, index) => ({\n key: `x.${column.key}`,\n label: column.label,\n width: EXTRA,\n hideBelow: extraFrom(index),\n })),\n { key: 'state', label: t('panel.column-state'), width: 150, hideBelow: 720 },\n {\n key: 'updated_at',\n label: t('panel.column-updated'),\n width: 130,\n hideBelow: Math.max(1180, extraFrom(extra.value.length - 1) + 130),\n },\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right' },\n ]\n})\n\nfunction query(state?: TableState): ProductQuery {\n return {\n q: state?.search ?? search.value,\n state: view.value as ProductQuery['state'],\n sort: sort.value,\n page: state?.page ?? pageNumber.value,\n per_page: state?.perPage ?? perPage,\n facets: chosen.value,\n }\n}\n\nlet asked = 0\n\nasync function load(state?: TableState): Promise<void> {\n const mine = ++asked\n\n loading.value = true\n\n try {\n const answer = await api.products(query(state))\n\n // A slower answer to an older question must not land over a newer one.\n if (mine === asked) page.value = answer\n } catch (error) {\n toast.danger(message(error))\n } finally {\n if (mine === asked) loading.value = false\n }\n}\n\nfunction onState(state: TableState): void {\n perPage = state.perPage\n\n const wanted: LocationQueryRaw = {\n ...route.query,\n q: state.search === '' ? undefined : state.search,\n page: state.page === 1 ? undefined : String(state.page),\n }\n\n void router.replace({ query: wanted })\n void load(state)\n}\n\nfunction narrow(key: string, choice: FacetChoice | null): void {\n void router.replace({\n query: { ...route.query, [`${FACET_PREFIX}${key}`]: writeFacet(choice), page: undefined },\n })\n}\n\nfunction order(value: unknown): void {\n void router.replace({\n query: {\n ...route.query,\n sort: typeof value === 'string' && value !== 'default' ? value : undefined,\n page: undefined,\n },\n })\n}\n\nfunction clearFilters(): void {\n const rest = Object.fromEntries(\n Object.entries(route.query).filter(([name]) => !name.startsWith(FACET_PREFIX)),\n )\n\n void router.replace({ query: { ...rest, page: undefined } })\n}\n\n/**\n * What a terms facet can be narrowed to: the values the list counted for it, with how many\n * products each would leave. The engine counts a facet without its own choice, so picking one\n * value does not take the others away; a chosen value that counts nothing any more stays offered,\n * or it could not be taken off.\n */\nfunction optionsOf(key: string): SelectOption[] {\n const counted = page.value?.facets?.[key]?.values ?? []\n const options: SelectOption[] = counted.map((one) => ({\n value: one.value,\n label: `${one.label} (${one.count})`,\n }))\n\n for (const value of valuesOf(key)) {\n if (!counted.some((one) => one.value === value)) options.push({ value, label: value })\n }\n\n return options\n}\n\n/** Values of a terms facet, or of the tree, for a chip: \"Category: Laptops, Tablets\". */\nfunction valueLabel(facet: FacetInfo, value: string | number): string {\n if (facet.kind === 'tree') return categoryNames.value.get(String(value)) ?? `#${value}`\n\n return (\n page.value?.facets?.[facet.key]?.values?.find((one) => one.value === String(value))?.label ??\n String(value)\n )\n}\n\n/** The bounds a range facet has in the list as it stands, said in its fields before anything is typed. */\nfunction boundOf(key: string, end: 'min' | 'max'): string {\n const bound = page.value?.facets?.[key]?.[end]\n\n return typeof bound === 'number' ? money.value.format(bound) : ''\n}\n\nconst applied = computed<AppliedFilter[]>(() =>\n facets.value.flatMap((facet) => {\n const choice = chosen.value[facet.key]\n\n if (choice === undefined) return []\n\n let said: string\n\n if (choice === true) said = facet.label\n else if (Array.isArray(choice)) {\n said = `${facet.label}: ${choice.map((value) => valueLabel(facet, value)).join(', ')}`\n } else {\n said = `${facet.label}: ${choice.min ?? '…'} – ${choice.max ?? '…'}`\n }\n\n return [{ key: facet.key, label: said, clear: () => narrow(facet.key, null) }]\n }),\n)\n\nconst emptyText = computed(() => {\n if (search.value !== '') return t('panel.empty-search')\n if (applied.value.length > 0 || view.value !== '') return t('panel.empty-search')\n\n return t('panel.empty-products')\n})\n\nfunction rangeOf(key: string): { min?: number | null; max?: number | null } {\n const choice = chosen.value[key]\n\n return choice && !Array.isArray(choice) && choice !== true ? choice : {}\n}\n\nfunction valuesOf(key: string): string[] {\n const choice = chosen.value[key]\n\n return Array.isArray(choice) ? choice.map(String) : []\n}\n\nfunction treeChoice(key: string): TreeSelectValue {\n return valuesOf(key).map(Number)\n}\n\n/** How many the list finds as it is filtered — what \"select everything found\" would pick. */\nconst found = computed(() => page.value?.total ?? 0)\n\n/**\n * \"Everything found\" is the list's query, not its ids (§11.4): the server turns it into ids when\n * the run starts, so forty thousand products never travel through the browser.\n */\nconst bulkSelection = computed<BulkSelection>(() => {\n if (!everything.value) return { ids: selected.value.map(Number) }\n\n const facetsQuery: Record<\n string,\n Array<string | number> | { min?: number | null; max?: number | null }\n > = {}\n\n for (const [key, choice] of Object.entries(chosen.value)) {\n // A toggle is sent the way the list sends it: a list with `1` in it.\n facetsQuery[key] = choice === true ? ['1'] : choice\n }\n\n return { query: { q: search.value, state: String(view.value), facets: facetsQuery } }\n})\n\nfunction clearSelection(): void {\n selected.value = []\n everything.value = false\n}\n\n/**\n * What refused stays picked, and nothing else: the strip keeps its list of refusals beside the\n * very products it names, ready for another go once they are put right.\n */\nfunction onBulkFinished(refused: number[]): void {\n everything.value = false\n selected.value = refused\n void load()\n}\n\n/* One source per value, not one getter returning an array: a page turn must not fire a second\n request racing the one `onState` already sent. */\nwatch([view, sort, () => JSON.stringify(chosen.value), search], () => {\n // \"Everything found\" was everything this filter found; another filter finds something else.\n everything.value = false\n})\n\nwatch([view, sort, () => JSON.stringify(chosen.value)], () => {\n // A new filter is a new list: the page it was on belongs to the list it was on. If that moves\n // the table's page, the table asks for itself; otherwise nobody would.\n if (pageNumber.value !== 1) pageNumber.value = 1\n else void load()\n})\n\nonMounted(() => {\n void registry.load().catch(() => undefined)\n void tree.load().catch(() => undefined)\n})\n\nfunction open(product: ProductRow): void {\n void router.push({ path: `${props.base}/products/${product.id}` })\n}\n\nasync function add(): Promise<void> {\n const detail = await create({})\n\n if (!detail) return\n\n open(detail.product)\n}\n\nasync function publish(product: ProductRow, on: boolean): Promise<void> {\n try {\n await api.saveProduct(product.id, { is_published: on })\n toast.success(on ? t('panel.product-published') : t('panel.product-unpublished'))\n await load()\n } catch (error) {\n // Publishing without a main category is refused (decision 3), and the server says so.\n toast.danger(message(error))\n }\n}\n\nasync function remove(product: ProductRow): Promise<void> {\n const agreed = await confirm({\n title: t('panel.delete-product-title', { name: product.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(product.id)\n selected.value = selected.value.filter((key) => key !== product.id)\n toast.success(t('panel.product-deleted'))\n await load()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction actionsFor(product: ProductRow): RowAction[] {\n const actions: RowAction[] = [\n { key: 'open', icon: 'edit', label: t('panel.open'), run: () => open(product) },\n ]\n\n if (product.url) {\n actions.push({ key: 'site', icon: 'link', label: t('panel.open-on-site'), href: product.url })\n }\n\n if (canManage.value) {\n actions.push(\n product.is_published\n ? {\n key: 'unpublish',\n icon: 'eye-off',\n label: t('panel.unpublish'),\n run: () => void publish(product, false),\n }\n : {\n key: 'publish',\n icon: 'eye',\n label: t('panel.publish'),\n run: () => void publish(product, true),\n },\n )\n }\n\n if (canDelete.value) {\n actions.push({\n key: 'delete',\n icon: 'trash',\n label: t('panel.delete'),\n danger: true,\n run: () => void remove(product),\n })\n }\n\n return actions\n}\n\nfunction stateType(product: ProductRow): 'success' | 'warning' | 'default' {\n if (!product.is_published) return 'default'\n\n return product.visible ? 'success' : 'warning'\n}\n\nconst money = computed(\n () =>\n new Intl.NumberFormat(context.i18n.state.locale, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }),\n)\n\nfunction price(value: number | null | undefined): string {\n return value === null || value === undefined ? '—' : money.value.format(value)\n}\n\nfunction unit(product: ProductRow): string {\n return product.unit ? ` / ${t(`units.${product.unit}`)}` : ''\n}\n\nfunction hasValue(value: unknown): boolean {\n return Array.isArray(value)\n ? value.length > 0\n : value !== null && value !== undefined && value !== ''\n}\n\nconst actions = computed<ScreenAction[]>(() => {\n const leads: ScreenAction[] = []\n\n if (canManage.value) {\n leads.push({\n key: 'new',\n label: t('panel.new-product'),\n icon: 'plus',\n primary: true,\n run: () => void add(),\n })\n }\n\n if (canDelete.value) {\n leads.push({\n key: 'deleted',\n label: t('module.deleted'),\n icon: 'trash',\n menu: true,\n run: () => void router.push(`${props.base}/deleted`),\n })\n }\n\n return leads\n})\n</script>\n\n<template>\n <div ref=\"root\" class=\"wx-catalog-products\">\n <wx-list-screen v-model:view=\"view\" :title=\"title\" :views=\"views\" :actions=\"actions\">\n <!-- What is picked, said above the rows it was picked from, with what can be done to it. -->\n <div v-if=\"selected.length > 0 || everything\" class=\"wx-catalog-products__selection\">\n <wx-text size=\"sm\" weight=\"medium\">\n {{\n everything\n ? t('panel.selected-all', { count: found })\n : t('panel.selected', { count: selected.length })\n }}\n </wx-text>\n <wx-button\n v-if=\"!everything && found > selected.length\"\n size=\"sm\"\n variant=\"text\"\n @click=\"everything = true\"\n >\n {{ t('panel.select-all-found', { count: found }) }}\n </wx-button>\n <wx-button size=\"sm\" variant=\"text\" @click=\"clearSelection\">\n {{ t('panel.select-clear') }}\n </wx-button>\n <bulk-bar\n v-if=\"canManage || canDelete\"\n :count=\"everything ? found : selected.length\"\n :selection=\"bulkSelection\"\n @finished=\"onBulkFinished\"\n />\n </div>\n\n <wx-table\n v-model:selected=\"selected\"\n v-model:search=\"search\"\n v-model:page=\"pageNumber\"\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n selectable\n clickable\n hover\n flush\n :loading=\"loading\"\n layout=\"fixed\"\n :cards-below=\"CARDS\"\n :filters-count=\"applied.length\"\n :filters-label=\"admin('filters.title')\"\n :search-placeholder=\"t('panel.search-products')\"\n :empty-text=\"emptyText\"\n :aria-label=\"title\"\n @row-click=\"open\"\n @state-change=\"onState\"\n >\n <template #filters>\n <wx-form-item :label=\"t('panel.sort')\">\n <wx-select\n :model-value=\"sort\"\n :options=\"sorts.map((one) => ({ value: one.key, label: one.label }))\"\n size=\"sm\"\n @update:model-value=\"order\"\n />\n </wx-form-item>\n\n <wx-form-item v-for=\"facet in facets\" :key=\"facet.key\" :label=\"facet.label\">\n <wx-tree-select\n v-if=\"facet.kind === 'tree'\"\n :model-value=\"treeChoice(facet.key)\"\n :nodes=\"tree.nodes.value ?? []\"\n node-key=\"id\"\n label-key=\"name\"\n children-key=\"children\"\n multiple\n check-strictly\n filterable\n clearable\n size=\"sm\"\n :placeholder=\"t('panel.any')\"\n @update:model-value=\"\n (value: TreeSelectValue) =>\n narrow(facet.key, Array.isArray(value) ? value.map(String) : [])\n \"\n />\n\n <div v-else-if=\"facet.kind === 'range'\" class=\"wx-catalog-products__range\">\n <wx-input-number\n :model-value=\"rangeOf(facet.key).min ?? null\"\n :min=\"0\"\n :controls=\"false\"\n size=\"sm\"\n :placeholder=\"boundOf(facet.key, 'min') || t('panel.range-from')\"\n :aria-label=\"`${facet.label}: ${t('panel.range-from')}`\"\n @update:model-value=\"\n (value: number | null | undefined) =>\n narrow(facet.key, { ...rangeOf(facet.key), min: value ?? null })\n \"\n />\n <wx-input-number\n :model-value=\"rangeOf(facet.key).max ?? null\"\n :min=\"0\"\n :controls=\"false\"\n size=\"sm\"\n :placeholder=\"boundOf(facet.key, 'max') || t('panel.range-to')\"\n :aria-label=\"`${facet.label}: ${t('panel.range-to')}`\"\n @update:model-value=\"\n (value: number | null | undefined) =>\n narrow(facet.key, { ...rangeOf(facet.key), max: value ?? null })\n \"\n />\n </div>\n\n <wx-switch\n v-else-if=\"facet.kind === 'toggle'\"\n :model-value=\"chosen[facet.key] === true\"\n size=\"sm\"\n @update:model-value=\"(on) => narrow(facet.key, on ? true : null)\"\n />\n\n <wx-select\n v-else\n :model-value=\"valuesOf(facet.key)\"\n :options=\"optionsOf(facet.key)\"\n multiple\n filterable\n clearable\n size=\"sm\"\n :placeholder=\"t('panel.any')\"\n @update:model-value=\"\n (value: unknown) => narrow(facet.key, Array.isArray(value) ? value.map(String) : [])\n \"\n />\n </wx-form-item>\n\n <wx-button v-if=\"applied.length > 0\" variant=\"text\" size=\"sm\" block @click=\"clearFilters\">\n <template #icon><wx-icon name=\"close\" /></template>\n {{ admin('filters.reset') }}\n </wx-button>\n </template>\n\n <template #applied>\n <wx-filter-chips :filters=\"applied\" />\n </template>\n\n <template #cell-image=\"{ row }\">\n <span class=\"wx-catalog-products__image\" :class=\"{ 'is-empty': !row.image }\">\n <img v-if=\"row.image\" :src=\"row.image.thumb ?? row.image.url\" alt=\"\" loading=\"lazy\" />\n <wx-icon v-else name=\"image\" size=\"sm\" />\n </span>\n </template>\n\n <template #cell-name=\"{ row }\">\n <div class=\"wx-catalog-products__name\">\n <span class=\"wx-catalog-products__title\">{{ row.name }}</span>\n <span class=\"wx-catalog-products__sku\">{{ row.sku ?? '' }}</span>\n </div>\n </template>\n\n <template #cell-category=\"{ row }\">\n <wx-badge v-if=\"!row.category\" type=\"warning\" size=\"sm\" round>\n {{ t('product.no-category') }}\n </wx-badge>\n <wx-text v-else size=\"sm\" truncate :tone=\"row.category.deleted ? 'muted' : undefined\">\n {{ row.category.name }}\n </wx-text>\n </template>\n\n <template #cell-price=\"{ row }\">\n <span class=\"wx-catalog-products__price\">\n <span\n >{{ price(row.price) }}<small v-if=\"row.price != null\">{{ unit(row) }}</small></span\n >\n <s v-if=\"row.old_price != null\">{{ price(row.old_price) }}</s>\n </span>\n </template>\n\n <template v-for=\"column in extra\" :key=\"column.key\" #[`cell-x.${column.key}`]=\"{ row }\">\n <column-value :value=\"row.columns?.[column.key]\" />\n </template>\n\n <template #cell-state=\"{ row }\">\n <wx-tooltip v-if=\"row.is_published && !row.visible\" :content=\"t('panel.invisible')\">\n <wx-badge :type=\"stateType(row)\" dot size=\"sm\">{{ t(`states.${row.state}`) }}</wx-badge>\n </wx-tooltip>\n <wx-badge v-else :type=\"stateType(row)\" dot size=\"sm\">\n {{ t(`states.${row.state}`) }}\n </wx-badge>\n </template>\n\n <template #cell-updated_at=\"{ row }\">\n <wx-date :value=\"row.updated_at\" compact />\n </template>\n\n <template #cell-card=\"{ row }\">\n <wx-entity-card\n variant=\"plain\"\n :title=\"row.name\"\n :image=\"row.image?.thumb ?? row.image?.url ?? undefined\"\n image-size=\"56px\"\n :title-lines=\"2\"\n :subtitle=\"row.sku ?? undefined\"\n >\n <template #meta>\n <wx-badge :type=\"stateType(row)\" dot size=\"sm\">{{\n t(`states.${row.state}`)\n }}</wx-badge>\n <wx-badge v-if=\"!row.category\" type=\"warning\" size=\"sm\" round>\n {{ t('product.no-category') }}\n </wx-badge>\n <wx-text v-if=\"priced && row.price != null\" size=\"sm\">\n {{ price(row.price) }}{{ unit(row) }}\n </wx-text>\n <!-- The satellites' values that are there; an empty one is not worth a dash on a card. -->\n <template v-for=\"column in extra\" :key=\"column.key\">\n <column-value\n v-if=\"hasValue(row.columns?.[column.key])\"\n :value=\"row.columns?.[column.key]\"\n />\n </template>\n </template>\n </wx-entity-card>\n </template>\n\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-products {\n min-width: 0;\n}\n\n.wx-catalog-products__selection {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-12);\n padding: var(--wx-space-8) var(--wx-space-16);\n border-bottom: 1px solid var(--wx-border-default);\n background: var(--wx-bg-subtle);\n}\n\n.wx-catalog-products__image {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: var(--wx-radius-sm);\n overflow: clip;\n background: var(--wx-bg-subtle);\n color: var(--wx-text-muted);\n vertical-align: middle;\n}\n\n.wx-catalog-products__image img {\n width: 100%;\n height: 100%;\n min-width: 0;\n object-fit: cover;\n}\n\n.wx-catalog-products__name {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.wx-catalog-products__title {\n font-weight: var(--wx-font-weight-medium);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-catalog-products__sku {\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-sm);\n color: var(--wx-text-muted);\n min-height: 1em;\n}\n\n.wx-catalog-products__price {\n display: inline-flex;\n flex-direction: column;\n align-items: flex-end;\n font-variant-numeric: tabular-nums;\n}\n\n/* A price and its unit are one reading: '99 990 / pcs' broken over two lines reads as two facts. */\n.wx-catalog-products__price > * {\n white-space: nowrap;\n}\n\n.wx-catalog-products__price small,\n.wx-catalog-products__price s {\n font-size: var(--wx-font-size-sm);\n color: var(--wx-text-muted);\n}\n\n.wx-catalog-products__range {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--wx-space-8);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue'\nimport { localizedValue, useLocales, WxBadge, type LocalizedValue } from '@webx-ui/core'\nimport { useCategoryEditor } from '@webx-ui/module-admin'\nimport { TONES, toneBadge, type Tone } from './tones'\n\n/**\n * The tone of a label or a stock status (`wx-catalog-tone`): six tags, one per tone, each in its\n * own — picking one is picking what it looks like — and beside them the record itself as the list\n * of products will show it, with the name that is being typed above.\n *\n * The words of the tones are the screen's (`props.options`, one per tone): the server says them\n * in the panel's language, and a tone without a word is shown by its key.\n */\ndefineOptions({ name: 'WxCatalogToneField' })\n\nconst props = withDefaults(\n defineProps<{\n options?: Array<{ label: string; value: string }>\n disabled?: boolean\n ariaLabel?: string\n }>(),\n { options: () => [], disabled: false, ariaLabel: undefined },\n)\n\nconst value = defineModel<string | null>({ default: null })\n\nconst locales = useLocales()\nconst editor = useCategoryEditor()\n\nconst root = useTemplateRef<HTMLElement>('root')\n\n/*\n * The group is named by the label of the form item it stands in. The core does not hand a field\n * built outside it the item's ids (`useFormField` is its own), and a group of radios cannot be the\n * target of a label's `for` — so the label is found where it is drawn.\n */\nconst labelledBy = ref<string | undefined>()\n\nonMounted(() => {\n labelledBy.value =\n root.value?.closest('.wx-form-item')?.querySelector('.wx-form-item__label')?.id || undefined\n})\n\nconst chosen = computed<Tone>(() =>\n (TONES as readonly string[]).includes(value.value ?? '') ? (value.value as Tone) : 'neutral',\n)\n\nconst words = computed(() => new Map(props.options.map((one) => [one.value, one.label])))\n\n/** The record's name as typed, in the language the form is showing; the tone's word until then. */\nconst sample = computed(() => {\n const title = editor?.values.value.title as LocalizedValue | string | null | undefined\n\n return localizedValue(title, locales.active.value, '') || words.value.get(chosen.value) || ''\n})\n\nfunction pick(tone: Tone): void {\n if (!props.disabled) value.value = tone\n}\n\n/* Arrows move the choice and the focus with it, as in any group of radio buttons. */\nfunction step(delta: number): void {\n const at = TONES.indexOf(chosen.value)\n\n pick(TONES[(at + delta + TONES.length) % TONES.length]!)\n void nextTick(() => root.value?.querySelector<HTMLElement>('[aria-checked=\"true\"]')?.focus())\n}\n</script>\n\n<template>\n <div ref=\"root\" class=\"wx-catalog-tone\">\n <div\n class=\"wx-catalog-tone__choices\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"ariaLabel ? undefined : labelledBy\"\n :aria-disabled=\"disabled || undefined\"\n >\n <button\n v-for=\"tone in TONES\"\n :key=\"tone\"\n type=\"button\"\n role=\"radio\"\n class=\"wx-catalog-tone__choice\"\n :class=\"{ 'is-chosen': tone === chosen }\"\n :aria-checked=\"tone === chosen\"\n :tabindex=\"tone === chosen ? 0 : -1\"\n :disabled=\"disabled\"\n :data-tone=\"tone\"\n @click=\"pick(tone)\"\n @keydown.right.prevent=\"step(1)\"\n @keydown.down.prevent=\"step(1)\"\n @keydown.left.prevent=\"step(-1)\"\n @keydown.up.prevent=\"step(-1)\"\n >\n <wx-badge :type=\"toneBadge(tone)\" :variant=\"tone === chosen ? 'solid' : 'soft'\" round>\n {{ words.get(tone) ?? tone }}\n </wx-badge>\n </button>\n </div>\n\n <span v-if=\"sample\" class=\"wx-catalog-tone__sample\" aria-hidden=\"true\">\n <wx-badge :type=\"toneBadge(chosen)\" size=\"sm\">{{ sample }}</wx-badge>\n </span>\n </div>\n</template>\n\n<style scoped>\n.wx-catalog-tone {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-12);\n}\n\n.wx-catalog-tone__choices {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-6);\n}\n\n.wx-catalog-tone__choice {\n display: inline-flex;\n padding: 0;\n border: 0;\n border-radius: var(--wx-radius-full);\n background: none;\n font: inherit;\n cursor: pointer;\n}\n\n.wx-catalog-tone__choice:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.wx-catalog-tone__choice:focus-visible {\n outline: 2px solid var(--wx-border-focus);\n outline-offset: 2px;\n}\n\n/* The record as the list will show it, set apart from the choices by a rule rather than a label. */\n.wx-catalog-tone__sample {\n display: inline-flex;\n padding-left: var(--wx-space-12);\n border-left: 1px solid var(--wx-border-default);\n}\n</style>\n","import type { AdminModule } from '@webx-ui/module-admin'\nimport CategoriesPage from './CategoriesPage.vue'\nimport CategoryEditorPage from './CategoryEditorPage.vue'\nimport CategoryField from './CategoryField.vue'\nimport DeletedPage from './DeletedPage.vue'\nimport FacetsField from './FacetsField.vue'\nimport GalleryField from './GalleryField.vue'\nimport ProductEditorPage from './ProductEditorPage.vue'\nimport ProductsPage from './ProductsPage.vue'\nimport ToneField from './ToneField.vue'\n\nexport interface CatalogOptions {\n /**\n * Where the catalogue lives inside the panel. The server's refusal of a taken article number\n * links to `{path}/products/{id}` under the panel's own path, so a panel that moves it has to\n * move the server's idea of it too — leave it unless there is a reason.\n */\n path?: string\n}\n\n/**\n * The catalogue as sections of the panel (§11): the products with «Deleted», and their categories\n * as a tree.\n *\n * Two modules, because the navigation is one entry per module and the tree is opened often enough\n * to want its own — the server registers `catalog` and `catalog-categories` in the `catalog`\n * group, beside the satellites (brands, stock, labels). «Deleted» is reached from the head of the\n * products: it holds categories too, but it is visited rarely, and by whoever may delete.\n *\n * The node types are what only this module can draw: a category picker that knows the tree, the\n * «Filters» tab of a category (§6.2), the gallery, and the tone of a reference book's record —\n * the satellites' screens use it, and the six tones are the core's (`Dictionary`).\n */\nexport function catalog(options: CatalogOptions = {}): AdminModule[] {\n const path = options.path ?? '/catalog'\n const props = { base: path }\n\n const products: AdminModule = {\n id: 'catalog',\n path: `${path}/products`,\n routes: [\n { path, redirect: `${path}/products` },\n { path: `${path}/products`, name: 'webx.catalog.products', component: ProductsPage, props },\n {\n path: `${path}/products/:id(\\\\d+)`,\n name: 'webx.catalog.products.edit',\n component: ProductEditorPage,\n props,\n },\n { path: `${path}/deleted`, name: 'webx.catalog.deleted', component: DeletedPage, props },\n ],\n types: {\n 'wx-catalog-category': { component: CategoryField, kind: 'field' },\n // Wide: a row of the list carries a name, a switch and a grip, and half a line is too narrow\n // to read which filter is which.\n 'wx-catalog-facets': { component: FacetsField, kind: 'field', wide: true },\n 'wx-catalog-gallery': { component: GalleryField, kind: 'display' },\n // The tone of a label or a stock status: here, because both satellites paint in the same six.\n 'wx-catalog-tone': { component: ToneField, kind: 'field' },\n },\n }\n\n const categories: AdminModule = {\n id: 'catalog-categories',\n path: `${path}/categories`,\n routes: [\n {\n path: `${path}/categories`,\n name: 'webx.catalog.categories',\n component: CategoriesPage,\n props,\n },\n {\n path: `${path}/categories/:id(\\\\d+)`,\n name: 'webx.catalog.categories.edit',\n component: CategoryEditorPage,\n props,\n },\n ],\n }\n\n return [products, categories]\n}\n","import { defineComponent, h, type Component } from 'vue'\nimport type { RouteRecordRaw } from 'vue-router'\nimport {\n categoryRoutes,\n useAdmin,\n type AdminModule,\n type CategoriesOptions,\n type CategoryWord,\n type Messages,\n} from '@webx-ui/module-admin'\nimport { FACET_PREFIX } from './filters'\n\n/**\n * A reference book of the catalogue — labels, stock statuses, brands — as the satellite that owns\n * it describes it (§4 of the dictionaries spec). Everything else is the same for each.\n */\nexport interface DictionaryOptions {\n /** The module the server registers: `catalog-labels`. */\n id: string\n /** Its path under the catalogue's, and under the catalogue's API: `labels`. */\n slug: string\n /** The described screen of one record: `catalog.label-form`. */\n screen: string\n /** The facet of the products list that picks a record's products: `label`. */\n facet: string\n /** The satellite's dictionary: `webx-catalog-labels`. */\n namespace: string\n /** The group of it the list's words are in: `label`. */\n group: string\n /** Its English, under the server's until the server's arrives. */\n messages: Record<string, Messages>\n /** Where the catalogue lives in the panel — `catalog({ path })`'s. */\n catalog?: string\n}\n\n/** The words of the shared screens a reference book says in its own way; the rest are the panel's. */\nconst OWN: CategoryWord[] = [\n 'new',\n 'empty',\n 'empty-help',\n 'order',\n 'hidden',\n 'count',\n 'show-items',\n 'delete-blocked',\n 'delete-text',\n 'deleted',\n 'saved',\n]\n\n/**\n * The panel's shared category screens for one reference book: the list with its order dragged\n * and the number of products on each row, and the page of one record, which is the satellite's\n * described screen. Rights are the catalogue's (decision 8), and \"Show its products\" is the list\n * of products narrowed by the satellite's own facet.\n */\nexport function dictionaryOptions(options: DictionaryOptions): CategoriesOptions {\n const catalog = options.catalog ?? '/catalog'\n\n return {\n api: `catalog/${options.slug}`,\n path: `${catalog}/${options.slug}`,\n name: `webx.${options.id}`,\n module: options.id,\n screen: options.screen,\n manage: 'catalog.manage',\n count: 'products_count',\n items: (id) => ({\n path: `${catalog}/products`,\n query: { [`${FACET_PREFIX}${options.facet}`]: String(id) },\n }),\n words: Object.fromEntries(\n OWN.map((word) => [word, `${options.namespace}::${options.group}.${word}`]),\n ),\n }\n}\n\n/** A reference book as a section of the panel: one entry, two routes. */\nexport function dictionarySection(options: DictionaryOptions): AdminModule {\n const described = dictionaryOptions(options)\n const seed = () => seedMessages(options.namespace, options.messages)\n\n return {\n id: options.id,\n path: described.path,\n routes: categoryRoutes(described).map((route) => seeded(route, seed)),\n }\n}\n\nconst seededIn = new WeakMap<object, Set<string>>()\n\n/**\n * The satellite's English under the panel's dictionary, once per panel. The screens are the\n * panel's, so nothing of the satellite's runs before them to do it — the route does.\n */\nfunction seedMessages(namespace: string, messages: Record<string, Messages>): void {\n try {\n const { i18n } = useAdmin()\n const done = seededIn.get(i18n) ?? new Set<string>()\n\n if (!done.has(namespace)) {\n i18n.defaults(namespace, messages)\n done.add(namespace)\n seededIn.set(i18n, done)\n }\n } catch {\n // Outside a panel there is no dictionary to seed, and `useTranslate` falls back on its own.\n }\n}\n\nfunction seeded(route: RouteRecordRaw, seed: () => void): RouteRecordRaw {\n const page = (route as { component?: Component }).component\n\n if (page === undefined) return route\n\n return {\n ...route,\n component: defineComponent({\n name: 'WxCatalogDictionaryView',\n setup() {\n seed()\n\n return () => h(page)\n },\n }),\n } as RouteRecordRaw\n}\n"],"names":["CATALOG_API","productSearch","query","search","key","choice","value","upload","admin","url","file","onProgress","body","resolve","reject","request","token","readCookie","event","answer","createCatalogApi","base","data","withQuery","path","id","values","source","images","image","alt","title","parent","before","action","params","selection","kind","firstError","errors","field","own","name","catalogMessages","seeded","useCatalogMessages","i18n","useAdmin","stores","storeOf","store","ref","fetchInto","shelf","fetch","force","pending","useCategoryTree","api","nodes","useFacetRegistry","computed","facet","flatten","node","ancestry","byId","chain","current","props","__props","open","dismiss","useModal","context","tree","locales","useLocales","t","useTranslate","saving","useTemplateRef","onMounted","nextTick","submit","under","error","_createBlock","_unref","WxDialog","$event","_createVNode","WxSpace","WxButton","WxFormItem","_createElementVNode","WxInput","WxTreeSelect","lastSegment","segments","segment","router","useRouter","message","useErrorText","rows","loading","loaded","create","createModal","CategoryCreateDialog","canManage","canDelete","asTree","matches","term","columns","rowMenuWidth","treeOptions","drag","drop","show","plain","load","toast","onState","state","add","detail","dropped","publish","on","remove","confirm","productsOf","actionsFor","actions","leads","_openBlock","_createElementBlock","_hoisted_1","WxListScreen","WxTable","_withCtx","row","_normalizeClass","_toDisplayString","WxText","_component_router_link","_createTextVNode","_cache","WxBadge","WxRowMenu","productEditorKey","provideProductEditor","editor","provide","useProductEditor","inject","categoryEditorKey","provideCatalogCategoryEditor","useCatalogCategoryEditor","route","useRoute","list","category","facetsFrom","snapshot","locked","dirty","written","localizedValue","address","parents","provideHistorySubject","provideRecordAddress","take","save","leaveGuard","onKeydown","useBodyKeys","watch","onBeforeUnmount","onBeforeRouteLeave","_Fragment","WxSkeleton","WxCard","WxScreenHead","WxBreadcrumb","WxBreadcrumbItem","_renderList","WxScreen","WxActionBar","WxSaveState","_useModel","useRecordAddress","main","chosen","mark","model","next","ids","_mergeProps","_ctx","page","canRestore","views","asked","mine","restore","bare","refOf","found","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","WxDate","registry","failed","facets","labels","complete","named","inherited","inheritedNames","toggleOwn","visible","findSource","from","setting","now","WxAlert","WxSwitch","WxSortableList","item","VideoUnreadable","reason","browser","frameTime","duration","probeVideo","options","environment","timeout","video","settled","timer","fail","done","canvas","width","height","length","blob","posterName","YOUTUBE_HOSTS","YOUTUBE_ID","youTubeId","watched","isVideoFileLink","isVideoLink","videoRefusal","types","maxBytes","hasPlaceholderPoster","formatBytes","bytes","locale","units","unit","formatClock","seconds","whole","hours","minutes","rest","VIDEO_PURPOSE","TARGETS_KEY","createGalleryVideo","storage","defaultStorage","useChunkedUpload","jobs","stored","running","uploading","busy","job","unfinished","product","targets","readTargets","going","uploadFingerprint","unfinishedUploads","entry","patch","change","find","setTarget","writeTargets","dropTarget","replace","one","pump","work","probe","failure","poster","picture","retry","cancel","forget","forgetUnfinishedUpload","guard","getCurrentScope","onScopeDispose","readonly","parsed","ready","percent","figures","parts","left","stage","WxProgress","part","index","PAUSE","PICTURES","PLAY","uploads","fetching","productId","uploader","videos","accept","openLinkDialog","VideoLinkDialog","sent","captions","saveGallery","carrying","caption","text","moved","acceptable","refusal","isVideo","files","progress","rejected","_file","queued","fetchAddress","picker","pickFor","pickFile","resume","picked","target","attachLink","detach","videoLabel","jobOf","unfinishedText","_hoisted_7","_hoisted_8","VideoProgress","_hoisted_9","_hoisted_10","_hoisted_12","WxUpload","_hoisted_14","holder","section","module","subtitle","holderLink","sku","reactive","param","optionsOf","required","onCategory","apply","WxSourceSelect","WxSelect","POLL","emit","__emit","BulkParamsDialog","run","starting","refused","asks","start","given","follow","poll","finish","WxDropdown","WxIcon","WxDropdownItem","TONES","toneBadge","tone","piece","record","pieces","FACET_PREFIX","readFacets","raw","min","max","end","range","writeFacet","CARDS","EXTRA","CATEGORY_FROM","root","useElementWidth","selected","everything","pageNumber","perPage","ProductCreateDialog","view","sorts","sort","missing","priced","extra","asCards","categoryNames","extraFrom","column","wanted","narrow","order","clearFilters","counted","valuesOf","valueLabel","boundOf","bound","money","applied","said","emptyText","rangeOf","treeChoice","bulkSelection","facetsQuery","clearSelection","onBulkFinished","stateType","price","hasValue","BulkBar","WxInputNumber","WxFilterChips","WxTooltip","WxEntityCard","ColumnValue","useCategoryEditor","labelledBy","words","sample","pick","step","delta","at","catalog","products","ProductsPage","ProductEditorPage","DeletedPage","CategoryField","FacetsField","GalleryField","ToneField","categories","CategoriesPage","CategoryEditorPage","OWN","dictionaryOptions","word","dictionarySection","described","seed","seedMessages","categoryRoutes","seededIn","namespace","messages","defineComponent","h"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/api.ts","../src/errors.ts","../src/messages.ts","../src/i18n.ts","../src/store.ts","../src/CategoryCreateDialog.vue","../src/paths.ts","../src/CategoriesPage.vue","../src/editor.ts","../src/CategoryEditorPage.vue","../src/CategoryField.vue","../src/DeletedPage.vue","../src/FacetsField.vue","../src/video.ts","../src/galleryVideo.ts","../src/VideoLinkDialog.vue","../src/VideoProgress.vue","../src/GalleryField.vue","../src/ProductEditorPage.vue","../src/BulkParamsDialog.vue","../src/BulkBar.vue","../src/tones.ts","../src/ColumnValue.vue","../src/ProductCreateDialog.vue","../src/filters.ts","../src/ProductsPage.vue","../src/ToneField.vue","../src/module.ts","../src/dictionaries.ts"],"sourcesContent":["import { readCookie, type AdminContext } from '@webx-ui/module-admin'\nimport type { Paginated } from '@webx-ui/core'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport type {\n BulkActionInfo,\n BulkRun,\n BulkSelection,\n CategoryDetail,\n CategoryNode,\n CategoryRow,\n DeletedCategory,\n DeletedKind,\n DeletedProduct,\n FacetInfo,\n FacetRegistryAnswer,\n SortInfo,\n ProductDetail,\n ProductImage,\n ProductQuery,\n QueuedVideo,\n ProductRow,\n ProductsPage,\n} from './types'\n\nexport interface CatalogApi {\n products(query?: ProductQuery): Promise<ProductsPage>\n product(id: number): Promise<ProductDetail>\n /** The values of `catalog.product-form`; a refused field comes back as a 422 under its name. */\n createProduct(values: ScreenModel): Promise<ProductDetail>\n saveProduct(id: number, values: ScreenModel): Promise<ProductDetail>\n removeProduct(id: number): Promise<void>\n restoreProduct(id: number): Promise<ProductRow>\n\n /** A file off the disk, with how far it has got; or an address the server fetches itself. */\n addImage(\n id: number,\n source: File | string,\n onProgress?: (percent: number) => void,\n ): Promise<ProductImage>\n /** The whole gallery in its order, with every picture's words (§11.2). */\n saveImages(\n id: number,\n images: Pick<ProductImage, 'id' | 'alt' | 'title'>[],\n ): Promise<ProductImage[]>\n removeImage(id: number, image: number): Promise<void>\n /**\n * An address the server fetches: a picture or a YouTube video comes back as the new row, a\n * direct link to a video file as `QueuedVideo` — the row appears once the queue has it.\n */\n fetchImage(id: number, url: string): Promise<ProductImage | QueuedVideo>\n /** A finished chunked upload (`upload` is its id) or an address, onto a picture of the gallery. */\n attachVideo(\n id: number,\n image: number,\n source: { upload: string; duration?: number | null } | { url: string },\n ): Promise<ProductImage | QueuedVideo>\n /** The file, if it is one, is deleted at once. */\n detachVideo(id: number, image: number): Promise<ProductImage>\n\n categories(): Promise<CategoryNode[]>\n category(id: number): Promise<CategoryDetail>\n createCategory(values: ScreenModel, parent: number | null): Promise<CategoryDetail>\n saveCategory(id: number, values: ScreenModel): Promise<CategoryDetail>\n /** Under `parent` (null — the top), before `before` (null — last). Answers with the whole tree. */\n moveCategory(id: number, parent: number | null, before: number | null): Promise<CategoryNode[]>\n removeCategory(id: number): Promise<void>\n restoreCategory(id: number): Promise<CategoryRow>\n\n /** The registry of facets, with the sorts the list is offered. */\n facets(): Promise<FacetRegistryAnswer>\n\n /** The bulk actions this administrator may start, with what each asks for (§11.4). */\n bulkActions(): Promise<BulkActionInfo[]>\n /** Done at once when small (`id` null in the answer), queued otherwise. */\n startBulk(\n action: string,\n params: Record<string, unknown>,\n selection: BulkSelection,\n ): Promise<BulkRun>\n bulkRun(id: number): Promise<BulkRun>\n\n deleted(\n kind: 'products',\n query?: { q?: string; page?: number; per_page?: number },\n ): Promise<Paginated<DeletedProduct>>\n deleted(\n kind: 'categories',\n query?: { q?: string; page?: number; per_page?: number },\n ): Promise<Paginated<DeletedCategory>>\n deleted(\n kind: DeletedKind,\n query?: { q?: string; page?: number; per_page?: number },\n ): Promise<Paginated<DeletedProduct | DeletedCategory>>\n}\n\n/** The path of the catalogue under the panel's API. */\nexport const CATALOG_API = 'catalog'\n\n/**\n * The list's query as Laravel reads nested parameters: `facets[brand][]=3`,\n * `facets[price][min]=100`. Every facet's choice is a list, a toggle's too — on is\n * `facets[in-stock][]=1`, the value the storefront's own links send; a bare `facets[in-stock]=1`\n * is refused. Empty choices are left out, so a filter that was opened and closed again asks for\n * nothing.\n */\nexport function productSearch(query: ProductQuery): URLSearchParams {\n const search = new URLSearchParams()\n\n if (query.q) search.set('q', query.q)\n if (query.state) search.set('state', query.state)\n if (query.sort && query.sort !== 'default') search.set('sort', query.sort)\n if (query.page && query.page > 1) search.set('page', String(query.page))\n if (query.per_page) search.set('per_page', String(query.per_page))\n\n for (const [key, choice] of Object.entries(query.facets ?? {})) {\n if (choice === true) {\n search.set(`facets[${key}][]`, '1')\n } else if (Array.isArray(choice)) {\n for (const value of choice) search.append(`facets[${key}][]`, String(value))\n } else {\n if (choice.min != null) search.set(`facets[${key}][min]`, String(choice.min))\n if (choice.max != null) search.set(`facets[${key}][max]`, String(choice.max))\n }\n }\n\n return search\n}\n\n/**\n * The upload is the one request that is not JSON, and the one worth a progress bar: a phone\n * sending a twelve-megapixel photo over a shop's wifi takes long enough for \"is it doing\n * anything?\". XHR for that, as `module-media` does it.\n */\nfunction upload<T>(admin: AdminContext, url: string, file: File, onProgress?: (p: number) => void) {\n const body = new FormData()\n body.append('file', file)\n\n return new Promise<T>((resolve, reject) => {\n const request = new XMLHttpRequest()\n\n request.open('POST', url)\n request.responseType = 'json'\n request.withCredentials = true\n request.setRequestHeader('Accept', 'application/json')\n request.setRequestHeader('X-Requested-With', 'XMLHttpRequest')\n // The panel's own client sends the language on every request; this one is not that client,\n // and without it a refusal comes back in English under a Russian panel.\n request.setRequestHeader('X-Webx-Locale', admin.i18n.state.locale)\n\n const token = readCookie('XSRF-TOKEN')\n if (token) request.setRequestHeader('X-XSRF-TOKEN', token)\n\n request.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) onProgress?.(Math.round((event.loaded / event.total) * 100))\n })\n\n request.addEventListener('load', () => {\n const answer = request.response as { data?: T; message?: string } | null\n\n if (request.status >= 200 && request.status < 300 && answer?.data) {\n resolve(answer.data)\n\n return\n }\n\n reject(\n Object.assign(new Error(answer?.message ?? 'Upload failed'), {\n status: request.status,\n body: answer,\n }),\n )\n })\n\n request.addEventListener('error', () => reject(new Error('Upload failed')))\n request.send(body)\n })\n}\n\nexport function createCatalogApi(admin: AdminContext): CatalogApi {\n const base = `${admin.apiPath}/${CATALOG_API}`\n const data = <T>(body: { data: T }): T => body.data\n const withQuery = (path: string, search: URLSearchParams) =>\n search.size > 0 ? `${path}?${search}` : path\n\n return {\n products: (query = {}) =>\n admin.http.get<ProductsPage>(withQuery(`${base}/products`, productSearch(query))),\n product: (id) => admin.http.get<{ data: ProductDetail }>(`${base}/products/${id}`).then(data),\n createProduct: (values) =>\n admin.http.post<{ data: ProductDetail }>(`${base}/products`, { values }).then(data),\n saveProduct: (id, values) =>\n admin.http.put<{ data: ProductDetail }>(`${base}/products/${id}`, { values }).then(data),\n removeProduct: (id) => admin.http.delete<void>(`${base}/products/${id}`).then(() => undefined),\n restoreProduct: (id) =>\n admin.http.post<{ data: ProductRow }>(`${base}/products/${id}/restore`, {}).then(data),\n\n addImage: (id, source, onProgress) =>\n typeof source === 'string'\n ? admin.http\n .post<{ data: ProductImage }>(`${base}/products/${id}/images`, { url: source })\n .then(data)\n : upload<ProductImage>(admin, `${base}/products/${id}/images`, source, onProgress),\n saveImages: (id, images) =>\n admin.http\n .put<{ data: ProductImage[] }>(`${base}/products/${id}/images`, {\n images: images.map(({ id: image, alt, title }) => ({ id: image, alt, title })),\n })\n .then(data),\n removeImage: (id, image) =>\n admin.http.delete<void>(`${base}/products/${id}/images/${image}`).then(() => undefined),\n fetchImage: (id, url) =>\n admin.http\n .post<{ data: ProductImage | QueuedVideo }>(`${base}/products/${id}/images`, { url })\n .then(data),\n attachVideo: (id, image, source) =>\n admin.http\n .post<{\n data: ProductImage | QueuedVideo\n }>(`${base}/products/${id}/images/${image}/video`, source)\n .then(data),\n detachVideo: (id, image) =>\n admin.http\n .delete<{ data: ProductImage }>(`${base}/products/${id}/images/${image}/video`)\n .then(data),\n\n categories: () => admin.http.get<{ data: CategoryNode[] }>(`${base}/categories`).then(data),\n category: (id) =>\n admin.http.get<{ data: CategoryDetail }>(`${base}/categories/${id}`).then(data),\n createCategory: (values, parent) =>\n admin.http\n .post<{ data: CategoryDetail }>(`${base}/categories`, { values, parent_id: parent })\n .then(data),\n saveCategory: (id, values) =>\n admin.http.put<{ data: CategoryDetail }>(`${base}/categories/${id}`, { values }).then(data),\n moveCategory: (id, parent, before) =>\n admin.http\n .post<{ data: CategoryNode[] }>(`${base}/categories/${id}/move`, {\n parent_id: parent,\n before_id: before,\n })\n .then(data),\n removeCategory: (id) =>\n admin.http.delete<void>(`${base}/categories/${id}`).then(() => undefined),\n restoreCategory: (id) =>\n admin.http.post<{ data: CategoryRow }>(`${base}/categories/${id}/restore`, {}).then(data),\n\n facets: () =>\n admin.http\n .get<{ data: FacetInfo[]; meta?: { sorts?: SortInfo[] } }>(`${base}/facets`)\n .then((body) => ({ facets: body.data, sorts: body.meta?.sorts ?? [] })),\n\n bulkActions: () => admin.http.get<{ data: BulkActionInfo[] }>(`${base}/bulk`).then(data),\n startBulk: (action, params, selection) =>\n admin.http.post<{ data: BulkRun }>(`${base}/bulk`, { action, params, selection }).then(data),\n bulkRun: (id) => admin.http.get<{ data: BulkRun }>(`${base}/bulk/${id}`).then(data),\n\n deleted: ((kind: DeletedKind, query: { q?: string; page?: number; per_page?: number } = {}) => {\n const search = new URLSearchParams({ type: kind })\n\n if (query.q) search.set('q', query.q)\n if (query.page && query.page > 1) search.set('page', String(query.page))\n if (query.per_page) search.set('per_page', String(query.per_page))\n\n return admin.http.get(`${base}/deleted?${search}`)\n }) as CatalogApi['deleted'],\n }\n}\n","/**\n * The first refusal of a field, whether the server named the field (`name`) or one language of it\n * (`name.en`). Matched on the name and a dot, not on the first dot of the key: names with a dot in\n * them exist (`stock.status`), and `name.en` must not be read as the field `name.en`.\n */\nexport function firstError(errors: Record<string, string[]>, field: string): string | undefined {\n const own = errors[field]?.[0]\n\n if (own !== undefined) return own\n\n const key = Object.keys(errors).find((name) => name.startsWith(`${field}.`))\n\n return key === undefined ? undefined : errors[key]?.[0]\n}\n","import type { Messages } from '@webx-ui/module-admin'\n\n/**\n * The English this package speaks on its own, before the server's dictionary arrives, or without\n * one. The server ships the same lines under `webx-catalog::` and overrides these;\n * `messages.test.ts` keeps the two sets of keys equal.\n *\n * `module`, `states`, `product`, `category` and `units` are the server's groups, which the panel\n * reads too — a column called \"Article number\" in the list and \"Article number\" on the form\n * are one word. `panel` is what only the panel says.\n */\nexport const catalogMessages: Record<string, Messages> = {\n module: {\n title: 'Products',\n group: 'Catalog',\n products: 'Products',\n categories: 'Categories',\n dictionaries: 'Dictionaries',\n deleted: 'Deleted',\n product: 'Product',\n category: 'Category',\n },\n states: {\n published: 'Published',\n unpublished: 'Unpublished',\n deleted: 'Deleted',\n },\n product: {\n name: 'Name',\n slug: 'Address',\n sku: 'Article number',\n barcode: 'Barcode',\n summary: 'Summary',\n description: 'Description',\n category_id: 'Main category',\n category: 'Main category',\n categories: 'Additional categories',\n price: 'Price',\n old_price: 'Old price',\n unit: 'Unit',\n priority: 'Priority',\n is_published: 'Published',\n images: 'Pictures',\n 'no-category': 'No category',\n },\n category: {\n name: 'Name',\n slug: 'Address',\n description: 'Text',\n cover: 'Picture',\n cover_id: 'Picture',\n is_published: 'Published',\n parent: 'Parent category',\n facets: 'Filters',\n },\n units: {\n pcs: 'pcs',\n kg: 'kg',\n g: 'g',\n m: 'm',\n m2: 'm²',\n m3: 'm³',\n l: 'l',\n pack: 'pack',\n set: 'set',\n },\n panel: {\n create: 'Create',\n cancel: 'Cancel',\n save: 'Save',\n saved: 'Saved.',\n 'save-failed': 'Not saved: what is wrong is said under the fields in red.',\n untitled: 'Untitled',\n open: 'Open',\n 'open-on-site': 'Open on the site',\n publish: 'Publish',\n unpublish: 'Take off the site',\n delete: 'Delete',\n restore: 'Restore',\n 'leave-title': 'Leave without saving?',\n 'leave-text': 'What you changed is not on the server.',\n leave: 'Leave',\n 'address-moving':\n 'The address is changing. The old one keeps working and leads to the new one.',\n\n 'new-product': 'New product',\n 'search-products': 'Search by name or article number',\n 'empty-products': 'No products yet.',\n 'empty-products-help': 'A product lives in one main category and any number of others.',\n 'empty-search': 'Nothing matches that.',\n 'view-all': 'All',\n 'view-published': 'Published',\n 'view-unpublished': 'Unpublished',\n 'view-no-category': 'No category',\n 'column-product': 'Product',\n 'column-state': 'State',\n 'column-updated': 'Changed',\n invisible: 'Published, but no category of it is on the site, so nobody sees it.',\n sort: 'Order',\n 'sort-default': 'By priority, then newest',\n 'range-from': 'From',\n 'range-to': 'To',\n any: 'Any',\n selected: 'Selected: :count',\n 'select-clear': 'Clear the selection',\n 'bulk-actions': 'Actions',\n 'select-all-found': 'Select everything found: :count',\n 'selected-all': 'Selected everything found: :count',\n 'bulk-apply': 'Apply',\n 'bulk-param-empty': 'Nothing — takes it off',\n 'bulk-confirm-title': 'Apply «:action»?',\n 'bulk-confirm-text': 'Products it applies to: :count',\n 'bulk-running': 'Running in the background. Done :done of :total',\n 'bulk-done': 'Done. Products: :count',\n 'bulk-refused': 'Refused: :count',\n 'bulk-stopped': 'The run stopped. Done so far: :count',\n 'bulk-errors': 'What was refused',\n 'bulk-close': 'Close',\n 'bulk-nothing': 'None of the selected products fit this action.',\n 'product-trail': 'Where this product sits',\n 'delete-product-title': 'Delete “:name”?',\n 'delete-product-text':\n 'It goes to «Deleted» and comes off the site. Its address leads to its category from then on.',\n 'product-deleted': 'The product is in «Deleted».',\n 'product-restored': 'The product is back.',\n 'product-restored-bare':\n 'The product is back without a category and unpublished: its category is deleted.',\n 'product-published': 'The product is on the site.',\n 'product-unpublished': 'The product is off the site.',\n 'sku-open': 'Open that product',\n 'sku-open-deleted': 'Find it in «Deleted»',\n\n 'gallery-add': 'Add pictures',\n 'gallery-hint': 'JPEG, PNG, WebP or GIF. The first picture is the main one.',\n 'gallery-url': 'Upload from an address',\n 'gallery-url-add': 'Fetch',\n 'gallery-empty': 'No pictures yet.',\n 'gallery-main': 'Main',\n 'gallery-alt': 'Alternative text',\n 'gallery-alt-help': 'What the picture shows, for search engines and screen readers.',\n 'gallery-title': 'Title',\n 'gallery-order': 'Drag to change the order. The first picture is the main one.',\n 'gallery-failed': 'The picture was not added.',\n 'gallery-remove-title': 'Delete this picture?',\n 'gallery-remove-text': 'The file is deleted from the disk at once.',\n 'gallery-removed': 'The picture is deleted.',\n 'gallery-reorder-failed': 'The new order was not saved.',\n 'gallery-captions-failed': 'The captions were not saved.',\n 'gallery-locked': 'Pictures are added to a product you may edit.',\n 'gallery-add-video': 'Add pictures or videos',\n 'gallery-hint-video':\n 'JPEG, PNG, WebP or GIF pictures, MP4 or WebM videos. The first picture is the main one.',\n 'gallery-wrong-type': 'Only JPEG, PNG, WebP and GIF pictures go into the gallery.',\n 'gallery-wrong-type-video':\n 'Only JPEG, PNG, WebP and GIF pictures and MP4 and WebM videos go into the gallery.',\n 'gallery-url-hint': 'The address of a JPEG, PNG, WebP or GIF picture.',\n 'gallery-url-hint-video':\n 'The address of a picture, a YouTube link, or a direct link to an MP4 or WebM file.',\n 'video-badge': 'Has a video',\n 'video-open': 'Open the video',\n 'video-attach': 'Attach',\n 'video-attach-file': 'Attach a video file…',\n 'video-attach-link': 'Attach a YouTube link…',\n 'video-attached': 'The video is attached.',\n 'video-remove': 'Remove the video',\n 'video-remove-title': 'Remove the video?',\n 'video-remove-file-text': 'The video file is deleted from the disk at once. The picture stays.',\n 'video-remove-link-text': 'The link to the video is dropped. The picture stays.',\n 'video-removed': 'The video is removed.',\n 'video-link-title': 'Attach a video by its link',\n 'video-link': 'Link',\n 'video-link-help': 'A YouTube link, or a direct link to an MP4 or WebM file.',\n 'video-link-wrong': 'This is neither a YouTube link nor a link to an MP4 or WebM file.',\n 'video-queued':\n 'The video is downloading on the server. It appears in the gallery when it is done — open the product again then.',\n 'video-wrong-type': 'Only MP4 and WebM videos.',\n 'video-too-large': 'The video is larger than :max.',\n 'video-unreadable':\n 'The browser cannot open this video, so there is no frame for a cover. Add a picture and attach the video to it.',\n 'video-placeholder':\n 'A plain cover. For a frame of the video: add a picture, attach the same link to it and delete this row.',\n 'video-waiting': 'Waits for the video before it.',\n 'video-poster': 'Taking a frame for the cover…',\n 'video-attaching': 'Attaching the video…',\n 'video-uploading': 'Uploading :name',\n 'video-sent': ':sent of :total',\n 'video-left': 'Time left: :time',\n 'video-paused': 'Paused',\n 'video-offline': 'Offline — continues when the connection is back',\n 'video-pause': 'Pause',\n 'video-resume': 'Resume',\n 'video-retry': 'Try again',\n 'video-dismiss': 'Dismiss',\n 'video-unfinished':\n 'The upload of :name did not finish: :sent of :total. Choose the same file to continue.',\n 'video-choose-again': 'Choose the file…',\n 'video-forget': 'Forget',\n 'video-other-file': 'That is another file. Choose :name (:size) to continue.',\n 'video-leave-title': 'Leave during the upload?',\n 'video-leave-text':\n 'The video stops going up. Choose the same file on this product again to continue where it stopped.',\n\n 'new-category': 'New category',\n 'add-child': 'Add a subcategory',\n 'search-categories': 'Search by name or address',\n 'empty-categories': 'No categories yet.',\n 'empty-categories-help':\n 'Categories are the shelves of the catalogue; each has an address of its own at the root of the site.',\n 'column-products': 'Products',\n 'category-hidden': 'Hidden: a category above it is unpublished.',\n 'category-order': 'Drag to move a category. Its address stays the same.',\n 'category-moved': 'The category is moved.',\n 'category-trail': 'Where this category sits',\n 'field-parent': 'Inside',\n 'parent-root': 'At the top level',\n 'show-products': 'Show its products',\n 'delete-category-title': 'Delete “:name”?',\n 'delete-category-text': 'It goes to «Deleted», and its address answers that it is gone.',\n 'category-deleted': 'The category is in «Deleted».',\n 'category-restored': 'The category is back.',\n 'category-published': 'The category is on the site.',\n 'category-unpublished': 'The category and everything under it is off the site.',\n 'pick-category': 'Choose a category',\n 'pick-categories': 'Choose categories',\n 'category-gone': 'deleted',\n\n 'facets-own': 'Own setting',\n 'facets-own-help': 'Which filters this category shows, and in which order.',\n 'facets-inherited': 'As in “:name”',\n 'facets-default': 'All filters, in their default order',\n 'facets-inherited-help':\n 'Switch it on to choose for this category. Categories under it take the setting unless they have their own.',\n 'facets-empty': 'No filters are installed.',\n 'facets-failed': 'The list of filters did not load.',\n 'facets-shown': 'Shown',\n 'facets-unknown': 'Its module is not installed',\n\n 'deleted-search': 'Search by name',\n 'deleted-empty': 'Nothing is deleted.',\n 'deleted-help': 'A deleted product keeps its pictures and comes back under its old address.',\n 'column-deleted': 'Deleted',\n 'column-parent': 'Was inside',\n 'restore-bare': 'Comes back without a category',\n },\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { catalogMessages } from './messages'\n\nconst seeded = new WeakSet<object>()\n\n/** Puts this package's English under the panel's dictionary, once per panel. */\nexport function useCatalogMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-catalog', catalogMessages)\n seeded.add(i18n)\n }\n } catch {\n // Outside a panel there is no dictionary to seed, and `useTranslate` falls back on its own.\n }\n}\n","import { computed, ref, type ComputedRef, type Ref } from 'vue'\nimport type { AdminContext } from '@webx-ui/module-admin'\nimport { createCatalogApi } from './api'\nimport type { CategoryNode, FacetInfo, FacetKind, FacetRegistryAnswer, SortInfo } from './types'\n\n/**\n * What several screens of the catalogue read and nobody should ask for twice: the tree of\n * categories and the registry of facets.\n *\n * The tree is read by the list's filter, by both category pickers of the product form and by the\n * tree screen; one panel, one copy, dropped whenever a category is created, saved, moved, deleted\n * or restored — the next reader asks again. Kept per panel rather than per module instance, so a\n * test that builds a panel of its own starts from nothing.\n */\ninterface Shelf<T> {\n value: Ref<T | null>\n pending: Promise<T> | null\n}\n\ninterface CatalogStore {\n tree: Shelf<CategoryNode[]>\n facets: Shelf<FacetRegistryAnswer>\n}\n\nconst stores = new WeakMap<AdminContext, CatalogStore>()\n\nfunction storeOf(admin: AdminContext): CatalogStore {\n let store = stores.get(admin)\n\n if (!store) {\n store = {\n tree: { value: ref(null), pending: null },\n facets: { value: ref(null), pending: null },\n }\n stores.set(admin, store)\n }\n\n return store\n}\n\nfunction fetchInto<T>(shelf: Shelf<T>, fetch: () => Promise<T>, force: boolean): Promise<T> {\n if (!force && shelf.value.value !== null) return Promise.resolve(shelf.value.value)\n if (!force && shelf.pending) return shelf.pending\n\n const pending = fetch()\n .then((value) => {\n if (shelf.pending === pending) shelf.value.value = value\n\n return value\n })\n .finally(() => {\n if (shelf.pending === pending) shelf.pending = null\n })\n\n shelf.pending = pending\n\n return pending\n}\n\nexport interface CategoryTree {\n /** `null` until the first answer. */\n nodes: Ref<CategoryNode[] | null>\n load(force?: boolean): Promise<CategoryNode[]>\n /** Put in what the server answered with — a move answers with the whole tree. */\n take(nodes: CategoryNode[]): void\n /** Forget it; the next reader asks again. */\n drop(): void\n}\n\nexport function useCategoryTree(admin: AdminContext): CategoryTree {\n const shelf = storeOf(admin).tree\n const api = createCatalogApi(admin)\n\n return {\n nodes: shelf.value,\n load: (force = false) => fetchInto(shelf, () => api.categories(), force),\n take: (nodes) => {\n shelf.pending = null\n shelf.value.value = nodes\n },\n drop: () => {\n shelf.pending = null\n shelf.value.value = null\n },\n }\n}\n\nexport interface FacetRegistry {\n facets: ComputedRef<FacetInfo[] | null>\n /** The orders the list offers, in the registry's order; empty until the answer. */\n sorts: ComputedRef<SortInfo[]>\n load(): Promise<FacetRegistryAnswer>\n}\n\n/**\n * The facets every category can show (§7.1) and the sorts of the list (§7.2) — one answer, since\n * the list needs both before it can draw its filter. The kind is lowercased on the way in: a panel\n * comparing with 'range' should not care how the server cased its enum.\n */\nexport function useFacetRegistry(admin: AdminContext): FacetRegistry {\n const shelf = storeOf(admin).facets\n const api = createCatalogApi(admin)\n\n return {\n facets: computed(() => shelf.value.value?.facets ?? null),\n sorts: computed(() => shelf.value.value?.sorts ?? []),\n load: () =>\n fetchInto(\n shelf,\n () =>\n api.facets().then((answer) => ({\n sorts: answer.sorts,\n facets: answer.facets.map((facet) => ({\n ...facet,\n kind: String(facet.kind).toLowerCase() as FacetKind,\n })),\n })),\n false,\n ),\n }\n}\n\n/** Every node of the tree, depth first, as the server ordered them. */\nexport function flatten(nodes: CategoryNode[]): CategoryNode[] {\n return nodes.flatMap((node) => [node, ...flatten(node.children ?? [])])\n}\n\n/** A node and the chain of its parents, the node first. Empty when it is not in the tree. */\nexport function ancestry(nodes: CategoryNode[], id: number): CategoryNode[] {\n const byId = new Map(flatten(nodes).map((node) => [node.id, node]))\n const chain: CategoryNode[] = []\n let current = byId.get(id)\n\n while (current) {\n chain.push(current)\n current = current.parent_id === null ? undefined : byId.get(current.parent_id)\n }\n\n return chain\n}\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport {\n useLocales,\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSpace,\n WxTreeSelect,\n type TreeSelectValue,\n} from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { firstError } from './errors'\nimport { useCatalogMessages } from './i18n'\nimport { useCategoryTree } from './store'\nimport type { CategoryDetail } from './types'\n\n/**\n * A new category: its name and where it hangs. Created unpublished — a shelf nobody filled is\n * not one to show visitors — at the end of its parent's children; the tree is where it is dragged\n * into place. The address is made of the name, and a taken one is refused under the name.\n */\nconst props = withDefaults(defineProps<{ parent?: number | null }>(), { parent: null })\n\nconst { open, resolve, dismiss } = useModal<CategoryDetail>()\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst tree = useCategoryTree(context)\nconst locales = useLocales()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n\nconst name = ref('')\nconst parent = ref<TreeSelectValue>(props.parent)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\nconst field = useTemplateRef<HTMLElement>('field')\n\nconst nodes = computed(() => tree.nodes.value ?? [])\n\nonMounted(async () => {\n void tree.load().catch(() => undefined)\n await nextTick()\n field.value?.querySelector('input')?.focus()\n})\n\nasync function submit(): Promise<void> {\n if (name.value.trim() === '' || saving.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n const under = typeof parent.value === 'number' ? parent.value : null\n\n resolve(\n await api.createCategory(\n { name: { [locales.active.value]: name.value.trim() }, is_published: false },\n under,\n ),\n )\n } catch (error) {\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-category')\" :width=\"480\">\n <wx-form-item\n :label=\"t('category.name')\"\n :error=\"firstError(errors, 'name') ?? firstError(errors, 'slug')\"\n required\n >\n <div ref=\"field\">\n <wx-input v-model=\"name\" :aria-label=\"t('category.name')\" @keyup.enter=\"submit\" />\n </div>\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.field-parent')\" :error=\"firstError(errors, 'parent_id')\">\n <wx-tree-select\n v-model=\"parent\"\n :nodes=\"nodes\"\n node-key=\"id\"\n label-key=\"name\"\n children-key=\"children\"\n show-path\n separator=\" / \"\n filterable\n clearable\n default-expand-all\n teleport\n :placeholder=\"t('panel.parent-root')\"\n />\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=\"name.trim() === ''\" @click=\"submit\">\n {{ t('panel.create') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","/**\n * The last segment of an address on the site, without slashes: `/laptops/` → `laptops`,\n * `https://shop.test/en/lamp-12` → `lamp-12`. The panel compares it with the slug being typed;\n * the language prefix and the host in front of it are not part of what the slug field edits.\n */\nexport function lastSegment(url: string | null | undefined): string | null {\n if (!url) return null\n\n let path: string\n\n try {\n path = new URL(url, 'http://panel.invalid').pathname\n } catch {\n return null\n }\n\n const segments = path.split('/').filter((segment) => segment !== '')\n\n return segments.length > 0 ? decodeURIComponent(segments[segments.length - 1]!) : null\n}\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\nimport { 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 createModal,\n toast,\n WxBadge,\n WxTable,\n WxText,\n type TableColumn,\n type TableNodeDropEvent,\n type TableState,\n type TableTreeOptions,\n} from '@webx-ui/core'\nimport CategoryCreateDialog from './CategoryCreateDialog.vue'\nimport { createCatalogApi } from './api'\nimport { useCatalogMessages } from './i18n'\nimport { lastSegment } from './paths'\nimport { flatten, useCategoryTree } from './store'\nimport type { CategoryDetail, CategoryNode } from './types'\n\n/**\n * The shelves of the catalogue: the whole tree at once, with how many products each holds\n * (§6.1), dragged into place.\n *\n * Whole, not a level at a time as the pages are: a catalogue has dozens of categories, not\n * thousands, and the counts are one query for all of them on the server. A drag moves a category\n * and everything under it; the addresses do not move with it — they are flat (decision 23) — so\n * no drop is ever questioned, unlike a page's.\n *\n * Publication is here as well as on the form, because taking a branch off the site is a thing\n * done to the tree: an unpublished category hides everything below it (§6.3), and the rows under\n * it say so.\n *\n * A search puts the tree away and lists the matches flat — a branch drawn for one match deep\n * inside it tells the reader nothing they asked about.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst tree = useCategoryTree(context)\nconst router = useRouter()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\nconst message = useErrorText()\n\nconst rows = ref<CategoryNode[]>([])\nconst loading = ref(true)\n/*\n * The tree opens every branch on its first render, and only then — so the table is mounted once\n * there is a tree to open, not on the empty one it would otherwise start from.\n */\nconst loaded = ref(false)\nconst search = ref('')\n\nconst create = createModal<CategoryDetail, { parent: number | null }>(CategoryCreateDialog)\n\nconst canManage = computed(() => context.can('catalog.manage'))\nconst canDelete = computed(() => context.can('catalog.delete'))\nconst asTree = computed(() => search.value.trim() === '')\n\nconst matches = computed(() => {\n const term = search.value.trim().toLowerCase()\n\n return flatten(rows.value)\n .filter(\n (node) =>\n node.name.toLowerCase().includes(term) || (node.slug ?? '').toLowerCase().includes(term),\n )\n .map((node) => ({ ...node, children: [] }))\n})\n\nconst data = computed(() => (asTree.value ? rows.value : matches.value))\n\nconst columns = computed<TableColumn<CategoryNode>[]>(() => [\n { key: 'name', label: t('category.name'), minWidth: 200 },\n { key: 'slug', label: t('category.slug'), width: 200, hideBelow: 620 },\n { key: 'products_count', label: t('panel.column-products'), width: 110, align: 'right' },\n { key: 'state', label: t('panel.column-state'), width: 150, hideBelow: 760 },\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right' },\n])\n\nconst treeOptions = computed<TableTreeOptions<CategoryNode> | undefined>(() =>\n asTree.value\n ? {\n childrenKey: 'children',\n defaultExpandAll: true,\n draggable: canManage.value,\n allowDrop: (drag, drop) => drag.id !== drop.id,\n }\n : undefined,\n)\n\n/** A copy: the table rearranges what it is given, and the cache is every picker's. */\nfunction show(nodes: CategoryNode[]): void {\n rows.value = nodes.map(plain)\n}\n\nfunction plain(node: CategoryNode): CategoryNode {\n return { ...node, children: (node.children ?? []).map(plain) }\n}\n\nasync function load(force = true): Promise<void> {\n loading.value = true\n\n try {\n show(await tree.load(force))\n loaded.value = true\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nfunction onState(state: TableState): void {\n search.value = state.search\n}\n\nfunction open(node: CategoryNode): void {\n void router.push(`${props.base}/categories/${node.id}`)\n}\n\nasync function add(parent: CategoryNode | null): Promise<void> {\n const detail = await create({ parent: parent?.id ?? null })\n\n if (!detail) return\n\n tree.drop()\n void router.push(`${props.base}/categories/${detail.category.id}`)\n}\n\n/**\n * The table has already put the row where it was dropped; the server is told the same thing in\n * its own terms — under which parent, before which sibling — and answers with the whole tree,\n * which is drawn as it says rather than as the drop left it.\n */\nasync function dropped(event: TableNodeDropEvent<CategoryNode>): Promise<void> {\n const parent = event.parent?.id ?? null\n const siblings = event.parent ? (event.parent.children ?? []) : rows.value\n const before = siblings[event.index + 1]?.id ?? null\n\n try {\n const answer = await api.moveCategory(event.row.id, parent, before)\n\n tree.take(answer)\n show(answer)\n toast.success(t('panel.category-moved'))\n } catch (error) {\n toast.danger(message(error))\n await load()\n }\n}\n\nasync function publish(node: CategoryNode, on: boolean): Promise<void> {\n try {\n await api.saveCategory(node.id, { is_published: on })\n toast.success(on ? t('panel.category-published') : t('panel.category-unpublished'))\n await load()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nasync function remove(node: CategoryNode): Promise<void> {\n const agreed = await confirm({\n title: t('panel.delete-category-title', { name: node.name }),\n message: t('panel.delete-category-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.removeCategory(node.id)\n toast.success(t('panel.category-deleted'))\n await load()\n } catch (error) {\n // Not empty: the server's sentence says how many products or subcategories are in the way.\n toast.danger(message(error))\n }\n}\n\nfunction productsOf(node: CategoryNode) {\n return { path: `${props.base}/products`, query: { 'f.category': String(node.id) } }\n}\n\nfunction actionsFor(node: CategoryNode): RowAction[] {\n const actions: RowAction[] = [\n { key: 'open', icon: 'edit', label: t('panel.open'), run: () => open(node) },\n {\n key: 'products',\n icon: 'list',\n label: t('panel.show-products'),\n run: () => void router.push(productsOf(node)),\n },\n ]\n\n if (node.url && node.visible) {\n actions.push({ key: 'site', icon: 'link', label: t('panel.open-on-site'), href: node.url })\n }\n\n if (canManage.value) {\n actions.push(\n { key: 'add', icon: 'plus', label: t('panel.add-child'), run: () => void add(node) },\n node.is_published\n ? {\n key: 'unpublish',\n icon: 'eye-off',\n label: t('panel.unpublish'),\n run: () => void publish(node, false),\n }\n : {\n key: 'publish',\n icon: 'eye',\n label: t('panel.publish'),\n run: () => void publish(node, true),\n },\n )\n }\n\n if (canDelete.value) {\n actions.push({\n key: 'delete',\n icon: 'trash',\n label: t('panel.delete'),\n danger: true,\n run: () => void remove(node),\n })\n }\n\n return actions\n}\n\nonMounted(() => void load(false))\n\nconst actions = computed<ScreenAction[]>(() => {\n const leads: ScreenAction[] = []\n\n if (canManage.value) {\n leads.push({\n key: 'new',\n label: t('panel.new-category'),\n icon: 'plus',\n primary: true,\n run: () => void add(null),\n })\n }\n\n if (canDelete.value) {\n leads.push({\n key: 'deleted',\n label: t('module.deleted'),\n icon: 'trash',\n menu: true,\n run: () => void router.push({ path: `${props.base}/deleted`, query: { type: 'categories' } }),\n })\n }\n\n return leads\n})\n</script>\n\n<template>\n <div class=\"wx-catalog-categories\">\n <wx-list-screen\n :title=\"t('module.categories')\"\n :subtitle=\"canManage && rows.length > 0 ? t('panel.category-order') : undefined\"\n :actions=\"actions\"\n >\n <wx-table\n :key=\"loaded ? 'tree' : 'waiting'\"\n :data=\"data\"\n :columns=\"columns\"\n row-key=\"id\"\n :tree=\"treeOptions\"\n searchable\n clickable\n hover\n flush\n layout=\"fixed\"\n :loading=\"loading\"\n :cards-below=\"0\"\n :search-placeholder=\"t('panel.search-categories')\"\n :empty-text=\"search ? t('panel.empty-search') : t('panel.empty-categories')\"\n :aria-label=\"t('module.categories')\"\n @row-click=\"open\"\n @state-change=\"onState\"\n @node-drop=\"dropped\"\n >\n <template #cell-name=\"{ row }\">\n <span class=\"wx-catalog-categories__name\" :class=\"{ 'is-hidden': !row.visible }\">\n {{ row.name }}\n </span>\n </template>\n\n <template #cell-slug=\"{ row }\">\n <wx-text v-if=\"row.slug\" mono size=\"sm\" tone=\"muted\" truncate>\n /{{ lastSegment(row.url) ?? row.slug }}/\n </wx-text>\n </template>\n\n <template #cell-products_count=\"{ row }\">\n <router-link\n v-if=\"row.products_count > 0\"\n class=\"wx-catalog-categories__count\"\n :to=\"productsOf(row)\"\n @click.stop\n >\n {{ row.products_count }}\n </router-link>\n <wx-text v-else size=\"sm\" tone=\"muted\">0</wx-text>\n </template>\n\n <template #cell-state=\"{ row }\">\n <wx-badge v-if=\"!row.is_published\" dot size=\"sm\">{{ t('states.unpublished') }}</wx-badge>\n <wx-badge\n v-else-if=\"!row.visible\"\n type=\"warning\"\n dot\n size=\"sm\"\n :title=\"t('panel.category-hidden')\"\n >\n {{ t('states.published') }}\n </wx-badge>\n <wx-badge v-else type=\"success\" dot size=\"sm\">{{ t('states.published') }}</wx-badge>\n </template>\n\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-categories {\n min-width: 0;\n}\n\n.wx-catalog-categories__name {\n font-weight: var(--wx-font-weight-medium);\n}\n\n/* Hidden by a parent: still here to be arranged, but plainly not what a visitor sees. */\n.wx-catalog-categories__name.is-hidden {\n color: var(--wx-text-muted);\n}\n\n.wx-catalog-categories__count {\n color: var(--wx-color-primary);\n font-variant-numeric: tabular-nums;\n text-decoration: none;\n}\n\n.wx-catalog-categories__count:hover {\n text-decoration: underline;\n}\n</style>\n","import { inject, provide, type InjectionKey, type Ref } from 'vue'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport type { GalleryVideo } from './galleryVideo'\nimport type { CategoryRow, ProductImage, ProductRow } from './types'\n\n/**\n * What the product editor knows and the nodes of `catalog.product-form` do not.\n *\n * The gallery needs it most: its pictures are not a value of the form but records of their own,\n * added, ordered and captioned by requests of their own (§11.2), so the node has to know which\n * product it is attached to and whether this administrator may write.\n */\nexport interface ProductEditorContext {\n /** The product as the server last answered it, or `null` while the first request is out. */\n product: Ref<ProductRow | null>\n /** The gallery in its order. The editor reads it with the product; the node keeps it. */\n images: Ref<ProductImage[]>\n /** Closed for writing: no permission, or a save in flight. */\n locked: Ref<boolean>\n /**\n * The values of the form as they are right now, edits included — what a satellite's node reads\n * when it depends on another field: the properties' tab redraws for a main category chosen and\n * not yet saved. Left out by an editor that has no form around its nodes.\n */\n values?: Ref<ScreenModel>\n /**\n * Video files on their way into the gallery. Kept up here rather than in the gallery field, whose\n * tab is taken down when another is opened — and an upload with it. The field makes its own\n * when an editor leaves it out.\n */\n video?: GalleryVideo\n}\n\nexport const productEditorKey: InjectionKey<ProductEditorContext> = Symbol('wx-catalog-product')\n\nexport function provideProductEditor(editor: ProductEditorContext): void {\n provide(productEditorKey, editor)\n}\n\n/** The editor above this node, or `null` outside one — a demo or a test draws a placeholder. */\nexport function useProductEditor(): ProductEditorContext | null {\n return inject(productEditorKey, null)\n}\n\n/**\n * What the category editor knows and `wx-catalog-facets` needs: which category it is, so that the\n * field can say whose setting it inherits — the parent's, the grandparent's, or nobody's (§6.2).\n */\nexport interface CategoryEditorContext {\n category: Ref<CategoryRow | null>\n /** Closed for writing — what the screen's form says to its own controls, for the ones it cannot. */\n locked: Ref<boolean>\n /**\n * The server's answer to \"whose facet setting does it show\" (`facets_from`): the ancestor, `null`\n * for its own or the default, `undefined` when the server did not say.\n */\n facetsFrom: Ref<{ id: number; name: string } | null | undefined>\n}\n\nexport const categoryEditorKey: InjectionKey<CategoryEditorContext> = Symbol('wx-catalog-category')\n\nexport function provideCatalogCategoryEditor(editor: CategoryEditorContext): void {\n provide(categoryEditorKey, editor)\n}\n\nexport function useCatalogCategoryEditor(): CategoryEditorContext | null {\n return inject(categoryEditorKey, null)\n}\n","<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 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 { provideCatalogCategoryEditor } from './editor'\nimport { useCatalogMessages } from './i18n'\nimport { lastSegment } from './paths'\nimport { ancestry, useCategoryTree } from './store'\nimport type { CategoryDetail, CategoryRow } from './types'\n\n/**\n * One category of the catalogue: a head, `catalog.category-form` under it, and one button.\n *\n * The shared category editor of the panel (`WxCategoryEditorPage`) is for flat lists with a\n * prefix; this one sits in a tree, at the root of the site (§4), and its trail is the branch it\n * hangs from. The «Filters» tab reads which category this is from here, to say whose setting it\n * inherits (§6.2).\n */\ndefineOptions({ name: 'WxCatalogCategoryEditor' })\n\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst tree = useCategoryTree(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}/categories`)\n\nconst category = ref<CategoryRow | null>(null)\nconst facetsFrom = ref<CategoryDetail['facets_from']>(undefined)\nconst values = ref<ScreenModel>({})\nconst snapshot = ref('')\n\nconst loading = ref(true)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\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\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, category.value?.name ?? '') || t('panel.untitled')\n )\n})\n\n/** Where it answers, as a reader would type it: `/laptops/`. */\nconst address = computed(() => {\n const segment = lastSegment(category.value?.url)\n\n return segment === null ? undefined : `/${segment}/`\n})\n\n/** The categories above this one, from the top down. */\nconst parents = computed(() =>\n category.value\n ? ancestry(tree.nodes.value ?? [], category.value.id)\n .slice(1)\n .reverse()\n : [],\n)\n\nprovideCatalogCategoryEditor({ category, locked, facetsFrom })\nprovideHistorySubject({ id: computed(() => category.value?.id ?? null) })\n\n/* The address of a category is flat at the root of the site at any depth (decision 23). */\nprovideRecordAddress({\n values,\n prefix: ref(''),\n path: computed(() => lastSegment(category.value?.url)),\n moving: () => t('panel.address-moving'),\n})\n\nfunction take(detail: CategoryDetail): void {\n category.value = detail.category\n facetsFrom.value = detail.facets_from\n values.value = detail.values\n snapshot.value = JSON.stringify(detail.values)\n}\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n const [detail] = await Promise.all([api.category(id.value), tree.load().catch(() => [])])\n\n take(detail)\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 (!category.value || saving.value || !canManage.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n take(await api.saveCategory(category.value.id, values.value))\n // A name, a publication or an address: every picker of the catalogue shows one of them.\n tree.drop()\n void tree.load().catch(() => undefined)\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 = category.value\n\n if (!row) return\n\n const agreed = await confirm({\n title: t('panel.delete-category-title', { name: row.name }),\n message: t('panel.delete-category-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.removeCategory(row.id)\n tree.drop()\n toast.success(t('panel.category-deleted'))\n snapshot.value = current.value\n void router.push(list.value)\n } catch (error) {\n // \"Not empty: products in it: 12\" — the server's sentence is the one worth reading here.\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\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 const row = category.value\n const leads: ScreenAction[] = []\n\n if (!row) return leads\n\n // A hidden category answers 404; a link to that would be a link to an error page.\n if (row.url && row.visible) {\n leads.push({ key: 'site', label: t('panel.open-on-site'), icon: 'link', href: row.url })\n }\n\n leads.push({\n key: 'products',\n label: t('panel.show-products'),\n icon: 'list',\n menu: true,\n run: () =>\n void router.push({\n path: `${props.base}/products`,\n query: { 'f.category': String(row.id) },\n }),\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-category-editor\" @keydown=\"onKeydown\">\n <template v-if=\"loading || !category\">\n <wx-skeleton class=\"wx-catalog-category-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=\"t('module.categories')\"\n :title=\"title\"\n :subtitle=\"address\"\n :actions=\"actions\"\n >\n <template #trail>\n <wx-breadcrumb size=\"sm\" :label=\"t('panel.category-trail')\">\n <wx-breadcrumb-item :as=\"'router-link'\" :to=\"list\">\n {{ t('module.categories') }}\n </wx-breadcrumb-item>\n <wx-breadcrumb-item\n v-for=\"parent in parents\"\n :key=\"parent.id\"\n :as=\"'router-link'\"\n :to=\"`${list}/${parent.id}`\"\n >\n {{ parent.name }}\n </wx-breadcrumb-item>\n <wx-breadcrumb-item current>{{ title }}</wx-breadcrumb-item>\n </wx-breadcrumb>\n </template>\n\n <template #title-after>\n <wx-badge v-if=\"!category.is_published\" dot>{{ t('states.unpublished') }}</wx-badge>\n <wx-badge v-else-if=\"!category.visible\" type=\"warning\" dot>\n {{ t('panel.category-hidden') }}\n </wx-badge>\n </template>\n </wx-screen-head>\n\n <wx-screen\n v-model=\"values\"\n name=\"catalog.category-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-category-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-category-editor__ghost {\n max-width: 420px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted } from 'vue'\nimport { useAdmin, useRecordAddress, useTranslate } from '@webx-ui/module-admin'\nimport { WxTreeSelect, type TreeSelectValue } from '@webx-ui/core'\nimport { useCatalogMessages } from './i18n'\nimport { useCategoryTree } from './store'\nimport type { CategoryNode } from './types'\n\n/**\n * `wx-catalog-category`: a category of the catalogue, or several with `props.multiple`.\n *\n * A tree and not a flat list, because the tree is how anybody finds a shelf — \"Laptops\" means\n * nothing until it is seen under \"Computers\" — and a single choice shows its whole path once\n * picked, for the same reason.\n *\n * The additional categories cannot include the main one (decision 2): the server would drop it\n * anyway, and a picker that lets somebody tick what will silently not be saved is a picker that\n * lies. The main one is read off the form the editor hosts.\n *\n * Nothing but `multiple` is declared: a wrapper that declared what `WxTreeSelect` takes would hand\n * it a `false` where the screen asked for nothing (CLAUDE.md §4).\n */\ndefineOptions({ name: 'WxCatalogCategoryField', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<{ multiple?: boolean }>(), { multiple: false })\n\nconst value = defineModel<number | number[] | null>({ default: null })\n\nconst admin = useAdmin()\nconst tree = useCategoryTree(admin)\nconst address = useRecordAddress()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n\ntype Choice = CategoryNode & { locked?: boolean }\n\n/** The main category, for the list of the others to leave out. */\nconst main = computed(() => {\n const chosen = address?.values.value.category_id\n\n return typeof chosen === 'number' ? chosen : null\n})\n\nconst nodes = computed<Choice[]>(() => {\n const mark = (list: CategoryNode[]): Choice[] =>\n list.map((node) => ({\n ...node,\n locked: props.multiple && node.id === main.value,\n children: mark(node.children ?? []),\n }))\n\n return mark(tree.nodes.value ?? [])\n})\n\nconst model = computed<TreeSelectValue>({\n get: () => {\n if (props.multiple) return Array.isArray(value.value) ? value.value : []\n\n return typeof value.value === 'number' ? value.value : null\n },\n set: (next) => {\n if (props.multiple) {\n const ids = (Array.isArray(next) ? next : []).map(Number)\n value.value = ids.filter((id) => id !== main.value)\n\n return\n }\n\n value.value = next === null || next === undefined || Array.isArray(next) ? null : Number(next)\n },\n})\n\nonMounted(() => {\n // A tree that did not arrive leaves the chosen ids where they are, drawn by number; the form\n // still saves what it opened with.\n void tree.load().catch(() => undefined)\n})\n</script>\n\n<template>\n <wx-tree-select\n v-bind=\"$attrs\"\n v-model=\"model\"\n class=\"wx-catalog-category\"\n :nodes=\"nodes\"\n :multiple=\"props.multiple\"\n :check-strictly=\"props.multiple\"\n node-key=\"id\"\n label-key=\"name\"\n children-key=\"children\"\n disabled-key=\"locked\"\n :show-path=\"!props.multiple\"\n separator=\" / \"\n filterable\n clearable\n default-expand-all\n teleport\n :placeholder=\"props.multiple ? t('panel.pick-categories') : t('panel.pick-category')\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport {\n rowMenuWidth,\n useAdmin,\n useErrorText,\n useTranslate,\n WxDate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport {\n toast,\n WxBadge,\n WxTable,\n WxText,\n type Paginated,\n type TabItem,\n type TableColumn,\n type TableState,\n type TabValue,\n} from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { useCatalogMessages } from './i18n'\nimport { useCategoryTree } from './store'\nimport type { CategoryRef, DeletedCategory, DeletedKind, DeletedProduct } from './types'\n\n/**\n * «Deleted» (§5, §6.3): what was deleted, and the one thing that can be done to it — bring it\n * back. There is no \"delete for ever\": orders will point at products, and their links have to\n * keep answering. No filters either; a search finds what somebody is looking for, and the list\n * is newest first, which is where a regret usually is.\n *\n * Products and categories are two tabs of one screen rather than two screens, because the\n * question a reader comes with is \"where did it go\", not which kind of thing it was.\n *\n * A row says what a restore will do to it before it is done: a product whose category is\n * deleted too comes back without one and unpublished, and that is worth knowing first.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst tree = useCategoryTree(context)\nconst route = useRoute()\nconst router = useRouter()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\nconst message = useErrorText()\n\ntype Row = DeletedProduct | DeletedCategory\n\nconst page = ref<Paginated<Row> | null>(null)\nconst loading = ref(true)\nconst search = ref(typeof route.query.q === 'string' ? route.query.q : '')\n\nconst canRestore = computed(() => context.can('catalog.delete'))\n\nconst kind = computed<TabValue>({\n get: () => (route.query.type === 'categories' ? 'categories' : 'products'),\n set: (value) => {\n // The table is keyed by the tab and mounts afresh, which asks for its first page itself.\n search.value = ''\n page.value = null\n void router.replace({\n query: { type: value === 'categories' ? 'categories' : undefined },\n })\n },\n})\n\nconst views = computed<TabItem[]>(() => [\n { value: 'products', label: t('module.products') },\n { value: 'categories', label: t('module.categories') },\n])\n\nconst columns = computed<TableColumn<Row>[]>(() =>\n kind.value === 'categories'\n ? [\n { key: 'name', label: t('category.name'), minWidth: 200 },\n { key: 'parent', label: t('panel.column-parent'), width: 200, hideBelow: 640 },\n { key: 'deleted_at', label: t('panel.column-deleted'), width: 150 },\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right' },\n ]\n : [\n { key: 'name', label: t('panel.column-product'), minWidth: 200 },\n { key: 'category', label: t('product.category'), width: 220, hideBelow: 640 },\n { key: 'deleted_at', label: t('panel.column-deleted'), width: 150 },\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right' },\n ],\n)\n\nlet asked = 0\n\nasync function load(state?: TableState): Promise<void> {\n const mine = ++asked\n\n loading.value = true\n\n try {\n const answer = await api.deleted(kind.value as DeletedKind, {\n q: state?.search ?? search.value,\n page: state?.page,\n per_page: state?.perPage,\n })\n\n if (mine === asked) page.value = answer\n } catch (error) {\n toast.danger(message(error))\n } finally {\n if (mine === asked) loading.value = false\n }\n}\n\nfunction onState(state: TableState): void {\n void router.replace({\n query: { ...route.query, q: state.search === '' ? undefined : state.search },\n })\n void load(state)\n}\n\nasync function restore(row: Row): Promise<void> {\n try {\n if (kind.value === 'categories') {\n await api.restoreCategory(row.id)\n tree.drop()\n toast.success(t('panel.category-restored'))\n } else {\n const product = await api.restoreProduct(row.id)\n const bare = product.category === null && (row as DeletedProduct).category !== null\n\n toast.success(bare ? t('panel.product-restored-bare') : t('panel.product-restored'))\n }\n\n await load()\n } catch (error) {\n // A category whose address was taken while it was away is refused, and the server says by whom.\n toast.danger(message(error))\n }\n}\n\n/** A product's category or a category's parent, as the row names it. */\nfunction refOf(row: Record<string, unknown>, key: 'category' | 'parent'): CategoryRef | null {\n const found = row[key]\n\n return found && typeof found === 'object' ? (found as CategoryRef) : null\n}\n\nfunction actionsFor(row: Row): RowAction[] {\n const actions: RowAction[] = []\n\n if (canRestore.value) {\n actions.push({\n key: 'restore',\n icon: 'refresh',\n label: t('panel.restore'),\n run: () => void restore(row),\n })\n }\n\n return actions\n}\n</script>\n\n<template>\n <div class=\"wx-catalog-deleted\">\n <wx-list-screen\n v-model:view=\"kind\"\n :title=\"t('module.deleted')\"\n :subtitle=\"t('panel.deleted-help')\"\n :back=\"`${props.base}/products`\"\n :back-label=\"t('module.products')\"\n :views=\"views\"\n >\n <wx-table\n :key=\"String(kind)\"\n v-model:search=\"search\"\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n :clickable=\"false\"\n :hover=\"false\"\n flush\n layout=\"fixed\"\n :loading=\"loading\"\n :search-placeholder=\"t('panel.deleted-search')\"\n :empty-text=\"search ? t('panel.empty-search') : t('panel.deleted-empty')\"\n :aria-label=\"t('module.deleted')\"\n @state-change=\"onState\"\n >\n <template #cell-name=\"{ row }\">\n <div class=\"wx-catalog-deleted__name\">\n <span class=\"wx-catalog-deleted__title\">{{ row.name }}</span>\n <span v-if=\"'sku' in row && row.sku\" class=\"wx-catalog-deleted__code\">{{\n row.sku\n }}</span>\n <span v-else-if=\"'slug' in row && row.slug\" class=\"wx-catalog-deleted__code\">\n /{{ row.slug }}/\n </span>\n </div>\n </template>\n\n <template #cell-category=\"{ row }\">\n <wx-text v-if=\"!refOf(row, 'category')\" size=\"sm\" tone=\"muted\">\n {{ t('product.no-category') }}\n </wx-text>\n <span v-else class=\"wx-catalog-deleted__ref\">\n <wx-text\n size=\"sm\"\n truncate\n :tone=\"refOf(row, 'category')?.deleted ? 'muted' : undefined\"\n >\n {{ refOf(row, 'category')?.name }}\n </wx-text>\n <wx-badge v-if=\"refOf(row, 'category')?.deleted\" type=\"warning\" size=\"sm\" round>\n {{ t('panel.restore-bare') }}\n </wx-badge>\n </span>\n </template>\n\n <template #cell-parent=\"{ row }\">\n <wx-text v-if=\"!refOf(row, 'parent')\" size=\"sm\" tone=\"muted\">\n {{ t('panel.parent-root') }}\n </wx-text>\n <wx-text\n v-else\n size=\"sm\"\n truncate\n :tone=\"refOf(row, 'parent')?.deleted ? 'muted' : undefined\"\n >\n {{ refOf(row, 'parent')?.name }}\n <template v-if=\"refOf(row, 'parent')?.deleted\">\n ({{ t('panel.category-gone') }})</template\n >\n </wx-text>\n </template>\n\n <template #cell-deleted_at=\"{ row }\">\n <wx-date :value=\"row.deleted_at\" compact />\n </template>\n\n <template #cell-actions=\"{ row }\">\n <wx-row-menu v-if=\"canRestore\" :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-deleted {\n min-width: 0;\n}\n\n.wx-catalog-deleted__name {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.wx-catalog-deleted__title {\n font-weight: var(--wx-font-weight-medium);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-catalog-deleted__code {\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-sm);\n color: var(--wx-text-muted);\n}\n\n.wx-catalog-deleted__ref {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-4) var(--wx-space-8);\n min-width: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, watch } from 'vue'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport { WxAlert, WxSkeleton, WxSortableList, WxSwitch, WxText } from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { useCatalogCategoryEditor } from './editor'\nimport { useCatalogMessages } from './i18n'\nimport { useFacetRegistry } from './store'\nimport type { FacetInfo, FacetSetting } from './types'\n\n/**\n * `wx-catalog-facets`: which filters a category shows and in which order (§6.2).\n *\n * The value is `null` — the category takes the setting of its nearest ancestor that has one, or\n * every facet in the registry's order — or its own list. Off, the field says where the setting\n * comes from, because \"inherited\" alone leaves the reader to walk up the tree to find out what\n * that means. On, it is the list itself: dragged into order, each facet shown or not.\n *\n * Switching it on starts from what was inherited, not from a blank list: the reader wanted to\n * change one thing about the setting they were looking at.\n *\n * A facet registered after the setting was written is not in it, and is shown here switched off\n * — a new module does not get to change somebody else's filters on its own (§6.2). A key whose\n * module has gone is kept as it is: putting the module back brings the setting back with it.\n */\ndefineOptions({ name: 'WxCatalogFacetsField' })\n\nconst value = defineModel<FacetSetting[] | null>({ default: null })\n\nconst admin = useAdmin()\nconst api = createCatalogApi(admin)\nconst registry = useFacetRegistry(admin)\nconst editor = useCatalogCategoryEditor()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n\nconst loading = ref(true)\nconst failed = ref(false)\n/** The nearest ancestor with a setting of its own, or `null` — the registry's order. */\nconst source = ref<{ name: string; rows: FacetSetting[] } | null>(null)\n\nconst facets = computed<FacetInfo[]>(() => registry.facets.value ?? [])\nconst labels = computed(() => new Map(facets.value.map((facet) => [facet.key, facet.label])))\n\nconst own = computed(() => Array.isArray(value.value))\nconst locked = computed(() => editor?.locked.value ?? false)\n\ninterface Row extends FacetSetting {\n label: string\n known: boolean\n}\n\n/** The setting with every facet the registry has that it does not, switched off, at the end. */\nfunction complete(rows: FacetSetting[]): FacetSetting[] {\n const named = new Set(rows.map((row) => row.key))\n\n return [\n ...rows.map((row) => ({ key: row.key, visible: row.visible })),\n ...facets.value\n .filter((facet) => !named.has(facet.key))\n .map((facet) => ({ key: facet.key, visible: false })),\n ]\n}\n\nconst inherited = computed<FacetSetting[]>(() =>\n source.value === null\n ? facets.value.map((facet) => ({ key: facet.key, visible: true }))\n : complete(source.value.rows),\n)\n\nconst rows = computed<Row[]>({\n get: () =>\n complete(value.value ?? []).map((row) => ({\n ...row,\n label: labels.value.get(row.key) ?? row.key,\n known: labels.value.has(row.key),\n })),\n set: (next) => {\n value.value = next.map((row) => ({ key: row.key, visible: row.visible }))\n },\n})\n\n/** What a visitor of the category sees while it inherits: the names, in order. */\nconst inheritedNames = computed(() =>\n inherited.value\n .filter((row) => row.visible && labels.value.has(row.key))\n .map((row) => labels.value.get(row.key))\n .join(', '),\n)\n\nfunction toggleOwn(on: boolean | string | number | null): void {\n value.value = on ? inherited.value.map((row) => ({ ...row })) : null\n}\n\nfunction show(key: string, visible: boolean | string | number | null): void {\n rows.value = rows.value.map((row) =>\n row.key === key ? { ...row, visible: Boolean(visible) } : row,\n )\n}\n\n/**\n * Whose setting this is. The server names the ancestor (`facets_from`), and one request reads\n * its rows — the setting is shown, not only named. A server that does not say is walked up the\n * tree from the parent until somebody has a setting of their own, one request per level.\n */\nasync function findSource(parent: number | null): Promise<void> {\n const from = editor?.facetsFrom.value\n\n source.value = null\n\n if (from === null) return\n\n if (from !== undefined) {\n const detail = await api.category(from.id)\n\n if (Array.isArray(detail.values.facets)) {\n source.value = { name: from.name, rows: detail.values.facets as FacetSetting[] }\n }\n\n return\n }\n\n let next = parent\n\n while (next !== null) {\n const detail = await api.category(next)\n const setting = detail.values.facets\n\n if (Array.isArray(setting)) {\n source.value = { name: detail.category.name, rows: setting as FacetSetting[] }\n\n return\n }\n\n next = detail.category.parent_id\n }\n}\n\nasync function load(): Promise<void> {\n loading.value = true\n failed.value = false\n\n try {\n await Promise.all([registry.load(), findSource(editor?.category.value?.parent_id ?? null)])\n } catch {\n failed.value = true\n } finally {\n loading.value = false\n }\n}\n\n/* A move or a save changes whose setting it is; the editor hands both over with the answer. */\nwatch(\n () => [editor?.category.value?.parent_id, JSON.stringify(editor?.facetsFrom.value)],\n (now, before) => {\n if (now[0] !== before[0] || now[1] !== before[1]) void load()\n },\n)\n\nonMounted(load)\n</script>\n\n<template>\n <div class=\"wx-catalog-facets\">\n <wx-skeleton v-if=\"loading\" :rows=\"3\" />\n\n <wx-alert\n v-else-if=\"failed\"\n type=\"warning\"\n variant=\"soft\"\n :description=\"t('panel.facets-failed')\"\n />\n\n <template v-else>\n <div class=\"wx-catalog-facets__own\">\n <wx-switch\n :model-value=\"own\"\n :disabled=\"locked\"\n :label=\"t('panel.facets-own')\"\n @update:model-value=\"toggleOwn\"\n />\n <wx-text size=\"sm\" tone=\"muted\">\n {{ own ? t('panel.facets-own-help') : t('panel.facets-inherited-help') }}\n </wx-text>\n </div>\n\n <div v-if=\"!own\" class=\"wx-catalog-facets__inherited\">\n <wx-text weight=\"medium\">\n {{\n source ? t('panel.facets-inherited', { name: source.name }) : t('panel.facets-default')\n }}\n </wx-text>\n <wx-text v-if=\"inheritedNames\" size=\"sm\" tone=\"muted\">{{ inheritedNames }}</wx-text>\n <wx-text v-else-if=\"facets.length === 0\" size=\"sm\" tone=\"muted\">\n {{ t('panel.facets-empty') }}\n </wx-text>\n </div>\n\n <wx-sortable-list\n v-else\n v-model=\"rows\"\n size=\"sm\"\n item-key=\"key\"\n item-label=\"label\"\n :disabled=\"locked\"\n :empty-text=\"t('panel.facets-empty')\"\n >\n <template #default=\"{ item }\">\n <span class=\"wx-catalog-facets__name\" :class=\"{ 'is-off': !(item as Row).visible }\">\n {{ (item as Row).label }}\n </span>\n <wx-text v-if=\"!(item as Row).known\" size=\"sm\" tone=\"muted\">\n {{ t('panel.facets-unknown') }}\n </wx-text>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-switch\n size=\"sm\"\n :model-value=\"(item as Row).visible\"\n :disabled=\"locked || !(item as Row).known\"\n :aria-label=\"`${t('panel.facets-shown')}: ${(item as Row).label}`\"\n @update:model-value=\"(on) => show((item as Row).key, on)\"\n />\n </template>\n </wx-sortable-list>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-catalog-facets {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n}\n\n.wx-catalog-facets__own,\n.wx-catalog-facets__inherited {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-4);\n}\n\n.wx-catalog-facets :deep(.wx-sortable-list__content) {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n min-width: 0;\n}\n\n.wx-catalog-facets__name {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Hidden facets stay in the list — their place is part of the setting — but read as switched off. */\n.wx-catalog-facets__name.is-off {\n color: var(--wx-text-muted);\n}\n</style>\n","import type { ProductImage } from './types'\n\n/**\n * The browser half of videos in the gallery (§1.6, §7 of the video spec): the frame that becomes\n * the poster, and the checks made before a byte is sent.\n */\n\n/** What the panel learns about a video file by opening it in the browser. */\nexport interface VideoProbe {\n /** The frame, as a JPEG; `null` when only the length was asked for. */\n frame: Blob | null\n /** Whole seconds, or `null` when the file does not say. */\n duration: number | null\n width: number\n height: number\n}\n\n/**\n * The pieces of the DOM the probe needs, handed in so that a test can stand them up: jsdom does\n * not decode video, so a probe against a real `<video>` there never gets past loading.\n */\nexport interface ProbeEnvironment {\n createVideo(): HTMLVideoElement\n createCanvas(): HTMLCanvasElement\n createUrl(file: Blob): string\n revokeUrl(url: string): void\n}\n\nexport interface ProbeOptions {\n /** Take a frame as well as the length. */\n frame?: boolean\n /** How long to wait for the browser to open the file before giving up on it. */\n timeout?: number\n environment?: ProbeEnvironment\n}\n\n/** The browser could not open the file — mostly a codec it does not play. */\nexport class VideoUnreadable extends Error {\n constructor(reason: string) {\n super(reason)\n this.name = 'VideoUnreadable'\n }\n}\n\nconst browser: ProbeEnvironment = {\n createVideo: () => document.createElement('video'),\n createCanvas: () => document.createElement('canvas'),\n createUrl: (file) => URL.createObjectURL(file),\n revokeUrl: (url) => URL.revokeObjectURL(url),\n}\n\n/** Where the frame is taken: a little in, as the very first frame is so often black. */\nexport function frameTime(duration: number): number {\n if (!Number.isFinite(duration) || duration <= 0) return 0\n\n return Math.min(1, duration / 10)\n}\n\n/**\n * Open a video file in the browser and read its length and, if asked, one frame of it.\n *\n * Read off the local file, before the upload, so a poster does not wait on gigabytes going up\n * (decision 6). Rejects with `VideoUnreadable` when the browser cannot open the file, which is\n * what the panel then says in words: add a picture and attach the video to it.\n */\nexport function probeVideo(file: Blob, options: ProbeOptions = {}): Promise<VideoProbe> {\n const environment = options.environment ?? browser\n const timeout = options.timeout ?? 15000\n const video = environment.createVideo()\n const url = environment.createUrl(file)\n\n video.muted = true\n video.playsInline = true\n video.preload = 'auto'\n\n return new Promise<VideoProbe>((resolve, reject) => {\n let settled = false\n const timer = setTimeout(() => fail('The browser took too long to open the video.'), timeout)\n\n function done(): void {\n settled = true\n clearTimeout(timer)\n video.removeAttribute('src')\n video.load?.()\n environment.revokeUrl(url)\n }\n\n function fail(reason: string): void {\n if (settled) return\n\n done()\n reject(new VideoUnreadable(reason))\n }\n\n function duration(): number | null {\n return Number.isFinite(video.duration) && video.duration > 0\n ? Math.round(video.duration)\n : null\n }\n\n video.addEventListener('error', () => fail('The browser cannot play this video.'))\n\n video.addEventListener('loadedmetadata', () => {\n if (settled) return\n\n if (!options.frame) {\n done()\n resolve({\n frame: null,\n duration: duration(),\n width: video.videoWidth,\n height: video.videoHeight,\n })\n\n return\n }\n\n // Sound only, or a codec that gives a length and no picture.\n if (video.videoWidth === 0 || video.videoHeight === 0) {\n fail('The video has no picture the browser can show.')\n\n return\n }\n\n video.currentTime = frameTime(video.duration)\n })\n\n video.addEventListener('seeked', () => {\n if (settled) return\n\n const canvas = environment.createCanvas()\n canvas.width = video.videoWidth\n canvas.height = video.videoHeight\n\n const context = canvas.getContext('2d')\n\n if (context === null) {\n fail('The browser gave no canvas to draw the frame on.')\n\n return\n }\n\n context.drawImage(video, 0, 0, canvas.width, canvas.height)\n\n const { videoWidth: width, videoHeight: height } = video\n const length = duration()\n\n canvas.toBlob(\n (blob) => {\n if (settled) return\n\n if (blob === null) {\n fail('The frame could not be turned into a picture.')\n\n return\n }\n\n done()\n resolve({ frame: blob, duration: length, width, height })\n },\n 'image/jpeg',\n 0.85,\n )\n })\n\n video.src = url\n })\n}\n\n/** The name of the poster: the video's own, as a JPEG. */\nexport function posterName(file: Pick<File, 'name'>): string {\n const base = file.name.replace(/\\.[^.]+$/, '')\n\n return `${base === '' ? 'video' : base}.jpg`\n}\n\nconst YOUTUBE_HOSTS = new Set([\n 'youtube.com',\n 'www.youtube.com',\n 'm.youtube.com',\n 'music.youtube.com',\n 'youtu.be',\n 'www.youtube-nocookie.com',\n 'youtube-nocookie.com',\n])\n\nconst YOUTUBE_ID = /^[A-Za-z0-9_-]{11}$/\n\n/**\n * The id of a YouTube video in any of the forms the server takes (§3), or `null`. The server\n * decides in the end; this only says what the field should call the address.\n */\nexport function youTubeId(address: string): string | null {\n let url: URL\n\n try {\n url = new URL(address.trim())\n } catch {\n return null\n }\n\n if (!/^https?:$/.test(url.protocol) || !YOUTUBE_HOSTS.has(url.hostname.toLowerCase())) {\n return null\n }\n\n if (url.hostname.toLowerCase() === 'youtu.be') {\n const id = url.pathname.split('/')[1] ?? ''\n\n return YOUTUBE_ID.test(id) ? id : null\n }\n\n const watched = url.pathname === '/watch' ? url.searchParams.get('v') : null\n\n if (watched !== null) return YOUTUBE_ID.test(watched) ? watched : null\n\n const [, kind, id] = url.pathname.split('/')\n\n return ['shorts', 'embed', 'live'].includes(kind ?? '') && YOUTUBE_ID.test(id ?? '')\n ? (id ?? null)\n : null\n}\n\n/** A direct link to an MP4 or WebM file, judged by its name — the server checks the answer. */\nexport function isVideoFileLink(address: string): boolean {\n try {\n const url = new URL(address.trim())\n\n return /^https?:$/.test(url.protocol) && /\\.(mp4|webm)$/i.test(url.pathname)\n } catch {\n return false\n }\n}\n\n/** Anything the gallery treats as a video rather than a picture. */\nexport function isVideoLink(address: string): boolean {\n return youTubeId(address) !== null || isVideoFileLink(address)\n}\n\n/** A file the gallery may take as a video, by the limits the screen gave it. */\nexport function videoRefusal(\n file: Pick<File, 'type' | 'size'>,\n types: readonly string[],\n maxBytes: number,\n): 'type' | 'size' | null {\n if (!types.includes(file.type)) return 'type'\n if (file.size > maxBytes) return 'size'\n\n return null\n}\n\n/**\n * The poster the server puts on a video that came by a direct link without a picture: a plain\n * dark PNG, 1280×720 (a single pixel where the server has no GD). Recognised by its shape, as the\n * resource carries no mark of it; a panel-made poster is a JPEG, so a mistake here needs a PNG of\n * exactly that size with a file video on it, and then the hint only says what may be done anyway.\n */\nexport function hasPlaceholderPoster(image: ProductImage): boolean {\n if (image.video?.provider !== 'file' || !/\\.png$/i.test(image.path)) return false\n\n return (image.width === 1280 && image.height === 720) || (image.width === 1 && image.height === 1)\n}\n\n/** Bytes as the panel says them: \"840 KB\", \"1.2 GB\". */\nexport function formatBytes(bytes: number, locale?: string): string {\n const units = ['B', 'KB', 'MB', 'GB', 'TB']\n let value = Math.max(0, bytes)\n let unit = 0\n\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024\n unit += 1\n }\n\n const figure = new Intl.NumberFormat(locale, {\n maximumFractionDigits: value >= 100 || unit === 0 ? 0 : 1,\n }).format(value)\n\n return `${figure} ${units[unit]}`\n}\n\n/** A length of time as a clock: \"0:42\", \"12:05\", \"1:02:03\". */\nexport function formatClock(seconds: number): string {\n const whole = Math.max(0, Math.round(seconds))\n const hours = Math.floor(whole / 3600)\n const minutes = Math.floor((whole % 3600) / 60)\n const rest = String(whole % 60).padStart(2, '0')\n\n return hours > 0 ? `${hours}:${String(minutes).padStart(2, '0')}:${rest}` : `${minutes}:${rest}`\n}\n","import {\n computed,\n getCurrentScope,\n onScopeDispose,\n readonly,\n ref,\n type ComputedRef,\n type Ref,\n} from 'vue'\nimport {\n forgetUnfinishedUpload,\n unfinishedUploads,\n uploadFingerprint,\n useAdmin,\n useChunkedUpload,\n useErrorText,\n useTranslate,\n type AdminContext,\n type ChunkedUpload,\n type UnfinishedUpload,\n} from '@webx-ui/module-admin'\nimport { toast } from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport type { ProductImage } from './types'\nimport { posterName, probeVideo, VideoUnreadable, type ProbeOptions } from './video'\n\n/**\n * Video files going into the gallery (§7 of the video spec): the frame, the picture, the file a\n * piece at a time, and the request that puts the two together.\n *\n * Kept by the product editor rather than by the gallery field. The field lives on a tab, and a tab\n * nobody is looking at is taken down — with the chunked upload inside it, which pauses when its\n * scope dies. Up here the upload carries on while the rest of the form is being filled in.\n *\n * One file at a time, as there is one chunked upload: several at once share one connection\n * anyway, and one bar that moves says more than three that crawl.\n */\n\nexport const VIDEO_PURPOSE = 'catalog.video'\n\nexport type VideoJobStage = 'waiting' | 'poster' | 'uploading' | 'attaching' | 'failed'\n\nexport interface VideoJob {\n key: number\n file: File\n /** The product it goes to, as it was when the file was chosen. */\n product: number\n /** The picture it goes onto; `null` until the frame has been uploaded as one. */\n image: number | null\n /** The job made that picture itself — a cancel takes it away again. */\n created: boolean\n /** The frame, until the picture is on the server and the row shows its own. */\n poster: string | null\n duration: number | null\n stage: VideoJobStage\n /** Why it stopped, in words; only in `failed`. */\n error: string | null\n /** The browser could not open the file, so there is no frame to make a picture of. */\n unreadable: boolean\n}\n\n/** An upload this browser left unfinished for this product, with the picture it was for. */\nexport interface UnfinishedVideo extends UnfinishedUpload {\n image: number | null\n}\n\nexport interface GalleryVideo {\n readonly jobs: Readonly<Ref<readonly VideoJob[]>>\n /** The one upload that moves — its state, progress, speed and time left. */\n readonly upload: ChunkedUpload\n /** Something is on its way to the server; leaving now loses the rest of it. */\n readonly busy: ComputedRef<boolean>\n /** What a reload interrupted on this product, to be continued by choosing the file again. */\n readonly unfinished: ComputedRef<UnfinishedVideo[]>\n /** Onto `image`, or a new row made of its own frame when there is none. */\n add(file: File, image?: number | null): void\n pause(): void\n resume(): void\n /** Try a failed one again: a file the server has part of continues from there. */\n retry(job: VideoJob): void\n cancel(job: VideoJob): Promise<void>\n /** Take a failed one off the list. */\n dismiss(job: VideoJob): void\n /** Stop offering to continue an upload. */\n forget(entry: UnfinishedVideo): void\n}\n\nexport interface GalleryVideoOptions {\n product: Ref<number | null>\n images: Ref<ProductImage[]>\n admin?: AdminContext\n storage?: Storage | null\n probe?: ProbeOptions\n}\n\nconst TARGETS_KEY = 'webx:catalog-video-targets'\n\ntype Targets = Record<string, { product: number; image: number | null }>\n\nexport function createGalleryVideo(options: GalleryVideoOptions): GalleryVideo {\n const admin = options.admin ?? useAdmin()\n const api = createCatalogApi(admin)\n const storage = options.storage === undefined ? defaultStorage() : options.storage\n const upload = useChunkedUpload({ admin, storage })\n const t = useTranslate('webx-catalog')\n const message = useErrorText()\n\n const jobs = ref<VideoJob[]>([])\n // Bumped whenever storage changes under the list of unfinished uploads.\n const stored = ref(0)\n let next = 1\n let running = false\n // The job whose file the chunked upload holds, which is the only one a cancel can stop.\n let uploading: number | null = null\n\n const busy = computed(() => jobs.value.some((job) => job.stage !== 'failed'))\n\n const unfinished = computed<UnfinishedVideo[]>(() => {\n void stored.value\n const product = options.product.value\n const targets = readTargets(storage)\n const going = new Set(jobs.value.map((job) => uploadFingerprint(job.file)))\n\n if (product === null) return []\n\n return unfinishedUploads(VIDEO_PURPOSE, storage)\n .filter((entry) => targets[entry.fingerprint]?.product === product)\n .filter((entry) => !going.has(entry.fingerprint))\n .map((entry) => ({ ...entry, image: targets[entry.fingerprint]?.image ?? null }))\n })\n\n function patch(key: number, change: Partial<VideoJob>): void {\n jobs.value = jobs.value.map((job) => (job.key === key ? { ...job, ...change } : job))\n }\n\n function find(key: number): VideoJob | undefined {\n return jobs.value.find((job) => job.key === key)\n }\n\n function setTarget(file: File, product: number, image: number | null): void {\n writeTargets(storage, {\n ...readTargets(storage),\n [uploadFingerprint(file)]: { product, image },\n })\n stored.value += 1\n }\n\n function dropTarget(file: File): void {\n const targets = readTargets(storage)\n delete targets[uploadFingerprint(file)]\n writeTargets(storage, targets)\n stored.value += 1\n }\n\n function replace(image: ProductImage): void {\n options.images.value = options.images.value.map((one) => (one.id === image.id ? image : one))\n }\n\n function add(file: File, image: number | null = null): void {\n const product = options.product.value\n\n if (product === null) return\n\n jobs.value = [\n ...jobs.value,\n {\n key: next++,\n file,\n product,\n image,\n created: false,\n poster: null,\n duration: null,\n stage: 'waiting',\n error: null,\n unreadable: false,\n },\n ]\n\n void pump()\n }\n\n /** The jobs one after another, until none is waiting. */\n async function pump(): Promise<void> {\n if (running) return\n\n running = true\n\n try {\n let job = jobs.value.find((one) => one.stage === 'waiting')\n\n while (job !== undefined) {\n await work(job.key)\n job = jobs.value.find((one) => one.stage === 'waiting')\n }\n } finally {\n running = false\n }\n }\n\n async function work(key: number): Promise<void> {\n let job = find(key)\n\n if (job === undefined) return\n\n try {\n if (job.image === null) {\n patch(key, { stage: 'poster' })\n\n let probe\n\n try {\n probe = await probeVideo(job.file, { ...options.probe, frame: true })\n } catch (failure) {\n if (!(failure instanceof VideoUnreadable)) throw failure\n\n patch(key, { stage: 'failed', unreadable: true, error: t('panel.video-unreadable') })\n\n return\n }\n\n if (find(key) === undefined) return\n\n const poster = URL.createObjectURL(probe.frame!)\n patch(key, { poster, duration: probe.duration })\n\n try {\n const picture = await api.addImage(\n job.product,\n new File([probe.frame!], posterName(job.file), { type: 'image/jpeg' }),\n )\n\n // Cancelled while the picture was going up: it is nobody's now.\n if (find(key) === undefined) {\n await api.removeImage(job.product, picture.id).catch(() => undefined)\n\n return\n }\n\n if (options.product.value === job.product) {\n options.images.value = [...options.images.value, picture]\n }\n\n patch(key, { image: picture.id, created: true })\n } finally {\n URL.revokeObjectURL(poster)\n patch(key, { poster: null })\n }\n } else if (job.duration === null) {\n // The length is all a picture that is already there needs; a file the browser cannot\n // open still goes up — the server judges it by what is in it.\n const probe = await probeVideo(job.file, options.probe).catch(() => null)\n\n patch(key, { duration: probe?.duration ?? null })\n }\n\n job = find(key)\n\n if (job === undefined || job.image === null) return\n\n patch(key, { stage: 'uploading', error: null })\n setTarget(job.file, job.product, job.image)\n\n uploading = key\n const id = await upload.start(job.file, VIDEO_PURPOSE)\n\n // Cancelled: `cancel` has already tidied up after it.\n if (id === null || find(key) === undefined) return\n\n dropTarget(job.file)\n patch(key, { stage: 'attaching' })\n\n const answer = await api.attachVideo(job.product, job.image, {\n upload: id,\n duration: job.duration,\n })\n\n if ('queued' in answer) return\n\n if (options.product.value === job.product) replace(answer)\n\n jobs.value = jobs.value.filter((one) => one.key !== key)\n toast.success(t('panel.video-attached'))\n } catch (failure) {\n if (find(key) !== undefined) {\n patch(key, { stage: 'failed', error: message(failure) })\n }\n }\n }\n\n function retry(job: VideoJob): void {\n if (find(job.key)?.stage !== 'failed') return\n\n patch(job.key, { stage: 'waiting', error: null, unreadable: false })\n void pump()\n }\n\n async function cancel(job: VideoJob): Promise<void> {\n const current = find(job.key)\n\n if (current === undefined) return\n\n jobs.value = jobs.value.filter((one) => one.key !== job.key)\n dropTarget(current.file)\n\n if (uploading === current.key) {\n uploading = null\n await upload.cancel()\n }\n\n // The picture was made of the video's own frame for this video; without it, it is not\n // something anybody chose to put in the gallery.\n if (current.created && current.image !== null) {\n try {\n await api.removeImage(current.product, current.image)\n\n if (options.product.value === current.product) {\n options.images.value = options.images.value.filter((one) => one.id !== current.image)\n }\n } catch (failure) {\n toast.danger(message(failure))\n }\n }\n }\n\n function dismiss(job: VideoJob): void {\n jobs.value = jobs.value.filter((one) => one.key !== job.key)\n }\n\n function forget(entry: UnfinishedVideo): void {\n forgetUnfinishedUpload(entry.fingerprint, VIDEO_PURPOSE, storage)\n\n const targets = readTargets(storage)\n delete targets[entry.fingerprint]\n writeTargets(storage, targets)\n stored.value += 1\n }\n\n /* The browser's own question on the way out: it cannot wait, so all it can do is ask. */\n function guard(event: BeforeUnloadEvent): void {\n if (busy.value) event.preventDefault()\n }\n\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', guard)\n }\n\n if (getCurrentScope() !== undefined) {\n onScopeDispose(() => {\n if (typeof window !== 'undefined') window.removeEventListener('beforeunload', guard)\n })\n }\n\n return {\n jobs: readonly(jobs) as Readonly<Ref<readonly VideoJob[]>>,\n upload,\n busy,\n unfinished,\n add,\n pause: () => upload.pause(),\n resume: () => void upload.resume(),\n retry,\n cancel,\n dismiss,\n forget,\n }\n}\n\nfunction defaultStorage(): Storage | null {\n try {\n return typeof localStorage === 'undefined' ? null : localStorage\n } catch {\n return null\n }\n}\n\nfunction readTargets(storage: Storage | null): Targets {\n if (storage === null) return {}\n\n try {\n const parsed: unknown = JSON.parse(storage.getItem(TARGETS_KEY) ?? '{}')\n\n return parsed !== null && typeof parsed === 'object' ? (parsed as Targets) : {}\n } catch {\n return {}\n }\n}\n\nfunction writeTargets(storage: Storage | null, targets: Targets): void {\n if (storage === null) return\n\n try {\n if (Object.keys(targets).length === 0) {\n storage.removeItem(TARGETS_KEY)\n } else {\n storage.setItem(TARGETS_KEY, JSON.stringify(targets))\n }\n } catch {\n // Refused or full: offering to continue after a reload is a courtesy.\n }\n}\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport { useModal, WxButton, WxDialog, WxFormItem, WxInput, WxSpace } from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { firstError } from './errors'\nimport { useCatalogMessages } from './i18n'\nimport type { ProductImage, QueuedVideo } from './types'\nimport { isVideoLink } from './video'\n\n/**\n * A video by its address, onto a picture of the gallery: a YouTube link, or a direct link to an\n * MP4 or WebM file the server then downloads in its queue (§5 of the video spec).\n */\nconst props = defineProps<{ product: number; image: number }>()\n\nconst { open, resolve, dismiss } = useModal<ProductImage | QueuedVideo>()\n\nconst api = createCatalogApi(useAdmin())\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\nconst message = useErrorText()\n\nconst address = ref('')\nconst saving = ref(false)\nconst error = ref<string | null>(null)\nconst field = useTemplateRef<HTMLElement>('field')\n\nconst ready = computed(() => isVideoLink(address.value))\n\nonMounted(async () => {\n await nextTick()\n field.value?.querySelector('input')?.focus()\n})\n\nasync function submit(): Promise<void> {\n if (saving.value) return\n\n if (!ready.value) {\n error.value = t('panel.video-link-wrong')\n\n return\n }\n\n saving.value = true\n error.value = null\n\n try {\n resolve(await api.attachVideo(props.product, props.image, { url: address.value.trim() }))\n } catch (failure) {\n const errors = (failure as { body?: { errors?: Record<string, string[]> } }).body?.errors ?? {}\n\n error.value = firstError(errors, 'url') ?? message(failure)\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('panel.video-link-title')\" :width=\"480\">\n <wx-form-item\n :label=\"t('panel.video-link')\"\n :help=\"t('panel.video-link-help')\"\n :error=\"error ?? undefined\"\n >\n <div ref=\"field\">\n <wx-input\n v-model=\"address\"\n type=\"url\"\n placeholder=\"https://www.youtube.com/watch?v=\"\n :aria-label=\"t('panel.video-link')\"\n @keydown.enter.prevent=\"submit\"\n />\n </div>\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\n type=\"primary\"\n :loading=\"saving\"\n :disabled=\"address.trim() === ''\"\n @click=\"submit\"\n >\n {{ t('panel.video-attach') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { useTranslate } from '@webx-ui/module-admin'\nimport { WxButton, WxProgress, WxText, useLocales } from '@webx-ui/core'\nimport type { GalleryVideo, VideoJob } from './galleryVideo'\nimport { formatBytes, formatClock } from './video'\n\n/**\n * One video on its way into the gallery: what it is doing, and the bar while the file goes up —\n * the share, the speed and the time left, with pause and cancel beside it.\n */\nconst props = defineProps<{ job: VideoJob; video: GalleryVideo }>()\n\nconst t = useTranslate('webx-catalog')\nconst locales = useLocales()\n\nconst upload = computed(() => props.video.upload)\nconst state = computed(() => upload.value.state.value)\nconst percent = computed(() => Math.floor(upload.value.progress.value * 100))\n\nconst figures = computed(() => {\n const locale = locales.active.value\n const parts = [\n t('panel.video-sent', {\n sent: formatBytes(upload.value.uploaded.value, locale),\n total: formatBytes(upload.value.total.value, locale),\n }),\n ]\n\n if (state.value === 'paused') {\n parts.push(t('panel.video-paused'))\n } else if (state.value === 'offline') {\n parts.push(t('panel.video-offline'))\n } else {\n if (upload.value.speed.value > 0) {\n parts.push(`${formatBytes(upload.value.speed.value, locale)}/s`)\n }\n\n const left = upload.value.remaining.value\n\n if (left !== null) parts.push(t('panel.video-left', { time: formatClock(left) }))\n }\n\n return parts\n})\n\nconst stage = computed(() => {\n switch (props.job.stage) {\n case 'waiting':\n return t('panel.video-waiting')\n case 'poster':\n return t('panel.video-poster')\n case 'attaching':\n return t('panel.video-attaching')\n default:\n return ''\n }\n})\n</script>\n\n<template>\n <div class=\"wx-catalog-video-progress\" :class=\"{ 'is-failed': job.stage === 'failed' }\">\n <template v-if=\"job.stage === 'uploading'\">\n <wx-progress\n :value=\"percent\"\n :max=\"100\"\n size=\"sm\"\n show-value\n :status=\"state === 'failed' ? 'danger' : 'default'\"\n :aria-label=\"t('panel.video-uploading', { name: job.file.name })\"\n />\n <div class=\"wx-catalog-video-progress__line\">\n <wx-text size=\"sm\" tone=\"muted\" class=\"wx-catalog-video-progress__figures\">\n <!-- The dot goes with the figure after it, so a line never ends on one. -->\n <template v-for=\"(part, index) in figures\" :key=\"index\">\n {{ ' '\n }}<span class=\"wx-catalog-video-progress__figure\">{{\n index > 0 ? `· ${part}` : part\n }}</span>\n </template>\n </wx-text>\n <div class=\"wx-catalog-video-progress__buttons\">\n <wx-button v-if=\"state === 'uploading'\" size=\"sm\" variant=\"text\" @click=\"video.pause()\">\n {{ t('panel.video-pause') }}\n </wx-button>\n <wx-button\n v-else-if=\"state === 'paused' || state === 'offline'\"\n size=\"sm\"\n variant=\"text\"\n @click=\"video.resume()\"\n >\n {{ t('panel.video-resume') }}\n </wx-button>\n <wx-button size=\"sm\" variant=\"text\" type=\"danger\" @click=\"video.cancel(job)\">\n {{ t('panel.cancel') }}\n </wx-button>\n </div>\n </div>\n </template>\n\n <template v-else-if=\"job.stage === 'failed'\">\n <wx-text size=\"sm\" tone=\"danger\" role=\"alert\">{{ job.error }}</wx-text>\n <div class=\"wx-catalog-video-progress__buttons\">\n <wx-button v-if=\"!job.unreadable\" size=\"sm\" variant=\"text\" @click=\"video.retry(job)\">\n {{ t('panel.video-retry') }}\n </wx-button>\n <wx-button v-if=\"job.unreadable\" size=\"sm\" variant=\"text\" @click=\"video.dismiss(job)\">\n {{ t('panel.video-dismiss') }}\n </wx-button>\n <wx-button v-else size=\"sm\" variant=\"text\" type=\"danger\" @click=\"video.cancel(job)\">\n {{ t('panel.cancel') }}\n </wx-button>\n </div>\n </template>\n\n <template v-else>\n <wx-progress indeterminate size=\"sm\" :aria-label=\"stage\" />\n <div class=\"wx-catalog-video-progress__line\">\n <wx-text size=\"sm\" tone=\"muted\">{{ stage }}</wx-text>\n <div class=\"wx-catalog-video-progress__buttons\">\n <wx-button size=\"sm\" variant=\"text\" type=\"danger\" @click=\"video.cancel(job)\">\n {{ t('panel.cancel') }}\n </wx-button>\n </div>\n </div>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-catalog-video-progress {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-4);\n min-width: 0;\n}\n\n.wx-catalog-video-progress__line {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--wx-space-4) var(--wx-space-8);\n min-width: 0;\n}\n\n.wx-catalog-video-progress__figures {\n font-variant-numeric: tabular-nums;\n min-width: 0;\n}\n\n/* A figure never breaks in two; the line breaks between them. */\n.wx-catalog-video-progress__figure {\n white-space: nowrap;\n}\n\n.wx-catalog-video-progress__buttons {\n display: flex;\n gap: var(--wx-space-4);\n margin-inline-start: auto;\n}\n\n.wx-catalog-video-progress.is-failed {\n flex-direction: row;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--wx-space-4) var(--wx-space-8);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, useTemplateRef } from 'vue'\nimport {\n uploadFingerprint,\n useAdmin,\n useErrorText,\n useTranslate,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n toast,\n useLocales,\n WxAlert,\n WxBadge,\n WxButton,\n WxCard,\n WxInput,\n WxSortableList,\n WxText,\n WxUpload,\n type LocalizedValue,\n type UploadFile,\n} from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { useProductEditor } from './editor'\nimport { createGalleryVideo, type GalleryVideo, type UnfinishedVideo } from './galleryVideo'\nimport { useCatalogMessages } from './i18n'\nimport type { ProductImage, QueuedVideo } from './types'\nimport { formatBytes, formatClock, hasPlaceholderPoster, videoRefusal } from './video'\nimport VideoLinkDialog from './VideoLinkDialog.vue'\nimport VideoProgress from './VideoProgress.vue'\n\n/**\n * `wx-catalog-gallery`: the pictures of the product being edited (§10.4, §11.1), and the videos\n * attached to them (the video spec, §7).\n *\n * Not a value of the form. A picture is a record of its own with files on the disk, so adding one\n * is a request the moment it is chosen, deleting one deletes its files at once, and the order and\n * the captions are one request for the whole gallery — the editor's \"Save\" has nothing to do with\n * any of it. That is also why this node has no `name`: there is nothing for the form to carry.\n *\n * The captions save themselves a moment after the last keystroke, like a draft does elsewhere in\n * the panel; a reorder saves on the drop. Both send the whole gallery in its order, which is what\n * the server takes (`PUT …/images`).\n *\n * A video is never a row of its own: it hangs on a picture, which is its poster. A file dropped\n * here becomes a picture of its own frame first, then goes up a piece at a time; the uploads are\n * the editor's (`galleryVideo.ts`), so they carry on while another tab of the form is open.\n */\ndefineOptions({ name: 'WxCatalogGallery' })\n\nconst props = withDefaults(\n defineProps<{\n /** Videos on this site (`webx-catalog.fields.video`); the server's screen patch says. */\n video?: boolean\n /** What a video file may be, checked before a byte of it is sent. */\n videoTypes?: string[]\n /** How large, in bytes. */\n videoMaxBytes?: number\n }>(),\n {\n // Off unless the server says so: a server without videos would refuse the file at the end.\n video: false,\n videoTypes: () => ['video/mp4', 'video/webm'],\n videoMaxBytes: 2048 * 1048576,\n },\n)\n\n/** How long after the last keystroke the captions go to the server. */\nconst PAUSE = 800\n\nconst PICTURES = 'image/jpeg,image/png,image/webp,image/gif'\n\nconst admin = useAdmin()\nconst api = createCatalogApi(admin)\nconst editor = useProductEditor()\nconst locales = useLocales()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\nconst message = useErrorText()\n\nconst uploads = ref<UploadFile[]>([])\nconst address = ref('')\nconst fetching = ref(false)\n\nconst productId = computed(() => editor?.product.value?.id ?? null)\nconst locked = computed(() => editor === null || editor.locked.value)\n\n/** Writable, because that is how `WxSortableList` hands a reorder back. */\nconst images = computed<ProductImage[]>({\n get: () => editor?.images.value ?? [],\n set: (next) => {\n if (editor) editor.images.value = next\n },\n})\n\n/*\n * The editor's, so that an upload outlives this tab; a gallery under an editor that keeps none\n * makes its own. Never used while videos are off, so no upload can start then.\n */\nconst uploader: GalleryVideo | null =\n editor === null ? null : (editor.video ?? createGalleryVideo({ product: productId, images }))\n\nconst videos = computed(() => props.video && uploader !== null)\nconst accept = computed(() => (videos.value ? [PICTURES, ...props.videoTypes].join(',') : PICTURES))\n\nconst jobs = computed(() => uploader?.jobs.value ?? [])\n/* The ones that have no row yet: the frame is being taken, or the browser could not open the file. */\nconst pending = computed(() => jobs.value.filter((job) => job.image === null))\nconst unfinished = computed(() => (videos.value && !locked.value ? uploader!.unfinished.value : []))\n\nconst openLinkDialog = createModal<ProductImage | QueuedVideo, { product: number; image: number }>(\n VideoLinkDialog,\n)\n\n/* What was sent last, so an answer that arrives after further typing does not undo it. */\nlet timer: ReturnType<typeof setTimeout> | undefined\nlet sent = ''\n\nfunction captions(list: ProductImage[]): string {\n return JSON.stringify(list.map((image) => [image.id, image.alt, image.title]))\n}\n\nasync function saveGallery(failure: string): Promise<void> {\n const id = productId.value\n\n if (id === null) return\n\n clearTimeout(timer)\n const carrying = captions(images.value)\n sent = carrying\n\n try {\n const answer = await api.saveImages(id, images.value)\n\n // Typed on while the request was out: keep what is on screen; the next pause sends it.\n // The videos of rows still uploading are not the server's to overwrite either way.\n if (captions(images.value) === carrying) images.value = answer\n } catch (error) {\n toast.danger(`${failure} ${message(error)}`)\n }\n}\n\nfunction caption(image: ProductImage, field: 'alt' | 'title', text: unknown): void {\n images.value = images.value.map((one) =>\n one.id === image.id ? { ...one, [field]: (text ?? {}) as LocalizedValue } : one,\n )\n\n clearTimeout(timer)\n timer = setTimeout(() => void saveGallery(t('panel.gallery-captions-failed')), PAUSE)\n}\n\nfunction moved(): void {\n void saveGallery(t('panel.gallery-reorder-failed'))\n}\n\n/** A video file the screen's limits allow, said in words when they do not. */\nfunction acceptable(file: File): boolean {\n const refusal = videoRefusal(file, props.videoTypes, props.videoMaxBytes)\n\n if (refusal === 'type') toast.danger(t('panel.video-wrong-type'))\n if (refusal === 'size') {\n toast.danger(\n t('panel.video-too-large', { max: formatBytes(props.videoMaxBytes, locales.active.value) }),\n )\n }\n\n return refusal === null\n}\n\nfunction isVideo(file: File): boolean {\n return file.type.startsWith('video/')\n}\n\n/**\n * One file after another rather than all at once: the server numbers them in the order they\n * arrive, and a gallery whose order is whichever upload finished first is not the order anybody\n * chose them in. Videos go their own way — to the editor's queue, which works one at a time.\n */\nasync function add(files: UploadFile[]): Promise<void> {\n const id = productId.value\n\n if (id === null) return\n\n for (const file of files) {\n if (!file.raw) continue\n\n if (isVideo(file.raw)) {\n uploads.value = uploads.value.filter((one) => one.id !== file.id)\n\n if (videos.value && acceptable(file.raw)) uploader!.add(file.raw)\n\n continue\n }\n\n const mark = (patch: Partial<UploadFile>) => {\n uploads.value = uploads.value.map((one) => (one.id === file.id ? { ...one, ...patch } : one))\n }\n\n mark({ status: 'uploading', progress: 0 })\n\n try {\n const image = await api.addImage(id, file.raw, (progress) => mark({ progress }))\n\n images.value = [...images.value, image]\n uploads.value = uploads.value.filter((one) => one.id !== file.id)\n } catch (error) {\n mark({ status: 'error', error: message(error) })\n }\n }\n}\n\nfunction rejected(_file: File, reason: string): void {\n if (reason === 'type') {\n toast.danger(videos.value ? t('panel.gallery-wrong-type-video') : t('panel.gallery-wrong-type'))\n }\n}\n\nfunction queued(): void {\n toast.info(t('panel.video-queued'))\n}\n\nasync function fetchAddress(): Promise<void> {\n const id = productId.value\n const url = address.value.trim()\n\n if (id === null || url === '') return\n\n fetching.value = true\n\n try {\n const answer = await api.fetchImage(id, url)\n\n if ('queued' in answer) {\n queued()\n } else {\n images.value = [...images.value, answer]\n }\n\n address.value = ''\n } catch (error) {\n toast.danger(`${t('panel.gallery-failed')} ${message(error)}`)\n } finally {\n fetching.value = false\n }\n}\n\nasync function remove(image: ProductImage): Promise<void> {\n const id = productId.value\n\n if (id === null) return\n\n const agreed = await confirm({\n title: t('panel.gallery-remove-title'),\n message: t('panel.gallery-remove-text'),\n confirmText: t('panel.delete'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n // A video still going onto it stops first; one that made this picture takes it away itself.\n const job = jobs.value.find((one) => one.image === image.id)\n\n if (job !== undefined) await uploader!.cancel(job)\n if (!images.value.some((one) => one.id === image.id)) return\n\n try {\n await api.removeImage(id, image.id)\n images.value = images.value.filter((one) => one.id !== image.id)\n toast.success(t('panel.gallery-removed'))\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/* ------------------------------------------------------------------------ videos ----- */\n\nconst picker = useTemplateRef<HTMLInputElement>('picker')\n/* The row «Attach a video file…» was chosen on; `null` for continuing an unfinished upload. */\nlet pickFor: { image: number | null; resume?: UnfinishedVideo } | null = null\n\nfunction pickFile(image: number | null, resume?: UnfinishedVideo): void {\n pickFor = { image, resume }\n\n if (picker.value) {\n picker.value.value = ''\n picker.value.click()\n }\n}\n\nfunction picked(event: Event): void {\n const file = (event.target as HTMLInputElement).files?.[0]\n const target = pickFor\n pickFor = null\n\n if (!file || target === null || uploader === null) return\n\n if (target.resume !== undefined) {\n if (uploadFingerprint(file) !== target.resume.fingerprint) {\n toast.danger(\n t('panel.video-other-file', {\n name: target.resume.name,\n size: formatBytes(target.resume.size, locales.active.value),\n }),\n )\n\n return\n }\n\n // Onto the picture it was for, while that is still here; a row of its own otherwise.\n const image = images.value.some((one) => one.id === target.resume!.image)\n ? target.resume.image\n : null\n\n if (acceptable(file)) uploader.add(file, image)\n\n return\n }\n\n if (acceptable(file)) uploader.add(file, target.image)\n}\n\nasync function attachLink(image: ProductImage): Promise<void> {\n const id = productId.value\n\n if (id === null) return\n\n const answer = await openLinkDialog({ product: id, image: image.id })\n\n if (answer === undefined) return\n\n if ('queued' in answer) {\n queued()\n } else {\n images.value = images.value.map((one) => (one.id === answer.id ? answer : one))\n toast.success(t('panel.video-attached'))\n }\n}\n\nasync function detach(image: ProductImage): Promise<void> {\n const id = productId.value\n\n if (id === null) return\n\n const agreed = await confirm({\n title: t('panel.video-remove-title'),\n message:\n image.video?.provider === 'file'\n ? t('panel.video-remove-file-text')\n : t('panel.video-remove-link-text'),\n confirmText: t('panel.video-remove'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n const answer = await api.detachVideo(id, image.id)\n\n images.value = images.value.map((one) => (one.id === answer.id ? answer : one))\n toast.success(t('panel.video-removed'))\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction actions(image: ProductImage): RowAction[] {\n const list: RowAction[] = []\n const going = jobs.value.some((job) => job.image === image.id)\n\n if (videos.value && !going) {\n if (image.video) {\n list.push({\n key: 'detach',\n label: t('panel.video-remove'),\n icon: 'close',\n // Not red: the picture stays, and the question that follows says what goes.\n run: () => void detach(image),\n })\n } else {\n list.push(\n {\n key: 'file',\n label: t('panel.video-attach-file'),\n icon: 'upload',\n run: () => pickFile(image.id),\n },\n {\n key: 'link',\n label: t('panel.video-attach-link'),\n icon: 'link',\n run: () => void attachLink(image),\n },\n )\n }\n }\n\n list.push({\n key: 'delete',\n label: t('panel.delete'),\n icon: 'trash',\n danger: true,\n run: () => void remove(image),\n })\n\n return list\n}\n\n/* The text form of the triangle: Windows draws the bare character as a coloured emoji. */\nconst PLAY = '\\u25B6\\uFE0E'\n\nfunction videoLabel(image: ProductImage): string {\n const duration = image.video?.duration\n\n return duration ? `${PLAY} ${formatClock(duration)}` : PLAY\n}\n\nfunction jobOf(image: ProductImage) {\n return jobs.value.find((job) => job.image === image.id)\n}\n\nfunction unfinishedText(entry: UnfinishedVideo): string {\n const locale = locales.active.value\n\n return t('panel.video-unfinished', {\n name: entry.name,\n sent: formatBytes(entry.offset, locale),\n total: formatBytes(entry.size, locale),\n })\n}\n\n/* Leaving with a pause still running: send it rather than drop the last words typed. */\nonBeforeUnmount(() => {\n if (timer !== undefined && captions(images.value) !== sent) void saveGallery('')\n clearTimeout(timer)\n})\n</script>\n\n<template>\n <div class=\"wx-catalog-gallery\">\n <wx-alert v-if=\"!editor\" type=\"info\" variant=\"soft\" :description=\"t('panel.gallery-empty')\" />\n\n <template v-else>\n <wx-alert\n v-if=\"locked && images.length === 0\"\n type=\"info\"\n variant=\"soft\"\n :description=\"t('panel.gallery-locked')\"\n />\n\n <wx-alert\n v-for=\"entry in unfinished\"\n :key=\"entry.fingerprint\"\n type=\"warning\"\n variant=\"soft\"\n :description=\"unfinishedText(entry)\"\n >\n <template #actions>\n <wx-button size=\"sm\" variant=\"outline\" @click=\"pickFile(null, entry)\">\n {{ t('panel.video-choose-again') }}\n </wx-button>\n <wx-button size=\"sm\" variant=\"text\" @click=\"uploader!.forget(entry)\">\n {{ t('panel.video-forget') }}\n </wx-button>\n </template>\n </wx-alert>\n\n <wx-sortable-list\n v-if=\"images.length > 0\"\n v-model=\"images\"\n class=\"wx-catalog-gallery__list\"\n item-key=\"id\"\n :item-label=\"(image: ProductImage) => image.path.split('/').pop() ?? String(image.id)\"\n :disabled=\"locked\"\n :title=\"t('panel.gallery-order')\"\n @move=\"moved\"\n >\n <template #default=\"{ item, index }\">\n <div class=\"wx-catalog-gallery__item\">\n <!-- A row with a video opens the video: the picture is only its poster. -->\n <a\n class=\"wx-catalog-gallery__thumb\"\n :href=\"(item as ProductImage).video?.url ?? (item as ProductImage).url\"\n target=\"_blank\"\n rel=\"noopener\"\n :title=\"(item as ProductImage).video ? t('panel.video-open') : undefined\"\n >\n <img :src=\"(item as ProductImage).thumb ?? (item as ProductImage).url\" alt=\"\" />\n <wx-badge\n v-if=\"index === 0\"\n class=\"wx-catalog-gallery__main\"\n type=\"primary\"\n size=\"sm\"\n >\n {{ t('panel.gallery-main') }}\n </wx-badge>\n <span\n v-if=\"(item as ProductImage).video\"\n class=\"wx-catalog-gallery__play\"\n :aria-label=\"t('panel.video-badge')\"\n >\n {{ videoLabel(item as ProductImage) }}\n </span>\n <!-- On the picture rather than under the fields: a line of its own made every row\n taller and left the fields hanging off-centre. -->\n <span\n v-if=\"(item as ProductImage).width && (item as ProductImage).height\"\n class=\"wx-catalog-gallery__size\"\n >\n {{ (item as ProductImage).width }}×{{ (item as ProductImage).height }}\n </span>\n </a>\n\n <div class=\"wx-catalog-gallery__body\">\n <!-- No label above each field: the placeholder says which is which, and a row a\n label taller is a list where three pictures fill the screen. -->\n <div class=\"wx-catalog-gallery__words\">\n <wx-input\n :model-value=\"(item as ProductImage).alt\"\n localized\n size=\"sm\"\n :placeholder=\"t('panel.gallery-alt')\"\n :aria-label=\"t('panel.gallery-alt')\"\n :title=\"t('panel.gallery-alt-help')\"\n :disabled=\"locked\"\n @update:model-value=\"(text) => caption(item as ProductImage, 'alt', text)\"\n />\n <wx-input\n :model-value=\"(item as ProductImage).title\"\n localized\n size=\"sm\"\n :placeholder=\"t('panel.gallery-title')\"\n :aria-label=\"t('panel.gallery-title')\"\n :disabled=\"locked\"\n @update:model-value=\"(text) => caption(item as ProductImage, 'title', text)\"\n />\n </div>\n\n <video-progress\n v-if=\"uploader && jobOf(item as ProductImage)\"\n :job=\"jobOf(item as ProductImage)!\"\n :video=\"uploader\"\n />\n\n <wx-text\n v-else-if=\"videos && hasPlaceholderPoster(item as ProductImage)\"\n size=\"sm\"\n tone=\"muted\"\n >\n {{ t('panel.video-placeholder') }}\n </wx-text>\n </div>\n </div>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-row-menu\n v-if=\"!locked\"\n :actions=\"actions(item as ProductImage)\"\n :label=\"(item as ProductImage).path.split('/').pop()\"\n />\n </template>\n </wx-sortable-list>\n\n <wx-text v-else-if=\"!locked && pending.length === 0\" tone=\"muted\">\n {{ t('panel.gallery-empty') }}\n </wx-text>\n\n <!-- A video dropped without a picture, before its frame is a row of the gallery. -->\n <ul v-if=\"uploader && pending.length > 0\" class=\"wx-catalog-gallery__pending\">\n <li v-for=\"job in pending\" :key=\"job.key\" class=\"wx-catalog-gallery__item\">\n <span class=\"wx-catalog-gallery__thumb\">\n <img v-if=\"job.poster\" :src=\"job.poster\" alt=\"\" />\n <span class=\"wx-catalog-gallery__play\">{{ PLAY }}</span>\n </span>\n <div class=\"wx-catalog-gallery__body\">\n <wx-text size=\"sm\" class=\"wx-catalog-gallery__name\">{{ job.file.name }}</wx-text>\n <video-progress :job=\"job\" :video=\"uploader\" />\n </div>\n </li>\n </ul>\n\n <template v-if=\"!locked\">\n <wx-upload\n v-model=\"uploads\"\n :accept=\"accept\"\n multiple\n :button-text=\"videos ? t('panel.gallery-add-video') : t('panel.gallery-add')\"\n :hint=\"videos ? t('panel.gallery-hint-video') : t('panel.gallery-hint')\"\n @add=\"add\"\n @reject=\"rejected\"\n />\n\n <input\n v-if=\"videos\"\n ref=\"picker\"\n type=\"file\"\n class=\"wx-catalog-gallery__picker\"\n :accept=\"videoTypes.join(',')\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n @change=\"picked\"\n />\n\n <!-- A card of its own: a way in that is rarely taken, kept out of the way of the one that is.\n Not a <form>: the gallery stands inside the screen's own, and Enter here must fetch\n the picture rather than submit the product. -->\n <wx-card :title=\"t('panel.gallery-url')\">\n <div class=\"wx-catalog-gallery__address\">\n <wx-input\n v-model=\"address\"\n type=\"url\"\n size=\"sm\"\n placeholder=\"https://\"\n :aria-label=\"t('panel.gallery-url')\"\n @keydown.enter.prevent=\"fetchAddress\"\n />\n <wx-button\n size=\"sm\"\n variant=\"outline\"\n :loading=\"fetching\"\n :disabled=\"address.trim() === ''\"\n @click=\"fetchAddress\"\n >\n {{ t('panel.gallery-url-add') }}\n </wx-button>\n </div>\n <wx-text size=\"sm\" tone=\"muted\" class=\"wx-catalog-gallery__address-hint\">\n {{ videos ? t('panel.gallery-url-hint-video') : t('panel.gallery-url-hint') }}\n </wx-text>\n </wx-card>\n </template>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-catalog-gallery {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-16);\n container-type: inline-size;\n}\n\n.wx-catalog-gallery :deep(.wx-sortable-list__content) {\n min-width: 0;\n}\n\n.wx-catalog-gallery__item {\n display: grid;\n grid-template-columns: 72px minmax(0, 1fr);\n gap: var(--wx-space-12);\n align-items: center;\n}\n\n.wx-catalog-gallery__thumb {\n position: relative;\n display: block;\n width: 72px;\n aspect-ratio: 1;\n overflow: clip;\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-subtle);\n border: 1px solid var(--wx-border-default);\n}\n\n.wx-catalog-gallery__thumb img {\n width: 100%;\n height: 100%;\n min-width: 0;\n object-fit: contain;\n display: block;\n}\n\n.wx-catalog-gallery__main {\n position: absolute;\n inset-inline-start: var(--wx-space-2);\n inset-block-start: var(--wx-space-2);\n}\n\n/*\n * The dim behind a dialog and a fixed white, not the inverse text: that one turns dark in the dark\n * theme, and the strip is dark in both — it has to read on whatever the picture is.\n */\n.wx-catalog-gallery__size,\n.wx-catalog-gallery__play {\n position: absolute;\n background: var(--wx-bg-overlay);\n color: var(--wx-color-white);\n font-size: var(--wx-font-size-xs);\n line-height: 1.2;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n}\n\n.wx-catalog-gallery__size {\n inset-inline: 0;\n inset-block-end: 0;\n padding-block: var(--wx-space-2);\n text-align: center;\n}\n\n/*\n * In the middle, where every video's poster has it: a corner beside «Main» has no room left on a\n * 72-pixel picture once the word is in a longer language.\n */\n.wx-catalog-gallery__play {\n inset-block-start: 50%;\n inset-inline-start: 50%;\n translate: -50% -50%;\n padding: var(--wx-space-2) var(--wx-space-4);\n border-radius: var(--wx-radius-xs);\n}\n\n.wx-catalog-gallery__body {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n min-width: 0;\n}\n\n/* Two fields side by side while there is room for two readable lines of text, stacked below. */\n.wx-catalog-gallery__words {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--wx-space-8);\n}\n\n@container (min-width: 640px) {\n .wx-catalog-gallery__words {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n\n/* A phone: the picture above its words rather than a narrow column beside them. */\n@container (max-width: 420px) {\n .wx-catalog-gallery__item {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n.wx-catalog-gallery__pending {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n margin: 0;\n padding: var(--wx-space-8) var(--wx-space-12);\n list-style: none;\n border: 1px dashed var(--wx-border-default);\n border-radius: var(--wx-radius-md);\n}\n\n.wx-catalog-gallery__name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-catalog-gallery__picker {\n display: none;\n}\n\n.wx-catalog-gallery__address {\n display: flex;\n gap: var(--wx-space-8);\n align-items: center;\n max-width: 560px;\n}\n\n.wx-catalog-gallery__address > :first-child {\n flex: 1;\n min-width: 0;\n}\n\n.wx-catalog-gallery__address-hint {\n display: block;\n margin-block-start: var(--wx-space-8);\n}\n</style>\n","<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","<script setup lang=\"ts\">\nimport { computed, onMounted, reactive } from 'vue'\nimport { useAdmin, useTranslate, WxSourceSelect } from '@webx-ui/module-admin'\nimport {\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSelect,\n WxSpace,\n WxText,\n WxTreeSelect,\n type SelectOption,\n type TreeSelectValue,\n} from '@webx-ui/core'\nimport { useCatalogMessages } from './i18n'\nimport { useCategoryTree } from './store'\nimport type { BulkActionInfo, BulkParam } from './types'\n\n/**\n * What a bulk action asks for before it runs — a category, a label — and the last word before it\n * does: how many products it is about to touch. The fields are drawn from what the server said\n * about the action, so a satellite's action gets its dialog without this file knowing it: a\n * param with a `source` is a choice out of that reference book (a label, a brand), and one that is\n * not required says what choosing nothing does — for `set-brand`, take the brand off.\n */\nconst props = defineProps<{ action: BulkActionInfo; count: number }>()\n\nconst { open, resolve, dismiss } = useModal<Record<string, unknown>>()\n\nconst context = useAdmin()\nconst tree = useCategoryTree(context)\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n\nconst values = reactive<Record<string, unknown>>({})\n\nconst ready = computed(() =>\n props.action.params.every(\n (param) =>\n !param.rules?.includes('required') ||\n (values[param.name] !== undefined &&\n values[param.name] !== null &&\n values[param.name] !== ''),\n ),\n)\n\nonMounted(() => {\n if (props.action.params.some((param) => param.type === 'category')) {\n void tree.load().catch(() => undefined)\n }\n})\n\nfunction optionsOf(param: BulkParam): SelectOption[] {\n return Array.isArray(param.values)\n ? param.values.map((one) =>\n typeof one === 'object'\n ? { value: one.value, label: one.label }\n : { value: one, label: String(one) },\n )\n : []\n}\n\nfunction required(param: BulkParam): boolean {\n return param.rules?.includes('required') ?? false\n}\n\nfunction onCategory(param: BulkParam, value: TreeSelectValue): void {\n values[param.name] = Array.isArray(value) ? (value[0] ?? null) : value\n}\n\nfunction apply(): void {\n if (ready.value) resolve({ ...values })\n}\n</script>\n\n<template>\n <wx-dialog\n v-model:open=\"open\"\n :title=\"t('panel.bulk-confirm-title', { action: action.label })\"\n :width=\"440\"\n >\n <wx-text tone=\"muted\" size=\"sm\">{{ t('panel.bulk-confirm-text', { count }) }}</wx-text>\n\n <wx-form-item\n v-for=\"param in action.params\"\n :key=\"param.name\"\n :label=\"param.label\"\n :required=\"required(param)\"\n class=\"wx-catalog-bulk-params__field\"\n >\n <wx-tree-select\n v-if=\"param.type === 'category'\"\n :model-value=\"(values[param.name] as number | null | undefined) ?? null\"\n :nodes=\"tree.nodes.value ?? []\"\n node-key=\"id\"\n label-key=\"name\"\n children-key=\"children\"\n check-strictly\n filterable\n :filter-placeholder=\"t('panel.search-categories')\"\n :aria-label=\"param.label\"\n @update:model-value=\"(value: TreeSelectValue) => onCategory(param, value)\"\n />\n <wx-source-select\n v-else-if=\"param.source\"\n :model-value=\"(values[param.name] as number | null | undefined) ?? null\"\n :source=\"param.source\"\n filterable\n :clearable=\"!required(param)\"\n :placeholder=\"required(param) ? undefined : t('panel.bulk-param-empty')\"\n :aria-label=\"param.label\"\n @update:model-value=\"(value: unknown) => (values[param.name] = value)\"\n />\n <wx-select\n v-else-if=\"Array.isArray(param.values)\"\n :model-value=\"(values[param.name] as string | number | undefined) ?? null\"\n :options=\"optionsOf(param)\"\n :aria-label=\"param.label\"\n @update:model-value=\"(value: unknown) => (values[param.name] = value)\"\n />\n <wx-input v-else v-model=\"values[param.name] as string\" :aria-label=\"param.label\" />\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\n :type=\"action.permission === 'catalog.delete' && !action.trashed ? 'danger' : 'primary'\"\n :disabled=\"!ready\"\n @click=\"apply\"\n >\n {{ t('panel.bulk-apply') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-catalog-bulk-params__field {\n margin-top: var(--wx-space-16);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n createModal,\n toast,\n WxButton,\n WxDropdown,\n WxDropdownItem,\n WxIcon,\n WxProgress,\n WxText,\n} from '@webx-ui/core'\nimport BulkParamsDialog from './BulkParamsDialog.vue'\nimport { createCatalogApi } from './api'\nimport { useCatalogMessages } from './i18n'\nimport type { BulkActionInfo, BulkRun, BulkSelection } from './types'\n\n/**\n * The actions over what is picked in the list (§11.4), and the run they start.\n *\n * Which actions there are is the server's answer — the core's and every satellite's, only the\n * ones this administrator may start. A small selection is done inside the request and answered at\n * once; a large one is a run in the background, polled here with its progress, and its refusals\n * are listed by product when it ends. The list reloads either way, and `finished` carries the ids\n * that refused — what the list leaves picked, so they can be put right and tried again.\n */\nconst props = defineProps<{ count: number; selection: BulkSelection }>()\nconst emit = defineEmits<{ finished: [refused: number[]] }>()\n\n/** Often enough to look alive, rarely enough that a run of forty thousand is not a request storm. */\nconst POLL = 1500\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\nconst message = useErrorText()\n\nconst params = createModal<Record<string, unknown>, { action: BulkActionInfo; count: number }>(\n BulkParamsDialog,\n)\n\nconst actions = ref<BulkActionInfo[]>([])\nconst run = ref<BulkRun | null>(null)\nconst starting = ref(false)\nlet timer: ReturnType<typeof setTimeout> | undefined\n\nconst running = computed(\n () => run.value !== null && (run.value.status === 'queued' || run.value.status === 'running'),\n)\nconst refused = computed(() =>\n run.value !== null && !running.value && run.value.failed > 0 ? run.value : null,\n)\n\nonMounted(async () => {\n try {\n actions.value = await api.bulkActions()\n } catch {\n actions.value = []\n }\n})\n\nonBeforeUnmount(() => clearTimeout(timer))\n\n/** An action that asks for something, or deletes, is said once more with the number in it. */\nfunction asks(action: BulkActionInfo): boolean {\n return action.params.length > 0 || (action.permission === 'catalog.delete' && !action.trashed)\n}\n\nasync function start(action: BulkActionInfo): Promise<void> {\n let given: Record<string, unknown> = {}\n\n if (asks(action)) {\n const answer = await params({ action, count: props.count })\n\n if (!answer) return\n given = answer\n }\n\n starting.value = true\n\n try {\n follow(await api.startBulk(action.key, given, props.selection))\n } catch (error) {\n toast.danger(message(error))\n } finally {\n starting.value = false\n }\n}\n\nfunction follow(answer: BulkRun): void {\n run.value = answer\n\n if (answer.status === 'queued' || answer.status === 'running') {\n timer = setTimeout(() => void poll(answer.id!), POLL)\n\n return\n }\n\n finish(answer)\n}\n\nasync function poll(id: number): Promise<void> {\n try {\n follow(await api.bulkRun(id))\n } catch {\n // A missed answer is not the end of the run: ask again.\n timer = setTimeout(() => void poll(id), POLL * 2)\n }\n}\n\nfunction finish(answer: BulkRun): void {\n if (answer.total === 0) toast.warning(t('panel.bulk-nothing'))\n else if (answer.status === 'failed') toast.danger(t('panel.bulk-stopped', { count: answer.done }))\n else if (answer.failed === 0) toast.success(t('panel.bulk-done', { count: answer.done }))\n\n if (answer.failed === 0 && answer.status !== 'failed') run.value = null\n\n emit(\n 'finished',\n answer.errors.map((error) => error.id),\n )\n}\n</script>\n\n<template>\n <div class=\"wx-catalog-bulk\">\n <wx-dropdown v-if=\"actions.length > 0 && !running\" align=\"start\">\n <template #trigger>\n <wx-button size=\"sm\" variant=\"outline\" :loading=\"starting\">\n {{ t('panel.bulk-actions') }}\n <wx-icon name=\"chevron-down\" size=\"sm\" />\n </wx-button>\n </template>\n\n <wx-dropdown-item\n v-for=\"action in actions\"\n :key=\"action.key\"\n :tone=\"action.permission === 'catalog.delete' && !action.trashed ? 'danger' : 'default'\"\n @click=\"start(action)\"\n >\n {{ action.label }}\n </wx-dropdown-item>\n </wx-dropdown>\n\n <div v-if=\"running && run\" class=\"wx-catalog-bulk__progress\" role=\"status\">\n <wx-progress\n :value=\"run.done + run.failed\"\n :max=\"Math.max(1, run.total)\"\n size=\"sm\"\n :aria-label=\"run.label\"\n />\n <wx-text size=\"sm\" tone=\"muted\">\n {{ run.label }} ·\n {{ t('panel.bulk-running', { done: run.done + run.failed, total: run.total }) }}\n </wx-text>\n </div>\n\n <div v-if=\"refused\" class=\"wx-catalog-bulk__refused\">\n <wx-text size=\"sm\" weight=\"medium\">\n {{ refused.label }} · {{ t('panel.bulk-done', { count: refused.done }) }} ·\n {{ t('panel.bulk-refused', { count: refused.failed }) }}\n </wx-text>\n <details class=\"wx-catalog-bulk__errors\">\n <summary>{{ t('panel.bulk-errors') }}</summary>\n <ul>\n <li v-for=\"error in refused.errors\" :key=\"error.id\">\n <strong>{{ error.name }}</strong> — {{ error.message }}\n </li>\n </ul>\n </details>\n <wx-button size=\"sm\" variant=\"text\" @click=\"run = null\">{{\n t('panel.bulk-close')\n }}</wx-button>\n </div>\n </div>\n</template>\n\n<style scoped>\n/* No box of its own: the button, the progress and the refusals stand in the selection strip\n around it, which already wraps — a box here would push the button onto a line of its own. */\n.wx-catalog-bulk {\n display: contents;\n}\n\n.wx-catalog-bulk__progress {\n display: grid;\n gap: var(--wx-space-4);\n flex: 1 1 240px;\n min-width: 0;\n}\n\n.wx-catalog-bulk__refused {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--wx-space-8);\n flex-basis: 100%;\n min-width: 0;\n}\n\n.wx-catalog-bulk__errors {\n flex-basis: 100%;\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-catalog-bulk__errors summary {\n cursor: pointer;\n color: var(--wx-text-muted);\n}\n\n.wx-catalog-bulk__errors ul {\n margin: var(--wx-space-8) 0 0;\n padding-inline-start: var(--wx-space-18);\n max-height: 200px;\n overflow: auto;\n}\n</style>\n","import type { BadgeType } from '@webx-ui/core'\n\n/**\n * The six tones a record of a reference book is painted in (labels, stock statuses) — the\n * server's `Dictionary::TONES`. A tone and not a colour: the site puts it into a class, the panel\n * into a tag of the same tone, and nobody writes a hex anywhere.\n */\nexport const TONES = ['neutral', 'primary', 'success', 'warning', 'danger', 'info'] as const\n\nexport type Tone = (typeof TONES)[number]\n\n/** The tag the panel draws a tone as. `neutral` is the core's `default`; the rest match by name. */\nexport function toneBadge(tone: unknown): BadgeType {\n return typeof tone === 'string' &&\n tone !== 'neutral' &&\n (TONES as readonly string[]).includes(tone)\n ? (tone as BadgeType)\n : 'default'\n}\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { WxBadge, WxText } from '@webx-ui/core'\nimport { toneBadge } from './tones'\nimport type { ColumnValue } from './types'\n\n/**\n * One satellite's value in a row of the list (`ProductColumns`, §7.4) — drawn by its shape, so\n * that a satellite gets a column without this package knowing it:\n *\n * - words or a number — as they are;\n * - a record `{ name }` — its name, muted when `visible: false` (a brand taken off the site stays\n * on its products, and the list is where somebody sees that it does);\n * - a record with a tone, `{ name, color }` — a tag of that tone (a stock status);\n * - a list of those — tags side by side (labels), in the order the server gave.\n */\ndefineOptions({ name: 'WxCatalogColumnValue' })\n\nconst props = defineProps<{ value: ColumnValue | undefined }>()\n\ninterface Piece {\n key: string\n name: string\n tone: string | null\n muted: boolean\n}\n\nfunction piece(value: unknown, index: number): Piece | null {\n if (value === null || value === undefined || value === '') return null\n\n if (typeof value === 'object' && !Array.isArray(value) && 'name' in value) {\n const record = value as { id?: unknown; name?: unknown; color?: unknown; visible?: unknown }\n\n return {\n key: String(record.id ?? index),\n name: String(record.name ?? ''),\n tone: typeof record.color === 'string' ? record.color : null,\n muted: record.visible === false,\n }\n }\n\n if (typeof value === 'boolean') {\n return { key: String(index), name: value ? '✓' : '—', tone: null, muted: !value }\n }\n\n return { key: String(index), name: String(value), tone: null, muted: false }\n}\n\nconst pieces = computed<Piece[]>(() =>\n (Array.isArray(props.value) ? props.value : [props.value])\n .map((one, index) => piece(one, index))\n .filter((one): one is Piece => one !== null),\n)\n</script>\n\n<template>\n <span v-if=\"pieces.length === 0\" class=\"wx-catalog-column-value is-empty\">—</span>\n <span v-else class=\"wx-catalog-column-value\">\n <template v-for=\"one in pieces\" :key=\"one.key\">\n <wx-badge v-if=\"one.tone !== null\" :type=\"toneBadge(one.tone)\" size=\"sm\">\n {{ one.name }}\n </wx-badge>\n <wx-text v-else size=\"sm\" truncate :tone=\"one.muted ? 'muted' : undefined\">\n {{ one.name }}\n </wx-text>\n </template>\n </span>\n</template>\n\n<style scoped>\n.wx-catalog-column-value {\n display: inline-flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-4);\n min-width: 0;\n max-width: 100%;\n}\n\n.wx-catalog-column-value.is-empty {\n color: var(--wx-text-muted);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, ref, useTemplateRef } from 'vue'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport {\n useLocales,\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSpace,\n} from '@webx-ui/core'\nimport { createCatalogApi } from './api'\nimport { useCatalogMessages } from './i18n'\nimport { firstError } from './errors'\nimport type { ProductDetail } from './types'\n\n/**\n * A new product: what it is called, and nothing else. It is created unpublished and without a\n * category — the form is where there is room for those, and a product cannot be published before\n * it has a main category anyway (decision 3). The address is made of the name.\n */\nconst { open, resolve, dismiss } = useModal<ProductDetail>()\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst locales = useLocales()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n\nconst name = ref('')\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\nconst field = useTemplateRef<HTMLElement>('field')\n\nonMounted(async () => {\n await nextTick()\n field.value?.querySelector('input')?.focus()\n})\n\nasync function submit(): Promise<void> {\n if (name.value.trim() === '' || saving.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n resolve(\n await api.createProduct({\n name: { [locales.active.value]: name.value.trim() },\n is_published: false,\n }),\n )\n } catch (error) {\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-product')\" :width=\"440\">\n <wx-form-item\n :label=\"t('product.name')\"\n :error=\"firstError(errors, 'name') ?? firstError(errors, 'slug')\"\n required\n >\n <div ref=\"field\">\n <wx-input v-model=\"name\" :aria-label=\"t('product.name')\" @keyup.enter=\"submit\" />\n </div>\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=\"name.trim() === ''\" @click=\"submit\">\n {{ t('panel.create') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","import type { LocationQuery } from 'vue-router'\nimport type { FacetChoice, FacetInfo } from './types'\n\n/**\n * The list's facet filters live in the address, one parameter per facet — `f.<key>` — so that\n * coming back from a product lands on the same filter, and a filtered list can be linked to\n * (the tree's \"Show its products\" does exactly that):\n *\n * f.category=12,14 values of a terms or tree facet\n * f.price=100-500 a range; either end may be empty\n * f.in-stock=1 a toggle\n *\n * The server's own format is another (`facets[key][]=…`, see `productSearch()`); this one is for\n * a person reading the address bar.\n */\nexport const FACET_PREFIX = 'f.'\n\nexport function readFacets(query: LocationQuery, facets: FacetInfo[]): Record<string, FacetChoice> {\n const chosen: Record<string, FacetChoice> = {}\n\n for (const facet of facets) {\n const raw = query[`${FACET_PREFIX}${facet.key}`]\n const text = typeof raw === 'string' ? raw.trim() : ''\n\n if (text === '') continue\n\n if (facet.kind === 'toggle') {\n if (text === '1') chosen[facet.key] = true\n } else if (facet.kind === 'range') {\n const [min, max] = text.split('-', 2).map((end) => (end === '' ? null : Number(end)))\n const range = {\n min: Number.isFinite(min) ? min : null,\n max: Number.isFinite(max) ? max : null,\n }\n\n if (range.min !== null || range.max !== null) chosen[facet.key] = range\n } else {\n const values = text.split(',').filter((value) => value !== '')\n\n if (values.length > 0) chosen[facet.key] = values\n }\n }\n\n return chosen\n}\n\n/** One facet's choice as its parameter; `undefined` takes the parameter away. */\nexport function writeFacet(choice: FacetChoice | null | undefined): string | undefined {\n if (choice === null || choice === undefined) return undefined\n if (choice === true) return '1'\n\n if (Array.isArray(choice)) return choice.length > 0 ? choice.join(',') : undefined\n\n const min = choice.min ?? null\n const max = choice.max ?? null\n\n return min === null && max === null ? undefined : `${min ?? ''}-${max ?? ''}`\n}\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref, useTemplateRef, watch } from 'vue'\nimport { useRoute, useRouter, type LocationQueryRaw } from 'vue-router'\nimport {\n rowMenuWidth,\n useAdmin,\n useErrorText,\n useTranslate,\n WxDate,\n WxFilterChips,\n WxListScreen,\n WxRowMenu,\n type AppliedFilter,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n toast,\n useElementWidth,\n WxBadge,\n WxButton,\n WxEntityCard,\n WxFormItem,\n WxIcon,\n WxInputNumber,\n WxSelect,\n WxSwitch,\n WxTable,\n WxText,\n WxTooltip,\n WxTreeSelect,\n type RowKey,\n type SelectOption,\n type TabItem,\n type TableColumn,\n type TableState,\n type TabValue,\n type TreeSelectValue,\n} from '@webx-ui/core'\nimport BulkBar from './BulkBar.vue'\nimport ColumnValue from './ColumnValue.vue'\nimport ProductCreateDialog from './ProductCreateDialog.vue'\nimport { createCatalogApi } from './api'\nimport { FACET_PREFIX, readFacets, writeFacet } from './filters'\nimport { useCatalogMessages } from './i18n'\nimport { flatten, useCategoryTree, useFacetRegistry } from './store'\nimport type {\n BulkSelection,\n FacetChoice,\n FacetInfo,\n ProductDetail,\n ProductQuery,\n ProductRow,\n ProductSort,\n SortInfo,\n ProductsPage,\n} from './types'\n\n/**\n * The products of the catalogue (§11.1): a page of rows, the views of decision 3 over it, the\n * facets as filters behind the funnel, and a checkbox per row.\n *\n * The checkboxes are for the bulk actions (§11.4): a selection outlives the page it was made on,\n * which is what \"select these twelve across three pages\" needs, and \"everything found\" picks the\n * filter itself rather than its ids. The actions and their progress are `BulkBar`'s.\n *\n * What the list shows beyond the core's columns is the satellites' (`ProductColumns`, §7.4): the\n * server says which, and each row carries its values under their keys.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/catalog' })\n\n/** Where the row stops being a row. Below it the table draws cards and this screen draws one. */\nconst CARDS = 640\n\nconst context = useAdmin()\nconst api = createCatalogApi(context)\nconst tree = useCategoryTree(context)\nconst registry = useFacetRegistry(context)\nconst route = useRoute()\nconst router = useRouter()\nuseCatalogMessages()\n\nconst t = useTranslate('webx-catalog')\n/* The words a list needs as soon as it has filters belong to the panel, not to the catalogue. */\nconst admin = useTranslate('webx-admin')\nconst message = useErrorText()\n\nconst root = useTemplateRef<HTMLElement>('root')\nconst width = useElementWidth(root)\n\nconst page = ref<ProductsPage | null>(null)\nconst loading = ref(true)\nconst selected = ref<RowKey[]>([])\n/** The whole of what the filter finds is picked, not only the ticked rows. */\nconst everything = ref(false)\n/* The table's own search and page, started from the address: coming back lands where it left. */\nconst search = ref(typeof route.query.q === 'string' ? route.query.q : '')\nconst pageNumber = ref(Number(route.query.page ?? 1) || 1)\n/* The page size the table last asked for: a reload after a filter has to page the same way. */\nlet perPage: number | undefined\n\nconst create = createModal<ProductDetail, Record<string, never>>(ProductCreateDialog)\n\nconst canManage = computed(() => context.can('catalog.manage'))\nconst canDelete = computed(() => context.can('catalog.delete'))\n\nconst title = computed(() => t('module.products'))\n\n/** Everything the list is looking at lives in the address — coming back lands in the same place. */\nconst view = computed<TabValue>({\n get: () => (typeof route.query.view === 'string' ? route.query.view : ''),\n set: (value) => {\n void router.replace({\n query: { ...route.query, view: value === '' ? undefined : String(value), page: undefined },\n })\n },\n})\n\n/**\n * The orders are the registry's (§7.2): a satellite adds its own, and a site without prices has\n * none by price — a sort the server does not know is a 422, so nothing is offered or asked for\n * that it did not list. Until it answers, the default order is the one there is.\n */\nconst sorts = computed<SortInfo[]>(() =>\n registry.sorts.value.length > 0\n ? registry.sorts.value\n : [{ key: 'default', label: t('panel.sort-default') }],\n)\n\nconst sort = computed<ProductSort>(() => {\n const asked = route.query.sort\n\n return typeof asked === 'string' && registry.sorts.value.some((one) => one.key === asked)\n ? asked\n : 'default'\n})\n\nconst facets = computed<FacetInfo[]>(() => registry.facets.value ?? [])\nconst chosen = computed(() => readFacets(route.query, facets.value))\n\n/** The counter of decision 3 rides on its own tab, where somebody looking for those products looks. */\nconst views = computed<TabItem[]>(() => {\n const missing = page.value?.counts.no_category ?? 0\n\n return [\n { value: '', label: t('panel.view-all') },\n { value: 'published', label: t('panel.view-published') },\n { value: 'unpublished', label: t('panel.view-unpublished') },\n {\n value: 'no-category',\n label: t('panel.view-no-category'),\n badge: missing > 0 ? missing : undefined,\n },\n ]\n})\n\n/** Absent from every row when the site switched prices off — then the column is not drawn. */\nconst priced = computed(() => (page.value?.data ?? []).some((row) => 'price' in row))\nconst extra = computed(() => page.value?.columns ?? [])\n\nconst asCards = computed(() => width.value > 0 && width.value < CARDS)\n\nconst categoryNames = computed(\n () => new Map(flatten(tree.nodes.value ?? []).map((node) => [String(node.id), node.name])),\n)\n\n/**\n * Every column but the name carries a width and the table is laid out `fixed`, so the ones that\n * drop below a breakpoint give their room to the name. They go in the order they can be spared:\n * when it changed, then the satellites', then the category — what is left is the picture, the\n * name and the state, which is what identifies a product and what somebody is looking for.\n */\nconst EXTRA = 140\nconst CATEGORY_FROM = 860\n\n/*\n * The width each satellite's column is drawn from on: one after another after the category's, so\n * a third satellite drops first rather than taking its room out of the name — the table is laid out\n * `fixed`, and there the name's `minWidth` gives way to the widths around it (three satellites at\n * 1080px left it 59px). «Updated» goes before any of them.\n */\nconst extraFrom = (index: number) => CATEGORY_FROM + EXTRA * (index + 1)\n\nconst columns = computed<TableColumn<ProductRow>[]>(() => {\n if (asCards.value) {\n // The card is the row: a tap opens the product, and a line of its own for the `···` would make\n // every card half as tall again for a menu whose first item is that same tap.\n return [{ key: 'card', label: '' }]\n }\n\n return [\n { key: 'image', label: '', width: 64 },\n { key: 'name', label: t('panel.column-product'), minWidth: 200 },\n { key: 'category', label: t('product.category'), width: 180, hideBelow: CATEGORY_FROM },\n ...(priced.value\n ? [{ key: 'price', label: t('product.price'), width: 150, align: 'right' as const }]\n : []),\n ...extra.value.map((column, index) => ({\n key: `x.${column.key}`,\n label: column.label,\n width: EXTRA,\n hideBelow: extraFrom(index),\n })),\n { key: 'state', label: t('panel.column-state'), width: 150, hideBelow: 720 },\n {\n key: 'updated_at',\n label: t('panel.column-updated'),\n width: 130,\n hideBelow: Math.max(1180, extraFrom(extra.value.length - 1) + 130),\n },\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right' },\n ]\n})\n\nfunction query(state?: TableState): ProductQuery {\n return {\n q: state?.search ?? search.value,\n state: view.value as ProductQuery['state'],\n sort: sort.value,\n page: state?.page ?? pageNumber.value,\n per_page: state?.perPage ?? perPage,\n facets: chosen.value,\n }\n}\n\nlet asked = 0\n\nasync function load(state?: TableState): Promise<void> {\n const mine = ++asked\n\n loading.value = true\n\n try {\n const answer = await api.products(query(state))\n\n // A slower answer to an older question must not land over a newer one.\n if (mine === asked) page.value = answer\n } catch (error) {\n toast.danger(message(error))\n } finally {\n if (mine === asked) loading.value = false\n }\n}\n\nfunction onState(state: TableState): void {\n perPage = state.perPage\n\n const wanted: LocationQueryRaw = {\n ...route.query,\n q: state.search === '' ? undefined : state.search,\n page: state.page === 1 ? undefined : String(state.page),\n }\n\n void router.replace({ query: wanted })\n void load(state)\n}\n\nfunction narrow(key: string, choice: FacetChoice | null): void {\n void router.replace({\n query: { ...route.query, [`${FACET_PREFIX}${key}`]: writeFacet(choice), page: undefined },\n })\n}\n\nfunction order(value: unknown): void {\n void router.replace({\n query: {\n ...route.query,\n sort: typeof value === 'string' && value !== 'default' ? value : undefined,\n page: undefined,\n },\n })\n}\n\nfunction clearFilters(): void {\n const rest = Object.fromEntries(\n Object.entries(route.query).filter(([name]) => !name.startsWith(FACET_PREFIX)),\n )\n\n void router.replace({ query: { ...rest, page: undefined } })\n}\n\n/**\n * What a terms facet can be narrowed to: the values the list counted for it, with how many\n * products each would leave. The engine counts a facet without its own choice, so picking one\n * value does not take the others away; a chosen value that counts nothing any more stays offered,\n * or it could not be taken off.\n */\nfunction optionsOf(key: string): SelectOption[] {\n const counted = page.value?.facets?.[key]?.values ?? []\n const options: SelectOption[] = counted.map((one) => ({\n value: one.value,\n label: `${one.label} (${one.count})`,\n }))\n\n for (const value of valuesOf(key)) {\n if (!counted.some((one) => one.value === value)) options.push({ value, label: value })\n }\n\n return options\n}\n\n/** Values of a terms facet, or of the tree, for a chip: \"Category: Laptops, Tablets\". */\nfunction valueLabel(facet: FacetInfo, value: string | number): string {\n if (facet.kind === 'tree') return categoryNames.value.get(String(value)) ?? `#${value}`\n\n return (\n page.value?.facets?.[facet.key]?.values?.find((one) => one.value === String(value))?.label ??\n String(value)\n )\n}\n\n/** The bounds a range facet has in the list as it stands, said in its fields before anything is typed. */\nfunction boundOf(key: string, end: 'min' | 'max'): string {\n const bound = page.value?.facets?.[key]?.[end]\n\n return typeof bound === 'number' ? money.value.format(bound) : ''\n}\n\nconst applied = computed<AppliedFilter[]>(() =>\n facets.value.flatMap((facet) => {\n const choice = chosen.value[facet.key]\n\n if (choice === undefined) return []\n\n let said: string\n\n if (choice === true) said = facet.label\n else if (Array.isArray(choice)) {\n said = `${facet.label}: ${choice.map((value) => valueLabel(facet, value)).join(', ')}`\n } else {\n said = `${facet.label}: ${choice.min ?? '…'} – ${choice.max ?? '…'}`\n }\n\n return [{ key: facet.key, label: said, clear: () => narrow(facet.key, null) }]\n }),\n)\n\nconst emptyText = computed(() => {\n if (search.value !== '') return t('panel.empty-search')\n if (applied.value.length > 0 || view.value !== '') return t('panel.empty-search')\n\n return t('panel.empty-products')\n})\n\nfunction rangeOf(key: string): { min?: number | null; max?: number | null } {\n const choice = chosen.value[key]\n\n return choice && !Array.isArray(choice) && choice !== true ? choice : {}\n}\n\nfunction valuesOf(key: string): string[] {\n const choice = chosen.value[key]\n\n return Array.isArray(choice) ? choice.map(String) : []\n}\n\nfunction treeChoice(key: string): TreeSelectValue {\n return valuesOf(key).map(Number)\n}\n\n/** How many the list finds as it is filtered — what \"select everything found\" would pick. */\nconst found = computed(() => page.value?.total ?? 0)\n\n/**\n * \"Everything found\" is the list's query, not its ids (§11.4): the server turns it into ids when\n * the run starts, so forty thousand products never travel through the browser.\n */\nconst bulkSelection = computed<BulkSelection>(() => {\n if (!everything.value) return { ids: selected.value.map(Number) }\n\n const facetsQuery: Record<\n string,\n Array<string | number> | { min?: number | null; max?: number | null }\n > = {}\n\n for (const [key, choice] of Object.entries(chosen.value)) {\n // A toggle is sent the way the list sends it: a list with `1` in it.\n facetsQuery[key] = choice === true ? ['1'] : choice\n }\n\n return { query: { q: search.value, state: String(view.value), facets: facetsQuery } }\n})\n\nfunction clearSelection(): void {\n selected.value = []\n everything.value = false\n}\n\n/**\n * What refused stays picked, and nothing else: the strip keeps its list of refusals beside the\n * very products it names, ready for another go once they are put right.\n */\nfunction onBulkFinished(refused: number[]): void {\n everything.value = false\n selected.value = refused\n void load()\n}\n\n/* One source per value, not one getter returning an array: a page turn must not fire a second\n request racing the one `onState` already sent. */\nwatch([view, sort, () => JSON.stringify(chosen.value), search], () => {\n // \"Everything found\" was everything this filter found; another filter finds something else.\n everything.value = false\n})\n\nwatch([view, sort, () => JSON.stringify(chosen.value)], () => {\n // A new filter is a new list: the page it was on belongs to the list it was on. If that moves\n // the table's page, the table asks for itself; otherwise nobody would.\n if (pageNumber.value !== 1) pageNumber.value = 1\n else void load()\n})\n\nonMounted(() => {\n void registry.load().catch(() => undefined)\n void tree.load().catch(() => undefined)\n})\n\nfunction open(product: ProductRow): void {\n void router.push({ path: `${props.base}/products/${product.id}` })\n}\n\nasync function add(): Promise<void> {\n const detail = await create({})\n\n if (!detail) return\n\n open(detail.product)\n}\n\nasync function publish(product: ProductRow, on: boolean): Promise<void> {\n try {\n await api.saveProduct(product.id, { is_published: on })\n toast.success(on ? t('panel.product-published') : t('panel.product-unpublished'))\n await load()\n } catch (error) {\n // Publishing without a main category is refused (decision 3), and the server says so.\n toast.danger(message(error))\n }\n}\n\nasync function remove(product: ProductRow): Promise<void> {\n const agreed = await confirm({\n title: t('panel.delete-product-title', { name: product.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(product.id)\n selected.value = selected.value.filter((key) => key !== product.id)\n toast.success(t('panel.product-deleted'))\n await load()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction actionsFor(product: ProductRow): RowAction[] {\n const actions: RowAction[] = [\n { key: 'open', icon: 'edit', label: t('panel.open'), run: () => open(product) },\n ]\n\n if (product.url) {\n actions.push({ key: 'site', icon: 'link', label: t('panel.open-on-site'), href: product.url })\n }\n\n if (canManage.value) {\n actions.push(\n product.is_published\n ? {\n key: 'unpublish',\n icon: 'eye-off',\n label: t('panel.unpublish'),\n run: () => void publish(product, false),\n }\n : {\n key: 'publish',\n icon: 'eye',\n label: t('panel.publish'),\n run: () => void publish(product, true),\n },\n )\n }\n\n if (canDelete.value) {\n actions.push({\n key: 'delete',\n icon: 'trash',\n label: t('panel.delete'),\n danger: true,\n run: () => void remove(product),\n })\n }\n\n return actions\n}\n\nfunction stateType(product: ProductRow): 'success' | 'warning' | 'default' {\n if (!product.is_published) return 'default'\n\n return product.visible ? 'success' : 'warning'\n}\n\nconst money = computed(\n () =>\n new Intl.NumberFormat(context.i18n.state.locale, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }),\n)\n\nfunction price(value: number | null | undefined): string {\n return value === null || value === undefined ? '—' : money.value.format(value)\n}\n\nfunction unit(product: ProductRow): string {\n return product.unit ? ` / ${t(`units.${product.unit}`)}` : ''\n}\n\nfunction hasValue(value: unknown): boolean {\n return Array.isArray(value)\n ? value.length > 0\n : value !== null && value !== undefined && value !== ''\n}\n\nconst actions = computed<ScreenAction[]>(() => {\n const leads: ScreenAction[] = []\n\n if (canManage.value) {\n leads.push({\n key: 'new',\n label: t('panel.new-product'),\n icon: 'plus',\n primary: true,\n run: () => void add(),\n })\n }\n\n if (canDelete.value) {\n leads.push({\n key: 'deleted',\n label: t('module.deleted'),\n icon: 'trash',\n menu: true,\n run: () => void router.push(`${props.base}/deleted`),\n })\n }\n\n return leads\n})\n</script>\n\n<template>\n <div ref=\"root\" class=\"wx-catalog-products\">\n <wx-list-screen v-model:view=\"view\" :title=\"title\" :views=\"views\" :actions=\"actions\">\n <!-- What is picked, said above the rows it was picked from, with what can be done to it. -->\n <div v-if=\"selected.length > 0 || everything\" class=\"wx-catalog-products__selection\">\n <wx-text size=\"sm\" weight=\"medium\">\n {{\n everything\n ? t('panel.selected-all', { count: found })\n : t('panel.selected', { count: selected.length })\n }}\n </wx-text>\n <wx-button\n v-if=\"!everything && found > selected.length\"\n size=\"sm\"\n variant=\"text\"\n @click=\"everything = true\"\n >\n {{ t('panel.select-all-found', { count: found }) }}\n </wx-button>\n <wx-button size=\"sm\" variant=\"text\" @click=\"clearSelection\">\n {{ t('panel.select-clear') }}\n </wx-button>\n <bulk-bar\n v-if=\"canManage || canDelete\"\n :count=\"everything ? found : selected.length\"\n :selection=\"bulkSelection\"\n @finished=\"onBulkFinished\"\n />\n </div>\n\n <wx-table\n v-model:selected=\"selected\"\n v-model:search=\"search\"\n v-model:page=\"pageNumber\"\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n selectable\n clickable\n hover\n flush\n :loading=\"loading\"\n layout=\"fixed\"\n :cards-below=\"CARDS\"\n :filters-count=\"applied.length\"\n :filters-label=\"admin('filters.title')\"\n :search-placeholder=\"t('panel.search-products')\"\n :empty-text=\"emptyText\"\n :aria-label=\"title\"\n @row-click=\"open\"\n @state-change=\"onState\"\n >\n <template #filters>\n <wx-form-item :label=\"t('panel.sort')\">\n <wx-select\n :model-value=\"sort\"\n :options=\"sorts.map((one) => ({ value: one.key, label: one.label }))\"\n size=\"sm\"\n @update:model-value=\"order\"\n />\n </wx-form-item>\n\n <wx-form-item v-for=\"facet in facets\" :key=\"facet.key\" :label=\"facet.label\">\n <wx-tree-select\n v-if=\"facet.kind === 'tree'\"\n :model-value=\"treeChoice(facet.key)\"\n :nodes=\"tree.nodes.value ?? []\"\n node-key=\"id\"\n label-key=\"name\"\n children-key=\"children\"\n multiple\n check-strictly\n filterable\n clearable\n size=\"sm\"\n :placeholder=\"t('panel.any')\"\n @update:model-value=\"\n (value: TreeSelectValue) =>\n narrow(facet.key, Array.isArray(value) ? value.map(String) : [])\n \"\n />\n\n <div v-else-if=\"facet.kind === 'range'\" class=\"wx-catalog-products__range\">\n <wx-input-number\n :model-value=\"rangeOf(facet.key).min ?? null\"\n :min=\"0\"\n :controls=\"false\"\n size=\"sm\"\n :placeholder=\"boundOf(facet.key, 'min') || t('panel.range-from')\"\n :aria-label=\"`${facet.label}: ${t('panel.range-from')}`\"\n @update:model-value=\"\n (value: number | null | undefined) =>\n narrow(facet.key, { ...rangeOf(facet.key), min: value ?? null })\n \"\n />\n <wx-input-number\n :model-value=\"rangeOf(facet.key).max ?? null\"\n :min=\"0\"\n :controls=\"false\"\n size=\"sm\"\n :placeholder=\"boundOf(facet.key, 'max') || t('panel.range-to')\"\n :aria-label=\"`${facet.label}: ${t('panel.range-to')}`\"\n @update:model-value=\"\n (value: number | null | undefined) =>\n narrow(facet.key, { ...rangeOf(facet.key), max: value ?? null })\n \"\n />\n </div>\n\n <wx-switch\n v-else-if=\"facet.kind === 'toggle'\"\n :model-value=\"chosen[facet.key] === true\"\n size=\"sm\"\n @update:model-value=\"(on) => narrow(facet.key, on ? true : null)\"\n />\n\n <wx-select\n v-else\n :model-value=\"valuesOf(facet.key)\"\n :options=\"optionsOf(facet.key)\"\n multiple\n filterable\n clearable\n size=\"sm\"\n :placeholder=\"t('panel.any')\"\n @update:model-value=\"\n (value: unknown) => narrow(facet.key, Array.isArray(value) ? value.map(String) : [])\n \"\n />\n </wx-form-item>\n\n <wx-button v-if=\"applied.length > 0\" variant=\"text\" size=\"sm\" block @click=\"clearFilters\">\n <template #icon><wx-icon name=\"close\" /></template>\n {{ admin('filters.reset') }}\n </wx-button>\n </template>\n\n <template #applied>\n <wx-filter-chips :filters=\"applied\" />\n </template>\n\n <template #cell-image=\"{ row }\">\n <span class=\"wx-catalog-products__image\" :class=\"{ 'is-empty': !row.image }\">\n <img v-if=\"row.image\" :src=\"row.image.thumb ?? row.image.url\" alt=\"\" loading=\"lazy\" />\n <wx-icon v-else name=\"image\" size=\"sm\" />\n </span>\n </template>\n\n <template #cell-name=\"{ row }\">\n <div class=\"wx-catalog-products__name\">\n <span class=\"wx-catalog-products__title\">{{ row.name }}</span>\n <span class=\"wx-catalog-products__sku\">{{ row.sku ?? '' }}</span>\n </div>\n </template>\n\n <template #cell-category=\"{ row }\">\n <wx-badge v-if=\"!row.category\" type=\"warning\" size=\"sm\" round>\n {{ t('product.no-category') }}\n </wx-badge>\n <wx-text v-else size=\"sm\" truncate :tone=\"row.category.deleted ? 'muted' : undefined\">\n {{ row.category.name }}\n </wx-text>\n </template>\n\n <template #cell-price=\"{ row }\">\n <span class=\"wx-catalog-products__price\">\n <span\n >{{ price(row.price) }}<small v-if=\"row.price != null\">{{ unit(row) }}</small></span\n >\n <s v-if=\"row.old_price != null\">{{ price(row.old_price) }}</s>\n </span>\n </template>\n\n <template v-for=\"column in extra\" :key=\"column.key\" #[`cell-x.${column.key}`]=\"{ row }\">\n <column-value :value=\"row.columns?.[column.key]\" />\n </template>\n\n <template #cell-state=\"{ row }\">\n <wx-tooltip v-if=\"row.is_published && !row.visible\" :content=\"t('panel.invisible')\">\n <wx-badge :type=\"stateType(row)\" dot size=\"sm\">{{ t(`states.${row.state}`) }}</wx-badge>\n </wx-tooltip>\n <wx-badge v-else :type=\"stateType(row)\" dot size=\"sm\">\n {{ t(`states.${row.state}`) }}\n </wx-badge>\n </template>\n\n <template #cell-updated_at=\"{ row }\">\n <wx-date :value=\"row.updated_at\" compact />\n </template>\n\n <template #cell-card=\"{ row }\">\n <wx-entity-card\n variant=\"plain\"\n :title=\"row.name\"\n :image=\"row.image?.thumb ?? row.image?.url ?? undefined\"\n image-size=\"56px\"\n :title-lines=\"2\"\n :subtitle=\"row.sku ?? undefined\"\n >\n <template #meta>\n <wx-badge :type=\"stateType(row)\" dot size=\"sm\">{{\n t(`states.${row.state}`)\n }}</wx-badge>\n <wx-badge v-if=\"!row.category\" type=\"warning\" size=\"sm\" round>\n {{ t('product.no-category') }}\n </wx-badge>\n <wx-text v-if=\"priced && row.price != null\" size=\"sm\">\n {{ price(row.price) }}{{ unit(row) }}\n </wx-text>\n <!-- The satellites' values that are there; an empty one is not worth a dash on a card. -->\n <template v-for=\"column in extra\" :key=\"column.key\">\n <column-value\n v-if=\"hasValue(row.columns?.[column.key])\"\n :value=\"row.columns?.[column.key]\"\n />\n </template>\n </template>\n </wx-entity-card>\n </template>\n\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-products {\n min-width: 0;\n}\n\n.wx-catalog-products__selection {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-12);\n padding: var(--wx-space-8) var(--wx-space-16);\n border-bottom: 1px solid var(--wx-border-default);\n background: var(--wx-bg-subtle);\n}\n\n.wx-catalog-products__image {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: var(--wx-radius-sm);\n overflow: clip;\n background: var(--wx-bg-subtle);\n color: var(--wx-text-muted);\n vertical-align: middle;\n}\n\n.wx-catalog-products__image img {\n width: 100%;\n height: 100%;\n min-width: 0;\n object-fit: cover;\n}\n\n.wx-catalog-products__name {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.wx-catalog-products__title {\n font-weight: var(--wx-font-weight-medium);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-catalog-products__sku {\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-sm);\n color: var(--wx-text-muted);\n min-height: 1em;\n}\n\n.wx-catalog-products__price {\n display: inline-flex;\n flex-direction: column;\n align-items: flex-end;\n font-variant-numeric: tabular-nums;\n}\n\n/* A price and its unit are one reading: '99 990 / pcs' broken over two lines reads as two facts. */\n.wx-catalog-products__price > * {\n white-space: nowrap;\n}\n\n.wx-catalog-products__price small,\n.wx-catalog-products__price s {\n font-size: var(--wx-font-size-sm);\n color: var(--wx-text-muted);\n}\n\n.wx-catalog-products__range {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--wx-space-8);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue'\nimport { localizedValue, useLocales, WxBadge, type LocalizedValue } from '@webx-ui/core'\nimport { useCategoryEditor } from '@webx-ui/module-admin'\nimport { TONES, toneBadge, type Tone } from './tones'\n\n/**\n * The tone of a label or a stock status (`wx-catalog-tone`): six tags, one per tone, each in its\n * own — picking one is picking what it looks like — and beside them the record itself as the list\n * of products will show it, with the name that is being typed above.\n *\n * The words of the tones are the screen's (`props.options`, one per tone): the server says them\n * in the panel's language, and a tone without a word is shown by its key.\n */\ndefineOptions({ name: 'WxCatalogToneField' })\n\nconst props = withDefaults(\n defineProps<{\n options?: Array<{ label: string; value: string }>\n disabled?: boolean\n ariaLabel?: string\n }>(),\n { options: () => [], disabled: false, ariaLabel: undefined },\n)\n\nconst value = defineModel<string | null>({ default: null })\n\nconst locales = useLocales()\nconst editor = useCategoryEditor()\n\nconst root = useTemplateRef<HTMLElement>('root')\n\n/*\n * The group is named by the label of the form item it stands in. The core does not hand a field\n * built outside it the item's ids (`useFormField` is its own), and a group of radios cannot be the\n * target of a label's `for` — so the label is found where it is drawn.\n */\nconst labelledBy = ref<string | undefined>()\n\nonMounted(() => {\n labelledBy.value =\n root.value?.closest('.wx-form-item')?.querySelector('.wx-form-item__label')?.id || undefined\n})\n\nconst chosen = computed<Tone>(() =>\n (TONES as readonly string[]).includes(value.value ?? '') ? (value.value as Tone) : 'neutral',\n)\n\nconst words = computed(() => new Map(props.options.map((one) => [one.value, one.label])))\n\n/** The record's name as typed, in the language the form is showing; the tone's word until then. */\nconst sample = computed(() => {\n const title = editor?.values.value.title as LocalizedValue | string | null | undefined\n\n return localizedValue(title, locales.active.value, '') || words.value.get(chosen.value) || ''\n})\n\nfunction pick(tone: Tone): void {\n if (!props.disabled) value.value = tone\n}\n\n/* Arrows move the choice and the focus with it, as in any group of radio buttons. */\nfunction step(delta: number): void {\n const at = TONES.indexOf(chosen.value)\n\n pick(TONES[(at + delta + TONES.length) % TONES.length]!)\n void nextTick(() => root.value?.querySelector<HTMLElement>('[aria-checked=\"true\"]')?.focus())\n}\n</script>\n\n<template>\n <div ref=\"root\" class=\"wx-catalog-tone\">\n <div\n class=\"wx-catalog-tone__choices\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"ariaLabel ? undefined : labelledBy\"\n :aria-disabled=\"disabled || undefined\"\n >\n <button\n v-for=\"tone in TONES\"\n :key=\"tone\"\n type=\"button\"\n role=\"radio\"\n class=\"wx-catalog-tone__choice\"\n :class=\"{ 'is-chosen': tone === chosen }\"\n :aria-checked=\"tone === chosen\"\n :tabindex=\"tone === chosen ? 0 : -1\"\n :disabled=\"disabled\"\n :data-tone=\"tone\"\n @click=\"pick(tone)\"\n @keydown.right.prevent=\"step(1)\"\n @keydown.down.prevent=\"step(1)\"\n @keydown.left.prevent=\"step(-1)\"\n @keydown.up.prevent=\"step(-1)\"\n >\n <wx-badge :type=\"toneBadge(tone)\" :variant=\"tone === chosen ? 'solid' : 'soft'\" round>\n {{ words.get(tone) ?? tone }}\n </wx-badge>\n </button>\n </div>\n\n <span v-if=\"sample\" class=\"wx-catalog-tone__sample\" aria-hidden=\"true\">\n <wx-badge :type=\"toneBadge(chosen)\" size=\"sm\">{{ sample }}</wx-badge>\n </span>\n </div>\n</template>\n\n<style scoped>\n.wx-catalog-tone {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-12);\n}\n\n.wx-catalog-tone__choices {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-6);\n}\n\n.wx-catalog-tone__choice {\n display: inline-flex;\n padding: 0;\n border: 0;\n border-radius: var(--wx-radius-full);\n background: none;\n font: inherit;\n cursor: pointer;\n}\n\n.wx-catalog-tone__choice:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.wx-catalog-tone__choice:focus-visible {\n outline: 2px solid var(--wx-border-focus);\n outline-offset: 2px;\n}\n\n/* The record as the list will show it, set apart from the choices by a rule rather than a label. */\n.wx-catalog-tone__sample {\n display: inline-flex;\n padding-left: var(--wx-space-12);\n border-left: 1px solid var(--wx-border-default);\n}\n</style>\n","import type { AdminModule } from '@webx-ui/module-admin'\nimport CategoriesPage from './CategoriesPage.vue'\nimport CategoryEditorPage from './CategoryEditorPage.vue'\nimport CategoryField from './CategoryField.vue'\nimport DeletedPage from './DeletedPage.vue'\nimport FacetsField from './FacetsField.vue'\nimport GalleryField from './GalleryField.vue'\nimport ProductEditorPage from './ProductEditorPage.vue'\nimport ProductsPage from './ProductsPage.vue'\nimport ToneField from './ToneField.vue'\n\nexport interface CatalogOptions {\n /**\n * Where the catalogue lives inside the panel. The server's refusal of a taken article number\n * links to `{path}/products/{id}` under the panel's own path, so a panel that moves it has to\n * move the server's idea of it too — leave it unless there is a reason.\n */\n path?: string\n}\n\n/**\n * The catalogue as sections of the panel (§11): the products with «Deleted», and their categories\n * as a tree.\n *\n * Two modules, because the navigation is one entry per module and the tree is opened often enough\n * to want its own — the server registers `catalog` and `catalog-categories` in the `catalog`\n * group, beside the satellites (brands, stock, labels). «Deleted» is reached from the head of the\n * products: it holds categories too, but it is visited rarely, and by whoever may delete.\n *\n * The node types are what only this module can draw: a category picker that knows the tree, the\n * «Filters» tab of a category (§6.2), the gallery, and the tone of a reference book's record —\n * the satellites' screens use it, and the six tones are the core's (`Dictionary`).\n */\nexport function catalog(options: CatalogOptions = {}): AdminModule[] {\n const path = options.path ?? '/catalog'\n const props = { base: path }\n\n const products: AdminModule = {\n id: 'catalog',\n path: `${path}/products`,\n routes: [\n { path, redirect: `${path}/products` },\n { path: `${path}/products`, name: 'webx.catalog.products', component: ProductsPage, props },\n {\n path: `${path}/products/:id(\\\\d+)`,\n name: 'webx.catalog.products.edit',\n component: ProductEditorPage,\n props,\n },\n { path: `${path}/deleted`, name: 'webx.catalog.deleted', component: DeletedPage, props },\n ],\n types: {\n 'wx-catalog-category': { component: CategoryField, kind: 'field' },\n // Wide: a row of the list carries a name, a switch and a grip, and half a line is too narrow\n // to read which filter is which.\n 'wx-catalog-facets': { component: FacetsField, kind: 'field', wide: true },\n 'wx-catalog-gallery': { component: GalleryField, kind: 'display' },\n // The tone of a label or a stock status: here, because both satellites paint in the same six.\n 'wx-catalog-tone': { component: ToneField, kind: 'field' },\n },\n }\n\n const categories: AdminModule = {\n id: 'catalog-categories',\n path: `${path}/categories`,\n routes: [\n {\n path: `${path}/categories`,\n name: 'webx.catalog.categories',\n component: CategoriesPage,\n props,\n },\n {\n path: `${path}/categories/:id(\\\\d+)`,\n name: 'webx.catalog.categories.edit',\n component: CategoryEditorPage,\n props,\n },\n ],\n }\n\n return [products, categories]\n}\n","import { defineComponent, h, type Component } from 'vue'\nimport type { RouteRecordRaw } from 'vue-router'\nimport {\n categoryRoutes,\n useAdmin,\n type AdminModule,\n type CategoriesOptions,\n type CategoryWord,\n type Messages,\n} from '@webx-ui/module-admin'\nimport { FACET_PREFIX } from './filters'\n\n/**\n * A reference book of the catalogue — labels, stock statuses, brands — as the satellite that owns\n * it describes it (§4 of the dictionaries spec). Everything else is the same for each.\n */\nexport interface DictionaryOptions {\n /** The module the server registers: `catalog-labels`. */\n id: string\n /** Its path under the catalogue's, and under the catalogue's API: `labels`. */\n slug: string\n /** The described screen of one record: `catalog.label-form`. */\n screen: string\n /** The facet of the products list that picks a record's products: `label`. */\n facet: string\n /** The satellite's dictionary: `webx-catalog-labels`. */\n namespace: string\n /** The group of it the list's words are in: `label`. */\n group: string\n /** Its English, under the server's until the server's arrives. */\n messages: Record<string, Messages>\n /** Where the catalogue lives in the panel — `catalog({ path })`'s. */\n catalog?: string\n}\n\n/** The words of the shared screens a reference book says in its own way; the rest are the panel's. */\nconst OWN: CategoryWord[] = [\n 'new',\n 'empty',\n 'empty-help',\n 'order',\n 'hidden',\n 'count',\n 'show-items',\n 'delete-blocked',\n 'delete-text',\n 'deleted',\n 'saved',\n]\n\n/**\n * The panel's shared category screens for one reference book: the list with its order dragged\n * and the number of products on each row, and the page of one record, which is the satellite's\n * described screen. Rights are the catalogue's (decision 8), and \"Show its products\" is the list\n * of products narrowed by the satellite's own facet.\n */\nexport function dictionaryOptions(options: DictionaryOptions): CategoriesOptions {\n const catalog = options.catalog ?? '/catalog'\n\n return {\n api: `catalog/${options.slug}`,\n path: `${catalog}/${options.slug}`,\n name: `webx.${options.id}`,\n module: options.id,\n screen: options.screen,\n manage: 'catalog.manage',\n count: 'products_count',\n items: (id) => ({\n path: `${catalog}/products`,\n query: { [`${FACET_PREFIX}${options.facet}`]: String(id) },\n }),\n words: Object.fromEntries(\n OWN.map((word) => [word, `${options.namespace}::${options.group}.${word}`]),\n ),\n }\n}\n\n/** A reference book as a section of the panel: one entry, two routes. */\nexport function dictionarySection(options: DictionaryOptions): AdminModule {\n const described = dictionaryOptions(options)\n const seed = () => seedMessages(options.namespace, options.messages)\n\n return {\n id: options.id,\n path: described.path,\n routes: categoryRoutes(described).map((route) => seeded(route, seed)),\n }\n}\n\nconst seededIn = new WeakMap<object, Set<string>>()\n\n/**\n * The satellite's English under the panel's dictionary, once per panel. The screens are the\n * panel's, so nothing of the satellite's runs before them to do it — the route does.\n */\nfunction seedMessages(namespace: string, messages: Record<string, Messages>): void {\n try {\n const { i18n } = useAdmin()\n const done = seededIn.get(i18n) ?? new Set<string>()\n\n if (!done.has(namespace)) {\n i18n.defaults(namespace, messages)\n done.add(namespace)\n seededIn.set(i18n, done)\n }\n } catch {\n // Outside a panel there is no dictionary to seed, and `useTranslate` falls back on its own.\n }\n}\n\nfunction seeded(route: RouteRecordRaw, seed: () => void): RouteRecordRaw {\n const page = (route as { component?: Component }).component\n\n if (page === undefined) return route\n\n return {\n ...route,\n component: defineComponent({\n name: 'WxCatalogDictionaryView',\n setup() {\n seed()\n\n return () => h(page)\n },\n }),\n } as RouteRecordRaw\n}\n"],"names":["CATALOG_API","productSearch","query","search","key","choice","value","upload","admin","url","file","onProgress","body","resolve","reject","request","token","readCookie","event","answer","createCatalogApi","base","data","withQuery","path","id","values","source","images","image","alt","title","parent","before","action","params","selection","kind","firstError","errors","field","own","name","catalogMessages","seeded","useCatalogMessages","i18n","useAdmin","stores","storeOf","store","ref","fetchInto","shelf","fetch","force","pending","useCategoryTree","api","nodes","useFacetRegistry","computed","facet","flatten","node","ancestry","byId","chain","current","props","__props","open","dismiss","useModal","context","tree","locales","useLocales","t","useTranslate","saving","useTemplateRef","onMounted","nextTick","submit","under","error","_createBlock","_unref","WxDialog","$event","_createVNode","WxSpace","WxButton","WxFormItem","_createElementVNode","WxInput","WxTreeSelect","lastSegment","segments","segment","router","useRouter","message","useErrorText","rows","loading","loaded","create","createModal","CategoryCreateDialog","canManage","canDelete","asTree","matches","term","columns","rowMenuWidth","treeOptions","drag","drop","show","plain","load","toast","onState","state","add","detail","dropped","publish","on","remove","confirm","productsOf","actionsFor","actions","leads","_openBlock","_createElementBlock","_hoisted_1","WxListScreen","WxTable","_withCtx","row","_normalizeClass","_toDisplayString","WxText","_component_router_link","_createTextVNode","_cache","WxBadge","WxRowMenu","productEditorKey","provideProductEditor","editor","provide","useProductEditor","inject","categoryEditorKey","provideCatalogCategoryEditor","useCatalogCategoryEditor","route","useRoute","list","category","facetsFrom","snapshot","locked","dirty","written","localizedValue","address","parents","provideHistorySubject","provideRecordAddress","take","save","leaveGuard","onKeydown","useBodyKeys","watch","onBeforeUnmount","onBeforeRouteLeave","_Fragment","WxSkeleton","WxCard","WxScreenHead","WxBreadcrumb","WxBreadcrumbItem","_renderList","WxScreen","WxActionBar","WxSaveState","_useModel","useRecordAddress","main","chosen","mark","model","next","ids","_mergeProps","_ctx","page","canRestore","views","asked","mine","restore","bare","refOf","found","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","WxDate","registry","failed","facets","labels","complete","named","inherited","inheritedNames","toggleOwn","visible","findSource","from","setting","now","WxAlert","WxSwitch","WxSortableList","item","VideoUnreadable","reason","browser","frameTime","duration","probeVideo","options","environment","timeout","video","settled","timer","fail","done","canvas","width","height","length","blob","posterName","YOUTUBE_HOSTS","YOUTUBE_ID","youTubeId","watched","isVideoFileLink","isVideoLink","videoRefusal","types","maxBytes","hasPlaceholderPoster","formatBytes","bytes","locale","units","unit","formatClock","seconds","whole","hours","minutes","rest","VIDEO_PURPOSE","TARGETS_KEY","createGalleryVideo","storage","defaultStorage","useChunkedUpload","jobs","stored","running","uploading","busy","job","unfinished","product","targets","readTargets","going","uploadFingerprint","unfinishedUploads","entry","patch","change","find","setTarget","writeTargets","dropTarget","replace","one","pump","work","probe","failure","poster","picture","retry","cancel","forget","forgetUnfinishedUpload","guard","getCurrentScope","onScopeDispose","readonly","parsed","ready","percent","figures","parts","left","stage","WxProgress","part","index","PAUSE","PICTURES","PLAY","uploads","fetching","productId","uploader","videos","accept","openLinkDialog","VideoLinkDialog","sent","captions","saveGallery","carrying","caption","text","moved","acceptable","refusal","isVideo","files","progress","rejected","_file","queued","fetchAddress","picker","pickFor","pickFile","resume","picked","target","attachLink","detach","videoLabel","jobOf","unfinishedText","_hoisted_7","_hoisted_8","VideoProgress","_hoisted_9","_hoisted_10","_hoisted_12","WxUpload","_hoisted_14","holder","section","module","subtitle","holderLink","sku","reactive","param","optionsOf","required","onCategory","apply","WxSourceSelect","WxSelect","POLL","emit","__emit","BulkParamsDialog","run","starting","refused","asks","start","given","follow","poll","finish","WxDropdown","WxIcon","WxDropdownItem","TONES","toneBadge","tone","piece","record","pieces","FACET_PREFIX","readFacets","raw","min","max","end","range","writeFacet","CARDS","EXTRA","CATEGORY_FROM","root","useElementWidth","selected","everything","pageNumber","perPage","ProductCreateDialog","view","sorts","sort","missing","priced","extra","asCards","categoryNames","extraFrom","column","wanted","narrow","order","clearFilters","counted","valuesOf","valueLabel","boundOf","bound","money","applied","said","emptyText","rangeOf","treeChoice","bulkSelection","facetsQuery","clearSelection","onBulkFinished","stateType","price","hasValue","BulkBar","WxInputNumber","WxFilterChips","WxTooltip","WxEntityCard","ColumnValue","useCategoryEditor","labelledBy","words","sample","pick","step","delta","at","catalog","products","ProductsPage","ProductEditorPage","DeletedPage","CategoryField","FacetsField","GalleryField","ToneField","categories","CategoriesPage","CategoryEditorPage","OWN","dictionaryOptions","word","dictionarySection","described","seed","seedMessages","categoryRoutes","seededIn","namespace","messages","defineComponent","h"],"mappings":"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"}
|