diff --git a/frontend/nyaysetu-frontend/src/components/dashboard/AdvancedCaseSearch.jsx b/frontend/nyaysetu-frontend/src/components/dashboard/AdvancedCaseSearch.jsx
new file mode 100644
index 000000000..c706223c4
--- /dev/null
+++ b/frontend/nyaysetu-frontend/src/components/dashboard/AdvancedCaseSearch.jsx
@@ -0,0 +1,183 @@
+import { Search, Filter } from "lucide-react";
+
+export default function AdvancedCaseSearch({
+ searchTerm,
+ setSearchTerm,
+ filterStatus,
+ setFilterStatus,
+ caseTypeFilter,
+ setCaseTypeFilter,
+ dateFilter,
+ setDateFilter,
+ recentSearches,
+ showSuggestions,
+ setShowSuggestions,
+ clearFilters
+}) {
+ return (
+
+ {/* Search Input */}
+
+
+
+
setSearchTerm(e.target.value)}
+ onFocus={() => setShowSuggestions(true)}
+ onBlur={() =>
+ setTimeout(() => setShowSuggestions(false), 200)
+ }
+ style={{
+ width: "100%",
+ padding: "0.8rem 1rem 0.8rem 3rem",
+ background: "var(--bg-glass)",
+ border: "var(--border-glass)",
+ borderRadius: "0.75rem",
+ color: "var(--text-main)",
+ outline: "none",
+ }}
+ />
+
+ {/* Recent Suggestions */}
+ {showSuggestions && recentSearches.length > 0 && (
+
+
+ Recent Searches
+
+
+ {recentSearches.map((item) => (
+
{
+ setSearchTerm(item);
+ setShowSuggestions(false);
+ }}
+ style={{
+ padding: "0.7rem",
+ cursor: "pointer",
+ borderRadius: "0.5rem",
+ }}
+ >
+ 🔍 {item}
+
+ ))}
+
+ )}
+
+
+ {/* Status Filter */}
+
+
+
+
+
+
+ {/* Case Type Filter */}
+
+
+ {/* Date Filter */}
+
setDateFilter(e.target.value)}
+ style={{
+ padding: "0.8rem",
+ borderRadius: "0.75rem",
+ background: "var(--bg-glass)",
+ border: "var(--border-glass)",
+ color: "var(--text-main)",
+ }}
+ />
+
+ {/* Clear Button */}
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/nyaysetu-frontend/src/components/dashboard/CaseCard.jsx b/frontend/nyaysetu-frontend/src/components/dashboard/CaseCard.jsx
index 342ad07c4..83c1b3a05 100644
--- a/frontend/nyaysetu-frontend/src/components/dashboard/CaseCard.jsx
+++ b/frontend/nyaysetu-frontend/src/components/dashboard/CaseCard.jsx
@@ -1,158 +1,188 @@
-/**
- * CaseCard — Reusable card component for displaying a legal case summary.
- *
- * Props:
- * id {string} Short display ID (e.g. "CS-a1b2c3")
- * title {string} Case title
- * status {string} Case status: "PENDING" | "OPEN" | "CLOSED" | "DRAFT_PENDING_CLIENT" | etc.
- * date {string} Filed date formatted as a locale string
- * onClick {function} Called when the card is clicked
- * filedLabel {string} Label text for the date row (e.g. "Filed")
- */
+ /**
+ * CaseCard — Reusable card component for displaying a legal case summary.
+ *
+ * Props:
+ * id {string} Short display ID (e.g. "CS-a1b2c3")
+ * title {string} Case title
+ * status {string} Case status: "PENDING" | "OPEN" | "CLOSED" | "DRAFT_PENDING_CLIENT" | etc.
+ * date {string} Filed date formatted as a locale string
+ * onClick {function} Called when the card is clicked
+ * filedLabel {string} Label text for the date row (e.g. "Filed")
+ */
-import { useState } from 'react';
-import { FolderOpen } from 'lucide-react';
+ import { useState } from 'react';
+ import { FolderOpen } from 'lucide-react';
-// Map each status to its colour palette using CSS-variable-safe values so
-// the card automatically adapts to light/dark mode via ThemeContext.
-const STATUS_STYLES = {
- PENDING: {
- background: 'rgba(245, 158, 11, 0.1)',
- color: '#f59e0b',
- border: 'rgba(245, 158, 11, 0.25)',
- },
- OPEN: {
- background: 'rgba(59, 130, 246, 0.1)',
- color: '#3b82f6',
- border: 'rgba(59, 130, 246, 0.25)',
- },
- CLOSED: {
- background: 'rgba(16, 185, 129, 0.1)',
- color: '#10b981',
- border: 'rgba(16, 185, 129, 0.25)',
- },
- DRAFT_PENDING_CLIENT: {
- background: 'rgba(139, 92, 246, 0.1)',
- color: '#8b5cf6',
- border: 'rgba(139, 92, 246, 0.25)',
- },
-};
+ // Map each status to its colour palette using CSS-variable-safe values so
+ // the card automatically adapts to light/dark mode via ThemeContext.
+ const STATUS_STYLES = {
+ PENDING: {
+ background: 'rgba(245, 158, 11, 0.1)',
+ color: '#f59e0b',
+ border: 'rgba(245, 158, 11, 0.25)',
+ },
+ OPEN: {
+ background: 'rgba(59, 130, 246, 0.1)',
+ color: '#3b82f6',
+ border: 'rgba(59, 130, 246, 0.25)',
+ },
+ CLOSED: {
+ background: 'rgba(16, 185, 129, 0.1)',
+ color: '#10b981',
+ border: 'rgba(16, 185, 129, 0.25)',
+ },
+ DRAFT_PENDING_CLIENT: {
+ background: 'rgba(139, 92, 246, 0.1)',
+ color: '#8b5cf6',
+ border: 'rgba(139, 92, 246, 0.25)',
+ },
+ };
-// Fallback for any unrecognised status value
-const DEFAULT_STATUS_STYLE = {
- background: 'rgba(100, 116, 139, 0.1)',
- color: '#64748b',
- border: 'rgba(100, 116, 139, 0.25)',
-};
+ // Fallback for any unrecognised status value
+ const DEFAULT_STATUS_STYLE = {
+ background: 'rgba(100, 116, 139, 0.1)',
+ color: '#64748b',
+ border: 'rgba(100, 116, 139, 0.25)',
+ };
-export default function CaseCard({ id, title, status, date, onClick, filedLabel = 'Filed', children }) {
- const [isHovered, setIsHovered] = useState(false);
+ export default function CaseCard({ id, title, status, date, caseType, courtName, assignedPerson, onClick, filedLabel = 'Filed', children }) {
+ const [isHovered, setIsHovered] = useState(false);
- const statusStyle = STATUS_STYLES[status] || DEFAULT_STATUS_STYLE;
+ const statusStyle = STATUS_STYLES[status] || DEFAULT_STATUS_STYLE;
- return (
- e.key === 'Enter' && onClick?.()}
- onMouseEnter={() => setIsHovered(true)}
- onMouseLeave={() => setIsHovered(false)}
- style={{
- padding: '1rem',
- background: 'var(--bg-glass)',
- borderRadius: '0.75rem',
- border: isHovered
- ? `1px solid var(--color-primary)`
- : 'var(--border-glass)',
- cursor: 'pointer',
- transition: 'all 0.2s ease',
- transform: isHovered ? 'translateY(-2px)' : 'translateY(0)',
- boxShadow: isHovered
- ? '0 6px 20px rgba(30, 42, 68, 0.1)'
- : 'none',
- outline: 'none',
- }}
- >
- {/* Top row — case ID + status badge */}
+ return (
e.key === 'Enter' && onClick?.()}
+ onMouseEnter={() => setIsHovered(true)}
+ onMouseLeave={() => setIsHovered(false)}
style={{
- display: 'flex',
- justifyContent: 'space-between',
- alignItems: 'center',
- marginBottom: '0.5rem',
- gap: '0.5rem',
+ padding: '1rem',
+ background: 'var(--bg-glass)',
+ borderRadius: '0.75rem',
+ border: isHovered
+ ? `1px solid var(--color-primary)`
+ : 'var(--border-glass)',
+ cursor: 'pointer',
+ transition: 'all 0.2s ease',
+ transform: isHovered ? 'translateY(-2px)' : 'translateY(0)',
+ boxShadow: isHovered
+ ? '0 6px 20px rgba(30, 42, 68, 0.1)'
+ : 'none',
+ outline: 'none',
}}
>
- {/* Case ID */}
-
-
- {id}
-
+ {/* Case ID */}
+
+
+ {id}
+
+
+ {/* Status pill */}
+
+ {status}
+
+
- {/* Status pill */}
-
- {status}
-
-
+ {title}
+
- {/* Case title */}
-
- {title}
-
+
+
+ {filedLabel}: {date}
+
- {/* Filed date */}
-
- {filedLabel}: {date}
-
+ {caseType && (
+
+ Type:
+
+ {' '}{caseType}
+
+
+ )}
- {/* Extra content (e.g. CaseStepper) */}
- {children && (
-
- {children}
-
- )}
-
- );
-}
+ {courtName && (
+
+ Court:
+
+ {' '}{courtName}
+
+
+ )}
+
+ {assignedPerson && (
+
+ Assigned:
+
+ {' '}{assignedPerson}
+
+
+ )}
+
+
+ {/* Extra content (e.g. CaseStepper) */}
+ {children && (
+
+ {children}
+
+ )}
+
+ );
+ }
diff --git a/frontend/nyaysetu-frontend/src/pages/lawyer/LawyerCasesPage.jsx b/frontend/nyaysetu-frontend/src/pages/lawyer/LawyerCasesPage.jsx
index 84fb86fcb..bdd290696 100644
--- a/frontend/nyaysetu-frontend/src/pages/lawyer/LawyerCasesPage.jsx
+++ b/frontend/nyaysetu-frontend/src/pages/lawyer/LawyerCasesPage.jsx
@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { caseAPI, lawyerAPI, caseAssignmentAPI } from '../../services/api';
+import AdvancedCaseSearch from "../../components/dashboard/AdvancedCaseSearch";
import {
Search,
Filter,
@@ -29,6 +30,10 @@ export default function LawyerCasesPage() {
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
const [filterStatus, setFilterStatus] = useState('ALL');
+ const [caseTypeFilter, setCaseTypeFilter] = useState('ALL');
+ const [dateFilter, setDateFilter] = useState('');
+ const [recentSearches, setRecentSearches] = useState([]);
+ const [showSuggestions, setShowSuggestions] = useState(false);
const [activeTab, setActiveTab] = useState('active'); // 'active' or 'proposals'
const [showAllSearchResults, setShowAllSearchResults] = useState(false);
const navigate = useNavigate();
@@ -96,19 +101,48 @@ export default function LawyerCasesPage() {
const displayCases = activeTab === 'active' ? activeCases : proposals;
const filteredCases = displayCases.filter(c => {
- const matchesSearch = (c.title?.toLowerCase().includes(searchTerm.toLowerCase()) ||
- c.description?.toLowerCase().includes(searchTerm.toLowerCase()));
- const matchesFilter = filterStatus === 'ALL' || c.status === filterStatus;
- return matchesSearch && matchesFilter;
- });
+ const search = searchTerm.toLowerCase();
+
+ const matchesSearch =
+ c.title?.toLowerCase().includes(search) ||
+ c.description?.toLowerCase().includes(search) ||
+ c.id?.toLowerCase().includes(search) ||
+ c.caseType?.toLowerCase().includes(search) ||
+ c.petitioner?.toLowerCase().includes(search);
+
+ const matchesStatus =
+ filterStatus === 'ALL' || c.status === filterStatus;
+
+ const matchesType =
+ caseTypeFilter === 'ALL' || c.caseType === caseTypeFilter;
+
+ const matchesDate =
+ !dateFilter ||
+ new Date(c.filedDate || c.createdAt)
+ .toISOString()
+ .split('T')[0] === dateFilter;
+
+ return (
+ matchesSearch &&
+ matchesStatus &&
+ matchesType &&
+ matchesDate
+ );
+});
const previewLimit = 4;
const visibleCases = showAllSearchResults ? filteredCases : filteredCases.slice(0, previewLimit);
const hiddenCases = showAllSearchResults ? [] : filteredCases.slice(previewLimit);
useEffect(() => {
- setShowAllSearchResults(false);
- }, [searchTerm, filterStatus, activeTab]);
+ setShowAllSearchResults(false);
+}, [
+ searchTerm,
+ filterStatus,
+ caseTypeFilter,
+ dateFilter,
+ activeTab
+]);
const glassStyle = {
background: 'var(--bg-glass-strong)',
@@ -119,6 +153,18 @@ export default function LawyerCasesPage() {
boxShadow: 'var(--shadow-glass-strong)'
};
+ useEffect(() => {
+ if (
+ searchTerm.trim() &&
+ !recentSearches.includes(searchTerm)
+ ) {
+ setRecentSearches(prev => [
+ searchTerm,
+ ...prev
+ ].slice(0, 5));
+ }
+}, [searchTerm, recentSearches]);
+
if (loading) {
return (
@@ -244,6 +290,10 @@ export default function LawyerCasesPage() {
placeholder={`Search ${activeTab === 'active' ? 'active portfolio' : 'proposals'}...`}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
+ onFocus={() => setShowSuggestions(true)}
+ onBlur={() =>
+ setTimeout(() => setShowSuggestions(false), 200)
+ }
style={{
width: '100%',
background: 'var(--bg-glass)',
@@ -255,6 +305,54 @@ export default function LawyerCasesPage() {
fontSize: '0.95rem'
}}
/>
+ {
+ showSuggestions &&
+ recentSearches.length > 0 && (
+
+
+ Recent Searches
+
+
+ {
+ recentSearches.map(item => (
+
{
+ setSearchTerm(item);
+ setShowSuggestions(false);
+ }}
+ style={{
+ padding: "0.7rem",
+ cursor: "pointer",
+ borderRadius: "0.5rem"
+ }}
+ >
+ 🔍 {item}
+
+ ))
+ }
+
+ )
+}
{activeTab === 'active' && (
@@ -279,6 +377,52 @@ export default function LawyerCasesPage() {
+
+ setDateFilter(e.target.value)}
+ style={{
+ background: 'var(--bg-glass)',
+ border: 'var(--border-glass)',
+ borderRadius: '0.75rem',
+ padding: '0.8rem',
+ color: 'var(--text-main)',
+ outline: 'none'
+ }}
+/>
)}