@shortstravelmgmt/component-lib 0.2.11 → 0.2.12-canary.30.g1e89a1a

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.
package/dist/index.esm.js CHANGED
@@ -12,18 +12,19 @@ import React, { createContext, useContext, useState, useEffect, useMemo, useCall
12
12
  // BRAND COLORS
13
13
  // ============================================================================
14
14
  const brandColors = {
15
- blue: '#15538C',
16
- blueDeep: '#0E3F6E',
17
- blueTint: '#E5EDF4',
15
+ blue: '#19345F', // Short's Blue
16
+ blueDeep: '#0C1E3E',
17
+ blueTint: '#E4F1F8',
18
18
  };
19
19
  // ============================================================================
20
20
  // PRIMARY COLORS (action / link / focus)
21
21
  // ============================================================================
22
+ // Short's Blue navy carries interactive text/fills — ~11:1 on white (WCAG AA).
22
23
  const primaryColors = {
23
- primary: '#0A53EF',
24
- primaryDark: '#1A43D1',
25
- primaryLight: '#E6ECFB',
26
- primaryLightHover: '#DAE3F8',
24
+ primary: '#19345F',
25
+ primaryDark: '#0C1E3E',
26
+ primaryLight: '#E4F1F8',
27
+ primaryLightHover: '#D3E4F0',
27
28
  };
28
29
  // ============================================================================
29
30
  // NEUTRAL SURFACES
@@ -54,16 +55,36 @@ const textColors = {
54
55
  // ============================================================================
55
56
  // STATUS COLORS
56
57
  // ============================================================================
58
+ // Foreground/text values stay AA-safe on light surfaces. The *Accent values are
59
+ // the Short's-aligned hues for SOLID FILLS + decorative use — on light they fall
60
+ // below 3:1, so meaningful non-text indicators (thin borders/standalone icons)
61
+ // use the base status colour or the *Ink values, never the accent.
57
62
  const statusColors = {
58
63
  success: '#138660',
59
64
  warning: '#9F5500',
60
65
  gold: '#FFC179',
61
66
  error: '#940000',
67
+ // AA-safe status text ON the light success/warning tint fills (chips/badges);
68
+ // success/warning above dip below 4.5:1 on those tints.
69
+ successInk: '#0F7350',
70
+ warningInk: '#8F4C00',
71
+ successAccent: '#2E9E6B',
72
+ warningAccent: '#E8A13A',
73
+ dangerAccent: '#D7503A', // fills/rules/borders (≥3:1 on white)
74
+ dangerAccentStrong: '#C0402C', // darker brick for solid fills that carry white text (≥4.5:1)
75
+ dangerAccentStrongHover: '#A33524', // hover for the strong danger fill
62
76
  };
63
77
  // ============================================================================
64
78
  // ACCENT COLORS
65
79
  // ============================================================================
66
80
  const accentColors = {
81
+ // Short's Teal — the reserved spark (rules, eyebrows, stats, focus rings).
82
+ // Never a text color on light (teal-on-white fails AA).
83
+ teal: '#00BCD0',
84
+ tealHover: '#00AEEF',
85
+ tealContrast: '#0C1E3E', // navy text ON a teal fill
86
+ tealInk: '#009AAB', // teal ink for light: 3.10:1 on --bg-page #F5F5F5 (3.38:1 on white) — clears the 3:1 UI/focus bar on the real surface, deliberately under the 4.5:1 text bar (brand-forward tradeoff)
87
+ tealGlow: 'rgba(0, 188, 208, 0.35)', // focus ring
67
88
  mint: '#37C3A4',
68
89
  forest: '#003E1B',
69
90
  forestLight: '#E2F0EC',
@@ -97,7 +118,7 @@ const vendorColors = {
97
118
  HA: { bg: '#660099', fg: '#FFFFFF' },
98
119
  };
99
120
  const gradients = {
100
- avatar: 'linear-gradient(135deg, #4F7FFF 0%, #4066E8 100%)',
121
+ avatar: 'linear-gradient(135deg, #19345F 0%, #00BCD0 100%)',
101
122
  };
102
123
  // ============================================================================
103
124
  // ALERT TOKENS
@@ -147,9 +168,12 @@ const semanticColors = {
147
168
  // ============================================================================
148
169
  // TYPOGRAPHY
149
170
  // ============================================================================
171
+ // Inter = body & UI · Montserrat = display & stats · Roboto Condensed = chrome
172
+ // (eyebrows, buttons, badges, footer content).
150
173
  const fontFamily = {
151
- base: "'Roboto Flex', system-ui, -apple-system, 'Segoe UI', sans-serif",
152
- display: "'Roboto Flex', system-ui, -apple-system, sans-serif",
174
+ base: "'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif",
175
+ display: "'Montserrat', system-ui, -apple-system, sans-serif",
176
+ label: "'Roboto Condensed', system-ui, -apple-system, sans-serif",
153
177
  mono: "'JetBrains Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace",
154
178
  };
155
179
  const fontSize = {
@@ -274,6 +298,22 @@ const darkTheme = {
274
298
  ...lightTheme,
275
299
  colors: {
276
300
  ...lightTheme.colors,
301
+ // Interactive colors stay a visible blue on dark surfaces. Light-mode navy
302
+ // would collide with the dark fills; a navy-first dark theme is a dedicated
303
+ // follow-up needing split surface/inverse and interactive tokens.
304
+ primary: {
305
+ primary: '#0A53EF',
306
+ primaryDark: '#1A43D1',
307
+ primaryLight: '#E6ECFB',
308
+ primaryLightHover: '#DAE3F8',
309
+ },
310
+ // On dark surfaces the brand teal is the label/focus ink — #00BCD0 reads
311
+ // cleanly on the dark page (7.55:1 on #1a1a1a). Mirrors the CSS
312
+ // --color-accent-ink dark override so both token sources stay in parity.
313
+ accent: {
314
+ ...accentColors,
315
+ tealInk: '#00BCD0',
316
+ },
277
317
  neutral: {
278
318
  basecolor: '#1a1a1a',
279
319
  subtle: '#2a2a2a',
@@ -301,8 +341,8 @@ const darkTheme = {
301
341
  3: '#a0a0a0',
302
342
  4: '#808080',
303
343
  disabled: '#606060',
304
- link: primaryColors.primary,
305
- linkHover: primaryColors.primaryDark,
344
+ link: '#0A53EF',
345
+ linkHover: '#1A43D1',
306
346
  },
307
347
  bg: {
308
348
  page: '#1a1a1a',
@@ -690,64 +730,124 @@ function styled(factory) {
690
730
  return factory;
691
731
  }
692
732
 
733
+ /**
734
+ * Builds an icon component from raw SVG path content.
735
+ *
736
+ * Internal to this module -- every exported icon is produced by it, which is
737
+ * what keeps the whole set consistent in viewBox, stroke weight, and sizing.
738
+ */
693
739
  const createIcon = (children, displayName) => {
694
740
  const Icon = ({ size = 24, color = 'currentColor', ...props }) => (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", width: size, height: size, ...props, children: children }));
695
741
  Icon.displayName = displayName;
696
742
  return Icon;
697
743
  };
698
744
  // Travel / segment type icons
745
+ /** Air segment (travel type `A`). */
699
746
  const PlaneIcon$3 = createIcon(jsx("path", { d: "M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z" }), 'PlaneIcon');
747
+ /** Rental car segment (travel type `C`). */
700
748
  const CarIcon$1 = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M19 17h2c.6 0 1-.4 1-1v-3c0-.9-.7-1.7-1.5-1.9C18.7 10.6 16 10 16 10s-1.3-1.4-2.2-2.3c-.5-.4-1.1-.7-1.8-.7H5c-.6 0-1.1.4-1.4.9l-1.4 2.9A3.7 3.7 0 0 0 2 12v4c0 .6.4 1 1 1h2" }), jsx("circle", { cx: "7", cy: "17", r: "2" }), jsx("path", { d: "M9 17h6" }), jsx("circle", { cx: "17", cy: "17", r: "2" })] }), 'CarIcon');
749
+ /** Hotel segment (travel type `H`). */
701
750
  const HotelIcon$3 = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M10 22v-6.57" }), jsx("path", { d: "M12 11h.01" }), jsx("path", { d: "M12 7h.01" }), jsx("path", { d: "M14 15.43V22" }), jsx("path", { d: "M15 16a5 5 0 0 0-6 0" }), jsx("path", { d: "M16 11h.01" }), jsx("path", { d: "M16 7h.01" }), jsx("path", { d: "M8 11h.01" }), jsx("path", { d: "M8 7h.01" }), jsx("rect", { x: "4", y: "2", width: "16", height: "20", rx: "2" })] }), 'HotelIcon');
751
+ /** Limo or black car segment (travel type `L`). */
702
752
  const LimoIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "m21 8-2 2-1.5-3.7A2 2 0 0 0 15.646 5H8.4a2 2 0 0 0-1.903 1.257L5 10 3 8" }), jsx("path", { d: "M7 14h.01" }), jsx("path", { d: "M17 14h.01" }), jsx("rect", { width: "18", height: "8", x: "3", y: "10", rx: "2" }), jsx("path", { d: "M5 18v2" }), jsx("path", { d: "M19 18v2" })] }), 'LimoIcon');
753
+ /** Rail segment (travel type `R`). */
703
754
  const RailIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M8 3.1V7a4 4 0 0 0 8 0V3.1" }), jsx("path", { d: "m9 15-1-1" }), jsx("path", { d: "m15 15 1-1" }), jsx("path", { d: "M9 19c-2.8 0-5-2.2-5-5v-4a8 8 0 0 1 16 0v4c0 2.8-2.2 5-5 5Z" }), jsx("path", { d: "m8 19-2 3" }), jsx("path", { d: "m16 19 2 3" })] }), 'RailIcon');
755
+ /** Motorcoach segment (travel type `B`), including team charters. */
704
756
  const BusIcon$2 = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M8 6v6" }), jsx("path", { d: "M15 6v6" }), jsx("path", { d: "M2 12h19.6" }), jsx("path", { d: "M18 18h3s.5-1.7.8-2.8c.1-.4.2-.8.2-1.2 0-.4-.1-.8-.2-1.2l-1.4-5C20.1 6.8 19.1 6 18 6H4a2 2 0 0 0-2 2v10h3" }), jsx("circle", { cx: "7", cy: "18", r: "2" }), jsx("path", { d: "M9 18h5" }), jsx("circle", { cx: "16", cy: "18", r: "2" })] }), 'BusIcon');
705
757
  // Control / chrome icons
758
+ /** Magnifying glass, for search inputs and filter triggers. */
706
759
  const SearchIcon$1 = createIcon(jsxs(Fragment, { children: [jsx("circle", { cx: "11", cy: "11", r: "8" }), jsx("path", { d: "m21 21-4.3-4.3" })] }), 'SearchIcon');
760
+ /** Downward chevron, for expanding a section or opening a dropdown. */
707
761
  const ChevronDownIcon$1 = createIcon(jsx("path", { d: "m6 9 6 6 6-6" }), 'ChevronDownIcon');
762
+ /** Upward chevron, for collapsing an expanded section. */
708
763
  const ChevronUpIcon = createIcon(jsx("path", { d: "m18 15-6-6-6 6" }), 'ChevronUpIcon');
764
+ /** Rightward chevron, for drill-in navigation and breadcrumbs. */
709
765
  const ChevronRightIcon$1 = createIcon(jsx("path", { d: "m9 18 6-6-6-6" }), 'ChevronRightIcon');
766
+ /** Leftward chevron, for back navigation and carousels. */
710
767
  const ChevronLeftIcon = createIcon(jsx("path", { d: "m15 18-6-6 6-6" }), 'ChevronLeftIcon');
768
+ /** Outbound arrow, for departing legs and external links. */
711
769
  const ArrowUpRightIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M7 7h10v10" }), jsx("path", { d: "M7 17 17 7" })] }), 'ArrowUpRightIcon');
770
+ /** Inbound arrow, for returning legs. */
712
771
  const ArrowDownLeftIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M17 7 7 17" }), jsx("path", { d: "M17 17H7V7" })] }), 'ArrowDownLeftIcon');
772
+ /** Eye, for view-only actions and visibility toggles. */
713
773
  const EyeIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" }), jsx("circle", { cx: "12", cy: "12", r: "3" })] }), 'EyeIcon');
774
+ /** Download, for exporting reports and manifests. */
714
775
  const DownloadIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), jsx("polyline", { points: "7 10 12 15 17 10" }), jsx("line", { x1: "12", x2: "12", y1: "15", y2: "3" })] }), 'DownloadIcon');
776
+ /** Upload, for importing rosters and attaching files. */
715
777
  const UploadIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), jsx("polyline", { points: "17 8 12 3 7 8" }), jsx("line", { x1: "12", x2: "12", y1: "3", y2: "15" })] }), 'UploadIcon');
778
+ /** Funnel, for opening filter controls. */
716
779
  const FilterIcon$1 = createIcon(jsx("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" }), 'FilterIcon');
780
+ /** Calendar, for date pickers and schedule views. */
717
781
  const CalendarIcon$1 = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M8 2v4" }), jsx("path", { d: "M16 2v4" }), jsx("rect", { width: "18", height: "18", x: "3", y: "4", rx: "2" }), jsx("path", { d: "M3 10h18" })] }), 'CalendarIcon');
782
+ /** Single person, for a traveler or profile. */
718
783
  const UserIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" }), jsx("circle", { cx: "12", cy: "7", r: "4" })] }), 'UserIcon');
784
+ /** Group of people, for teams, rosters, and group travel. */
719
785
  const UsersIcon$1 = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }), jsx("circle", { cx: "9", cy: "7", r: "4" }), jsx("path", { d: "M22 21v-2a4 4 0 0 0-3-3.87" }), jsx("path", { d: "M16 3.13a4 4 0 0 1 0 7.75" })] }), 'UsersIcon');
786
+ /** Envelope, for email addresses and send actions. */
720
787
  const MailIcon = createIcon(jsxs(Fragment, { children: [jsx("rect", { width: "20", height: "16", x: "2", y: "4", rx: "2" }), jsx("path", { d: "m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" })] }), 'MailIcon');
788
+ /** Handset, for phone numbers. */
721
789
  const PhoneIcon = createIcon(jsx("path", { d: "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" }), 'PhoneIcon');
790
+ /** Agent headset, for travel agent contact and support. */
722
791
  const HeadsetIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M4 13v-1a8 8 0 0 1 16 0v1" }), jsx("rect", { x: "2", y: "13", width: "4", height: "6", rx: "1" }), jsx("rect", { x: "18", y: "13", width: "4", height: "6", rx: "1" }), jsx("path", { d: "M4 19v1a2 2 0 0 0 2 2h4" })] }), 'HeadsetIcon');
792
+ /** Printer, for print actions. */
723
793
  const PrinterIcon = createIcon(jsxs(Fragment, { children: [jsx("polyline", { points: "6 9 6 2 18 2 18 9" }), jsx("path", { d: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" }), jsx("rect", { width: "12", height: "8", x: "6", y: "14" })] }), 'PrinterIcon');
794
+ /** Circular arrows, for reloading data and retrying a failed request. */
724
795
  const RefreshIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }), jsx("path", { d: "M21 3v5h-5" }), jsx("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }), jsx("path", { d: "M8 16H3v5" })] }), 'RefreshIcon');
796
+ /** Warning triangle, for warning and error states. */
725
797
  const WarningIcon$1 = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" }), jsx("path", { d: "M12 9v4" }), jsx("path", { d: "M12 17h.01" })] }), 'WarningIcon');
798
+ /** Checkmark, for success states and confirmed selections. */
726
799
  const CheckIcon$2 = createIcon(jsx("polyline", { points: "20 6 9 17 4 12" }), 'CheckIcon');
800
+ /** Cross, for clearing a value or marking a negative state. */
727
801
  const XIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M18 6 6 18" }), jsx("path", { d: "m6 6 12 12" })] }), 'XIcon');
802
+ /** Plus, for add and create actions. */
728
803
  const PlusIcon$2 = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M5 12h14" }), jsx("path", { d: "M12 5v14" })] }), 'PlusIcon');
804
+ /** Minus, for remove and decrement actions. */
729
805
  const MinusIcon = createIcon(jsx("path", { d: "M5 12h14" }), 'MinusIcon');
806
+ /** Hamburger, for opening navigation on small screens. */
730
807
  const MenuIcon = createIcon(jsxs(Fragment, { children: [jsx("line", { x1: "4", x2: "20", y1: "12", y2: "12" }), jsx("line", { x1: "4", x2: "20", y1: "6", y2: "6" }), jsx("line", { x1: "4", x2: "20", y1: "18", y2: "18" })] }), 'MenuIcon');
808
+ /** Lightbulb, for tips and informational callouts. */
731
809
  const LightbulbIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5" }), jsx("path", { d: "M9 18h6" }), jsx("path", { d: "M10 22h4" })] }), 'LightbulbIcon');
810
+ /** Megaphone, for announcements and broadcast banners. */
732
811
  const MegaphoneIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "m3 11 18-5v12L3 13v-2z" }), jsx("path", { d: "M11.6 16.8a3 3 0 1 1-5.8-1.6" })] }), 'MegaphoneIcon');
733
812
  // Additional icons
813
+ /** Close, for dismissing modals, drawers, and alerts. */
734
814
  const CloseIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M18 6 6 18" }), jsx("path", { d: "m6 6 12 12" })] }), 'CloseIcon');
815
+ /** Dashboard, for the home and overview destinations. */
735
816
  const DashboardIcon = createIcon(jsxs(Fragment, { children: [jsx("rect", { width: "7", height: "9", x: "3", y: "3", rx: "1" }), jsx("rect", { width: "7", height: "5", x: "14", y: "3", rx: "1" }), jsx("rect", { width: "7", height: "9", x: "14", y: "12", rx: "1" }), jsx("rect", { width: "7", height: "5", x: "3", y: "16", rx: "1" })] }), 'DashboardIcon');
817
+ /** Report, for reporting destinations and exports. */
736
818
  const ReportIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7l-5-5z" }), jsx("path", { d: "M14 2v5h5" }), jsx("path", { d: "M10 9h4" }), jsx("path", { d: "M10 13h4" }), jsx("path", { d: "M10 17h4" })] }), 'ReportIcon');
819
+ /** Gear, for settings and configuration. */
737
820
  const SettingsIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" }), jsx("circle", { cx: "12", cy: "12", r: "3" })] }), 'SettingsIcon');
821
+ /** Short's Travel mark, for brand placements and app headers. */
738
822
  const LogoIcon = createIcon(jsxs(Fragment, { children: [jsx("circle", { cx: "12", cy: "12", r: "10" }), jsx("path", { d: "M12 6v6l4 2" })] }), 'LogoIcon');
823
+ /** Rising trend line, for a metric moving up. */
739
824
  const TrendUpIcon = createIcon(jsxs(Fragment, { children: [jsx("polyline", { points: "22 7 13.5 15.5 8.5 10.5 2 17" }), jsx("polyline", { points: "16 7 22 7 22 13" })] }), 'TrendUpIcon');
825
+ /** Falling trend line, for a metric moving down. */
740
826
  const TrendDownIcon = createIcon(jsxs(Fragment, { children: [jsx("polyline", { points: "22 17 13.5 8.5 8.5 13.5 2 7" }), jsx("polyline", { points: "16 17 22 17 22 11" })] }), 'TrendDownIcon');
827
+ /** Information, for help text and info banners. */
741
828
  const InfoIcon$1 = createIcon(jsxs(Fragment, { children: [jsx("circle", { cx: "12", cy: "12", r: "10" }), jsx("path", { d: "M12 16v-4" }), jsx("path", { d: "M12 8h.01" })] }), 'InfoIcon');
829
+ /** Document, for itineraries, invoices, and attachments. */
742
830
  const DocumentIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6z" }), jsx("path", { d: "M14 2v6h6" }), jsx("path", { d: "M16 13H8" }), jsx("path", { d: "M16 17H8" })] }), 'DocumentIcon');
831
+ /** Building, for schools, venues, and vendor offices. */
743
832
  const BuildingIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18Z" }), jsx("path", { d: "M6 12H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2" }), jsx("path", { d: "M18 9h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-2" }), jsx("path", { d: "M10 6h4" }), jsx("path", { d: "M10 10h4" }), jsx("path", { d: "M10 14h4" }), jsx("path", { d: "M10 18h4" })] }), 'BuildingIcon');
833
+ /** Grid, for switching to a card or tile layout. */
744
834
  const GridIcon = createIcon(jsxs(Fragment, { children: [jsx("rect", { width: "7", height: "7", x: "3", y: "3", rx: "1" }), jsx("rect", { width: "7", height: "7", x: "14", y: "3", rx: "1" }), jsx("rect", { width: "7", height: "7", x: "14", y: "14", rx: "1" }), jsx("rect", { width: "7", height: "7", x: "3", y: "14", rx: "1" })] }), 'GridIcon');
835
+ /** Pencil, for edit actions. */
745
836
  const EditIcon$2 = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" }), jsx("path", { d: "m15 5 4 4" })] }), 'EditIcon');
837
+ /** House, for the home destination. */
746
838
  const HomeIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "m3 11 9-8 9 8" }), jsx("path", { d: "M5 10v10h14V10" }), jsx("path", { d: "M9 20v-6h6v6" })] }), 'HomeIcon');
839
+ /** Cube, for packages, equipment, and bundled services. */
747
840
  const CubeIcon = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "m21 16-9 5-9-5V8l9-5 9 5z" }), jsx("path", { d: "m3.3 7 8.7 5 8.7-5" }), jsx("path", { d: "M12 22V12" })] }), 'CubeIcon');
841
+ /** Padlock, for restricted access and locked records. */
748
842
  const LockIcon$1 = createIcon(jsxs(Fragment, { children: [jsx("rect", { width: "18", height: "11", x: "3", y: "11", rx: "2" }), jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })] }), 'LockIcon');
843
+ /** Trash can, for delete actions. */
749
844
  const TrashIcon$2 = createIcon(jsxs(Fragment, { children: [jsx("path", { d: "M3 6h18" }), jsx("path", { d: "M8 6V4h8v2" }), jsx("path", { d: "m19 6-1 15H6L5 6" }), jsx("path", { d: "M10 11v5" }), jsx("path", { d: "M14 11v5" })] }), 'TrashIcon');
750
- // Export all icons as a map for dynamic access
845
+ /**
846
+ * Every icon keyed by name, for choosing one at runtime.
847
+ *
848
+ * Use a direct import when the icon is known at author time; reach for this
849
+ * only when the name arrives as data (a nav config, an API response).
850
+ */
751
851
  const Icons = {
752
852
  Plane: PlaneIcon$3,
753
853
  Car: CarIcon$1,
@@ -806,6 +906,10 @@ const variantIcons$1 = {
806
906
  warning: WarningIcon$1,
807
907
  error: WarningIcon$1,
808
908
  };
909
+ /**
910
+ * Inline message calling out status or guidance in place, without interrupting the user.
911
+ * Use a modal instead when the user has to act before continuing.
912
+ */
809
913
  const Alert = ({ variant = 'primary', title, children, dismissible = false, onDismiss, className = '', }) => {
810
914
  const IconComponent = variantIcons$1[variant];
811
915
  return (jsx("div", { className: `${styles$1I.alert} ${styles$1I[variant]} ${className}`, role: "alert", children: jsxs("div", { className: styles$1I.content, children: [jsx(IconComponent, { className: styles$1I.icon, size: 20 }), jsxs("div", { className: styles$1I.body, children: [title && jsx("h4", { className: styles$1I.title, children: title }), jsx("div", { className: styles$1I.message, children: children })] }), dismissible && onDismiss && (jsx("button", { type: "button", onClick: onDismiss, className: styles$1I.dismissButton, "aria-label": "Dismiss alert", children: jsx(CloseIcon, { size: 16 }) }))] }) }));
@@ -992,6 +1096,10 @@ const Avatar = ({ src, alt, initials, name, size = 'md', className = '', style,
992
1096
  return (jsx("div", { className: className, "data-avatar-size": size, style: toInlineStyle(merge(getAvatarStyle(theme, size), style)), ...props, children: src ? (jsx("img", { src: src, alt: alt || name || 'Avatar', style: toInlineStyle(avatarImageStyle) })) : (jsx("span", { style: toInlineStyle(getAvatarInitialsStyle(theme)), children: displayInitials })) }));
993
1097
  };
994
1098
  Avatar.displayName = 'Avatar';
1099
+ /**
1100
+ * Row of overlapping avatars for a team or traveler party.
1101
+ * Collapses everything past `max` into a +N counter so the row keeps a fixed footprint.
1102
+ */
995
1103
  const AvatarGroup = ({ max = 4, className = '', children, style, ...props }) => {
996
1104
  const { theme } = useTheme();
997
1105
  const childArray = React.Children.toArray(children);
@@ -1010,6 +1118,7 @@ const badgeBaseStyle = (theme) => css({
1010
1118
  alignItems: 'center',
1011
1119
  gap: theme.spacing['1.5'],
1012
1120
  borderRadius: theme.radius.full,
1121
+ fontFamily: theme.typography.fontFamily.label,
1013
1122
  fontWeight: theme.typography.fontWeight.medium,
1014
1123
  whiteSpace: 'nowrap',
1015
1124
  });
@@ -1030,19 +1139,20 @@ const badgeVariantStyles = (theme) => ({
1030
1139
  }),
1031
1140
  success: css({
1032
1141
  background: theme.colors.accent.forestLight,
1033
- color: theme.colors.status.success,
1142
+ color: theme.colors.status.successInk,
1034
1143
  }),
1035
1144
  warning: css({
1036
1145
  background: theme.colors.accent.tangerineLight,
1037
- color: theme.colors.status.warning,
1146
+ color: theme.colors.status.warningInk,
1038
1147
  }),
1039
1148
  error: css({
1040
1149
  background: theme.colors.alert.error.bg,
1041
1150
  color: theme.colors.status.error,
1042
1151
  }),
1043
1152
  info: css({
1153
+ // "New"-style badge reads clearly blue, distinct from the neutral-gray default
1044
1154
  background: theme.colors.primary.primaryLight,
1045
- color: theme.colors.primary.primaryDark,
1155
+ color: theme.colors.primary.primary,
1046
1156
  }),
1047
1157
  });
1048
1158
  const badgeDotColors = (theme) => ({
@@ -1158,11 +1268,11 @@ const variantStyles = (theme) => ({
1158
1268
  color: theme.colors.semantic.fg[1],
1159
1269
  }),
1160
1270
  danger: css({
1161
- backgroundColor: theme.colors.status.error,
1271
+ backgroundColor: theme.colors.status.dangerAccentStrong,
1162
1272
  color: 'white',
1163
1273
  }),
1164
1274
  dangerHover: css({
1165
- backgroundColor: '#7a0000',
1275
+ backgroundColor: theme.colors.status.dangerAccentStrongHover,
1166
1276
  }),
1167
1277
  // --------------------------------------------------------------------------
1168
1278
  // Variants ported from Hub (frontend/src/styles/dynamic/Button.ts) so the
@@ -1244,7 +1354,8 @@ const squareStyles = (_theme) => ({
1244
1354
  const baseButtonStyle = (theme) => merge(buttonReset, flexCenter, css({
1245
1355
  gap: theme.spacing[2],
1246
1356
  borderRadius: theme.radius.full,
1247
- fontFamily: theme.typography.fontFamily.base,
1357
+ fontFamily: theme.typography.fontFamily.label,
1358
+ letterSpacing: '0.02em',
1248
1359
  fontWeight: theme.typography.fontWeight.medium,
1249
1360
  cursor: 'pointer',
1250
1361
  transition: `all ${theme.transition.fast}`,
@@ -1273,8 +1384,9 @@ const activeStyle = css({
1273
1384
  // FOCUS STYLES
1274
1385
  // ============================================================================
1275
1386
  const focusVisibleStyle = (theme) => css({
1276
- outline: `2px solid ${theme.colors.primary.primary}`,
1387
+ outline: `2px solid ${theme.colors.accent.tealInk}`,
1277
1388
  outlineOffset: '2px',
1389
+ boxShadow: `0 0 0 3px ${theme.colors.accent.tealGlow}`,
1278
1390
  });
1279
1391
  // ============================================================================
1280
1392
  // SPINNER STYLES
@@ -1520,6 +1632,9 @@ const Input = forwardRef(({ size = 'md', label, helperText, error, leftIcon, rig
1520
1632
  return (jsxs("div", { className: wrapperClasses, children: [label && (jsx("label", { htmlFor: inputId, className: styles$1E.label, children: label })), jsxs("div", { className: inputWrapperClasses, children: [leftIcon && jsx("span", { className: styles$1E.icon, children: leftIcon }), jsx("input", { ref: ref, id: inputId, className: styles$1E.input, disabled: disabled, "aria-invalid": !!error, "aria-describedby": error ? `${inputId}-error` : helperText ? `${inputId}-helper` : undefined, ...props }), rightIcon && jsx("span", { className: styles$1E.icon, children: rightIcon })] }), error && (jsx("span", { id: `${inputId}-error`, className: styles$1E.errorText, children: error })), helperText && !error && (jsx("span", { id: `${inputId}-helper`, className: styles$1E.helperText, children: helperText }))] }));
1521
1633
  });
1522
1634
  Input.displayName = 'Input';
1635
+ /**
1636
+ * Multi-line text field sharing the Input label, helper text, and error treatment.
1637
+ */
1523
1638
  const Textarea = forwardRef(({ size = 'md', label, helperText, error, fullWidth = false, className = '', id, disabled, rows = 4, ...props }, ref) => {
1524
1639
  const textareaId = id || `textarea-${Math.random().toString(36).substr(2, 9)}`;
1525
1640
  const wrapperClasses = [
@@ -1601,6 +1716,10 @@ Select.displayName = 'Select';
1601
1716
 
1602
1717
  var styles$1B = {"spinner":"Spinner-module_spinner__OZwmB","svg":"Spinner-module_svg__Tusqk","spin":"Spinner-module_spin__YAPc9","track":"Spinner-module_track__NxLfV","indicator":"Spinner-module_indicator__gSUxF","xs":"Spinner-module_xs__4-WEg","sm":"Spinner-module_sm__enoDn","md":"Spinner-module_md__6axf9","lg":"Spinner-module_lg__EvJyC","xl":"Spinner-module_xl__yIXoK","srOnly":"Spinner-module_srOnly__WnZYf"};
1603
1718
 
1719
+ /**
1720
+ * Indeterminate loading indicator for waits with no known duration.
1721
+ * Exposes its label to screen readers, so give it one that says what is loading.
1722
+ */
1604
1723
  const Spinner = ({ size = 'md', className = '', color, label = 'Loading...', }) => {
1605
1724
  return (jsxs("span", { className: `${styles$1B.spinner} ${styles$1B[size]} ${className}`, role: "status", "aria-label": label, style: color ? { color } : undefined, children: [jsxs("svg", { className: styles$1B.svg, xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [jsx("circle", { className: styles$1B.track, cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), jsx("path", { className: styles$1B.indicator, fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }), jsx("span", { className: styles$1B.srOnly, children: label })] }));
1606
1725
  };
@@ -1608,7 +1727,7 @@ const Spinner = ({ size = 'md', className = '', color, label = 'Loading...', })
1608
1727
  const statusVariantStyles = (theme) => ({
1609
1728
  ticketed: css({
1610
1729
  backgroundColor: theme.colors.accent.forestLight,
1611
- color: theme.colors.status.success,
1730
+ color: theme.colors.status.successInk,
1612
1731
  }),
1613
1732
  itinerary: css({
1614
1733
  backgroundColor: theme.colors.primary.primaryLight,
@@ -1616,7 +1735,7 @@ const statusVariantStyles = (theme) => ({
1616
1735
  }),
1617
1736
  'not-ticketed': css({
1618
1737
  backgroundColor: theme.colors.accent.tangerineLight,
1619
- color: theme.colors.status.warning,
1738
+ color: theme.colors.status.warningInk,
1620
1739
  }),
1621
1740
  submitted: css({
1622
1741
  backgroundColor: theme.colors.primary.primaryLight,
@@ -1650,6 +1769,7 @@ function getStatusBadgeStyle(theme, status) {
1650
1769
  justifyContent: 'center',
1651
1770
  padding: `${theme.spacing[1]} 10px`,
1652
1771
  borderRadius: theme.radius.full,
1772
+ fontFamily: theme.typography.fontFamily.label,
1653
1773
  fontSize: theme.typography.fontSize.xs,
1654
1774
  fontWeight: theme.typography.fontWeight.semibold,
1655
1775
  lineHeight: 1,
@@ -1669,6 +1789,10 @@ const statusLabels = {
1669
1789
  rejected: 'Rejected',
1670
1790
  'in-review': 'In Review',
1671
1791
  };
1792
+ /**
1793
+ * Badge for a travel record status, styled per status so the same state always reads the same.
1794
+ * Falls back to a formatted version of the status code when no label is given.
1795
+ */
1672
1796
  const StatusBadge = ({ status, label, className = '', style, ...props }) => {
1673
1797
  const { theme } = useTheme();
1674
1798
  const displayLabel = label || statusLabels[status] || status;
@@ -1683,11 +1807,11 @@ const tagSizeStyles = {
1683
1807
  function getTagVariantStyles(theme) {
1684
1808
  return {
1685
1809
  default: css({ backgroundColor: theme.colors.neutral.secondary, color: theme.colors.semantic.fg[2] }),
1686
- primary: css({ backgroundColor: '#dbeafe', color: '#1d4ed8' }),
1687
- success: css({ backgroundColor: '#dcfce7', color: '#15803d' }),
1688
- warning: css({ backgroundColor: '#fef3c7', color: '#b45309' }),
1689
- error: css({ backgroundColor: '#fee2e2', color: '#b91c1c' }),
1690
- info: css({ backgroundColor: '#e0f2fe', color: '#0369a1' }),
1810
+ primary: css({ backgroundColor: theme.colors.primary.primaryLight, color: theme.colors.primary.primary }),
1811
+ success: css({ backgroundColor: theme.colors.accent.forestLight, color: theme.colors.status.successInk }),
1812
+ warning: css({ backgroundColor: theme.colors.accent.tangerineLight, color: theme.colors.status.warningInk }),
1813
+ error: css({ backgroundColor: theme.colors.alert.error.bg, color: theme.colors.status.error }),
1814
+ info: css({ backgroundColor: theme.colors.primary.primaryLight, color: theme.colors.primary.primaryDark }),
1691
1815
  // Deliberately paints nothing: `plain` exists so the tag can be used as a
1692
1816
  // bare inline grouping element that takes its colour from its surroundings.
1693
1817
  plain: css({ backgroundColor: 'transparent', color: 'inherit' }),
@@ -1699,12 +1823,13 @@ function getTagStyle(theme, variant, size, isClickable, isFocusVisible) {
1699
1823
  alignItems: 'center',
1700
1824
  gap: theme.spacing[1],
1701
1825
  borderRadius: theme.radius.md,
1826
+ fontFamily: theme.typography.fontFamily.label,
1702
1827
  fontWeight: theme.typography.fontWeight.medium,
1703
1828
  lineHeight: 1,
1704
1829
  whiteSpace: 'nowrap',
1705
1830
  transition: `all ${theme.transition.fast}`,
1706
1831
  cursor: isClickable ? 'pointer' : 'default',
1707
- }), tagSizeStyles[size], getTagVariantStyles(theme)[variant], isFocusVisible && css({ outline: `2px solid ${theme.colors.primary.primary}`, outlineOffset: '2px' }));
1832
+ }), tagSizeStyles[size], getTagVariantStyles(theme)[variant], isFocusVisible && css({ outline: `2px solid ${theme.colors.accent.tealInk}`, outlineOffset: '2px', boxShadow: `0 0 0 3px ${theme.colors.accent.tealGlow}` }));
1708
1833
  }
1709
1834
  const tagContentStyle = css({ display: 'inline-flex', alignItems: 'center', gap: '4px' });
1710
1835
  function getRemoveButtonStyle(theme, isHovered) {
@@ -1731,6 +1856,10 @@ function getRemoveIconStyle(size) {
1731
1856
  return removeIconSizeStyles[size];
1732
1857
  }
1733
1858
 
1859
+ /**
1860
+ * Compact label for keywords, categories, and applied filters.
1861
+ * Becomes a button when clickable, and can carry a remove affordance.
1862
+ */
1734
1863
  const Tag = React.forwardRef(({ children, variant = 'default', size = 'md', removable = false, onRemove, onClick, className = '', style, }, ref) => {
1735
1864
  const { theme } = useTheme();
1736
1865
  const [isRemoveHovered, setIsRemoveHovered] = useState(false);
@@ -1800,11 +1929,14 @@ function getToggleStyle(theme, size, checked, disabled, isFocusVisible) {
1800
1929
  width: config.width,
1801
1930
  height: config.height,
1802
1931
  borderRadius: theme.radius.full,
1803
- backgroundColor: checked ? theme.colors.primary.primary : '#d1d5db',
1932
+ // Teal is the earned on-state for the switch (white thumb kept). The track
1933
+ // is a graphical indicator, held to the 3:1 UI bar (not the 4.5:1 text bar),
1934
+ // so tealInk (#009AAB, 3.10:1 on the #F5F5F5 page in light) clears it.
1935
+ backgroundColor: checked ? theme.colors.accent.tealInk : '#d1d5db',
1804
1936
  transition: `background-color ${theme.transition.fast}`,
1805
1937
  cursor: disabled ? 'not-allowed' : 'pointer',
1806
1938
  opacity: disabled ? 0.5 : 1,
1807
- }), isFocusVisible && css({ outline: `2px solid ${theme.colors.primary.primary}`, outlineOffset: '2px' }));
1939
+ }), isFocusVisible && css({ outline: `2px solid ${theme.colors.accent.tealInk}`, outlineOffset: '2px', boxShadow: `0 0 0 3px ${theme.colors.accent.tealGlow}` }));
1808
1940
  }
1809
1941
  function getToggleThumbStyle(theme, size, checked) {
1810
1942
  const config = toggleSizeConfigs[size];
@@ -1859,6 +1991,10 @@ const toggleHiddenInputStyle = css({
1859
1991
  border: 0,
1860
1992
  });
1861
1993
 
1994
+ /**
1995
+ * Switch for a setting that applies immediately, with no separate save step.
1996
+ * Use a checkbox instead when the change is submitted with a form.
1997
+ */
1862
1998
  const Toggle = ({ checked, onChange, disabled = false, size = 'md', label, labelPosition = 'right', className = '', 'aria-label': ariaLabel, name, id, wrapperClassName = '', disabledIcon, }) => {
1863
1999
  const { theme } = useTheme();
1864
2000
  const [isFocusVisible, setIsFocusVisible] = useState(false);
@@ -1889,6 +2025,10 @@ const Toggle = ({ checked, onChange, disabled = false, size = 'md', label, label
1889
2025
 
1890
2026
  var styles$1y = {"wrapper":"Tooltip-module_wrapper__9SUwl","tooltip":"Tooltip-module_tooltip__KQ7km","fadeIn":"Tooltip-module_fadeIn__dL8S5","content":"Tooltip-module_content__kO8yx","dark":"Tooltip-module_dark__Y66ss","light":"Tooltip-module_light__mItfR","semantic":"Tooltip-module_semantic__shhec","primary":"Tooltip-module_primary__no1LJ","secondary":"Tooltip-module_secondary__HEoKK","success":"Tooltip-module_success__EG2f-","warning":"Tooltip-module_warning__1zoqE","error":"Tooltip-module_error__Nn6y6","top":"Tooltip-module_top__1jrIn","bottom":"Tooltip-module_bottom__99YBc","left":"Tooltip-module_left__ZNd1H","right":"Tooltip-module_right__r0j5Q","arrow":"Tooltip-module_arrow__qcKWx","arrowTop":"Tooltip-module_arrowTop__k13dt","arrowBottom":"Tooltip-module_arrowBottom__FP-w2","arrowLeft":"Tooltip-module_arrowLeft__7T1uO","arrowRight":"Tooltip-module_arrowRight__FbPdB"};
1891
2027
 
2028
+ /**
2029
+ * Hover and focus label for a control whose purpose is not obvious from its face.
2030
+ * Supplementary only -- never put information here that a user must have.
2031
+ */
1892
2032
  const Tooltip = ({ content, children, position = 'top', variant = 'dark', delay = 200, disabled = false, className = '', }) => {
1893
2033
  const [isVisible, setIsVisible] = useState(false);
1894
2034
  const [actualPosition, setActualPosition] = useState(position);
@@ -2041,6 +2181,10 @@ const typeIcons$1 = {
2041
2181
  B: (jsx("path", { d: "M4 16c0 .88.39 1.67 1 2.22V20c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h8v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1.78c.61-.55 1-1.34 1-2.22V6c0-3.5-3.58-4-8-4s-8 .5-8 4v10zm3.5 1c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm9 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm1.5-6H6V6h12v5z" })),
2042
2182
  U: (jsx("path", { d: "M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z" })),
2043
2183
  };
2184
+ /**
2185
+ * Segment icon that identifies the vendor as well as the travel type.
2186
+ * Shows the vendor mark when one is known, and can flag a multi-segment leg.
2187
+ */
2044
2188
  const TravelServiceIcon = ({ type, vendor, mode = 'icon', size = 28, multi = false, className = '', style, onMouseEnter, onMouseLeave, ...props }) => {
2045
2189
  const { theme } = useTheme();
2046
2190
  const [isHovered, setIsHovered] = useState(false);
@@ -2102,6 +2246,10 @@ const typeIcons = {
2102
2246
  B: (jsx("path", { d: "M4 16c0 .88.39 1.67 1 2.22V20c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h8v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1.78c.61-.55 1-1.34 1-2.22V6c0-3.5-3.58-4-8-4s-8 .5-8 4v10zm3.5 1c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm9 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm1.5-6H6V6h12v5z" })),
2103
2247
  U: (jsx("path", { d: "M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z" })),
2104
2248
  };
2249
+ /**
2250
+ * Segment icon keyed to a travel type code, color-coded per type.
2251
+ * Use TravelServiceIcon when the vendor matters as well as the type.
2252
+ */
2105
2253
  const TypeIcon = ({ type, size = 18, showTooltip = true, className = '', style, onMouseEnter, onMouseLeave, ...props }) => {
2106
2254
  const { theme } = useTheme();
2107
2255
  const [isHovered, setIsHovered] = useState(false);
@@ -2140,6 +2288,10 @@ Accordion.displayName = 'Accordion';
2140
2288
  var styles$1w = {"selector":"CalendarSelectors-module_selector__1nDos","item":"CalendarSelectors-module_item__CoR5s","active":"CalendarSelectors-module_active__u4w6Z","sm":"CalendarSelectors-module_sm__ju5t-","withLabel":"CalendarSelectors-module_withLabel__Jr9Re","md":"CalendarSelectors-module_md__a-U38","lg":"CalendarSelectors-module_lg__WHvAF","iconOnly":"CalendarSelectors-module_iconOnly__cAjyD"};
2141
2289
 
2142
2290
  const ListIcon = () => (jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", children: [jsx("line", { x1: "8", y1: "6", x2: "21", y2: "6" }), jsx("line", { x1: "8", y1: "12", x2: "21", y2: "12" }), jsx("line", { x1: "8", y1: "18", x2: "21", y2: "18" }), jsx("circle", { cx: "3", cy: "6", r: "1", fill: "currentColor", stroke: "none" }), jsx("circle", { cx: "3", cy: "12", r: "1", fill: "currentColor", stroke: "none" }), jsx("circle", { cx: "3", cy: "18", r: "1", fill: "currentColor", stroke: "none" })] }));
2291
+ /**
2292
+ * Single-select control rendering its options as one connected row of buttons.
2293
+ * Backs the calendar selectors below; use those when their options fit.
2294
+ */
2143
2295
  const SegmentedSelector = ({ options, value, onChange, disabled = false, size = 'md', className = '', ariaLabel = 'View options', dataTest, itemClassName = '', activeItemClassName = '', iconPosition = 'left', }) => (jsx("div", { className: [styles$1w.selector, className].filter(Boolean).join(' '), role: "group", "aria-label": ariaLabel, "data-test": dataTest, children: options.map((option) => {
2144
2296
  const active = option.id === value;
2145
2297
  const accessibleName = option.alt ?? option.label ?? option.id;
@@ -2152,11 +2304,17 @@ const SegmentedSelector = ({ options, value, onChange, disabled = false, size =
2152
2304
  active ? activeItemClassName : '',
2153
2305
  ].filter(Boolean).join(' '), onClick: () => onChange?.(option.id), children: [iconPosition === 'left' && option.icon, option.label && jsx("span", { children: option.label }), iconPosition === 'right' && option.icon] }, option.id));
2154
2306
  }) }));
2307
+ /**
2308
+ * Chooses how the team travel calendar lays out: calendar grid, cards, or list.
2309
+ */
2155
2310
  const CalendarViewSelector = ({ value, onChange, ...props }) => (jsx(SegmentedSelector, { ...props, ariaLabel: "Calendar view", dataTest: "calendar-view-selector", value: value, onChange: (nextValue) => onChange?.(nextValue), options: [
2156
2311
  { id: 'calendar', alt: 'Calendar', icon: jsx(CalendarIcon$1, { size: 20 }) },
2157
2312
  { id: 'card', alt: 'Card', icon: jsx(GridIcon, { size: 20 }) },
2158
2313
  { id: 'list', alt: 'List', icon: jsx(ListIcon, {}) },
2159
2314
  ] }));
2315
+ /**
2316
+ * Chooses what the team travel calendar plots -- trips or due dates.
2317
+ */
2160
2318
  const CalendarTypeSelector = ({ value, onChange, ...props }) => (jsx(SegmentedSelector, { ...props, ariaLabel: "Calendar item type", dataTest: "calendar-type-selector", value: value, onChange: (nextValue) => onChange?.(nextValue), options: [
2161
2319
  { id: 'trips', label: 'Trips' },
2162
2320
  { id: 'due_dates', label: 'Due Dates' },
@@ -2211,6 +2369,10 @@ DetailList.displayName = 'DetailList';
2211
2369
 
2212
2370
  var styles$1t = {"dropdown":"Dropdown-module_dropdown__K4-yz","arrow":"Dropdown-module_arrow__2Xuwf","arrowOpen":"Dropdown-module_arrowOpen__5g2iY","menu":"Dropdown-module_menu__dCgm8","menuOpen":"Dropdown-module_menuOpen__-IXFR","left":"Dropdown-module_left__tnNcS","center":"Dropdown-module_center__s9zhj","right":"Dropdown-module_right__s71gz","item":"Dropdown-module_item__R008o","itemDisabled":"Dropdown-module_itemDisabled__XIuNm","itemIcon":"Dropdown-module_itemIcon__DbSnD"};
2213
2371
 
2372
+ /**
2373
+ * Button that reveals a menu of actions, closing on outside click or Escape.
2374
+ * Compose the menu from DropdownItem; for choosing a value, use Segment or FilterChip instead.
2375
+ */
2214
2376
  const Dropdown = ({ label, children, alignment = 'left', width, className = '', disabled = false, trigger, }) => {
2215
2377
  const [isOpen, setIsOpen] = useState(false);
2216
2378
  const dropdownRef = useRef(null);
@@ -2280,6 +2442,10 @@ FileUploadButton.displayName = 'FileUploadButton';
2280
2442
 
2281
2443
  var styles$1p = {"wrapper":"FilterChip-module_wrapper__YDRSW","chip":"FilterChip-module_chip__pF7K5","open":"FilterChip-module_open__EYBp7","active":"FilterChip-module_active__JEkku","label":"FilterChip-module_label__tRr-y","value":"FilterChip-module_value__0EcVu","chevron":"FilterChip-module_chevron__kh13z","rotated":"FilterChip-module_rotated__U-FzI","dropdown":"FilterChip-module_dropdown__w3ytW","infoMessage":"FilterChip-module_infoMessage__0TzcA","searchWrapper":"FilterChip-module_searchWrapper__VHOSK","searchIcon":"FilterChip-module_searchIcon__jJ2K-","searchInput":"FilterChip-module_searchInput__FqwvR","optionsList":"FilterChip-module_optionsList__P0dgd","option":"FilterChip-module_option__DMuqM","selected":"FilterChip-module_selected__ltt9Y","noResults":"FilterChip-module_noResults__QGNao"};
2282
2444
 
2445
+ /**
2446
+ * Compact single-select filter showing its current value on the chip itself.
2447
+ * Can filter its own options when the list is long enough to need searching.
2448
+ */
2283
2449
  const FilterChip = ({ label, value, options, onChange, searchable = false, searchPlaceholder = 'Search...', infoMessage, disabled = false, className, }) => {
2284
2450
  const [open, setOpen] = useState(false);
2285
2451
  const [searchQuery, setSearchQuery] = useState('');
@@ -2327,6 +2493,11 @@ const FilterChip = ({ label, value, options, onChange, searchable = false, searc
2327
2493
 
2328
2494
  var styles$1o = {"formField":"FormField-module_formField__VhLhK","label":"FormField-module_label__CNCs6","required":"FormField-module_required__HkPQf","inputElement":"FormField-module_inputElement__9uvGR","inputError":"FormField-module_inputError__xD7fk","helperText":"FormField-module_helperText__D99kd","errorText":"FormField-module_errorText__8mXAJ","visuallyHidden":"FormField-module_visuallyHidden__YQc7S"};
2329
2495
 
2496
+ /**
2497
+ * Pairs a labelled control with its helper text and error message, wiring up the
2498
+ * accessible associations between them.
2499
+ * Renders an Input or Textarea via `as`, or any control passed as children.
2500
+ */
2330
2501
  const FormField = forwardRef(({ label, helperText, error, required = false, hideLabel = false, className, as = 'input', inputProps, textareaProps, children, }, ref) => {
2331
2502
  const generatedId = useId();
2332
2503
  const inputId = inputProps?.id || textareaProps?.id || generatedId;
@@ -2467,6 +2638,10 @@ InfoTile.displayName = 'InfoTile';
2467
2638
 
2468
2639
  var styles$1k = {"toggle":"ManifestViewToggle-module_toggle__sHnAd","lockedToggle":"ManifestViewToggle-module_lockedToggle__m70h9","tab":"ManifestViewToggle-module_tab__qcwg7","active":"ManifestViewToggle-module_active__ln3B8","locked":"ManifestViewToggle-module_locked__Udun8","lockedActive":"ManifestViewToggle-module_lockedActive__kYSaF","count":"ManifestViewToggle-module_count__M-8a8"};
2469
2640
 
2641
+ /**
2642
+ * Tabs switching a charter manifest between its passenger and equipment lists,
2643
+ * each showing its current count.
2644
+ */
2470
2645
  const ManifestViewToggle = ({ view, onView, paxCount, eqCount, locked, className = '', }) => {
2471
2646
  const tabs = [
2472
2647
  { key: 'passengers', label: 'Passengers', count: paxCount, icon: UsersIcon$1 },
@@ -2631,6 +2806,11 @@ MultiUserSearch.displayName = 'MultiUserSearch';
2631
2806
 
2632
2807
  var styles$1i = {"navItem":"NavItem-module_navItem__4PpZU","disabled":"NavItem-module_disabled__uYqez","default":"NavItem-module_default__VB92P","active":"NavItem-module_active__3bcnW","pill":"NavItem-module_pill__pn5gl","icon":"NavItem-module_icon__j9VQ5","label":"NavItem-module_label__3S3KY","sm":"NavItem-module_sm__1oeZI","md":"NavItem-module_md__ySO90","lg":"NavItem-module_lg__OmaBw","collapsed":"NavItem-module_collapsed__ciZ6-","iconSm":"NavItem-module_iconSm__UvWnu","iconMd":"NavItem-module_iconMd__uAMlQ","iconLg":"NavItem-module_iconLg__PFPKD","badge":"NavItem-module_badge__oP9u2"};
2633
2808
 
2809
+ /**
2810
+ * One destination in a navigation list, with an optional icon and badge.
2811
+ * Renders as a link when given `href`, otherwise as a button.
2812
+ * Collapses to icon-only, keeping its label available to screen readers.
2813
+ */
2634
2814
  const NavItem = ({ icon, label, active = false, collapsed = false, variant = 'default', size = 'md', badge, badgeVariant = 'default', onClick, disabled = false, href, external = false, className, 'aria-label': ariaLabel, }) => {
2635
2815
  const content = (jsxs(Fragment, { children: [icon && jsx("span", { className: `${styles$1i.icon} ${styles$1i[`icon${size.charAt(0).toUpperCase()}${size.slice(1)}`]}`, "aria-hidden": "true", children: icon }), !collapsed && (jsx("span", { className: styles$1i.label, children: label })), badge !== undefined && !collapsed && (jsx(Badge, { variant: badgeVariant, className: styles$1i.badge, children: badge }))] }));
2636
2816
  const classNames = [
@@ -2728,10 +2908,17 @@ const strokeWidths = {
2728
2908
  md: 5,
2729
2909
  lg: 6,
2730
2910
  };
2911
+ /**
2912
+ * Horizontal bar for determinate progress -- a known percentage of a known total.
2913
+ * Use Spinner when the duration is unknown.
2914
+ */
2731
2915
  const ProgressBar = ({ value, max = 100, size = 'md', variant = 'primary', showLabel = false, className = '', }) => {
2732
2916
  const percentage = Math.min(Math.max((value / max) * 100, 0), 100);
2733
2917
  return (jsxs("div", { className: `${styles$1f.barContainer} ${styles$1f[`bar${size}`]} ${className}`, children: [jsx("div", { className: styles$1f.barTrack, children: jsx("div", { className: `${styles$1f.barFill} ${styles$1f[variant]}`, style: { width: `${percentage}%` }, role: "progressbar", "aria-valuenow": value, "aria-valuemin": 0, "aria-valuemax": max }) }), showLabel && (jsxs("span", { className: styles$1f.barLabel, children: [Math.round(percentage), "%"] }))] }));
2734
2918
  };
2919
+ /**
2920
+ * Ring showing determinate progress, for tighter spaces than ProgressBar fits.
2921
+ */
2735
2922
  const ProgressCircle = ({ value, max = 100, size = 'md', variant = 'primary', showLabel = false, className = '', diameter, }) => {
2736
2923
  const percentage = Math.min(Math.max((value / max) * 100, 0), 100);
2737
2924
  const dimension = diameter ?? dimensions[size];
@@ -2744,6 +2931,9 @@ const ProgressCircle = ({ value, max = 100, size = 'md', variant = 'primary', sh
2744
2931
  return (jsxs("div", { className: `${styles$1f.circleContainer} ${className}`, style: { width: dimension, height: dimension }, children: [jsxs("svg", { width: dimension, height: dimension, className: styles$1f.circleSvg, children: [jsx("circle", { className: styles$1f.circleTrack, cx: dimension / 2, cy: dimension / 2, r: radius, strokeWidth: strokeWidth, fill: "transparent" }), jsx("circle", { className: `${styles$1f.circleFill} ${styles$1f[variant]}`, cx: dimension / 2, cy: dimension / 2, r: radius, strokeWidth: strokeWidth, fill: "transparent", strokeDasharray: circumference, strokeDashoffset: strokeDashoffset, strokeLinecap: "round", role: "progressbar", "aria-valuenow": value, "aria-valuemin": 0, "aria-valuemax": max })] }), showLabel && (jsxs("span", { className: styles$1f.circleLabel, style: { fontSize: size === 'xs' ? '8px' : size === 'sm' ? '10px' : size === 'md' ? '12px' : '14px' }, children: [Math.round(percentage), "%"] }))] }));
2745
2932
  };
2746
2933
  // Combined export
2934
+ /**
2935
+ * Namespace bundling the progress indicators as `Progress.Bar` and `Progress.Circle`.
2936
+ */
2747
2937
  const Progress = {
2748
2938
  Bar: ProgressBar,
2749
2939
  Circle: ProgressCircle,
@@ -2775,6 +2965,11 @@ RosterToolbar.displayName = 'RosterToolbar';
2775
2965
 
2776
2966
  var styles$1d = {"searchField":"SearchField-module_searchField__c7gUE","inputWrapper":"SearchField-module_inputWrapper__SUzHv","searchIcon":"SearchField-module_searchIcon__MOZ2m","input":"SearchField-module_input__abJcl","clearButton":"SearchField-module_clearButton__RMIg2","sm":"SearchField-module_sm__-sayT","lg":"SearchField-module_lg__QKacI"};
2777
2967
 
2968
+ /**
2969
+ * Search input with clear and submit affordances.
2970
+ * Works controlled via `value` or uncontrolled via `defaultValue`; `onChange` fires
2971
+ * per keystroke while `onSearch` fires only on submit.
2972
+ */
2778
2973
  const SearchField = forwardRef(({ placeholder = 'Search...', value, defaultValue = '', onChange, onSearch, onClear, size = 'md', disabled = false, showClear = true, showButton = true, className, id, 'aria-label': ariaLabel = 'Search', ...props }, ref) => {
2779
2974
  const [internalValue, setInternalValue] = useState(defaultValue);
2780
2975
  const currentValue = value !== undefined ? value : internalValue;
@@ -2815,6 +3010,10 @@ const SchoolContactForm = ({ mode, createFromPortalUser, firstNameField, middleI
2815
3010
 
2816
3011
  var styles$1b = {"segment":"Segment-module_segment__UF-SG","fullWidth":"Segment-module_fullWidth__Hi19i","option":"Segment-module_option__UJUhj","disabled":"Segment-module_disabled__CSxuQ","selected":"Segment-module_selected__gRnVj","sm":"Segment-module_sm__h8S7Z","md":"Segment-module_md__4uTae","lg":"Segment-module_lg__zL0Hv","icon":"Segment-module_icon__ZHRr6"};
2817
3012
 
3013
+ /**
3014
+ * Segmented control for picking one of a few options that are all worth showing.
3015
+ * Prefer a Dropdown once the list outgrows the available width.
3016
+ */
2818
3017
  const Segment = ({ options, value, onChange, size = 'md', fullWidth = false, className = '', 'aria-label': ariaLabel, }) => {
2819
3018
  return (jsx("div", { className: `${styles$1b.segment} ${styles$1b[size]} ${fullWidth ? styles$1b.fullWidth : ''} ${className}`, role: "radiogroup", "aria-label": ariaLabel, children: options.map((option) => (jsxs("button", { type: "button", className: `${styles$1b.option} ${value === option.value ? styles$1b.selected : ''} ${option.disabled ? styles$1b.disabled : ''}`, onClick: () => !option.disabled && onChange(option.value), disabled: option.disabled, role: "radio", "aria-checked": value === option.value, children: [option.icon && jsx("span", { className: styles$1b.icon, children: option.icon }), option.label] }, option.value))) }));
2820
3019
  };
@@ -2923,6 +3122,9 @@ ServiceToggleList.displayName = 'ServiceToggleList';
2923
3122
 
2924
3123
  var styles$18 = {"toggle":"SupplierTypeToggle-module_toggle__x4pTJ","active":"SupplierTypeToggle-module_active__8BGSe"};
2925
3124
 
3125
+ /**
3126
+ * The selectable supplier types, without the "All" entry the toggle adds.
3127
+ */
2926
3128
  const SUPPLIER_TYPE_OPTIONS = [
2927
3129
  { value: 'A', label: 'Air' },
2928
3130
  { value: 'C', label: 'Car' },
@@ -2932,6 +3134,10 @@ const OPTIONS = [
2932
3134
  { value: '', label: 'All' },
2933
3135
  ...SUPPLIER_TYPE_OPTIONS,
2934
3136
  ];
3137
+ /**
3138
+ * Filters a supplier list down to air, car, or hotel.
3139
+ * Works controlled via `value` or uncontrolled via `defaultValue`.
3140
+ */
2935
3141
  const SupplierTypeToggle = ({ value, defaultValue = '', onChange, disabled = false, className = '', }) => {
2936
3142
  const [internalValue, setInternalValue] = useState(defaultValue);
2937
3143
  const activeValue = value ?? internalValue;
@@ -3399,6 +3605,10 @@ Topbar.displayName = 'Topbar';
3399
3605
 
3400
3606
  var styles$10 = {"appLayout":"AppLayout-module_appLayout__NFbZk","mainArea":"AppLayout-module_mainArea__t-6oq","content":"AppLayout-module_content__5kV7S"};
3401
3607
 
3608
+ /**
3609
+ * Generic page frame: sidebar, top bar, and content area.
3610
+ * Use HubAppShell for Hub screens -- it adds the Hub navigation and branding.
3611
+ */
3402
3612
  const AppLayout = ({ navItems = [], activeNavKey, logo, collapsedLogo, account, defaultExpanded = true, onNavItemClick, onAccountClick, children, navFooter, topbarActions, className, }) => {
3403
3613
  return (jsxs("div", { className: `${styles$10.appLayout} ${className || ''}`, children: [jsx(Sidenav, { items: navItems, activeKey: activeNavKey, defaultExpanded: defaultExpanded, onItemClick: onNavItemClick, logo: logo, logoCollapsed: collapsedLogo, footer: navFooter }), jsxs("div", { className: styles$10.mainArea, children: [jsx(Topbar, { account: account, onAccountClick: onAccountClick, actions: topbarActions }), jsx("main", { className: styles$10.content, children: children })] })] }));
3404
3614
  };
@@ -3421,16 +3631,25 @@ const Card = forwardRef(({ variant = 'default', padding = 'md', interactive = fa
3421
3631
  return (jsx("div", { ref: ref, className: classNames, ...props, children: children }));
3422
3632
  });
3423
3633
  Card.displayName = 'Card';
3634
+ /**
3635
+ * Top of a Card: title, subtitle, and any header actions.
3636
+ */
3424
3637
  const CardHeader = ({ title, subtitle, actions, className = '', children, ...props }) => {
3425
3638
  const classNames = [styles$$.header, className].filter(Boolean).join(' ');
3426
3639
  return (jsxs("div", { className: classNames, ...props, children: [jsxs("div", { className: styles$$.headerContent, children: [title && jsx("h3", { className: styles$$.title, children: title }), subtitle && jsx("p", { className: styles$$.subtitle, children: subtitle }), children] }), actions && jsx("div", { className: styles$$.actions, children: actions })] }));
3427
3640
  };
3428
3641
  CardHeader.displayName = 'CardHeader';
3642
+ /**
3643
+ * Main content region of a Card, carrying the card padding.
3644
+ */
3429
3645
  const CardBody = ({ className = '', children, ...props }) => {
3430
3646
  const classNames = [styles$$.body, className].filter(Boolean).join(' ');
3431
3647
  return (jsx("div", { className: classNames, ...props, children: children }));
3432
3648
  };
3433
3649
  CardBody.displayName = 'CardBody';
3650
+ /**
3651
+ * Bottom of a Card, for actions and summary detail.
3652
+ */
3434
3653
  const CardFooter = ({ className = '', children, ...props }) => {
3435
3654
  const classNames = [styles$$.footer, className].filter(Boolean).join(' ');
3436
3655
  return (jsx("div", { className: classNames, ...props, children: children }));
@@ -3798,9 +4017,22 @@ ContactList.displayName = 'ContactList';
3798
4017
 
3799
4018
  var styles$Y = {"container":"Drawer-module_container__hXuOY","backdrop":"Drawer-module_backdrop__MIipQ","fadeIn":"Drawer-module_fadeIn__QsuvQ","backdropOpen":"Drawer-module_backdropOpen__YpH--","drawer":"Drawer-module_drawer__EHbbs","right":"Drawer-module_right__x6P6Q","drawerOpen":"Drawer-module_drawerOpen__KTgFg","left":"Drawer-module_left__aVF9t","top":"Drawer-module_top__wuvhi","bottom":"Drawer-module_bottom__rt-VZ","sizeSm":"Drawer-module_sizeSm__LFrm3","sizeMd":"Drawer-module_sizeMd__A1QTQ","sizeLg":"Drawer-module_sizeLg__6laD8","sizeXl":"Drawer-module_sizeXl__FY9mZ","sizeFull":"Drawer-module_sizeFull__BGQ1q","header":"Drawer-module_header__MpR14","headerContent":"Drawer-module_headerContent__dFcLH","title":"Drawer-module_title__0AmOX","closeButton":"Drawer-module_closeButton__VuXsE","body":"Drawer-module_body__GLUGo","footer":"Drawer-module_footer__K5ekS"};
3800
4019
 
4020
+ /**
4021
+ * Top of a Drawer: title and the close affordance.
4022
+ */
3801
4023
  const DrawerHeader = ({ title, children, showClose = true, onClose, className = '', }) => (jsxs("div", { className: `${styles$Y.header} ${className}`, children: [jsxs("div", { className: styles$Y.headerContent, children: [title && jsx("h2", { className: styles$Y.title, children: title }), children] }), showClose && (jsx("button", { type: "button", className: styles$Y.closeButton, onClick: onClose, "aria-label": "Close drawer", children: jsx("svg", { width: "20", height: "20", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }))] }));
4024
+ /**
4025
+ * Scrollable content region of a Drawer.
4026
+ */
3802
4027
  const DrawerBody = ({ children, className = '' }) => (jsx("div", { className: `${styles$Y.body} ${className}`, children: children }));
4028
+ /**
4029
+ * Bottom of a Drawer, for actions that stay visible while the body scrolls.
4030
+ */
3803
4031
  const DrawerFooter = ({ children, className = '' }) => (jsx("div", { className: `${styles$Y.footer} ${className}`, children: children }));
4032
+ /**
4033
+ * Panel sliding in from an edge for secondary work that keeps the page behind it
4034
+ * in context. Traps focus while open and closes on Escape or backdrop click.
4035
+ */
3804
4036
  const Drawer = ({ isOpen, onClose, position = 'right', size = 'md', children, closeOnBackdropClick = true, closeOnEscape = true, showBackdrop = true, className = '', }) => {
3805
4037
  const handleEscape = useCallback((e) => {
3806
4038
  if (closeOnEscape && e.key === 'Escape') {
@@ -3843,6 +4075,11 @@ Drawer.Footer = DrawerFooter;
3843
4075
 
3844
4076
  var styles$X = {"drawer":"DueDatesDrawer-module_drawer__TqC4Q","open":"DueDatesDrawer-module_open__Yblf3","body":"DueDatesDrawer-module_body__OwwF-","search":"DueDatesDrawer-module_search__g7-Rl","cards":"DueDatesDrawer-module_cards__bFG90","empty":"DueDatesDrawer-module_empty__K2ULN","card":"DueDatesDrawer-module_card__4Os-5","cardTitle":"DueDatesDrawer-module_cardTitle__fU-cP","status":"DueDatesDrawer-module_status__dXifz","completed":"DueDatesDrawer-module_completed__jWoYi","pastDue":"DueDatesDrawer-module_pastDue__TSikC","tags":"DueDatesDrawer-module_tags__8P87y","completion":"DueDatesDrawer-module_completion__mIrWm","readOnlyStatus":"DueDatesDrawer-module_readOnlyStatus__23fdg"};
3845
4077
 
4078
+ /**
4079
+ * Whether a task counts as done.
4080
+ * Treats a completion timestamp as authoritative, since the source systems do not
4081
+ * always write the status text back.
4082
+ */
3846
4083
  const isTravelDueDateCompleted = (item) => item.status.trim().toLowerCase() === 'completed' || !!item.completeddatetime;
3847
4084
  const isTravelDueDateOverdue = (item) => !item.completeddatetime && !!item.pastdue;
3848
4085
  const parseLocalDate$1 = (value) => {
@@ -3955,9 +4192,15 @@ GridTable.displayName = 'GridTable';
3955
4192
 
3956
4193
  var styles$V = {"shell":"HubAppShell-module_shell__Y13Fo","main":"HubAppShell-module_main__OvnjA","beta":"HubAppShell-module_beta__V8Dzh","sidebar":"HubAppShell-module_sidebar__73gt6","shellExpanded":"HubAppShell-module_shellExpanded__z9cLL","expandButton":"HubAppShell-module_expandButton__hyYi-","logo":"HubAppShell-module_logo__iylLM","defaultLogo":"HubAppShell-module_defaultLogo__tVCz-","desktopNav":"HubAppShell-module_desktopNav__-ZRGD","navItem":"HubAppShell-module_navItem__FKkXN","active":"HubAppShell-module_active__zu1-B","navIcon":"HubAppShell-module_navIcon__QRImh","disabledNav":"HubAppShell-module_disabledNav__zj07n","sidebarFooter":"HubAppShell-module_sidebarFooter__kA2bq","profile":"HubAppShell-module_profile__JCGL5","avatar":"HubAppShell-module_avatar__V-lFG","profileMenu":"HubAppShell-module_profileMenu__qqWSu","topbar":"HubAppShell-module_topbar__U4w0A","defaultAccount":"HubAppShell-module_defaultAccount__3vohz","defaultActions":"HubAppShell-module_defaultActions__wAEJb","mobileActions":"HubAppShell-module_mobileActions__W6Rf9","content":"HubAppShell-module_content__yCgGs","contentDisabled":"HubAppShell-module_contentDisabled__btmCY","mobileNav":"HubAppShell-module_mobileNav__X0zBs","mobileNavItem":"HubAppShell-module_mobileNavItem__1x94j","mobileExtras":"HubAppShell-module_mobileExtras__xo3qI","mobilePrimary":"HubAppShell-module_mobilePrimary__p5PCn","mobileFooter":"HubAppShell-module_mobileFooter__QvsE-","loading":"HubAppShell-module_loading__V8RiG"};
3957
4194
 
4195
+ /**
4196
+ * Individual Travel entry, kept separate because not every account has it.
4197
+ */
3958
4198
  const HUB_INDIVIDUAL_TRAVEL_ITEM = { key: 'individual-travel', label: 'Individual Travel', path: '/individual-travel', icon: jsx(UserIcon, { size: 24 }) };
3959
4199
  /** Canonical navigation identity for the carrier-facing STM Charters portal. */
3960
4200
  const HUB_STM_CHARTERS_ITEM = { key: 'stm-charters', label: 'STM Charters', path: '/stm-charters', icon: jsx(PlaneIcon$3, { size: 24 }) };
4201
+ /**
4202
+ * The default primary navigation, in display order.
4203
+ */
3961
4204
  const HUB_NAV_ITEMS = [
3962
4205
  { key: 'home', label: 'Home', path: '/home', icon: jsx(HomeIcon, { size: 24 }) },
3963
4206
  { key: 'team-travel-calendar', label: 'Team Travel Calendar', path: '/team-travel-calendar', icon: jsx(CalendarIcon$1, { size: 24 }) },
@@ -3965,7 +4208,13 @@ const HUB_NAV_ITEMS = [
3965
4208
  { key: 'team-management', label: 'Team Management', path: '/team-management', icon: jsx(UsersIcon$1, { size: 24 }) },
3966
4209
  HUB_STM_CHARTERS_ITEM,
3967
4210
  ];
4211
+ /**
4212
+ * Administration entry, shown in the footer to admins.
4213
+ */
3968
4214
  const HUB_ADMIN_ITEM = { key: 'administration', label: 'Administration', path: '/administration', icon: jsx(SettingsIcon, { size: 24 }) };
4215
+ /**
4216
+ * Agent workspace entry, shown in the footer to admins.
4217
+ */
3969
4218
  const HUB_AGENTS_ITEM = { key: 'agents', label: 'Agents', path: '/agents', icon: jsx(HeadsetIcon, { size: 24 }) };
3970
4219
  /**
3971
4220
  * Sourcing sits with the admin-gated footer items rather than in `HUB_NAV_ITEMS`: the
@@ -4367,6 +4616,11 @@ const ManifestCapacityStats = ({ paxCount, seats, paxWeight, cargoWeight, payloa
4367
4616
 
4368
4617
  var styles$Q = {"overlay":"Modal-module_overlay__n7Paz","fadeIn":"Modal-module_fadeIn__upWKg","modal":"Modal-module_modal__YZQ-K","slideIn":"Modal-module_slideIn__mtAFr","sm":"Modal-module_sm__QSWSJ","md":"Modal-module_md__6t9J8","lg":"Modal-module_lg__5r3uQ","xl":"Modal-module_xl__JPpjn","full":"Modal-module_full__y9leS","header":"Modal-module_header__xTw6j","title":"Modal-module_title__KvPHy","closeButton":"Modal-module_closeButton__JoRui","body":"Modal-module_body__K3swC","footer":"Modal-module_footer__Z1UcB","footerButtons":"Modal-module_footerButtons__u-Ayv","confirmMessage":"Modal-module_confirmMessage__ZlKV8"};
4369
4618
 
4619
+ /**
4620
+ * Dialog that interrupts the page until the user resolves it.
4621
+ * Traps focus, closes on Escape, and restores focus to the trigger on close.
4622
+ * Use an Alert instead when the user does not have to act.
4623
+ */
4370
4624
  const Modal = ({ isOpen, onClose, title, children, size = 'md', showCloseButton = true, closeOnBackdropClick = true, closeOnEscape = true, footer, className = '', }) => {
4371
4625
  const handleEscape = useCallback((e) => {
4372
4626
  if (closeOnEscape && e.key === 'Escape') {
@@ -4392,7 +4646,14 @@ const Modal = ({ isOpen, onClose, title, children, size = 'md', showCloseButton
4392
4646
  return null;
4393
4647
  return (jsx("div", { className: styles$Q.overlay, onClick: handleBackdropClick, children: jsxs("div", { className: `${styles$Q.modal} ${styles$Q[size]} ${className}`, role: "dialog", "aria-modal": "true", "aria-labelledby": title ? 'modal-title' : undefined, children: [(title || showCloseButton) && (jsxs("div", { className: styles$Q.header, children: [title && (jsx("h2", { id: "modal-title", className: styles$Q.title, children: title })), showCloseButton && (jsx("button", { type: "button", className: styles$Q.closeButton, onClick: onClose, "aria-label": "Close modal", children: jsx("svg", { width: "20", height: "20", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }))] })), jsx("div", { className: styles$Q.body, children: children }), footer && jsx("div", { className: styles$Q.footer, children: footer })] }) }));
4394
4648
  };
4649
+ /**
4650
+ * Action row for the bottom of a Modal, keeping button order consistent across dialogs.
4651
+ */
4395
4652
  const ModalFooter = ({ children, className = '' }) => (jsx("div", { className: `${styles$Q.footerButtons} ${className}`, children: children }));
4653
+ /**
4654
+ * Modal preset for confirming a single action, with the confirm and cancel
4655
+ * buttons already wired.
4656
+ */
4396
4657
  const ConfirmModal = ({ message, confirmText = 'Confirm', cancelText = 'Cancel', onConfirm, onClose, destructive = false, loading = false, ...props }) => (jsx(Modal, { ...props, onClose: onClose, size: "sm", footer: jsxs(ModalFooter, { children: [jsx(Button, { variant: "secondary", onClick: onClose, children: cancelText }), jsx(Button, { variant: destructive ? 'danger' : 'primary', onClick: onConfirm, disabled: loading, children: loading ? 'Loading...' : confirmText })] }), children: jsx("div", { className: styles$Q.confirmMessage, children: message }) }));
4397
4658
 
4398
4659
  var styles$P = {"module":"Module-module_module__1Lglz","header":"Module-module_header__-qP6m","closed":"Module-module_closed__J5FYJ","sm":"Module-module_sm__g-iVI","md":"Module-module_md__zmKQE","lg":"Module-module_lg__5uVgV","titleRow":"Module-module_titleRow__3upJc","icon":"Module-module_icon__MXVy7","iconSpaced":"Module-module_iconSpaced__O8F0i","title":"Module-module_title__Qdyv0","badge":"Module-module_badge__zoNEC","tools":"Module-module_tools__w6Z1b","toggleBtn":"Module-module_toggleBtn__VxH2u","bodyWrapper":"Module-module_bodyWrapper__IogVT","body":"Module-module_body__O9n9B","hidden":"Module-module_hidden__mmUYg","footer":"Module-module_footer__hrUY9","divider":"Module-module_divider__YQKdf","dark":"Module-module_dark__w8Fb-","verticalDivider":"Module-module_verticalDivider__t3g-3"};
@@ -4438,6 +4699,9 @@ const decodeEntities = (value) => {
4438
4699
  element.innerHTML = value;
4439
4700
  return element.value;
4440
4701
  };
4702
+ /**
4703
+ * Stacked notices for the top of a page: platform notices first, then travel alerts.
4704
+ */
4441
4705
  const PageBanners = ({ systemBanners, alertBanners, className = '', }) => {
4442
4706
  const [dismissed, setDismissed] = useState(new Set());
4443
4707
  const previousSystem = useRef(systemBanners);
@@ -4828,6 +5092,9 @@ RegularSeasonBidsTable.displayName = 'RegularSeasonBidsTable';
4828
5092
 
4829
5093
  var styles$H = {"wrapper":"Table-module_wrapper__1klo5","table":"Table-module_table__fkuUB","header":"Table-module_header__vRy6j","headerCell":"Table-module_headerCell__2KzYn","headerContent":"Table-module_headerContent__HPl8-","sortable":"Table-module_sortable__NiS6E","sortIcon":"Table-module_sortIcon__Fy6z7","body":"Table-module_body__Wt52U","row":"Table-module_row__4GEgX","cell":"Table-module_cell__Xr-7H","selectCell":"Table-module_selectCell__lJC7I","selected":"Table-module_selected__a-Gft","striped":"Table-module_striped__6TMF-","hoverable":"Table-module_hoverable__KN5Fp","alignLeft":"Table-module_alignLeft__FtI9E","alignCenter":"Table-module_alignCenter__p8Tr8","alignRight":"Table-module_alignRight__Ca7Yr","bordered":"Table-module_bordered__9bM3L","compact":"Table-module_compact__opzS-","clickable":"Table-module_clickable__2ummV","loading":"Table-module_loading__tFELE","empty":"Table-module_empty__urRiw","pulse":"Table-module_pulse__t32v9"};
4830
5094
 
5095
+ /**
5096
+ * Data table with sorting, selection, and empty and loading states.
5097
+ */
4831
5098
  const Table = ({ columns, data, rowKey = 'id', bordered = false, striped = false, hoverable = true, compact = false, loading = false, emptyMessage = 'No data available', emptyContent, selectable = false, selectedRowKeys, defaultSelectedRowKeys = [], onSelectionChange, isRowSelectable, rowSelectionLabel, selectAllLabel = 'Select all rows', onRowClick, className = '', }) => {
4832
5099
  const [sortColumn, setSortColumn] = useState(null);
4833
5100
  const [sortDirection, setSortDirection] = useState('asc');
@@ -5111,6 +5378,10 @@ const ContactPicker = ({ title, contacts, selectedId, onSelect, allowNone = true
5111
5378
  };
5112
5379
  return (jsxs("div", { className: styles$E.picker, ref: containerRef, "data-test": dataTestId, children: [jsx("div", { className: styles$E.fieldLabel, children: title }), jsxs("div", { className: [styles$E.contactSegment, compact ? styles$E.compactSegment : ''].filter(Boolean).join(' '), children: [!compact && selected && jsx(Avatar, { className: styles$E.contactAvatar, name: selected.name, size: "sm" }), jsxs("div", { className: styles$E.contactDetails, children: [jsxs("button", { type: "button", className: [styles$E.contactTrigger, !selected ? styles$E.emptyContact : '', selected?.active === false ? styles$E.inactiveContact : ''].filter(Boolean).join(' '), disabled: disabled || pending, "data-test": `${dataTestId}-trigger`, "aria-expanded": open, "aria-haspopup": "listbox", "aria-busy": pending, "aria-label": `${title}: ${selected?.name ?? `Select ${title}`}`, onClick: () => setOpen((current) => !current), children: [jsx("span", { children: selected?.name ?? `Select ${title}` }), selected?.active === false && (jsx(Tooltip, { content: "A contact displayed in red is no longer an active portal user and must be replaced by an active user to ensure the accurate delivery of documents and notifications.", position: "right", variant: "error", delay: 0, children: jsx(WarningIcon$1, { size: 16, "aria-label": "Inactive portal user" }) })), pending ? jsx(Spinner, { size: "sm", label: `Updating ${title}` }) : open ? jsx(ChevronUpIcon, { size: 16 }) : jsx(ChevronDownIcon$1, { size: 16 })] }), !compact && selected?.title && jsx("span", { className: styles$E.contactTitle, children: selected.title }), !compact && selected?.phone && jsxs("a", { href: `tel:${selected.phone}`, className: styles$E.contactLink, children: [jsx(PhoneIcon, { size: 20 }), " ", selected.phone] }), !compact && selected?.email && jsxs("a", { href: `mailto:${selected.email}`, className: styles$E.contactLink, children: [jsx(MailIcon, { size: 20 }), " ", selected.email] })] })] }), open && (jsxs("div", { className: [styles$E.contactMenu, compact ? styles$E.compactMenu : ''].filter(Boolean).join(' '), role: "listbox", "aria-label": `${title} options`, children: [jsx("div", { className: styles$E.searchRow, children: jsx(Input, { size: "xs", fullWidth: true, autoFocus: true, value: search, placeholder: "Search contacts", "aria-label": `Search ${title}`, "data-test": `${dataTestId}-search`, leftIcon: jsx(SearchIcon$1, { size: 15 }), onChange: (event) => setSearch(event.target.value) }) }), jsx("div", { className: styles$E.menuDivider }), jsxs("div", { className: styles$E.contactOptions, children: [allowNone && selected && (jsxs("button", { type: "button", className: styles$E.clearContact, "data-test": `${dataTestId}-clear`, disabled: selecting, onClick: () => void select(undefined), children: [jsx("span", { className: styles$E.clearIcon, children: jsx(TrashIcon$2, { size: 16 }) }), jsxs("span", { children: ["Clear ", title] })] })), filteredContacts.map((contact) => (jsxs("button", { type: "button", role: "option", "aria-selected": false, className: styles$E.contactOption, "data-test": `${dataTestId}-option-${contact.id}`, disabled: selecting, onClick: () => void select(contact.id), children: [jsx(Avatar, { className: styles$E.optionAvatar, name: contact.name, size: "sm" }), jsxs("span", { children: [jsx("strong", { children: contact.name }), contact.title && jsx("small", { children: contact.title })] })] }, contact.id))), !filteredContacts.length && jsx("div", { className: styles$E.emptyOptions, children: "No matching contacts" })] })] }))] }));
5113
5380
  };
5381
+ /**
5382
+ * Assigns a team's primary, secondary, and copied travel contacts.
5383
+ * Each slot saves independently, so `loading` is per slot rather than per panel.
5384
+ */
5114
5385
  const TeamContactsPanel = ({ contacts, value, onChange, disabled = false, loading = {}, dataTestId = 'team-contacts', className = '', }) => {
5115
5386
  const ccContactIds = Array.from({ length: 4 }, (_, index) => value.ccContactIds?.[index]);
5116
5387
  const updateCC = (index, id) => {
@@ -12076,6 +12347,10 @@ const NewEventRow = ({ values: initialValues, opponentOptions, onSave, onCancel,
12076
12347
  setValues({ ...values, opponent, eventTitle: values.eventTitle || opponent });
12077
12348
  }, required: true, fullWidth: true }), jsx("datalist", { id: "team-schedule-new-opponents", children: opponentOptions.map((option) => jsx("option", { value: option }, option)) })] }), jsx("div", { className: styles$A.cell, "data-label": "Event Title", children: jsx(Input, { "aria-label": "Event Title", size: "sm", value: values.eventTitle, onChange: (event) => setValues({ ...values, eventTitle: event.target.value }), required: true, fullWidth: true }) }), jsx("div", { className: styles$A.cell, "data-label": "Hm/Awy", children: jsx(Select, { "aria-label": "Home or Away", size: "sm", options: ['Home', 'Away', 'Neutral'].map((option) => ({ label: option, value: option })), value: values.homeOrAway, onChange: (event) => setValues({ ...values, homeOrAway: event.target.value }), fullWidth: true }) }), jsxs("div", { className: `${styles$A.cell} ${styles$A.actionsCell}`, children: [jsx(Button, { type: "submit", variant: "ghost", size: "sm", square: true, "aria-label": "Save new event", children: jsx(CheckIcon$2, { size: 17 }) }), jsx(Button, { type: "button", variant: "ghost", size: "sm", square: true, "aria-label": "Cancel new event", onClick: onCancel, children: jsx(XIcon, { size: 17 }) })] })] }));
12078
12349
  };
12350
+ /**
12351
+ * Inline editor for a team's season: trips, and the events inside each trip.
12352
+ * Works controlled via `rows` or uncontrolled via `initialRows`.
12353
+ */
12079
12354
  const TeamScheduleEditor = ({ rows, initialRows = [], opponentOptions = [], onChange, onSaveTrip, onDeleteTrip, onSaveEvent, onDeleteEvent, onOpenGroupTravelRequest, onSelectedTripsChange, onSelectedEventsChange, className, }) => {
12080
12355
  const [internalRows, setInternalRows] = useState(initialRows);
12081
12356
  const [editState, setEditState] = useState(null);
@@ -12234,6 +12509,10 @@ const sportColor = (sportCode) => {
12234
12509
  return '#D1C1C3';
12235
12510
  return '#8AC6D9';
12236
12511
  };
12512
+ /**
12513
+ * Period controls shared by the calendar views: previous, next, today, and month/week.
12514
+ * Stateless -- the host owns the active date and passes the label back in.
12515
+ */
12237
12516
  const TeamTravelCalendarToolbar = ({ duration, label = '', navigation, onDurationChange, onToday, onPrevious, onNext, className = '', }) => (jsxs("div", { className: [styles$z.toolbar, className].filter(Boolean).join(' '), "data-test": "calendar-toolbar", children: [jsx("button", { type: "button", className: styles$z.todayButton, onClick: onToday, "data-test": "calendar-today", children: "Today" }), jsx(SegmentedSelector, { value: duration, size: "sm", ariaLabel: "Calendar duration", onChange: (value) => onDurationChange?.(value), options: [{ id: 'month', label: 'Month' }, { id: 'week', label: 'Week' }] }), navigation ?? (jsxs("div", { className: styles$z.periodNavigation, children: [jsx("button", { type: "button", className: styles$z.navigationButton, "aria-label": `Previous ${duration}`, onClick: onPrevious, children: jsx(ChevronLeft$1, {}) }), jsx("button", { type: "button", className: styles$z.periodButton, children: label }), jsx("button", { type: "button", className: styles$z.navigationButton, "aria-label": `Next ${duration}`, onClick: onNext, children: jsx(ChevronRight$2, {}) })] }))] }));
12238
12517
  const getVisibleDates = (activeDate, duration) => {
12239
12518
  const active = parseDate(activeDate);
@@ -12249,6 +12528,9 @@ const getVisibleDates = (activeDate, duration) => {
12249
12528
  const length = Math.round((last.getTime() - first.getTime()) / 86400000) + 1;
12250
12529
  return Array.from({ length }, (_, index) => addDays(first, index));
12251
12530
  };
12531
+ /**
12532
+ * Month or week grid plotting a team's trips or task due dates onto calendar days.
12533
+ */
12252
12534
  const TeamTravelCalendarView = ({ activeDate, duration = 'month', type = 'trips', trips = [], dueDates = [], today, toolbar, onTripClick, onDueDateClick, onDueDateToggle, className = '', }) => {
12253
12535
  const active = parseDate(activeDate);
12254
12536
  const visibleDates = getVisibleDates(activeDate, duration);
@@ -12300,6 +12582,10 @@ const DueDateCard = ({ dueDate, onClick, onToggle }) => {
12300
12582
  onToggle?.();
12301
12583
  }, children: completed ? jsx(CheckGlyph, {}) : jsx(CircleGlyph, {}) }), jsxs("div", { className: styles$z.dueCardCopy, children: [jsx("strong", { children: dueDate.tasktitle }), dueDate.description && jsx("span", { children: dueDate.description }), jsxs("small", { children: [dueDate.tripName, dueDate.tripStart ? ` • ${formatShortDate(dueDate.tripStart)}` : ''] })] })] }));
12302
12584
  };
12585
+ /**
12586
+ * The same travel plotted as cards grouped by sport, for scanning across sports
12587
+ * rather than across dates.
12588
+ */
12303
12589
  const TeamTravelCalendarCardView = ({ groups, type = 'trips', toolbar, onTripClick, onDueDateClick, onDueDateToggle, className = '', }) => (jsxs("section", { className: [styles$z.cardView, className].filter(Boolean).join(' '), "aria-label": "Team travel cards", children: [toolbar, jsx("div", { className: styles$z.teamGroups, children: groups.map((group) => {
12304
12590
  const groupDueDates = group.trips.flatMap((trip) => (trip.duedates ?? []).map((dueDate) => ({
12305
12591
  ...dueDate,
@@ -12318,6 +12604,10 @@ const TeamTravelCalendarCardView = ({ groups, type = 'trips', toolbar, onTripCli
12318
12604
  const StatusPill$1 = ({ status }) => status
12319
12605
  ? jsx("span", { className: styles$z.tableStatus, children: status })
12320
12606
  : jsx("span", { className: styles$z.tableRequest, children: "Request" });
12607
+ /**
12608
+ * The same travel as a dense chronological list, for long periods that a grid
12609
+ * cannot show at once.
12610
+ */
12321
12611
  const TeamTravelCalendarListView = ({ trips, type = 'trips', toolbar, periodLabel = '', upcomingTrips = trips.length, onTripClick, onDueDatesClick, onDueDateClick, onDueDateToggle, className = '', }) => {
12322
12612
  const dueDates = trips.flatMap((trip) => (trip.duedates ?? []).map((dueDate) => ({
12323
12613
  ...dueDate,
@@ -12335,6 +12625,10 @@ const TeamTravelCalendarListView = ({ trips, type = 'trips', toolbar, periodLabe
12335
12625
  return (jsxs("tr", { children: [jsx("td", { children: jsx("button", { type: "button", className: styles$z.tableLink, onClick: () => onDueDateClick?.(dueDate), children: dueDate.tasktitle }) }), jsx("td", { children: dueDate.description || '–' }), jsx("td", { children: formatLongDate(dueDate.duedate) }), jsx("td", { children: dueDate.tripName }), jsx("td", { children: formatLongDate(dueDate.tripStart) }), jsx("td", { children: dueDate.servicetype || '–' }), jsx("td", { children: jsx("button", { type: "button", className: [styles$z.listStatusButton, completed ? styles$z.completeStatus : ''].filter(Boolean).join(' '), "aria-label": `${completed ? 'Mark incomplete' : 'Mark complete'}: ${dueDate.tasktitle}`, onClick: () => onDueDateToggle?.(dueDate), children: completed ? jsx(CheckGlyph, {}) : jsx(CircleGlyph, {}) }) })] }, `${dueDate.masterTripGUID}-${dueDate.taskid}`));
12336
12626
  }) })] }) })] }))] }));
12337
12627
  };
12628
+ /**
12629
+ * Formats the heading for a calendar period -- "March 2026" for a month,
12630
+ * a date range for a week.
12631
+ */
12338
12632
  const getTeamTravelCalendarPeriodLabel = (activeDate, duration) => {
12339
12633
  const active = parseDate(activeDate);
12340
12634
  if (duration === 'month')
@@ -12347,6 +12641,10 @@ const getTeamTravelCalendarPeriodLabel = (activeDate, duration) => {
12347
12641
  : last.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
12348
12642
  return `${firstLabel} – ${lastLabel}`;
12349
12643
  };
12644
+ /**
12645
+ * Steps a date forward or back by whole periods, returning the new active date.
12646
+ * Pass a negative `amount` to step backwards.
12647
+ */
12350
12648
  const moveTeamTravelCalendarPeriod = (activeDate, duration, amount) => {
12351
12649
  const active = parseDate(activeDate);
12352
12650
  if (duration === 'week')
@@ -12386,10 +12684,17 @@ const EMPTY_EQUIPMENT = {
12386
12684
  width: '',
12387
12685
  depth: '',
12388
12686
  };
12687
+ /**
12688
+ * Adds or edits a piece of team equipment, including the dimensions and weight
12689
+ * that charter payload planning depends on.
12690
+ */
12389
12691
  const TeamEquipmentForm = ({ mode = 'Add', values, initialValues, onChange, onSubmit, onCancel, showActions = true, disabled = false, className, }) => {
12390
12692
  const [formValues, updateValues] = useManagedValues(values, { ...EMPTY_EQUIPMENT, ...initialValues }, onChange);
12391
12693
  return (jsx(FormShell, { ariaLabel: `${mode} equipment`, className: className, disabled: disabled, onCancel: onCancel, onSubmit: onSubmit, showActions: showActions, values: formValues, children: jsxs("div", { className: styles$y.stack, children: [jsx(Input, { label: "Item Name *", value: formValues.description, onChange: (event) => updateValues({ description: event.target.value }), required: true, disabled: disabled, fullWidth: true }), jsxs("div", { className: styles$y.twoColumnGrid, children: [jsx(Input, { label: "Weight (lbs) *", type: "number", min: "0", step: "0.1", value: formValues.weight, onChange: (event) => updateValues({ weight: numericValue$1(event.target.value) }), required: true, disabled: disabled, fullWidth: true }), jsx(Input, { label: "Quantity *", type: "number", min: "1", step: "1", value: formValues.quantity, onChange: (event) => updateValues({ quantity: numericValue$1(event.target.value) }), required: true, disabled: disabled, fullWidth: true }), jsx(Input, { label: "Height (inches)", type: "number", min: "0", step: "0.1", value: formValues.height, onChange: (event) => updateValues({ height: numericValue$1(event.target.value) }), disabled: disabled, fullWidth: true }), jsx(Input, { label: "Width (inches)", type: "number", min: "0", step: "0.1", value: formValues.width, onChange: (event) => updateValues({ width: numericValue$1(event.target.value) }), disabled: disabled, fullWidth: true }), jsx(Input, { label: "Depth (inches)", type: "number", min: "0", step: "0.1", value: formValues.depth, onChange: (event) => updateValues({ depth: numericValue$1(event.target.value) }), disabled: disabled, fullWidth: true })] })] }) }));
12392
12694
  };
12695
+ /**
12696
+ * Grants an existing portal user access to a team.
12697
+ */
12393
12698
  const TeamUserAccessForm = ({ options, values, initialValues, onChange, onSubmit, onCancel, showActions = true, disabled = false, className, }) => {
12394
12699
  const [formValues, updateValues] = useManagedValues(values, { userId: '', ...initialValues }, onChange);
12395
12700
  return (jsx(FormShell, { ariaLabel: "Add team access", className: className, disabled: disabled, onCancel: onCancel, onSubmit: onSubmit, showActions: showActions, values: formValues, children: jsx(Select, { label: "Available Users *", placeholder: "Select a school contact", options: options, value: formValues.userId, onChange: (event) => updateValues({ userId: event.target.value }), required: true, disabled: disabled, fullWidth: true }) }));
@@ -12411,6 +12716,9 @@ const DEFAULT_SUPPLIERS = {
12411
12716
  { label: 'Marriott', value: 'Marriott' },
12412
12717
  ],
12413
12718
  };
12719
+ /**
12720
+ * Records a loyalty or corporate program number the team travels under.
12721
+ */
12414
12722
  const TeamProgramForm = ({ values, initialValues, onChange, onSubmit, onCancel, showActions = true, disabled = false, className, supplierOptions, }) => {
12415
12723
  const [formValues, updateValues] = useManagedValues(values, { type: 'Air', supplier: '', accountNumber: '', ...initialValues }, onChange);
12416
12724
  const options = { ...DEFAULT_SUPPLIERS, ...supplierOptions };
@@ -12462,6 +12770,10 @@ const SectionBadge = ({ complete, optional, label }) => {
12462
12770
  return jsxs("span", { className: `${styles$y.badge} ${styles$y.incompleteBadge}`, children: [jsx(WarningIcon$1, { size: 12 }), label ?? 'Incomplete'] });
12463
12771
  };
12464
12772
  const SectionCard = ({ icon, title, subtitle, badge, blue = false, children, }) => (jsxs("section", { className: `${styles$y.sectionCard} ${blue ? styles$y.blueSection : ''}`, children: [jsxs("div", { className: styles$y.sectionHeader, children: [jsx("div", { className: styles$y.sectionIcon, children: icon }), jsxs("div", { className: styles$y.sectionHeading, children: [jsx("h3", { children: title }), jsx("p", { children: subtitle })] }), badge] }), children] }));
12773
+ /**
12774
+ * Adds or edits a roster member, including the identity details carriers require
12775
+ * for ticketing. Can prefill from a linked portal user.
12776
+ */
12465
12777
  const TeamRosterMemberForm = ({ mode = 'Add', values, initialValues, onChange, onSubmit, onCancel, showActions = true, disabled = false, className, portalUsers = [], airlineOptions = DEFAULT_AIRLINES, travelerTypeOptions = DEFAULT_TRAVELER_TYPES, }) => {
12466
12778
  const [formValues, updateValues] = useManagedValues(values, { ...EMPTY_ROSTER_MEMBER, ...initialValues }, onChange);
12467
12779
  const minBirthDate = new Date();
@@ -12507,6 +12819,9 @@ const IMPORT_COPY = {
12507
12819
  ],
12508
12820
  },
12509
12821
  };
12822
+ /**
12823
+ * Bulk-loads roster or equipment records from an uploaded file.
12824
+ */
12510
12825
  const TeamImportForm = ({ kind, onSubmit, onCancel, disabled = false, className = '' }) => {
12511
12826
  const [file, setFile] = useState(null);
12512
12827
  const copy = IMPORT_COPY[kind];
@@ -12525,6 +12840,10 @@ const numericValue = (value) => {
12525
12840
  const parsed = Number(String(value).replace(/[^0-9.-]/g, ''));
12526
12841
  return Number.isFinite(parsed) ? parsed : undefined;
12527
12842
  };
12843
+ /**
12844
+ * Formats a trip cost for a metric tile, abbreviating thousands so long figures
12845
+ * do not wrap.
12846
+ */
12528
12847
  const formatCompactTripCost = (value) => {
12529
12848
  const cost = numericValue(value);
12530
12849
  if (cost === undefined)
@@ -12533,6 +12852,9 @@ const formatCompactTripCost = (value) => {
12533
12852
  return `${(cost / 1000000).toFixed(2)}M`;
12534
12853
  return `${Math.floor(cost / 1000)}K`;
12535
12854
  };
12855
+ /**
12856
+ * Season-at-a-glance tiles: trips taken, trips remaining, spend, and overdue tasks.
12857
+ */
12536
12858
  const TravelSummaryMetrics = ({ tripsTraveled, remainingTrips, seasonTripCosts, seasonLabel, overdueActions, onOverdueActionsClick, className = '', }) => {
12537
12859
  const overdueCount = numericValue(overdueActions) ?? 0;
12538
12860
  return (jsxs("div", { className: [styles$x.grid, className].filter(Boolean).join(' '), children: [jsx(Metric, { value: tripsTraveled, label: "Trips Travelled" }), jsx(Metric, { value: remainingTrips, label: "Remaining Trips" }), jsx(Metric, { value: formatCompactTripCost(seasonTripCosts), label: `${seasonLabel} Trip Costs`, prefix: "$" }), jsx(Metric, { value: overdueActions, label: "Overdue Actions", warning: overdueCount > 0, dark: true, onClick: onOverdueActionsClick })] }));
@@ -12587,6 +12909,9 @@ const resolveStatus = (status) => {
12587
12909
  return { variant: 'itinerary', label: status.trim() || 'Unknown' };
12588
12910
  };
12589
12911
  const getLegacyIdentifier = (trip, requested) => (requested ? trip.reqId || trip.id : trip.pnr || trip.id);
12912
+ /**
12913
+ * Trip listing with the segment, status, and date columns the travel screens share.
12914
+ */
12590
12915
  const TripTable = ({ trips, variant = 'current', density = 'comfortable', vendorMode = 'icon', useServiceColumn = false, onSelectTrip, onViewTrip, emptyMessage, className, }) => {
12591
12916
  const isRequested = variant === 'requested';
12592
12917
  const isCompact = density === 'compact';
@@ -13871,6 +14196,11 @@ const RequestSummary = ({ values, enabled }) => {
13871
14196
  const populated = sections.filter((section) => section.rows.some(([, value]) => Boolean(value)));
13872
14197
  return jsxs("div", { className: styles$h.summaryCard, children: [jsx("h2", { children: "Request Summary" }), populated.map((section) => jsxs("section", { className: styles$h.summarySection, children: [jsx("h3", { children: section.title }), section.rows.map(([label, value]) => jsx(SummaryRow, { label: label, value: value }, label))] }, section.key)), !populated.length && jsx("p", { className: styles$h.emptySummary, children: "No services selected" })] });
13873
14198
  };
14199
+ /**
14200
+ * Multi-step wizard for requesting travel for a whole team.
14201
+ * Collects the trip, the services needed, and the traveling party, and hands the
14202
+ * completed request to the host through `onSubmit`.
14203
+ */
13874
14204
  const GroupTravelRequestPage = ({ eventName = 'New Trip', totalPages = 1, onSubmit, onCancel, onChange, initialValues = {}, contacts = [], cateringOptions = DEFAULT_CATERING, schoolAddress = '', services, summarySections, className = '', }) => {
13875
14205
  const resolvedServices = services ?? DEFAULT_SERVICES;
13876
14206
  const [currentPage, setCurrentPage] = useState(1);
@@ -14044,6 +14374,10 @@ const matchesLegacyFilters = (trip, filters) => {
14044
14374
  return false;
14045
14375
  return true;
14046
14376
  };
14377
+ /**
14378
+ * Landing page for a single traveler's own requests, listing what they have raised
14379
+ * and offering a new one.
14380
+ */
14047
14381
  const IndividualTravelPage = ({ trips, tabCounts, enabledTabs = ['current', 'past'], activeTab, onTabChange, filters, defaultFilters, onFiltersChange, pagination, onPageChange, loading = false, refreshing = false, error, onRetry, exporting = false, onExportFiltered, onSelectTrip, onSelectRequest, onCreateRequest, emptyMessage, searchLabel = 'Search trips', searchPlaceholder = 'Search traveler, PNR, route…', currentTrips = [], pastTrips = [], requestedTrips, sportOptions = defaultSportOptions, travelerOptions, initialTab = 'current', density = 'comfortable', vendorMode = 'icon', useServiceColumn = false, onViewTrip, onViewRequest, onExport, showTravelerScopeInfo = false, className, }) => {
14048
14382
  const serverMode = trips !== undefined;
14049
14383
  const hasRequestedSource = serverMode || requestedTrips !== undefined;
@@ -14723,6 +15057,12 @@ const QuestionField = ({ question, value, onChange, disabled, dataTest, error, }
14723
15057
  return (jsx(Field, { label: question.label, value: typeof value === 'string' ? value : '', onChange: onChange, options: question.type === 'single-select' ? question.options ?? [] : undefined, required: question.required, disabled: disabled, help: question.description, error: error, maxLength: question.maxLength, dataTest: dataTest }));
14724
15058
  };
14725
15059
  const Section = ({ title, description, children, action, dataTest, }) => (jsxs("section", { className: styles$f.section, "data-test": dataTest, children: [jsxs("div", { className: styles$f.sectionHeader, children: [jsxs("div", { children: [jsx("h2", { className: styles$f.sectionTitle, children: title }), description && jsx("p", { className: styles$f.sectionDescription, children: description })] }), action] }), children] }));
15060
+ /**
15061
+ * Form for one traveler to request their own travel.
15062
+ * The questions asked come from the account configuration, so the host supplies
15063
+ * them rather than the page assuming a fixed set.
15064
+ * Renders read-only when no `onSubmit` is given.
15065
+ */
14726
15066
  const IndividualTravelRequestFormPage = ({ currentTraveler, configuration, values, initialValues, onChange, onSubmit, onCancel, loading = false, submitting = false, error, fieldErrors, onRetry, notice, submitLabel = 'Submit travel request', className, }) => {
14727
15067
  const [internalValues, setInternalValues] = useState(() => (createInitialValues(configuration, initialValues)));
14728
15068
  const [internalSubmitting, setInternalSubmitting] = useState(false);
@@ -15189,6 +15529,10 @@ OffFleetSubmissionDetailPage.displayName = 'OffFleetSubmissionDetailPage';
15189
15529
 
15190
15530
  var styles$8 = {"page":"RosterProgramNumbersPage-module_page__eNDG8","header":"RosterProgramNumbersPage-module_header__1YtCr","heading":"RosterProgramNumbersPage-module_heading__6tBIK","tableWrap":"RosterProgramNumbersPage-module_tableWrap__l0smr","table":"RosterProgramNumbersPage-module_table__6UGEu","traveler":"RosterProgramNumbersPage-module_traveler__7ZbBS","supplier":"RosterProgramNumbersPage-module_supplier__EEXuR","inputCell":"RosterProgramNumbersPage-module_inputCell__OaiTN","empty":"RosterProgramNumbersPage-module_empty__iFweL","footer":"RosterProgramNumbersPage-module_footer__p4K-j"};
15191
15531
 
15532
+ /**
15533
+ * Edits the loyalty and corporate program numbers a team travels under, saving them
15534
+ * as one set.
15535
+ */
15192
15536
  const RosterProgramNumbersPage = ({ programs, loading = false, saving = false, onBack, onSubmit, }) => {
15193
15537
  const initialValues = useMemo(() => Object.fromEntries(programs.map((program) => [String(program.id), program.accountNumber])), [programs]);
15194
15538
  const [values, setValues] = useState(initialValues);
@@ -15239,6 +15583,9 @@ RegularSeasonBidsPage.displayName = 'RegularSeasonBidsPage';
15239
15583
 
15240
15584
  var styles$6 = {"page":"RosterReportsPage-module_page__ZU5hz","header":"RosterReportsPage-module_header__FFSTB","heading":"RosterReportsPage-module_heading__rIwdQ","list":"RosterReportsPage-module_list__9K8tF","report":"RosterReportsPage-module_report__iXROM","details":"RosterReportsPage-module_details__WZ6cT","empty":"RosterReportsPage-module_empty__Nrynv"};
15241
15585
 
15586
+ /**
15587
+ * Lists the roster reports a user can run, and reports progress while one runs.
15588
+ */
15242
15589
  const RosterReportsPage = ({ reports, runningReportId, onBack, onRun }) => (jsxs("section", { className: styles$6.page, "data-test": "roster-reports-page", children: [jsxs("header", { className: styles$6.header, children: [jsxs("div", { className: styles$6.heading, children: [jsx("h1", { children: "Roster Reports" }), jsx("p", { children: "Run the available reports for the selected team roster." })] }), jsx(Button, { type: "button", variant: "outline", size: "sm", "data-test": "roster-reports-back", onClick: onBack, children: "Back to Roster" })] }), jsx(Module, { title: "Team Reports", useToggle: false, children: reports.length ? (jsx("div", { className: styles$6.list, "data-test": "roster-report-list", children: reports.map((report) => (jsxs("article", { className: styles$6.report, "data-test": `roster-report-${report.id}`, children: [jsxs("div", { className: styles$6.details, children: [jsx("h2", { children: report.title }), report.description && jsx("p", { children: report.description })] }), jsx(Button, { type: "button", variant: "outline", size: "sm", loading: runningReportId === report.id, disabled: Boolean(runningReportId) && runningReportId !== report.id, "data-test": `run-roster-report-${report.id}`, onClick: () => void onRun?.(report), children: "Run Report" })] }, report.id))) })) : (jsx("div", { className: styles$6.empty, children: "No roster reports are currently available." })) })] }));
15243
15590
  RosterReportsPage.displayName = 'RosterReportsPage';
15244
15591