astrogators-shared-ui 0.16.3 → 0.16.4

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 (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -4
  3. package/dist/index.js +255 -239
  4. package/package.json +8 -6
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 psytor
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # astrogators-shared-ui
2
2
 
3
3
  Shared React components, auth, and API client for the Astrogator's Table
4
- frontends (`astrogators-hub`, `mod-ledger-ui`, `nightwatcher-ui`). Published
5
- to the public npm registry as `astrogators-shared-ui` (unscoped).
4
+ frontends (`astrogators-hub`, `mod-ledger-ui`, `nightwatcher-ui`,
5
+ `navicharts-ui`). Published to the public npm registry as
6
+ `astrogators-shared-ui` (unscoped).
6
7
 
7
8
  > The git repo lives under `psytor/astrogators-shared-ui` on GitHub, but the
8
9
  > package itself is published to **npmjs.org**, not GitHub Packages. No
@@ -177,9 +178,14 @@ const result = await apiClient.post('/api/v1/mod-ledger/evaluate/123456789', {
177
178
 
178
179
  The client:
179
180
  - injects the access token into `Authorization`
180
- - on `401`, transparently refreshes via `/api/v1/auth/refresh-token` and
181
- retries the original request once
181
+ - proactively refreshes before a request goes out if the access token is
182
+ expired or expiring within 30s (decoded client-side), and reactively on a
183
+ `401` from the resource server — both via `/api/v1/auth/refresh`, retrying
184
+ the original request once
182
185
  - calls `onUnauthorized` if refresh fails
186
+ - retries a `502`/`503`/`504` response (idempotent methods only) or a thrown
187
+ `fetch` (any method, since nothing reached the server) a couple of times
188
+ with a short backoff, to ride out a backend container swap mid-deploy
183
189
 
184
190
  Endpoints are written **without** the service prefix — the prefix lives in
185
191
  the configured `baseURL`.
@@ -254,6 +260,7 @@ Sci-fi cut-corner effect, available as utility classes or via `Card`:
254
260
  npm install
255
261
  npm run build # tsc && vite build → dist/
256
262
  npm run type-check # tsc --noEmit
263
+ npm test # vitest run — logic tests in tests/ (not visual)
257
264
  ```
258
265
 
259
266
  There is no `dev` server worth running (this is a library, not an app).
package/dist/index.js CHANGED
@@ -140,12 +140,12 @@ var _ = [
140
140
  x(e), w(t);
141
141
  }, D = () => {
142
142
  S(), T();
143
- }, O = () => !!b(), ee = "", k, A = null, te = (e) => {
144
- ee = e.baseURL, k = e.onUnauthorized;
145
- }, j = () => A || (A = (async () => {
143
+ }, ee = () => !!b(), O = "", k, A = null, j = (e) => {
144
+ O = e.baseURL, k = e.onUnauthorized;
145
+ }, M = () => A || (A = (async () => {
146
146
  let e = C();
147
147
  if (!e) throw Error("No refresh token available");
148
- let t = await fetch(`${ee}/api/v1/auth/refresh`, {
148
+ let t = await fetch(`${O}/api/v1/auth/refresh`, {
149
149
  method: "POST",
150
150
  headers: { "Content-Type": "application/json" },
151
151
  body: JSON.stringify({ refresh_token: e })
@@ -155,21 +155,21 @@ var _ = [
155
155
  return E(n.access_token, n.refresh_token), n.access_token;
156
156
  })().finally(() => {
157
157
  A = null;
158
- }), A), ne = 30, M = [700, 1500], N = new Set([
158
+ }), A), N = 30, P = [700, 1500], F = /* @__PURE__ */ new Set([
159
159
  502,
160
160
  503,
161
161
  504
162
- ]), re = (e) => {
162
+ ]), te = (e) => {
163
163
  let t = (e ?? "GET").toUpperCase();
164
164
  return t === "GET" || t === "HEAD" || t === "OPTIONS";
165
- }, P = (e) => e == null || typeof e == "string", ie = (e) => new Promise((t) => setTimeout(t, e)), ae = async (e, t) => {
166
- let n = re(t.method) && P(t.body), r = P(t.body), i;
167
- for (let a = 0; a <= M.length; a++) {
168
- a > 0 && await ie(M[a - 1]);
169
- let o = a < M.length;
165
+ }, I = (e) => e == null || typeof e == "string", ne = (e) => new Promise((t) => setTimeout(t, e)), re = async (e, t) => {
166
+ let n = te(t.method) && I(t.body), r = I(t.body), i;
167
+ for (let a = 0; a <= P.length; a++) {
168
+ a > 0 && await ne(P[a - 1]);
169
+ let o = a < P.length;
170
170
  try {
171
171
  let r = await fetch(e, t);
172
- if (o && n && N.has(r.status)) continue;
172
+ if (o && n && F.has(r.status)) continue;
173
173
  return r;
174
174
  } catch (e) {
175
175
  if (i = e, o && r) continue;
@@ -177,39 +177,39 @@ var _ = [
177
177
  }
178
178
  }
179
179
  throw i ?? /* @__PURE__ */ Error("fetchWithRetry: retries exhausted");
180
- }, oe = (e) => {
180
+ }, ie = (e) => {
181
181
  try {
182
182
  let t = e.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"), { exp: n } = JSON.parse(atob(t));
183
- return typeof n == "number" && Date.now() / 1e3 >= n - ne;
183
+ return typeof n == "number" && Date.now() / 1e3 >= n - N;
184
184
  } catch {
185
185
  return !1;
186
186
  }
187
- }, se = async (e, t = {}) => {
187
+ }, ae = async (e, t = {}) => {
188
188
  let n = b();
189
- if (n && oe(n)) try {
190
- n = await j();
189
+ if (n && ie(n)) try {
190
+ n = await M();
191
191
  } catch {
192
192
  n = b();
193
193
  }
194
194
  let r = { ...t.headers };
195
195
  n && (r.Authorization = `Bearer ${n}`);
196
- let i = await ae(e, {
196
+ let i = await re(e, {
197
197
  ...t,
198
198
  headers: r
199
199
  });
200
200
  if (i.status !== 401 || !n) return i;
201
201
  try {
202
- return r.Authorization = `Bearer ${await j()}`, await ae(e, {
202
+ return r.Authorization = `Bearer ${await M()}`, await re(e, {
203
203
  ...t,
204
204
  headers: r
205
205
  });
206
206
  } catch {
207
207
  return i;
208
208
  }
209
- }, ce = class {
209
+ }, oe = class {
210
210
  baseURL;
211
211
  constructor(e) {
212
- this.baseURL = e.baseURL, te({
212
+ this.baseURL = e.baseURL, j({
213
213
  baseURL: e.baseURL,
214
214
  onUnauthorized: e.onUnauthorized
215
215
  });
@@ -218,7 +218,7 @@ var _ = [
218
218
  let n = {
219
219
  "Content-Type": "application/json",
220
220
  ...t.headers
221
- }, r = await se(`${this.baseURL}${e}`, {
221
+ }, r = await ae(`${this.baseURL}${e}`, {
222
222
  ...t,
223
223
  headers: n
224
224
  });
@@ -268,53 +268,54 @@ var _ = [
268
268
  method: "DELETE"
269
269
  });
270
270
  }
271
- }, F, le = (e) => {
272
- F = new ce(e);
273
- }, I = "astrogators_ally_codes", ue = "astrogators_selected_ally_code", L = () => {
271
+ }, L, se = (e) => {
272
+ L = new oe(e);
273
+ }, R = "astrogators_ally_codes", ce = "astrogators_selected_ally_code", z = () => {
274
274
  try {
275
- let e = localStorage.getItem(I);
275
+ let e = localStorage.getItem(R);
276
276
  return e ? JSON.parse(e) : [];
277
277
  } catch (e) {
278
278
  return console.error("Failed to read ally codes from localStorage:", e), [];
279
279
  }
280
- }, de = (e) => {
280
+ }, le = (e) => {
281
281
  try {
282
- let t = L();
282
+ let t = z();
283
283
  if (t.some((t) => t.ally_code === e.ally_code)) return;
284
- t.push(e), localStorage.setItem(I, JSON.stringify(t));
284
+ t.push(e), localStorage.setItem(R, JSON.stringify(t));
285
285
  } catch (e) {
286
286
  console.error("Failed to save ally code to localStorage:", e);
287
287
  }
288
- }, fe = (e) => {
288
+ }, ue = (e) => {
289
289
  try {
290
- let t = L().filter((t) => t.ally_code !== e);
291
- localStorage.setItem(I, JSON.stringify(t));
290
+ let t = z().filter((t) => t.ally_code !== e);
291
+ localStorage.setItem(R, JSON.stringify(t));
292
292
  } catch (e) {
293
293
  console.error("Failed to remove ally code from localStorage:", e);
294
294
  }
295
- }, pe = (e) => {
295
+ }, de = (e) => {
296
296
  try {
297
- let t = L().map((t) => t.ally_code === e ? {
297
+ let t = z().map((t) => t.ally_code === e ? {
298
298
  ...t,
299
299
  last_used_at: (/* @__PURE__ */ new Date()).toISOString()
300
300
  } : t);
301
- localStorage.setItem(I, JSON.stringify(t));
301
+ localStorage.setItem(R, JSON.stringify(t));
302
302
  } catch (e) {
303
303
  console.error("Failed to update ally code last used:", e);
304
304
  }
305
- }, me = () => localStorage.getItem(ue), R = (e) => {
306
- e ? localStorage.setItem(ue, e) : localStorage.removeItem(ue);
307
- }, he = () => {
308
- localStorage.removeItem(I), localStorage.removeItem(ue);
309
- }, ge = t(void 0), _e = ({ children: e, apiBaseUrl: t }) => {
305
+ }, fe = () => localStorage.getItem(ce), B = (e) => {
306
+ e ? localStorage.setItem(ce, e) : localStorage.removeItem(ce);
307
+ }, pe = () => {
308
+ localStorage.removeItem(R), localStorage.removeItem(ce);
309
+ }, me = t(void 0), he = ({ children: e, apiBaseUrl: t }) => {
310
310
  let [n, i] = s(null), [o, c] = s(!0), [u, d] = s(!0), [f, p] = s(!0), [m, h] = s([]), [g, _] = s(null), [v, y] = s(!1), [b, x] = s({
311
311
  show: !1,
312
312
  localStorageCodes: []
313
313
  });
314
- le({ baseURL: t }), a(() => {
314
+ se({ baseURL: t }), a(() => {
315
315
  (async () => {
316
316
  try {
317
- d((await F.get("/api/v1/config/features")).auth_enabled);
317
+ let e = await L.get("/api/v1/config/features");
318
+ d(e.auth_enabled);
318
319
  } catch (e) {
319
320
  console.error("Failed to fetch features:", e), d(!0);
320
321
  } finally {
@@ -323,12 +324,13 @@ var _ = [
323
324
  })();
324
325
  }, [t]);
325
326
  let S = r(async () => {
326
- if (!O()) {
327
+ if (!ee()) {
327
328
  i(null), c(!1);
328
329
  return;
329
330
  }
330
331
  try {
331
- i(await F.get("/api/v1/users/me"));
332
+ let e = await L.get("/api/v1/users/me");
333
+ i(e);
332
334
  } catch (e) {
333
335
  console.error("Failed to fetch user:", e), i(null), D();
334
336
  } finally {
@@ -340,159 +342,161 @@ var _ = [
340
342
  }, [S]);
341
343
  let C = async (e) => {
342
344
  try {
343
- let t = await F.post("/api/v1/auth/login", e);
344
- E(t.access_token, t.refresh_token), i(await F.get("/api/v1/users/me"));
345
+ let t = await L.post("/api/v1/auth/login", e);
346
+ E(t.access_token, t.refresh_token);
347
+ let n = await L.get("/api/v1/users/me");
348
+ i(n);
345
349
  } catch (e) {
346
350
  throw console.error("Login failed:", e), e;
347
351
  }
348
352
  }, w = async (e) => {
349
353
  try {
350
- await F.post("/api/v1/auth/register", e);
354
+ await L.post("/api/v1/auth/register", e);
351
355
  } catch (e) {
352
356
  throw console.error("Registration failed:", e), e;
353
357
  }
354
358
  }, T = () => {
355
- D(), i(null), h([]), _(null), R(null), x({
359
+ D(), i(null), h([]), _(null), B(null), x({
356
360
  show: !1,
357
361
  localStorageCodes: []
358
362
  });
359
- }, ee = async () => {
363
+ }, O = async () => {
360
364
  await S();
361
365
  }, k = async (e) => {
362
366
  try {
363
- return (await F.post("/api/v1/auth/forgot-password", e)).message;
367
+ return (await L.post("/api/v1/auth/forgot-password", e)).message;
364
368
  } catch (e) {
365
369
  throw console.error("Forgot password failed:", e), e;
366
370
  }
367
371
  }, A = async (e) => {
368
372
  try {
369
- return (await F.post("/api/v1/auth/reset-password", e)).message;
373
+ return (await L.post("/api/v1/auth/reset-password", e)).message;
370
374
  } catch (e) {
371
375
  throw console.error("Reset password failed:", e), e;
372
376
  }
373
- }, te = async (e) => {
377
+ }, j = async (e) => {
374
378
  try {
375
- return (await F.post("/api/v1/auth/resend-verification", e)).message;
379
+ return (await L.post("/api/v1/auth/resend-verification", e)).message;
376
380
  } catch (e) {
377
381
  throw console.error("Resend verification failed:", e), e;
378
382
  }
379
- }, j = r(async () => {
383
+ }, M = r(async () => {
380
384
  y(!0);
381
385
  try {
382
386
  let e = [];
383
387
  if (n) {
384
- let t = await F.get("/api/v1/users/me/ally-codes");
388
+ let t = await L.get("/api/v1/users/me/ally-codes");
385
389
  h(t.ally_codes), e = t.ally_codes.map((e) => e.ally_code);
386
- let n = L();
390
+ let n = z();
387
391
  n.length > 0 && x({
388
392
  show: !0,
389
393
  localStorageCodes: n.map((e) => e.ally_code)
390
394
  });
391
395
  } else {
392
- let t = L();
396
+ let t = z();
393
397
  h(t), e = t.map((e) => e.ally_code);
394
398
  }
395
- let t = me(), r = t && e.includes(t) ? t : null;
396
- _(r), r !== t && R(r);
399
+ let t = fe(), r = t && e.includes(t) ? t : null;
400
+ _(r), r !== t && B(r);
397
401
  } catch (e) {
398
402
  console.error("Failed to fetch ally codes:", e), h([]);
399
403
  } finally {
400
404
  y(!1);
401
405
  }
402
- }, [n]), ne = r(async (e) => {
403
- n && typeof e == "number" ? (await F.put(`/api/v1/users/me/ally-codes/${e}/use`, {}), await j()) : !n && typeof e == "string" && (pe(e), h(L()));
404
- }, [n, j]), M = r((e) => {
405
- if (_(e), R(e), e && n) {
406
+ }, [n]), N = r(async (e) => {
407
+ n && typeof e == "number" ? (await L.put(`/api/v1/users/me/ally-codes/${e}/use`, {}), await M()) : !n && typeof e == "string" && (de(e), h(z()));
408
+ }, [n, M]), P = r((e) => {
409
+ if (_(e), B(e), e && n) {
406
410
  let t = m.find((t) => "ally_code" in t && t.ally_code === e);
407
- t && "id" in t && ne(t.id);
408
- } else e && !n && pe(e);
411
+ t && "id" in t && N(t.id);
412
+ } else e && !n && de(e);
409
413
  }, [
410
414
  n,
411
415
  m,
412
- ne
413
- ]), N = r(async (e) => {
416
+ N
417
+ ]), F = r(async (e) => {
414
418
  if (!/^\d{9}$/.test(e)) throw Error("Ally code must be exactly 9 digits");
415
419
  if (n) {
416
- let t = await F.post("/api/v1/users/me/ally-codes", { ally_code: e });
417
- h((e) => [t, ...e]), m.length === 0 && M(e);
420
+ let t = await L.post("/api/v1/users/me/ally-codes", { ally_code: e });
421
+ h((e) => [t, ...e]), m.length === 0 && P(e);
418
422
  } else {
419
- let t = await F.get(`/api/v1/player-data/player/${e}`);
423
+ let t = await L.get(`/api/v1/player-data/player/${e}`);
420
424
  if (!t) throw Error("Invalid ally code - player not found");
421
- de({
425
+ le({
422
426
  ally_code: e,
423
427
  player_name: t.data?.name ?? null,
424
428
  last_used_at: (/* @__PURE__ */ new Date()).toISOString()
425
- }), h(L()), m.length === 0 && M(e);
429
+ }), h(z()), m.length === 0 && P(e);
426
430
  }
427
431
  }, [
428
432
  n,
429
433
  m.length,
430
- M
431
- ]), re = r(async (e) => {
432
- n && typeof e == "number" ? (await F.delete(`/api/v1/users/me/ally-codes/${e}`), h((t) => t.filter((t) => "id" in t && t.id !== e))) : !n && typeof e == "string" && (fe(e), h(L()), g === e && M(null));
434
+ P
435
+ ]), te = r(async (e) => {
436
+ n && typeof e == "number" ? (await L.delete(`/api/v1/users/me/ally-codes/${e}`), h((t) => t.filter((t) => "id" in t && t.id !== e))) : !n && typeof e == "string" && (ue(e), h(z()), g === e && P(null));
433
437
  }, [
434
438
  n,
435
439
  g,
436
- M
437
- ]), P = r(() => {
440
+ P
441
+ ]), I = r(() => {
438
442
  x({
439
443
  show: !1,
440
444
  localStorageCodes: []
441
445
  });
442
- }, []), ie = r(async () => {
446
+ }, []), ne = r(async () => {
443
447
  if (!n) return;
444
- let e = L();
448
+ let e = z();
445
449
  for (let t of e) try {
446
- await N(t.ally_code);
450
+ await F(t.ally_code);
447
451
  } catch (e) {
448
452
  console.error(`Failed to migrate ally code ${t.ally_code}:`, e);
449
453
  }
450
- he(), P();
454
+ pe(), I();
451
455
  }, [
452
456
  n,
453
- N,
454
- P
457
+ F,
458
+ I
455
459
  ]);
456
460
  a(() => {
457
- j();
458
- }, [j]);
459
- let ae = {
461
+ M();
462
+ }, [M]);
463
+ let re = {
460
464
  user: n,
461
465
  isAuthenticated: !!n,
462
466
  isLoading: o,
463
467
  login: C,
464
468
  register: w,
465
469
  logout: T,
466
- refreshUser: ee,
470
+ refreshUser: O,
467
471
  forgotPassword: k,
468
472
  resetPassword: A,
469
- resendVerification: te,
473
+ resendVerification: j,
470
474
  authEnabled: u,
471
475
  isLoadingFeatures: f,
472
476
  allyCodes: m,
473
477
  selectedAllyCode: g,
474
478
  isLoadingAllyCodes: v,
475
- fetchAllyCodes: j,
476
- addAllyCode: N,
477
- removeAllyCode: re,
478
- selectAllyCode: M,
479
- updateAllyCodeLastUsed: ne,
479
+ fetchAllyCodes: M,
480
+ addAllyCode: F,
481
+ removeAllyCode: te,
482
+ selectAllyCode: P,
483
+ updateAllyCodeLastUsed: N,
480
484
  migrationPrompt: b,
481
- dismissMigrationPrompt: P,
482
- migrateLocalStorageCodes: ie
485
+ dismissMigrationPrompt: I,
486
+ migrateLocalStorageCodes: ne
483
487
  };
484
- return /* @__PURE__ */ l(ge.Provider, {
485
- value: ae,
488
+ return /* @__PURE__ */ l(me.Provider, {
489
+ value: re,
486
490
  children: e
487
491
  });
488
- }, z = () => {
489
- let e = i(ge);
492
+ }, V = () => {
493
+ let e = i(me);
490
494
  if (e === void 0) throw Error("useAuth must be used within an AuthProvider");
491
495
  return e;
492
496
  };
493
497
  //#endregion
494
498
  //#region src/hooks/useDismissableMenu.ts
495
- function ve({ isOpen: e, onClose: t, triggerRef: n, panelRef: r }) {
499
+ function ge({ isOpen: e, onClose: t, triggerRef: n, panelRef: r }) {
496
500
  a(() => {
497
501
  if (!e) return;
498
502
  let i = (e) => {
@@ -511,7 +515,7 @@ function ve({ isOpen: e, onClose: t, triggerRef: n, panelRef: r }) {
511
515
  r
512
516
  ]);
513
517
  }
514
- var B = {
518
+ var H = {
515
519
  navMenu: "_navMenu_vspnm_1",
516
520
  trigger: "_trigger_vspnm_8",
517
521
  tabActive: "_tabActive_vspnm_29",
@@ -524,9 +528,9 @@ var B = {
524
528
  groupLabel: "_groupLabel_vspnm_92",
525
529
  groupItems: "_groupItems_vspnm_97",
526
530
  groupItem: "_groupItem_vspnm_97"
527
- }, ye = ({ app: e, isCurrentApp: t, activeSectionId: n, isOpen: r, onOpenChange: i, onSectionClick: a }) => {
528
- let { isAuthenticated: s, user: c } = z(), d = o(null), f = o(null), p = `navmenu-panel-${e.id}`;
529
- ve({
531
+ }, _e = ({ app: e, isCurrentApp: t, activeSectionId: n, isOpen: r, onOpenChange: i, onSectionClick: a }) => {
532
+ let { isAuthenticated: s, user: c } = V(), d = o(null), f = o(null), p = `navmenu-panel-${e.id}`;
533
+ ge({
530
534
  isOpen: r,
531
535
  onClose: () => i(!1),
532
536
  triggerRef: d,
@@ -542,24 +546,24 @@ var B = {
542
546
  let i = t && n === e.id;
543
547
  return /* @__PURE__ */ l("a", {
544
548
  href: e.href,
545
- className: `${B.sectionLink} ${r ?? ""} ${i ? B.sectionActive : ""}`,
549
+ className: `${H.sectionLink} ${r ?? ""} ${i ? H.sectionActive : ""}`,
546
550
  "aria-current": i ? "page" : void 0,
547
551
  onClick: h(e),
548
552
  children: e.label
549
553
  }, e.id);
550
554
  };
551
555
  return /* @__PURE__ */ u("div", {
552
- className: B.navMenu,
556
+ className: H.navMenu,
553
557
  children: [/* @__PURE__ */ u("button", {
554
558
  type: "button",
555
559
  ref: d,
556
- className: `${B.trigger} ${t ? B.tabActive : ""}`,
560
+ className: `${H.trigger} ${t ? H.tabActive : ""}`,
557
561
  "aria-expanded": r,
558
562
  "aria-controls": p,
559
563
  "aria-haspopup": "true",
560
564
  onClick: () => i(!r),
561
565
  children: [e.label, /* @__PURE__ */ l("svg", {
562
- className: B.chevron,
566
+ className: H.chevron,
563
567
  width: "10",
564
568
  height: "6",
565
569
  viewBox: "0 0 10 6",
@@ -576,9 +580,9 @@ var B = {
576
580
  }), r && /* @__PURE__ */ l("div", {
577
581
  id: p,
578
582
  ref: f,
579
- className: B.panel,
583
+ className: H.panel,
580
584
  children: e.status === "coming-soon" ? /* @__PURE__ */ l("div", {
581
- className: B.comingSoon,
585
+ className: H.comingSoon,
582
586
  children: "Coming soon"
583
587
  }) : /* @__PURE__ */ l("nav", {
584
588
  "aria-label": `${e.label} sections`,
@@ -587,20 +591,20 @@ var B = {
587
591
  if (!g(e)) return m(e) ? v(e) : null;
588
592
  let t = e.items.filter(m);
589
593
  return /* @__PURE__ */ u("div", {
590
- className: B.group,
591
- children: [v(e, B.groupLabel), /* @__PURE__ */ l("div", {
592
- className: B.groupItems,
593
- children: t.map((e) => v(e, B.groupItem))
594
+ className: H.group,
595
+ children: [v(e, H.groupLabel), /* @__PURE__ */ l("div", {
596
+ className: H.groupItems,
597
+ children: t.map((e) => v(e, H.groupItem))
594
598
  })]
595
599
  }, e.id);
596
600
  })
597
601
  })
598
602
  })]
599
603
  });
600
- }, be = { burger: "_burger_javjr_1" }, xe = n(({ isOpen: e, onClick: t, controlsId: n }, r) => /* @__PURE__ */ l("button", {
604
+ }, ve = { burger: "_burger_javjr_1" }, ye = n(({ isOpen: e, onClick: t, controlsId: n }, r) => /* @__PURE__ */ l("button", {
601
605
  ref: r,
602
606
  type: "button",
603
- className: be.burger,
607
+ className: ve.burger,
604
608
  onClick: t,
605
609
  "aria-expanded": e,
606
610
  "aria-controls": n,
@@ -629,8 +633,8 @@ var B = {
629
633
  })
630
634
  })
631
635
  }));
632
- xe.displayName = "NavBurger";
633
- var V = {
636
+ ye.displayName = "NavBurger";
637
+ var U = {
634
638
  selectWrapper: "_selectWrapper_ir4bq_1",
635
639
  fullWidth: "_fullWidth_ir4bq_7",
636
640
  label: "_label_ir4bq_11",
@@ -641,25 +645,25 @@ var V = {
641
645
  arrow: "_arrow_ir4bq_71",
642
646
  errorText: "_errorText_ir4bq_81",
643
647
  helperText: "_helperText_ir4bq_86"
644
- }, Se = e.forwardRef(({ label: e, error: t, helperText: n, options: r, placeholder: i, fullWidth: a = !1, className: o = "", id: s, ...c }, d) => {
648
+ }, be = e.forwardRef(({ label: e, error: t, helperText: n, options: r, placeholder: i, fullWidth: a = !1, className: o = "", id: s, ...c }, d) => {
645
649
  let f = s || `select-${Math.random().toString(36).substr(2, 9)}`, p = !!t;
646
650
  return /* @__PURE__ */ u("div", {
647
- className: `${V.selectWrapper} ${a ? V.fullWidth : ""} ${o}`,
651
+ className: `${U.selectWrapper} ${a ? U.fullWidth : ""} ${o}`,
648
652
  children: [
649
653
  e && /* @__PURE__ */ u("label", {
650
654
  htmlFor: f,
651
- className: V.label,
655
+ className: U.label,
652
656
  children: [e, c.required && /* @__PURE__ */ l("span", {
653
- className: V.required,
657
+ className: U.required,
654
658
  children: "*"
655
659
  })]
656
660
  }),
657
661
  /* @__PURE__ */ u("div", {
658
- className: V.selectContainer,
662
+ className: U.selectContainer,
659
663
  children: [/* @__PURE__ */ u("select", {
660
664
  ref: d,
661
665
  id: f,
662
- className: `${V.select} ${p ? V.error : ""}`,
666
+ className: `${U.select} ${p ? U.error : ""}`,
663
667
  ...c,
664
668
  children: [
665
669
  i && /* @__PURE__ */ l("option", {
@@ -675,23 +679,23 @@ var V = {
675
679
  !r && c.children
676
680
  ]
677
681
  }), /* @__PURE__ */ l("span", {
678
- className: V.arrow,
682
+ className: U.arrow,
679
683
  children: "▼"
680
684
  })]
681
685
  }),
682
686
  t && /* @__PURE__ */ l("span", {
683
- className: V.errorText,
687
+ className: U.errorText,
684
688
  children: t
685
689
  }),
686
690
  n && !t && /* @__PURE__ */ l("span", {
687
- className: V.helperText,
691
+ className: U.helperText,
688
692
  children: n
689
693
  })
690
694
  ]
691
695
  });
692
696
  });
693
- Se.displayName = "Select";
694
- var H = {
697
+ be.displayName = "Select";
698
+ var W = {
695
699
  button: "_button_159ms_1",
696
700
  sm: "_sm_159ms_22",
697
701
  md: "_md_159ms_28",
@@ -707,18 +711,18 @@ var H = {
707
711
  spinner: "_spinner_159ms_116",
708
712
  spin: "_spin_159ms_116",
709
713
  loadingText: "_loadingText_159ms_131"
710
- }, U = ({ children: e, variant: t = "primary", size: n = "md", fullWidth: r = !1, loading: i = !1, disabled: a = !1, className: o = "", ...s }) => /* @__PURE__ */ l("button", {
711
- className: `${H.button} ${H[t]} ${H[n]} ${r ? H.fullWidth : ""} ${i ? H.loading : ""} ${o}`,
714
+ }, G = ({ children: e, variant: t = "primary", size: n = "md", fullWidth: r = !1, loading: i = !1, disabled: a = !1, className: o = "", ...s }) => /* @__PURE__ */ l("button", {
715
+ className: `${W.button} ${W[t]} ${W[n]} ${r ? W.fullWidth : ""} ${i ? W.loading : ""} ${o}`,
712
716
  disabled: a || i,
713
717
  ...s,
714
718
  children: i ? /* @__PURE__ */ u("span", {
715
- className: H.loadingSpinner,
716
- children: [/* @__PURE__ */ l("span", { className: H.spinner }), /* @__PURE__ */ l("span", {
717
- className: H.loadingText,
719
+ className: W.loadingSpinner,
720
+ children: [/* @__PURE__ */ l("span", { className: W.spinner }), /* @__PURE__ */ l("span", {
721
+ className: W.loadingText,
718
722
  children: e
719
723
  })]
720
724
  }) : e
721
- }), W = {
725
+ }), K = {
722
726
  inputWrapper: "_inputWrapper_15t4g_1",
723
727
  fullWidth: "_fullWidth_15t4g_7",
724
728
  label: "_label_15t4g_11",
@@ -727,40 +731,40 @@ var H = {
727
731
  error: "_error_15t4g_55",
728
732
  errorText: "_errorText_15t4g_63",
729
733
  helperText: "_helperText_15t4g_68"
730
- }, Ce = e.forwardRef(({ label: e, error: t, helperText: n, fullWidth: r = !1, className: i = "", id: a, ...o }, s) => {
734
+ }, xe = e.forwardRef(({ label: e, error: t, helperText: n, fullWidth: r = !1, className: i = "", id: a, ...o }, s) => {
731
735
  let c = a || `input-${Math.random().toString(36).substr(2, 9)}`, d = !!t;
732
736
  return /* @__PURE__ */ u("div", {
733
- className: `${W.inputWrapper} ${r ? W.fullWidth : ""} ${i}`,
737
+ className: `${K.inputWrapper} ${r ? K.fullWidth : ""} ${i}`,
734
738
  children: [
735
739
  e && /* @__PURE__ */ u("label", {
736
740
  htmlFor: c,
737
- className: W.label,
741
+ className: K.label,
738
742
  children: [e, o.required && /* @__PURE__ */ l("span", {
739
- className: W.required,
743
+ className: K.required,
740
744
  children: "*"
741
745
  })]
742
746
  }),
743
747
  /* @__PURE__ */ l("input", {
744
748
  ref: s,
745
749
  id: c,
746
- className: `${W.input} ${d ? W.error : ""}`,
750
+ className: `${K.input} ${d ? K.error : ""}`,
747
751
  ...o
748
752
  }),
749
753
  t && /* @__PURE__ */ l("span", {
750
- className: W.errorText,
754
+ className: K.errorText,
751
755
  children: t
752
756
  }),
753
757
  n && !t && /* @__PURE__ */ l("span", {
754
- className: W.helperText,
758
+ className: K.helperText,
755
759
  children: n
756
760
  })
757
761
  ]
758
762
  });
759
763
  });
760
- Ce.displayName = "Input";
764
+ xe.displayName = "Input";
761
765
  //#endregion
762
766
  //#region src/utils/formatAllyCode.ts
763
- var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e.slice(6, 9)}`, we = (e) => e.replace(/-/g, ""), K = {
767
+ var q = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e.slice(6, 9)}`, Se = (e) => e.replace(/-/g, ""), J = {
764
768
  allyCodeDropdown: "_allyCodeDropdown_1pawi_1",
765
769
  select: "_select_1pawi_8",
766
770
  manageButton: "_manageButton_1pawi_12",
@@ -773,26 +777,29 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
773
777
  codeItem: "_codeItem_1pawi_71",
774
778
  doneButton: "_doneButton_1pawi_88",
775
779
  inline: "_inline_1pawi_107"
776
- }, Te = ({ onAllyCodeSelected: e, className: t = "", isOpen: n, onOpenChange: r, variant: i = "floating" }) => {
777
- let { allyCodes: a, selectedAllyCode: c, selectAllyCode: d, removeAllyCode: f, addAllyCode: p, isLoadingAllyCodes: m } = z(), [h, g] = s(!1), _ = n ?? h, v = (e) => {
780
+ }, Ce = ({ onAllyCodeSelected: e, className: t = "", isOpen: n, onOpenChange: r, variant: i = "floating" }) => {
781
+ let { allyCodes: a, selectedAllyCode: c, selectAllyCode: d, removeAllyCode: f, addAllyCode: p, isLoadingAllyCodes: m } = V(), [h, g] = s(!1), _ = n ?? h, v = (e) => {
778
782
  g(e), r?.(e);
779
- }, [y, b] = s(""), [x, S] = s(""), [C, w] = s(""), [T, E] = s(!1), D = o(null), O = o(null);
780
- ve({
783
+ }, [y, b] = s(""), [x, S] = s(""), [C, w] = s(""), [T, E] = s(!1), D = o(null), ee = o(null);
784
+ ge({
781
785
  isOpen: _ && i === "floating",
782
786
  onClose: () => v(!1),
783
787
  triggerRef: D,
784
- panelRef: O
788
+ panelRef: ee
785
789
  });
786
- let ee = a.map((e) => ({
790
+ let O = a.map((e) => ({
787
791
  value: e.ally_code,
788
- label: e.player_name ? `${e.player_name} (${G(e.ally_code)})` : G(e.ally_code)
792
+ label: e.player_name ? `${e.player_name} (${q(e.ally_code)})` : q(e.ally_code)
789
793
  })), k = (t) => {
790
794
  let n = t.target.value || null;
791
795
  d(n), e?.(n);
792
796
  }, A = async (e) => {
793
797
  let t = a.find((t) => t.ally_code === e);
794
- t && await f("id" in t ? t.id : t.ally_code);
795
- }, te = async () => {
798
+ if (t) {
799
+ let e = "id" in t ? t.id : t.ally_code;
800
+ await f(e);
801
+ }
802
+ }, j = async () => {
796
803
  if (S(""), w(""), !/^\d{9}$/.test(y)) {
797
804
  S("Ally code must be exactly 9 digits");
798
805
  return;
@@ -805,59 +812,59 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
805
812
  } finally {
806
813
  E(!1);
807
814
  }
808
- }, j = a.length > 0;
815
+ }, M = a.length > 0;
809
816
  return /* @__PURE__ */ u("div", {
810
- className: `${K.allyCodeDropdown} ${i === "inline" ? K.inline : ""} ${t}`,
817
+ className: `${J.allyCodeDropdown} ${i === "inline" ? J.inline : ""} ${t}`,
811
818
  children: [
812
- j && /* @__PURE__ */ l(Se, {
813
- options: ee,
819
+ M && /* @__PURE__ */ l(be, {
820
+ options: O,
814
821
  value: c || "",
815
822
  onChange: k,
816
823
  placeholder: "Select ally code",
817
824
  disabled: m,
818
- className: K.select
825
+ className: J.select
819
826
  }),
820
827
  _ && /* @__PURE__ */ u("div", {
821
- className: K.managePanel,
822
- ref: O,
828
+ className: J.managePanel,
829
+ ref: ee,
823
830
  id: "ally-code-manage-panel",
824
831
  children: [
825
832
  /* @__PURE__ */ l("h4", { children: "Manage Ally Codes" }),
826
833
  /* @__PURE__ */ u("div", {
827
- className: K.addSection,
834
+ className: J.addSection,
828
835
  children: [
829
836
  /* @__PURE__ */ u("div", {
830
- className: K.addInputGroup,
831
- children: [/* @__PURE__ */ l(Ce, {
837
+ className: J.addInputGroup,
838
+ children: [/* @__PURE__ */ l(xe, {
832
839
  type: "text",
833
840
  placeholder: "9-digit ally code",
834
841
  value: y,
835
842
  onChange: (e) => b(e.target.value.replace(/\D/g, "").slice(0, 9)),
836
843
  disabled: T
837
- }), /* @__PURE__ */ l(U, {
844
+ }), /* @__PURE__ */ l(G, {
838
845
  variant: "primary",
839
846
  size: "sm",
840
- onClick: te,
847
+ onClick: j,
841
848
  loading: T,
842
849
  disabled: y.length !== 9,
843
850
  children: "Add"
844
851
  })]
845
852
  }),
846
853
  x && /* @__PURE__ */ l("div", {
847
- className: K.error,
854
+ className: J.error,
848
855
  children: x
849
856
  }),
850
857
  C && /* @__PURE__ */ l("div", {
851
- className: K.success,
858
+ className: J.success,
852
859
  children: C
853
860
  })
854
861
  ]
855
862
  }),
856
863
  /* @__PURE__ */ l("div", {
857
- className: K.codesList,
864
+ className: J.codesList,
858
865
  children: a.map((e) => /* @__PURE__ */ u("div", {
859
- className: K.codeItem,
860
- children: [/* @__PURE__ */ l("span", { children: e.player_name ? `${e.player_name} (${G(e.ally_code)})` : G(e.ally_code) }), /* @__PURE__ */ l(U, {
866
+ className: J.codeItem,
867
+ children: [/* @__PURE__ */ l("span", { children: e.player_name ? `${e.player_name} (${q(e.ally_code)})` : q(e.ally_code) }), /* @__PURE__ */ l(G, {
861
868
  variant: "ghost",
862
869
  size: "sm",
863
870
  onClick: () => A(e.ally_code),
@@ -865,50 +872,50 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
865
872
  })]
866
873
  }, e.ally_code))
867
874
  }),
868
- /* @__PURE__ */ l(U, {
875
+ /* @__PURE__ */ l(G, {
869
876
  variant: "secondary",
870
877
  size: "sm",
871
878
  onClick: () => v(!1),
872
- className: K.doneButton,
879
+ className: J.doneButton,
873
880
  children: "Done"
874
881
  })
875
882
  ]
876
883
  }),
877
884
  /* @__PURE__ */ l("span", {
878
885
  ref: D,
879
- children: /* @__PURE__ */ l(U, {
886
+ children: /* @__PURE__ */ l(G, {
880
887
  variant: "ghost",
881
888
  size: "sm",
882
889
  onClick: () => v(!_),
883
- className: K.manageButton,
890
+ className: J.manageButton,
884
891
  "aria-expanded": _,
885
892
  "aria-controls": "ally-code-manage-panel",
886
893
  "aria-haspopup": "true",
887
- children: j ? "Manage" : "+ Add ally code"
894
+ children: M ? "Manage" : "+ Add ally code"
888
895
  })
889
896
  })
890
897
  ]
891
898
  });
892
- }, q = {
899
+ }, we = {
893
900
  authCluster: "_authCluster_17o10_1",
894
901
  userLink: "_userLink_17o10_7",
895
902
  adminLink: "_adminLink_17o10_16"
896
- }, Ee = ({ showAuth: e = !0 }) => {
897
- let { user: t, isAuthenticated: n, logout: r, authEnabled: i } = z();
903
+ }, Te = ({ showAuth: e = !0 }) => {
904
+ let { user: t, isAuthenticated: n, logout: r, authEnabled: i } = V();
898
905
  return e ? n ? /* @__PURE__ */ u("div", {
899
- className: q.authCluster,
906
+ className: we.authCluster,
900
907
  children: [
901
908
  t?.role === "admin" && /* @__PURE__ */ l("a", {
902
909
  href: "/admin/users",
903
- className: q.adminLink,
910
+ className: we.adminLink,
904
911
  children: "Admin"
905
912
  }),
906
913
  /* @__PURE__ */ l("a", {
907
914
  href: "/profile",
908
- className: q.userLink,
915
+ className: we.userLink,
909
916
  children: t?.username
910
917
  }),
911
- /* @__PURE__ */ l(U, {
918
+ /* @__PURE__ */ l(G, {
912
919
  variant: "ghost",
913
920
  size: "sm",
914
921
  onClick: () => {
@@ -918,24 +925,24 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
918
925
  })
919
926
  ]
920
927
  }) : i ? /* @__PURE__ */ u("div", {
921
- className: q.authCluster,
928
+ className: we.authCluster,
922
929
  children: [/* @__PURE__ */ l("a", {
923
930
  href: "/login",
924
- children: /* @__PURE__ */ l(U, {
931
+ children: /* @__PURE__ */ l(G, {
925
932
  variant: "ghost",
926
933
  size: "sm",
927
934
  children: "Login"
928
935
  })
929
936
  }), /* @__PURE__ */ l("a", {
930
937
  href: "/register",
931
- children: /* @__PURE__ */ l(U, {
938
+ children: /* @__PURE__ */ l(G, {
932
939
  variant: "primary",
933
940
  size: "sm",
934
941
  children: "Sign Up"
935
942
  })
936
943
  })]
937
944
  }) : null : null;
938
- }, J = {
945
+ }, Y = {
939
946
  backdrop: "_backdrop_3ax21_1",
940
947
  panel: "_panel_3ax21_11",
941
948
  accordion: "_accordion_3ax21_24",
@@ -955,14 +962,23 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
955
962
  allySection: "_allySection_3ax21_112",
956
963
  extrasSection: "_extrasSection_3ax21_113",
957
964
  accountSection: "_accountSection_3ax21_114"
958
- }, De = ({ isOpen: e, onClose: t, currentApp: n, activeSectionId: r, onSectionClick: i, rightExtras: c, showAuth: f, panelId: p, returnFocusRef: m }) => {
959
- let { isAuthenticated: h, user: v } = z(), [y, b] = s(n === "hub" ? null : n), x = o(null);
965
+ }, Ee = "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])", De = ({ isOpen: e, onClose: t, currentApp: n, activeSectionId: r, onSectionClick: i, rightExtras: c, showAuth: f, panelId: p, returnFocusRef: m }) => {
966
+ let { isAuthenticated: h, user: v } = V(), [y, b] = s(n === "hub" ? null : n), x = o(null);
960
967
  if (a(() => (e ? document.body.style.overflow = "hidden" : document.body.style.overflow = "unset", () => {
961
968
  document.body.style.overflow = "unset";
962
969
  }), [e]), a(() => {
963
970
  if (!e) return;
964
971
  let r = (e) => {
965
- e.key === "Escape" && (t(), m.current?.focus());
972
+ if (e.key === "Escape") {
973
+ t(), m.current?.focus();
974
+ return;
975
+ }
976
+ if (e.key === "Tab") {
977
+ let t = x.current?.querySelectorAll(Ee);
978
+ if (!t || t.length === 0) return;
979
+ let n = t[0], r = t[t.length - 1], i = document.activeElement, a = i === x.current || !x.current?.contains(i);
980
+ e.shiftKey && (i === n || a) ? (e.preventDefault(), r.focus()) : !e.shiftKey && (i === r || a) && (e.preventDefault(), n.focus());
981
+ }
966
982
  };
967
983
  return document.addEventListener("keydown", r), b(n === "hub" ? null : n), x.current?.focus(), () => document.removeEventListener("keydown", r);
968
984
  }, [e]), !e) return null;
@@ -970,7 +986,7 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
970
986
  e === n && i?.(r, a), t();
971
987
  };
972
988
  return d(/* @__PURE__ */ l("div", {
973
- className: J.backdrop,
989
+ className: Y.backdrop,
974
990
  onClick: t,
975
991
  children: /* @__PURE__ */ u("div", {
976
992
  ref: x,
@@ -979,32 +995,32 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
979
995
  "aria-modal": "true",
980
996
  "aria-label": "Navigation menu",
981
997
  tabIndex: -1,
982
- className: J.panel,
998
+ className: Y.panel,
983
999
  onClick: (e) => e.stopPropagation(),
984
1000
  children: [
985
1001
  /* @__PURE__ */ l("div", {
986
- className: J.accordion,
1002
+ className: Y.accordion,
987
1003
  children: _.map((e) => {
988
1004
  let t = y === e.id, i = n === e.id, a = (e) => !(e.requiresAuth && !h || e.roles && (!v || !e.roles.includes(v.role))), o = `mobile-accordion-${e.id}`, s = (t, n) => {
989
1005
  let a = i && r === t.id;
990
1006
  return /* @__PURE__ */ l("a", {
991
1007
  href: t.href,
992
- className: `${J.sectionLink} ${n ?? ""} ${a ? J.sectionActive : ""}`,
1008
+ className: `${Y.sectionLink} ${n ?? ""} ${a ? Y.sectionActive : ""}`,
993
1009
  "aria-current": a ? "page" : void 0,
994
1010
  onClick: S(e.id, t),
995
1011
  children: t.label
996
1012
  }, t.id);
997
1013
  };
998
1014
  return /* @__PURE__ */ u("div", {
999
- className: J.accordionItem,
1015
+ className: Y.accordionItem,
1000
1016
  children: [/* @__PURE__ */ u("button", {
1001
1017
  type: "button",
1002
- className: `${J.accordionHeader} ${i ? J.currentApp : ""}`,
1018
+ className: `${Y.accordionHeader} ${i ? Y.currentApp : ""}`,
1003
1019
  "aria-expanded": t,
1004
1020
  "aria-controls": o,
1005
1021
  onClick: () => b(t ? null : e.id),
1006
1022
  children: [e.label, /* @__PURE__ */ l("svg", {
1007
- className: `${J.chevron} ${t ? J.chevronOpen : ""}`,
1023
+ className: `${Y.chevron} ${t ? Y.chevronOpen : ""}`,
1008
1024
  width: "10",
1009
1025
  height: "6",
1010
1026
  viewBox: "0 0 10 6",
@@ -1020,18 +1036,18 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
1020
1036
  })]
1021
1037
  }), t && /* @__PURE__ */ l("div", {
1022
1038
  id: o,
1023
- className: J.accordionPanel,
1039
+ className: Y.accordionPanel,
1024
1040
  children: e.status === "coming-soon" ? /* @__PURE__ */ l("div", {
1025
- className: J.comingSoon,
1041
+ className: Y.comingSoon,
1026
1042
  children: "Coming soon"
1027
1043
  }) : e.sections.map((e) => {
1028
1044
  if (!g(e)) return a(e) ? s(e) : null;
1029
1045
  let t = e.items.filter(a);
1030
1046
  return /* @__PURE__ */ u("div", {
1031
- className: J.group,
1032
- children: [s(e, J.groupLabel), /* @__PURE__ */ l("div", {
1033
- className: J.groupItems,
1034
- children: t.map((e) => s(e, J.groupItem))
1047
+ className: Y.group,
1048
+ children: [s(e, Y.groupLabel), /* @__PURE__ */ l("div", {
1049
+ className: Y.groupItems,
1050
+ children: t.map((e) => s(e, Y.groupItem))
1035
1051
  })]
1036
1052
  }, e.id);
1037
1053
  })
@@ -1040,26 +1056,26 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
1040
1056
  })
1041
1057
  }),
1042
1058
  /* @__PURE__ */ l("div", {
1043
- className: J.allySection,
1044
- children: /* @__PURE__ */ l(Te, { variant: "inline" })
1059
+ className: Y.allySection,
1060
+ children: /* @__PURE__ */ l(Ce, { variant: "inline" })
1045
1061
  }),
1046
1062
  c && /* @__PURE__ */ l("div", {
1047
- className: J.extrasSection,
1063
+ className: Y.extrasSection,
1048
1064
  children: c
1049
1065
  }),
1050
1066
  /* @__PURE__ */ l("div", {
1051
- className: J.accountSection,
1052
- children: /* @__PURE__ */ l(Ee, { showAuth: f })
1067
+ className: Y.accountSection,
1068
+ children: /* @__PURE__ */ l(Te, { showAuth: f })
1053
1069
  })
1054
1070
  ]
1055
1071
  })
1056
1072
  }), document.body);
1057
- }, Y = {
1073
+ }, Oe = {
1058
1074
  logoLink: "_logoLink_12f5f_3",
1059
1075
  appMenus: "_appMenus_12f5f_10",
1060
1076
  desktopRight: "_desktopRight_12f5f_19",
1061
1077
  extrasGroup: "_extrasGroup_12f5f_25"
1062
- }, Oe = ({ currentApp: e, activeSectionId: t, onNavigate: n, rightExtras: r, showAuth: i = !0, className: d }) => {
1078
+ }, ke = ({ currentApp: e, activeSectionId: t, onNavigate: n, rightExtras: r, showAuth: i = !0, className: d }) => {
1063
1079
  let [f, p] = s(null), [m, g] = s(!1), v = o(null), y = "suite-mobile-nav-panel";
1064
1080
  return a(() => {
1065
1081
  let e = window.matchMedia("(max-width: 768px)"), t = () => {
@@ -1070,12 +1086,12 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
1070
1086
  className: d,
1071
1087
  logo: /* @__PURE__ */ l("a", {
1072
1088
  href: "/",
1073
- className: Y.logoLink,
1089
+ className: Oe.logoLink,
1074
1090
  children: "The Astrogator's Table"
1075
1091
  }),
1076
1092
  leftContent: /* @__PURE__ */ l("nav", {
1077
- className: Y.appMenus,
1078
- children: _.map((r) => /* @__PURE__ */ l(ye, {
1093
+ className: Oe.appMenus,
1094
+ children: _.map((r) => /* @__PURE__ */ l(_e, {
1079
1095
  app: r,
1080
1096
  isCurrentApp: e === r.id,
1081
1097
  activeSectionId: t,
@@ -1085,19 +1101,19 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
1085
1101
  }, r.id))
1086
1102
  }),
1087
1103
  rightContent: /* @__PURE__ */ u(c, { children: [/* @__PURE__ */ u("div", {
1088
- className: Y.desktopRight,
1104
+ className: Oe.desktopRight,
1089
1105
  children: [
1090
1106
  r && /* @__PURE__ */ l("div", {
1091
- className: Y.extrasGroup,
1107
+ className: Oe.extrasGroup,
1092
1108
  children: r
1093
1109
  }),
1094
- /* @__PURE__ */ l(Te, {
1110
+ /* @__PURE__ */ l(Ce, {
1095
1111
  isOpen: f === "ally",
1096
1112
  onOpenChange: (e) => p(e ? "ally" : null)
1097
1113
  }),
1098
- /* @__PURE__ */ l(Ee, { showAuth: i })
1114
+ /* @__PURE__ */ l(Te, { showAuth: i })
1099
1115
  ]
1100
- }), /* @__PURE__ */ l(xe, {
1116
+ }), /* @__PURE__ */ l(ye, {
1101
1117
  ref: v,
1102
1118
  isOpen: m,
1103
1119
  onClick: () => {
@@ -1122,7 +1138,7 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
1122
1138
  defaultContent: "_defaultContent_zjuj8_13",
1123
1139
  copyright: "_copyright_zjuj8_21",
1124
1140
  disclaimer: "_disclaimer_zjuj8_26"
1125
- }, ke = ({ children: e, className: t = "" }) => /* @__PURE__ */ l("footer", {
1141
+ }, Ae = ({ children: e, className: t = "" }) => /* @__PURE__ */ l("footer", {
1126
1142
  className: `${X.footer} ${t}`,
1127
1143
  children: /* @__PURE__ */ l("div", {
1128
1144
  className: X.content,
@@ -1144,11 +1160,11 @@ var G = (e) => !e || e.length !== 9 ? e : `${e.slice(0, 3)}-${e.slice(3, 6)}-${e
1144
1160
  });
1145
1161
  //#endregion
1146
1162
  //#region src/utils/formatTime.ts
1147
- function Ae(e) {
1163
+ function je(e) {
1148
1164
  let t = /([zZ]|[+-]\d{2}:?\d{2})$/.test(e);
1149
1165
  return new Date(t ? e : `${e}Z`);
1150
1166
  }
1151
- function je(e, t) {
1167
+ function Me(e, t) {
1152
1168
  let n = Math.max(0, Math.floor((t - e.getTime()) / 1e3));
1153
1169
  if (n < 60) return `${n}s ago`;
1154
1170
  let r = Math.floor(n / 60);
@@ -1156,38 +1172,38 @@ function je(e, t) {
1156
1172
  let i = Math.floor(r / 60);
1157
1173
  return i < 24 ? `${i}h ago` : `${Math.floor(i / 24)}d ago`;
1158
1174
  }
1159
- function Me(e) {
1175
+ function Ne(e) {
1160
1176
  return `${Math.floor(e / 60)}:${(e % 60).toString().padStart(2, "0")}`;
1161
1177
  }
1162
- var Ne = {
1178
+ var Pe = {
1163
1179
  rosterRefresh: "_rosterRefresh_1rbul_3",
1164
1180
  updatedLabel: "_updatedLabel_1rbul_11",
1165
1181
  refreshButton: "_refreshButton_1rbul_19",
1166
1182
  spin: "_spin_1rbul_31"
1167
- }, Pe = ({ onRefresh: e, isRefreshing: t, cachedAt: n = null, refreshAvailableAt: r = null, labelPrefix: i = "Updated", className: o = "" }) => {
1183
+ }, Fe = ({ onRefresh: e, isRefreshing: t, cachedAt: n = null, refreshAvailableAt: r = null, labelPrefix: i = "Updated", className: o = "" }) => {
1168
1184
  let [c, d] = s(() => Date.now()), f = !!n || r != null && r > c;
1169
1185
  a(() => {
1170
1186
  if (!f) return;
1171
1187
  let e = setInterval(() => d(Date.now()), 1e3);
1172
1188
  return () => clearInterval(e);
1173
1189
  }, [f]);
1174
- let p = r ? Math.max(0, Math.ceil((r - c) / 1e3)) : 0, m = n ? je(Ae(n), c) : null, h = t || p > 0, g = p > 0 ? `Fresh data available in ${Me(p)}` : "Refresh roster";
1190
+ let p = r ? Math.max(0, Math.ceil((r - c) / 1e3)) : 0, m = n ? Me(je(n), c) : null, h = t || p > 0, g = p > 0 ? `Fresh data available in ${Ne(p)}` : "Refresh roster";
1175
1191
  return /* @__PURE__ */ u("div", {
1176
- className: `${Ne.rosterRefresh} ${o}`,
1192
+ className: `${Pe.rosterRefresh} ${o}`,
1177
1193
  children: [m && /* @__PURE__ */ l("span", {
1178
- className: Ne.updatedLabel,
1194
+ className: Pe.updatedLabel,
1179
1195
  title: g,
1180
- children: p > 0 ? `${i} ${m} · fresh in ${Me(p)}` : `${i} ${m}`
1181
- }), /* @__PURE__ */ l(U, {
1196
+ children: p > 0 ? `${i} ${m} · fresh in ${Ne(p)}` : `${i} ${m}`
1197
+ }), /* @__PURE__ */ l(G, {
1182
1198
  variant: "outline",
1183
1199
  size: "sm",
1184
1200
  onClick: e,
1185
1201
  disabled: h,
1186
1202
  title: g,
1187
1203
  "aria-label": "Refresh roster",
1188
- className: Ne.refreshButton,
1204
+ className: Pe.refreshButton,
1189
1205
  children: /* @__PURE__ */ l("span", {
1190
- className: t ? Ne.spin : void 0,
1206
+ className: t ? Pe.spin : void 0,
1191
1207
  children: "⟳"
1192
1208
  })
1193
1209
  })]
@@ -1203,7 +1219,7 @@ var Ne = {
1203
1219
  "padding-md": "_padding-md_1vw8g_43",
1204
1220
  "padding-lg": "_padding-lg_1vw8g_47",
1205
1221
  hoverable: "_hoverable_1vw8g_52"
1206
- }, Fe = ({ children: e, variant: t = "default", chamfered: n = !1, chamferSize: r = "md", padding: i = "md", hoverable: a = !1, showDiagonalBorders: o = !0, edgeColor: s, className: d = "", onClick: f, style: p }) => {
1222
+ }, Ie = ({ children: e, variant: t = "default", chamfered: n = !1, chamferSize: r = "md", padding: i = "md", hoverable: a = !1, showDiagonalBorders: o = !0, edgeColor: s, className: d = "", onClick: f, style: p }) => {
1207
1223
  let m = n ? r === "asymmetric" ? "chamfered-box-asymmetric" : r === "sm" ? "chamfered-box-sm" : r === "lg" ? "chamfered-box-lg" : "chamfered-box" : "", h = s ?? (n ? "var(--color-border)" : void 0), g = h ? { color: h } : void 0, _ = h ? {
1208
1224
  ...p,
1209
1225
  "--card-edge-color": h
@@ -1233,7 +1249,7 @@ var Ne = {
1233
1249
  })
1234
1250
  ] }), e]
1235
1251
  });
1236
- }, Ie = {
1252
+ }, Le = {
1237
1253
  badge: "_badge_rk7t5_1",
1238
1254
  sm: "_sm_rk7t5_11",
1239
1255
  md: "_md_rk7t5_16",
@@ -1245,8 +1261,8 @@ var Ne = {
1245
1261
  warning: "_warning_rk7t5_47",
1246
1262
  error: "_error_rk7t5_52",
1247
1263
  info: "_info_rk7t5_57"
1248
- }, Le = ({ children: e, variant: t = "default", size: n = "md", className: r = "" }) => /* @__PURE__ */ l("span", {
1249
- className: `${Ie.badge} ${Ie[t]} ${Ie[n]} ${r}`,
1264
+ }, Re = ({ children: e, variant: t = "default", size: n = "md", className: r = "" }) => /* @__PURE__ */ l("span", {
1265
+ className: `${Le.badge} ${Le[t]} ${Le[n]} ${r}`,
1250
1266
  children: e
1251
1267
  }), Q = {
1252
1268
  overlay: "_overlay_1b3yt_1",
@@ -1261,7 +1277,7 @@ var Ne = {
1261
1277
  title: "_title_1b3yt_53",
1262
1278
  closeButton: "_closeButton_1b3yt_60",
1263
1279
  content: "_content_1b3yt_78"
1264
- }, Re = ({ isOpen: e, onClose: t, title: n, children: r, size: i = "md", closeOnOverlayClick: o = !0, className: s = "" }) => (a(() => (e ? document.body.style.overflow = "hidden" : document.body.style.overflow = "unset", () => {
1280
+ }, ze = ({ isOpen: e, onClose: t, title: n, children: r, size: i = "md", closeOnOverlayClick: o = !0, className: s = "" }) => (a(() => (e ? document.body.style.overflow = "hidden" : document.body.style.overflow = "unset", () => {
1265
1281
  document.body.style.overflow = "unset";
1266
1282
  }), [e]), a(() => {
1267
1283
  let n = (n) => {
@@ -1303,7 +1319,7 @@ var Ne = {
1303
1319
  dot: "_dot_1uhnh_34",
1304
1320
  bounce: "_bounce_1uhnh_1",
1305
1321
  visuallyHidden: "_visuallyHidden_1uhnh_91"
1306
- }, ze = ({ size: e = "md", variant: t = "spinner", className: n = "" }) => t === "dots" ? /* @__PURE__ */ u("div", {
1322
+ }, Be = ({ size: e = "md", variant: t = "spinner", className: n = "" }) => t === "dots" ? /* @__PURE__ */ u("div", {
1307
1323
  className: `${$.dots} ${$[e]} ${n}`,
1308
1324
  children: [
1309
1325
  /* @__PURE__ */ l("span", { className: $.dot }),
@@ -1317,10 +1333,10 @@ var Ne = {
1317
1333
  className: $.visuallyHidden,
1318
1334
  children: "Loading..."
1319
1335
  })
1320
- }), Be = async (e) => {
1336
+ }), Ve = async (e) => {
1321
1337
  if (e.length === 0) return {};
1322
- let t = [...new Set(e)], n = await F.get(`/api/v1/users/lookup?ids=${t.join(",")}`);
1338
+ let t = [...new Set(e)], n = await L.get(`/api/v1/users/lookup?ids=${t.join(",")}`);
1323
1339
  return Object.fromEntries(n.users.map((e) => [e.id, e.username]));
1324
1340
  };
1325
1341
  //#endregion
1326
- export { Te as AllyCodeDropdown, ce as ApiClient, _e as AuthProvider, Le as Badge, U as Button, Fe as Card, p as Container, ke as Footer, Ce as Input, ze as Loader, Re as Modal, Oe as NavBar, Pe as RosterRefresh, _ as SUITE_NAV, Se as Select, h as TopBar, F as apiClient, se as authedFetch, he as clearAllyCodes, D as clearTokens, te as configureAuthRefresh, Be as fetchUsernames, G as formatAllyCode, Me as formatCountdown, je as formatRelativeTime, b as getAccessToken, L as getAllyCodesFromStorage, C as getRefreshToken, me as getSelectedAllyCode, le as initializeApiClient, O as isAuthenticated, g as isSuiteNavGroup, Ae as parseBackendTimestamp, j as refreshAccessToken, S as removeAccessToken, fe as removeAllyCodeFromStorage, T as removeRefreshToken, de as saveAllyCodeToStorage, x as setAccessToken, w as setRefreshToken, R as setSelectedAllyCode, E as setTokens, we as unformatAllyCode, pe as updateAllyCodeLastUsed, z as useAuth };
1342
+ export { Ce as AllyCodeDropdown, oe as ApiClient, he as AuthProvider, Re as Badge, G as Button, Ie as Card, p as Container, Ae as Footer, xe as Input, Be as Loader, ze as Modal, ke as NavBar, Fe as RosterRefresh, _ as SUITE_NAV, be as Select, h as TopBar, L as apiClient, ae as authedFetch, pe as clearAllyCodes, D as clearTokens, j as configureAuthRefresh, Ve as fetchUsernames, q as formatAllyCode, Ne as formatCountdown, Me as formatRelativeTime, b as getAccessToken, z as getAllyCodesFromStorage, C as getRefreshToken, fe as getSelectedAllyCode, se as initializeApiClient, ee as isAuthenticated, g as isSuiteNavGroup, je as parseBackendTimestamp, M as refreshAccessToken, S as removeAccessToken, ue as removeAllyCodeFromStorage, T as removeRefreshToken, le as saveAllyCodeToStorage, x as setAccessToken, w as setRefreshToken, B as setSelectedAllyCode, E as setTokens, Se as unformatAllyCode, de as updateAllyCodeLastUsed, V as useAuth };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astrogators-shared-ui",
3
- "version": "0.16.3",
3
+ "version": "0.16.4",
4
4
  "description": "Shared UI components and utilities for Astrogator's Table applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,7 +23,9 @@
23
23
  "dev": "vite",
24
24
  "build": "tsc && vite build",
25
25
  "preview": "vite preview",
26
- "type-check": "tsc --noEmit"
26
+ "type-check": "tsc --noEmit",
27
+ "test": "vitest run",
28
+ "prepublishOnly": "npm run type-check && npm test"
27
29
  },
28
30
  "repository": {
29
31
  "type": "git",
@@ -48,8 +50,8 @@
48
50
  "react": "^19.2.5",
49
51
  "react-dom": "^19.2.5",
50
52
  "typescript": "^6.0.3",
51
- "vite": "^8.0.9",
52
- "vite-plugin-dts": "^4.5.4"
53
- },
54
- "dependencies": {}
53
+ "vite": "^8.3.0",
54
+ "vite-plugin-dts": "^4.5.4",
55
+ "vitest": "^5.0.1"
56
+ }
55
57
  }