@webx-ui/module-catalog 0.5.0 → 0.7.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/BulkBar.d.ts CHANGED
@@ -7,14 +7,20 @@ import { BulkSelection } from './types';
7
7
  * once; a large one is a run in the background, polled here with its progress, and its refusals
8
8
  * are listed by product when it ends. The list reloads either way, and `finished` carries the ids
9
9
  * that refused — what the list leaves picked, so they can be put right and tried again.
10
+ *
11
+ * «Export» heads the menu (§8.1 of the exchange spec): the same selection, written to a file. It
12
+ * is the exchange's run rather than a bulk one, and is followed in «Exchange».
10
13
  */
11
14
  type __VLS_Props = {
12
15
  count: number;
13
16
  selection: BulkSelection;
17
+ base?: string;
14
18
  };
15
19
  declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
16
20
  finished: (refused: number[]) => any;
17
21
  }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
18
22
  onFinished?: ((refused: number[]) => any) | undefined;
19
- }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
23
+ }>, {
24
+ base: string;
25
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
20
26
  export default _default;
@@ -0,0 +1,19 @@
1
+ import { BulkSelection } from './types';
2
+ /**
3
+ * An export (§8.1 of the exchange spec): a saved profile, or columns chosen here with the
4
+ * languages their translations go out in, and a format. What goes out is the list's selection —
5
+ * ticked rows or everything a filter finds — or, opened from «Exchange», the whole catalogue.
6
+ *
7
+ * The run is handed back as the server answered it; the opener decides where to follow it.
8
+ */
9
+ type __VLS_Props = {
10
+ /** What the list picked; the whole catalogue outside «Deleted» when left out. */
11
+ selection?: BulkSelection | null;
12
+ /** How many products that is, said above the choice; nothing is said without it. */
13
+ count?: number | null;
14
+ };
15
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
16
+ count: number | null;
17
+ selection: BulkSelection | null;
18
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
19
+ export default _default;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The import wizard (§8.1 of the exchange spec, screen `catalog.exchange-import`): a file — an
3
+ * upload in pieces or an address the server downloads — then its columns matched to the
4
+ * catalogue's with five rows of the file under each, then how the rows are written. The last
5
+ * step checks the whole file without writing, or runs it; either way the run is followed in
6
+ * «Exchange».
7
+ *
8
+ * The settings are a described screen: a project can take a setting away or fix it with a
9
+ * patch, the way it does any form of the panel. A profile fills all three steps at once — its
10
+ * columns are laid over the header of the file, and what the file has that the profile does not
11
+ * know keeps the server's guess.
12
+ */
13
+ type __VLS_Props = {
14
+ base?: string;
15
+ };
16
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
17
+ base: string;
18
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
19
+ picker: HTMLInputElement;
20
+ }, HTMLDivElement>;
21
+ export default _default;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * What an import changed, as the journal has it (§1 п. 12 of the exchange spec): the run's rows,
3
+ * one per product, with the fields each got. The record's own history links back to the run;
4
+ * this is the other direction — from the run to the records.
5
+ */
6
+ type __VLS_Props = {
7
+ runId: number;
8
+ base: string;
9
+ };
10
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
11
+ export default _default;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * «Exchange» (§8.1 of the exchange spec): every import and export with what it did, newest first,
3
+ * and the two ways in — the import wizard and the export dialog. A row opens in place: its
4
+ * totals, its errors, its file, its run of the journal.
5
+ *
6
+ * A run that is still going is asked after every two seconds, the way the bulk actions are; the
7
+ * row moves on its own while the list is open. A run started elsewhere — the wizard, the export
8
+ * of the product list — arrives here as `?run=<id>`, opened and followed.
9
+ */
10
+ type __VLS_Props = {
11
+ base?: string;
12
+ };
13
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
14
+ base: string;
15
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
16
+ export default _default;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * One profile of the exchange (§8.1): its head (name, format, how a CSV is read) and, by
3
+ * direction, either how an import writes and which header goes into which column, or the
4
+ * columns an export writes in their order.
5
+ *
6
+ * The head and the import's settings are the two described screens the wizard uses too, so a
7
+ * setting a project took away is gone from both. The columns are this page's own: a mapping is
8
+ * a list of pairs the server checks against the columns the caller may write.
9
+ */
10
+ type __VLS_Props = {
11
+ base?: string;
12
+ };
13
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
14
+ base: string;
15
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
16
+ export default _default;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The saved profiles of the exchange (§8.1): a name, a direction, a format, and what a file of
3
+ * that kind looks like — so the supplier's price list of every Monday is one step, not three.
4
+ * A row opens the form; an import profile also starts the wizard with itself filled in.
5
+ */
6
+ type __VLS_Props = {
7
+ base?: string;
8
+ };
9
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
10
+ base: string;
11
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
12
+ export default _default;
@@ -0,0 +1,16 @@
1
+ import { ExchangeRun } from './exchange';
2
+ /**
3
+ * A run opened in the list of «Exchange» (§8.1): what it read and who started it, its totals,
4
+ * the first hundred errors with all of them a click away as CSV, the finished file of an
5
+ * export, and the run of the journal that holds what an import changed.
6
+ *
7
+ * The errors are asked for again when the run's count of them moves, which is how a run still
8
+ * going fills its table while it is open.
9
+ */
10
+ type __VLS_Props = {
11
+ run: ExchangeRun;
12
+ base: string;
13
+ profileName?: string | null;
14
+ };
15
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
16
+ export default _default;
@@ -0,0 +1,151 @@
1
+ import { Ref } from 'vue';
2
+ import { AdminContext } from '@webx-ui/module-admin';
3
+ import { Paginated } from '@webx-ui/core';
4
+ import { BulkSelection } from './types';
5
+ /**
6
+ * The exchange of the catalogue as the panel sees it (§8 of the exchange spec): the columns a
7
+ * file may carry, a look at a file before it goes in, the runs with their errors and files, and
8
+ * the saved profiles. The server's shapes as they are — `ExchangeController`,
9
+ * `ExchangeProfileController`, `ExchangeRun::toResponse()`.
10
+ */
11
+ /** What a chunked upload is claimed for — the exchange's own, beside `catalog.video`. */
12
+ export declare const EXCHANGE_PURPOSE = "catalog.exchange";
13
+ export type ExchangeDirection = 'import' | 'export';
14
+ export type ExchangeFormat = 'csv' | 'xlsx';
15
+ export type ExchangeStatus = 'queued' | 'running' | 'done' | 'stopped' | 'failed';
16
+ /** A column the caller may map or export; `locales` are the other languages of a translated one. */
17
+ export interface ExchangeColumnInfo {
18
+ key: string;
19
+ label: string;
20
+ field: string;
21
+ localized: boolean;
22
+ locales: string[];
23
+ }
24
+ /** How an import writes (§1). Encoding and separator only when somebody knows better than a guess. */
25
+ export interface ImportOptions {
26
+ key: 'sku' | 'external_id' | 'barcode' | 'id';
27
+ mode: 'upsert' | 'update' | 'create';
28
+ empty_clears: boolean;
29
+ absent: 'keep' | 'unpublish';
30
+ absent_scope: 'categories' | 'all';
31
+ create_missing: boolean;
32
+ images: 'append' | 'replace';
33
+ encoding?: string;
34
+ delimiter?: string;
35
+ format?: ExchangeFormat;
36
+ }
37
+ export declare const IMPORT_DEFAULTS: ImportOptions;
38
+ /** A file as the server read it: how, its header, five rows, and what each header seems to be. */
39
+ export interface ExchangeInspection {
40
+ format: ExchangeFormat;
41
+ options: {
42
+ encoding?: string;
43
+ delimiter?: string;
44
+ };
45
+ header: string[];
46
+ sample: string[][];
47
+ mapping: Record<string, string | null>;
48
+ }
49
+ export type ExchangeSource = {
50
+ upload_id: string;
51
+ } | {
52
+ url: string;
53
+ };
54
+ export interface ExchangeRun {
55
+ id: number;
56
+ profile_id: number | null;
57
+ direction: ExchangeDirection;
58
+ format: ExchangeFormat;
59
+ options: Partial<ImportOptions> & Record<string, unknown>;
60
+ /** Import: header → column code. Export: the codes in their order. */
61
+ mapping: Record<string, string> | string[];
62
+ dry_run: boolean;
63
+ status: ExchangeStatus;
64
+ source: string | null;
65
+ rows_total: number;
66
+ rows_done: number;
67
+ created: number;
68
+ updated: number;
69
+ skipped: number;
70
+ failed: number;
71
+ absent: number;
72
+ history_id: number | null;
73
+ admin_id: number | null;
74
+ admin_name: string | null;
75
+ /** The finished export by a signed address, while the file is kept; null otherwise. */
76
+ file_url: string | null;
77
+ started_at: string | null;
78
+ finished_at: string | null;
79
+ created_at: string | null;
80
+ }
81
+ export interface ExchangeRunError {
82
+ row: number;
83
+ /** Null — the row as a whole. */
84
+ column: string | null;
85
+ value: string | null;
86
+ message: string;
87
+ }
88
+ export interface ExchangeProfile {
89
+ id: number;
90
+ name: string;
91
+ direction: ExchangeDirection;
92
+ format: ExchangeFormat;
93
+ options: Partial<ImportOptions> & Record<string, unknown>;
94
+ mapping: Record<string, string> | string[];
95
+ last_run_id: number | null;
96
+ created_at: string | null;
97
+ updated_at: string | null;
98
+ }
99
+ export type ExchangeProfileInput = Partial<Pick<ExchangeProfile, 'name' | 'direction' | 'format' | 'options' | 'mapping'>>;
100
+ export interface ExchangeApi {
101
+ columns(): Promise<ExchangeColumnInfo[]>;
102
+ inspect(source: ExchangeSource, read?: {
103
+ format?: string;
104
+ encoding?: string;
105
+ delimiter?: string;
106
+ }): Promise<ExchangeInspection>;
107
+ /** A small file is done inside the request; a large one comes back queued, to be polled. */
108
+ startImport(source: ExchangeSource, plan: {
109
+ profile_id?: number;
110
+ mapping?: Record<string, string | null>;
111
+ options?: object;
112
+ }, dryRun: boolean): Promise<ExchangeRun>;
113
+ startExport(selection: BulkSelection & {
114
+ trashed?: boolean;
115
+ }, plan: {
116
+ profile_id: number;
117
+ } | {
118
+ columns: string[];
119
+ format: ExchangeFormat;
120
+ }): Promise<ExchangeRun>;
121
+ runs(query?: {
122
+ page?: number;
123
+ per_page?: number;
124
+ }): Promise<Paginated<ExchangeRun>>;
125
+ run(id: number): Promise<ExchangeRun>;
126
+ errors(id: number, page?: number): Promise<Paginated<ExchangeRunError>>;
127
+ /** Where the browser downloads every error of a run as CSV, under the panel's session. */
128
+ errorsFile(id: number): string;
129
+ /** Where the browser downloads a finished export under the panel's session. */
130
+ file(id: number): string;
131
+ profiles(direction?: ExchangeDirection): Promise<ExchangeProfile[]>;
132
+ profile(id: number): Promise<ExchangeProfile>;
133
+ createProfile(values: ExchangeProfileInput): Promise<ExchangeProfile>;
134
+ saveProfile(id: number, values: ExchangeProfileInput): Promise<ExchangeProfile>;
135
+ removeProfile(id: number): Promise<void>;
136
+ }
137
+ export declare function createExchangeApi(admin: AdminContext): ExchangeApi;
138
+ export declare function isRunning(run: Pick<ExchangeRun, 'status'>): boolean;
139
+ /** As the server spells one column of an export: `name`, or `name@de` for a translation. */
140
+ export declare function columnCode(key: string, locale?: string | null): string;
141
+ /** As often as the bulk actions ask (§8.1): a run of eighty thousand rows is not a request storm. */
142
+ export declare const EXCHANGE_POLL = 2000;
143
+ /**
144
+ * Runs that are still going, asked after every two seconds until they end. `watch` puts a run
145
+ * under watch; `onUpdate` hears every fresh answer, the last one included. A missed answer is
146
+ * not the end of a run — it is asked again on the next turn.
147
+ */
148
+ export declare function useRunPolling(api: Pick<ExchangeApi, 'run'>, onUpdate: (run: ExchangeRun) => void): {
149
+ watch: (id: number) => void;
150
+ watching: Ref<number[]>;
151
+ };
@@ -0,0 +1,20 @@
1
+ import { BadgeType } from '@webx-ui/core';
2
+ import { ExchangeRun } from './exchange';
3
+ type Translate = (key: string, params?: Record<string, string | number>) => string;
4
+ export interface RunTotal {
5
+ key: string;
6
+ text: string;
7
+ tone: 'success' | 'danger' | 'muted' | 'default';
8
+ }
9
+ /**
10
+ * What a run did, as short lines: an import by its counters — only the ones that are not zero —
11
+ * an export by the products it wrote. Counted at the end of each line ("Created: 12"), because
12
+ * the panel has no plurals (CLAUDE.md §4).
13
+ */
14
+ export declare function runTotals(run: ExchangeRun, t: Translate): RunTotal[];
15
+ /** The badge of a run's status. A finished import with errors is done, and says so in amber. */
16
+ export declare function runStatus(run: ExchangeRun, t: Translate): {
17
+ label: string;
18
+ type: BadgeType;
19
+ };
20
+ export {};
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export { TONES, toneBadge, type Tone } from './tones';
7
7
  export { provideProductEditor, productEditorKey, useProductEditor, provideCatalogCategoryEditor, categoryEditorKey, useCatalogCategoryEditor, type ProductEditorContext, type CategoryEditorContext, } from './editor';
8
8
  export { createGalleryVideo, VIDEO_PURPOSE, type GalleryVideo, type GalleryVideoOptions, type UnfinishedVideo, type VideoJob, type VideoJobStage, } from './galleryVideo';
9
9
  export { useCategoryTree, useFacetRegistry, type CategoryTree, type FacetRegistry } from './store';
10
+ export { columnCode, createExchangeApi, EXCHANGE_POLL, EXCHANGE_PURPOSE, IMPORT_DEFAULTS, isRunning, useRunPolling, type ExchangeApi, type ExchangeColumnInfo, type ExchangeDirection, type ExchangeFormat, type ExchangeInspection, type ExchangeProfile, type ExchangeProfileInput, type ExchangeRun, type ExchangeRunError, type ExchangeSource, type ExchangeStatus, type ImportOptions, } from './exchange';
10
11
  export { default as WxCatalogProductsPage } from './ProductsPage';
11
12
  export { default as WxCatalogProductEditor } from './ProductEditorPage';
12
13
  export { default as WxCatalogProductCreateDialog } from './ProductCreateDialog';
@@ -14,6 +15,11 @@ export { default as WxCatalogCategoriesPage } from './CategoriesPage';
14
15
  export { default as WxCatalogCategoryEditor } from './CategoryEditorPage';
15
16
  export { default as WxCatalogCategoryCreateDialog } from './CategoryCreateDialog';
16
17
  export { default as WxCatalogDeletedPage } from './DeletedPage';
18
+ export { default as WxCatalogExchangePage } from './ExchangePage';
19
+ export { default as WxCatalogExchangeImport } from './ExchangeImportPage';
20
+ export { default as WxCatalogExchangeExportDialog } from './ExchangeExportDialog';
21
+ export { default as WxCatalogExchangeProfiles } from './ExchangeProfilesPage';
22
+ export { default as WxCatalogExchangeProfile } from './ExchangeProfilePage';
17
23
  export { default as WxCatalogCategoryField } from './CategoryField';
18
24
  export { default as WxCatalogFacetsField } from './FacetsField';
19
25
  export { default as WxCatalogGallery } from './GalleryField';