@haus-tech/product-import-export-plugin 3.0.24-0 → 3.0.26-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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 3.0.25-0
2
+
3
+ This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
4
+
5
+ ## 3.0.24-0
6
+
7
+ This was a version bump only for product-import-export-plugin to align it with other projects, there were no code changes.
8
+
1
9
  ## 3.0.23-0
2
10
 
3
11
  ### 🚀 Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haus-tech/product-import-export-plugin",
3
- "version": "3.0.24-0",
3
+ "version": "3.0.26-0",
4
4
  "description": "A Vendure plugin for importing and exporting products using a CSV file",
5
5
  "author": "Haus Tech",
6
6
  "repository": {
@@ -0,0 +1,83 @@
1
+ <ng-template vdrDialogTitle>{{ 'product-export.dialog.title' | translate }}</ng-template>
2
+ <div class="input-row">
3
+ <vdr-form-field [label]="'product-export.dialog.label' | translate">
4
+ <input
5
+ type="text"
6
+ [(ngModel)]="fileName"
7
+ [placeholder]="config?.defaultFileName || 'products_export.csv'"
8
+ style="width: 100%"
9
+ #fileNameInput
10
+ />
11
+ </vdr-form-field>
12
+ </div>
13
+
14
+ <div class="input-row" style="margin-top: 16px">
15
+ <input type="checkbox" (change)="toggleSelectAll($event)" [checked]="toggleAllChecked" />
16
+ {{ 'product-export.dialog.selectAll' | translate }}
17
+ </div>
18
+
19
+ <div class="input-row" style="margin-top: 16px">
20
+ <label style="margin-bottom: 8px; display: inline-block">{{
21
+ 'product-export.dialog.availableExportFieldsLabel' | translate
22
+ }}</label>
23
+ <div *ngFor="let field of availableExportFields">
24
+ <input
25
+ type="checkbox"
26
+ [value]="field"
27
+ (change)="toggleExportFieldSelection(field)"
28
+ [checked]="selectedExportFields.includes(field)"
29
+ [disabled]="config.requiredExportFields.includes(field)"
30
+ />
31
+ {{ field }}
32
+ </div>
33
+ </div>
34
+
35
+ <div class="input-row" style="margin-top: 16px">
36
+ <label style="margin-bottom: 8px; display: inline-block">{{
37
+ 'product-export.dialog.customFieldsLabel' | translate
38
+ }}</label>
39
+ <div *ngFor="let field of customFields">
40
+ <input
41
+ type="checkbox"
42
+ [value]="field"
43
+ (change)="toggleFieldSelection(field)"
44
+ [checked]="selectedFields.includes(field)"
45
+ />
46
+ {{ field }}
47
+ </div>
48
+ </div>
49
+
50
+ <span style="margin-top: 16px; margin-bottom: 8px; display: block">{{
51
+ 'product-export.dialog.exportAssetsAs' | translate
52
+ }}</span>
53
+ <div class="input-row" style="margin-top: 8px">
54
+ <label *ngIf="config?.exportAssetsAsOptions?.includes('url')">
55
+ <input
56
+ type="radio"
57
+ name="exportAssets"
58
+ value="url"
59
+ [(ngModel)]="exportAssetsAs"
60
+ style="width: fit-content"
61
+ />
62
+ {{ 'product-export.dialog.exportAssetsAsOptions.url' | translate }}
63
+ </label>
64
+ <label *ngIf="config?.exportAssetsAsOptions?.includes('json')">
65
+ <input
66
+ type="radio"
67
+ name="exportAssets"
68
+ value="json"
69
+ [(ngModel)]="exportAssetsAs"
70
+ style="width: fit-content"
71
+ />
72
+ {{ 'product-export.dialog.exportAssetsAsOptions.json' | translate }}
73
+ </label>
74
+ </div>
75
+
76
+ <ng-template vdrDialogButtons>
77
+ <button type="button" class="btn" (click)="cancel()">
78
+ {{ 'common.cancel' | translate }}
79
+ </button>
80
+ <button type="button" class="btn btn-primary" (click)="export()">
81
+ {{ 'product-export.dialog.export' | translate }}
82
+ </button>
83
+ </ng-template>
@@ -0,0 +1,165 @@
1
+ import { Component, ViewChild, ElementRef, AfterViewInit, OnInit } from '@angular/core'
2
+ import { Dialog } from '@vendure/admin-ui/core'
3
+ import { FormsModule } from '@angular/forms'
4
+ import { SharedModule } from '@vendure/admin-ui/core'
5
+ import { uniq } from 'lodash'
6
+
7
+ type ProductFields =
8
+ | 'productId'
9
+ | 'name'
10
+ | 'slug'
11
+ | 'description'
12
+ | 'assets'
13
+ | 'facets'
14
+ | 'optionGroups'
15
+
16
+ type VariantFields =
17
+ | 'sku'
18
+ | 'optionValues'
19
+ | 'price'
20
+ | 'taxCategory'
21
+ | 'stockOnHand'
22
+ | 'trackInventory'
23
+ | 'variantAssets'
24
+ | 'variantFacets'
25
+ | 'enabled'
26
+
27
+ type ExportFields = Array<ProductFields | VariantFields>
28
+ interface PluginInitOptions {
29
+ defaultFileName?: string
30
+ exportAssetsAsOptions?: Array<'url' | 'json'>
31
+ defaultExportAssetsAs?: 'url' | 'json'
32
+ defaultExportFields?: ExportFields
33
+ requiredExportFields?: ExportFields
34
+ }
35
+
36
+ @Component({
37
+ selector: 'vdr-export-dialog',
38
+ templateUrl: './export-dialog.component.html',
39
+ standalone: true,
40
+ imports: [SharedModule, FormsModule],
41
+ })
42
+ export class ExportDialogComponent
43
+ implements
44
+ Dialog<{
45
+ result: boolean
46
+ fileName?: string
47
+ selectedFields?: string[]
48
+ exportAssetsAs?: 'url' | 'json'
49
+ selectedExportFields?: ExportFields
50
+ }>,
51
+ AfterViewInit,
52
+ OnInit
53
+ {
54
+ @ViewChild('fileNameInput', { static: false }) fileNameElement: ElementRef<HTMLInputElement>
55
+
56
+ resolveWith: (result?: {
57
+ result: boolean
58
+ fileName?: string
59
+ selectedFields?: string[]
60
+ exportAssetsAs?: 'url' | 'json'
61
+ selectedExportFields?: ExportFields
62
+ }) => void
63
+ selection: any[] = []
64
+ fileName: string = ''
65
+ customFields: string[] = []
66
+ selectedFields: string[] = []
67
+ exportAssetsAs: 'url' | 'json' = 'url'
68
+ selectedExportFields: ExportFields = []
69
+ availableExportFields: ExportFields = [
70
+ 'productId',
71
+ 'name',
72
+ 'slug',
73
+ 'description',
74
+ 'assets',
75
+ 'facets',
76
+ 'optionGroups',
77
+ 'sku',
78
+ 'optionValues',
79
+ 'price',
80
+ 'taxCategory',
81
+ 'stockOnHand',
82
+ 'trackInventory',
83
+ 'variantAssets',
84
+ 'variantFacets',
85
+ 'enabled',
86
+ ]
87
+
88
+ config: PluginInitOptions
89
+ toggleAllChecked: boolean = false
90
+
91
+ ngOnInit(): void {
92
+ this.selectedFields = [...this.customFields]
93
+ if (this.config.defaultExportAssetsAs?.includes(this.config.defaultExportAssetsAs)) {
94
+ this.exportAssetsAs = this.config.defaultExportAssetsAs
95
+ } else {
96
+ this.exportAssetsAs = this.config.exportAssetsAsOptions?.[0] || 'url'
97
+ }
98
+
99
+ this.selectedExportFields = uniq([
100
+ ...(this.config.defaultExportFields || []),
101
+ ...(this.config.requiredExportFields || []),
102
+ ])
103
+ }
104
+
105
+ ngAfterViewInit(): void {
106
+ if (this.fileNameElement) {
107
+ setTimeout(() => {
108
+ this.fileNameElement.nativeElement.focus()
109
+ }, 0)
110
+ }
111
+ }
112
+
113
+ export() {
114
+ this.fileName = this.fileName?.trim()
115
+ this.resolveWith({
116
+ result: true,
117
+ fileName: this.fileName,
118
+ selectedFields: this.selectedFields,
119
+ exportAssetsAs: this.exportAssetsAs,
120
+ selectedExportFields: this.selectedExportFields,
121
+ })
122
+ }
123
+
124
+ cancel() {
125
+ this.resolveWith({ result: false })
126
+ }
127
+
128
+ toggleFieldSelection(fieldName: string) {
129
+ const index = this.selectedFields.indexOf(fieldName)
130
+ if (index > -1) {
131
+ this.selectedFields.splice(index, 1)
132
+ } else {
133
+ this.selectedFields.push(fieldName)
134
+ }
135
+
136
+ this.toggleAllChecked = this.selectedFields.length === this.availableExportFields.length
137
+ }
138
+
139
+ toggleExportFieldSelection(fieldName: ProductFields | VariantFields) {
140
+ const index = this.selectedExportFields.indexOf(fieldName)
141
+ if (index > -1) {
142
+ this.selectedExportFields.splice(index, 1)
143
+ } else {
144
+ this.selectedExportFields.push(fieldName)
145
+ }
146
+
147
+ this.toggleAllChecked = this.selectedExportFields.length === this.availableExportFields.length
148
+ }
149
+
150
+ toggleSelectAll(event: Event) {
151
+ const isChecked = (event.target as HTMLInputElement).checked
152
+ this.toggleAllChecked = isChecked
153
+ if (isChecked) {
154
+ this.selectedExportFields = [...this.availableExportFields]
155
+ this.selectedFields = [...this.customFields]
156
+ } else {
157
+ this.selectedExportFields = [
158
+ ...this.availableExportFields.filter((field) =>
159
+ this.config.requiredExportFields?.includes(field),
160
+ ),
161
+ ]
162
+ this.selectedFields = []
163
+ }
164
+ }
165
+ }
@@ -0,0 +1,123 @@
1
+ <!-- product-import.component.html -->
2
+ <vdr-page-block>
3
+ <vdr-card [title]="'product-import.card.title' | translate">
4
+ <div class="form-grid">
5
+ <vdr-form-field [label]="'product-import.card.description' | translate">
6
+ <input type="file" (change)="onFileSelected($event)" class="file-input" #fileUpload />
7
+
8
+ <div class="file-upload" style="display: flex; align-items: center; gap: 8px">
9
+ <button *ngIf="selectedFile" class="button-small" (click)="fileUpload.click()">
10
+ <clr-icon shape="file"></clr-icon>
11
+ {{ selectedFile?.name }}
12
+ </button>
13
+
14
+ <button class="button primary" *ngIf="!selectedFile" (click)="fileUpload.click()">
15
+ {{ 'product-import.card.button-select' | translate }}
16
+ </button>
17
+ <button
18
+ class="button primary"
19
+ *ngIf="selectedFile"
20
+ (click)="uploadFile()"
21
+ [disabled]="!selectedMainLanguage"
22
+ >
23
+ {{ 'product-import.card.button-upload' | translate }}
24
+ </button>
25
+ </div>
26
+ </vdr-form-field>
27
+ </div>
28
+ <div
29
+ class="form-grid"
30
+ *ngIf="selectedFile"
31
+ style="margin-top: 24px; border-top: 1px solid #cdcdcd"
32
+ >
33
+ <div
34
+ class="input-row"
35
+ style="margin-top: 16px"
36
+ *ngIf="config.visibleOptions?.includes('updateProductSlug')"
37
+ >
38
+ <label>
39
+ <input
40
+ type="checkBox"
41
+ name="updateProductSlug"
42
+ value="url"
43
+ [(ngModel)]="updateProductSlug"
44
+ style="width: fit-content"
45
+ />
46
+ {{ 'product-import.card.update-product-slugs' | translate }}
47
+ </label>
48
+ </div>
49
+
50
+ <div class="input-row" style="margin-top: 16px">
51
+ <div>{{ 'product-import.card.select-main-language' | translate }}</div>
52
+ <div *ngFor="let lang of availableLanguages">
53
+ <label>
54
+ <input
55
+ type="radio"
56
+ name="selectedMainLanguage"
57
+ [value]="lang"
58
+ [(ngModel)]="selectedMainLanguage"
59
+ style="width: fit-content"
60
+ (change)="onMainLanguageChange(lang)"
61
+ />
62
+ {{ lang }}
63
+ </label>
64
+ </div>
65
+ <div
66
+ style="
67
+ margin-top: 8px;
68
+ padding-top: 8px;
69
+ font-style: italic;
70
+ font-size: 12px;
71
+ line-height: 1.4;
72
+ "
73
+ >
74
+ <clr-icon shape="info-circle" size="16" class="is-info"></clr-icon>
75
+ {{ 'product-import.card.main-language-info' | translate }}
76
+ </div>
77
+ </div>
78
+
79
+ <div class="input-row" style="margin-top: 16px">
80
+ <div>{{ 'product-import.card.updating-strategy' | translate }}</div>
81
+ <div>
82
+ <label>
83
+ <input
84
+ type="radio"
85
+ name="updatingStrategy"
86
+ value="merge"
87
+ [(ngModel)]="updatingStrategy"
88
+ style="width: fit-content"
89
+ />
90
+ {{ 'product-import.card.merge' | translate }}
91
+ </label>
92
+ </div>
93
+ <div>
94
+ <label>
95
+ <input
96
+ type="radio"
97
+ name="updatingStrategy"
98
+ value="replace"
99
+ [(ngModel)]="updatingStrategy"
100
+ style="width: fit-content"
101
+ />
102
+ {{ 'product-import.card.overwrite' | translate }}<br />
103
+ <span *ngIf="updatingStrategy === 'replace'" style="color: red; font-size: 12px">{{
104
+ 'product-import.card.overwrite-warning' | translate
105
+ }}</span>
106
+ </label>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </vdr-card>
111
+ </vdr-page-block>
112
+
113
+ <style>
114
+ .form-grid {
115
+ display: grid;
116
+ grid-template-columns: 1fr 1fr;
117
+ grid-gap: 1rem;
118
+ }
119
+
120
+ .file-input {
121
+ display: none;
122
+ }
123
+ </style>