SCGR commited on
Commit
38b820e
·
1 Parent(s): ad1a614

fiter component

Browse files
frontend/src/components/FilterBar.module.css ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ /* FilterBar.module.css */
2
+ /* This file can be empty since we're using Tailwind classes */
frontend/src/components/FilterBar.tsx ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { Container, TextInput, SelectInput, MultiSelectInput, Button } from '@ifrc-go/ui';
3
+ import { useFilterContext } from '../contexts/FilterContext';
4
+ import styles from './FilterBar.module.css';
5
+
6
+ interface FilterBarProps {
7
+ sources: {s_code: string, label: string}[];
8
+ types: {t_code: string, label: string}[];
9
+ regions: {r_code: string, label: string}[];
10
+ countries: {c_code: string, label: string, r_code: string}[];
11
+ imageTypes: {image_type: string, label: string}[];
12
+ isLoadingFilters?: boolean;
13
+ }
14
+
15
+ export default function FilterBar({
16
+ sources,
17
+ types,
18
+ regions,
19
+ countries,
20
+ imageTypes,
21
+ isLoadingFilters = false
22
+ }: FilterBarProps) {
23
+ const {
24
+ search, setSearch,
25
+ srcFilter, setSrcFilter,
26
+ catFilter, setCatFilter,
27
+ regionFilter, setRegionFilter,
28
+ countryFilter, setCountryFilter,
29
+ imageTypeFilter, setImageTypeFilter,
30
+ showReferenceExamples, setShowReferenceExamples,
31
+ clearAllFilters
32
+ } = useFilterContext();
33
+
34
+ return (
35
+ <div className="mb-6 space-y-4">
36
+ {/* Layer 1: Search, Reference Examples, Clear Filters */}
37
+ <div className="flex flex-wrap items-center gap-4">
38
+ <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2 flex-1 min-w-[300px]">
39
+ <TextInput
40
+ name="search"
41
+ placeholder="Search examples..."
42
+ value={search}
43
+ onChange={(v) => setSearch(v || '')}
44
+ />
45
+ </Container>
46
+
47
+ <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
48
+ <Button
49
+ name="reference-examples"
50
+ variant={showReferenceExamples ? "primary" : "secondary"}
51
+ onClick={() => setShowReferenceExamples(!showReferenceExamples)}
52
+ className="whitespace-nowrap"
53
+ >
54
+ <span className="mr-2">
55
+ {showReferenceExamples ? "★" : "☆"}
56
+ </span>
57
+ Reference Examples
58
+ </Button>
59
+ </Container>
60
+
61
+ <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
62
+ <Button
63
+ name="clear-filters"
64
+ variant="secondary"
65
+ onClick={clearAllFilters}
66
+ >
67
+ Clear Filters
68
+ </Button>
69
+ </Container>
70
+ </div>
71
+
72
+ {/* Layer 2: 5 Filter Bars */}
73
+ <div className="flex flex-wrap items-center gap-4">
74
+ <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
75
+ <SelectInput
76
+ name="source"
77
+ placeholder={isLoadingFilters ? "Loading..." : "All Sources"}
78
+ options={sources}
79
+ value={srcFilter || null}
80
+ onChange={(v) => setSrcFilter(v as string || '')}
81
+ keySelector={(o) => o.s_code}
82
+ labelSelector={(o) => o.label}
83
+ required={false}
84
+ disabled={isLoadingFilters}
85
+ />
86
+ </Container>
87
+
88
+ <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
89
+ <SelectInput
90
+ name="category"
91
+ placeholder={isLoadingFilters ? "Loading..." : "All Categories"}
92
+ options={types}
93
+ value={catFilter || null}
94
+ onChange={(v) => setCatFilter(v as string || '')}
95
+ keySelector={(o) => o.t_code}
96
+ labelSelector={(o) => o.label}
97
+ required={false}
98
+ disabled={isLoadingFilters}
99
+ />
100
+ </Container>
101
+
102
+ <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
103
+ <SelectInput
104
+ name="region"
105
+ placeholder={isLoadingFilters ? "Loading..." : "All Regions"}
106
+ options={regions}
107
+ value={regionFilter || null}
108
+ onChange={(v) => setRegionFilter(v as string || '')}
109
+ keySelector={(o) => o.r_code}
110
+ labelSelector={(o) => o.label}
111
+ required={false}
112
+ disabled={isLoadingFilters}
113
+ />
114
+ </Container>
115
+
116
+ <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
117
+ <MultiSelectInput
118
+ name="country"
119
+ placeholder={isLoadingFilters ? "Loading..." : "All Countries"}
120
+ options={countries}
121
+ value={countryFilter ? [countryFilter] : []}
122
+ onChange={(v) => setCountryFilter((v as string[])[0] || '')}
123
+ keySelector={(o) => o.c_code}
124
+ labelSelector={(o) => o.label}
125
+ disabled={isLoadingFilters}
126
+ />
127
+ </Container>
128
+
129
+ <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
130
+ <SelectInput
131
+ name="imageType"
132
+ placeholder={isLoadingFilters ? "Loading..." : "All Image Types"}
133
+ options={imageTypes}
134
+ value={imageTypeFilter || null}
135
+ onChange={(v) => setImageTypeFilter(v as string || '')}
136
+ keySelector={(o) => o.image_type}
137
+ labelSelector={(o) => o.label}
138
+ required={false}
139
+ disabled={isLoadingFilters}
140
+ />
141
+ </Container>
142
+ </div>
143
+ </div>
144
+ );
145
+ }
frontend/src/pages/ExplorePage/ExplorePage.tsx CHANGED
@@ -1,7 +1,8 @@
1
  import { useState, useEffect, useMemo } from 'react';
2
  import { useNavigate } from 'react-router-dom';
3
- import { PageContainer, TextInput, SelectInput, MultiSelectInput, Container, SegmentInput, Spinner, Button, Checkbox } from '@ifrc-go/ui';
4
  import { useFilterContext } from '../../contexts/FilterContext';
 
5
  import styles from './ExplorePage.module.css';
6
 
7
  interface ImageWithCaptionOut {
@@ -484,112 +485,13 @@ export default function ExplorePage() {
484
  {/* Layer 1: Search, Reference Examples, Clear Filters */}
485
  <div className="flex flex-wrap items-center gap-4">
486
  <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2 flex-1 min-w-[300px]">
487
- <TextInput
488
- name="search"
489
- placeholder="Search examples..."
490
- value={search}
491
- onChange={(v) => setSearch(v || '')}
492
- />
493
- </Container>
494
-
495
- {/* Reference Examples Filter - Available to all users */}
496
- <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
497
- <Button
498
- name="reference-examples"
499
- variant={showReferenceExamples ? "primary" : "secondary"}
500
- onClick={() => setShowReferenceExamples(!showReferenceExamples)}
501
- className="whitespace-nowrap"
502
- >
503
- <span className="mr-2">
504
- {showReferenceExamples ? (
505
- <span className="text-yellow-400">★</span>
506
- ) : (
507
- <span className="text-yellow-400">☆</span>
508
- )}
509
- </span>
510
- Reference Examples
511
- </Button>
512
- </Container>
513
-
514
- <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
515
- <Button
516
- name="clear-filters"
517
- variant="secondary"
518
- onClick={clearAllFilters}
519
- >
520
- Clear Filters
521
- </Button>
522
- </Container>
523
- </div>
524
-
525
- {/* Layer 2: 5 Filter Bars */}
526
- <div className="flex flex-wrap items-center gap-4">
527
- <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
528
- <SelectInput
529
- name="source"
530
- placeholder={isLoadingFilters ? "Loading..." : "All Sources"}
531
- options={sources}
532
- value={srcFilter || null}
533
- onChange={(v) => setSrcFilter(v as string || '')}
534
- keySelector={(o) => o.s_code}
535
- labelSelector={(o) => o.label}
536
- required={false}
537
- disabled={isLoadingFilters}
538
- />
539
- </Container>
540
-
541
- <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
542
- <SelectInput
543
- name="category"
544
- placeholder={isLoadingFilters ? "Loading..." : "All Categories"}
545
- options={types}
546
- value={catFilter || null}
547
- onChange={(v) => setCatFilter(v as string || '')}
548
- keySelector={(o) => o.t_code}
549
- labelSelector={(o) => o.label}
550
- required={false}
551
- disabled={isLoadingFilters}
552
- />
553
- </Container>
554
-
555
- <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
556
- <SelectInput
557
- name="region"
558
- placeholder={isLoadingFilters ? "Loading..." : "All Regions"}
559
- options={regions}
560
- value={regionFilter || null}
561
- onChange={(v) => setRegionFilter(v as string || '')}
562
- keySelector={(o) => o.r_code}
563
- labelSelector={(o) => o.label}
564
- required={false}
565
- disabled={isLoadingFilters}
566
- />
567
- </Container>
568
-
569
- <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
570
- <MultiSelectInput
571
- name="country"
572
- placeholder={isLoadingFilters ? "Loading..." : "All Countries"}
573
- options={countries}
574
- value={countryFilter ? [countryFilter] : []}
575
- onChange={(v) => setCountryFilter((v as string[])[0] || '')}
576
- keySelector={(o) => o.c_code}
577
- labelSelector={(o) => o.label}
578
- disabled={isLoadingFilters}
579
- />
580
- </Container>
581
-
582
- <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
583
- <SelectInput
584
- name="imageType"
585
- placeholder={isLoadingFilters ? "Loading..." : "All Image Types"}
586
- options={imageTypes}
587
- value={imageTypeFilter || null}
588
- onChange={(v) => setImageTypeFilter(v as string || '')}
589
- keySelector={(o) => o.image_type}
590
- labelSelector={(o) => o.label}
591
- required={false}
592
- disabled={isLoadingFilters}
593
  />
594
  </Container>
595
  </div>
 
1
  import { useState, useEffect, useMemo } from 'react';
2
  import { useNavigate } from 'react-router-dom';
3
+ import { PageContainer, Container, SegmentInput, Spinner, Button, Checkbox } from '@ifrc-go/ui';
4
  import { useFilterContext } from '../../contexts/FilterContext';
5
+ import FilterBar from '../../components/FilterBar';
6
  import styles from './ExplorePage.module.css';
7
 
8
  interface ImageWithCaptionOut {
 
485
  {/* Layer 1: Search, Reference Examples, Clear Filters */}
486
  <div className="flex flex-wrap items-center gap-4">
487
  <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2 flex-1 min-w-[300px]">
488
+ <FilterBar
489
+ sources={sources}
490
+ types={types}
491
+ regions={regions}
492
+ countries={countries}
493
+ imageTypes={imageTypes}
494
+ isLoadingFilters={isLoadingFilters}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  />
496
  </Container>
497
  </div>
frontend/src/pages/MapDetailsPage/MapDetailPage.tsx CHANGED
@@ -240,16 +240,26 @@ export default function MapDetailPage() {
240
 
241
  const currentIndex = filteredImages.findIndex((img: { image_id: string }) => img.image_id === mapId);
242
 
 
243
  if (currentIndex === -1) {
 
 
 
 
 
 
 
 
 
244
  console.error('Current image not found in filtered list');
245
  return;
246
  }
247
 
248
  let targetIndex: number;
249
  if (direction === 'previous') {
250
- targetIndex = currentIndex > 0 ? currentIndex - 1 : filteredImages.length - 1;
251
  } else {
252
- targetIndex = currentIndex < filteredImages.length - 1 ? currentIndex + 1 : 0;
253
  }
254
 
255
  const targetImage = filteredImages[targetIndex];
 
240
 
241
  const currentIndex = filteredImages.findIndex((img: { image_id: string }) => img.image_id === mapId);
242
 
243
+ // If current image is not in filtered list, add it temporarily for navigation
244
  if (currentIndex === -1) {
245
+ const currentImage = images.find((img: any) => img.image_id === mapId);
246
+ if (currentImage) {
247
+ filteredImages.push(currentImage);
248
+ }
249
+ }
250
+
251
+ const adjustedCurrentIndex = filteredImages.findIndex((img: { image_id: string }) => img.image_id === mapId);
252
+
253
+ if (adjustedCurrentIndex === -1) {
254
  console.error('Current image not found in filtered list');
255
  return;
256
  }
257
 
258
  let targetIndex: number;
259
  if (direction === 'previous') {
260
+ targetIndex = adjustedCurrentIndex > 0 ? adjustedCurrentIndex - 1 : filteredImages.length - 1;
261
  } else {
262
+ targetIndex = adjustedCurrentIndex < filteredImages.length - 1 ? adjustedCurrentIndex + 1 : 0;
263
  }
264
 
265
  const targetImage = filteredImages[targetIndex];