snice 4.10.0 → 4.12.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 (269) hide show
  1. package/adapters/react/carousel.d.ts +1 -0
  2. package/adapters/react/carousel.d.ts.map +1 -1
  3. package/adapters/react/carousel.js +1 -1
  4. package/adapters/react/carousel.js.map +1 -1
  5. package/adapters/react/carousel.tsx +2 -1
  6. package/bin/templates/CLAUDE.md +169 -113
  7. package/bin/templates/pwa/README.md +72 -94
  8. package/bin/templates/pwa/src/components/app-header.ts +218 -0
  9. package/bin/templates/pwa/src/components/notification-badge.ts +68 -0
  10. package/bin/templates/pwa/src/components/search-bar.ts +99 -0
  11. package/bin/templates/pwa/src/controllers/notification-controller.ts +42 -0
  12. package/bin/templates/pwa/src/guards/auth.ts +1 -1
  13. package/bin/templates/pwa/src/main.ts +19 -0
  14. package/bin/templates/pwa/src/pages/dashboard.ts +124 -23
  15. package/bin/templates/pwa/src/pages/data.ts +329 -0
  16. package/bin/templates/pwa/src/pages/notifications.ts +112 -30
  17. package/bin/templates/pwa/src/pages/profile.ts +86 -52
  18. package/bin/templates/pwa/src/pages/settings.ts +291 -0
  19. package/bin/templates/pwa/src/styles/global.css +12 -3
  20. package/dist/cdn/accordion/snice-accordion.js +1 -1
  21. package/dist/cdn/accordion/snice-accordion.min.js +1 -1
  22. package/dist/cdn/alert/snice-alert.js +1 -1
  23. package/dist/cdn/alert/snice-alert.min.js +1 -1
  24. package/dist/cdn/app-tiles/snice-app-tiles.js +1 -1
  25. package/dist/cdn/app-tiles/snice-app-tiles.min.js +1 -1
  26. package/dist/cdn/audio-recorder/snice-audio-recorder.js +1 -1
  27. package/dist/cdn/audio-recorder/snice-audio-recorder.min.js +1 -1
  28. package/dist/cdn/avatar/snice-avatar.js +1 -1
  29. package/dist/cdn/avatar/snice-avatar.min.js +1 -1
  30. package/dist/cdn/badge/snice-badge.js +1 -1
  31. package/dist/cdn/badge/snice-badge.min.js +1 -1
  32. package/dist/cdn/banner/snice-banner.js +1 -1
  33. package/dist/cdn/banner/snice-banner.min.js +1 -1
  34. package/dist/cdn/book/snice-book.js +1 -1
  35. package/dist/cdn/book/snice-book.min.js +1 -1
  36. package/dist/cdn/breadcrumbs/snice-breadcrumbs.js +1 -1
  37. package/dist/cdn/breadcrumbs/snice-breadcrumbs.min.js +1 -1
  38. package/dist/cdn/button/snice-button.js +1 -1
  39. package/dist/cdn/button/snice-button.min.js +1 -1
  40. package/dist/cdn/calendar/snice-calendar.js +1 -1
  41. package/dist/cdn/calendar/snice-calendar.min.js +1 -1
  42. package/dist/cdn/camera/README.md +1 -1
  43. package/dist/cdn/camera/snice-camera.js +1 -1
  44. package/dist/cdn/camera/snice-camera.min.js +1 -1
  45. package/dist/cdn/camera-annotate/snice-camera-annotate.js +1 -1
  46. package/dist/cdn/camera-annotate/snice-camera-annotate.min.js +1 -1
  47. package/dist/cdn/candlestick/snice-candlestick.js +1 -1
  48. package/dist/cdn/candlestick/snice-candlestick.min.js +1 -1
  49. package/dist/cdn/card/snice-card.js +1 -1
  50. package/dist/cdn/card/snice-card.min.js +1 -1
  51. package/dist/cdn/carousel/README.md +2 -2
  52. package/dist/cdn/carousel/snice-carousel.js +9 -3
  53. package/dist/cdn/carousel/snice-carousel.js.map +1 -1
  54. package/dist/cdn/carousel/snice-carousel.min.js +3 -3
  55. package/dist/cdn/carousel/snice-carousel.min.js.map +1 -1
  56. package/dist/cdn/chart/snice-chart.js +1 -1
  57. package/dist/cdn/chart/snice-chart.min.js +1 -1
  58. package/dist/cdn/chat/snice-chat.js +1 -1
  59. package/dist/cdn/chat/snice-chat.min.js +1 -1
  60. package/dist/cdn/checkbox/snice-checkbox.js +1 -1
  61. package/dist/cdn/checkbox/snice-checkbox.min.js +1 -1
  62. package/dist/cdn/chip/snice-chip.js +1 -1
  63. package/dist/cdn/chip/snice-chip.min.js +1 -1
  64. package/dist/cdn/code-block/snice-code-block.js +1 -1
  65. package/dist/cdn/code-block/snice-code-block.min.js +1 -1
  66. package/dist/cdn/color-display/snice-color-display.js +1 -1
  67. package/dist/cdn/color-display/snice-color-display.min.js +1 -1
  68. package/dist/cdn/color-picker/snice-color-picker.js +1 -1
  69. package/dist/cdn/color-picker/snice-color-picker.min.js +1 -1
  70. package/dist/cdn/command-palette/snice-command-palette.js +1 -1
  71. package/dist/cdn/command-palette/snice-command-palette.min.js +1 -1
  72. package/dist/cdn/comments/snice-comments.js +1 -1
  73. package/dist/cdn/comments/snice-comments.min.js +1 -1
  74. package/dist/cdn/countdown/snice-countdown.js +1 -1
  75. package/dist/cdn/countdown/snice-countdown.min.js +1 -1
  76. package/dist/cdn/cropper/snice-cropper.js +1 -1
  77. package/dist/cdn/cropper/snice-cropper.min.js +1 -1
  78. package/dist/cdn/date-picker/snice-date-picker.js +1 -1
  79. package/dist/cdn/date-picker/snice-date-picker.min.js +1 -1
  80. package/dist/cdn/diff/snice-diff.js +1 -1
  81. package/dist/cdn/diff/snice-diff.min.js +1 -1
  82. package/dist/cdn/divider/snice-divider.js +1 -1
  83. package/dist/cdn/divider/snice-divider.min.js +1 -1
  84. package/dist/cdn/doc/snice-doc.js +1 -1
  85. package/dist/cdn/doc/snice-doc.min.js +1 -1
  86. package/dist/cdn/draw/snice-draw.js +1 -1
  87. package/dist/cdn/draw/snice-draw.min.js +1 -1
  88. package/dist/cdn/drawer/snice-drawer.js +1 -1
  89. package/dist/cdn/drawer/snice-drawer.min.js +1 -1
  90. package/dist/cdn/empty-state/snice-empty-state.js +1 -1
  91. package/dist/cdn/empty-state/snice-empty-state.min.js +1 -1
  92. package/dist/cdn/file-gallery/snice-file-gallery.js +1 -1
  93. package/dist/cdn/file-gallery/snice-file-gallery.min.js +1 -1
  94. package/dist/cdn/file-upload/snice-file-upload.js +1 -1
  95. package/dist/cdn/file-upload/snice-file-upload.min.js +1 -1
  96. package/dist/cdn/flip-card/snice-flip-card.js +1 -1
  97. package/dist/cdn/flip-card/snice-flip-card.min.js +1 -1
  98. package/dist/cdn/flow/snice-flow.js +1 -1
  99. package/dist/cdn/flow/snice-flow.min.js +1 -1
  100. package/dist/cdn/funnel/snice-funnel.js +1 -1
  101. package/dist/cdn/funnel/snice-funnel.min.js +1 -1
  102. package/dist/cdn/gantt/README.md +1 -1
  103. package/dist/cdn/gantt/snice-gantt.js +1 -1
  104. package/dist/cdn/gantt/snice-gantt.min.js +1 -1
  105. package/dist/cdn/gauge/snice-gauge.js +1 -1
  106. package/dist/cdn/gauge/snice-gauge.min.js +1 -1
  107. package/dist/cdn/heatmap/snice-heatmap.js +1 -1
  108. package/dist/cdn/heatmap/snice-heatmap.min.js +1 -1
  109. package/dist/cdn/image/snice-image.js +1 -1
  110. package/dist/cdn/image/snice-image.min.js +1 -1
  111. package/dist/cdn/input/snice-input.js +1 -1
  112. package/dist/cdn/input/snice-input.min.js +1 -1
  113. package/dist/cdn/kanban/snice-kanban.js +1 -1
  114. package/dist/cdn/kanban/snice-kanban.min.js +1 -1
  115. package/dist/cdn/kpi/snice-kpi.js +1 -1
  116. package/dist/cdn/kpi/snice-kpi.min.js +1 -1
  117. package/dist/cdn/layout/snice-layout.js +1 -1
  118. package/dist/cdn/layout/snice-layout.min.js +1 -1
  119. package/dist/cdn/link/snice-link.js +1 -1
  120. package/dist/cdn/link/snice-link.min.js +1 -1
  121. package/dist/cdn/link-preview/snice-link-preview.js +1 -1
  122. package/dist/cdn/link-preview/snice-link-preview.min.js +1 -1
  123. package/dist/cdn/list/snice-list.js +1 -1
  124. package/dist/cdn/list/snice-list.min.js +1 -1
  125. package/dist/cdn/location/snice-location.js +1 -1
  126. package/dist/cdn/location/snice-location.min.js +1 -1
  127. package/dist/cdn/login/snice-login.js +1 -1
  128. package/dist/cdn/login/snice-login.min.js +1 -1
  129. package/dist/cdn/map/snice-map.js +1 -1
  130. package/dist/cdn/map/snice-map.min.js +1 -1
  131. package/dist/cdn/markdown/README.md +1 -1
  132. package/dist/cdn/markdown/snice-markdown.js +2 -2
  133. package/dist/cdn/markdown/snice-markdown.js.map +1 -1
  134. package/dist/cdn/markdown/snice-markdown.min.js +2 -2
  135. package/dist/cdn/markdown/snice-markdown.min.js.map +1 -1
  136. package/dist/cdn/masonry/snice-masonry.js +1 -1
  137. package/dist/cdn/masonry/snice-masonry.min.js +1 -1
  138. package/dist/cdn/menu/snice-menu.js +1 -1
  139. package/dist/cdn/menu/snice-menu.min.js +1 -1
  140. package/dist/cdn/modal/snice-modal.js +1 -1
  141. package/dist/cdn/modal/snice-modal.min.js +1 -1
  142. package/dist/cdn/music-player/snice-music-player.js +1 -1
  143. package/dist/cdn/music-player/snice-music-player.min.js +1 -1
  144. package/dist/cdn/nav/snice-nav.js +1 -1
  145. package/dist/cdn/nav/snice-nav.min.js +1 -1
  146. package/dist/cdn/network-graph/snice-network-graph.js +1 -1
  147. package/dist/cdn/network-graph/snice-network-graph.min.js +1 -1
  148. package/dist/cdn/notification-center/snice-notification-center.js +1 -1
  149. package/dist/cdn/notification-center/snice-notification-center.min.js +1 -1
  150. package/dist/cdn/org-chart/snice-org-chart.js +1 -1
  151. package/dist/cdn/org-chart/snice-org-chart.min.js +1 -1
  152. package/dist/cdn/pagination/snice-pagination.js +1 -1
  153. package/dist/cdn/pagination/snice-pagination.min.js +1 -1
  154. package/dist/cdn/paint/snice-paint.js +1 -1
  155. package/dist/cdn/paint/snice-paint.min.js +1 -1
  156. package/dist/cdn/pdf-viewer/README.md +2 -2
  157. package/dist/cdn/pdf-viewer/snice-pdf-viewer.js +72 -78
  158. package/dist/cdn/pdf-viewer/snice-pdf-viewer.js.map +1 -1
  159. package/dist/cdn/pdf-viewer/snice-pdf-viewer.min.js +3 -3
  160. package/dist/cdn/pdf-viewer/snice-pdf-viewer.min.js.map +1 -1
  161. package/dist/cdn/podcast-player/README.md +1 -1
  162. package/dist/cdn/podcast-player/snice-podcast-player.js +2 -2
  163. package/dist/cdn/podcast-player/snice-podcast-player.js.map +1 -1
  164. package/dist/cdn/podcast-player/snice-podcast-player.min.js +2 -2
  165. package/dist/cdn/podcast-player/snice-podcast-player.min.js.map +1 -1
  166. package/dist/cdn/pricing-table/snice-pricing-table.js +1 -1
  167. package/dist/cdn/pricing-table/snice-pricing-table.min.js +1 -1
  168. package/dist/cdn/progress/snice-progress.js +1 -1
  169. package/dist/cdn/progress/snice-progress.min.js +1 -1
  170. package/dist/cdn/qr-code/snice-qr-code.js +1 -1
  171. package/dist/cdn/qr-code/snice-qr-code.min.js +1 -1
  172. package/dist/cdn/qr-reader/snice-qr-reader.js +1 -1
  173. package/dist/cdn/qr-reader/snice-qr-reader.min.js +1 -1
  174. package/dist/cdn/radio/snice-radio.js +1 -1
  175. package/dist/cdn/radio/snice-radio.min.js +1 -1
  176. package/dist/cdn/rating/snice-rating.js +1 -1
  177. package/dist/cdn/rating/snice-rating.min.js +1 -1
  178. package/dist/cdn/recipe/snice-recipe.js +1 -1
  179. package/dist/cdn/recipe/snice-recipe.min.js +1 -1
  180. package/dist/cdn/runtime/README.md +1 -1
  181. package/dist/cdn/runtime/snice-runtime.esm.js +10 -3
  182. package/dist/cdn/runtime/snice-runtime.esm.js.map +1 -1
  183. package/dist/cdn/runtime/snice-runtime.esm.min.js +4 -4
  184. package/dist/cdn/runtime/snice-runtime.esm.min.js.map +1 -1
  185. package/dist/cdn/runtime/snice-runtime.js +10 -3
  186. package/dist/cdn/runtime/snice-runtime.js.map +1 -1
  187. package/dist/cdn/runtime/snice-runtime.min.js +4 -4
  188. package/dist/cdn/runtime/snice-runtime.min.js.map +1 -1
  189. package/dist/cdn/sankey/snice-sankey.js +1 -1
  190. package/dist/cdn/sankey/snice-sankey.min.js +1 -1
  191. package/dist/cdn/select/snice-select.js +1 -1
  192. package/dist/cdn/select/snice-select.min.js +1 -1
  193. package/dist/cdn/skeleton/snice-skeleton.js +1 -1
  194. package/dist/cdn/skeleton/snice-skeleton.min.js +1 -1
  195. package/dist/cdn/slider/snice-slider.js +1 -1
  196. package/dist/cdn/slider/snice-slider.min.js +1 -1
  197. package/dist/cdn/sortable/snice-sortable.js +1 -1
  198. package/dist/cdn/sortable/snice-sortable.min.js +1 -1
  199. package/dist/cdn/sparkline/snice-sparkline.js +1 -1
  200. package/dist/cdn/sparkline/snice-sparkline.min.js +1 -1
  201. package/dist/cdn/spinner/snice-spinner.js +1 -1
  202. package/dist/cdn/spinner/snice-spinner.min.js +1 -1
  203. package/dist/cdn/split-pane/snice-split-pane.js +1 -1
  204. package/dist/cdn/split-pane/snice-split-pane.min.js +1 -1
  205. package/dist/cdn/spotlight/snice-spotlight.js +1 -1
  206. package/dist/cdn/spotlight/snice-spotlight.min.js +1 -1
  207. package/dist/cdn/spreadsheet/snice-spreadsheet.js +1 -1
  208. package/dist/cdn/spreadsheet/snice-spreadsheet.min.js +1 -1
  209. package/dist/cdn/stepper/snice-stepper.js +1 -1
  210. package/dist/cdn/stepper/snice-stepper.min.js +1 -1
  211. package/dist/cdn/switch/snice-switch.js +1 -1
  212. package/dist/cdn/switch/snice-switch.min.js +1 -1
  213. package/dist/cdn/table/snice-table.js +1 -1
  214. package/dist/cdn/table/snice-table.min.js +1 -1
  215. package/dist/cdn/tabs/snice-tabs.js +1 -1
  216. package/dist/cdn/tabs/snice-tabs.min.js +1 -1
  217. package/dist/cdn/tag-input/snice-tag-input.js +1 -1
  218. package/dist/cdn/tag-input/snice-tag-input.min.js +1 -1
  219. package/dist/cdn/terminal/snice-terminal.js +1 -1
  220. package/dist/cdn/terminal/snice-terminal.min.js +1 -1
  221. package/dist/cdn/testimonial/snice-testimonial.js +1 -1
  222. package/dist/cdn/testimonial/snice-testimonial.min.js +1 -1
  223. package/dist/cdn/textarea/snice-textarea.js +1 -1
  224. package/dist/cdn/textarea/snice-textarea.min.js +1 -1
  225. package/dist/cdn/time-range-picker/snice-time-range-picker.js +1 -1
  226. package/dist/cdn/time-range-picker/snice-time-range-picker.min.js +1 -1
  227. package/dist/cdn/timeline/snice-timeline.js +1 -1
  228. package/dist/cdn/timeline/snice-timeline.min.js +1 -1
  229. package/dist/cdn/timer/snice-timer.js +1 -1
  230. package/dist/cdn/timer/snice-timer.min.js +1 -1
  231. package/dist/cdn/toast/snice-toast.js +1 -1
  232. package/dist/cdn/toast/snice-toast.min.js +1 -1
  233. package/dist/cdn/tooltip/snice-tooltip.js +1 -1
  234. package/dist/cdn/tooltip/snice-tooltip.min.js +1 -1
  235. package/dist/cdn/tree/snice-tree.js +1 -1
  236. package/dist/cdn/tree/snice-tree.min.js +1 -1
  237. package/dist/cdn/treemap/snice-treemap.js +1 -1
  238. package/dist/cdn/treemap/snice-treemap.min.js +1 -1
  239. package/dist/cdn/video-player/snice-video-player.js +1 -1
  240. package/dist/cdn/video-player/snice-video-player.min.js +1 -1
  241. package/dist/cdn/virtual-scroller/snice-virtual-scroller.js +1 -1
  242. package/dist/cdn/virtual-scroller/snice-virtual-scroller.min.js +1 -1
  243. package/dist/cdn/waterfall/snice-waterfall.js +1 -1
  244. package/dist/cdn/waterfall/snice-waterfall.min.js +1 -1
  245. package/dist/cdn/weather/snice-weather.js +1 -1
  246. package/dist/cdn/weather/snice-weather.min.js +1 -1
  247. package/dist/components/carousel/snice-carousel.js +8 -2
  248. package/dist/components/carousel/snice-carousel.js.map +1 -1
  249. package/dist/components/code-block/grammars/html.json +48 -0
  250. package/dist/components/markdown/snice-markdown.js +1 -1
  251. package/dist/components/markdown/snice-markdown.js.map +1 -1
  252. package/dist/components/pdf-viewer/snice-pdf-viewer.d.ts +9 -8
  253. package/dist/components/pdf-viewer/snice-pdf-viewer.js +71 -77
  254. package/dist/components/pdf-viewer/snice-pdf-viewer.js.map +1 -1
  255. package/dist/components/podcast-player/snice-podcast-player.js +1 -1
  256. package/dist/components/podcast-player/snice-podcast-player.js.map +1 -1
  257. package/dist/index.cjs +8 -1
  258. package/dist/index.cjs.map +1 -1
  259. package/dist/index.esm.js +8 -1
  260. package/dist/index.esm.js.map +1 -1
  261. package/dist/index.iife.js +8 -1
  262. package/dist/index.iife.js.map +1 -1
  263. package/dist/symbols.cjs +1 -1
  264. package/dist/symbols.esm.js +1 -1
  265. package/dist/transitions.cjs +1 -1
  266. package/dist/transitions.esm.js +1 -1
  267. package/docs/ai/patterns.md +1 -1
  268. package/docs/routing.md +2 -2
  269. package/package.json +4 -2
@@ -1,21 +1,22 @@
1
1
  import { page } from '../router';
2
- import { render, styles, html, css, ready, dispose } from 'snice';
2
+ import { render, styles, html, css, ready, dispose, watch, on } from 'snice';
3
3
  import type { Placard } from 'snice';
4
- import { authGuard } from '../guards/auth';
4
+ import { isAuthenticated } from '../guards/auth';
5
5
  import { getNotificationsDaemon } from '../daemons/notifications';
6
- import type { Notification } from '../types/notifications';
6
+ import type { Notification, NotificationType } from '../types/notifications';
7
7
 
8
8
  const placard: Placard = {
9
9
  name: 'notifications',
10
10
  title: 'Notifications',
11
- icon: '🔔',
11
+ icon: '\ud83d\udd14',
12
12
  show: true,
13
13
  order: 3
14
14
  };
15
15
 
16
- @page({ tag: 'notifications-page', routes: ['/notifications'], guards: [authGuard], placard })
16
+ @page({ tag: 'notifications-page', routes: ['/notifications'], guards: [isAuthenticated], placard })
17
17
  export class NotificationsPage extends HTMLElement {
18
18
  notifications: Notification[] = [];
19
+ filter: NotificationType | 'all' = 'all';
19
20
  private unsubscribe: (() => void) | null = null;
20
21
 
21
22
  @ready()
@@ -34,6 +35,16 @@ export class NotificationsPage extends HTMLElement {
34
35
  }
35
36
  }
36
37
 
38
+ @watch('filter')
39
+ onFilterChange() {
40
+ // Re-render triggers automatically via property change
41
+ }
42
+
43
+ get filteredNotifications(): Notification[] {
44
+ if (this.filter === 'all') return this.notifications;
45
+ return this.notifications.filter(n => n.type === this.filter);
46
+ }
47
+
37
48
  getVariant(type: string): string {
38
49
  const variants: Record<string, string> = {
39
50
  info: 'info',
@@ -44,55 +55,93 @@ export class NotificationsPage extends HTMLElement {
44
55
  return variants[type] || 'info';
45
56
  }
46
57
 
58
+ @on('keydown:ctrl+Backspace')
47
59
  clearAll() {
48
60
  this.notifications = [];
49
61
  }
50
62
 
63
+ setFilter(filter: NotificationType | 'all') {
64
+ this.filter = filter;
65
+ }
66
+
51
67
  removeNotification(id: string) {
52
68
  this.notifications = this.notifications.filter(n => n.id !== id);
53
69
  }
54
70
 
55
71
  @render()
56
72
  renderContent() {
73
+ const filtered = this.filteredNotifications;
74
+ const hasNotifications = filtered.length > 0;
75
+ const isEmpty = filtered.length === 0;
76
+
57
77
  return html`
58
78
  <div class="container">
59
79
  <div class="header">
60
- <h1>Notifications</h1>
80
+ <div>
81
+ <h1>Notifications</h1>
82
+ <span class="count">${this.notifications.length} total</span>
83
+ </div>
61
84
  <if ${this.notifications.length > 0}>
62
85
  <snice-button
63
86
  variant="secondary"
64
87
  size="small"
65
88
  @click=${this.clearAll}
66
89
  >
67
- Clear All
90
+ Clear All (Ctrl+Backspace)
68
91
  </snice-button>
69
92
  </if>
70
93
  </div>
71
94
 
72
- <if ${this.notifications.length === 0}>
73
- <snice-empty-state
74
- icon="🔔"
75
- title="No notifications"
76
- description="You'll see live notifications here as they arrive"
77
- ></snice-empty-state>
78
- </if>
79
-
80
95
  <if ${this.notifications.length > 0}>
81
- <div class="notifications">
82
- ${this.notifications.map(notification => html`
83
- <snice-alert
84
- key=${notification.id}
85
- variant="${this.getVariant(notification.type)}"
86
- dismissible
87
- @dismiss=${() => this.removeNotification(notification.id)}
88
- >
89
- <strong>${notification.title}</strong>
90
- <p>${notification.message}</p>
91
- <small>${new Date(notification.timestamp).toLocaleTimeString()}</small>
92
- </snice-alert>
93
- `)}
96
+ <div class="filters">
97
+ <button
98
+ class="filter-btn ${this.filter === 'all' ? 'active' : ''}"
99
+ @click=${() => this.setFilter('all')}
100
+ >All</button>
101
+ <button
102
+ class="filter-btn ${this.filter === 'info' ? 'active' : ''}"
103
+ @click=${() => this.setFilter('info')}
104
+ >Info</button>
105
+ <button
106
+ class="filter-btn ${this.filter === 'success' ? 'active' : ''}"
107
+ @click=${() => this.setFilter('success')}
108
+ >Success</button>
109
+ <button
110
+ class="filter-btn ${this.filter === 'warning' ? 'active' : ''}"
111
+ @click=${() => this.setFilter('warning')}
112
+ >Warning</button>
113
+ <button
114
+ class="filter-btn ${this.filter === 'error' ? 'active' : ''}"
115
+ @click=${() => this.setFilter('error')}
116
+ >Error</button>
94
117
  </div>
95
118
  </if>
119
+
120
+ <case ${isEmpty ? 'empty' : 'list'}>
121
+ <when value="empty">
122
+ <snice-empty-state
123
+ icon="\ud83d\udd14"
124
+ title="No notifications"
125
+ description="You'll see live notifications here as they arrive"
126
+ ></snice-empty-state>
127
+ </when>
128
+ <default>
129
+ <div class="notifications">
130
+ ${filtered.map(notification => html`
131
+ <snice-alert
132
+ key=${notification.id}
133
+ variant="${this.getVariant(notification.type)}"
134
+ dismissible
135
+ @dismiss=${() => this.removeNotification(notification.id)}
136
+ >
137
+ <strong>${notification.title}</strong>
138
+ <p>${notification.message}</p>
139
+ <small>${new Date(notification.timestamp).toLocaleTimeString()}</small>
140
+ </snice-alert>
141
+ `)}
142
+ </div>
143
+ </default>
144
+ </case>
96
145
  </div>
97
146
  `;
98
147
  }
@@ -109,8 +158,8 @@ export class NotificationsPage extends HTMLElement {
109
158
  .header {
110
159
  display: flex;
111
160
  justify-content: space-between;
112
- align-items: center;
113
- margin-bottom: 2rem;
161
+ align-items: flex-start;
162
+ margin-bottom: 1.5rem;
114
163
  }
115
164
 
116
165
  h1 {
@@ -118,6 +167,39 @@ export class NotificationsPage extends HTMLElement {
118
167
  color: var(--primary-color);
119
168
  }
120
169
 
170
+ .count {
171
+ font-size: 0.8125rem;
172
+ color: var(--text-light);
173
+ }
174
+
175
+ .filters {
176
+ display: flex;
177
+ gap: 0.5rem;
178
+ margin-bottom: 1.5rem;
179
+ }
180
+
181
+ .filter-btn {
182
+ padding: 0.375rem 0.75rem;
183
+ border: 1px solid var(--border-color);
184
+ border-radius: var(--radius-sm);
185
+ background: var(--bg-primary);
186
+ color: var(--text-light);
187
+ font-size: 0.8125rem;
188
+ cursor: pointer;
189
+ transition: all 0.15s;
190
+ }
191
+
192
+ .filter-btn:hover {
193
+ border-color: var(--primary-color);
194
+ color: var(--primary-color);
195
+ }
196
+
197
+ .filter-btn.active {
198
+ background: var(--primary-color);
199
+ border-color: var(--primary-color);
200
+ color: white;
201
+ }
202
+
121
203
  .notifications {
122
204
  display: flex;
123
205
  flex-direction: column;
@@ -1,22 +1,23 @@
1
1
  import { page } from '../router';
2
- import { render, styles, html, css, context } from 'snice';
2
+ import { render, styles, html, css, context, dispatch, observe } from 'snice';
3
3
  import type { Placard, Context } from 'snice';
4
4
  import type { Principal, User } from '../types/auth';
5
- import { authGuard } from '../guards/auth';
5
+ import { isAuthenticated } from '../guards/auth';
6
6
  import { logout } from '../services/auth';
7
7
 
8
8
  const placard: Placard = {
9
9
  name: 'profile',
10
10
  title: 'Profile',
11
- icon: '👤',
11
+ icon: '\ud83d\udc64',
12
12
  show: true,
13
13
  order: 2
14
14
  };
15
15
 
16
- @page({ tag: 'profile-page', routes: ['/profile'], guards: [authGuard], placard })
16
+ @page({ tag: 'profile-page', routes: ['/profile'], guards: [isAuthenticated], placard })
17
17
  export class ProfilePage extends HTMLElement {
18
18
  user: User | null = null;
19
19
  private ctx?: Context;
20
+ isMobile = false;
20
21
 
21
22
  @context()
22
23
  handleContext(ctx: Context) {
@@ -25,67 +26,87 @@ export class ProfilePage extends HTMLElement {
25
26
  this.user = principal?.user || null;
26
27
  }
27
28
 
29
+ @observe('media:(max-width: 640px)')
30
+ handleMediaChange(matches: boolean) {
31
+ this.isMobile = matches;
32
+ }
33
+
34
+ @dispatch('user-logout')
28
35
  async handleLogout() {
29
36
  await logout();
30
37
 
31
- // Update context to reflect logged out state
32
38
  if (this.ctx && this.ctx.application.principal) {
33
39
  const principal = this.ctx.application.principal as Principal;
34
40
  principal.user = null;
35
41
  principal.isAuthenticated = false;
36
42
  }
37
43
 
38
- window.location.href = '#/login';
44
+ window.location.hash = '#/login';
45
+ return { loggedOut: true };
39
46
  }
40
47
 
41
48
  @render()
42
49
  renderContent() {
43
- if (!this.user) {
44
- return html`<div class="container"><snice-spinner></snice-spinner></div>`;
45
- }
50
+ const hasUser = this.user !== null;
46
51
 
47
52
  return html`
48
53
  <div class="container">
49
- <snice-card class="profile-card">
50
- <div class="header">
51
- <snice-avatar
52
- src="${this.user.avatar}"
53
- name="${this.user.name}"
54
- size="large"
55
- ></snice-avatar>
56
- <h1>${this.user.name}</h1>
57
- <p class="email">${this.user.email}</p>
58
- </div>
59
-
60
- <div class="section">
61
- <h3>Account Information</h3>
62
- <div class="info-grid">
63
- <div class="info-item">
64
- <span class="label">User ID:</span>
65
- <span class="value">${this.user.id}</span>
54
+ <case ${hasUser ? 'loaded' : 'loading'}>
55
+ <when value="loading">
56
+ <div class="center"><snice-spinner></snice-spinner></div>
57
+ </when>
58
+ <default>
59
+ <snice-card class="profile-card">
60
+ <div class="profile-header">
61
+ <snice-avatar
62
+ src="${this.user?.avatar || ''}"
63
+ name="${this.user?.name || ''}"
64
+ size="large"
65
+ ></snice-avatar>
66
+ <h1>${this.user?.name}</h1>
67
+ <p class="email">${this.user?.email}</p>
68
+ <div class="header-actions">
69
+ <a href="#/settings">
70
+ <snice-button variant="secondary" size="small">Edit Profile</snice-button>
71
+ </a>
72
+ </div>
66
73
  </div>
67
- <div class="info-item">
68
- <span class="label">Email:</span>
69
- <span class="value">${this.user.email}</span>
74
+
75
+ <snice-divider></snice-divider>
76
+
77
+ <div class="section">
78
+ <h3>Account Information</h3>
79
+ <div class="info-grid">
80
+ <div class="info-item">
81
+ <span class="label">User ID</span>
82
+ <span class="value">${this.user?.id}</span>
83
+ </div>
84
+ <div class="info-item">
85
+ <span class="label">Email</span>
86
+ <span class="value">${this.user?.email}</span>
87
+ </div>
88
+ <div class="info-item">
89
+ <span class="label">Name</span>
90
+ <span class="value">${this.user?.name}</span>
91
+ </div>
92
+ </div>
70
93
  </div>
71
- <div class="info-item">
72
- <span class="label">Name:</span>
73
- <span class="value">${this.user.name}</span>
94
+
95
+ <snice-divider></snice-divider>
96
+
97
+ <div class="section">
98
+ <h3>Session</h3>
99
+ <p>You are currently logged in with JWT authentication.</p>
100
+ <snice-button
101
+ variant="danger"
102
+ @click=${this.handleLogout}
103
+ >
104
+ Sign Out
105
+ </snice-button>
74
106
  </div>
75
- </div>
76
- </div>
77
-
78
- <div class="section">
79
- <h3>Session</h3>
80
- <p>You are currently logged in with JWT authentication.</p>
81
- <snice-button
82
- variant="danger"
83
- @click=${this.handleLogout}
84
- >
85
- Sign Out
86
- </snice-button>
87
- </div>
88
- </snice-card>
107
+ </snice-card>
108
+ </default>
109
+ </case>
89
110
  </div>
90
111
  `;
91
112
  }
@@ -99,14 +120,19 @@ export class ProfilePage extends HTMLElement {
99
120
  margin: 0 auto;
100
121
  }
101
122
 
123
+ .center {
124
+ display: flex;
125
+ justify-content: center;
126
+ padding: 3rem;
127
+ }
128
+
102
129
  .profile-card {
103
130
  padding: 2rem;
104
131
  }
105
132
 
106
- .header {
133
+ .profile-header {
107
134
  text-align: center;
108
- padding-bottom: 2rem;
109
- border-bottom: 1px solid var(--border-color);
135
+ padding-bottom: 1.5rem;
110
136
  }
111
137
 
112
138
  snice-avatar {
@@ -119,12 +145,20 @@ export class ProfilePage extends HTMLElement {
119
145
  }
120
146
 
121
147
  .email {
122
- margin: 0;
148
+ margin: 0 0 1rem 0;
123
149
  color: var(--text-light);
124
150
  }
125
151
 
152
+ .header-actions a {
153
+ text-decoration: none;
154
+ }
155
+
156
+ snice-divider {
157
+ margin: 0.5rem 0;
158
+ }
159
+
126
160
  .section {
127
- padding-top: 2rem;
161
+ padding-top: 1.5rem;
128
162
  }
129
163
 
130
164
  h3 {
@@ -135,7 +169,7 @@ export class ProfilePage extends HTMLElement {
135
169
  .info-grid {
136
170
  display: flex;
137
171
  flex-direction: column;
138
- gap: 1rem;
172
+ gap: 0.75rem;
139
173
  }
140
174
 
141
175
  .info-item {