@snapdragonsnursery/react-components 1.1.38 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,6 @@ import {
13
13
  import { trackEvent } from "./telemetry";
14
14
  import { Button } from "./components/ui/button";
15
15
  import { Input } from "./components/ui/input";
16
- import { Select, SelectOption } from "./components/ui/select";
17
16
  import {
18
17
  Table,
19
18
  TableBody,
@@ -32,17 +31,13 @@ import {
32
31
  PaginationPrevious,
33
32
  } from "./components/ui/pagination";
34
33
  import {
35
- MagnifyingGlassIcon,
36
- FunnelIcon,
37
- ChevronDownIcon,
38
- ChevronUpIcon,
39
- UserIcon,
40
- MapPinIcon,
41
- CalendarIcon,
42
34
  CheckCircleIcon,
43
35
  XCircleIcon,
36
+ ChevronUpIcon,
37
+ ChevronDownIcon,
44
38
  } from "@heroicons/react/24/outline";
45
39
  import { cn } from "./lib/utils";
40
+ import ChildSearchFilters from "./components/ChildSearchFilters";
46
41
 
47
42
  const ChildSearchPage = ({
48
43
  title = "Child Search",
@@ -90,6 +85,10 @@ const ChildSearchPage = ({
90
85
  sortOrder: sortOrder,
91
86
  });
92
87
 
88
+ // Table sorting state
89
+ const [sorting, setSorting] = useState([]);
90
+ const [debouncedAdvancedFilters, setDebouncedAdvancedFilters] = useState(advancedFilters);
91
+
93
92
  // State for multi-select mode
94
93
  const [selectedChildrenState, setSelectedChildrenState] = useState(
95
94
  selectedChildren || []
@@ -100,6 +99,35 @@ const ChildSearchPage = ({
100
99
  // Column helper for TanStack table
101
100
  const columnHelper = createColumnHelper();
102
101
 
102
+ // Helper function to create sortable header
103
+ const createSortableHeader = (column, label) => {
104
+ return ({ column: tableColumn }) => {
105
+ const isSorted = tableColumn.getIsSorted();
106
+ return (
107
+ <div
108
+ className="flex items-center space-x-1 cursor-pointer select-none"
109
+ onClick={tableColumn.getToggleSortingHandler()}
110
+ >
111
+ <span>{label}</span>
112
+ <div className="flex flex-col">
113
+ <ChevronUpIcon
114
+ className={cn(
115
+ "h-3 w-3 transition-colors",
116
+ isSorted === "asc" ? "text-blue-500" : "text-gray-400"
117
+ )}
118
+ />
119
+ <ChevronDownIcon
120
+ className={cn(
121
+ "h-3 w-3 transition-colors -mt-1",
122
+ isSorted === "desc" ? "text-blue-500" : "text-gray-400"
123
+ )}
124
+ />
125
+ </div>
126
+ </div>
127
+ );
128
+ };
129
+ };
130
+
103
131
  // Define table columns
104
132
  const columns = [
105
133
  // Checkbox column for multi-select
@@ -127,9 +155,9 @@ const ChildSearchPage = ({
127
155
  }),
128
156
  ]
129
157
  : []),
130
- // Name column
158
+ // Name column - sortable
131
159
  columnHelper.accessor("full_name", {
132
- header: "Name",
160
+ header: createSortableHeader("full_name", "Name"),
133
161
  cell: ({ row }) => (
134
162
  <div>
135
163
  <div className="font-medium">{row.original.full_name}</div>
@@ -139,16 +167,16 @@ const ChildSearchPage = ({
139
167
  </div>
140
168
  ),
141
169
  }),
142
- // Site column
170
+ // Site column - sortable
143
171
  columnHelper.accessor("site_name", {
144
- header: "Site",
172
+ header: createSortableHeader("site_name", "Site"),
145
173
  cell: ({ row }) => (
146
174
  <span>{row.original.site_name}</span>
147
175
  ),
148
176
  }),
149
- // Date of Birth column
177
+ // Date of Birth column - sortable
150
178
  columnHelper.accessor("date_of_birth", {
151
- header: "Date of Birth",
179
+ header: createSortableHeader("date_of_birth", "Date of Birth"),
152
180
  cell: ({ row }) => (
153
181
  <span>
154
182
  {row.original.date_of_birth
@@ -157,9 +185,9 @@ const ChildSearchPage = ({
157
185
  </span>
158
186
  ),
159
187
  }),
160
- // Age column
188
+ // Age column - sortable (using age_years for sorting)
161
189
  columnHelper.accessor("age_years", {
162
- header: "Age",
190
+ header: createSortableHeader("age_years", "Age"),
163
191
  cell: ({ row }) => {
164
192
  const { age_years, age_months } = row.original;
165
193
 
@@ -175,27 +203,20 @@ const ChildSearchPage = ({
175
203
  return <span>{ageText}</span>;
176
204
  },
177
205
  }),
178
- // Status column
206
+ // Status column - sortable
179
207
  columnHelper.accessor("is_active", {
180
- header: "Status",
208
+ header: createSortableHeader("is_active", "Status"),
181
209
  cell: ({ row }) => (
182
- <div className="flex items-center space-x-2">
183
- {row.original.is_active ? (
184
- <CheckCircleIcon className="h-4 w-4 text-green-500" />
185
- ) : (
186
- <XCircleIcon className="h-4 w-4 text-red-500" />
210
+ <span
211
+ className={cn(
212
+ "px-2 py-1 text-xs rounded-full",
213
+ row.original.is_active
214
+ ? "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"
215
+ : "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200"
187
216
  )}
188
- <span
189
- className={cn(
190
- "px-2 py-1 text-xs rounded-full",
191
- row.original.is_active
192
- ? "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"
193
- : "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200"
194
- )}
195
- >
196
- {row.original.is_active ? "Active" : "Inactive"}
197
- </span>
198
- </div>
217
+ >
218
+ {row.original.is_active ? "Active" : "Inactive"}
219
+ </span>
199
220
  ),
200
221
  }),
201
222
  ];
@@ -207,6 +228,7 @@ const ChildSearchPage = ({
207
228
  getCoreRowModel: getCoreRowModel(),
208
229
  getSortedRowModel: getSortedRowModel(),
209
230
  state: {
231
+ sorting,
210
232
  rowSelection: selectedChildrenState.reduce((acc, selectedChild) => {
211
233
  const rowIndex = children.findIndex(child => child.child_id === selectedChild.child_id);
212
234
  if (rowIndex !== -1) {
@@ -215,6 +237,7 @@ const ChildSearchPage = ({
215
237
  return acc;
216
238
  }, {}),
217
239
  },
240
+ onSortingChange: setSorting,
218
241
  onRowSelectionChange: (updater) => {
219
242
  const newSelection =
220
243
  typeof updater === "function" ? updater({}) : updater;
@@ -235,6 +258,16 @@ const ChildSearchPage = ({
235
258
  return () => clearTimeout(timer);
236
259
  }, [searchTerm]);
237
260
 
261
+ // Debounce advanced filters
262
+ useEffect(() => {
263
+ const timer = setTimeout(() => {
264
+ setDebouncedAdvancedFilters(advancedFilters);
265
+ setPagination((prev) => ({ ...prev, page: 1 }));
266
+ }, 500); // Slightly longer delay for filters
267
+
268
+ return () => clearTimeout(timer);
269
+ }, [advancedFilters]);
270
+
238
271
  // Search children
239
272
  const searchChildren = useCallback(async () => {
240
273
  if (!instance || !accounts[0]) {
@@ -260,38 +293,38 @@ const ChildSearchPage = ({
260
293
  // Handle site filtering
261
294
  if (siteIds && siteIds.length > 0) {
262
295
  params.append("site_ids", siteIds.join(","));
263
- } else if (advancedFilters.selectedSiteId) {
264
- params.append("site_id", advancedFilters.selectedSiteId.toString());
296
+ } else if (debouncedAdvancedFilters.selectedSiteId) {
297
+ params.append("site_id", debouncedAdvancedFilters.selectedSiteId.toString());
265
298
  } else if (siteId) {
266
299
  params.append("site_id", siteId.toString());
267
300
  }
268
301
 
269
302
  // Handle status filtering
270
- if (advancedFilters.status && advancedFilters.status !== "all") {
271
- params.append("status", advancedFilters.status);
303
+ if (debouncedAdvancedFilters.status && debouncedAdvancedFilters.status !== "all") {
304
+ params.append("status", debouncedAdvancedFilters.status);
272
305
  } else if (activeOnly) {
273
306
  params.append("active_only", "true");
274
307
  }
275
308
 
276
309
  // Add date of birth filters
277
- if (advancedFilters.dobFrom) {
278
- params.append("dob_from", advancedFilters.dobFrom);
310
+ if (debouncedAdvancedFilters.dobFrom) {
311
+ params.append("dob_from", debouncedAdvancedFilters.dobFrom);
279
312
  }
280
- if (advancedFilters.dobTo) {
281
- params.append("dob_to", advancedFilters.dobTo);
313
+ if (debouncedAdvancedFilters.dobTo) {
314
+ params.append("dob_to", debouncedAdvancedFilters.dobTo);
282
315
  }
283
316
 
284
317
  // Add age filters
285
- if (advancedFilters.ageFrom) {
286
- params.append("age_from", advancedFilters.ageFrom);
318
+ if (debouncedAdvancedFilters.ageFrom) {
319
+ params.append("age_from", debouncedAdvancedFilters.ageFrom);
287
320
  }
288
- if (advancedFilters.ageTo) {
289
- params.append("age_to", advancedFilters.ageTo);
321
+ if (debouncedAdvancedFilters.ageTo) {
322
+ params.append("age_to", debouncedAdvancedFilters.ageTo);
290
323
  }
291
324
 
292
325
  // Add sorting
293
- params.append("sort_by", advancedFilters.sortBy);
294
- params.append("sort_order", advancedFilters.sortOrder);
326
+ params.append("sort_by", debouncedAdvancedFilters.sortBy);
327
+ params.append("sort_order", debouncedAdvancedFilters.sortOrder);
295
328
 
296
329
  const apiResponse = await fetch(
297
330
  `${
@@ -349,7 +382,7 @@ const ChildSearchPage = ({
349
382
  siteId,
350
383
  siteIds,
351
384
  activeOnly,
352
- advancedFilters,
385
+ debouncedAdvancedFilters,
353
386
  applicationContext,
354
387
  bypassPermissions,
355
388
  ]);
@@ -398,7 +431,7 @@ const ChildSearchPage = ({
398
431
  };
399
432
 
400
433
  const clearFilters = () => {
401
- setAdvancedFilters({
434
+ const clearedFilters = {
402
435
  status: activeOnly ? "active" : "all",
403
436
  selectedSiteId: "",
404
437
  dobFrom: "",
@@ -407,7 +440,9 @@ const ChildSearchPage = ({
407
440
  ageTo: "",
408
441
  sortBy: "last_name",
409
442
  sortOrder: "asc",
410
- });
443
+ };
444
+ setAdvancedFilters(clearedFilters);
445
+ setDebouncedAdvancedFilters(clearedFilters); // Clear immediately
411
446
  };
412
447
 
413
448
  // Calculate pagination display values
@@ -433,11 +468,10 @@ const ChildSearchPage = ({
433
468
  )}
434
469
  </div>
435
470
 
436
- {/* Search and Filters */}
471
+ {/* Search Input */}
437
472
  <div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 mb-6">
438
473
  <div className="p-6">
439
- {/* Search Input */}
440
- <div className="relative mb-4">
474
+ <div className="relative">
441
475
  <Input
442
476
  type="text"
443
477
  placeholder={searchPlaceholder}
@@ -446,211 +480,21 @@ const ChildSearchPage = ({
446
480
  className="pl-4"
447
481
  />
448
482
  </div>
449
-
450
- {/* Advanced Filters Toggle */}
451
- <div className="flex items-center justify-between">
452
- <button
453
- onClick={() => setIsAdvancedFiltersOpen(!isAdvancedFiltersOpen)}
454
- className="flex items-center space-x-2 text-sm text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300"
455
- >
456
- <FunnelIcon className="h-4 w-4" />
457
- <span>Advanced Filters</span>
458
- {isAdvancedFiltersOpen ? (
459
- <ChevronUpIcon className="h-4 w-4" />
460
- ) : (
461
- <ChevronDownIcon className="h-4 w-4" />
462
- )}
463
- </button>
464
- <button
465
- onClick={clearFilters}
466
- className="text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200"
467
- >
468
- Clear Filters
469
- </button>
470
- </div>
471
-
472
- {/* Advanced Filters */}
473
- {isAdvancedFiltersOpen && (
474
- <div
475
- className="mt-4 p-4 rounded-lg"
476
- style={{
477
- backgroundColor: 'hsl(var(--card))',
478
- border: '1px solid hsl(var(--border))'
479
- }}
480
- >
481
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
482
- {/* Status Filter */}
483
- <div>
484
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
485
- Status
486
- </label>
487
- <Select
488
- value={advancedFilters.status}
489
- onChange={(e) =>
490
- setAdvancedFilters((prev) => ({
491
- ...prev,
492
- status: e.target.value,
493
- }))
494
- }
495
- className="bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white"
496
- >
497
- <SelectOption value="all">All Children</SelectOption>
498
- <SelectOption value="active">Active Only</SelectOption>
499
- <SelectOption value="inactive">
500
- Inactive Only
501
- </SelectOption>
502
- </Select>
503
- </div>
504
-
505
- {/* Site Filter */}
506
- {sites && sites.length > 0 && (
507
- <div>
508
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
509
- Site
510
- </label>
511
- <Select
512
- value={advancedFilters.selectedSiteId}
513
- onChange={(e) =>
514
- setAdvancedFilters((prev) => ({
515
- ...prev,
516
- selectedSiteId: e.target.value,
517
- }))
518
- }
519
- className="bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white"
520
- >
521
- <SelectOption value="">All Sites</SelectOption>
522
- {sites.map((site) => (
523
- <SelectOption key={site.site_id} value={site.site_id}>
524
- {site.site_name}
525
- </SelectOption>
526
- ))}
527
- </Select>
528
- </div>
529
- )}
530
-
531
- {/* Date of Birth Range */}
532
- <div>
533
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
534
- Date of Birth From
535
- </label>
536
- <Input
537
- type="date"
538
- value={advancedFilters.dobFrom}
539
- onChange={(e) =>
540
- setAdvancedFilters((prev) => ({
541
- ...prev,
542
- dobFrom: e.target.value,
543
- }))
544
- }
545
- className="bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white"
546
- />
547
- </div>
548
-
549
- <div>
550
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
551
- Date of Birth To
552
- </label>
553
- <Input
554
- type="date"
555
- value={advancedFilters.dobTo}
556
- onChange={(e) =>
557
- setAdvancedFilters((prev) => ({
558
- ...prev,
559
- dobTo: e.target.value,
560
- }))
561
- }
562
- className="bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white"
563
- />
564
- </div>
565
-
566
- {/* Age Range */}
567
- <div>
568
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
569
- Age From (months)
570
- </label>
571
- <Input
572
- type="number"
573
- min="0"
574
- value={advancedFilters.ageFrom}
575
- onChange={(e) =>
576
- setAdvancedFilters((prev) => ({
577
- ...prev,
578
- ageFrom: e.target.value,
579
- }))
580
- }
581
- placeholder="0"
582
- className="bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white"
583
- />
584
- </div>
585
-
586
- <div>
587
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
588
- Age To (months)
589
- </label>
590
- <Input
591
- type="number"
592
- min="0"
593
- value={advancedFilters.ageTo}
594
- onChange={(e) =>
595
- setAdvancedFilters((prev) => ({
596
- ...prev,
597
- ageTo: e.target.value,
598
- }))
599
- }
600
- placeholder="60"
601
- className="bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white"
602
- />
603
- </div>
604
-
605
- {/* Sort Options */}
606
- <div>
607
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
608
- Sort By
609
- </label>
610
- <Select
611
- value={advancedFilters.sortBy}
612
- onChange={(e) =>
613
- setAdvancedFilters((prev) => ({
614
- ...prev,
615
- sortBy: e.target.value,
616
- }))
617
- }
618
- className="bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white"
619
- >
620
- <SelectOption value="last_name">Last Name</SelectOption>
621
- <SelectOption value="first_name">First Name</SelectOption>
622
- <SelectOption value="full_name">Full Name</SelectOption>
623
- <SelectOption value="date_of_birth">
624
- Date of Birth
625
- </SelectOption>
626
- <SelectOption value="site_name">Site Name</SelectOption>
627
- </Select>
628
- </div>
629
-
630
- <div>
631
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
632
- Sort Order
633
- </label>
634
- <Select
635
- value={advancedFilters.sortOrder}
636
- onChange={(e) =>
637
- setAdvancedFilters((prev) => ({
638
- ...prev,
639
- sortOrder: e.target.value,
640
- }))
641
- }
642
- className="bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white"
643
- >
644
- <SelectOption value="asc">Ascending</SelectOption>
645
- <SelectOption value="desc">Descending</SelectOption>
646
- </Select>
647
- </div>
648
- </div>
649
- </div>
650
- )}
651
483
  </div>
652
484
  </div>
653
485
 
486
+ {/* Advanced Filters */}
487
+ <ChildSearchFilters
488
+ filters={advancedFilters}
489
+ onFiltersChange={setAdvancedFilters}
490
+ onApplyFilters={setAdvancedFilters}
491
+ sites={sites}
492
+ activeOnly={activeOnly}
493
+ isAdvancedFiltersOpen={isAdvancedFiltersOpen}
494
+ onToggleAdvancedFilters={() => setIsAdvancedFiltersOpen(!isAdvancedFiltersOpen)}
495
+ onClearFilters={clearFilters}
496
+ />
497
+
654
498
  {/* Results */}
655
499
  <div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
656
500
  {loading && (
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,58 @@
1
+ // Demo component showing how to use DateRangePicker and DatePicker components
2
+ // Demonstrates both single date and date range selection functionality
3
+
4
+ import React, { useState } from 'react'
5
+ import { DateRangePicker, DatePicker } from './components/ui/date-range-picker'
6
+
7
+ export default function DateRangePickerDemo() {
8
+ const [selectedRange, setSelectedRange] = useState(null)
9
+ const [selectedDate, setSelectedDate] = useState(null)
10
+
11
+ return (
12
+ <div className="p-6 space-y-6 max-w-md mx-auto">
13
+ <h2 className="text-2xl font-bold mb-4">Date Picker Components Demo</h2>
14
+
15
+ <div className="space-y-4">
16
+ <div>
17
+ <h3 className="text-lg font-semibold mb-2">Date Range Picker</h3>
18
+ <DateRangePicker
19
+ selectedRange={selectedRange}
20
+ onSelect={setSelectedRange}
21
+ placeholder="Select a date range"
22
+ />
23
+ {selectedRange?.from && selectedRange?.to && (
24
+ <p className="text-sm text-gray-600 mt-2">
25
+ Selected: {selectedRange.from.toLocaleDateString()} - {selectedRange.to.toLocaleDateString()}
26
+ </p>
27
+ )}
28
+ </div>
29
+
30
+ <div>
31
+ <h3 className="text-lg font-semibold mb-2">Single Date Picker</h3>
32
+ <DatePicker
33
+ selectedDate={selectedDate}
34
+ onSelect={setSelectedDate}
35
+ placeholder="Select a date"
36
+ />
37
+ {selectedDate && (
38
+ <p className="text-sm text-gray-600 mt-2">
39
+ Selected: {selectedDate.toLocaleDateString()}
40
+ </p>
41
+ )}
42
+ </div>
43
+
44
+ <div className="pt-4">
45
+ <button
46
+ onClick={() => {
47
+ setSelectedRange(null)
48
+ setSelectedDate(null)
49
+ }}
50
+ className="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded"
51
+ >
52
+ Clear All
53
+ </button>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ )
58
+ }
@@ -0,0 +1,71 @@
1
+ // Test component to verify DateRangePicker behavior
2
+ // This can be used to test if the date range picker stays open when selecting first date
3
+
4
+ import React, { useState } from 'react'
5
+ import { DateRangePicker } from './components/ui/date-range-picker'
6
+
7
+ export default function DateRangePickerTest() {
8
+ const [selectedRange, setSelectedRange] = useState(null)
9
+ const [filters, setFilters] = useState({
10
+ dobFrom: '',
11
+ dobTo: '',
12
+ })
13
+
14
+ const handleDateRangeChange = (range) => {
15
+ console.log('DateRangePickerTest handleDateRangeChange called with:', range)
16
+
17
+ // DateRangePicker now only calls onSelect when both dates are selected or when cleared
18
+ if (range?.from && range?.to) {
19
+ console.log('Both dates selected - updating filters')
20
+ setFilters({
21
+ dobFrom: range.from.toISOString().split('T')[0],
22
+ dobTo: range.to.toISOString().split('T')[0],
23
+ })
24
+ } else if (!range?.from && !range?.to) {
25
+ console.log('Range cleared - clearing filters')
26
+ setFilters({
27
+ dobFrom: '',
28
+ dobTo: '',
29
+ })
30
+ }
31
+ // No need to handle single date selection as DateRangePicker handles it internally
32
+
33
+ setSelectedRange(range)
34
+ }
35
+
36
+ return (
37
+ <div className="p-6 space-y-4 max-w-md mx-auto">
38
+ <h2 className="text-2xl font-bold">Date Range Picker Test</h2>
39
+
40
+ <div className="space-y-2">
41
+ <label className="block text-sm font-medium">Date of Birth Range</label>
42
+ <DateRangePicker
43
+ selectedRange={selectedRange}
44
+ onSelect={handleDateRangeChange}
45
+ placeholder="Select date of birth range"
46
+ />
47
+ </div>
48
+
49
+ <div className="space-y-2">
50
+ <h3 className="text-lg font-semibold">Current State:</h3>
51
+ <div className="text-sm space-y-1">
52
+ <div>Selected Range: {selectedRange ? JSON.stringify(selectedRange, null, 2) : 'null'}</div>
53
+ <div>Filters dobFrom: {filters.dobFrom || 'empty'}</div>
54
+ <div>Filters dobTo: {filters.dobTo || 'empty'}</div>
55
+ </div>
56
+ </div>
57
+
58
+ <div className="pt-4">
59
+ <button
60
+ onClick={() => {
61
+ setSelectedRange(null)
62
+ setFilters({ dobFrom: '', dobTo: '' })
63
+ }}
64
+ className="px-4 py-2 bg-gray-200 hover:bg-gray-300 rounded"
65
+ >
66
+ Clear All
67
+ </button>
68
+ </div>
69
+ </div>
70
+ )
71
+ }