@awes-io/ui 2.143.0 → 2.144.1

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 (89) hide show
  1. package/assets/css/components/action-card.css +1 -0
  2. package/assets/css/components/action-icon.css +2 -2
  3. package/assets/css/components/alert.css +49 -33
  4. package/assets/css/components/animation.css +14 -0
  5. package/assets/css/components/badge.css +1 -0
  6. package/assets/css/components/banner-text.css +15 -4
  7. package/assets/css/components/button.css +8 -0
  8. package/assets/css/components/card.css +0 -1
  9. package/assets/css/components/content-placeholder.css +4 -3
  10. package/assets/css/components/dropdown.css +20 -7
  11. package/assets/css/components/icon-menu-item.css +12 -7
  12. package/assets/css/components/mobile-menu-nav.css +8 -4
  13. package/assets/css/components/text-field.css +4 -0
  14. package/assets/css/main.css +1 -0
  15. package/components/1_atoms/AwActionIcon.vue +11 -2
  16. package/components/1_atoms/AwFlow.vue +26 -11
  17. package/components/1_atoms/AwGrid.vue +11 -3
  18. package/components/1_atoms/AwIcon/AwIcon.vue +5 -3
  19. package/components/1_atoms/AwIcon/AwIconSystemMono.vue +3 -2
  20. package/components/1_atoms/AwInput.vue +2 -2
  21. package/components/1_atoms/AwList.vue +3 -1
  22. package/components/1_atoms/AwRadio.vue +1 -1
  23. package/components/1_atoms/AwSlider.vue +15 -1
  24. package/components/1_atoms/AwTag.vue +6 -1
  25. package/components/2_molecules/AwAlert.vue +67 -41
  26. package/components/2_molecules/AwBadge.vue +1 -1
  27. package/components/2_molecules/AwBannerText.vue +8 -2
  28. package/components/2_molecules/AwButton.vue +1 -0
  29. package/components/2_molecules/AwDescriptionInput.vue +19 -1
  30. package/components/3_organisms/AwBottomBar.vue +22 -4
  31. package/components/3_organisms/AwSubnav.vue +11 -1
  32. package/components/3_organisms/AwTable/AwTableBuilder.vue +8 -0
  33. package/components/3_organisms/AwTable/_AwTableCellDropdown.vue +6 -1
  34. package/components/3_organisms/AwTable/_AwTableRow.vue +2 -1
  35. package/components/4_pages/AwPage.vue +1 -0
  36. package/components/5_layouts/_AwMenuItemIcon.vue +9 -2
  37. package/components/5_layouts/_AwMobileMenuItem.vue +5 -3
  38. package/components/_config.js +26 -1
  39. package/docs/components/atoms/aw-accordion-fold.md +55 -17
  40. package/docs/components/atoms/aw-action-card-body.md +37 -5
  41. package/docs/components/atoms/aw-action-card.md +47 -11
  42. package/docs/components/atoms/aw-action-icon.md +50 -12
  43. package/docs/components/atoms/aw-card.md +35 -10
  44. package/docs/components/atoms/aw-checkbox.md +176 -0
  45. package/docs/components/atoms/aw-content-placeholder.md +33 -2
  46. package/docs/components/atoms/aw-dock.md +20 -14
  47. package/docs/components/atoms/aw-dropdown.md +72 -22
  48. package/docs/components/atoms/aw-flow.md +71 -23
  49. package/docs/components/atoms/aw-grid.md +30 -12
  50. package/docs/components/atoms/aw-icon-system-color.md +1 -0
  51. package/docs/components/atoms/aw-icon-system-mono.md +1 -0
  52. package/docs/components/atoms/aw-icon.md +3 -3
  53. package/docs/components/atoms/aw-info.md +38 -0
  54. package/docs/components/atoms/aw-input.md +94 -2
  55. package/docs/components/atoms/aw-label.md +62 -9
  56. package/docs/components/atoms/aw-link.md +61 -9
  57. package/docs/components/atoms/aw-list.md +68 -4
  58. package/docs/components/atoms/aw-progress.md +52 -3
  59. package/docs/components/atoms/aw-radio.md +73 -0
  60. package/docs/components/atoms/aw-select-native.md +128 -0
  61. package/docs/components/atoms/aw-slider.md +114 -7
  62. package/docs/components/atoms/aw-switcher.md +77 -0
  63. package/docs/components/atoms/aw-tag.md +67 -3
  64. package/docs/components/atoms/aw-toggler.md +22 -1
  65. package/docs/components/molecules/aw-action-button.md +58 -11
  66. package/docs/components/molecules/aw-alert.md +154 -32
  67. package/docs/components/molecules/aw-badge.md +31 -10
  68. package/docs/components/molecules/aw-banner-text.md +84 -18
  69. package/docs/components/molecules/aw-button-nav.md +65 -0
  70. package/docs/components/molecules/aw-button.md +76 -6
  71. package/docs/components/molecules/aw-description-input.md +63 -6
  72. package/docs/components/molecules/aw-empty-container.md +172 -23
  73. package/docs/components/molecules/aw-island.md +282 -10
  74. package/docs/components/organisms/aw-filter-select.md +14 -0
  75. package/docs/components/pages/aw-page-single.md +47 -0
  76. package/docs/guides/page-patterns/detail-pages.md +89 -26
  77. package/docs/guides/page-patterns/list-pages.md +158 -10
  78. package/docs/reference/colors.md +232 -0
  79. package/docs/reference/icons.md +163 -0
  80. package/docs/reference/troubleshooting.md +19 -0
  81. package/package.json +2 -2
  82. package/store/awesIo.js +11 -0
  83. package/CHANGELOG.md +0 -4544
  84. package/dist/css/aw-icons.css +0 -26
  85. package/dist/fonts/aw-icons.svg +0 -18
  86. package/dist/fonts/aw-icons.ttf +0 -0
  87. package/dist/fonts/aw-icons.woff +0 -0
  88. package/dist/fonts/aw-icons.woff2 +0 -0
  89. package/nuxt/icons.css +0 -26
@@ -137,6 +137,13 @@ The primary action button (Save, Create, etc.) MUST use the `:action` prop and `
137
137
  />
138
138
  </AwGrid>
139
139
  </AwCard>
140
+
141
+ <AwCard title="Profile picture">
142
+ <AwImageUpload
143
+ v-model="customer.avatar"
144
+ @loading="customer.saving = $event"
145
+ />
146
+ </AwCard>
140
147
  </AwPageSingle>
141
148
  </template>
142
149
 
@@ -177,7 +184,7 @@ export default {
177
184
  await this.customer.fetch()
178
185
  } catch (error) {
179
186
  this.$notify({
180
- message: 'Customer not found',
187
+ title: 'Customer not found',
181
188
  type: 'error'
182
189
  })
183
190
  this.$router.push(`/${this.$route.params.shop_uuid}/customers`)
@@ -191,14 +198,14 @@ export default {
191
198
  await this.customer.save()
192
199
 
193
200
  this.$notify({
194
- message: `Customer ${this.customer.isNew() ? 'created' : 'updated'} successfully`,
201
+ title: `Customer ${this.customer.isNew() ? 'created' : 'updated'} successfully`,
195
202
  type: 'success'
196
203
  })
197
204
 
198
205
  this.$router.push(`/${this.$route.params.shop_uuid}/customers`)
199
206
  } catch (error) {
200
207
  this.$notify({
201
- message: 'Failed to save customer',
208
+ title: 'Failed to save customer',
202
209
  type: 'error'
203
210
  })
204
211
  }
@@ -208,13 +215,70 @@ export default {
208
215
  </script>
209
216
  ```
210
217
 
211
- **What happens:**
212
- 1. ✅ Model initialized with ID from route params
213
- 2. If editing, `fetch()` loads existing data
214
- 3. ✅ Form fields bound to model properties
215
- 4. Save button shows loading state via `customer.saving`
216
- 5. ✅ Validation errors display next to fields
217
- 6. Success redirects to list page
218
+ ## Preview Slot Patterns
219
+
220
+ We have two standard patterns for the `#preview` slot in `AwPageSingle` to maintain visual consistency across the application.
221
+
222
+ ### Human Entities (Users, Customers)
223
+
224
+ Used for people. Displays a large avatar and the person's name.
225
+
226
+ ```markup
227
+ <template #preview>
228
+ <AwCard class="text-center">
229
+ <AwAvatar
230
+ :src="user.avatar"
231
+ :name="userName"
232
+ size="240"
233
+ class="mx-auto"
234
+ />
235
+ <AwHeadline class="mt-4">
236
+ {{ userName }}
237
+ </AwHeadline>
238
+ </AwCard>
239
+ </template>
240
+ ```
241
+
242
+ ### Non-Human Entities (Services, Products, Locations)
243
+
244
+ Used for items with a representative image. Displays a large image with rounded corners.
245
+
246
+ ```markup
247
+ <template #preview>
248
+ <AwCard style="--card-padding-x: 0.5rem; --card-padding-y: 0.5rem;">
249
+ <AwActionIcon
250
+ :size="380"
251
+ :image="item.image ? { src: item.image, alt: item.name } : null"
252
+ icon="awesio/image"
253
+ icon-color="mono-400"
254
+ color="mono-800"
255
+ class="w-full"
256
+ style="--icon-size: 48px; --radius: 0.5rem;"
257
+ />
258
+ <AwHeadline class="p-4 mt-2">
259
+ {{ item.name }}
260
+ </AwHeadline>
261
+ </AwCard>
262
+ </template>
263
+ ```
264
+
265
+ ## Image Uploads
266
+
267
+ For entities with images (avatars or item photos), provide an `AwImageUpload` component in its own `AwCard`.
268
+
269
+ ```markup
270
+ <AwCard title="Profile picture">
271
+ <AwImageUpload
272
+ v-model="user.avatar"
273
+ @loading="user.saving = $event"
274
+ />
275
+ </AwCard>
276
+ ```
277
+
278
+ **Key Points:**
279
+ - ✅ Use a separate `AwCard` with an appropriate title.
280
+ - ✅ Bind `v-model` to the model's image/avatar property.
281
+ - ✅ Listen to `@loading` to set the model's `saving` state, preventing form submission during upload.
218
282
 
219
283
  ## Model Initialization
220
284
 
@@ -322,7 +386,7 @@ export default {
322
386
  await this.service.fetch()
323
387
  } catch (error) {
324
388
  this.$notify({
325
- message: 'Service not found',
389
+ title: 'Service not found',
326
390
  type: 'error'
327
391
  })
328
392
  this.$router.push('/services')
@@ -610,7 +674,7 @@ async save() {
610
674
  // Check if validation failed
611
675
  if (Object.keys(this.model.errors).length > 0) {
612
676
  this.$notify({
613
- message: 'Please fix validation errors',
677
+ title: 'Please fix validation errors',
614
678
  type: 'error'
615
679
  })
616
680
  return
@@ -618,7 +682,7 @@ async save() {
618
682
 
619
683
  // Success path
620
684
  this.$notify({
621
- message: 'Saved successfully',
685
+ title: 'Saved successfully',
622
686
  type: 'success'
623
687
  })
624
688
  this.$router.push('/list')
@@ -636,7 +700,7 @@ async mounted() {
636
700
  // 404 - Record not found
637
701
  if (error.response?.status === 404) {
638
702
  this.$notify({
639
- message: 'Record not found',
703
+ title: 'Record not found',
640
704
  type: 'error'
641
705
  })
642
706
  this.$router.push('/list')
@@ -645,7 +709,7 @@ async mounted() {
645
709
 
646
710
  // Other errors
647
711
  this.$notify({
648
- message: 'Failed to load data',
712
+ title: 'Failed to load data',
649
713
  type: 'error'
650
714
  })
651
715
  }
@@ -897,13 +961,13 @@ export default {
897
961
  try {
898
962
  await this.model.save()
899
963
  this.$notify({
900
- message: `Item ${this.model.isNew() ? 'created' : 'updated'} successfully`,
964
+ title: `Item ${this.model.isNew() ? 'created' : 'updated'} successfully`,
901
965
  type: 'success'
902
966
  })
903
967
  this.$router.push('/list')
904
968
  } catch (error) {
905
969
  this.$notify({
906
- message: 'Failed to save item',
970
+ title: 'Failed to save item',
907
971
  type: 'error'
908
972
  })
909
973
  }
@@ -918,7 +982,7 @@ export default {
918
982
 
919
983
  await this.model.delete()
920
984
  this.$notify({
921
- message: 'Item deleted successfully',
985
+ title: 'Item deleted successfully',
922
986
  type: 'success'
923
987
  })
924
988
  this.$router.push('/list')
@@ -928,7 +992,7 @@ export default {
928
992
  }
929
993
 
930
994
  this.$notify({
931
- message: 'Failed to delete item',
995
+ title: 'Failed to delete item',
932
996
  type: 'error'
933
997
  })
934
998
  }
@@ -1126,7 +1190,7 @@ export default {
1126
1190
  await this.template.fetch()
1127
1191
  } catch (error) {
1128
1192
  this.$notify({
1129
- message: 'Template not found',
1193
+ title: 'Template not found',
1130
1194
  type: 'error'
1131
1195
  })
1132
1196
  this.$router.push(`/${this.$route.params.shop_uuid}/templates`)
@@ -1140,14 +1204,14 @@ export default {
1140
1204
  await this.template.save()
1141
1205
 
1142
1206
  this.$notify({
1143
- message: `Template ${this.template.isNew() ? 'created' : 'updated'} successfully`,
1207
+ title: `Template ${this.template.isNew() ? 'created' : 'updated'} successfully`,
1144
1208
  type: 'success'
1145
1209
  })
1146
1210
 
1147
1211
  this.$router.push(`/${this.$route.params.shop_uuid}/templates`)
1148
1212
  } catch (error) {
1149
1213
  this.$notify({
1150
- message: 'Failed to save template',
1214
+ title: 'Failed to save template',
1151
1215
  type: 'error'
1152
1216
  })
1153
1217
  }
@@ -1347,7 +1411,7 @@ export default {
1347
1411
  await this.product.fetch()
1348
1412
  } catch (error) {
1349
1413
  this.$notify({
1350
- message: 'Product not found',
1414
+ title: 'Product not found',
1351
1415
  type: 'error'
1352
1416
  })
1353
1417
  this.$router.push(`/${this.$route.params.shop_uuid}/products`)
@@ -1366,14 +1430,14 @@ export default {
1366
1430
  await this.product.save()
1367
1431
 
1368
1432
  this.$notify({
1369
- message: `Product ${this.product.isNew() ? 'created' : 'updated'} successfully`,
1433
+ title: `Product ${this.product.isNew() ? 'created' : 'updated'} successfully`,
1370
1434
  type: 'success'
1371
1435
  })
1372
1436
 
1373
1437
  this.$router.push(`/${this.$route.params.shop_uuid}/products`)
1374
1438
  } catch (error) {
1375
1439
  this.$notify({
1376
- message: 'Failed to save product',
1440
+ title: 'Failed to save product',
1377
1441
  type: 'error'
1378
1442
  })
1379
1443
  }
@@ -1406,7 +1470,6 @@ export default {
1406
1470
  }
1407
1471
  }
1408
1472
  </script>
1409
- ```
1410
1473
 
1411
1474
  ## Best Practices
1412
1475
 
@@ -235,8 +235,8 @@ export default {
235
235
  computed: {
236
236
  statusOptions() {
237
237
  return [
238
- { id: true, title: 'Active' },
239
- { id: false, title: 'Inactive' }
238
+ { id: 'true', title: 'Active' },
239
+ { id: 'false', title: 'Inactive' }
240
240
  ]
241
241
  },
242
242
 
@@ -284,6 +284,154 @@ export default {
284
284
  - ✅ **Place search on the right** using `ml-auto` class
285
285
  - ✅ **Handle different value types** - Values can be strings, booleans, arrays, or comma-separated strings
286
286
 
287
+ ## Button Nav Filtering
288
+
289
+ For simple toggle filters (like Active/Inactive, All/Pending/Completed), use `AwButtonNav` with query parameters:
290
+
291
+ ### Basic Button Nav Filter
292
+
293
+ ```markup
294
+ <template>
295
+ <AwPage title="Services">
296
+ <AwButtonNav :items="filterItems" class="mb-6" />
297
+
298
+ <AwTableBuilder
299
+ :collection="services"
300
+ :watch-params="['is_active']"
301
+ >
302
+ <AwTableCol field="name" title="Service" />
303
+ <AwTableCol field="price" title="Price" />
304
+ </AwTableBuilder>
305
+ </AwPage>
306
+ </template>
307
+
308
+ <script>
309
+ import { BaseCollection } from '@awes-io/vue-mc'
310
+
311
+ class Services extends BaseCollection {
312
+ routes() {
313
+ return {
314
+ fetch: '/api/services'
315
+ }
316
+ }
317
+ }
318
+
319
+ export default {
320
+ data() {
321
+ return {
322
+ services: new Services()
323
+ }
324
+ },
325
+
326
+ computed: {
327
+ filterItems() {
328
+ return [
329
+ {
330
+ text: 'Active',
331
+ href: { query: { is_active: null } }
332
+ },
333
+ {
334
+ text: 'Inactive',
335
+ href: { query: { is_active: '0' } }
336
+ }
337
+ ]
338
+ }
339
+ }
340
+ }
341
+ </script>
342
+ ```
343
+
344
+ **How it works:**
345
+ 1. ✅ `AwButtonNav` items use `href: { query: { param: value } }` format
346
+ 2. ✅ Setting `param: null` removes the parameter from URL (shows all active items)
347
+ 3. ✅ Setting `param: '0'` adds `is_active=0` to URL (shows inactive items)
348
+ 4. ✅ `AwTableBuilder` watches `is_active` param via `:watch-params`
349
+ 5. ✅ Table automatically refetches when query parameter changes
350
+
351
+ ### Multiple Filter Options
352
+
353
+ For filters with more than two options:
354
+
355
+ ```markup
356
+ <template>
357
+ <AwPage title="Orders">
358
+ <AwButtonNav :items="statusFilters" class="mb-6" />
359
+
360
+ <AwTableBuilder
361
+ :collection="orders"
362
+ :watch-params="['status']"
363
+ >
364
+ <AwTableCol field="number" title="Order #" />
365
+ <AwTableCol field="total" title="Total" />
366
+ </AwTableBuilder>
367
+ </AwPage>
368
+ </template>
369
+
370
+ <script>
371
+ export default {
372
+ computed: {
373
+ statusFilters() {
374
+ return [
375
+ {
376
+ text: 'All',
377
+ href: { query: { status: null } }
378
+ },
379
+ {
380
+ text: 'Pending',
381
+ href: { query: { status: 'pending' } }
382
+ },
383
+ {
384
+ text: 'Completed',
385
+ href: { query: { status: 'completed' } }
386
+ },
387
+ {
388
+ text: 'Cancelled',
389
+ href: { query: { status: 'cancelled' } }
390
+ }
391
+ ]
392
+ }
393
+ }
394
+ }
395
+ </script>
396
+ ```
397
+
398
+ ### Button Nav with Icons
399
+
400
+ You can add icons to filter buttons:
401
+
402
+ ```markup
403
+ <template>
404
+ <AwButtonNav :items="filterItems" class="mb-6" />
405
+ </template>
406
+
407
+ <script>
408
+ export default {
409
+ computed: {
410
+ filterItems() {
411
+ return [
412
+ {
413
+ text: 'Active',
414
+ icon: 'awesio/check-circle',
415
+ href: { query: { is_active: null } }
416
+ },
417
+ {
418
+ text: 'Inactive',
419
+ icon: 'awesio/x-circle',
420
+ href: { query: { is_active: '0' } }
421
+ }
422
+ ]
423
+ }
424
+ }
425
+ }
426
+ </script>
427
+ ```
428
+
429
+ **Best Practices:**
430
+ - ✅ **Use for simple toggle filters** - Best for 2-4 options (Active/Inactive, All/Pending/Completed)
431
+ - ✅ **Use `null` to remove parameters** - Setting `param: null` removes it from URL, showing unfiltered results
432
+ - ✅ **Use `AwFilterSelect` for complex filters** - When you have many options or need multi-select, use `AwFilterSelect` instead
433
+ - ✅ **Combine with search** - Button nav filters work well alongside `AwSearch` for combined filtering
434
+
287
435
  ## Custom Column Rendering
288
436
 
289
437
  ### Understanding the `field` Prop
@@ -689,13 +837,13 @@ export default {
689
837
  try {
690
838
  await this.$axios.delete(`/api/templates/${item.id}`)
691
839
  this.$notify({
692
- message: 'Template deleted successfully',
840
+ title: 'Template deleted successfully',
693
841
  type: 'success'
694
842
  })
695
843
  this.templates.fetch() // Refetch list
696
844
  } catch (error) {
697
845
  this.$notify({
698
- message: 'Failed to delete template',
846
+ title: 'Failed to delete template',
699
847
  type: 'error'
700
848
  })
701
849
  }
@@ -960,13 +1108,13 @@ export default {
960
1108
  try {
961
1109
  await this.$axios.post(`/api/templates/${template.id}/duplicate`)
962
1110
  this.$notify({
963
- message: 'Template duplicated successfully',
1111
+ title: 'Template duplicated successfully',
964
1112
  type: 'success'
965
1113
  })
966
1114
  this.templates.fetch()
967
1115
  } catch (error) {
968
1116
  this.$notify({
969
- message: 'Failed to duplicate template',
1117
+ title: 'Failed to duplicate template',
970
1118
  type: 'error'
971
1119
  })
972
1120
  }
@@ -978,13 +1126,13 @@ export default {
978
1126
  is_active: !template.is_active
979
1127
  })
980
1128
  this.$notify({
981
- message: `Template ${template.is_active ? 'deactivated' : 'activated'}`,
1129
+ title: `Template ${template.is_active ? 'deactivated' : 'activated'}`,
982
1130
  type: 'success'
983
1131
  })
984
1132
  this.templates.fetch()
985
1133
  } catch (error) {
986
1134
  this.$notify({
987
- message: 'Failed to update template',
1135
+ title: 'Failed to update template',
988
1136
  type: 'error'
989
1137
  })
990
1138
  }
@@ -1001,13 +1149,13 @@ export default {
1001
1149
  try {
1002
1150
  await this.$axios.delete(`/api/templates/${template.id}`)
1003
1151
  this.$notify({
1004
- message: 'Template deleted successfully',
1152
+ title: 'Template deleted successfully',
1005
1153
  type: 'success'
1006
1154
  })
1007
1155
  this.templates.fetch()
1008
1156
  } catch (error) {
1009
1157
  this.$notify({
1010
- message: 'Failed to delete template',
1158
+ title: 'Failed to delete template',
1011
1159
  type: 'error'
1012
1160
  })
1013
1161
  }