Spaces:
				
			
			
	
			
			
					
		Running
		
	
	
	
			
			
	
	
	
	
		
		
					
		Running
		
	image type filter bar
Browse files
    	
        docker-compose.yml
    CHANGED
    
    | 
         @@ -9,7 +9,7 @@ services: 
     | 
|
| 9 | 
         
             
                  POSTGRES_PASSWORD: promptaid
         
     | 
| 10 | 
         
             
                  POSTGRES_DB: promptaid
         
     | 
| 11 | 
         
             
                ports:
         
     | 
| 12 | 
         
            -
                  - "5434:5432" 
     | 
| 13 | 
         
             
                volumes:
         
     | 
| 14 | 
         
             
                  - pgdata:/var/lib/postgresql/data
         
     | 
| 15 | 
         | 
| 
         @@ -39,11 +39,7 @@ services: 
     | 
|
| 39 | 
         
             
                  - minio_data:/data
         
     | 
| 40 | 
         
             
                depends_on:
         
     | 
| 41 | 
         
             
                  - postgres
         
     | 
| 42 | 
         
            -
             
     | 
| 43 | 
         
            -
                  test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
         
     | 
| 44 | 
         
            -
                  interval: 30s
         
     | 
| 45 | 
         
            -
                  timeout: 20s
         
     | 
| 46 | 
         
            -
                  retries: 3
         
     | 
| 47 | 
         | 
| 48 | 
         
             
              app:
         
     | 
| 49 | 
         
             
                build: .
         
     | 
| 
         @@ -55,10 +51,8 @@ services: 
     | 
|
| 55 | 
         
             
                  - DATABASE_URL=postgresql://promptaid:promptaid@postgres:5432/promptaid
         
     | 
| 56 | 
         
             
                  - S3_ENDPOINT=http://minio:9000  # Override for container networking
         
     | 
| 57 | 
         
             
                depends_on:
         
     | 
| 58 | 
         
            -
                  postgres 
     | 
| 59 | 
         
            -
             
     | 
| 60 | 
         
            -
                  minio:
         
     | 
| 61 | 
         
            -
                    condition: service_healthy
         
     | 
| 62 | 
         
             
                volumes:
         
     | 
| 63 | 
         
             
                  - ./py_backend:/app
         
     | 
| 64 | 
         
             
                  - /app/__pycache__
         
     | 
| 
         | 
|
| 9 | 
         
             
                  POSTGRES_PASSWORD: promptaid
         
     | 
| 10 | 
         
             
                  POSTGRES_DB: promptaid
         
     | 
| 11 | 
         
             
                ports:
         
     | 
| 12 | 
         
            +
                  - "5434:5432" 
         
     | 
| 13 | 
         
             
                volumes:
         
     | 
| 14 | 
         
             
                  - pgdata:/var/lib/postgresql/data
         
     | 
| 15 | 
         | 
| 
         | 
|
| 39 | 
         
             
                  - minio_data:/data
         
     | 
| 40 | 
         
             
                depends_on:
         
     | 
| 41 | 
         
             
                  - postgres
         
     | 
| 42 | 
         
            +
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 43 | 
         | 
| 44 | 
         
             
              app:
         
     | 
| 45 | 
         
             
                build: .
         
     | 
| 
         | 
|
| 51 | 
         
             
                  - DATABASE_URL=postgresql://promptaid:promptaid@postgres:5432/promptaid
         
     | 
| 52 | 
         
             
                  - S3_ENDPOINT=http://minio:9000  # Override for container networking
         
     | 
| 53 | 
         
             
                depends_on:
         
     | 
| 54 | 
         
            +
                  - postgres
         
     | 
| 55 | 
         
            +
                  - minio
         
     | 
| 
         | 
|
| 
         | 
|
| 56 | 
         
             
                volumes:
         
     | 
| 57 | 
         
             
                  - ./py_backend:/app
         
     | 
| 58 | 
         
             
                  - /app/__pycache__
         
     | 
    	
        frontend/src/pages/ExplorePage/ExplorePage.tsx
    CHANGED
    
    | 
         @@ -1,4 +1,4 @@ 
     | 
|
| 1 | 
         
            -
            import { PageContainer, TextInput, SelectInput, MultiSelectInput, Container, SegmentInput, Spinner } from '@ifrc-go/ui';
         
     | 
| 2 | 
         
             
            import { useState, useEffect, useMemo } from 'react';
         
     | 
| 3 | 
         
             
            import { useNavigate } from 'react-router-dom';
         
     | 
| 4 | 
         
             
            import styles from './ExplorePage.module.css';
         
     | 
| 
         @@ -36,6 +36,7 @@ export default function ExplorePage() { 
     | 
|
| 36 | 
         
             
              const [catFilter, setCatFilter] = useState('');
         
     | 
| 37 | 
         
             
              const [regionFilter, setRegionFilter] = useState('');
         
     | 
| 38 | 
         
             
              const [countryFilter, setCountryFilter] = useState('');
         
     | 
| 
         | 
|
| 39 | 
         
             
              const [sources, setSources] = useState<{s_code: string, label: string}[]>([]);
         
     | 
| 40 | 
         
             
              const [types, setTypes] = useState<{t_code: string, label: string}[]>([]);
         
     | 
| 41 | 
         
             
              const [regions, setRegions] = useState<{r_code: string, label: string}[]>([]);
         
     | 
| 
         @@ -145,10 +146,11 @@ export default function ExplorePage() { 
     | 
|
| 145 | 
         
             
                    c.countries.some(country => country.r_code === regionFilter);
         
     | 
| 146 | 
         
             
                  const matchesCountry = !countryFilter || 
         
     | 
| 147 | 
         
             
                    c.countries.some(country => country.c_code === countryFilter);
         
     | 
| 
         | 
|
| 148 | 
         | 
| 149 | 
         
            -
                  return matchesSearch && matchesSource && matchesCategory && matchesRegion && matchesCountry;
         
     | 
| 150 | 
         
             
                });
         
     | 
| 151 | 
         
            -
              }, [captions, search, srcFilter, catFilter, regionFilter, countryFilter]);
         
     | 
| 152 | 
         | 
| 153 | 
         | 
| 154 | 
         
             
              return (
         
     | 
| 
         @@ -246,6 +248,20 @@ export default function ExplorePage() { 
     | 
|
| 246 | 
         
             
                                disabled={isLoadingFilters}
         
     | 
| 247 | 
         
             
                              />
         
     | 
| 248 | 
         
             
                            </Container>
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 249 | 
         
             
                          </div>
         
     | 
| 250 | 
         
             
                        </div>
         
     | 
| 251 | 
         | 
| 
         @@ -255,6 +271,20 @@ export default function ExplorePage() { 
     | 
|
| 255 | 
         
             
                            <p className="text-sm text-gray-600">
         
     | 
| 256 | 
         
             
                              {filtered.length} of {captions.length} examples
         
     | 
| 257 | 
         
             
                            </p>
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 258 | 
         
             
                          </div>
         
     | 
| 259 | 
         | 
| 260 | 
         
             
                          {/* Loading State */}
         
     | 
| 
         | 
|
| 1 | 
         
            +
            import { PageContainer, TextInput, SelectInput, MultiSelectInput, Container, SegmentInput, Spinner, Button } from '@ifrc-go/ui';
         
     | 
| 2 | 
         
             
            import { useState, useEffect, useMemo } from 'react';
         
     | 
| 3 | 
         
             
            import { useNavigate } from 'react-router-dom';
         
     | 
| 4 | 
         
             
            import styles from './ExplorePage.module.css';
         
     | 
| 
         | 
|
| 36 | 
         
             
              const [catFilter, setCatFilter] = useState('');
         
     | 
| 37 | 
         
             
              const [regionFilter, setRegionFilter] = useState('');
         
     | 
| 38 | 
         
             
              const [countryFilter, setCountryFilter] = useState('');
         
     | 
| 39 | 
         
            +
              const [imageTypeFilter, setImageTypeFilter] = useState('');
         
     | 
| 40 | 
         
             
              const [sources, setSources] = useState<{s_code: string, label: string}[]>([]);
         
     | 
| 41 | 
         
             
              const [types, setTypes] = useState<{t_code: string, label: string}[]>([]);
         
     | 
| 42 | 
         
             
              const [regions, setRegions] = useState<{r_code: string, label: string}[]>([]);
         
     | 
| 
         | 
|
| 146 | 
         
             
                    c.countries.some(country => country.r_code === regionFilter);
         
     | 
| 147 | 
         
             
                  const matchesCountry = !countryFilter || 
         
     | 
| 148 | 
         
             
                    c.countries.some(country => country.c_code === countryFilter);
         
     | 
| 149 | 
         
            +
                  const matchesImageType = !imageTypeFilter || c.image_type === imageTypeFilter;
         
     | 
| 150 | 
         | 
| 151 | 
         
            +
                  return matchesSearch && matchesSource && matchesCategory && matchesRegion && matchesCountry && matchesImageType;
         
     | 
| 152 | 
         
             
                });
         
     | 
| 153 | 
         
            +
              }, [captions, search, srcFilter, catFilter, regionFilter, countryFilter, imageTypeFilter]);
         
     | 
| 154 | 
         | 
| 155 | 
         | 
| 156 | 
         
             
              return (
         
     | 
| 
         | 
|
| 248 | 
         
             
                                disabled={isLoadingFilters}
         
     | 
| 249 | 
         
             
                              />
         
     | 
| 250 | 
         
             
                            </Container>
         
     | 
| 251 | 
         
            +
             
     | 
| 252 | 
         
            +
                            <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
         
     | 
| 253 | 
         
            +
                              <SelectInput
         
     | 
| 254 | 
         
            +
                                name="imageType"
         
     | 
| 255 | 
         
            +
                                placeholder={isLoadingFilters ? "Loading..." : "All Image Types"}
         
     | 
| 256 | 
         
            +
                                options={imageTypes}
         
     | 
| 257 | 
         
            +
                                value={imageTypeFilter || null}
         
     | 
| 258 | 
         
            +
                                onChange={(v) => setImageTypeFilter(v as string || '')}
         
     | 
| 259 | 
         
            +
                                keySelector={(o) => o.image_type}
         
     | 
| 260 | 
         
            +
                                labelSelector={(o) => o.label}
         
     | 
| 261 | 
         
            +
                                required={false}
         
     | 
| 262 | 
         
            +
                                disabled={isLoadingFilters}
         
     | 
| 263 | 
         
            +
                              />
         
     | 
| 264 | 
         
            +
                            </Container>
         
     | 
| 265 | 
         
             
                          </div>
         
     | 
| 266 | 
         
             
                        </div>
         
     | 
| 267 | 
         | 
| 
         | 
|
| 271 | 
         
             
                            <p className="text-sm text-gray-600">
         
     | 
| 272 | 
         
             
                              {filtered.length} of {captions.length} examples
         
     | 
| 273 | 
         
             
                            </p>
         
     | 
| 274 | 
         
            +
                            <Button
         
     | 
| 275 | 
         
            +
                              name="clear-filters"
         
     | 
| 276 | 
         
            +
                              variant="secondary"
         
     | 
| 277 | 
         
            +
                              onClick={() => {
         
     | 
| 278 | 
         
            +
                                setSearch('');
         
     | 
| 279 | 
         
            +
                                setSrcFilter('');
         
     | 
| 280 | 
         
            +
                                setCatFilter('');
         
     | 
| 281 | 
         
            +
                                setRegionFilter('');
         
     | 
| 282 | 
         
            +
                                setCountryFilter('');
         
     | 
| 283 | 
         
            +
                                setImageTypeFilter('');
         
     | 
| 284 | 
         
            +
                              }}
         
     | 
| 285 | 
         
            +
                            >
         
     | 
| 286 | 
         
            +
                              Clear Filters
         
     | 
| 287 | 
         
            +
                            </Button>
         
     | 
| 288 | 
         
             
                          </div>
         
     | 
| 289 | 
         | 
| 290 | 
         
             
                          {/* Loading State */}
         
     | 
    	
        frontend/src/pages/MapDetailsPage/MapDetailPage.tsx
    CHANGED
    
    | 
         @@ -55,6 +55,7 @@ export default function MapDetailPage() { 
     | 
|
| 55 | 
         
             
              const [catFilter, setCatFilter] = useState('');
         
     | 
| 56 | 
         
             
              const [regionFilter, setRegionFilter] = useState('');
         
     | 
| 57 | 
         
             
              const [countryFilter, setCountryFilter] = useState('');
         
     | 
| 
         | 
|
| 58 | 
         | 
| 59 | 
         
             
              const viewOptions = [
         
     | 
| 60 | 
         
             
                { key: 'explore' as const, label: 'Explore' },
         
     | 
| 
         @@ -164,9 +165,10 @@ export default function MapDetailPage() { 
     | 
|
| 164 | 
         
             
                  map.countries.some(country => country.r_code === regionFilter);
         
     | 
| 165 | 
         
             
                const matchesCountry = !countryFilter || 
         
     | 
| 166 | 
         
             
                  map.countries.some(country => country.c_code === countryFilter);
         
     | 
| 
         | 
|
| 167 | 
         | 
| 168 | 
         
            -
                return matchesSearch && matchesSource && matchesCategory && matchesRegion && matchesCountry ? map : null;
         
     | 
| 169 | 
         
            -
              }, [map, search, srcFilter, catFilter, regionFilter, countryFilter]);
         
     | 
| 170 | 
         | 
| 171 | 
         
             
              const handleContribute = async () => {
         
     | 
| 172 | 
         
             
                if (!map) return;
         
     | 
| 
         @@ -346,6 +348,19 @@ export default function MapDetailPage() { 
     | 
|
| 346 | 
         
             
                            labelSelector={(o) => o.label}
         
     | 
| 347 | 
         
             
                          />
         
     | 
| 348 | 
         
             
                        </Container>
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 349 | 
         
             
                      </div>
         
     | 
| 350 | 
         
             
                    </div>
         
     | 
| 351 | 
         | 
| 
         @@ -461,7 +476,14 @@ export default function MapDetailPage() { 
     | 
|
| 461 | 
         
             
                                      onClick={handleContribute}
         
     | 
| 462 | 
         
             
                                      disabled={isGenerating}
         
     | 
| 463 | 
         
             
                                    >
         
     | 
| 464 | 
         
            -
                                      {isGenerating ?  
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 465 | 
         
             
                                    </Button>
         
     | 
| 466 | 
         
             
                                  </Container>
         
     | 
| 467 | 
         | 
| 
         @@ -506,6 +528,7 @@ export default function MapDetailPage() { 
     | 
|
| 506 | 
         
             
                                  setCatFilter('');
         
     | 
| 507 | 
         
             
                                  setRegionFilter('');
         
     | 
| 508 | 
         
             
                                  setCountryFilter('');
         
     | 
| 
         | 
|
| 509 | 
         
             
                                }}
         
     | 
| 510 | 
         
             
                              >
         
     | 
| 511 | 
         
             
                                Clear Filters
         
     | 
| 
         | 
|
| 55 | 
         
             
              const [catFilter, setCatFilter] = useState('');
         
     | 
| 56 | 
         
             
              const [regionFilter, setRegionFilter] = useState('');
         
     | 
| 57 | 
         
             
              const [countryFilter, setCountryFilter] = useState('');
         
     | 
| 58 | 
         
            +
              const [imageTypeFilter, setImageTypeFilter] = useState('');
         
     | 
| 59 | 
         | 
| 60 | 
         
             
              const viewOptions = [
         
     | 
| 61 | 
         
             
                { key: 'explore' as const, label: 'Explore' },
         
     | 
| 
         | 
|
| 165 | 
         
             
                  map.countries.some(country => country.r_code === regionFilter);
         
     | 
| 166 | 
         
             
                const matchesCountry = !countryFilter || 
         
     | 
| 167 | 
         
             
                  map.countries.some(country => country.c_code === countryFilter);
         
     | 
| 168 | 
         
            +
                const matchesImageType = !imageTypeFilter || map.image_type === imageTypeFilter;
         
     | 
| 169 | 
         | 
| 170 | 
         
            +
                return matchesSearch && matchesSource && matchesCategory && matchesRegion && matchesCountry && matchesImageType ? map : null;
         
     | 
| 171 | 
         
            +
              }, [map, search, srcFilter, catFilter, regionFilter, countryFilter, imageTypeFilter]);
         
     | 
| 172 | 
         | 
| 173 | 
         
             
              const handleContribute = async () => {
         
     | 
| 174 | 
         
             
                if (!map) return;
         
     | 
| 
         | 
|
| 348 | 
         
             
                            labelSelector={(o) => o.label}
         
     | 
| 349 | 
         
             
                          />
         
     | 
| 350 | 
         
             
                        </Container>
         
     | 
| 351 | 
         
            +
             
     | 
| 352 | 
         
            +
                        <Container withInternalPadding className="bg-white/20 backdrop-blur-sm rounded-md p-2">
         
     | 
| 353 | 
         
            +
                          <SelectInput
         
     | 
| 354 | 
         
            +
                            name="imageType"
         
     | 
| 355 | 
         
            +
                            placeholder="All Image Types"
         
     | 
| 356 | 
         
            +
                            options={imageTypes}
         
     | 
| 357 | 
         
            +
                            value={imageTypeFilter || null}
         
     | 
| 358 | 
         
            +
                            onChange={(v) => setImageTypeFilter(v as string || '')}
         
     | 
| 359 | 
         
            +
                            keySelector={(o) => o.image_type}
         
     | 
| 360 | 
         
            +
                            labelSelector={(o) => o.label}
         
     | 
| 361 | 
         
            +
                            required={false}
         
     | 
| 362 | 
         
            +
                          />
         
     | 
| 363 | 
         
            +
                        </Container>
         
     | 
| 364 | 
         
             
                      </div>
         
     | 
| 365 | 
         
             
                    </div>
         
     | 
| 366 | 
         | 
| 
         | 
|
| 476 | 
         
             
                                      onClick={handleContribute}
         
     | 
| 477 | 
         
             
                                      disabled={isGenerating}
         
     | 
| 478 | 
         
             
                                    >
         
     | 
| 479 | 
         
            +
                                      {isGenerating ? (
         
     | 
| 480 | 
         
            +
                                        <div className="flex items-center gap-2">
         
     | 
| 481 | 
         
            +
                                          <Spinner className="text-white" />
         
     | 
| 482 | 
         
            +
                                          <span>Generating...</span>
         
     | 
| 483 | 
         
            +
                                        </div>
         
     | 
| 484 | 
         
            +
                                      ) : (
         
     | 
| 485 | 
         
            +
                                        'Contribute'
         
     | 
| 486 | 
         
            +
                                      )}
         
     | 
| 487 | 
         
             
                                    </Button>
         
     | 
| 488 | 
         
             
                                  </Container>
         
     | 
| 489 | 
         | 
| 
         | 
|
| 528 | 
         
             
                                  setCatFilter('');
         
     | 
| 529 | 
         
             
                                  setRegionFilter('');
         
     | 
| 530 | 
         
             
                                  setCountryFilter('');
         
     | 
| 531 | 
         
            +
                                  setImageTypeFilter('');
         
     | 
| 532 | 
         
             
                                }}
         
     | 
| 533 | 
         
             
                              >
         
     | 
| 534 | 
         
             
                                Clear Filters
         
     |