@awes-io/ui 2.142.0 → 2.143.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.
Files changed (239) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/assets/css/components/_index.css +7 -1
  3. package/assets/css/components/animation.css +38 -32
  4. package/assets/css/components/content-placeholder.css +103 -0
  5. package/assets/css/components/empty-container.css +69 -1
  6. package/assets/css/components/filter-chosen.css +6 -0
  7. package/assets/css/components/filter-date-range.css +17 -1
  8. package/assets/css/components/filter-month.css +23 -17
  9. package/assets/css/components/filter-select.css +11 -0
  10. package/assets/css/components/layout.css +1 -32
  11. package/assets/css/components/modal.css +1 -1
  12. package/assets/css/components/number.css +12 -0
  13. package/assets/css/components/page-aside.css +54 -0
  14. package/assets/js/css.js +1 -1
  15. package/assets/js/icons/mono.js +59 -91
  16. package/assets/js/icons/multicolor.js +1 -31
  17. package/components/1_atoms/AwContentPlaceholder.vue +60 -0
  18. package/components/1_atoms/AwFlow.vue +21 -48
  19. package/components/1_atoms/AwLabel.vue +1 -1
  20. package/components/2_molecules/AwButton.vue +1 -1
  21. package/components/2_molecules/AwEmptyContainer.vue +74 -72
  22. package/components/2_molecules/AwNumber.vue +180 -0
  23. package/components/2_molecules/AwSelect.vue +11 -4
  24. package/components/3_organisms/AwFilterChosen.vue +73 -0
  25. package/components/3_organisms/AwFilterDateRange.vue +177 -0
  26. package/components/3_organisms/AwFilterMonth.vue +37 -40
  27. package/components/3_organisms/AwFilterSelect.vue +368 -0
  28. package/components/3_organisms/AwImageUpload.vue +1 -1
  29. package/components/3_organisms/AwMarkdownEditor.vue +0 -0
  30. package/components/3_organisms/AwMultiBlockBuilder.vue +1 -1
  31. package/components/3_organisms/AwTable/AwTableBuilder.vue +12 -60
  32. package/components/4_pages/AwPageAside.vue +108 -0
  33. package/components/5_layouts/AwLayoutCenter.vue +3 -8
  34. package/components/5_layouts/_AwUserMenu.vue +1 -1
  35. package/dist/css/aw-icons.css +26 -0
  36. package/dist/fonts/aw-icons.svg +18 -0
  37. package/dist/fonts/aw-icons.ttf +0 -0
  38. package/dist/fonts/aw-icons.woff +0 -0
  39. package/dist/fonts/aw-icons.woff2 +0 -0
  40. package/docs/_template.md +80 -0
  41. package/docs/components/atoms/aw-accordion-fold.md +91 -0
  42. package/docs/components/atoms/aw-action-card-body.md +67 -0
  43. package/docs/components/atoms/aw-action-card.md +94 -0
  44. package/docs/components/atoms/aw-action-icon.md +88 -0
  45. package/docs/components/atoms/aw-avatar.md +106 -0
  46. package/docs/components/atoms/aw-card.md +112 -0
  47. package/docs/components/atoms/aw-checkbox.md +112 -0
  48. package/docs/components/atoms/aw-content-placeholder.md +116 -0
  49. package/docs/components/atoms/aw-description.md +83 -0
  50. package/docs/components/atoms/aw-dock.md +84 -0
  51. package/docs/components/atoms/aw-dropdown-button.md +94 -0
  52. package/docs/components/atoms/aw-dropdown.md +128 -0
  53. package/docs/components/atoms/aw-file.md +73 -0
  54. package/docs/components/atoms/aw-flow.md +92 -0
  55. package/docs/components/atoms/aw-grid.md +91 -0
  56. package/docs/components/atoms/aw-headline.md +71 -0
  57. package/docs/components/atoms/aw-icon-system-color.md +121 -0
  58. package/docs/components/atoms/aw-icon-system-mono.md +205 -0
  59. package/docs/components/atoms/aw-icon.md +235 -0
  60. package/docs/components/atoms/aw-info.md +85 -0
  61. package/docs/components/atoms/aw-input.md +120 -0
  62. package/docs/components/atoms/aw-label.md +83 -0
  63. package/docs/components/atoms/aw-link.md +99 -0
  64. package/docs/components/atoms/aw-list.md +88 -0
  65. package/docs/components/atoms/aw-progress.md +70 -0
  66. package/docs/components/atoms/aw-radio.md +109 -0
  67. package/docs/components/atoms/aw-refresh-wrapper.md +81 -0
  68. package/docs/components/atoms/aw-select-native.md +106 -0
  69. package/docs/components/atoms/aw-slider.md +82 -0
  70. package/docs/components/atoms/aw-sub-headline.md +73 -0
  71. package/docs/components/atoms/aw-switcher.md +115 -0
  72. package/docs/components/atoms/aw-tag.md +80 -0
  73. package/docs/components/atoms/aw-title.md +70 -0
  74. package/docs/components/atoms/aw-toggler.md +69 -0
  75. package/docs/components/layouts/aw-layout-center.md +168 -0
  76. package/docs/components/layouts/aw-layout-error.md +153 -0
  77. package/docs/components/layouts/aw-layout-provider.md +238 -0
  78. package/docs/components/layouts/aw-layout.md +88 -0
  79. package/docs/components/molecules/aw-action-button.md +91 -0
  80. package/docs/components/molecules/aw-alert.md +96 -0
  81. package/docs/components/molecules/aw-badge.md +108 -0
  82. package/docs/components/molecules/aw-banner-text.md +90 -0
  83. package/docs/components/molecules/aw-button-nav.md +46 -0
  84. package/docs/components/molecules/aw-button.md +123 -0
  85. package/docs/components/molecules/aw-description-input.md +67 -0
  86. package/docs/components/molecules/aw-empty-container.md +86 -0
  87. package/docs/components/molecules/aw-island.md +234 -0
  88. package/docs/components/molecules/aw-number.md +138 -0
  89. package/docs/components/molecules/aw-select-object.md +401 -0
  90. package/docs/components/molecules/aw-select.md +215 -0
  91. package/docs/components/molecules/aw-tab-nav.md +108 -0
  92. package/docs/components/molecules/aw-tel.md +129 -0
  93. package/docs/components/molecules/aw-textarea.md +83 -0
  94. package/docs/components/molecules/aw-userpic.md +115 -0
  95. package/docs/components/organisms/aw-address-block.md +64 -0
  96. package/docs/components/organisms/aw-address.md +132 -0
  97. package/docs/components/organisms/aw-birthday-picker.md +73 -0
  98. package/docs/components/organisms/aw-bottom-bar.md +66 -0
  99. package/docs/components/organisms/aw-calendar-days.md +115 -0
  100. package/docs/components/organisms/aw-calendar-nav.md +98 -0
  101. package/docs/components/organisms/aw-calendar-view.md +98 -0
  102. package/docs/components/organisms/aw-calendar.md +166 -0
  103. package/docs/components/organisms/aw-chart.md +154 -0
  104. package/docs/components/organisms/aw-chip-select.md +164 -0
  105. package/docs/components/organisms/aw-chip.md +126 -0
  106. package/docs/components/organisms/aw-code-snippet.md +94 -0
  107. package/docs/components/organisms/aw-code.md +132 -0
  108. package/docs/components/organisms/aw-context-menu.md +117 -0
  109. package/docs/components/organisms/aw-cropper.md +151 -0
  110. package/docs/components/organisms/aw-date.md +161 -0
  111. package/docs/components/organisms/aw-display-date.md +33 -0
  112. package/docs/components/organisms/aw-download-link.md +46 -0
  113. package/docs/components/organisms/aw-fetch-data.md +161 -0
  114. package/docs/components/organisms/aw-filter-chosen.md +226 -0
  115. package/docs/components/organisms/aw-filter-date-range.md +205 -0
  116. package/docs/components/organisms/aw-filter-month.md +43 -0
  117. package/docs/components/organisms/aw-filter-select.md +225 -0
  118. package/docs/components/organisms/aw-form.md +174 -0
  119. package/docs/components/organisms/aw-gmap-marker.md +86 -0
  120. package/docs/components/organisms/aw-gmap.md +90 -0
  121. package/docs/components/organisms/aw-image-upload.md +56 -0
  122. package/docs/components/organisms/aw-island-avatar.md +87 -0
  123. package/docs/components/organisms/aw-markdown-editor.md +104 -0
  124. package/docs/components/organisms/aw-modal-buttons.md +57 -0
  125. package/docs/components/organisms/aw-modal.md +246 -0
  126. package/docs/components/organisms/aw-model-edit.md +74 -0
  127. package/docs/components/organisms/aw-money.md +53 -0
  128. package/docs/components/organisms/aw-multi-block-builder.md +165 -0
  129. package/docs/components/organisms/aw-pagination.md +121 -0
  130. package/docs/components/organisms/aw-password.md +103 -0
  131. package/docs/components/organisms/aw-preview-card.md +45 -0
  132. package/docs/components/organisms/aw-search.md +116 -0
  133. package/docs/components/organisms/aw-subnav.md +122 -0
  134. package/docs/components/organisms/aw-table-builder.md +165 -0
  135. package/docs/components/organisms/aw-table-col.md +123 -0
  136. package/docs/components/organisms/aw-table-head.md +92 -0
  137. package/docs/components/organisms/aw-table-row.md +91 -0
  138. package/docs/components/organisms/aw-table.md +172 -0
  139. package/docs/components/organisms/aw-tags.md +54 -0
  140. package/docs/components/organisms/aw-toggle-show-aside.md +43 -0
  141. package/docs/components/organisms/aw-uploader-files.md +125 -0
  142. package/docs/components/organisms/aw-uploader.md +163 -0
  143. package/docs/components/organisms/aw-user-menu.md +87 -0
  144. package/docs/components/pages/aw-page-aside.md +296 -0
  145. package/docs/components/pages/aw-page-menu-buttons.md +172 -0
  146. package/docs/components/pages/aw-page-modal.md +198 -0
  147. package/docs/components/pages/aw-page-single.md +253 -0
  148. package/docs/components/pages/aw-page.md +194 -0
  149. package/docs/configuration.md +493 -0
  150. package/docs/cookbook/advanced-patterns.md +1388 -0
  151. package/docs/cookbook/common-patterns.md +965 -0
  152. package/docs/cookbook/index.md +786 -0
  153. package/docs/getting-started.md +596 -0
  154. package/docs/guides/best-practices.md +1106 -0
  155. package/docs/guides/data-fetching.md +852 -0
  156. package/docs/guides/error-handling.md +1172 -0
  157. package/docs/guides/forms-guide.md +1329 -0
  158. package/docs/guides/mobile-subnavigation.md +359 -0
  159. package/docs/guides/page-patterns/aside-pages.md +1418 -0
  160. package/docs/guides/page-patterns/dashboard-pages.md +990 -0
  161. package/docs/guides/page-patterns/detail-pages.md +1493 -0
  162. package/docs/guides/page-patterns/list-pages.md +1094 -0
  163. package/docs/index.md +263 -1
  164. package/docs/integrations.md +870 -0
  165. package/docs/reference/menu.md +462 -0
  166. package/docs/reference/plugins.md +970 -0
  167. package/docs/reference/troubleshooting.md +945 -0
  168. package/nuxt/awes.config.js +9 -8
  169. package/nuxt/icons.css +26 -0
  170. package/nuxt/index.js +2 -2
  171. package/nuxt/pages/more.vue +1 -1
  172. package/package.json +5 -3
  173. package/readme.md +171 -1
  174. package/docs/aw-accordion-fold.md +0 -46
  175. package/docs/aw-address.md +0 -44
  176. package/docs/aw-avatar.md +0 -51
  177. package/docs/aw-badge.md +0 -32
  178. package/docs/aw-button-nav.md +0 -44
  179. package/docs/aw-button.md +0 -50
  180. package/docs/aw-calendar-days.md +0 -46
  181. package/docs/aw-calendar-nav.md +0 -25
  182. package/docs/aw-calendar-view.md +0 -12
  183. package/docs/aw-calendar.md +0 -59
  184. package/docs/aw-card.md +0 -48
  185. package/docs/aw-chart.md +0 -51
  186. package/docs/aw-checkbox.md +0 -56
  187. package/docs/aw-chip-select.md +0 -46
  188. package/docs/aw-chip.md +0 -53
  189. package/docs/aw-code-snippet.md +0 -18
  190. package/docs/aw-code.md +0 -56
  191. package/docs/aw-content-wrapper.md +0 -40
  192. package/docs/aw-context-menu.md +0 -31
  193. package/docs/aw-cropper.md +0 -60
  194. package/docs/aw-dashboard-card.md +0 -37
  195. package/docs/aw-dashboard-donut.md +0 -30
  196. package/docs/aw-dashboard-line.md +0 -20
  197. package/docs/aw-dashboard-progress.md +0 -33
  198. package/docs/aw-dashboard-section.md +0 -32
  199. package/docs/aw-dashboard-speed.md +0 -30
  200. package/docs/aw-date.md +0 -52
  201. package/docs/aw-dropdown-button.md +0 -31
  202. package/docs/aw-dropdown.md +0 -69
  203. package/docs/aw-fetch-data.md +0 -45
  204. package/docs/aw-form.md +0 -52
  205. package/docs/aw-grid.md +0 -48
  206. package/docs/aw-icon.md +0 -50
  207. package/docs/aw-info.md +0 -53
  208. package/docs/aw-input.md +0 -55
  209. package/docs/aw-layout-default.md +0 -30
  210. package/docs/aw-layout-frame-center.md +0 -29
  211. package/docs/aw-layout-simple.md +0 -49
  212. package/docs/aw-link.md +0 -54
  213. package/docs/aw-markdown-editor.md +0 -51
  214. package/docs/aw-modal.md +0 -63
  215. package/docs/aw-multi-block-builder.md +0 -66
  216. package/docs/aw-page.md +0 -36
  217. package/docs/aw-pagination.md +0 -54
  218. package/docs/aw-password.md +0 -48
  219. package/docs/aw-radio.md +0 -54
  220. package/docs/aw-search.md +0 -49
  221. package/docs/aw-select.md +0 -93
  222. package/docs/aw-slider.md +0 -40
  223. package/docs/aw-svg-image.md +0 -19
  224. package/docs/aw-switcher.md +0 -51
  225. package/docs/aw-tab-nav.md +0 -55
  226. package/docs/aw-table-builder.md +0 -58
  227. package/docs/aw-table-col.md +0 -33
  228. package/docs/aw-table-head.md +0 -28
  229. package/docs/aw-table-row.md +0 -33
  230. package/docs/aw-table.md +0 -59
  231. package/docs/aw-tel.md +0 -47
  232. package/docs/aw-textarea.md +0 -47
  233. package/docs/aw-timeline-builder.md +0 -50
  234. package/docs/aw-toggler.md +0 -41
  235. package/docs/aw-uploader-files.md +0 -20
  236. package/docs/aw-uploader.md +0 -60
  237. package/docs/aw-user-menu.md +0 -34
  238. package/docs/aw-userpic.md +0 -34
  239. /package/components/{3_organisms → 2_molecules}/AwTel.vue +0 -0
@@ -0,0 +1,1094 @@
1
+ # List Page Patterns
2
+
3
+ Complete guide to building list pages with tables, search, and actions using AwPage and AwTableBuilder.
4
+
5
+ ## When to Use AwPage
6
+
7
+ Use `AwPage` for:
8
+ - **List pages** - Display collections of items in tables
9
+ - **Dashboards** - Overview pages with metrics and data
10
+ - **Index pages** - Entry points to sections with navigation
11
+
12
+ **Key characteristics:**
13
+ - Shows aside menu by default
14
+ - Simple title prop
15
+ - Automatic integration with menu system
16
+ - Best for browsing and navigation
17
+
18
+ ## Basic List Page
19
+
20
+ ### Minimal Example
21
+
22
+ ```markup
23
+ <template>
24
+ <AwPage title="Customers">
25
+ <AwTableBuilder
26
+ :collection="customers"
27
+ @click:row="viewCustomer"
28
+ >
29
+ <AwTableCol field="name" title="Name" />
30
+ <AwTableCol field="email" title="Email" />
31
+ <AwTableCol field="phone" title="Phone" />
32
+ </AwTableBuilder>
33
+ </AwPage>
34
+ </template>
35
+
36
+ <script>
37
+ import Customers from '~/collections/Customers'
38
+
39
+ export default {
40
+ middleware: 'auth',
41
+
42
+ data() {
43
+ return {
44
+ customers: new Customers([], {
45
+ shop_uuid: this.$route.params.shop_uuid
46
+ })
47
+ }
48
+ },
49
+
50
+ methods: {
51
+ viewCustomer(customer) {
52
+ this.$router.push(`/${this.$route.params.shop_uuid}/customers/${customer.id}`)
53
+ }
54
+ }
55
+ }
56
+ </script>
57
+ ```
58
+
59
+ **What happens:**
60
+ 1. ✅ AwTableBuilder automatically calls `customers.fetch()` on mount
61
+ 2. ✅ No manual `fetch()` or `mounted()` needed
62
+ 3. ✅ Collection handles pagination automatically
63
+ 4. ✅ Loading states managed internally
64
+ 5. ✅ Row click navigates to detail page
65
+
66
+ **⚠️ Important: Don't use v-if for loading**
67
+
68
+ ```markup
69
+ <!-- ❌ BAD - Prevents AwTableBuilder from mounting and fetching -->
70
+ <template>
71
+ <AwPage title="Customers">
72
+ <div v-if="loading">Loading...</div>
73
+ <AwTableBuilder v-else :collection="customers">
74
+ <!-- Never mounts if loading=true initially -->
75
+ </AwTableBuilder>
76
+ </AwPage>
77
+ </template>
78
+
79
+ <!-- ✅ GOOD - Let AwTableBuilder handle its own loading -->
80
+ <template>
81
+ <AwPage title="Customers">
82
+ <AwTableBuilder :collection="customers">
83
+ <!-- Mounts immediately, shows internal loading state -->
84
+ </AwTableBuilder>
85
+ </AwPage>
86
+ </template>
87
+ ```
88
+
89
+ ## Header Action Buttons
90
+
91
+ **⚠️ Important: Create buttons should not be in menu items**
92
+
93
+ Create actions should only appear on list pages using `AwPageMenuButtons` and in empty states. Do not add create pages to menu navigation items.
94
+
95
+ ### Single CTA Button
96
+
97
+ For navigation to create pages, use `href` instead of `listeners`:
98
+
99
+ ```markup
100
+ <template>
101
+ <AwPage title="Templates">
102
+ <template #buttons>
103
+ <AwPageMenuButtons
104
+ :items="[{
105
+ text: 'Create Template',
106
+ icon: 'awesio/plus',
107
+ cta: true,
108
+ href: '/templates/create'
109
+ }]"
110
+ />
111
+ </template>
112
+
113
+ <AwTableBuilder :collection="templates">
114
+ <!-- Table columns -->
115
+ </AwTableBuilder>
116
+ </AwPage>
117
+ </template>
118
+ ```
119
+
120
+ ### Multiple Action Buttons
121
+
122
+ ```markup
123
+ <template #buttons>
124
+ <AwPageMenuButtons
125
+ :items="[
126
+ {
127
+ text: 'Export',
128
+ icon: 'awesio/download',
129
+ color: 'mono',
130
+ listeners: { click: exportData }
131
+ },
132
+ {
133
+ text: 'Import',
134
+ icon: 'awesio/upload',
135
+ color: 'mono',
136
+ listeners: { click: importData }
137
+ },
138
+ {
139
+ text: 'Create',
140
+ icon: 'awesio/plus',
141
+ cta: true,
142
+ listeners: { click: createItem }
143
+ }
144
+ ]"
145
+ />
146
+ </template>
147
+ ```
148
+
149
+ **Button Configuration:**
150
+ - `text` - Button label (required)
151
+ - `icon` - Icon name (optional)
152
+ - `color` - `'mono'`, `'primary'`, `'accent'` (default: 'mono')
153
+ - `cta` - Makes button prominent with accent color (default: false)
154
+ - `href` - Navigation URL (use for navigation, preferred over listeners)
155
+ - `listeners` - Event handlers object `{ click: handler }` (use only for non-navigation actions)
156
+
157
+ **Button Ordering:**
158
+ - Secondary actions (Export, Import) first
159
+ - Primary CTA (Create, Add) last
160
+ - CTA buttons automatically styled with accent color
161
+
162
+ ## Search Integration
163
+
164
+ ### Basic Search
165
+
166
+ ```markup
167
+ <template>
168
+ <AwPage title="Customers">
169
+ <!-- Search bar -->
170
+ <div class="flex justify-end mb-6">
171
+ <AwSearch class="w-full lg:w-auto" />
172
+ </div>
173
+
174
+ <!-- Table watches for search param changes -->
175
+ <AwTableBuilder
176
+ :collection="customers"
177
+ :watch-params="['search']"
178
+ >
179
+ <AwTableCol field="name" title="Name" />
180
+ <AwTableCol field="email" title="Email" />
181
+ </AwTableBuilder>
182
+ </AwPage>
183
+ </template>
184
+ ```
185
+
186
+ **How it works:**
187
+ 1. ✅ AwSearch updates `$route.query.search`
188
+ 2. ✅ AwTableBuilder watches `search` param via `:watch-params`
189
+ 3. ✅ Table automatically refetches when search changes
190
+ 4. ✅ Collection receives search param in API request
191
+
192
+ ### Search with Filters
193
+
194
+ When using `AwFilterSelect` components that sync with URL parameters, use `AwFilterChosen` to display active filters:
195
+
196
+ ```markup
197
+ <template>
198
+ <AwPage title="Users">
199
+ <div class="flex flex-wrap gap-4 mb-6">
200
+ <AwFilterSelect
201
+ param="is_active"
202
+ label="Status"
203
+ :options="statusOptions"
204
+ single
205
+ />
206
+ <AwFilterSelect
207
+ param="permissions"
208
+ label="Permissions"
209
+ :options="permissionOptions"
210
+ />
211
+ <AwSearch class="ml-auto" />
212
+ </div>
213
+
214
+ <AwFilterChosen :watch-params="['is_active', 'permissions']">
215
+ <template #is_active="{ value }">
216
+ {{ getStatusLabel(value) }}
217
+ </template>
218
+ <template #permissions="{ value }">
219
+ {{ getPermissionLabel(value) }}
220
+ </template>
221
+ </AwFilterChosen>
222
+
223
+ <AwTableBuilder
224
+ :collection="users"
225
+ :watch-params="['search', 'is_active', 'permissions']"
226
+ >
227
+ <AwTableCol field="first_name" title="First Name" />
228
+ <AwTableCol field="last_name" title="Last Name" />
229
+ </AwTableBuilder>
230
+ </AwPage>
231
+ </template>
232
+
233
+ <script>
234
+ export default {
235
+ computed: {
236
+ statusOptions() {
237
+ return [
238
+ { id: true, title: 'Active' },
239
+ { id: false, title: 'Inactive' }
240
+ ]
241
+ },
242
+
243
+ permissionOptions() {
244
+ return [
245
+ { id: 'manage-profile', title: 'Manage profile' },
246
+ { id: 'manage-users', title: 'Manage users' }
247
+ ]
248
+ }
249
+ },
250
+
251
+ methods: {
252
+ getStatusLabel(value) {
253
+ if (value === true || value === 'true') return 'Active'
254
+ if (value === false || value === 'false') return 'Inactive'
255
+ return value
256
+ },
257
+
258
+ getPermissionLabel(value) {
259
+ if (!value) return value
260
+
261
+ const permissions = Array.isArray(value)
262
+ ? value
263
+ : typeof value === 'string'
264
+ ? value.split(',').map((p) => p.trim())
265
+ : [value]
266
+
267
+ return permissions
268
+ .map((perm) => {
269
+ const option = this.permissionOptions.find(
270
+ (opt) => opt.id === perm || String(opt.id) === String(perm)
271
+ )
272
+ return option ? option.title : perm
273
+ })
274
+ .join(', ')
275
+ }
276
+ }
277
+ }
278
+ </script>
279
+ ```
280
+
281
+ **Best Practices:**
282
+ - ✅ **Always use custom slots** in `AwFilterChosen` to display user-friendly text instead of raw parameter values
283
+ - ✅ **Omit `'search'` from watch-params** - Search doesn't need to be displayed as a chip
284
+ - ✅ **Place search on the right** using `ml-auto` class
285
+ - ✅ **Handle different value types** - Values can be strings, booleans, arrays, or comma-separated strings
286
+
287
+ ## Custom Column Rendering
288
+
289
+ ### Understanding the `field` Prop
290
+
291
+ **⚠️ Critical Distinction:**
292
+
293
+ ```markup
294
+ <!-- WITH field: cell = field value only -->
295
+ <AwTableCol title="Name" field="name">
296
+ <template #default="{ cell }">
297
+ {{ cell }} <!-- cell is just the string value of 'name' -->
298
+ </template>
299
+ </AwTableCol>
300
+
301
+ <!-- WITHOUT field: cell = entire row object -->
302
+ <AwTableCol title="Name">
303
+ <template #default="{ cell }">
304
+ {{ cell.name }} <!-- cell is the full row object -->
305
+ {{ cell.email }} <!-- can access other fields -->
306
+ </template>
307
+ </AwTableCol>
308
+ ```
309
+
310
+ ### Simple Field Display
311
+
312
+ ```markup
313
+ <!-- No custom template needed for simple fields -->
314
+ <AwTableCol field="name" title="Name" />
315
+ <AwTableCol field="email" title="Email" />
316
+ <AwTableCol field="phone" title="Phone" />
317
+ ```
318
+
319
+ ### Status Badge
320
+
321
+ ```markup
322
+ <AwTableCol title="Status">
323
+ <template #default="{ cell }">
324
+ <AwLabel
325
+ :label="cell.is_active ? 'Active' : 'Inactive'"
326
+ :color="cell.is_active ? 'success' : 'mono'"
327
+ />
328
+ </template>
329
+ </AwTableCol>
330
+ ```
331
+
332
+ ### Date Formatting
333
+
334
+ ```markup
335
+ <AwTableCol title="Created">
336
+ <template #default="{ cell }">
337
+ {{ $dayjs(cell.created_at).format('ll') }}
338
+ </template>
339
+ </AwTableCol>
340
+ ```
341
+
342
+ **Common date formats:**
343
+ - `ll` - Jan 15, 2024
344
+ - `lll` - Jan 15, 2024 10:30 AM
345
+ - `LL` - January 15, 2024
346
+ - `MMMM YYYY` - January 2024
347
+
348
+ ### Currency Formatting
349
+
350
+ ```markup
351
+ <AwTableCol title="Total">
352
+ <template #default="{ cell }">
353
+ ${{ (cell.total / 100).toFixed(2) }}
354
+ </template>
355
+ </AwTableCol>
356
+ ```
357
+
358
+ ### Combined Data
359
+
360
+ ```markup
361
+ <AwTableCol title="Customer">
362
+ <template #default="{ cell }">
363
+ <div class="flex items-center gap-2">
364
+ <AwAvatar :src="cell.avatar" size="sm" />
365
+ <div>
366
+ <div class="font-medium">{{ cell.name }}</div>
367
+ <div class="text-sm text-secondary">{{ cell.email }}</div>
368
+ </div>
369
+ </div>
370
+ </template>
371
+ </AwTableCol>
372
+ ```
373
+
374
+ ## Mobile vs Desktop Table Layouts
375
+
376
+ ### Reordering Columns for Mobile
377
+
378
+ A common pattern is to display important information differently on mobile vs desktop. For example, an employee userpic might be in the 3rd or 4th column on desktop, but should appear in the mobile header for better visual hierarchy.
379
+
380
+ **Pattern: Userpic in Mobile Header**
381
+
382
+ ```markup
383
+ <template>
384
+ <AwPage title="Jobs">
385
+ <AwTableBuilder :collection="jobs">
386
+ <!-- Job title - shown on both desktop and mobile -->
387
+ <AwTableCol field="title" title="Job Title" />
388
+
389
+ <!-- Client info -->
390
+ <AwTableCol field="client_name" title="Client" />
391
+
392
+ <!-- Assigned employee - 3rd column on desktop, hidden on mobile -->
393
+ <AwTableCol title="Assigned To" hide-mobile>
394
+ <template #default="{ cell }">
395
+ <AwUserpic
396
+ :src="cell.employee?.avatar"
397
+ :name="cell.employee?.name"
398
+ :description="cell.employee?.role"
399
+ />
400
+ </template>
401
+ </AwTableCol>
402
+
403
+ <!-- Due date -->
404
+ <AwTableCol title="Due Date">
405
+ <template #default="{ cell }">
406
+ {{ $dayjs(cell.due_date).format('ll') }}
407
+ </template>
408
+ </AwTableCol>
409
+
410
+ <!-- Mobile header: Show userpic prominently -->
411
+ <template #mobile-header-content="{ cell }">
412
+ <AwUserpic
413
+ :src="cell.employee?.avatar"
414
+ :name="cell.employee?.name"
415
+ :description="cell.employee?.role"
416
+ />
417
+ </template>
418
+ </AwTableBuilder>
419
+ </AwPage>
420
+ </template>
421
+ ```
422
+
423
+ **What happens:**
424
+ - **Desktop:** Userpic appears in "Assigned To" column (3rd position)
425
+ - **Mobile:** Row collapses into card layout with userpic in header
426
+ - `hide-mobile` prop hides the desktop column on mobile to avoid duplication
427
+ - `mobile-header-content` slot positions userpic prominently in collapsed row header
428
+
429
+ **Pattern: Main Info in Mobile Header**
430
+
431
+ Sometimes the most important information should be highlighted in the mobile header:
432
+
433
+ ```markup
434
+ <template>
435
+ <AwPage title="Services">
436
+ <AwTableBuilder :collection="services">
437
+ <!-- Service name - desktop only -->
438
+ <AwTableCol field="name" title="Service" hide-mobile />
439
+
440
+ <!-- Price -->
441
+ <AwTableCol title="Price">
442
+ <template #default="{ cell }">
443
+ ${{ (cell.price / 100).toFixed(2) }}
444
+ </template>
445
+ </AwTableCol>
446
+
447
+ <!-- Duration -->
448
+ <AwTableCol field="duration" title="Duration" />
449
+
450
+ <!-- Category -->
451
+ <AwTableCol field="category" title="Category" />
452
+
453
+ <!-- Mobile header: Show service name as headline -->
454
+ <template #mobile-header-content="{ cell }">
455
+ <AwHeadline>{{ cell.name }}</AwHeadline>
456
+ </template>
457
+ </AwTableBuilder>
458
+ </AwPage>
459
+ </template>
460
+ ```
461
+
462
+ **What happens:**
463
+ - **Desktop:** Service name in first column
464
+ - **Mobile:** Service name becomes prominent headline in collapsed row header
465
+ - Other details (price, duration, category) shown below in mobile card layout
466
+ - `hide-mobile` on desktop column prevents duplicate service name
467
+
468
+ **Pattern: Combined User + Main Info**
469
+
470
+ For complex tables, combine userpic with main information in mobile header:
471
+
472
+ ```markup
473
+ <template>
474
+ <AwPage title="Bookings">
475
+ <AwTableBuilder :collection="bookings">
476
+ <!-- Service name - hidden on mobile -->
477
+ <AwTableCol field="service_name" title="Service" hide-mobile />
478
+
479
+ <!-- Date & Time -->
480
+ <AwTableCol title="Date & Time">
481
+ <template #default="{ cell }">
482
+ {{ $dayjs(cell.scheduled_at).format('lll') }}
483
+ </template>
484
+ </AwTableCol>
485
+
486
+ <!-- Client - shown on desktop, hidden on mobile -->
487
+ <AwTableCol title="Client" hide-mobile>
488
+ <template #default="{ cell }">
489
+ <AwUserpic
490
+ :src="cell.client?.avatar"
491
+ :name="cell.client?.name"
492
+ :description="cell.client?.email"
493
+ />
494
+ </template>
495
+ </AwTableCol>
496
+
497
+ <!-- Status -->
498
+ <AwTableCol title="Status">
499
+ <template #default="{ cell }">
500
+ <AwLabel
501
+ :label="cell.status"
502
+ :color="getStatusColor(cell.status)"
503
+ />
504
+ </template>
505
+ </AwTableCol>
506
+
507
+ <!-- Mobile header: Service + Client -->
508
+ <template #mobile-header-content="{ cell }">
509
+ <div class="space-y-2">
510
+ <AwHeadline>{{ cell.service_name }}</AwHeadline>
511
+ <AwUserpic
512
+ :src="cell.client?.avatar"
513
+ :name="cell.client?.name"
514
+ :description="cell.client?.email"
515
+ />
516
+ </div>
517
+ </template>
518
+ </AwTableBuilder>
519
+ </AwPage>
520
+ </template>
521
+
522
+ <script>
523
+ export default {
524
+ methods: {
525
+ getStatusColor(status) {
526
+ const colors = {
527
+ confirmed: 'success',
528
+ pending: 'warning',
529
+ cancelled: 'error',
530
+ completed: 'info'
531
+ }
532
+ return colors[status] || 'mono'
533
+ }
534
+ }
535
+ }
536
+ </script>
537
+ ```
538
+
539
+ ### Best Practices for Mobile Layouts
540
+
541
+ **1. Use `hide-mobile` to Prevent Duplication**
542
+
543
+ ```markup
544
+ <!-- ✅ GOOD - Hide desktop column to avoid showing info twice -->
545
+ <AwTableCol title="Employee" hide-mobile>
546
+ <template #default="{ cell }">
547
+ <AwUserpic :name="cell.employee.name" />
548
+ </template>
549
+ </AwTableCol>
550
+
551
+ <template #mobile-header-content="{ cell }">
552
+ <AwUserpic :name="cell.employee.name" />
553
+ </template>
554
+
555
+ <!-- ❌ BAD - Shows employee info twice on mobile -->
556
+ <AwTableCol title="Employee">
557
+ <template #default="{ cell }">
558
+ <AwUserpic :name="cell.employee.name" />
559
+ </template>
560
+ </AwTableCol>
561
+
562
+ <template #mobile-header-content="{ cell }">
563
+ <AwUserpic :name="cell.employee.name" />
564
+ </template>
565
+ ```
566
+
567
+ **2. Prioritize Visual Hierarchy on Mobile**
568
+
569
+ ```markup
570
+ <!-- ✅ GOOD - Most important info in mobile header -->
571
+ <template #mobile-header-content="{ cell }">
572
+ <AwHeadline>{{ cell.title }}</AwHeadline>
573
+ <AwUserpic :name="cell.owner.name" />
574
+ </template>
575
+
576
+ <!-- ❌ BAD - Generic info that doesn't help user scan -->
577
+ <template #mobile-header-content="{ cell }">
578
+ <span>{{ cell.id }}</span>
579
+ </template>
580
+ ```
581
+
582
+ **3. Use Appropriate Components**
583
+
584
+ - **AwHeadline** - For main titles/names
585
+ - **AwUserpic** - For person/employee information with avatar
586
+ - **AwLabel** - For status badges
587
+ - Keep mobile headers concise and scannable
588
+
589
+ **4. Consider Touch Targets**
590
+
591
+ ```markup
592
+ <!-- Mobile header content should support touch/tap -->
593
+ <AwTableBuilder
594
+ :collection="items"
595
+ @click:row="viewItem"
596
+ >
597
+ <template #mobile-header-content="{ cell }">
598
+ <AwUserpic :name="cell.name" />
599
+ </template>
600
+ </AwTableBuilder>
601
+ ```
602
+
603
+ ## Row Actions
604
+
605
+ ### Click Row (Navigation)
606
+
607
+ ```markup
608
+ <template>
609
+ <AwPage title="Customers">
610
+ <AwTableBuilder
611
+ :collection="customers"
612
+ @click:row="viewCustomer"
613
+ >
614
+ <AwTableCol field="name" title="Name" />
615
+ <AwTableCol field="email" title="Email" />
616
+ </AwTableBuilder>
617
+ </AwPage>
618
+ </template>
619
+
620
+ <script>
621
+ export default {
622
+ methods: {
623
+ viewCustomer(customer) {
624
+ this.$router.push(
625
+ `/${this.$route.params.shop_uuid}/customers/${customer.id}`
626
+ )
627
+ }
628
+ }
629
+ }
630
+ </script>
631
+ ```
632
+
633
+ ### Dropdown Actions
634
+
635
+ ```markup
636
+ <template>
637
+ <AwPage title="Templates">
638
+ <AwTableBuilder :collection="templates">
639
+ <AwTableCol field="name" title="Name" />
640
+ <AwTableCol field="status" title="Status" />
641
+
642
+ <!-- Dropdown slot for row actions -->
643
+ <template #dropdown="{ cell }">
644
+ <AwDropdownButton
645
+ text="View"
646
+ @click="viewItem(cell)"
647
+ />
648
+ <AwDropdownButton
649
+ text="Edit"
650
+ @click="editItem(cell)"
651
+ />
652
+ <AwDropdownButton
653
+ text="Duplicate"
654
+ @click="duplicateItem(cell)"
655
+ />
656
+ <AwDropdownButton
657
+ text="Delete"
658
+ color="error"
659
+ @click="deleteItem(cell)"
660
+ />
661
+ </template>
662
+ </AwTableBuilder>
663
+ </AwPage>
664
+ </template>
665
+
666
+ <script>
667
+ export default {
668
+ methods: {
669
+ viewItem(item) {
670
+ this.$router.push(`/templates/${item.id}`)
671
+ },
672
+
673
+ editItem(item) {
674
+ this.$router.push(`/templates/${item.id}/edit`)
675
+ },
676
+
677
+ duplicateItem(item) {
678
+ // Duplicate logic
679
+ },
680
+
681
+ async deleteItem(item) {
682
+ const confirmed = await this.$confirm({
683
+ title: 'Delete Template',
684
+ message: `Are you sure you want to delete "${item.name}"?`
685
+ })
686
+
687
+ if (!confirmed) return
688
+
689
+ try {
690
+ await this.$axios.delete(`/api/templates/${item.id}`)
691
+ this.$notify({
692
+ message: 'Template deleted successfully',
693
+ type: 'success'
694
+ })
695
+ this.templates.fetch() // Refetch list
696
+ } catch (error) {
697
+ this.$notify({
698
+ message: 'Failed to delete template',
699
+ type: 'error'
700
+ })
701
+ }
702
+ }
703
+ }
704
+ }
705
+ </script>
706
+ ```
707
+
708
+ **⚠️ Important: Can't use both @click:row and #dropdown**
709
+
710
+ ```markup
711
+ <!-- ❌ BAD - Conflict! Row click and dropdown both trigger -->
712
+ <AwTableBuilder
713
+ :collection="items"
714
+ @click:row="viewItem"
715
+ >
716
+ <template #dropdown="{ cell }">
717
+ <AwDropdownButton text="Edit" />
718
+ </template>
719
+ </AwTableBuilder>
720
+
721
+ <!-- ✅ GOOD - Choose one approach -->
722
+ <!-- Option 1: Row click only -->
723
+ <AwTableBuilder
724
+ :collection="items"
725
+ @click:row="viewItem"
726
+ >
727
+ <!-- No dropdown -->
728
+ </AwTableBuilder>
729
+
730
+ <!-- Option 2: Dropdown only -->
731
+ <AwTableBuilder :collection="items">
732
+ <template #dropdown="{ cell }">
733
+ <AwDropdownButton text="View" @click="viewItem(cell)" />
734
+ <AwDropdownButton text="Edit" @click="editItem(cell)" />
735
+ </template>
736
+ </AwTableBuilder>
737
+ ```
738
+
739
+ **Dropdown Action Ordering:**
740
+ 1. View (if separate from row click)
741
+ 2. Edit
742
+ 3. Other actions (Duplicate, Archive, etc.)
743
+ 4. Delete (last, in error color)
744
+
745
+ ## Mobile Breadcrumbs
746
+
747
+ For pages in subfolders, add breadcrumbs for mobile navigation:
748
+
749
+ ```markup
750
+ <template>
751
+ <AwPage title="Email Templates">
752
+ <!-- Mobile breadcrumb -->
753
+ <template #mobile-breadcrumbs>
754
+ <AwButton
755
+ :href="`/${$route.params.shop_uuid}/notifications`"
756
+ theme="text"
757
+ icon="arrow-left"
758
+ text="Notifications"
759
+ />
760
+ </template>
761
+
762
+ <!-- Page content -->
763
+ </AwPage>
764
+ </template>
765
+ ```
766
+
767
+ ## Empty States
768
+
769
+ Use the `cta-button` prop on `AwEmptyContainer` to add a create button in the empty state. The button should use the same `href` as the header button:
770
+
771
+ ```markup
772
+ <template>
773
+ <AwPage title="Customers">
774
+ <template #buttons>
775
+ <AwPageMenuButtons
776
+ :items="[{
777
+ text: 'Create Customer',
778
+ icon: 'awesio/plus',
779
+ cta: true,
780
+ href: '/customers/create'
781
+ }]"
782
+ />
783
+ </template>
784
+
785
+ <AwTableBuilder :collection="customers">
786
+ <AwTableCol field="name" title="Name" />
787
+
788
+ <!-- Empty state with create button -->
789
+ <template #empty-container>
790
+ <AwEmptyContainer
791
+ icon="users"
792
+ title="No customers yet"
793
+ description="Create your first customer to get started"
794
+ :cta-button="{
795
+ text: 'Create Customer',
796
+ icon: 'awesio/plus',
797
+ href: '/customers/create'
798
+ }"
799
+ />
800
+ </template>
801
+ </AwTableBuilder>
802
+ </AwPage>
803
+ </template>
804
+ ```
805
+
806
+ **Best Practice:** Always use the same create button in both the header (`AwPageMenuButtons`) and empty state (`AwEmptyContainer` cta-button) with the same `href` for consistency.
807
+
808
+ ## Complete Example
809
+
810
+ Full-featured list page with all patterns:
811
+
812
+ ```markup
813
+ <template>
814
+ <AwPage title="Email Templates">
815
+ <!-- Mobile breadcrumbs -->
816
+ <template #mobile-breadcrumbs>
817
+ <AwButton
818
+ :href="`/${$route.params.shop_uuid}/notifications`"
819
+ theme="text"
820
+ icon="arrow-left"
821
+ text="Back to Notifications"
822
+ />
823
+ </template>
824
+
825
+ <!-- Header action buttons -->
826
+ <template #buttons>
827
+ <AwPageMenuButtons
828
+ :items="[
829
+ {
830
+ text: 'Import',
831
+ icon: 'awesio/upload',
832
+ color: 'mono',
833
+ listeners: { click: importTemplates }
834
+ },
835
+ {
836
+ text: 'Create Template',
837
+ icon: 'awesio/plus',
838
+ cta: true,
839
+ href: `/${shopUuid}/templates/create`
840
+ }
841
+ ]"
842
+ />
843
+ </template>
844
+
845
+ <!-- Search and filters -->
846
+ <div class="flex flex-col lg:flex-row gap-4 mb-6">
847
+ <AwSearch class="flex-1" />
848
+ <AwSelect
849
+ v-model="channelFilter"
850
+ :options="['all', 'email', 'sms', 'push']"
851
+ label="Channel"
852
+ class="w-full lg:w-48"
853
+ />
854
+ </div>
855
+
856
+ <!-- Table with data -->
857
+ <AwTableBuilder
858
+ :collection="templates"
859
+ :watch-params="['search']"
860
+ :options="{ shop_uuid: shopUuid, channel: channelFilter }"
861
+ >
862
+ <!-- Simple field columns -->
863
+ <AwTableCol field="name" title="Template Name" />
864
+
865
+ <!-- Custom status column -->
866
+ <AwTableCol title="Status">
867
+ <template #default="{ cell }">
868
+ <AwLabel
869
+ :label="cell.is_active ? 'Active' : 'Inactive'"
870
+ :color="cell.is_active ? 'success' : 'mono'"
871
+ />
872
+ </template>
873
+ </AwTableCol>
874
+
875
+ <!-- Channel badge -->
876
+ <AwTableCol title="Channel">
877
+ <template #default="{ cell }">
878
+ <AwBadge :text="cell.channel" />
879
+ </template>
880
+ </AwTableCol>
881
+
882
+ <!-- Date column -->
883
+ <AwTableCol title="Last Modified">
884
+ <template #default="{ cell }">
885
+ {{ $dayjs(cell.updated_at).format('ll') }}
886
+ </template>
887
+ </AwTableCol>
888
+
889
+ <!-- Row actions dropdown -->
890
+ <template #dropdown="{ cell }">
891
+ <AwDropdownButton
892
+ text="Edit"
893
+ @click="editTemplate(cell)"
894
+ />
895
+ <AwDropdownButton
896
+ text="Duplicate"
897
+ @click="duplicateTemplate(cell)"
898
+ />
899
+ <AwDropdownButton
900
+ :text="cell.is_active ? 'Deactivate' : 'Activate'"
901
+ @click="toggleActive(cell)"
902
+ />
903
+ <AwDropdownButton
904
+ text="Delete"
905
+ color="error"
906
+ @click="deleteTemplate(cell)"
907
+ />
908
+ </template>
909
+
910
+ <!-- Empty state -->
911
+ <template #empty-container>
912
+ <AwEmptyContainer
913
+ icon="awesio/mail"
914
+ title="No templates yet"
915
+ description="Create your first email template"
916
+ :cta-button="{
917
+ text: 'Create Template',
918
+ icon: 'awesio/plus',
919
+ href: `/${shopUuid}/templates/create`
920
+ }"
921
+ />
922
+ </template>
923
+ </AwTableBuilder>
924
+ </AwPage>
925
+ </template>
926
+
927
+ <script>
928
+ import EmailTemplates from '~/collections/EmailTemplates'
929
+
930
+ export default {
931
+ middleware: 'auth',
932
+
933
+ data() {
934
+ return {
935
+ channelFilter: 'all',
936
+ templates: new EmailTemplates([], {
937
+ shop_uuid: this.$route.params.shop_uuid
938
+ })
939
+ }
940
+ },
941
+
942
+ computed: {
943
+ shopUuid() {
944
+ return this.$route.params.shop_uuid
945
+ }
946
+ },
947
+
948
+ methods: {
949
+ editTemplate(template) {
950
+ this.$router.push({
951
+ name: 'templates-edit',
952
+ params: {
953
+ shop_uuid: this.shopUuid,
954
+ id: template.id
955
+ }
956
+ })
957
+ },
958
+
959
+ async duplicateTemplate(template) {
960
+ try {
961
+ await this.$axios.post(`/api/templates/${template.id}/duplicate`)
962
+ this.$notify({
963
+ message: 'Template duplicated successfully',
964
+ type: 'success'
965
+ })
966
+ this.templates.fetch()
967
+ } catch (error) {
968
+ this.$notify({
969
+ message: 'Failed to duplicate template',
970
+ type: 'error'
971
+ })
972
+ }
973
+ },
974
+
975
+ async toggleActive(template) {
976
+ try {
977
+ await this.$axios.patch(`/api/templates/${template.id}`, {
978
+ is_active: !template.is_active
979
+ })
980
+ this.$notify({
981
+ message: `Template ${template.is_active ? 'deactivated' : 'activated'}`,
982
+ type: 'success'
983
+ })
984
+ this.templates.fetch()
985
+ } catch (error) {
986
+ this.$notify({
987
+ message: 'Failed to update template',
988
+ type: 'error'
989
+ })
990
+ }
991
+ },
992
+
993
+ async deleteTemplate(template) {
994
+ const confirmed = await this.$confirm({
995
+ title: 'Delete Template',
996
+ message: `Are you sure you want to delete "${template.name}"?`
997
+ })
998
+
999
+ if (!confirmed) return
1000
+
1001
+ try {
1002
+ await this.$axios.delete(`/api/templates/${template.id}`)
1003
+ this.$notify({
1004
+ message: 'Template deleted successfully',
1005
+ type: 'success'
1006
+ })
1007
+ this.templates.fetch()
1008
+ } catch (error) {
1009
+ this.$notify({
1010
+ message: 'Failed to delete template',
1011
+ type: 'error'
1012
+ })
1013
+ }
1014
+ },
1015
+
1016
+ importTemplates() {
1017
+ // Import logic
1018
+ }
1019
+ }
1020
+ }
1021
+ </script>
1022
+ ```
1023
+
1024
+ ## Best Practices
1025
+
1026
+ ### 1. Collection Initialization
1027
+
1028
+ ✅ **Always include shop_uuid in options:**
1029
+ ```javascript
1030
+ customers: new Customers([], {
1031
+ shop_uuid: this.$route.params.shop_uuid
1032
+ })
1033
+ ```
1034
+
1035
+ ### 2. No Manual Fetching
1036
+
1037
+ ✅ **Let AwTableBuilder handle fetching:**
1038
+ ```markup
1039
+ <!-- ✅ GOOD -->
1040
+ <AwTableBuilder :collection="customers" />
1041
+
1042
+ <!-- ❌ BAD - Don't fetch manually -->
1043
+ <script>
1044
+ async mounted() {
1045
+ await this.customers.fetch() // Unnecessary!
1046
+ }
1047
+ </script>
1048
+ ```
1049
+
1050
+ ### 3. Loading States
1051
+
1052
+ ✅ **Don't wrap in v-if:**
1053
+ ```markup
1054
+ <!-- ✅ GOOD - Component handles loading -->
1055
+ <AwTableBuilder :collection="customers" />
1056
+
1057
+ <!-- ❌ BAD - Prevents mounting -->
1058
+ <AwTableBuilder v-if="!loading" :collection="customers" />
1059
+ ```
1060
+
1061
+ ### 4. Date Formatting
1062
+
1063
+ ✅ **Always use $dayjs:**
1064
+ ```markup
1065
+ <!-- ✅ GOOD -->
1066
+ {{ $dayjs(cell.created_at).format('ll') }}
1067
+
1068
+ <!-- ❌ BAD - Inconsistent formatting -->
1069
+ {{ new Date(cell.created_at).toLocaleDateString() }}
1070
+ ```
1071
+
1072
+ ### 5. Confirmations for Destructive Actions
1073
+
1074
+ ✅ **Always confirm deletes:**
1075
+ ```javascript
1076
+ async deleteItem(item) {
1077
+ const confirmed = await this.$confirm({
1078
+ title: 'Delete Item',
1079
+ message: `Are you sure you want to delete "${item.name}"?`
1080
+ })
1081
+
1082
+ if (!confirmed) return
1083
+
1084
+ // Proceed with deletion
1085
+ }
1086
+ ```
1087
+
1088
+ ## See Also
1089
+
1090
+ - [Detail Pages](./detail-pages.md) - Create and edit pages
1091
+ - [Dashboard Pages](./dashboard-pages.md) - Metrics and overview pages
1092
+ - [AwTableBuilder](../../components/organisms/aw-table-builder.md) - Table component reference
1093
+ - [AwPage](../../components/pages/aw-page.md) - Page component reference
1094
+ - [Data Fetching Guide](../data-fetching.md) - Working with collections