-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from Nine-Minds/new-documents-screen
New documents screen
- Loading branch information
Showing
7 changed files
with
333 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
"use client"; | ||
|
||
import { useState, useEffect, KeyboardEvent } from 'react'; | ||
import { IDocument } from '../../../interfaces/document.interface'; | ||
import Documents from '../../../components/documents/Documents'; | ||
import { Card } from '../../../components/ui/Card'; | ||
import { Input } from '../../../components/ui/Input'; | ||
import CustomSelect from '../../../components/ui/CustomSelect'; | ||
import { SelectOption } from '../../../components/ui/Select'; | ||
import { getAllDocuments } from '../../../lib/actions/document-actions/documentActions'; | ||
import { toast } from 'react-hot-toast'; | ||
|
||
export default function DocumentsPage() { | ||
const [documents, setDocuments] = useState<IDocument[]>([]); | ||
const [isLoading, setIsLoading] = useState(false); | ||
|
||
const [filterInputs, setFilterInputs] = useState({ | ||
type: 'all', | ||
entityType: '', | ||
searchTerm: '' | ||
}); | ||
|
||
const documentTypes: SelectOption[] = [ | ||
{ value: 'all', label: 'All Document Types' }, | ||
{ value: 'application/pdf', label: 'PDF' }, | ||
{ value: 'image', label: 'Images' }, | ||
{ value: 'text', label: 'Documents' }, | ||
{ value: 'application', label: 'Other' } | ||
]; | ||
|
||
const entityTypes: SelectOption[] = [ | ||
{ value: 'ticket', label: 'Tickets' }, | ||
{ value: 'client', label: 'Clients' }, | ||
{ value: 'contact', label: 'Contacts' }, | ||
{ value: 'project', label: 'Projects' } | ||
]; | ||
|
||
const handleSearch = async () => { | ||
try { | ||
setIsLoading(true); | ||
// Only include type in filters if it's not 'all' | ||
const searchFilters = { | ||
...filterInputs, | ||
type: filterInputs.type === 'all' ? '' : filterInputs.type | ||
}; | ||
const docs = await getAllDocuments(searchFilters); | ||
setDocuments(docs); | ||
} catch (error) { | ||
console.error('Error fetching documents:', error); | ||
toast.error('Failed to fetch documents'); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
// Run initial search on component mount | ||
useEffect(() => { | ||
handleSearch(); | ||
}, []); // Empty dependency array means this runs once on mount | ||
|
||
const handleKeyPress = (e: KeyboardEvent<HTMLInputElement>) => { | ||
if (e.key === 'Enter') { | ||
handleSearch(); | ||
} | ||
}; | ||
|
||
const handleDocumentUpdate = async () => { | ||
try { | ||
setIsLoading(true); | ||
const searchFilters = { | ||
...filterInputs, | ||
type: filterInputs.type === 'all' ? '' : filterInputs.type | ||
}; | ||
const updatedDocs = await getAllDocuments(searchFilters); | ||
setDocuments(updatedDocs); | ||
} catch (error) { | ||
console.error('Error refreshing documents:', error); | ||
toast.error('Failed to refresh documents'); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
const handleClearFilters = () => { | ||
setFilterInputs({ | ||
type: 'all', | ||
entityType: '', | ||
searchTerm: '' | ||
}); | ||
handleSearch(); | ||
}; | ||
|
||
return ( | ||
<div className="p-6"> | ||
<div className="mb-6"> | ||
<h1 className="text-2xl font-semibold">Documents</h1> | ||
</div> | ||
|
||
<div className="flex gap-6"> | ||
{/* Left Column - Filters */} | ||
<div className="w-80"> | ||
<Card className="p-4 sticky top-6"> | ||
<div className="space-y-4"> | ||
<div> | ||
<label className="block text-sm font-medium text-gray-700 mb-1"> | ||
Search Documents | ||
</label> | ||
<Input | ||
placeholder="Search by document name..." | ||
value={filterInputs.searchTerm} | ||
onChange={(e) => setFilterInputs({ ...filterInputs, searchTerm: e.target.value })} | ||
onKeyPress={handleKeyPress} | ||
/> | ||
</div> | ||
|
||
<div> | ||
<label className="block text-sm font-medium text-gray-700 mb-1"> | ||
Document Type | ||
</label> | ||
<CustomSelect | ||
options={documentTypes} | ||
value={filterInputs.type} | ||
onValueChange={(value: string) => { | ||
setFilterInputs({ ...filterInputs, type: value }); | ||
handleSearch(); | ||
}} | ||
/> | ||
</div> | ||
|
||
<div> | ||
<label className="block text-sm font-medium text-gray-700 mb-1"> | ||
Entity Type | ||
</label> | ||
<CustomSelect | ||
options={entityTypes} | ||
value={filterInputs.entityType} | ||
onValueChange={(value: string) => { | ||
setFilterInputs({ ...filterInputs, entityType: value }); | ||
handleSearch(); | ||
}} | ||
placeholder="All Entities" | ||
/> | ||
</div> | ||
|
||
<div className="pt-4"> | ||
<button | ||
onClick={handleClearFilters} | ||
className="w-full px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" | ||
> | ||
Clear Filters | ||
</button> | ||
</div> | ||
</div> | ||
</Card> | ||
</div> | ||
|
||
{/* Right Column - Documents */} | ||
<div className="flex-1"> | ||
<Card className="p-4"> | ||
<Documents | ||
documents={documents} | ||
gridColumns={3} | ||
userId="current-user-id" | ||
filters={filterInputs} | ||
isLoading={isLoading} | ||
onDocumentCreated={handleDocumentUpdate} | ||
/> | ||
</Card> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import React from 'react'; | ||
import { Label } from './Label'; | ||
import { Input } from './Input'; | ||
|
||
interface DateRange { | ||
from: string; | ||
to: string; | ||
} | ||
|
||
interface DateRangePickerProps { | ||
label?: string; | ||
value: DateRange; | ||
onChange: (range: DateRange) => void; | ||
} | ||
|
||
export const DateRangePicker: React.FC<DateRangePickerProps> = ({ | ||
label, | ||
value, | ||
onChange | ||
}) => { | ||
return ( | ||
<div className="space-y-2"> | ||
{label && <Label>{label}</Label>} | ||
<div className="flex gap-2"> | ||
<Input | ||
type="date" | ||
value={value.from} | ||
onChange={(e) => onChange({ ...value, from: e.target.value })} | ||
placeholder="From" | ||
/> | ||
<Input | ||
type="date" | ||
value={value.to} | ||
onChange={(e) => onChange({ ...value, to: e.target.value })} | ||
placeholder="To" | ||
/> | ||
</div> | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.