astro-tractstack 2.0.0 → 2.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-tractstack",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Astro integration for TractStack - redeeming the web from boring experiences",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -4,12 +4,14 @@ import { createListCollection } from '@ark-ui/react/collection';
4
4
  import { Select } from '@ark-ui/react/select';
5
5
  import { RadioGroup } from '@ark-ui/react/radio-group';
6
6
  import { Portal } from '@ark-ui/react/portal';
7
+ import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
7
8
  import CheckCircleIcon from '@heroicons/react/24/outline/CheckCircleIcon';
8
9
  import ChevronLeftIcon from '@heroicons/react/24/outline/ChevronLeftIcon';
9
10
  import ChevronRightIcon from '@heroicons/react/24/outline/ChevronRightIcon';
10
11
  import { epinetCustomFilters } from '@/stores/analytics';
11
12
  import EpinetTableView from './EpinetTableView';
12
13
  import { MAX_ANALYTICS_HOURS } from '@/constants';
14
+ import type { AppliedFilter } from '@/stores/analytics';
13
15
 
14
16
  const getHourOptions = () => {
15
17
  const hours = Array.from({ length: 24 }, (_, i) => ({
@@ -25,7 +27,7 @@ const getHourOptions = () => {
25
27
  const getEndHourOptions = () => {
26
28
  const hours = Array.from({ length: 24 }, (_, i) => ({
27
29
  value: i.toString().padStart(2, '0'),
28
- label: `${i.toString().padStart(2, '0')}:59`, // Always show :59 for end hours
30
+ label: `${i.toString().padStart(2, '0')}:59`,
29
31
  sortOrder: i,
30
32
  }));
31
33
  hours.push({ value: '24', label: '23:59', sortOrder: 24 });
@@ -42,17 +44,28 @@ interface ContentMapItem {
42
44
  type: string;
43
45
  }
44
46
 
47
+ interface AvailableFilter {
48
+ beliefSlug: string;
49
+ values: string[];
50
+ }
51
+
45
52
  interface EpinetDurationSelectorProps {
46
53
  fullContentMap?: ContentMapItem[];
47
54
  isLoading?: boolean;
48
55
  hourlyNodeActivity?: any;
56
+ availableFilters?: AvailableFilter[];
57
+ appliedFilters?: AppliedFilter[];
58
+ onBeliefFilterChange: (beliefSlug: string, value: string) => void;
49
59
  }
50
60
 
51
61
  const EpinetDurationSelector = ({
52
62
  fullContentMap,
53
63
  isLoading,
54
64
  hourlyNodeActivity,
55
- }: EpinetDurationSelectorProps = {}) => {
65
+ availableFilters = [],
66
+ appliedFilters = [],
67
+ onBeliefFilterChange,
68
+ }: EpinetDurationSelectorProps) => {
56
69
  const [startDate, setStartDate] = useState<Date | null>(null);
57
70
  const [endDate, setEndDate] = useState<Date | null>(null);
58
71
  const [isDatePickerOpen, setIsDatePickerOpen] = useState(false);
@@ -78,7 +91,7 @@ const EpinetDurationSelector = ({
78
91
  const now = new Date();
79
92
  const endDate = new Date(now);
80
93
  let startDate = new Date(endDate);
81
- startDate.setDate(startDate.getDate() - 7); // Default to one week
94
+ startDate.setDate(startDate.getDate() - 7);
82
95
 
83
96
  setStartDate(startDate);
84
97
  setEndDate(endDate);
@@ -90,13 +103,11 @@ const EpinetDurationSelector = ({
90
103
  }));
91
104
  }, []);
92
105
 
93
- // MODIFIED: Enhanced sync from store
94
106
  useEffect(() => {
95
107
  if ($epinetCustomFilters.startTimeUTC && $epinetCustomFilters.endTimeUTC) {
96
108
  const startUTC = new Date($epinetCustomFilters.startTimeUTC);
97
109
  const endUTC = new Date($epinetCustomFilters.endTimeUTC);
98
110
 
99
- // Update local dates and hours to match store
100
111
  setStartDate(
101
112
  new Date(
102
113
  startUTC.getFullYear(),
@@ -124,20 +135,24 @@ const EpinetDurationSelector = ({
124
135
  selectedUserId: $epinetCustomFilters.selectedUserId,
125
136
  }));
126
137
  }
127
- }, [$epinetCustomFilters]);
138
+ }, [
139
+ $epinetCustomFilters.startTimeUTC,
140
+ $epinetCustomFilters.endTimeUTC,
141
+ $epinetCustomFilters.visitorType,
142
+ $epinetCustomFilters.selectedUserId,
143
+ ]);
128
144
 
129
145
  useEffect(() => {
130
146
  setCurrentUserPage(0);
131
147
  }, [localFilters.visitorType]);
132
148
 
133
- // Handle UnsavedChangesBar-style visibility animations
134
149
  useEffect(() => {
135
150
  if (hasLocalChanges && !isVisible) {
136
151
  setIsVisible(true);
137
- setTimeout(() => setIsAnimating(true), 10); // Trigger entrance animation
152
+ setTimeout(() => setIsAnimating(true), 10);
138
153
  } else if (!hasLocalChanges && isVisible && !isApplying) {
139
154
  setIsAnimating(false);
140
- setTimeout(() => setIsVisible(false), 300); // Brief delay before hiding
155
+ setTimeout(() => setIsVisible(false), 300);
141
156
  }
142
157
  }, [hasLocalChanges, isVisible, isApplying]);
143
158
 
@@ -167,14 +182,11 @@ const EpinetDurationSelector = ({
167
182
  setErrorMessage(null);
168
183
  };
169
184
 
170
- // Create UTC datetime from local date and hour selection
171
185
  const createUTCDateTime = (date: Date, hourStr: string): Date => {
172
186
  const localDateTime = new Date(date);
173
187
  const hour = hourStr === '24' ? 23 : parseInt(hourStr);
174
188
  const minute = hourStr === '24' ? 59 : 0;
175
189
  localDateTime.setHours(hour, minute, 0, 0);
176
-
177
- // Convert to UTC by creating new Date with UTC values
178
190
  return new Date(localDateTime.getTime());
179
191
  };
180
192
 
@@ -183,26 +195,19 @@ const EpinetDurationSelector = ({
183
195
  setErrorMessage('Please select both start and end dates.');
184
196
  return;
185
197
  }
186
-
187
198
  const startUTCTime = createUTCDateTime(startDate, localFilters.startHour);
188
199
  const endUTCTime = createUTCDateTime(endDate, localFilters.endHour);
189
-
190
200
  if (endUTCTime < startUTCTime) {
191
201
  setErrorMessage('End time must be after start time.');
192
202
  return;
193
203
  }
194
-
195
204
  const nowUTC = new Date();
196
-
197
205
  if (endUTCTime > nowUTC) {
198
206
  setErrorMessage('End time cannot be in the future.');
199
207
  return;
200
208
  }
201
-
202
209
  setIsApplying(true);
203
-
204
210
  try {
205
- // Update with UTC timestamps
206
211
  epinetCustomFilters.set(window.TRACTSTACK_CONFIG?.tenantId || 'default', {
207
212
  ...$epinetCustomFilters,
208
213
  visitorType: localFilters.visitorType,
@@ -210,11 +215,8 @@ const EpinetDurationSelector = ({
210
215
  startTimeUTC: startUTCTime.toISOString(),
211
216
  endTimeUTC: endUTCTime.toISOString(),
212
217
  });
213
-
214
218
  setHasLocalChanges(false);
215
219
  setErrorMessage(null);
216
-
217
- // Brief delay to show success state
218
220
  setTimeout(() => {
219
221
  setIsApplying(false);
220
222
  }, 1000);
@@ -232,31 +234,26 @@ const EpinetDurationSelector = ({
232
234
  setErrorMessage('Please select a valid date.');
233
235
  return;
234
236
  }
235
-
236
237
  const newDate = new Date(
237
238
  parseInt(dateValue.split('-')[0]),
238
239
  parseInt(dateValue.split('-')[1]) - 1,
239
240
  parseInt(dateValue.split('-')[2])
240
241
  );
241
-
242
242
  const nowUTC = new Date();
243
243
  const maxPastTime = new Date(
244
244
  nowUTC.getTime() - MAX_ANALYTICS_HOURS * 60 * 60 * 1000
245
245
  );
246
-
247
246
  if (newDate < maxPastTime) {
248
247
  setErrorMessage(
249
248
  `Date cannot be more than ${MAX_ANALYTICS_HOURS} hours in the past.`
250
249
  );
251
250
  return;
252
251
  }
253
-
254
252
  if (type === 'start') {
255
253
  setStartDate(newDate);
256
254
  } else {
257
255
  setEndDate(newDate);
258
256
  }
259
-
260
257
  setHasLocalChanges(true);
261
258
  setErrorMessage(null);
262
259
  };
@@ -276,44 +273,23 @@ const EpinetDurationSelector = ({
276
273
  isEndTime = false
277
274
  ) => {
278
275
  if (!date) return '';
279
-
280
276
  const localDateTime = new Date(date);
281
277
  const hour = hourStr === '24' ? 23 : parseInt(hourStr);
282
- // For end times, always show :59 to represent the full hour
283
278
  const minute = isEndTime ? 59 : hourStr === '24' ? 59 : 0;
284
279
  localDateTime.setHours(hour, minute, 0, 0);
285
-
286
280
  const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
287
-
288
- return (
289
- localDateTime.toLocaleString(undefined, {
290
- year: 'numeric',
291
- month: '2-digit',
292
- day: '2-digit',
293
- hour: '2-digit',
294
- minute: '2-digit',
295
- hour12: true,
296
- timeZone,
297
- }) + ` (${timeZone})`
298
- );
281
+ return `${localDateTime.toLocaleString(undefined, { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: true, timeZone })} (${timeZone})`;
299
282
  };
300
283
 
301
- // Display current UTC range in local timezone with proper end time formatting
302
284
  const formatCurrentUTCRange = () => {
303
285
  const { startTimeUTC, endTimeUTC } = $epinetCustomFilters;
304
286
  if (!startTimeUTC || !endTimeUTC) return '';
305
-
306
287
  const startLocal = new Date(startTimeUTC);
307
288
  const endLocal = new Date(endTimeUTC);
308
289
  const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
309
-
310
290
  const formatTime = (date: Date, isEndTime = false) => {
311
- // For end times, always show :59 for presentation
312
291
  const displayDate = isEndTime ? new Date(date.getTime()) : date;
313
- if (isEndTime) {
314
- displayDate.setMinutes(59);
315
- }
316
-
292
+ if (isEndTime) displayDate.setMinutes(59);
317
293
  return displayDate.toLocaleString(undefined, {
318
294
  year: 'numeric',
319
295
  month: '2-digit',
@@ -324,7 +300,6 @@ const EpinetDurationSelector = ({
324
300
  timeZone,
325
301
  });
326
302
  };
327
-
328
303
  return `${formatTime(startLocal, false)} to ${formatTime(endLocal, true)} (${timeZone})`;
329
304
  };
330
305
 
@@ -332,20 +307,16 @@ const EpinetDurationSelector = ({
332
307
  currentUserPage * usersPerPage,
333
308
  (currentUserPage + 1) * usersPerPage
334
309
  );
335
-
336
310
  const totalUserPages = Math.ceil(
337
311
  (($epinetCustomFilters.userCounts || []).length || 0) / usersPerPage
338
312
  );
339
-
340
313
  const nextUserPage = () => {
341
314
  if (currentUserPage < totalUserPages - 1)
342
315
  setCurrentUserPage(currentUserPage + 1);
343
316
  };
344
-
345
317
  const prevUserPage = () => {
346
318
  if (currentUserPage > 0) setCurrentUserPage(currentUserPage - 1);
347
319
  };
348
-
349
320
  const maxDate = new Date();
350
321
  const minDate = new Date();
351
322
  minDate.setHours(minDate.getHours() - MAX_ANALYTICS_HOURS);
@@ -353,25 +324,15 @@ const EpinetDurationSelector = ({
353
324
  const setPresetDateRange = (period: string) => {
354
325
  const nowUTC = new Date();
355
326
  let hoursBack: number;
356
-
357
- if (period === '24h') {
358
- hoursBack = 24;
359
- } else if (period === '7d') {
360
- hoursBack = 168;
361
- } else if (period === '28d') {
362
- hoursBack = 672;
363
- } else {
364
- return;
365
- }
366
-
327
+ if (period === '24h') hoursBack = 24;
328
+ else if (period === '7d') hoursBack = 168;
329
+ else if (period === '28d') hoursBack = 672;
330
+ else return;
367
331
  const startTimeUTC = new Date(
368
332
  nowUTC.getTime() - hoursBack * 60 * 60 * 1000
369
333
  );
370
-
371
- // Convert to local dates and set local state
372
334
  const startLocal = new Date(startTimeUTC);
373
335
  const endLocal = new Date(nowUTC);
374
-
375
336
  setStartDate(
376
337
  new Date(
377
338
  startLocal.getFullYear(),
@@ -382,13 +343,11 @@ const EpinetDurationSelector = ({
382
343
  setEndDate(
383
344
  new Date(endLocal.getFullYear(), endLocal.getMonth(), endLocal.getDate())
384
345
  );
385
-
386
346
  setLocalFilters((prev) => ({
387
347
  ...prev,
388
348
  startHour: startLocal.getHours().toString().padStart(2, '0'),
389
349
  endHour: endLocal.getHours().toString().padStart(2, '0'),
390
350
  }));
391
-
392
351
  setHasLocalChanges(true);
393
352
  setIsDatePickerOpen(false);
394
353
  setErrorMessage(null);
@@ -402,11 +361,9 @@ const EpinetDurationSelector = ({
402
361
  startHour: now.getHours().toString().padStart(2, '0'),
403
362
  endHour: now.getHours().toString().padStart(2, '0'),
404
363
  });
405
-
406
364
  const endDate = new Date(now);
407
365
  let startDate = new Date(endDate);
408
366
  startDate.setDate(startDate.getDate() - 7);
409
-
410
367
  setStartDate(startDate);
411
368
  setEndDate(endDate);
412
369
  setIsDatePickerOpen(false);
@@ -428,7 +385,6 @@ const EpinetDurationSelector = ({
428
385
  const getFilterStatusMessage = () => {
429
386
  const needsApply = hasLocalChanges;
430
387
  const prefix = needsApply ? 'Press Apply Filters to load' : 'Showing';
431
-
432
388
  let baseMessage: string;
433
389
  if (needsApply && startDate && endDate) {
434
390
  baseMessage = `${prefix} from ${formatDateHourDisplay(startDate, localFilters.startHour, false)} to ${formatDateHourDisplay(endDate, localFilters.endHour, true)}`;
@@ -441,31 +397,16 @@ const EpinetDurationSelector = ({
441
397
  } else {
442
398
  baseMessage = `${prefix} from last 7 days`;
443
399
  }
444
-
445
400
  const userInfo = needsApply
446
401
  ? localFilters.selectedUserId
447
402
  ? ` for individual user ${localFilters.selectedUserId}`
448
- : ` for ${
449
- localFilters.visitorType === 'all'
450
- ? 'all visitors'
451
- : localFilters.visitorType === 'anonymous'
452
- ? 'anonymous visitors'
453
- : 'known leads'
454
- }`
403
+ : ` for ${localFilters.visitorType === 'all' ? 'all visitors' : localFilters.visitorType === 'anonymous' ? 'anonymous visitors' : 'known leads'}`
455
404
  : $epinetCustomFilters.selectedUserId
456
405
  ? ` for individual user ${$epinetCustomFilters.selectedUserId}`
457
- : ` for ${
458
- $epinetCustomFilters.visitorType === 'all'
459
- ? 'all visitors'
460
- : $epinetCustomFilters.visitorType === 'anonymous'
461
- ? 'anonymous visitors'
462
- : 'known leads'
463
- }`;
464
-
406
+ : ` for ${$epinetCustomFilters.visitorType === 'all' ? 'all visitors' : $epinetCustomFilters.visitorType === 'anonymous' ? 'anonymous visitors' : 'known leads'}`;
465
407
  return baseMessage + userInfo;
466
408
  };
467
409
 
468
- // Get bar styling based on current state
469
410
  const getBarStyling = () => {
470
411
  if (isApplying) {
471
412
  return {
@@ -527,16 +468,10 @@ const EpinetDurationSelector = ({
527
468
  };
528
469
 
529
470
  const styling = getBarStyling();
530
-
531
- // Get message based on current state
532
471
  const getMessage = () => {
533
- if (isApplying) {
534
- return 'Applying filters...';
535
- } else if (errorMessage) {
536
- return errorMessage;
537
- } else {
538
- return getFilterStatusMessage();
539
- }
472
+ if (isApplying) return 'Applying filters...';
473
+ if (errorMessage) return errorMessage;
474
+ return getFilterStatusMessage();
540
475
  };
541
476
 
542
477
  return (
@@ -546,22 +481,69 @@ const EpinetDurationSelector = ({
546
481
  <div
547
482
  className={`space-y-4 rounded-lg border-2 border-dashed border-gray-200 bg-gray-50 p-4`}
548
483
  >
484
+ <div className="space-y-4">
485
+ {availableFilters.map((filter) => {
486
+ // Determine the currently selected value for this specific filter slug. Default to 'All'.
487
+ const selectedValue =
488
+ appliedFilters.find((f) => f.beliefSlug === filter.beliefSlug)
489
+ ?.value || 'All';
490
+
491
+ const customSortedValues = [...filter.values].sort((a, b) => {
492
+ if (a === 'Anonymous Traffic') return -1; // Always move 'Anonymous Traffic' to the front
493
+ if (b === 'Anonymous Traffic') return 1;
494
+ return a.localeCompare(b); // Sort the rest alphabetically
495
+ });
496
+
497
+ // Prepend 'All' to the custom-sorted list
498
+ const pillValues = ['All', ...customSortedValues];
499
+
500
+ return (
501
+ <div key={filter.beliefSlug}>
502
+ <label className="block text-sm font-bold capitalize text-gray-700">
503
+ {/* Makes "beliefSlug" -> "Belief Slug" */}
504
+ {filter.beliefSlug.replace(/([A-Z])/g, ' $1').trim()}
505
+ </label>
506
+ <div className="mt-2 flex flex-wrap gap-2">
507
+ {/* Map over the newly ordered array of pill values */}
508
+ {pillValues.map((value) => (
509
+ <button
510
+ key={value}
511
+ onClick={() =>
512
+ onBeliefFilterChange(filter.beliefSlug, value)
513
+ }
514
+ type="button"
515
+ className={`flex items-center gap-x-1.5 rounded-full px-3 py-1 text-sm font-medium transition-all duration-150 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2 ${
516
+ selectedValue === value
517
+ ? 'bg-cyan-600 text-white shadow-sm'
518
+ : 'bg-gray-100 text-gray-700 hover:bg-gray-200'
519
+ } `}
520
+ >
521
+ {value}
522
+ {/* Conditionally render the 'X' icon only on the selected pill, if it's not 'All' */}
523
+ {selectedValue === value && value !== 'All' && (
524
+ <XMarkIcon
525
+ className="h-4 w-4 cursor-pointer stroke-2 text-white/70 hover:text-white"
526
+ onClick={(e) => {
527
+ // Stop the event from bubbling up to the parent button's onClick
528
+ e.stopPropagation();
529
+ // Reset the filter to 'All'
530
+ onBeliefFilterChange(filter.beliefSlug, 'All');
531
+ }}
532
+ />
533
+ )}
534
+ </button>
535
+ ))}
536
+ </div>
537
+ </div>
538
+ );
539
+ })}
540
+ </div>
549
541
  <div
550
542
  className="flex flex-col space-y-4 md:flex-row md:gap-4 md:space-y-0"
551
543
  style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}
552
544
  >
553
- <style>{`
554
- @media (min-width: 768px) {
555
- .duration-selector-container {
556
- display: grid;
557
- grid-template-columns: 200px 1fr;
558
- gap: 1rem;
559
- }
560
- }
561
- `}</style>
562
-
545
+ <style>{`@media (min-width: 768px) {.duration-selector-container {display: grid; grid-template-columns: 200px 1fr; gap: 1rem;}}`}</style>
563
546
  <div className="duration-selector-container flex flex-col space-y-4 md:space-y-0">
564
- {/* Left column: Narrow visitor types stacked */}
565
547
  <div className="space-y-2">
566
548
  <style>{radioGroupStyles}</style>
567
549
  <RadioGroup.Root
@@ -607,32 +589,9 @@ const EpinetDurationSelector = ({
607
589
  </div>
608
590
  </RadioGroup.Root>
609
591
  </div>
610
-
611
- {/* Right column: Date/time controls and user select in 2 rows */}
612
592
  <div className="space-y-4">
613
- {/* Row 1: Date Range | Start Hour | End Hour */}
614
593
  <div className="date-time-row grid grid-cols-1 gap-4">
615
- <style>{`
616
- .date-time-row {
617
- display: grid;
618
- grid-template-columns: 1fr auto auto;
619
- gap: 1rem;
620
- }
621
-
622
- @media (max-width: 767px) {
623
- .date-time-grid {
624
- grid-template-columns: 1fr !important;
625
- }
626
- .date-time-row {
627
- grid-template-columns: 1fr auto;
628
- grid-template-rows: auto auto;
629
- }
630
- .date-time-row > div:nth-child(3) {
631
- grid-column: 2;
632
- grid-row: 2;
633
- }
634
- }
635
- `}</style>
594
+ <style>{`.date-time-row {display: grid; grid-template-columns: 1fr auto auto; gap: 1rem;} @media (max-width: 767px) {.date-time-grid {grid-template-columns: 1fr !important;} .date-time-row {grid-template-columns: 1fr auto; grid-template-rows: auto auto;} .date-time-row > div:nth-child(3) {grid-column: 2; grid-row: 2;}}`}</style>
636
595
  <div className="date-time-grid space-y-1">
637
596
  <div className="block text-sm font-bold text-gray-700">
638
597
  Date Range (Local Time)
@@ -647,7 +606,6 @@ const EpinetDurationSelector = ({
647
606
  ? `${formatDateDisplay(startDate)} - ${formatDateDisplay(endDate)}`
648
607
  : 'Select date range'}
649
608
  </button>
650
-
651
609
  {isDatePickerOpen && (
652
610
  <div className="absolute z-10 mt-1 w-full rounded-md bg-white p-2 shadow-lg sm:w-auto">
653
611
  <div className="mb-2 flex flex-wrap justify-between gap-2">
@@ -676,7 +634,6 @@ const EpinetDurationSelector = ({
676
634
  Close
677
635
  </button>
678
636
  </div>
679
-
680
637
  <div className="mb-2">
681
638
  <p className="text-sm font-bold">
682
639
  Start date: {formatDateDisplay(startDate)}
@@ -685,7 +642,6 @@ const EpinetDurationSelector = ({
685
642
  End date: {formatDateDisplay(endDate)}
686
643
  </p>
687
644
  </div>
688
-
689
645
  <div className="flex flex-col gap-4 sm:flex-row">
690
646
  <div className="flex-1">
691
647
  <label
@@ -742,7 +698,6 @@ const EpinetDurationSelector = ({
742
698
  )}
743
699
  </div>
744
700
  </div>
745
-
746
701
  <div className="space-y-1" style={{ minWidth: '120px' }}>
747
702
  <div className="block text-sm font-bold text-gray-700">
748
703
  Start Hour
@@ -762,7 +717,6 @@ const EpinetDurationSelector = ({
762
717
  ))}
763
718
  </select>
764
719
  </div>
765
-
766
720
  <div className="space-y-1" style={{ minWidth: '120px' }}>
767
721
  <div className="block text-sm font-bold text-gray-700">
768
722
  End Hour
@@ -781,8 +735,6 @@ const EpinetDurationSelector = ({
781
735
  </select>
782
736
  </div>
783
737
  </div>
784
-
785
- {/* Row 2: User select (spans full width) */}
786
738
  {paginatedUserCounts.length > 0 && (
787
739
  <div
788
740
  className="rounded-lg border border-gray-200 bg-white p-3 shadow-sm"
@@ -816,7 +768,6 @@ const EpinetDurationSelector = ({
816
768
  </div>
817
769
  )}
818
770
  </div>
819
-
820
771
  <div>
821
772
  <Select.Root
822
773
  collection={createListCollection({
@@ -905,9 +856,7 @@ const EpinetDurationSelector = ({
905
856
  </div>
906
857
  </div>
907
858
  </div>
908
-
909
- {/* Current status display - only show when no changes pending */}
910
- {!hasLocalChanges && $epinetCustomFilters.enabled && (
859
+ {!hasLocalChanges && (
911
860
  <div className="flex items-center justify-between rounded-md bg-gray-100 p-2">
912
861
  <p className="text-sm font-bold text-gray-700">
913
862
  {getFilterStatusMessage()}
@@ -920,7 +869,6 @@ const EpinetDurationSelector = ({
920
869
  </button>
921
870
  </div>
922
871
  )}
923
-
924
872
  {showTable && (
925
873
  <EpinetTableView
926
874
  fullContentMap={fullContentMap || []}
@@ -931,25 +879,15 @@ const EpinetDurationSelector = ({
931
879
  </div>
932
880
  )}
933
881
  </div>
934
-
935
- {/* UnsavedChangesBar-style sticky bottom bar */}
936
882
  {isVisible && (
937
883
  <div
938
- className={`fixed bottom-0 left-0 right-0 z-50 transform pr-12 transition-all duration-300 ease-in-out ${
939
- isAnimating
940
- ? 'translate-y-0 opacity-100'
941
- : 'translate-y-full opacity-0'
942
- }`}
884
+ className={`fixed bottom-0 left-0 right-0 z-50 transform pr-12 transition-all duration-300 ease-in-out ${isAnimating ? 'translate-y-0 opacity-100' : 'translate-y-full opacity-0'}`}
943
885
  >
944
- {/* Backdrop blur overlay */}
945
886
  <div className="absolute inset-0 bg-black/10 backdrop-blur-sm" />
946
-
947
- {/* Main content bar */}
948
887
  <div className="relative mx-auto max-w-7xl px-4 py-4 sm:px-6 lg:px-8">
949
888
  <div
950
889
  className={`flex items-center justify-between rounded-lg border px-6 py-4 shadow-lg ${styling.bgColor} ${styling.borderColor}`}
951
890
  >
952
- {/* Icon + message */}
953
891
  <div className="flex items-center space-x-3">
954
892
  <div className={`flex-shrink-0 ${styling.iconColor}`}>
955
893
  {styling.icon}
@@ -965,41 +903,22 @@ const EpinetDurationSelector = ({
965
903
  )}
966
904
  </div>
967
905
  </div>
968
-
969
- {/* Action buttons */}
970
906
  <div className="flex items-center space-x-3">
971
- {/* Cancel button - only show when not applying */}
972
907
  {!isApplying && (
973
908
  <button
974
909
  type="button"
975
910
  onClick={cancelChanges}
976
- className={`rounded-md border px-3 py-2 text-sm font-bold shadow-sm transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2 ${
977
- errorMessage
978
- ? 'border-red-300 bg-white text-red-800 hover:bg-red-50 focus:ring-red-500'
979
- : 'border-amber-300 bg-white text-amber-800 hover:bg-amber-50 focus:ring-amber-500'
980
- }`}
911
+ className={`rounded-md border px-3 py-2 text-sm font-bold shadow-sm transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2 ${errorMessage ? 'border-red-300 bg-white text-red-800 hover:bg-red-50 focus:ring-red-500' : 'border-amber-300 bg-white text-amber-800 hover:bg-amber-50 focus:ring-amber-500'}`}
981
912
  >
982
913
  Cancel
983
914
  </button>
984
915
  )}
985
-
986
- {/* Apply Filters button - only show when changes exist and not already applied */}
987
916
  {hasLocalChanges && (
988
917
  <button
989
918
  type="button"
990
919
  onClick={updateDateRange}
991
920
  disabled={!startDate || !endDate || isApplying}
992
- className={`rounded-md border border-transparent px-4 py-2 text-sm font-bold text-white shadow-sm transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed ${
993
- isApplying
994
- ? 'bg-blue-500 opacity-75'
995
- : errorMessage
996
- ? startDate && endDate
997
- ? 'bg-red-600 hover:bg-red-700 focus:ring-red-500'
998
- : 'bg-red-400 opacity-50'
999
- : startDate && endDate
1000
- ? 'bg-amber-600 hover:bg-amber-700 focus:ring-amber-500'
1001
- : 'bg-amber-400 opacity-50'
1002
- }`}
921
+ className={`rounded-md border border-transparent px-4 py-2 text-sm font-bold text-white shadow-sm transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed ${isApplying ? 'bg-blue-500 opacity-75' : errorMessage ? (startDate && endDate ? 'bg-red-600 hover:bg-red-700 focus:ring-red-500' : 'bg-red-400 opacity-50') : startDate && endDate ? 'bg-amber-600 hover:bg-amber-700 focus:ring-amber-500' : 'bg-amber-400 opacity-50'}`}
1003
922
  >
1004
923
  {isApplying ? 'Applying...' : 'Apply Filters'}
1005
924
  </button>