@getmicdrop/svelte-components 5.3.12 → 5.3.13

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 (248) hide show
  1. package/dist/calendar/AboutShow/AboutShow.svelte +172 -172
  2. package/dist/calendar/Calendar/MiniMonthCalendar.svelte +782 -782
  3. package/dist/calendar/FAQs/FAQs.svelte +75 -75
  4. package/dist/calendar/MonthSwitcher/MonthSwitcher.svelte +126 -126
  5. package/dist/calendar/OrderSummary/OrderSummary.svelte +367 -367
  6. package/dist/calendar/PublicCard/PublicCard.svelte +145 -145
  7. package/dist/calendar/ShowCard/ShowCard.svelte +157 -157
  8. package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte +61 -61
  9. package/dist/components/Layout/Grid.svelte +109 -109
  10. package/dist/components/Layout/Section.svelte +80 -80
  11. package/dist/components/Layout/Sidebar.svelte +108 -108
  12. package/dist/components/Layout/Stack.svelte +90 -90
  13. package/dist/constants/formOptions.js +26 -26
  14. package/dist/constants/validation.js +91 -91
  15. package/dist/constants/validation.spec.js +64 -64
  16. package/dist/datetime/__tests__/format.test.d.ts +2 -0
  17. package/dist/datetime/__tests__/format.test.d.ts.map +1 -0
  18. package/dist/datetime/__tests__/format.test.js +268 -0
  19. package/dist/datetime/__tests__/integration.test.d.ts +2 -0
  20. package/dist/datetime/__tests__/integration.test.d.ts.map +1 -0
  21. package/dist/datetime/__tests__/integration.test.js +243 -0
  22. package/dist/datetime/__tests__/parse.test.d.ts +2 -0
  23. package/dist/datetime/__tests__/parse.test.d.ts.map +1 -0
  24. package/dist/datetime/__tests__/parse.test.js +261 -0
  25. package/dist/datetime/__tests__/timezone.test.d.ts +2 -0
  26. package/dist/datetime/__tests__/timezone.test.d.ts.map +1 -0
  27. package/dist/datetime/__tests__/timezone.test.js +214 -0
  28. package/dist/datetime/constants.d.ts +133 -0
  29. package/dist/datetime/constants.d.ts.map +1 -0
  30. package/dist/datetime/constants.js +112 -0
  31. package/dist/datetime/format.d.ts +158 -0
  32. package/dist/datetime/format.d.ts.map +1 -0
  33. package/dist/datetime/format.js +315 -0
  34. package/dist/datetime/index.d.ts +42 -0
  35. package/dist/datetime/index.d.ts.map +1 -0
  36. package/dist/datetime/index.js +44 -0
  37. package/dist/datetime/parse.d.ts +149 -0
  38. package/dist/datetime/parse.d.ts.map +1 -0
  39. package/dist/datetime/parse.js +276 -0
  40. package/dist/datetime/timezone.d.ts +95 -0
  41. package/dist/datetime/timezone.d.ts.map +1 -0
  42. package/dist/datetime/timezone.js +241 -0
  43. package/dist/datetime/types.d.ts +105 -0
  44. package/dist/datetime/types.d.ts.map +1 -0
  45. package/dist/datetime/types.js +31 -0
  46. package/dist/index.d.ts +10 -0
  47. package/dist/index.js +232 -218
  48. package/dist/patterns/data/DataGrid.svelte +45 -45
  49. package/dist/patterns/data/DataList.svelte +24 -24
  50. package/dist/patterns/data/DataTable.svelte +40 -40
  51. package/dist/patterns/forms/FormActions.spec.js +88 -88
  52. package/dist/patterns/forms/FormActions.stories.svelte +97 -97
  53. package/dist/patterns/forms/FormActions.svelte +46 -46
  54. package/dist/patterns/forms/FormGrid.svelte +33 -33
  55. package/dist/patterns/forms/FormSection.svelte +32 -32
  56. package/dist/patterns/forms/FormValidationSummary.spec.js +203 -203
  57. package/dist/patterns/forms/FormValidationSummary.stories.svelte +97 -97
  58. package/dist/patterns/forms/FormValidationSummary.svelte +67 -67
  59. package/dist/patterns/layout/Grid.svelte +35 -35
  60. package/dist/patterns/layout/Sidebar.svelte +39 -39
  61. package/dist/patterns/layout/Stack.svelte +45 -45
  62. package/dist/patterns/navigation/BottomNav.spec.js +130 -130
  63. package/dist/patterns/navigation/BottomNav.stories.svelte +117 -117
  64. package/dist/patterns/navigation/BottomNav.svelte +54 -54
  65. package/dist/patterns/navigation/Header.spec.js +203 -203
  66. package/dist/patterns/navigation/Header.stories.svelte +77 -77
  67. package/dist/patterns/navigation/Header.svelte +240 -240
  68. package/dist/patterns/page/PageHeader.svelte +36 -36
  69. package/dist/patterns/page/PageLayout.svelte +40 -40
  70. package/dist/patterns/page/PageLoader.spec.js +54 -54
  71. package/dist/patterns/page/PageLoader.stories.svelte +137 -137
  72. package/dist/patterns/page/PageLoader.svelte +41 -41
  73. package/dist/patterns/page/SectionHeader.svelte +41 -41
  74. package/dist/presets/badges.js +112 -112
  75. package/dist/presets/buttons.js +76 -76
  76. package/dist/presets/index.js +9 -9
  77. package/dist/primitives/Accordion/Accordion.stories.svelte +75 -75
  78. package/dist/primitives/Accordion/Accordion.svelte +61 -61
  79. package/dist/primitives/Accordion/AccordionItem.svelte +95 -95
  80. package/dist/primitives/Alert/Alert.spec.js +170 -170
  81. package/dist/primitives/Alert/Alert.stories.svelte +88 -88
  82. package/dist/primitives/Alert/Alert.svelte +65 -65
  83. package/dist/primitives/Avatar/Avatar.stories.svelte +94 -94
  84. package/dist/primitives/Avatar/Avatar.svelte +66 -66
  85. package/dist/primitives/Badges/Badge.spec.js +103 -103
  86. package/dist/primitives/Badges/Badge.stories.svelte +86 -86
  87. package/dist/primitives/Badges/Badge.svelte +142 -142
  88. package/dist/primitives/BottomSheet/BottomSheet.spec.js +127 -127
  89. package/dist/primitives/BottomSheet/BottomSheet.stories.svelte +83 -83
  90. package/dist/primitives/BottomSheet/BottomSheet.svelte +100 -100
  91. package/dist/primitives/Breadcrumb/Breadcrumb.spec.js +120 -120
  92. package/dist/primitives/Breadcrumb/Breadcrumb.stories.svelte +23 -23
  93. package/dist/primitives/Breadcrumb/Breadcrumb.svelte +89 -89
  94. package/dist/primitives/Button/Button.spec.js +211 -211
  95. package/dist/primitives/Button/Button.stories.svelte +76 -76
  96. package/dist/primitives/Button/Button.svelte +301 -301
  97. package/dist/primitives/Button/ButtonSaveDemo.spec.js +48 -48
  98. package/dist/primitives/Button/ButtonSaveDemo.svelte +25 -25
  99. package/dist/primitives/Button/ButtonVariantShowcase.svelte +129 -129
  100. package/dist/primitives/Card.spec.js +49 -49
  101. package/dist/primitives/Card.stories.svelte +22 -22
  102. package/dist/primitives/Card.svelte +28 -28
  103. package/dist/primitives/Checkbox/Checkbox.stories.svelte +84 -84
  104. package/dist/primitives/Checkbox/Checkbox.svelte +88 -88
  105. package/dist/primitives/DarkModeToggle.spec.js +357 -357
  106. package/dist/primitives/DarkModeToggle.stories.svelte +57 -57
  107. package/dist/primitives/DarkModeToggle.svelte +136 -136
  108. package/dist/primitives/Drawer/Drawer.stories.svelte +100 -100
  109. package/dist/primitives/Drawer/Drawer.svelte +214 -214
  110. package/dist/primitives/Dropdown/Dropdown.stories.svelte +137 -137
  111. package/dist/primitives/Dropdown/Dropdown.svelte +148 -148
  112. package/dist/primitives/Dropdown/DropdownItem.svelte +80 -80
  113. package/dist/primitives/Icons/ArrowLeft.svelte +20 -20
  114. package/dist/primitives/Icons/ArrowRight.svelte +20 -20
  115. package/dist/primitives/Icons/Availability.svelte +26 -26
  116. package/dist/primitives/Icons/Back.svelte +26 -26
  117. package/dist/primitives/Icons/CheckCircle.svelte +18 -18
  118. package/dist/primitives/Icons/CheckCircleOutline.svelte +27 -27
  119. package/dist/primitives/Icons/ChevronLeft.svelte +16 -16
  120. package/dist/primitives/Icons/ChevronRight.svelte +16 -16
  121. package/dist/primitives/Icons/Copy.svelte +27 -27
  122. package/dist/primitives/Icons/Cross.svelte +17 -17
  123. package/dist/primitives/Icons/DownArrow.svelte +20 -20
  124. package/dist/primitives/Icons/ErrorCircle.svelte +18 -18
  125. package/dist/primitives/Icons/FacebookIcon.svelte +13 -13
  126. package/dist/primitives/Icons/Home.svelte +27 -27
  127. package/dist/primitives/Icons/Icon.spec.js +175 -175
  128. package/dist/primitives/Icons/Icon.stories.svelte +100 -100
  129. package/dist/primitives/Icons/Icon.svelte +63 -63
  130. package/dist/primitives/Icons/IconGallery.stories.svelte +235 -235
  131. package/dist/primitives/Icons/ImageOutline.svelte +19 -19
  132. package/dist/primitives/Icons/Info.svelte +19 -19
  133. package/dist/primitives/Icons/InstagramIcon.svelte +19 -19
  134. package/dist/primitives/Icons/LogoInstagram.svelte +15 -15
  135. package/dist/primitives/Icons/Message.svelte +27 -27
  136. package/dist/primitives/Icons/MoonIcon.svelte +16 -16
  137. package/dist/primitives/Icons/More.svelte +33 -33
  138. package/dist/primitives/Icons/MoreHori.spec.js +67 -67
  139. package/dist/primitives/Icons/MoreHori.svelte +34 -34
  140. package/dist/primitives/Icons/Notification.svelte +26 -26
  141. package/dist/primitives/Icons/Payment.svelte +26 -26
  142. package/dist/primitives/Icons/Profile.svelte +33 -33
  143. package/dist/primitives/Icons/Reload.svelte +41 -41
  144. package/dist/primitives/Icons/Shows.svelte +33 -33
  145. package/dist/primitives/Icons/Signout.svelte +33 -33
  146. package/dist/primitives/Icons/SunIcon.svelte +19 -19
  147. package/dist/primitives/Icons/TiktokIcon.svelte +13 -13
  148. package/dist/primitives/Icons/TrashBinOutline.svelte +19 -19
  149. package/dist/primitives/Icons/TwitterIcon.svelte +13 -13
  150. package/dist/primitives/Icons/WarningIcon.spec.js +30 -30
  151. package/dist/primitives/Icons/WarningIcon.svelte +24 -24
  152. package/dist/primitives/Input/Input.spec.js +573 -573
  153. package/dist/primitives/Input/Input.stories.svelte +139 -139
  154. package/dist/primitives/Input/Input.svelte +444 -444
  155. package/dist/primitives/Input/Select.spec.js +218 -218
  156. package/dist/primitives/Input/Select.stories.svelte +112 -112
  157. package/dist/primitives/Input/Select.svelte +232 -232
  158. package/dist/primitives/Input/Textarea.stories.svelte +137 -137
  159. package/dist/primitives/Input/Textarea.svelte +79 -79
  160. package/dist/primitives/Label/Label.svelte +37 -37
  161. package/dist/primitives/Modal/Modal.spec.js +95 -95
  162. package/dist/primitives/Modal/Modal.stories.svelte +86 -86
  163. package/dist/primitives/Modal/Modal.svelte +158 -158
  164. package/dist/primitives/Pagination/Pagination.stories.svelte +76 -76
  165. package/dist/primitives/Pagination/Pagination.svelte +261 -261
  166. package/dist/primitives/Radio/Radio.stories.svelte +80 -80
  167. package/dist/primitives/Radio/Radio.svelte +67 -67
  168. package/dist/primitives/Skeleton/CardPlaceholder.svelte +87 -87
  169. package/dist/primitives/Skeleton/ImagePlaceholder.svelte +59 -59
  170. package/dist/primitives/Skeleton/ListPlaceholder.svelte +76 -76
  171. package/dist/primitives/Skeleton/Skeleton.stories.svelte +151 -151
  172. package/dist/primitives/Skeleton/Skeleton.svelte +52 -52
  173. package/dist/primitives/Spinner/Spinner.spec.js +75 -75
  174. package/dist/primitives/Spinner/Spinner.stories.svelte +29 -29
  175. package/dist/primitives/Spinner/Spinner.svelte +57 -57
  176. package/dist/primitives/Tabs/TabItem.svelte +51 -51
  177. package/dist/primitives/Tabs/Tabs.stories.svelte +112 -112
  178. package/dist/primitives/Tabs/Tabs.svelte +128 -128
  179. package/dist/primitives/Toggle.spec.js +127 -127
  180. package/dist/primitives/Toggle.stories.svelte +92 -92
  181. package/dist/primitives/Toggle.svelte +71 -71
  182. package/dist/primitives/Typography/Typography.svelte +53 -53
  183. package/dist/primitives/ValidationError.spec.js +103 -103
  184. package/dist/primitives/ValidationError.stories.svelte +111 -111
  185. package/dist/primitives/ValidationError.svelte +29 -29
  186. package/dist/recipes/CropImage/CropImage.spec.js +216 -216
  187. package/dist/recipes/CropImage/CropImage.stories.svelte +104 -104
  188. package/dist/recipes/CropImage/CropImage.svelte +238 -238
  189. package/dist/recipes/ImageUploader/ImageUploader.stories.svelte +125 -125
  190. package/dist/recipes/ImageUploader/ImageUploader.svelte +980 -980
  191. package/dist/recipes/Toaster/Toaster.stories.svelte +62 -62
  192. package/dist/recipes/feedback/EmptyState/EmptyState.svelte +47 -47
  193. package/dist/recipes/feedback/ErrorDisplay.spec.js +69 -69
  194. package/dist/recipes/feedback/ErrorDisplay.stories.svelte +112 -112
  195. package/dist/recipes/feedback/ErrorDisplay.svelte +38 -38
  196. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.spec.js +129 -129
  197. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.svelte +167 -167
  198. package/dist/recipes/fields/CheckboxField.svelte +85 -85
  199. package/dist/recipes/fields/FormField.svelte +58 -58
  200. package/dist/recipes/fields/RadioGroup.svelte +95 -95
  201. package/dist/recipes/fields/SelectField.svelte +82 -82
  202. package/dist/recipes/fields/TextareaField.svelte +101 -101
  203. package/dist/recipes/fields/ToggleField.svelte +60 -60
  204. package/dist/recipes/fields/index.js +7 -7
  205. package/dist/recipes/inputs/MultiSelect.spec.js +257 -257
  206. package/dist/recipes/inputs/MultiSelect.stories.svelte +133 -133
  207. package/dist/recipes/inputs/MultiSelect.svelte +244 -244
  208. package/dist/recipes/inputs/OTPInput.spec.js +238 -238
  209. package/dist/recipes/inputs/OTPInput.stories.svelte +162 -162
  210. package/dist/recipes/inputs/OTPInput.svelte +102 -102
  211. package/dist/recipes/inputs/PasswordInput.svelte +100 -100
  212. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.spec.js +173 -173
  213. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.svelte +108 -108
  214. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.spec.js +300 -300
  215. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.stories.svelte +165 -165
  216. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.svelte +337 -337
  217. package/dist/recipes/inputs/Search.svelte +85 -85
  218. package/dist/recipes/inputs/SelectDropdown.svelte +161 -161
  219. package/dist/recipes/modals/AlertModal.svelte +130 -130
  220. package/dist/recipes/modals/ConfirmationModal.spec.js +191 -191
  221. package/dist/recipes/modals/ConfirmationModal.stories.svelte +119 -119
  222. package/dist/recipes/modals/ConfirmationModal.svelte +152 -152
  223. package/dist/recipes/modals/InputModal.svelte +182 -182
  224. package/dist/recipes/modals/ModalStateManager.spec.js +100 -100
  225. package/dist/recipes/modals/ModalStateManager.svelte +77 -77
  226. package/dist/recipes/modals/ModalTestWrapper.svelte +65 -65
  227. package/dist/recipes/modals/StatusModal.svelte +206 -206
  228. package/dist/services/EventService.js +75 -75
  229. package/dist/services/EventService.spec.js +217 -217
  230. package/dist/services/ShowService.spec.js +342 -342
  231. package/dist/stores/auth.js +93 -6
  232. package/dist/stores/auth.spec.js +310 -2
  233. package/dist/stores/toaster.js +13 -13
  234. package/dist/stories/ButtonAuditReview.stories.svelte +14 -14
  235. package/dist/stories/ButtonAuditReview.svelte +427 -427
  236. package/dist/stories/PatternsGallery.stories.svelte +19 -19
  237. package/dist/stories/PatternsGallery.svelte +388 -388
  238. package/dist/stories/PrimitivesGallery.stories.svelte +19 -19
  239. package/dist/stories/PrimitivesGallery.svelte +752 -752
  240. package/dist/stories/RecipesGallery.stories.svelte +19 -19
  241. package/dist/stories/RecipesGallery.svelte +441 -441
  242. package/dist/stories/button-audit-manifest.json +11186 -11186
  243. package/dist/tailwind/preset.cjs +82 -82
  244. package/dist/telemetry.js +357 -357
  245. package/dist/tokens/tokens.css +87 -87
  246. package/dist/utils/apiConfig.js +49 -49
  247. package/dist/utils/utils.js +9 -1
  248. package/package.json +233 -191
@@ -1,12 +1,22 @@
1
- import { writable } from "svelte/store";
1
+ import { writable, get, derived } from "svelte/store";
2
+ import { jwtDecode } from "jwt-decode";
2
3
 
3
4
  // Store to manage authentication state
4
5
  export const auth = writable({
5
6
  isAuthenticated: false,
6
7
  token: null,
8
+ refreshToken: null,
7
9
  userDetails: null, // To store email and first name
10
+ tokenExpiry: null, // Token expiration timestamp
11
+ permissions: null, // RBAC permissions
12
+ context: null, // User context (performer)
8
13
  });
9
14
 
15
+ // Derived store for checking if user has performer permissions
16
+ export const isPerformer = derived(auth, ($auth) =>
17
+ $auth.context?.contextType === 'performer' || $auth.isAuthenticated
18
+ );
19
+
10
20
  // Function to set authentication state
11
21
  export const setAuthState = (state) => {
12
22
  auth.set(state);
@@ -14,23 +24,100 @@ export const setAuthState = (state) => {
14
24
 
15
25
  // Function to clear the authentication state
16
26
  export const clearAuthState = () => {
17
- auth.set({ isAuthenticated: false, token: null, userDetails: null });
27
+ auth.set({
28
+ isAuthenticated: false,
29
+ token: null,
30
+ refreshToken: null,
31
+ userDetails: null,
32
+ tokenExpiry: null,
33
+ permissions: null,
34
+ context: null,
35
+ });
18
36
  };
19
37
 
20
- // Initialize the store from cookies
21
- export const initializeAuthState = () => {
22
- const cookies = Object.fromEntries(
38
+ // Parse cookies helper
39
+ const parseCookies = () => {
40
+ if (typeof document === "undefined") return {};
41
+ return Object.fromEntries(
23
42
  document.cookie
24
43
  .split("; ")
44
+ .filter(c => c)
25
45
  .map((c) => c.split("="))
26
- .map(([k, v]) => [k, decodeURIComponent(v)])
46
+ .map(([k, v]) => [k, decodeURIComponent(v || "")])
27
47
  );
48
+ };
49
+
50
+ // Get token expiry from JWT
51
+ const getTokenExpiry = (token) => {
52
+ if (!token) return null;
53
+ try {
54
+ const decoded = jwtDecode(token);
55
+ return decoded.exp ? decoded.exp * 1000 : null; // Convert to milliseconds
56
+ } catch {
57
+ return null;
58
+ }
59
+ };
60
+
61
+ // Check if token is expired or about to expire (within 5 minutes)
62
+ export const isTokenExpiringSoon = (bufferMs = 5 * 60 * 1000) => {
63
+ const state = get(auth);
64
+ if (!state.tokenExpiry) return true;
65
+ return Date.now() >= (state.tokenExpiry - bufferMs);
66
+ };
67
+
68
+ // Initialize the store from cookies
69
+ export const initializeAuthState = () => {
70
+ const cookies = parseCookies();
28
71
 
29
72
  if (cookies.performer_token) {
73
+ const tokenExpiry = getTokenExpiry(cookies.performer_token);
74
+
30
75
  setAuthState({
31
76
  isAuthenticated: true,
32
77
  token: cookies.performer_token,
78
+ refreshToken: cookies.performer_refresh_token || null,
33
79
  userDetails: cookies.userDetails ? JSON.parse(cookies.userDetails) : null,
80
+ tokenExpiry,
34
81
  });
35
82
  }
36
83
  };
84
+
85
+ // Update tokens after a refresh
86
+ export const updateTokens = (accessToken, refreshToken) => {
87
+ auth.update(state => ({
88
+ ...state,
89
+ token: accessToken,
90
+ refreshToken: refreshToken || state.refreshToken,
91
+ tokenExpiry: getTokenExpiry(accessToken),
92
+ }));
93
+ };
94
+
95
+ // Update permissions
96
+ export const setPermissions = (permissions) => {
97
+ auth.update(state => ({
98
+ ...state,
99
+ permissions,
100
+ }));
101
+ };
102
+
103
+ // Set user context
104
+ export const setContext = (context) => {
105
+ auth.update(state => ({
106
+ ...state,
107
+ context,
108
+ }));
109
+ };
110
+
111
+ // Check if user has a specific permission
112
+ export const hasPermission = (category, permission) => {
113
+ const state = get(auth);
114
+ if (!state.permissions) return false;
115
+
116
+ // Performers have limited permissions by design
117
+ return state.permissions[category]?.[permission] ?? false;
118
+ };
119
+
120
+ // Get current permissions
121
+ export const getPermissions = () => {
122
+ return get(auth).permissions;
123
+ };
@@ -1,6 +1,31 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
2
  import { get } from 'svelte/store';
3
- import { auth, setAuthState, clearAuthState, initializeAuthState } from './auth';
3
+ import {
4
+ auth,
5
+ isPerformer,
6
+ setAuthState,
7
+ clearAuthState,
8
+ initializeAuthState,
9
+ updateTokens,
10
+ setPermissions,
11
+ setContext,
12
+ hasPermission,
13
+ getPermissions,
14
+ isTokenExpiringSoon
15
+ } from './auth';
16
+
17
+ // Mock jwt-decode
18
+ vi.mock('jwt-decode', () => ({
19
+ jwtDecode: vi.fn((token) => {
20
+ // Parse mock tokens in format: "token-exp-{timestamp}"
21
+ if (token.startsWith('token-exp-')) {
22
+ const exp = parseInt(token.replace('token-exp-', ''), 10);
23
+ return { exp: exp / 1000 }; // Convert ms to seconds (JWT uses seconds)
24
+ }
25
+ // Default: return token that expires in 1 hour
26
+ return { exp: (Date.now() + 3600000) / 1000 };
27
+ })
28
+ }));
4
29
 
5
30
  describe('auth store', () => {
6
31
  beforeEach(() => {
@@ -136,4 +161,287 @@ describe('auth store', () => {
136
161
  expect(values).toEqual([false, true, false]);
137
162
  });
138
163
  });
164
+
165
+ describe('updateTokens', () => {
166
+ it('updates access token and computes new expiry', () => {
167
+ setAuthState({
168
+ isAuthenticated: true,
169
+ token: 'old-token',
170
+ refreshToken: 'old-refresh',
171
+ userDetails: null,
172
+ });
173
+
174
+ updateTokens('new-access-token', 'new-refresh-token');
175
+
176
+ const state = get(auth);
177
+ expect(state.token).toBe('new-access-token');
178
+ expect(state.refreshToken).toBe('new-refresh-token');
179
+ expect(state.tokenExpiry).toBeDefined();
180
+ });
181
+
182
+ it('preserves existing refresh token if not provided', () => {
183
+ setAuthState({
184
+ isAuthenticated: true,
185
+ token: 'old-token',
186
+ refreshToken: 'existing-refresh',
187
+ userDetails: null,
188
+ });
189
+
190
+ updateTokens('new-access-token');
191
+
192
+ const state = get(auth);
193
+ expect(state.token).toBe('new-access-token');
194
+ expect(state.refreshToken).toBe('existing-refresh');
195
+ });
196
+
197
+ it('preserves other state properties when updating tokens', () => {
198
+ setAuthState({
199
+ isAuthenticated: true,
200
+ token: 'old-token',
201
+ refreshToken: 'refresh',
202
+ userDetails: { email: 'test@test.com' },
203
+ permissions: { events: { read: true } },
204
+ context: { contextType: 'performer' },
205
+ });
206
+
207
+ updateTokens('new-token');
208
+
209
+ const state = get(auth);
210
+ expect(state.userDetails).toEqual({ email: 'test@test.com' });
211
+ expect(state.permissions).toEqual({ events: { read: true } });
212
+ expect(state.context).toEqual({ contextType: 'performer' });
213
+ });
214
+ });
215
+
216
+ describe('isTokenExpiringSoon', () => {
217
+ it('returns true when no token expiry is set', () => {
218
+ clearAuthState();
219
+ expect(isTokenExpiringSoon()).toBe(true);
220
+ });
221
+
222
+ it('returns true when token is expired', () => {
223
+ const pastTime = Date.now() - 10000; // 10 seconds ago
224
+ setAuthState({
225
+ isAuthenticated: true,
226
+ token: 'token',
227
+ tokenExpiry: pastTime,
228
+ });
229
+
230
+ expect(isTokenExpiringSoon()).toBe(true);
231
+ });
232
+
233
+ it('returns true when token expires within buffer time', () => {
234
+ const soonExpiry = Date.now() + (2 * 60 * 1000); // 2 minutes from now
235
+ setAuthState({
236
+ isAuthenticated: true,
237
+ token: 'token',
238
+ tokenExpiry: soonExpiry,
239
+ });
240
+
241
+ // Default buffer is 5 minutes, so 2 minutes is within buffer
242
+ expect(isTokenExpiringSoon()).toBe(true);
243
+ });
244
+
245
+ it('returns false when token has plenty of time left', () => {
246
+ const futureExpiry = Date.now() + (30 * 60 * 1000); // 30 minutes from now
247
+ setAuthState({
248
+ isAuthenticated: true,
249
+ token: 'token',
250
+ tokenExpiry: futureExpiry,
251
+ });
252
+
253
+ expect(isTokenExpiringSoon()).toBe(false);
254
+ });
255
+
256
+ it('accepts custom buffer time', () => {
257
+ const expiry = Date.now() + (10 * 60 * 1000); // 10 minutes from now
258
+ setAuthState({
259
+ isAuthenticated: true,
260
+ token: 'token',
261
+ tokenExpiry: expiry,
262
+ });
263
+
264
+ // With 5 minute buffer (default), should be fine
265
+ expect(isTokenExpiringSoon(5 * 60 * 1000)).toBe(false);
266
+
267
+ // With 15 minute buffer, should be expiring soon
268
+ expect(isTokenExpiringSoon(15 * 60 * 1000)).toBe(true);
269
+ });
270
+ });
271
+
272
+ describe('setPermissions and getPermissions', () => {
273
+ it('sets permissions on auth state', () => {
274
+ const permissions = {
275
+ events: { read: true, write: false },
276
+ shows: { read: true, write: true },
277
+ };
278
+
279
+ setPermissions(permissions);
280
+
281
+ const state = get(auth);
282
+ expect(state.permissions).toEqual(permissions);
283
+ });
284
+
285
+ it('getPermissions returns current permissions', () => {
286
+ const permissions = {
287
+ events: { read: true },
288
+ };
289
+
290
+ setPermissions(permissions);
291
+
292
+ expect(getPermissions()).toEqual(permissions);
293
+ });
294
+
295
+ it('getPermissions returns null when no permissions set', () => {
296
+ clearAuthState();
297
+ expect(getPermissions()).toBe(null);
298
+ });
299
+
300
+ it('preserves other state when setting permissions', () => {
301
+ setAuthState({
302
+ isAuthenticated: true,
303
+ token: 'test-token',
304
+ userDetails: { email: 'test@test.com' },
305
+ });
306
+
307
+ setPermissions({ events: { read: true } });
308
+
309
+ const state = get(auth);
310
+ expect(state.isAuthenticated).toBe(true);
311
+ expect(state.token).toBe('test-token');
312
+ expect(state.userDetails).toEqual({ email: 'test@test.com' });
313
+ });
314
+ });
315
+
316
+ describe('setContext', () => {
317
+ it('sets user context on auth state', () => {
318
+ const context = {
319
+ contextType: 'performer',
320
+ performerId: 'perf-123',
321
+ };
322
+
323
+ setContext(context);
324
+
325
+ const state = get(auth);
326
+ expect(state.context).toEqual(context);
327
+ });
328
+
329
+ it('preserves other state when setting context', () => {
330
+ setAuthState({
331
+ isAuthenticated: true,
332
+ token: 'test-token',
333
+ permissions: { events: { read: true } },
334
+ });
335
+
336
+ setContext({ contextType: 'performer' });
337
+
338
+ const state = get(auth);
339
+ expect(state.isAuthenticated).toBe(true);
340
+ expect(state.token).toBe('test-token');
341
+ expect(state.permissions).toEqual({ events: { read: true } });
342
+ });
343
+
344
+ it('can update existing context', () => {
345
+ setContext({ contextType: 'performer', performerId: 'old' });
346
+ setContext({ contextType: 'performer', performerId: 'new' });
347
+
348
+ const state = get(auth);
349
+ expect(state.context.performerId).toBe('new');
350
+ });
351
+ });
352
+
353
+ describe('hasPermission', () => {
354
+ it('returns false when no permissions are set', () => {
355
+ clearAuthState();
356
+ expect(hasPermission('events', 'read')).toBe(false);
357
+ });
358
+
359
+ it('returns true when permission exists and is true', () => {
360
+ setPermissions({
361
+ events: { read: true, write: false },
362
+ });
363
+
364
+ expect(hasPermission('events', 'read')).toBe(true);
365
+ });
366
+
367
+ it('returns false when permission exists and is false', () => {
368
+ setPermissions({
369
+ events: { read: true, write: false },
370
+ });
371
+
372
+ expect(hasPermission('events', 'write')).toBe(false);
373
+ });
374
+
375
+ it('returns false when category does not exist', () => {
376
+ setPermissions({
377
+ events: { read: true },
378
+ });
379
+
380
+ expect(hasPermission('shows', 'read')).toBe(false);
381
+ });
382
+
383
+ it('returns false when permission does not exist in category', () => {
384
+ setPermissions({
385
+ events: { read: true },
386
+ });
387
+
388
+ expect(hasPermission('events', 'delete')).toBe(false);
389
+ });
390
+
391
+ it('works with nested permission categories', () => {
392
+ setPermissions({
393
+ events: { read: true, write: true },
394
+ shows: { read: true, write: false, delete: false },
395
+ admin: { read: false },
396
+ });
397
+
398
+ expect(hasPermission('events', 'read')).toBe(true);
399
+ expect(hasPermission('events', 'write')).toBe(true);
400
+ expect(hasPermission('shows', 'read')).toBe(true);
401
+ expect(hasPermission('shows', 'write')).toBe(false);
402
+ expect(hasPermission('admin', 'read')).toBe(false);
403
+ });
404
+ });
405
+
406
+ describe('isPerformer derived store', () => {
407
+ it('returns true when context type is performer', () => {
408
+ setContext({ contextType: 'performer' });
409
+
410
+ expect(get(isPerformer)).toBe(true);
411
+ });
412
+
413
+ it('returns true when user is authenticated (fallback)', () => {
414
+ setAuthState({
415
+ isAuthenticated: true,
416
+ token: 'token',
417
+ userDetails: null,
418
+ });
419
+
420
+ expect(get(isPerformer)).toBe(true);
421
+ });
422
+
423
+ it('returns false when not authenticated and no performer context', () => {
424
+ clearAuthState();
425
+
426
+ expect(get(isPerformer)).toBe(false);
427
+ });
428
+
429
+ it('updates reactively when context changes', () => {
430
+ const values = [];
431
+ const unsubscribe = isPerformer.subscribe((value) => {
432
+ values.push(value);
433
+ });
434
+
435
+ clearAuthState();
436
+ setContext({ contextType: 'performer' });
437
+ setContext({ contextType: 'staff' });
438
+ setAuthState({ isAuthenticated: true, token: 'token' });
439
+
440
+ unsubscribe();
441
+
442
+ // false (initial), true (performer context), false (staff context, not authenticated), true (authenticated)
443
+ expect(values).toContain(false);
444
+ expect(values).toContain(true);
445
+ });
446
+ });
139
447
  });
@@ -1,13 +1,13 @@
1
- import { toast } from "svelte-sonner";
2
-
3
- export { toast };
4
-
5
- export const showToast = (message, type = "success") => {
6
- if (type === "success") {
7
- toast.success(message);
8
- } else if (type === "error") {
9
- toast.error(message);
10
- } else {
11
- toast.message(message);
12
- }
13
- };
1
+ import { toast } from "svelte-sonner";
2
+
3
+ export { toast };
4
+
5
+ export const showToast = (message, type = "success") => {
6
+ if (type === "success") {
7
+ toast.success(message);
8
+ } else if (type === "error") {
9
+ toast.error(message);
10
+ } else {
11
+ toast.message(message);
12
+ }
13
+ };
@@ -1,14 +1,14 @@
1
- <script module>
2
- import { defineMeta } from "@storybook/addon-svelte-csf";
3
- import ButtonAuditReview from "./ButtonAuditReview.svelte";
4
-
5
- const { Story } = defineMeta({
6
- title: "Design System/Button Audit Review",
7
- component: ButtonAuditReview,
8
- parameters: {
9
- layout: 'fullscreen',
10
- }
11
- });
12
- </script>
13
-
14
- <Story name="All Issues" />
1
+ <script module>
2
+ import { defineMeta } from "@storybook/addon-svelte-csf";
3
+ import ButtonAuditReview from "./ButtonAuditReview.svelte";
4
+
5
+ const { Story } = defineMeta({
6
+ title: "Design System/Button Audit Review",
7
+ component: ButtonAuditReview,
8
+ parameters: {
9
+ layout: 'fullscreen',
10
+ }
11
+ });
12
+ </script>
13
+
14
+ <Story name="All Issues" />