Add search functionality
This commit is contained in:
66
src/components/SearchOverlay.tsx
Normal file
66
src/components/SearchOverlay.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { X } from 'lucide-react';
|
||||
|
||||
interface SearchOverlayProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSearch: (query: string) => void;
|
||||
}
|
||||
|
||||
const SearchOverlay = ({ isOpen, onClose, onSearch }: SearchOverlayProps) => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
// Reset search when opened
|
||||
setSearchQuery('');
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
if (isOpen) {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [isOpen, onClose]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed top-4 left-1/2 transform -translate-x-1/2 z-50 w-full max-w-lg">
|
||||
<div className="bg-gray-800 rounded-lg shadow-lg p-4 mx-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => {
|
||||
setSearchQuery(e.target.value);
|
||||
onSearch(e.target.value);
|
||||
}}
|
||||
className="flex-1"
|
||||
autoFocus
|
||||
/>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-2 hover:bg-gray-700 rounded-full"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchOverlay;
|
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Plus, Factory, TrendingUp, Briefcase, FileText } from 'lucide-react';
|
||||
@@ -10,6 +10,7 @@ import { IndJob } from '@/lib/types';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import BatchTransactionForm from '@/components/BatchTransactionForm';
|
||||
import { useJobs } from '@/hooks/useDataService';
|
||||
import SearchOverlay from '@/components/SearchOverlay';
|
||||
|
||||
const Index = () => {
|
||||
const {
|
||||
@@ -26,11 +27,25 @@ const Index = () => {
|
||||
const [showJobForm, setShowJobForm] = useState(false);
|
||||
const [editingJob, setEditingJob] = useState<IndJob | null>(null);
|
||||
const [showBatchForm, setShowBatchForm] = useState(false);
|
||||
const [searchOpen, setSearchOpen] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [collapsedGroups, setCollapsedGroups] = useState<Record<string, boolean>>(() => {
|
||||
const saved = localStorage.getItem('jobGroupsCollapsed');
|
||||
return saved ? JSON.parse(saved) : {};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
|
||||
e.preventDefault();
|
||||
setSearchOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-950 p-6 flex items-center justify-center">
|
||||
@@ -73,6 +88,14 @@ const Index = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const filterJobs = (jobs: IndJob[]) => {
|
||||
if (!searchQuery) return jobs;
|
||||
const query = searchQuery.toLowerCase();
|
||||
return jobs.filter(job =>
|
||||
job.outputItem.toLowerCase().includes(query)
|
||||
);
|
||||
};
|
||||
|
||||
const sortedJobs = [...jobs].sort((a, b) => {
|
||||
const priorityA = getStatusPriority(a.status);
|
||||
const priorityB = getStatusPriority(b.status);
|
||||
@@ -82,8 +105,8 @@ const Index = () => {
|
||||
return priorityA - priorityB;
|
||||
});
|
||||
|
||||
const regularJobs = sortedJobs.filter(job => job.status !== 'Tracked');
|
||||
const trackedJobs = sortedJobs.filter(job => job.status === 'Tracked');
|
||||
const regularJobs = filterJobs(sortedJobs.filter(job => job.status !== 'Tracked'));
|
||||
const trackedJobs = filterJobs(sortedJobs.filter(job => job.status === 'Tracked'));
|
||||
|
||||
const totalJobs = regularJobs.length;
|
||||
const totalProfit = regularJobs.reduce((sum, job) => {
|
||||
@@ -197,6 +220,14 @@ const Index = () => {
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-4 space-y-4">
|
||||
<SearchOverlay
|
||||
isOpen={searchOpen}
|
||||
onClose={() => {
|
||||
setSearchOpen(false);
|
||||
setSearchQuery('');
|
||||
}}
|
||||
onSearch={setSearchQuery}
|
||||
/>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<Card className="bg-gray-900 border-gray-700 text-white">
|
||||
<CardHeader>
|
||||
|
Reference in New Issue
Block a user