@designcrowd/fe-shared-lib 1.5.34 → 1.5.36

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.
Files changed (36) hide show
  1. package/dist/css/tailwind-brandCrowd.css +2 -65
  2. package/dist/css/tailwind-brandPage.css +3 -54
  3. package/dist/css/tailwind-crazyDomains.css +2 -65
  4. package/dist/css/tailwind-designCom.css +2 -65
  5. package/dist/css/tailwind-designCrowd.css +2 -65
  6. package/docs/plans/DY-957-plan-phase-2-fe-shared-lib.md +382 -0
  7. package/package.json +1 -1
  8. package/public/css/tailwind-brandCrowd.css +2472 -0
  9. package/public/css/tailwind-brandPage.css +2156 -0
  10. package/public/css/tailwind-crazyDomains.css +2472 -0
  11. package/public/css/tailwind-designCom.css +2472 -0
  12. package/public/css/tailwind-designCrowd.css +2472 -0
  13. package/src/atoms/components/Icon/Icon.vue +4 -0
  14. package/src/atoms/components/Icon/icons/history.vue +12 -0
  15. package/src/atoms/components/Icon/icons/save.vue +6 -0
  16. package/src/bundles/bundled-translations.de-DE.json +3 -1
  17. package/src/bundles/bundled-translations.es-ES.json +3 -1
  18. package/src/bundles/bundled-translations.fr-CA.json +3 -1
  19. package/src/bundles/bundled-translations.fr-FR.json +3 -1
  20. package/src/bundles/bundled-translations.json +3 -1
  21. package/src/bundles/bundled-translations.pt-BR.json +3 -1
  22. package/src/bundles/bundled-translations.pt-PT.json +3 -1
  23. package/src/experiences/clients/brand-crowd-api.client.js +18 -0
  24. package/src/experiences/components/UploadYourLogoApplication/UploadYourLogoApplication.stories.js +49 -0
  25. package/src/experiences/components/UploadYourLogoApplication/UploadYourLogoApplication.vue +28 -0
  26. package/src/experiences/components/UploadYourLogoOnBoarding/LogoKeywords.stories.js +65 -0
  27. package/src/experiences/components/UploadYourLogoOnBoarding/LogoKeywords.vue +156 -0
  28. package/src/experiences/components/UploadYourLogoOnBoarding/UploadYourLogoOnBoarding.vue +61 -9
  29. package/src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.de-DE.json +34 -31
  30. package/src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.es-ES.json +34 -31
  31. package/src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.fr-CA.json +34 -31
  32. package/src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.fr-FR.json +34 -31
  33. package/src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.json +3 -1
  34. package/src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.pt-BR.json +34 -31
  35. package/src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.pt-PT.json +34 -31
  36. package/src/experiences/constants/api.js +1 -0
@@ -617,9 +617,6 @@ video {
617
617
  .tw-m-3 {
618
618
  margin: 0.75rem;
619
619
  }
620
- .tw-m-4 {
621
- margin: 1rem;
622
- }
623
620
  .tw-m-5 {
624
621
  margin: 1.25rem;
625
622
  }
@@ -946,9 +943,6 @@ video {
946
943
  .tw-w-96 {
947
944
  width: 24rem;
948
945
  }
949
- .tw-w-\[14rem\] {
950
- width: 14rem;
951
- }
952
946
  .tw-w-auto {
953
947
  width: auto;
954
948
  }
@@ -962,9 +956,6 @@ video {
962
956
  .tw-max-w-2xl {
963
957
  max-width: 42rem;
964
958
  }
965
- .tw-max-w-3xl {
966
- max-width: 48rem;
967
- }
968
959
  .tw-max-w-full {
969
960
  max-width: 100%;
970
961
  }
@@ -1213,9 +1204,6 @@ video {
1213
1204
  .tw-rounded-full {
1214
1205
  border-radius: 9999px;
1215
1206
  }
1216
- .tw-rounded-lg {
1217
- border-radius: 0.5rem;
1218
- }
1219
1207
  .tw-rounded-md {
1220
1208
  border-radius: 0.375rem;
1221
1209
  }
@@ -1332,10 +1320,6 @@ video {
1332
1320
  --tw-border-opacity: 1;
1333
1321
  border-color: rgb(125 130 132 / var(--tw-border-opacity));
1334
1322
  }
1335
- .tw-border-grayscale-700 {
1336
- --tw-border-opacity: 1;
1337
- border-color: rgb(82 93 96 / var(--tw-border-opacity));
1338
- }
1339
1323
  .tw-border-info-300 {
1340
1324
  --tw-border-opacity: 1;
1341
1325
  border-color: rgb(112 193 243 / var(--tw-border-opacity));
@@ -1830,10 +1814,6 @@ video {
1830
1814
  font-size: 1.5rem;
1831
1815
  line-height: 2rem;
1832
1816
  }
1833
- .tw-text-3xl {
1834
- font-size: 1.875rem;
1835
- line-height: 2.25rem;
1836
- }
1837
1817
  .tw-text-4xl {
1838
1818
  font-size: 2.25rem;
1839
1819
  line-height: 2.5rem;
@@ -1857,9 +1837,6 @@ video {
1857
1837
  .tw-font-bold {
1858
1838
  font-weight: 700;
1859
1839
  }
1860
- .tw-font-medium {
1861
- font-weight: 500;
1862
- }
1863
1840
  .tw-font-normal {
1864
1841
  font-weight: 400;
1865
1842
  }
@@ -2071,10 +2048,6 @@ video {
2071
2048
  --tw-border-opacity: 1;
2072
2049
  border-color: rgb(230 230 230 / var(--tw-border-opacity));
2073
2050
  }
2074
- .hover\:tw-border-grayscale-500:hover {
2075
- --tw-border-opacity: 1;
2076
- border-color: rgb(204 204 204 / var(--tw-border-opacity));
2077
- }
2078
2051
  .hover\:tw-border-info-600:hover {
2079
2052
  --tw-border-opacity: 1;
2080
2053
  border-color: rgb(14 121 188 / var(--tw-border-opacity));
@@ -2111,10 +2084,6 @@ video {
2111
2084
  --tw-bg-opacity: 1;
2112
2085
  background-color: rgb(230 230 230 / var(--tw-bg-opacity));
2113
2086
  }
2114
- .hover\:tw-bg-grayscale-500:hover {
2115
- --tw-bg-opacity: 1;
2116
- background-color: rgb(204 204 204 / var(--tw-bg-opacity));
2117
- }
2118
2087
  .hover\:tw-bg-info-100:hover {
2119
2088
  --tw-bg-opacity: 1;
2120
2089
  background-color: rgb(207 234 251 / var(--tw-bg-opacity));
@@ -2139,10 +2108,6 @@ video {
2139
2108
  --tw-bg-opacity: 1;
2140
2109
  background-color: rgb(14 121 188 / var(--tw-bg-opacity));
2141
2110
  }
2142
- .hover\:tw-bg-primary-700:hover {
2143
- --tw-bg-opacity: 1;
2144
- background-color: rgb(10 91 141 / var(--tw-bg-opacity));
2145
- }
2146
2111
  .hover\:tw-bg-secondary-100:hover {
2147
2112
  --tw-bg-opacity: 1;
2148
2113
  background-color: rgb(223 226 230 / var(--tw-bg-opacity));
@@ -2159,10 +2124,6 @@ video {
2159
2124
  --tw-bg-opacity: 1;
2160
2125
  background-color: rgb(74 87 103 / var(--tw-bg-opacity));
2161
2126
  }
2162
- .hover\:tw-bg-secondary-700:hover {
2163
- --tw-bg-opacity: 1;
2164
- background-color: rgb(56 65 77 / var(--tw-bg-opacity));
2165
- }
2166
2127
  .hover\:tw-bg-success-100:hover {
2167
2128
  --tw-bg-opacity: 1;
2168
2129
  background-color: rgb(213 242 223 / var(--tw-bg-opacity));
@@ -2175,9 +2136,9 @@ video {
2175
2136
  --tw-bg-opacity: 1;
2176
2137
  background-color: rgb(194 140 34 / var(--tw-bg-opacity));
2177
2138
  }
2178
- .hover\:tw-text-primary-600:hover {
2139
+ .hover\:tw-text-grayscale-800:hover {
2179
2140
  --tw-text-opacity: 1;
2180
- color: rgb(14 121 188 / var(--tw-text-opacity));
2141
+ color: rgb(39 52 56 / var(--tw-text-opacity));
2181
2142
  }
2182
2143
  .hover\:tw-text-secondary-500:hover {
2183
2144
  --tw-text-opacity: 1;
@@ -2333,18 +2294,10 @@ video {
2333
2294
  }
2334
2295
  @media (min-width: 768px) {
2335
2296
 
2336
- .md\:tw-relative {
2337
- position: relative;
2338
- }
2339
-
2340
2297
  .md\:tw-right-6 {
2341
2298
  right: 1.5rem;
2342
2299
  }
2343
2300
 
2344
- .md\:tw-order-first {
2345
- order: -9999;
2346
- }
2347
-
2348
2301
  .md\:tw-mb-0 {
2349
2302
  margin-bottom: 0px;
2350
2303
  }
@@ -2409,10 +2362,6 @@ video {
2409
2362
  width: 6rem;
2410
2363
  }
2411
2364
 
2412
- .md\:tw-w-3\/4 {
2413
- width: 75%;
2414
- }
2415
-
2416
2365
  .md\:tw-w-auto {
2417
2366
  width: auto;
2418
2367
  }
@@ -2425,10 +2374,6 @@ video {
2425
2374
  max-width: 100%;
2426
2375
  }
2427
2376
 
2428
- .md\:tw-flex-1 {
2429
- flex: 1 1 0%;
2430
- }
2431
-
2432
2377
  .md\:tw-grow {
2433
2378
  flex-grow: 1;
2434
2379
  }
@@ -2488,10 +2433,6 @@ video {
2488
2433
  object-fit: contain;
2489
2434
  }
2490
2435
 
2491
- .md\:tw-p-0 {
2492
- padding: 0px;
2493
- }
2494
-
2495
2436
  .md\:tw-p-2 {
2496
2437
  padding: 0.5rem;
2497
2438
  }
@@ -2528,10 +2469,6 @@ video {
2528
2469
  padding-bottom: 0.5rem;
2529
2470
  }
2530
2471
 
2531
- .md\:tw-pb-0 {
2532
- padding-bottom: 0px;
2533
- }
2534
-
2535
2472
  .md\:tw-pl-4 {
2536
2473
  padding-left: 1rem;
2537
2474
  }
@@ -0,0 +1,382 @@
1
+ # DY-957 Phase 2: fe-shared-lib Wizard Changes
2
+
3
+ ## Goal
4
+
5
+ Add an optional keywords step to the BYO upload wizard in `@designcrowd/fe-shared-lib`. The step fetches AI keyword suggestions, displays them in an editable textbox, and emits the keywords for the consumer to use. The step is opt-in via a `showKeywordsStep` prop (default `false`).
6
+
7
+ ## Repo
8
+
9
+ `fe-shared-lib`
10
+
11
+ ## Prerequisites
12
+
13
+ - Phase 1 backend endpoint deployed (needed for integration testing, not for Storybook development)
14
+
15
+ ## Implementation Decisions (Confirmed)
16
+
17
+ - The keyword suggestions endpoint path is `/api/ai-keyword-suggestion` (this is intentionally **not** maker-prefixed).
18
+ - `templateType` is required end-to-end; consumers must pass a non-null default value.
19
+ - Continue action in the keywords step must be disabled while suggestions are loading.
20
+ - Storybook coverage for `LogoKeywords` is required for this task (not optional).
21
+ - Publishing/version bump and final validation are human-owned follow-up steps, not coding-agent scope.
22
+
23
+ ---
24
+
25
+ ## Tasks
26
+
27
+ ### 1. Add API constant
28
+
29
+ **File**: `src/experiences/constants/api.js`
30
+
31
+ Add one line to the exported object:
32
+
33
+ ```js
34
+ KEYWORD_SUGGESTIONS_API_URL: '/api/ai-keyword-suggestion',
35
+ ```
36
+
37
+ ### 2. Add `getKeywordSuggestionsAsync` to API client
38
+
39
+ **File**: `src/experiences/clients/brand-crowd-api.client.js`
40
+
41
+ Add a new async method following the `searchDomainNamesByKeywordAsync` pattern (lines 71-117):
42
+
43
+ ```js
44
+ const getKeywordSuggestionsAsync = async ({ businessName, templateType }) => {
45
+ try {
46
+ const url = `${API.KEYWORD_SUGGESTIONS_API_URL}?businessName=${encodeURIComponent(businessName)}&templateType=${encodeURIComponent(templateType)}`;
47
+ const response = await getAxios()({
48
+ method: 'get',
49
+ url,
50
+ });
51
+ return response.data;
52
+ } catch (error) {
53
+ return false;
54
+ }
55
+ };
56
+ ```
57
+
58
+ Behavior note: if `businessName` is empty, still call the endpoint with `businessName=` and continue normally (expected to return empty suggestions).
59
+
60
+ Add `getKeywordSuggestionsAsync` to the default export object (lines 119-124).
61
+
62
+ ### 3. Create `LogoKeywords.vue` component
63
+
64
+ **File**: Create `src/experiences/components/UploadYourLogoOnBoarding/LogoKeywords.vue`
65
+
66
+ Clone the structure from `LogoBusinessText.vue` (same directory). The component should:
67
+
68
+ **Props:**
69
+ - `progressLabel` (String, required) — "Step X of Y"
70
+ - `eventCategory` (String, required) — for analytics tracking
71
+ - `savedKeywords` (String, optional, default: `null`) — restored keywords if user navigated back then forward
72
+ - `businessName` (String, optional, default: `''`) — from previous step (may be empty)
73
+ - `templateType` (String, required) — e.g. "business card"
74
+
75
+ Behavior note: keep current wizard behavior where empty business name is allowed; do not block navigation if no business name is entered.
76
+
77
+ **Data:**
78
+ - `keywordsText: ''` — the editable comma-separated keywords string
79
+ - `isLoading: false`
80
+
81
+ **Mounted lifecycle:**
82
+ ```js
83
+ async mounted() {
84
+ if (this.savedKeywords !== null) {
85
+ this.keywordsText = this.savedKeywords;
86
+ return;
87
+ }
88
+ this.isLoading = true;
89
+ try {
90
+ const result = await brandCrowdClient.getKeywordSuggestionsAsync({
91
+ businessName: this.businessName,
92
+ templateType: this.templateType,
93
+ });
94
+ if (result && result.keywords && result.keywords.length > 0) {
95
+ this.keywordsText = result.keywords.join(', ');
96
+ }
97
+ } catch {
98
+ // leave keywordsText as empty string
99
+ } finally {
100
+ this.isLoading = false;
101
+ }
102
+ },
103
+ ```
104
+
105
+ **Methods:**
106
+ - `back()` — emits `on-go-back`
107
+ - `save()` — no-op if `isLoading` is true; otherwise tracks event with `eventAction: 'keywords'`, `eventLabel: 'Clicked_Continue'`, then emits `on-save` with `{ keywords: this.keywordsText }`
108
+ - `onKeywordsKeyUp(e)` — if `e.key === 'Enter'` and `!isLoading`, call `this.save()`
109
+
110
+ **Template:** Mirror `LogoBusinessText.vue` layout:
111
+ - Progress label header: `{{ progressLabel }}`
112
+ - Heading: `{{ uploadYourLogoTr('keywords') }}`
113
+ - Description: `{{ uploadYourLogoTr('keywordsDescription') }}`
114
+ - Single `<input>` or `<textarea>` bound to `keywordsText`, disabled while `isLoading`, with `@keyup="onKeywordsKeyUp"`
115
+ - Show a loading indicator (spinner or skeleton) when `isLoading` is true
116
+ - Footer with Back button (emits `on-go-back`) and Continue button (calls `save()`)
117
+ - Continue button must be disabled while `isLoading` is `true`
118
+
119
+ **i18n:** Use the `uploadYourLogoTr` mixin (same as `LogoBusinessText.vue`).
120
+
121
+ ### 4. Add i18n translation keys
122
+
123
+ **Files**: All 7 locale files in `src/experiences/components/UploadYourLogoOnBoarding/i18n/`
124
+
125
+ Add two new keys inside the `"uploadYourLogo"` object in **every** locale file, using the **English text** in all of them:
126
+
127
+ ```json
128
+ "keywords": "Keywords",
129
+ "keywordsDescription": "Add keywords related to your business and logo"
130
+ ```
131
+
132
+ Add this identical English text to:
133
+ - `upload-your-logo.json` (English)
134
+ - `upload-your-logo.de-DE.json`
135
+ - `upload-your-logo.es-ES.json`
136
+ - `upload-your-logo.fr-CA.json`
137
+ - `upload-your-logo.fr-FR.json`
138
+ - `upload-your-logo.pt-BR.json`
139
+ - `upload-your-logo.pt-PT.json`
140
+
141
+ **Do not translate** — a separate process will sweep the codebase and replace the English placeholders with proper translations.
142
+
143
+ After editing all files, run `npm run bundle-translation` to regenerate bundled translation files.
144
+
145
+ ### 5. Update `UploadYourLogoOnBoarding.vue` orchestrator
146
+
147
+ **File**: `src/experiences/components/UploadYourLogoOnBoarding/UploadYourLogoOnBoarding.vue`
148
+
149
+ #### 5a. Add new props (after existing props at lines 115-136)
150
+
151
+ ```js
152
+ showKeywordsStep: {
153
+ type: Boolean,
154
+ required: false,
155
+ default: false,
156
+ },
157
+ templateType: {
158
+ type: String,
159
+ required: true,
160
+ },
161
+ ```
162
+
163
+ Note: `templateType` must be provided by the consumer (fallback/defaulting happens in consumer app config, not in this component).
164
+
165
+ #### 5b. Update `data()` (lines 142-154)
166
+
167
+ Change `totalNumSteps` to be dynamic:
168
+ ```js
169
+ totalNumSteps: this.useDropzone
170
+ ? (this.showKeywordsStep ? 6 : 5)
171
+ : (this.showKeywordsStep ? 5 : 4),
172
+ ```
173
+
174
+ Add new data property:
175
+ ```js
176
+ savedKeywords: null,
177
+ ```
178
+
179
+ #### 5c. Add computed property for brand colours step number
180
+
181
+ ```js
182
+ brandColoursStep() {
183
+ return this.showKeywordsStep ? 5 : 4;
184
+ },
185
+ ```
186
+
187
+ #### 5d. Update `currentStepProgressLabel` (lines 157-163)
188
+
189
+ Replace hard-coded totals with `this.totalNumSteps`:
190
+ ```js
191
+ currentStepProgressLabel() {
192
+ if (this.includeDropzoneInModal) {
193
+ const currentStepDisplay = this.currentStep === 0 ? 1 : this.currentStep;
194
+ return this.uploadYourLogoTr('stepOf', { CURRENT: currentStepDisplay, TOTAL: this.totalNumSteps });
195
+ }
196
+ return this.uploadYourLogoTr('stepOf', { CURRENT: this.currentStep - 1, TOTAL: this.totalNumSteps });
197
+ },
198
+ ```
199
+
200
+ #### 5e. Update `currentStepTrackingLabel` (lines 170-176)
201
+
202
+ Make it keyword-step aware:
203
+ ```js
204
+ currentStepTrackingLabel() {
205
+ const stepTwoLabel = this.currentStep === 2 && !this.canCropImage ? 'previewLogo' : 'cropLogo';
206
+ const steps = this.showKeywordsStep
207
+ ? ['logoUploader', stepTwoLabel, 'businessName', 'keywords', 'brandColors']
208
+ : ['logoUploader', stepTwoLabel, 'businessName', 'brandColors'];
209
+ return steps[this.currentStep - 1];
210
+ },
211
+ ```
212
+
213
+ #### 5f. Import LogoKeywords component
214
+
215
+ Add import at top of script:
216
+ ```js
217
+ import LogoKeywords from './LogoKeywords.vue';
218
+ ```
219
+
220
+ Register in `components: { ... }`.
221
+
222
+ #### 5g. Update `onSaveBusinessText` method (lines 251-255)
223
+
224
+ No change needed — it already sets `currentStep = 4`. When `showKeywordsStep` is true, step 4 shows LogoKeywords. When false, step 4 shows LogoBusinessBrandColours (via `brandColoursStep` computed).
225
+
226
+ #### 5h. Add `onSaveKeywords` method
227
+
228
+ ```js
229
+ onSaveKeywords(payload) {
230
+ this.$emit('on-save-keywords', { keywords: payload.keywords });
231
+ this.savedKeywords = payload.keywords;
232
+ this.currentStep = this.brandColoursStep;
233
+ },
234
+ ```
235
+
236
+ #### 5i. Add `onGoBackFromBrandColours` method (or rename existing `onGoBackToBusinessText` usage on brand colours)
237
+
238
+ ```js
239
+ onGoBackFromBrandColours() {
240
+ this.$emit('on-back', { currentStepTrackingLabel: this.currentStepTrackingLabel });
241
+ this.currentStep = this.showKeywordsStep ? 4 : 3;
242
+ },
243
+ ```
244
+
245
+ **Important**: The brand colours component currently uses `@on-go-back="onGoBackToBusinessText"` (which sets `currentStep = 3`). Change this to `@on-go-back="onGoBackFromBrandColours"` so it goes back to keywords (step 4) when the keywords step is enabled.
246
+
247
+ #### 5j. Update template — add LogoKeywords between BusinessText and BrandColours
248
+
249
+ Add after the LogoBusinessText block and before LogoBusinessBrandColours:
250
+
251
+ ```vue
252
+ <LogoKeywords
253
+ v-if="showKeywordsStep && !isAttemptingToExit && currentStep === 4 && uploadedLogoData"
254
+ :progress-label="currentStepProgressLabel"
255
+ :event-category="eventCategory"
256
+ :saved-keywords="savedKeywords"
257
+ :business-name="(savedBusinessText && savedBusinessText.businessText) || ''"
258
+ :template-type="templateType"
259
+ @on-go-back="onGoBackToBusinessText"
260
+ @on-save="onSaveKeywords"
261
+ />
262
+ ```
263
+
264
+ #### 5k. Update template — change BrandColours step condition
265
+
266
+ Change `currentStep === 4` to `currentStep === brandColoursStep`:
267
+
268
+ ```vue
269
+ <LogoBusinessBrandColours
270
+ v-if="!isAttemptingToExit && currentStep === brandColoursStep && uploadedLogoData && !isCurrentlySaving"
271
+ ...
272
+ @on-go-back="onGoBackFromBrandColours"
273
+ ...
274
+ />
275
+ ```
276
+
277
+ #### 5l. Update `reset()` method (lines 290-299)
278
+
279
+ Add:
280
+ ```js
281
+ this.savedKeywords = null;
282
+ ```
283
+
284
+ ### 6. Update `UploadYourLogoApplication.vue` wrapper
285
+
286
+ **File**: `src/experiences/components/UploadYourLogoApplication/UploadYourLogoApplication.vue`
287
+
288
+ #### 6a. Add pass-through props (after existing props at lines 29-45)
289
+
290
+ ```js
291
+ showKeywordsStep: {
292
+ type: Boolean,
293
+ required: false,
294
+ default: false,
295
+ },
296
+ templateType: {
297
+ type: String,
298
+ required: true,
299
+ },
300
+ ```
301
+
302
+ Note: `templateType` must be provided by the consumer (fallback/defaulting happens in consumer app config, not in this component).
303
+
304
+ #### 6b. Pass props to `UploadYourLogoOnBoarding` in template
305
+
306
+ Add to the existing `<UploadYourLogoOnBoarding>` tag:
307
+ ```vue
308
+ :show-keywords-step="showKeywordsStep"
309
+ :template-type="templateType"
310
+ @on-save-keywords="onSaveKeywords"
311
+ ```
312
+
313
+ #### 6c. Add event handler method
314
+
315
+ ```js
316
+ onSaveKeywords(payload) {
317
+ this.$emit('on-save-keywords', payload);
318
+ },
319
+ ```
320
+
321
+ ### 7. Add Storybook story (required)
322
+
323
+ **File**: Create `src/experiences/components/UploadYourLogoOnBoarding/LogoKeywords.stories.js`
324
+
325
+ Create a story following standard Storybook patterns covering:
326
+ - Loading state (mock delayed API)
327
+ - Populated state (mock successful response with keywords)
328
+ - Empty state (mock failed response)
329
+ - Pre-populated state (savedKeywords prop set)
330
+
331
+ ### 8. Publish package
332
+
333
+ After all changes are verified (human release process; **not** a coding-agent task):
334
+ 1. Bump package version in `package.json`
335
+ 2. Run `npm run bundle-translation` to regenerate i18n bundles
336
+ 3. Perform manual validation in Storybook/integration context (no automated test runner in this repo)
337
+ 4. Publish the new version of `@designcrowd/fe-shared-lib`
338
+
339
+ ---
340
+
341
+ ## Step Number Reference Table
342
+
343
+ ### When `showKeywordsStep = false` (default, unchanged):
344
+
345
+ | currentStep | Component | Progress Display |
346
+ |---|---|---|
347
+ | 1 | LogoUploader | Step 0 of 4 |
348
+ | 2 | LogoCropper/Preview | Step 1 of 4 |
349
+ | 3 | LogoBusinessText | Step 2 of 4 |
350
+ | 4 | LogoBusinessBrandColours | Step 3 of 4 |
351
+
352
+ ### When `showKeywordsStep = true`:
353
+
354
+ | currentStep | Component | Progress Display |
355
+ |---|---|---|
356
+ | 1 | LogoUploader | Step 0 of 5 |
357
+ | 2 | LogoCropper/Preview | Step 1 of 5 |
358
+ | 3 | LogoBusinessText | Step 2 of 5 |
359
+ | **4** | **LogoKeywords (NEW)** | **Step 3 of 5** |
360
+ | 5 | LogoBusinessBrandColours | Step 4 of 5 |
361
+
362
+ Note: The `useDropzone` variant adds step 0 (dropzone) and shifts totals by +1. The same logic applies — `totalNumSteps` handles this via the ternary in `data()`.
363
+
364
+ ---
365
+
366
+ ## Files Changed
367
+
368
+ | File | Change |
369
+ |---|---|
370
+ | `src/experiences/constants/api.js` | Add `KEYWORD_SUGGESTIONS_API_URL` |
371
+ | `src/experiences/clients/brand-crowd-api.client.js` | Add `getKeywordSuggestionsAsync` method + export |
372
+ | `src/experiences/components/UploadYourLogoOnBoarding/LogoKeywords.vue` | **New file** — keywords step component |
373
+ | `src/experiences/components/UploadYourLogoOnBoarding/UploadYourLogoOnBoarding.vue` | New props, step routing, computed properties, methods, template |
374
+ | `src/experiences/components/UploadYourLogoApplication/UploadYourLogoApplication.vue` | Pass-through props + event |
375
+ | `src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.json` | Add `keywords`, `keywordsDescription` |
376
+ | `src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.de-DE.json` | Add `keywords`, `keywordsDescription` (English placeholder) |
377
+ | `src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.es-ES.json` | Add `keywords`, `keywordsDescription` (English placeholder) |
378
+ | `src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.fr-CA.json` | Add `keywords`, `keywordsDescription` (English placeholder) |
379
+ | `src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.fr-FR.json` | Add `keywords`, `keywordsDescription` (English placeholder) |
380
+ | `src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.pt-BR.json` | Add `keywords`, `keywordsDescription` (English placeholder) |
381
+ | `src/experiences/components/UploadYourLogoOnBoarding/i18n/upload-your-logo.pt-PT.json` | Add `keywords`, `keywordsDescription` (English placeholder) |
382
+ | `src/experiences/components/UploadYourLogoOnBoarding/LogoKeywords.stories.js` | **New file** (required) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@designcrowd/fe-shared-lib",
3
- "version": "1.5.34",
3
+ "version": "1.5.36",
4
4
  "scripts": {
5
5
  "start": "run-p storybook watch:translation",
6
6
  "build": "npm run build:css --production",