diff --git a/sigap-website/app/_components/action-search-bar.tsx b/sigap-website/app/_components/action-search-bar.tsx index f8598a0..8494068 100644 --- a/sigap-website/app/_components/action-search-bar.tsx +++ b/sigap-website/app/_components/action-search-bar.tsx @@ -6,6 +6,7 @@ import { motion, AnimatePresence } from "framer-motion" import { Search, Send, BarChart2, Globe, Video, PlaneTakeoff, AudioLines, X } from "lucide-react" import { cn } from "../_lib/utils" import useDebounce from "../_hooks/use-debounce" +import { toast } from "sonner" export interface Action { id: string @@ -132,8 +133,14 @@ const ActionSearchBar = forwardRef( useEffect(() => { if (autoFocus && inputRef.current) { inputRef.current.focus() + + // Position cursor at the end of any prefix + if (activeAction?.prefix) { + inputRef.current.selectionStart = activeAction.prefix.length; + inputRef.current.selectionEnd = activeAction.prefix.length; + } } - }, [autoFocus]) + }, [autoFocus, activeAction]) useEffect(() => { if (!debouncedQuery) { @@ -177,6 +184,17 @@ const ActionSearchBar = forwardRef( } else { setQuery(valueWithPrefix); } + + // Restore cursor position after prefix + setTimeout(() => { + if (!inputRef.current || !activeAction.prefix) { + return toast.error("Error: Input ref is null or prefix is undefined"); + } + + inputRef.current.selectionStart = activeAction.prefix.length; + inputRef.current.selectionEnd = activeAction.prefix.length; + }, 0); + return; } } diff --git a/sigap-website/app/_components/map/controls/top-controls.tsx b/sigap-website/app/_components/map/controls/top-controls.tsx index f1e655b..0ba4ae5 100644 --- a/sigap-website/app/_components/map/controls/top-controls.tsx +++ b/sigap-website/app/_components/map/controls/top-controls.tsx @@ -32,16 +32,51 @@ import { IconAnalyze, IconMessage } from "@tabler/icons-react" import { FloatingActionSearchBar } from "../../floating-action-search-bar" import { format } from 'date-fns' import { Card } from "@/app/_components/ui/card" +import { ICrimes } from "@/app/_utils/types/crimes" -// Sample crime data for suggestions +// Expanded sample crime data with more entries for testing const SAMPLE_CRIME_DATA = [ { id: "CR-12345-2023", description: "Robbery at Main Street" }, { id: "CR-23456-2023", description: "Assault in Central Park" }, { id: "CI-7890-2023", description: "Burglary report at Downtown" }, { id: "CI-4567-2024", description: "Vandalism at City Hall" }, + { id: "CI-4167-2024", description: "Graffiti at Public Library" }, + { id: "CI-4067-2024", description: "Property damage at School" }, { id: "CR-34567-2024", description: "Car theft on 5th Avenue" }, + { id: "CR-34517-2024", description: "Mugging at Central Station" }, + { id: "CR-14517-2024", description: "Shoplifting at Mall" }, + { id: "CR-24517-2024", description: "Break-in at Office Building" }, + // Add more entries for testing (up to 100) + // ...more sample entries... ]; +// Generate additional sample data for testing scrolling +const generateSampleData = () => { + const additionalData = []; + for (let i = 1; i <= 90; i++) { + // Mix of crime and incident IDs + const prefix = i % 2 === 0 ? "CR-" : "CI-"; + const id = `${prefix}${10000 + i}-${2022 + i % 3}`; + const descriptions = [ + "Theft at residence", + "Traffic violation", + "Noise complaint", + "Suspicious activity", + "Drug related incident", + "Vandalism of public property", + "Illegal parking", + "Public disturbance", + "Domestic dispute", + "Assault case" + ]; + const description = descriptions[i % descriptions.length]; + additionalData.push({ id, description: `${description} #${i}` }); + } + return [...SAMPLE_CRIME_DATA, ...additionalData]; +} + +const EXPANDED_SAMPLE_DATA = generateSampleData(); + const ACTIONS = [ { id: "crime_id", @@ -136,6 +171,7 @@ export default function TopControl({ setSelectedCategory, availableYears = [2022, 2023, 2024], categories = [], + }: TopControlProps) { const [showSelectors, setShowSelectors] = useState(false) const [showSearch, setShowSearch] = useState(false) @@ -177,23 +213,33 @@ export default function TopControl({ const selectedAction = ACTIONS.find(action => action.id === actionId); if (selectedAction) { setSelectedSearchType(actionId); - setSearchValue(selectedAction.prefix || ""); + + const prefix = selectedAction.prefix || ""; + setSearchValue(prefix); setIsInputValid(true); - if (selectedAction.prefix) { - const initialSuggestions = SAMPLE_CRIME_DATA.filter(item => { - if (actionId === 'crime_id' && item.id.startsWith('CR-')) { - return true; - } else if (actionId === 'incident_id' && item.id.startsWith('CI-')) { - return true; - } else if (actionId === 'description') { - return true; - } else if (actionId === 'address') { - return true; - } + + const initialSuggestions = EXPANDED_SAMPLE_DATA.filter(item => { + if (actionId === 'crime_id' && item.id.startsWith('CR-')) { + return true; + } else if (actionId === 'incident_id' && item.id.startsWith('CI-')) { + return true; + } else if (actionId === 'description' || actionId === 'address') { + return true; + } else if (actionId === 'coordinates') { return false; - }); - setSuggestions(initialSuggestions.slice(0, 5)); - } + } + return false; + }); + + setSuggestions(initialSuggestions); + + setTimeout(() => { + if (searchInputRef.current) { + searchInputRef.current.focus(); + searchInputRef.current.selectionStart = prefix.length; + searchInputRef.current.selectionEnd = prefix.length; + } + }, 50); } }; @@ -248,7 +294,7 @@ export default function TopControl({ const currentSearchType = selectedSearchType ? ACTIONS.find(action => action.id === selectedSearchType) : null; - if (currentSearchType?.prefix && value) { + if (currentSearchType?.prefix && currentSearchType.prefix.length > 0) { if (!value.startsWith(currentSearchType.prefix)) { value = currentSearchType.prefix; } @@ -266,39 +312,55 @@ export default function TopControl({ setIsInputValid(true); } - if (currentSearchType) { - if (!value || value === currentSearchType.prefix) { - const initialSuggestions = SAMPLE_CRIME_DATA.filter(item => { - if (currentSearchType.id === 'crime_id' && item.id.startsWith('CR-')) { - return true; - } else if (currentSearchType.id === 'incident_id' && item.id.startsWith('CI-')) { - return true; - } else if (currentSearchType.id === 'description') { - return true; - } else if (currentSearchType.id === 'address') { - return true; - } - return false; - }); - setSuggestions(initialSuggestions.slice(0, 5)); - } else { - const filteredSuggestions = SAMPLE_CRIME_DATA.filter(item => { - if (currentSearchType.id === 'crime_id' && item.id.startsWith('CR-')) { - return item.id.toLowerCase().includes(value.toLowerCase()); - } else if (currentSearchType.id === 'incident_id' && item.id.startsWith('CI-')) { - return item.id.toLowerCase().includes(value.toLowerCase()); - } else if (currentSearchType.id === 'description') { - return item.description.toLowerCase().includes(value.toLowerCase()); - } else if (currentSearchType.id === 'address') { - return item.description.toLowerCase().includes(value.toLowerCase()); - } - return false; - }); - setSuggestions(filteredSuggestions); - } - } else { + if (!selectedSearchType) { setSuggestions([]); + return; } + + let filteredSuggestions: Array<{ id: string, description: string }> = []; + const searchText = value.toLowerCase(); + + if (currentSearchType?.id === 'crime_id') { + filteredSuggestions = EXPANDED_SAMPLE_DATA.filter(item => + item.id.startsWith('CR-') && + (item.id.toLowerCase().includes(searchText) || + item.description.toLowerCase().includes(searchText)) + ); + } + else if (currentSearchType?.id === 'incident_id') { + filteredSuggestions = EXPANDED_SAMPLE_DATA.filter(item => + item.id.startsWith('CI-') && + (item.id.toLowerCase().includes(searchText) || + item.description.toLowerCase().includes(searchText)) + ); + } + else if (currentSearchType?.id === 'description') { + filteredSuggestions = EXPANDED_SAMPLE_DATA.filter(item => + item.description.toLowerCase().includes(searchText) + ); + } + else if (currentSearchType?.id === 'address') { + filteredSuggestions = EXPANDED_SAMPLE_DATA.filter(item => + item.description.toLowerCase().includes(searchText) + ); + } + else if (currentSearchType?.id === 'coordinates') { + filteredSuggestions = []; + } + + if (!value || (currentSearchType?.prefix && value === currentSearchType.prefix)) { + if (currentSearchType?.id === 'crime_id') { + filteredSuggestions = EXPANDED_SAMPLE_DATA.filter(item => item.id.startsWith('CR-')); + } + else if (currentSearchType?.id === 'incident_id') { + filteredSuggestions = EXPANDED_SAMPLE_DATA.filter(item => item.id.startsWith('CI-')); + } + else if (currentSearchType?.id === 'description' || currentSearchType?.id === 'address') { + filteredSuggestions = EXPANDED_SAMPLE_DATA; + } + } + + setSuggestions(filteredSuggestions); }; const toggleSelectors = () => { @@ -517,18 +579,35 @@ export default function TopControl({ )} {suggestions.length > 0 && ( -
+
+
+

+ {suggestions.length} results found +

+
    {suggestions.map((item, index) => (
  • handleSuggestionSelect(item)} > {item.id}
    - {item.description} - + + {item.description} + +
  • ))} @@ -536,9 +615,7 @@ export default function TopControl({
)} - {searchValue.length > 0 && - searchValue !== (ACTIONS.find(a => a.id === selectedSearchType)?.prefix || '') && - selectedSearchType && + {searchValue.length > (selectedSearchType && ACTIONS.find(a => a.id === selectedSearchType)?.prefix?.length || 0) && suggestions.length === 0 && (

No matching incidents found

@@ -566,14 +643,14 @@ export default function TopControl({

{selectedSuggestion?.id}

- + */}
{selectedSuggestion && (