filer based on template_type added

This commit is contained in:
laxmanhalaki 2025-12-23 19:24:27 +05:30
parent ce90fcf9ef
commit 12f8affd15
28 changed files with 227 additions and 50 deletions

View File

@ -27,6 +27,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
searchTerm: filters.searchTerm, searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter, statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter, priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
sortBy: filters.sortBy, sortBy: filters.sortBy,
sortOrder: filters.sortOrder, sortOrder: filters.sortOrder,
}); });
@ -39,6 +40,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined, status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy, sortBy: filters.sortBy,
sortOrder: filters.sortOrder, sortOrder: filters.sortOrder,
}); });
@ -55,6 +57,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
prev.searchTerm !== filters.searchTerm || prev.searchTerm !== filters.searchTerm ||
prev.statusFilter !== filters.statusFilter || prev.statusFilter !== filters.statusFilter ||
prev.priorityFilter !== filters.priorityFilter || prev.priorityFilter !== filters.priorityFilter ||
prev.templateTypeFilter !== filters.templateTypeFilter ||
prev.sortBy !== filters.sortBy || prev.sortBy !== filters.sortBy ||
prev.sortOrder !== filters.sortOrder; prev.sortOrder !== filters.sortOrder;
@ -67,6 +70,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined, status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy, sortBy: filters.sortBy,
sortOrder: filters.sortOrder, sortOrder: filters.sortOrder,
}); });
@ -76,6 +80,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
searchTerm: filters.searchTerm, searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter, statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter, priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
sortBy: filters.sortBy, sortBy: filters.sortBy,
sortOrder: filters.sortOrder, sortOrder: filters.sortOrder,
}; };
@ -83,7 +88,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
return () => clearTimeout(timeoutId); return () => clearTimeout(timeoutId);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [filters.searchTerm, filters.statusFilter, filters.priorityFilter, filters.sortBy, filters.sortOrder]); }, [filters.searchTerm, filters.statusFilter, filters.priorityFilter, filters.templateTypeFilter, filters.sortBy, filters.sortOrder]);
// Page change handler // Page change handler
const handlePageChange = useCallback( const handlePageChange = useCallback(
@ -94,6 +99,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined, status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy, sortBy: filters.sortBy,
sortOrder: filters.sortOrder, sortOrder: filters.sortOrder,
}); });
@ -108,6 +114,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined, status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy, sortBy: filters.sortBy,
sortOrder: filters.sortOrder, sortOrder: filters.sortOrder,
}); });
@ -128,12 +135,14 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
searchTerm={filters.searchTerm} searchTerm={filters.searchTerm}
priorityFilter={filters.priorityFilter} priorityFilter={filters.priorityFilter}
statusFilter={filters.statusFilter} statusFilter={filters.statusFilter}
templateTypeFilter={filters.templateTypeFilter}
sortBy={filters.sortBy} sortBy={filters.sortBy}
sortOrder={filters.sortOrder} sortOrder={filters.sortOrder}
activeFiltersCount={filters.activeFiltersCount} activeFiltersCount={filters.activeFiltersCount}
onSearchChange={filters.setSearchTerm} onSearchChange={filters.setSearchTerm}
onPriorityChange={filters.setPriorityFilter} onPriorityChange={filters.setPriorityFilter}
onStatusChange={filters.setStatusFilter} onStatusChange={filters.setStatusFilter}
onTemplateTypeChange={filters.setTemplateTypeFilter}
onSortByChange={filters.setSortBy} onSortByChange={filters.setSortBy}
onSortOrderChange={() => filters.setSortOrder(filters.sortOrder === 'asc' ? 'desc' : 'asc')} onSortOrderChange={() => filters.setSortOrder(filters.sortOrder === 'asc' ? 'desc' : 'asc')}
onClearFilters={filters.clearFilters} onClearFilters={filters.clearFilters}

View File

@ -71,7 +71,7 @@ export function ClosedRequestCard({ request, onViewRequest }: ClosedRequestCardP
let templateColor = 'bg-purple-100 !text-purple-600 border-purple-200'; let templateColor = 'bg-purple-100 !text-purple-600 border-purple-200';
if (templateTypeUpper === 'DEALER CLAIM') { if (templateTypeUpper === 'DEALER CLAIM') {
templateLabel = 'Claim Management'; templateLabel = 'Dealer Claim';
templateColor = 'bg-blue-100 !text-blue-700 border-blue-200'; templateColor = 'bg-blue-100 !text-blue-700 border-blue-200';
} else if (templateTypeUpper === 'TEMPLATE') { } else if (templateTypeUpper === 'TEMPLATE') {
templateLabel = 'Template'; templateLabel = 'Template';

View File

@ -12,12 +12,14 @@ interface ClosedRequestsFiltersProps {
searchTerm: string; searchTerm: string;
priorityFilter: string; priorityFilter: string;
statusFilter: string; statusFilter: string;
templateTypeFilter: string;
sortBy: 'created' | 'due' | 'priority'; sortBy: 'created' | 'due' | 'priority';
sortOrder: 'asc' | 'desc'; sortOrder: 'asc' | 'desc';
activeFiltersCount: number; activeFiltersCount: number;
onSearchChange: (value: string) => void; onSearchChange: (value: string) => void;
onPriorityChange: (value: string) => void; onPriorityChange: (value: string) => void;
onStatusChange: (value: string) => void; onStatusChange: (value: string) => void;
onTemplateTypeChange: (value: string) => void;
onSortByChange: (value: 'created' | 'due' | 'priority') => void; onSortByChange: (value: 'created' | 'due' | 'priority') => void;
onSortOrderChange: () => void; onSortOrderChange: () => void;
onClearFilters: () => void; onClearFilters: () => void;
@ -27,12 +29,14 @@ export function ClosedRequestsFilters({
searchTerm, searchTerm,
priorityFilter, priorityFilter,
statusFilter, statusFilter,
templateTypeFilter,
sortBy, sortBy,
sortOrder, sortOrder,
activeFiltersCount, activeFiltersCount,
onSearchChange, onSearchChange,
onPriorityChange, onPriorityChange,
onStatusChange, onStatusChange,
onTemplateTypeChange,
onSortByChange, onSortByChange,
onSortOrderChange, onSortOrderChange,
onClearFilters, onClearFilters,
@ -71,7 +75,7 @@ export function ClosedRequestsFilters({
</div> </div>
</CardHeader> </CardHeader>
<CardContent className="space-y-3 sm:space-y-4 px-3 sm:px-6"> <CardContent className="space-y-3 sm:space-y-4 px-3 sm:px-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-3 sm:gap-4">
<div className="relative"> <div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-3.5 h-3.5 sm:w-4 sm:h-4" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-3.5 h-3.5 sm:w-4 sm:h-4" />
<Input <Input
@ -125,6 +129,17 @@ export function ClosedRequestsFilters({
</SelectContent> </SelectContent>
</Select> </Select>
<Select value={templateTypeFilter} onValueChange={onTemplateTypeChange}>
<SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white" data-testid="closed-requests-template-type-filter">
<SelectValue placeholder="All Templates" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Templates</SelectItem>
<SelectItem value="CUSTOM">Custom</SelectItem>
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
</SelectContent>
</Select>
<div className="flex gap-2"> <div className="flex gap-2">
<Select value={sortBy} onValueChange={(value) => onSortByChange(value as 'created' | 'due' | 'priority')}> <Select value={sortBy} onValueChange={(value) => onSortByChange(value as 'created' | 'due' | 'priority')}>
<SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white" data-testid="closed-requests-sort-by"> <SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white" data-testid="closed-requests-sort-by">

View File

@ -30,7 +30,7 @@ export function useClosedRequests({ itemsPerPage = 10 }: UseClosedRequestsOption
}); });
const fetchRequests = useCallback( const fetchRequests = useCallback(
async (page: number = 1, filters?: { search?: string; status?: string; priority?: string; sortBy?: string; sortOrder?: string }) => { async (page: number = 1, filters?: { search?: string; status?: string; priority?: string; templateType?: string; sortBy?: string; sortOrder?: string }) => {
try { try {
if (page === 1) { if (page === 1) {
setLoading(true); setLoading(true);
@ -51,6 +51,7 @@ export function useClosedRequests({ itemsPerPage = 10 }: UseClosedRequestsOption
search: filters?.search, search: filters?.search,
status: filters?.status && filters.status !== 'all' ? filters.status : undefined, status: filters?.status && filters.status !== 'all' ? filters.status : undefined,
priority: filters?.priority, priority: filters?.priority,
templateType: filters?.templateType,
sortBy: filters?.sortBy, sortBy: filters?.sortBy,
sortOrder: filters?.sortOrder sortOrder: filters?.sortOrder
}); });
@ -90,7 +91,7 @@ export function useClosedRequests({ itemsPerPage = 10 }: UseClosedRequestsOption
// Initial fetch removed - component handles initial fetch using Redux stored page // Initial fetch removed - component handles initial fetch using Redux stored page
// This prevents duplicate fetches and allows page persistence // This prevents duplicate fetches and allows page persistence
const handleRefresh = useCallback((filters?: { search?: string; status?: string; priority?: string; sortBy?: string; sortOrder?: string }) => { const handleRefresh = useCallback((filters?: { search?: string; status?: string; priority?: string; templateType?: string; sortBy?: string; sortOrder?: string }) => {
setRefreshing(true); setRefreshing(true);
fetchRequests(pagination.currentPage, filters); fetchRequests(pagination.currentPage, filters);
}, [fetchRequests, pagination.currentPage]); }, [fetchRequests, pagination.currentPage]);

View File

@ -9,6 +9,7 @@ import {
setSearchTerm as setSearchTermAction, setSearchTerm as setSearchTermAction,
setStatusFilter as setStatusFilterAction, setStatusFilter as setStatusFilterAction,
setPriorityFilter as setPriorityFilterAction, setPriorityFilter as setPriorityFilterAction,
setTemplateTypeFilter as setTemplateTypeFilterAction,
setSortBy as setSortByAction, setSortBy as setSortByAction,
setSortOrder as setSortOrderAction, setSortOrder as setSortOrderAction,
setCurrentPage as setCurrentPageAction, setCurrentPage as setCurrentPageAction,
@ -26,12 +27,13 @@ export function useClosedRequestsFilters({ onFiltersChange, debounceMs = 500 }:
const isInitialMount = useRef(true); const isInitialMount = useRef(true);
// Get filters from Redux // Get filters from Redux
const { searchTerm, statusFilter, priorityFilter, sortBy, sortOrder, currentPage } = useAppSelector((state) => state.closedRequests); const { searchTerm, statusFilter, priorityFilter, templateTypeFilter, sortBy, sortOrder, currentPage } = useAppSelector((state) => state.closedRequests);
// Create setters that dispatch Redux actions // Create setters that dispatch Redux actions
const setSearchTerm = useCallback((value: string) => dispatch(setSearchTermAction(value)), [dispatch]); const setSearchTerm = useCallback((value: string) => dispatch(setSearchTermAction(value)), [dispatch]);
const setStatusFilter = useCallback((value: string) => dispatch(setStatusFilterAction(value)), [dispatch]); const setStatusFilter = useCallback((value: string) => dispatch(setStatusFilterAction(value)), [dispatch]);
const setPriorityFilter = useCallback((value: string) => dispatch(setPriorityFilterAction(value)), [dispatch]); const setPriorityFilter = useCallback((value: string) => dispatch(setPriorityFilterAction(value)), [dispatch]);
const setTemplateTypeFilter = useCallback((value: string) => dispatch(setTemplateTypeFilterAction(value)), [dispatch]);
const setSortBy = useCallback((value: 'created' | 'due' | 'priority') => dispatch(setSortByAction(value)), [dispatch]); const setSortBy = useCallback((value: 'created' | 'due' | 'priority') => dispatch(setSortByAction(value)), [dispatch]);
const setSortOrder = useCallback((value: 'asc' | 'desc') => dispatch(setSortOrderAction(value)), [dispatch]); const setSortOrder = useCallback((value: 'asc' | 'desc') => dispatch(setSortOrderAction(value)), [dispatch]);
const setCurrentPage = useCallback((value: number) => dispatch(setCurrentPageAction(value)), [dispatch]); const setCurrentPage = useCallback((value: number) => dispatch(setCurrentPageAction(value)), [dispatch]);
@ -41,10 +43,11 @@ export function useClosedRequestsFilters({ onFiltersChange, debounceMs = 500 }:
search: searchTerm, search: searchTerm,
status: statusFilter, status: statusFilter,
priority: priorityFilter, priority: priorityFilter,
templateType: templateTypeFilter !== 'all' ? templateTypeFilter : undefined,
sortBy, sortBy,
sortOrder, sortOrder,
}; };
}, [searchTerm, statusFilter, priorityFilter, sortBy, sortOrder]); }, [searchTerm, statusFilter, priorityFilter, templateTypeFilter, sortBy, sortOrder]);
// Debounced filter change handler // Debounced filter change handler
useEffect(() => { useEffect(() => {
@ -71,7 +74,7 @@ export function useClosedRequestsFilters({ onFiltersChange, debounceMs = 500 }:
clearTimeout(debounceTimeoutRef.current); clearTimeout(debounceTimeoutRef.current);
} }
}; };
}, [searchTerm, statusFilter, priorityFilter, sortBy, sortOrder, onFiltersChange, getFilters, debounceMs]); }, [searchTerm, statusFilter, priorityFilter, templateTypeFilter, sortBy, sortOrder, onFiltersChange, getFilters, debounceMs]);
const clearFilters = useCallback(() => { const clearFilters = useCallback(() => {
dispatch(clearFiltersAction()); dispatch(clearFiltersAction());
@ -80,19 +83,22 @@ export function useClosedRequestsFilters({ onFiltersChange, debounceMs = 500 }:
const activeFiltersCount = [ const activeFiltersCount = [
searchTerm, searchTerm,
priorityFilter !== 'all' ? priorityFilter : null, priorityFilter !== 'all' ? priorityFilter : null,
statusFilter !== 'all' ? statusFilter : null statusFilter !== 'all' ? statusFilter : null,
templateTypeFilter !== 'all' ? templateTypeFilter : null
].filter(Boolean).length; ].filter(Boolean).length;
return { return {
searchTerm, searchTerm,
priorityFilter, priorityFilter,
statusFilter, statusFilter,
templateTypeFilter,
sortBy, sortBy,
sortOrder, sortOrder,
currentPage, currentPage,
setSearchTerm, setSearchTerm,
setPriorityFilter, setPriorityFilter,
setStatusFilter, setStatusFilter,
setTemplateTypeFilter,
setSortBy, setSortBy,
setSortOrder, setSortOrder,
setCurrentPage, setCurrentPage,

View File

@ -4,6 +4,7 @@ export interface ClosedRequestsFiltersState {
searchTerm: string; searchTerm: string;
statusFilter: string; statusFilter: string;
priorityFilter: string; priorityFilter: string;
templateTypeFilter: string;
sortBy: 'created' | 'due' | 'priority'; sortBy: 'created' | 'due' | 'priority';
sortOrder: 'asc' | 'desc'; sortOrder: 'asc' | 'desc';
currentPage: number; currentPage: number;
@ -13,6 +14,7 @@ const initialState: ClosedRequestsFiltersState = {
searchTerm: '', searchTerm: '',
statusFilter: 'all', statusFilter: 'all',
priorityFilter: 'all', priorityFilter: 'all',
templateTypeFilter: 'all',
sortBy: 'created', sortBy: 'created',
sortOrder: 'desc', sortOrder: 'desc',
currentPage: 1, currentPage: 1,
@ -31,6 +33,9 @@ const closedRequestsSlice = createSlice({
setPriorityFilter: (state, action: PayloadAction<string>) => { setPriorityFilter: (state, action: PayloadAction<string>) => {
state.priorityFilter = action.payload; state.priorityFilter = action.payload;
}, },
setTemplateTypeFilter: (state, action: PayloadAction<string>) => {
state.templateTypeFilter = action.payload;
},
setSortBy: (state, action: PayloadAction<'created' | 'due' | 'priority'>) => { setSortBy: (state, action: PayloadAction<'created' | 'due' | 'priority'>) => {
state.sortBy = action.payload; state.sortBy = action.payload;
}, },
@ -44,6 +49,7 @@ const closedRequestsSlice = createSlice({
state.searchTerm = ''; state.searchTerm = '';
state.statusFilter = 'all'; state.statusFilter = 'all';
state.priorityFilter = 'all'; state.priorityFilter = 'all';
state.templateTypeFilter = 'all';
state.currentPage = 1; state.currentPage = 1;
}, },
}, },
@ -53,6 +59,7 @@ export const {
setSearchTerm, setSearchTerm,
setStatusFilter, setStatusFilter,
setPriorityFilter, setPriorityFilter,
setTemplateTypeFilter,
setSortBy, setSortBy,
setSortOrder, setSortOrder,
setCurrentPage, setCurrentPage,

View File

@ -28,6 +28,7 @@ export interface ClosedRequestsFilters {
search: string; search: string;
status: string; status: string;
priority: string; priority: string;
templateType?: string;
sortBy: 'created' | 'due' | 'priority'; sortBy: 'created' | 'due' | 'priority';
sortOrder: 'asc' | 'desc'; sortOrder: 'asc' | 'desc';
} }

View File

@ -72,12 +72,14 @@ export function useDashboardData({
customEndDate?.toISOString(), customEndDate?.toISOString(),
undefined, // status undefined, // status
undefined, // priority undefined, // priority
undefined, // templateType
undefined, // department undefined, // department
userId, // initiator - filter by user's ID to get ONLY their initiated requests userId, // initiator - filter by user's ID to get ONLY their initiated requests
undefined, // approver undefined, // approver
undefined, // approverType undefined, // approverType
undefined, // search undefined, // search
undefined // slaCompliance undefined, // slaCompliance
viewAsUser // viewAsUser - treat as normal user even if admin
) )
: null; : null;

View File

@ -37,6 +37,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
searchTerm: filters.searchTerm, searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter, statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter, priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
}); });
const hasInitialFetchRun = useRef(false); const hasInitialFetchRun = useRef(false);
@ -47,6 +48,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined, status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
}); });
hasInitialFetchRun.current = true; hasInitialFetchRun.current = true;
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@ -60,7 +62,8 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
const hasChanged = const hasChanged =
prev.searchTerm !== filters.searchTerm || prev.searchTerm !== filters.searchTerm ||
prev.statusFilter !== filters.statusFilter || prev.statusFilter !== filters.statusFilter ||
prev.priorityFilter !== filters.priorityFilter; prev.priorityFilter !== filters.priorityFilter ||
prev.templateTypeFilter !== filters.templateTypeFilter;
if (!hasChanged) return; // No actual change, skip if (!hasChanged) return; // No actual change, skip
@ -71,6 +74,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined, status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
}); });
// Update previous values // Update previous values
@ -78,12 +82,13 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
searchTerm: filters.searchTerm, searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter, statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter, priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
}; };
}, filters.searchTerm !== prev.searchTerm ? 500 : 0); }, filters.searchTerm !== prev.searchTerm ? 500 : 0);
return () => clearTimeout(timeoutId); return () => clearTimeout(timeoutId);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [filters.searchTerm, filters.statusFilter, filters.priorityFilter]); }, [filters.searchTerm, filters.statusFilter, filters.priorityFilter, filters.templateTypeFilter]);
// State for backend stats (calculated from entire dataset via SQL queries) // State for backend stats (calculated from entire dataset via SQL queries)
const [backendStats, setBackendStats] = useState<{ const [backendStats, setBackendStats] = useState<{
@ -112,19 +117,21 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
// Even for admin users, we want to see only their own requests in "My Requests" // Even for admin users, we want to see only their own requests in "My Requests"
// This calculates stats from entire dataset using SQL COUNT queries, not by fetching data // This calculates stats from entire dataset using SQL COUNT queries, not by fetching data
// Note: status is undefined - stats should show all statuses, not filtered by status // Note: status is undefined - stats should show all statuses, not filtered by status
// Note: No date range filter - stats should match the list which shows all requests // Note: dateRange is 'all' - stats should match the list which shows all requests
const stats = await dashboardService.getRequestStats( const stats = await dashboardService.getRequestStats(
undefined, // dateRange - no date filter to match the list 'all', // dateRange - 'all' means no date filter to match the list
undefined, // startDate undefined, // startDate
undefined, // endDate undefined, // endDate
undefined, // status - My Requests stats show all statuses, not filtered by status (same as All Requests) undefined, // status - My Requests stats show all statuses, not filtered by status (same as All Requests)
filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined, // templateType
undefined, // department undefined, // department
user.userId, // initiator - explicitly filter by user's own userId to ensure only their requests user.userId, // initiator - explicitly filter by user's own userId to ensure only their requests
undefined, // approver undefined, // approver
undefined, // approverType undefined, // approverType
filters.searchTerm || undefined, filters.searchTerm || undefined,
undefined // slaCompliance undefined, // slaCompliance
true // viewAsUser - treat as normal user even if admin
); );
setBackendStats({ setBackendStats({
@ -142,7 +149,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
} finally { } finally {
setLoadingStats(false); setLoadingStats(false);
} }
}, [user?.userId, filters.searchTerm, filters.priorityFilter]); // Exclude statusFilter - stats don't change when only status changes }, [user?.userId, filters.searchTerm, filters.priorityFilter, filters.templateTypeFilter]); // Exclude statusFilter - stats don't change when only status changes
// Fetch stats when filters change (excluding status filter) // Fetch stats when filters change (excluding status filter)
// Stats should reflect priority and search filters, but NOT status filter // Stats should reflect priority and search filters, but NOT status filter
@ -153,7 +160,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
}, filters.searchTerm ? 500 : 0); }, filters.searchTerm ? 500 : 0);
return () => clearTimeout(timeoutId); return () => clearTimeout(timeoutId);
}, [filters.searchTerm, filters.priorityFilter, fetchBackendStats]); // Exclude statusFilter - stats don't change when only status changes }, [filters.searchTerm, filters.priorityFilter, filters.templateTypeFilter, fetchBackendStats]); // Exclude statusFilter - stats don't change when only status changes
// Handle dynamic requests (fallback until API loads) // Handle dynamic requests (fallback until API loads)
const convertedDynamicRequests = transformRequests(dynamicRequests); const convertedDynamicRequests = transformRequests(dynamicRequests);
@ -196,6 +203,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined, status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
}); });
} }
}, },
@ -230,9 +238,11 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
searchTerm={filters.searchTerm} searchTerm={filters.searchTerm}
statusFilter={filters.statusFilter} statusFilter={filters.statusFilter}
priorityFilter={filters.priorityFilter} priorityFilter={filters.priorityFilter}
templateTypeFilter={filters.templateTypeFilter}
onSearchChange={filters.setSearchTerm} onSearchChange={filters.setSearchTerm}
onStatusChange={filters.setStatusFilter} onStatusChange={filters.setStatusFilter}
onPriorityChange={filters.setPriorityFilter} onPriorityChange={filters.setPriorityFilter}
onTemplateTypeChange={filters.setTemplateTypeFilter}
/> />
{/* Requests List */} {/* Requests List */}

View File

@ -11,18 +11,22 @@ interface MyRequestsFiltersProps {
searchTerm: string; searchTerm: string;
statusFilter: string; statusFilter: string;
priorityFilter: string; priorityFilter: string;
templateTypeFilter: string;
onSearchChange: (value: string) => void; onSearchChange: (value: string) => void;
onStatusChange: (value: string) => void; onStatusChange: (value: string) => void;
onPriorityChange: (value: string) => void; onPriorityChange: (value: string) => void;
onTemplateTypeChange: (value: string) => void;
} }
export function MyRequestsFilters({ export function MyRequestsFilters({
searchTerm, searchTerm,
statusFilter, statusFilter,
priorityFilter, priorityFilter,
templateTypeFilter,
onSearchChange, onSearchChange,
onStatusChange, onStatusChange,
onPriorityChange, onPriorityChange,
onTemplateTypeChange,
}: MyRequestsFiltersProps) { }: MyRequestsFiltersProps) {
return ( return (
<Card className="border-gray-200" data-testid="my-requests-filters"> <Card className="border-gray-200" data-testid="my-requests-filters">
@ -71,6 +75,20 @@ export function MyRequestsFilters({
<SelectItem value="standard">Standard</SelectItem> <SelectItem value="standard">Standard</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<Select value={templateTypeFilter} onValueChange={onTemplateTypeChange}>
<SelectTrigger
className="flex-1 md:w-28 lg:w-32 text-xs sm:text-sm bg-white border-gray-300 hover:border-gray-400 focus:border-blue-400 focus:ring-1 focus:ring-blue-200 h-9 sm:h-10"
data-testid="template-type-filter"
>
<SelectValue placeholder="Template Type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Templates</SelectItem>
<SelectItem value="CUSTOM">Custom</SelectItem>
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
</SelectContent>
</Select>
</div> </div>
</div> </div>
</CardContent> </CardContent>

View File

@ -107,7 +107,7 @@ export function RequestCard({ request, index, onViewRequest }: RequestCardProps)
let templateColor = 'bg-purple-100 !text-purple-600 border-purple-200'; let templateColor = 'bg-purple-100 !text-purple-600 border-purple-200';
if (templateTypeUpper === 'DEALER CLAIM') { if (templateTypeUpper === 'DEALER CLAIM') {
templateLabel = 'Claim Management'; templateLabel = 'Dealer Claim';
templateColor = 'bg-blue-100 !text-blue-700 border-blue-200'; templateColor = 'bg-blue-100 !text-blue-700 border-blue-200';
} else if (templateTypeUpper === 'TEMPLATE') { } else if (templateTypeUpper === 'TEMPLATE') {
templateLabel = 'Template'; templateLabel = 'Template';

View File

@ -13,6 +13,7 @@ interface UseMyRequestsOptions {
search?: string; search?: string;
status?: string; status?: string;
priority?: string; priority?: string;
templateType?: string;
}; };
} }
@ -28,7 +29,7 @@ export function useMyRequests({ itemsPerPage = 10 }: UseMyRequestsOptions = {})
}); });
const fetchMyRequests = useCallback( const fetchMyRequests = useCallback(
async (page: number = 1, filters?: { search?: string; status?: string; priority?: string }) => { async (page: number = 1, filters?: { search?: string; status?: string; priority?: string; templateType?: string }) => {
try { try {
if (page === 1) { if (page === 1) {
setLoading(true); setLoading(true);
@ -41,6 +42,7 @@ export function useMyRequests({ itemsPerPage = 10 }: UseMyRequestsOptions = {})
search: filters?.search, search: filters?.search,
status: filters?.status, status: filters?.status,
priority: filters?.priority, priority: filters?.priority,
templateType: filters?.templateType,
}); });
// Extract data - workflowApi now returns { data: [], pagination: {} } // Extract data - workflowApi now returns { data: [], pagination: {} }

View File

@ -9,6 +9,7 @@ import {
setSearchTerm as setSearchTermAction, setSearchTerm as setSearchTermAction,
setStatusFilter as setStatusFilterAction, setStatusFilter as setStatusFilterAction,
setPriorityFilter as setPriorityFilterAction, setPriorityFilter as setPriorityFilterAction,
setTemplateTypeFilter as setTemplateTypeFilterAction,
setCurrentPage as setCurrentPageAction, setCurrentPage as setCurrentPageAction,
clearFilters as clearFiltersAction, clearFilters as clearFiltersAction,
} from '../redux/myRequestsSlice'; } from '../redux/myRequestsSlice';
@ -24,12 +25,13 @@ export function useMyRequestsFilters({ onFiltersChange, debounceMs = 500 }: UseM
const isInitialMount = useRef(true); const isInitialMount = useRef(true);
// Get filters from Redux // Get filters from Redux
const { searchTerm, statusFilter, priorityFilter, currentPage } = useAppSelector((state) => state.myRequests); const { searchTerm, statusFilter, priorityFilter, templateTypeFilter, currentPage } = useAppSelector((state) => state.myRequests);
// Create setters that dispatch Redux actions // Create setters that dispatch Redux actions
const setSearchTerm = useCallback((value: string) => dispatch(setSearchTermAction(value)), [dispatch]); const setSearchTerm = useCallback((value: string) => dispatch(setSearchTermAction(value)), [dispatch]);
const setStatusFilter = useCallback((value: string) => dispatch(setStatusFilterAction(value)), [dispatch]); const setStatusFilter = useCallback((value: string) => dispatch(setStatusFilterAction(value)), [dispatch]);
const setPriorityFilter = useCallback((value: string) => dispatch(setPriorityFilterAction(value)), [dispatch]); const setPriorityFilter = useCallback((value: string) => dispatch(setPriorityFilterAction(value)), [dispatch]);
const setTemplateTypeFilter = useCallback((value: string) => dispatch(setTemplateTypeFilterAction(value)), [dispatch]);
const setCurrentPage = useCallback((value: number) => dispatch(setCurrentPageAction(value)), [dispatch]); const setCurrentPage = useCallback((value: number) => dispatch(setCurrentPageAction(value)), [dispatch]);
const getFilters = useCallback((): MyRequestsFilters => { const getFilters = useCallback((): MyRequestsFilters => {
@ -37,8 +39,9 @@ export function useMyRequestsFilters({ onFiltersChange, debounceMs = 500 }: UseM
search: searchTerm, search: searchTerm,
status: statusFilter, status: statusFilter,
priority: priorityFilter, priority: priorityFilter,
templateType: templateTypeFilter,
}; };
}, [searchTerm, statusFilter, priorityFilter]); }, [searchTerm, statusFilter, priorityFilter, templateTypeFilter]);
// Debounced filter change handler // Debounced filter change handler
useEffect(() => { useEffect(() => {
@ -65,7 +68,7 @@ export function useMyRequestsFilters({ onFiltersChange, debounceMs = 500 }: UseM
clearTimeout(debounceTimeoutRef.current); clearTimeout(debounceTimeoutRef.current);
} }
}; };
}, [searchTerm, statusFilter, priorityFilter, onFiltersChange, getFilters, debounceMs]); }, [searchTerm, statusFilter, priorityFilter, templateTypeFilter, onFiltersChange, getFilters, debounceMs]);
const resetFilters = useCallback(() => { const resetFilters = useCallback(() => {
dispatch(clearFiltersAction()); dispatch(clearFiltersAction());
@ -75,10 +78,12 @@ export function useMyRequestsFilters({ onFiltersChange, debounceMs = 500 }: UseM
searchTerm, searchTerm,
statusFilter, statusFilter,
priorityFilter, priorityFilter,
templateTypeFilter,
currentPage, currentPage,
setSearchTerm, setSearchTerm,
setStatusFilter, setStatusFilter,
setPriorityFilter, setPriorityFilter,
setTemplateTypeFilter,
setCurrentPage, setCurrentPage,
getFilters, getFilters,
resetFilters, resetFilters,

View File

@ -4,6 +4,7 @@ export interface MyRequestsFiltersState {
searchTerm: string; searchTerm: string;
statusFilter: string; statusFilter: string;
priorityFilter: string; priorityFilter: string;
templateTypeFilter: string;
currentPage: number; currentPage: number;
} }
@ -11,6 +12,7 @@ const initialState: MyRequestsFiltersState = {
searchTerm: '', searchTerm: '',
statusFilter: 'all', statusFilter: 'all',
priorityFilter: 'all', priorityFilter: 'all',
templateTypeFilter: 'all',
currentPage: 1, currentPage: 1,
}; };
@ -28,6 +30,10 @@ const myRequestsSlice = createSlice({
state.priorityFilter = action.payload; state.priorityFilter = action.payload;
state.currentPage = 1; // Reset to page 1 when filter changes state.currentPage = 1; // Reset to page 1 when filter changes
}, },
setTemplateTypeFilter: (state, action: PayloadAction<string>) => {
state.templateTypeFilter = action.payload;
state.currentPage = 1; // Reset to page 1 when filter changes
},
setCurrentPage: (state, action: PayloadAction<number>) => { setCurrentPage: (state, action: PayloadAction<number>) => {
state.currentPage = action.payload; state.currentPage = action.payload;
}, },
@ -35,6 +41,7 @@ const myRequestsSlice = createSlice({
state.searchTerm = ''; state.searchTerm = '';
state.statusFilter = 'all'; state.statusFilter = 'all';
state.priorityFilter = 'all'; state.priorityFilter = 'all';
state.templateTypeFilter = 'all';
state.currentPage = 1; state.currentPage = 1;
}, },
}, },
@ -44,6 +51,7 @@ export const {
setSearchTerm, setSearchTerm,
setStatusFilter, setStatusFilter,
setPriorityFilter, setPriorityFilter,
setTemplateTypeFilter,
setCurrentPage, setCurrentPage,
clearFilters, clearFilters,
} = myRequestsSlice.actions; } = myRequestsSlice.actions;

View File

@ -40,6 +40,7 @@ export interface MyRequestsFilters {
search: string; search: string;
status: string; status: string;
priority: string; priority: string;
templateType?: string;
} }
export interface PaginationState { export interface PaginationState {

View File

@ -122,7 +122,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
// - An initiator (for approved requests awaiting closure) // - An initiator (for approved requests awaiting closure)
// This applies to ALL users including regular users, MANAGEMENT, and ADMIN roles // This applies to ALL users including regular users, MANAGEMENT, and ADMIN roles
// For organization-wide view, users should use the "All Requests" screen (/requests) // For organization-wide view, users should use the "All Requests" screen (/requests)
const fetchRequests = useCallback(async (page: number = 1, filterParams?: { search?: string; status?: string; priority?: string; sortBy?: string; sortOrder?: string }) => { const fetchRequests = useCallback(async (page: number = 1, filterParams?: { search?: string; status?: string; priority?: string; templateType?: string; sortBy?: string; sortOrder?: string }) => {
try { try {
if (page === 1) { if (page === 1) {
setLoading(true); setLoading(true);
@ -138,6 +138,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
search: filterParams?.search, search: filterParams?.search,
status: filterParams?.status, status: filterParams?.status,
priority: filterParams?.priority, priority: filterParams?.priority,
templateType: filterParams?.templateType,
sortBy: filterParams?.sortBy, sortBy: filterParams?.sortBy,
sortOrder: filterParams?.sortOrder sortOrder: filterParams?.sortOrder
}); });
@ -197,6 +198,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined, status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy, sortBy: filters.sortBy,
sortOrder: filters.sortOrder sortOrder: filters.sortOrder
}); });
@ -209,6 +211,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined, status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy, sortBy: filters.sortBy,
sortOrder: filters.sortOrder sortOrder: filters.sortOrder
}); });
@ -244,6 +247,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined, status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy, sortBy: filters.sortBy,
sortOrder: filters.sortOrder, sortOrder: filters.sortOrder,
}); });
@ -263,6 +267,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined, status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy, sortBy: filters.sortBy,
sortOrder: filters.sortOrder, sortOrder: filters.sortOrder,
}); });
@ -270,7 +275,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
return () => clearTimeout(timeoutId); return () => clearTimeout(timeoutId);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [filters.searchTerm, filters.statusFilter, filters.priorityFilter, filters.sortBy, filters.sortOrder]); }, [filters.searchTerm, filters.statusFilter, filters.priorityFilter, filters.templateTypeFilter, filters.sortBy, filters.sortOrder]);
// Backend handles both filtering and sorting - use items directly // Backend handles both filtering and sorting - use items directly
// No client-side sorting needed anymore // No client-side sorting needed anymore
@ -345,7 +350,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
</CardHeader> </CardHeader>
<CardContent className="space-y-3 sm:space-y-4 px-3 sm:px-6"> <CardContent className="space-y-3 sm:space-y-4 px-3 sm:px-6">
{/* Primary filters */} {/* Primary filters */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-3 sm:gap-4">
<div className="relative"> <div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-3.5 h-3.5 sm:w-4 sm:h-4" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-3.5 h-3.5 sm:w-4 sm:h-4" />
<Input <Input
@ -388,6 +393,17 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
</SelectContent> </SelectContent>
</Select> </Select>
<Select value={filters.templateTypeFilter} onValueChange={filters.setTemplateTypeFilter}>
<SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white focus:border-blue-400 focus:ring-1 focus:ring-blue-200">
<SelectValue placeholder="All Templates" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Templates</SelectItem>
<SelectItem value="CUSTOM">Custom</SelectItem>
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
</SelectContent>
</Select>
<div className="flex gap-2"> <div className="flex gap-2">
<Select value={filters.sortBy} onValueChange={(value: any) => filters.setSortBy(value)}> <Select value={filters.sortBy} onValueChange={(value: any) => filters.setSortBy(value)}>
<SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white focus:border-blue-400 focus:ring-1 focus:ring-blue-200"> <SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white focus:border-blue-400 focus:ring-1 focus:ring-blue-200">
@ -470,7 +486,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
let templateColor = 'bg-purple-100 !text-purple-600 border-purple-200'; let templateColor = 'bg-purple-100 !text-purple-600 border-purple-200';
if (templateTypeUpper === 'DEALER CLAIM') { if (templateTypeUpper === 'DEALER CLAIM') {
templateLabel = 'Claim Management'; templateLabel = 'Dealer Claim';
templateColor = 'bg-blue-100 !text-blue-700 border-blue-200'; templateColor = 'bg-blue-100 !text-blue-700 border-blue-200';
} else if (templateTypeUpper === 'TEMPLATE') { } else if (templateTypeUpper === 'TEMPLATE') {
templateLabel = 'Template'; templateLabel = 'Template';

View File

@ -8,6 +8,7 @@ import {
setSearchTerm as setSearchTermAction, setSearchTerm as setSearchTermAction,
setStatusFilter as setStatusFilterAction, setStatusFilter as setStatusFilterAction,
setPriorityFilter as setPriorityFilterAction, setPriorityFilter as setPriorityFilterAction,
setTemplateTypeFilter as setTemplateTypeFilterAction,
setSortBy as setSortByAction, setSortBy as setSortByAction,
setSortOrder as setSortOrderAction, setSortOrder as setSortOrderAction,
setCurrentPage as setCurrentPageAction, setCurrentPage as setCurrentPageAction,
@ -18,12 +19,13 @@ export function useOpenRequestsFilters() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
// Get filters from Redux // Get filters from Redux
const { searchTerm, statusFilter, priorityFilter, sortBy, sortOrder, currentPage } = useAppSelector((state) => state.openRequests); const { searchTerm, statusFilter, priorityFilter, templateTypeFilter, sortBy, sortOrder, currentPage } = useAppSelector((state) => state.openRequests);
// Create setters that dispatch Redux actions // Create setters that dispatch Redux actions
const setSearchTerm = useCallback((value: string) => dispatch(setSearchTermAction(value)), [dispatch]); const setSearchTerm = useCallback((value: string) => dispatch(setSearchTermAction(value)), [dispatch]);
const setStatusFilter = useCallback((value: string) => dispatch(setStatusFilterAction(value)), [dispatch]); const setStatusFilter = useCallback((value: string) => dispatch(setStatusFilterAction(value)), [dispatch]);
const setPriorityFilter = useCallback((value: string) => dispatch(setPriorityFilterAction(value)), [dispatch]); const setPriorityFilter = useCallback((value: string) => dispatch(setPriorityFilterAction(value)), [dispatch]);
const setTemplateTypeFilter = useCallback((value: string) => dispatch(setTemplateTypeFilterAction(value)), [dispatch]);
const setSortBy = useCallback((value: 'created' | 'due' | 'priority' | 'sla') => dispatch(setSortByAction(value)), [dispatch]); const setSortBy = useCallback((value: 'created' | 'due' | 'priority' | 'sla') => dispatch(setSortByAction(value)), [dispatch]);
const setSortOrder = useCallback((value: 'asc' | 'desc') => dispatch(setSortOrderAction(value)), [dispatch]); const setSortOrder = useCallback((value: 'asc' | 'desc') => dispatch(setSortOrderAction(value)), [dispatch]);
const setCurrentPage = useCallback((value: number) => dispatch(setCurrentPageAction(value)), [dispatch]); const setCurrentPage = useCallback((value: number) => dispatch(setCurrentPageAction(value)), [dispatch]);
@ -32,19 +34,22 @@ export function useOpenRequestsFilters() {
const activeFiltersCount = [ const activeFiltersCount = [
searchTerm, searchTerm,
priorityFilter !== 'all' ? priorityFilter : null, priorityFilter !== 'all' ? priorityFilter : null,
statusFilter !== 'all' ? statusFilter : null statusFilter !== 'all' ? statusFilter : null,
templateTypeFilter !== 'all' ? templateTypeFilter : null
].filter(Boolean).length; ].filter(Boolean).length;
return { return {
searchTerm, searchTerm,
statusFilter, statusFilter,
priorityFilter, priorityFilter,
templateTypeFilter,
sortBy, sortBy,
sortOrder, sortOrder,
currentPage, currentPage,
setSearchTerm, setSearchTerm,
setStatusFilter, setStatusFilter,
setPriorityFilter, setPriorityFilter,
setTemplateTypeFilter,
setSortBy, setSortBy,
setSortOrder, setSortOrder,
setCurrentPage, setCurrentPage,

View File

@ -4,6 +4,7 @@ export interface OpenRequestsFiltersState {
searchTerm: string; searchTerm: string;
statusFilter: string; statusFilter: string;
priorityFilter: string; priorityFilter: string;
templateTypeFilter: string;
sortBy: 'created' | 'due' | 'priority' | 'sla'; sortBy: 'created' | 'due' | 'priority' | 'sla';
sortOrder: 'asc' | 'desc'; sortOrder: 'asc' | 'desc';
currentPage: number; currentPage: number;
@ -13,6 +14,7 @@ const initialState: OpenRequestsFiltersState = {
searchTerm: '', searchTerm: '',
statusFilter: 'all', statusFilter: 'all',
priorityFilter: 'all', priorityFilter: 'all',
templateTypeFilter: 'all',
sortBy: 'created', sortBy: 'created',
sortOrder: 'desc', sortOrder: 'desc',
currentPage: 1, currentPage: 1,
@ -31,6 +33,9 @@ const openRequestsSlice = createSlice({
setPriorityFilter: (state, action: PayloadAction<string>) => { setPriorityFilter: (state, action: PayloadAction<string>) => {
state.priorityFilter = action.payload; state.priorityFilter = action.payload;
}, },
setTemplateTypeFilter: (state, action: PayloadAction<string>) => {
state.templateTypeFilter = action.payload;
},
setSortBy: (state, action: PayloadAction<'created' | 'due' | 'priority' | 'sla'>) => { setSortBy: (state, action: PayloadAction<'created' | 'due' | 'priority' | 'sla'>) => {
state.sortBy = action.payload; state.sortBy = action.payload;
}, },
@ -44,6 +49,7 @@ const openRequestsSlice = createSlice({
state.searchTerm = ''; state.searchTerm = '';
state.statusFilter = 'all'; state.statusFilter = 'all';
state.priorityFilter = 'all'; state.priorityFilter = 'all';
state.templateTypeFilter = 'all';
state.currentPage = 1; state.currentPage = 1;
}, },
}, },
@ -53,6 +59,7 @@ export const {
setSearchTerm, setSearchTerm,
setStatusFilter, setStatusFilter,
setPriorityFilter, setPriorityFilter,
setTemplateTypeFilter,
setSortBy, setSortBy,
setSortOrder, setSortOrder,
setCurrentPage, setCurrentPage,

View File

@ -310,6 +310,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {
const filtersWithoutStatus = { const filtersWithoutStatus = {
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
department: filters.departmentFilter !== 'all' ? filters.departmentFilter : undefined, department: filters.departmentFilter !== 'all' ? filters.departmentFilter : undefined,
initiator: filters.initiatorFilter !== 'all' ? filters.initiatorFilter : undefined, initiator: filters.initiatorFilter !== 'all' ? filters.initiatorFilter : undefined,
approver: filters.approverFilter !== 'all' ? filters.approverFilter : undefined, approver: filters.approverFilter !== 'all' ? filters.approverFilter : undefined,
@ -337,6 +338,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
filters.customStartDate, filters.customStartDate,
filters.customEndDate, filters.customEndDate,
filters.priorityFilter, filters.priorityFilter,
filters.templateTypeFilter,
filters.departmentFilter, filters.departmentFilter,
filters.initiatorFilter, filters.initiatorFilter,
filters.approverFilter, filters.approverFilter,
@ -351,6 +353,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
searchTerm: filters.searchTerm, searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter, statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter, priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
slaComplianceFilter: filters.slaComplianceFilter, slaComplianceFilter: filters.slaComplianceFilter,
departmentFilter: filters.departmentFilter, departmentFilter: filters.departmentFilter,
initiatorFilter: filters.initiatorFilter, initiatorFilter: filters.initiatorFilter,
@ -380,6 +383,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
prev.searchTerm !== filters.searchTerm || prev.searchTerm !== filters.searchTerm ||
prev.statusFilter !== filters.statusFilter || prev.statusFilter !== filters.statusFilter ||
prev.priorityFilter !== filters.priorityFilter || prev.priorityFilter !== filters.priorityFilter ||
prev.templateTypeFilter !== filters.templateTypeFilter ||
prev.slaComplianceFilter !== filters.slaComplianceFilter || prev.slaComplianceFilter !== filters.slaComplianceFilter ||
prev.departmentFilter !== filters.departmentFilter || prev.departmentFilter !== filters.departmentFilter ||
prev.initiatorFilter !== filters.initiatorFilter || prev.initiatorFilter !== filters.initiatorFilter ||
@ -400,6 +404,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
searchTerm: filters.searchTerm, searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter, statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter, priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
slaComplianceFilter: filters.slaComplianceFilter, slaComplianceFilter: filters.slaComplianceFilter,
departmentFilter: filters.departmentFilter, departmentFilter: filters.departmentFilter,
initiatorFilter: filters.initiatorFilter, initiatorFilter: filters.initiatorFilter,
@ -419,6 +424,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
filters.searchTerm, filters.searchTerm,
filters.statusFilter, filters.statusFilter,
filters.priorityFilter, filters.priorityFilter,
filters.templateTypeFilter,
filters.slaComplianceFilter, filters.slaComplianceFilter,
filters.departmentFilter, filters.departmentFilter,
filters.initiatorFilter, filters.initiatorFilter,
@ -513,7 +519,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
<Separator /> <Separator />
{/* Primary Filters */} {/* Primary Filters */}
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-3 sm:gap-4"> <div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-6 gap-3 sm:gap-4">
<div className="relative md:col-span-3 lg:col-span-1"> <div className="relative md:col-span-3 lg:col-span-1">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<Input <Input
@ -550,6 +556,17 @@ export function Requests({ onViewRequest }: RequestsProps) {
</SelectContent> </SelectContent>
</Select> </Select>
<Select value={filters.templateTypeFilter} onValueChange={filters.setTemplateTypeFilter}>
<SelectTrigger className="h-10" data-testid="template-type-filter">
<SelectValue placeholder="All Templates" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Templates</SelectItem>
<SelectItem value="CUSTOM">Custom</SelectItem>
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
</SelectContent>
</Select>
<Select <Select
value={filters.departmentFilter} value={filters.departmentFilter}
onValueChange={filters.setDepartmentFilter} onValueChange={filters.setDepartmentFilter}

View File

@ -84,6 +84,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
statsEndDate?: Date, statsEndDate?: Date,
filtersWithoutStatus?: { filtersWithoutStatus?: {
priority?: string; priority?: string;
templateType?: string;
department?: string; department?: string;
initiator?: string; initiator?: string;
approver?: string; approver?: string;
@ -100,6 +101,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
statsEndDate ? statsEndDate.toISOString() : undefined, statsEndDate ? statsEndDate.toISOString() : undefined,
undefined, // status - stats should show all statuses undefined, // status - stats should show all statuses
filtersWithoutStatus?.priority, filtersWithoutStatus?.priority,
filtersWithoutStatus?.templateType,
filtersWithoutStatus?.department, filtersWithoutStatus?.department,
filtersWithoutStatus?.initiator, filtersWithoutStatus?.initiator,
filtersWithoutStatus?.approver, filtersWithoutStatus?.approver,
@ -216,6 +218,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {
const filtersWithoutStatus = { const filtersWithoutStatus = {
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined, priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
department: filters.departmentFilter !== 'all' ? filters.departmentFilter : undefined, department: filters.departmentFilter !== 'all' ? filters.departmentFilter : undefined,
initiator: filters.initiatorFilter !== 'all' ? filters.initiatorFilter : undefined, initiator: filters.initiatorFilter !== 'all' ? filters.initiatorFilter : undefined,
approver: filters.approverFilter !== 'all' ? filters.approverFilter : undefined, approver: filters.approverFilter !== 'all' ? filters.approverFilter : undefined,
@ -223,7 +226,8 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
search: filters.searchTerm || undefined, search: filters.searchTerm || undefined,
slaCompliance: filters.slaComplianceFilter !== 'all' ? filters.slaComplianceFilter : undefined slaCompliance: filters.slaComplianceFilter !== 'all' ? filters.slaComplianceFilter : undefined
}; };
const statsDateRange = filters.dateRange || 'month'; // Use 'all' if dateRange is 'all', otherwise use the selected dateRange or default to 'month'
const statsDateRange = filters.dateRange === 'all' ? 'all' : (filters.dateRange || 'month');
fetchBackendStatsRef.current( fetchBackendStatsRef.current(
statsDateRange, statsDateRange,
@ -245,7 +249,8 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
filters.approverFilterType, filters.approverFilterType,
filters.dateRange, filters.dateRange,
filters.customStartDate, filters.customStartDate,
filters.customEndDate filters.customEndDate,
filters.templateTypeFilter
// Note: statusFilter is NOT in dependencies - stats don't change when only status changes // Note: statusFilter is NOT in dependencies - stats don't change when only status changes
]); ]);
@ -254,6 +259,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
searchTerm: filters.searchTerm, searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter, statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter, priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
slaComplianceFilter: filters.slaComplianceFilter, slaComplianceFilter: filters.slaComplianceFilter,
departmentFilter: filters.departmentFilter, departmentFilter: filters.departmentFilter,
initiatorFilter: filters.initiatorFilter, initiatorFilter: filters.initiatorFilter,
@ -282,6 +288,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
prev.searchTerm !== filters.searchTerm || prev.searchTerm !== filters.searchTerm ||
prev.statusFilter !== filters.statusFilter || prev.statusFilter !== filters.statusFilter ||
prev.priorityFilter !== filters.priorityFilter || prev.priorityFilter !== filters.priorityFilter ||
prev.templateTypeFilter !== filters.templateTypeFilter ||
prev.slaComplianceFilter !== filters.slaComplianceFilter || prev.slaComplianceFilter !== filters.slaComplianceFilter ||
prev.departmentFilter !== filters.departmentFilter || prev.departmentFilter !== filters.departmentFilter ||
prev.initiatorFilter !== filters.initiatorFilter || prev.initiatorFilter !== filters.initiatorFilter ||
@ -301,6 +308,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
searchTerm: filters.searchTerm, searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter, statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter, priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
slaComplianceFilter: filters.slaComplianceFilter, slaComplianceFilter: filters.slaComplianceFilter,
departmentFilter: filters.departmentFilter, departmentFilter: filters.departmentFilter,
initiatorFilter: filters.initiatorFilter, initiatorFilter: filters.initiatorFilter,
@ -318,6 +326,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
filters.searchTerm, filters.searchTerm,
filters.statusFilter, filters.statusFilter,
filters.priorityFilter, filters.priorityFilter,
filters.templateTypeFilter,
filters.slaComplianceFilter, filters.slaComplianceFilter,
filters.departmentFilter, filters.departmentFilter,
filters.initiatorFilter, filters.initiatorFilter,
@ -431,7 +440,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
<Separator /> <Separator />
{/* Primary Filters */} {/* Primary Filters */}
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-3 sm:gap-4"> <div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-6 gap-3 sm:gap-4">
<div className="relative md:col-span-3 lg:col-span-1"> <div className="relative md:col-span-3 lg:col-span-1">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<Input <Input
@ -468,6 +477,17 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
</SelectContent> </SelectContent>
</Select> </Select>
<Select value={filters.templateTypeFilter} onValueChange={filters.setTemplateTypeFilter}>
<SelectTrigger className="h-10" data-testid="template-type-filter">
<SelectValue placeholder="All Templates" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Templates</SelectItem>
<SelectItem value="CUSTOM">Custom</SelectItem>
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
</SelectContent>
</Select>
<Select <Select
value={filters.departmentFilter} value={filters.departmentFilter}
onValueChange={filters.setDepartmentFilter} onValueChange={filters.setDepartmentFilter}

View File

@ -106,7 +106,7 @@ export function RequestCard({ request, index, onViewRequest }: RequestCardProps)
let templateColor = 'bg-purple-100 !text-purple-600 border-purple-200'; let templateColor = 'bg-purple-100 !text-purple-600 border-purple-200';
if (templateTypeUpper === 'DEALER CLAIM') { if (templateTypeUpper === 'DEALER CLAIM') {
templateLabel = 'Claim Management'; templateLabel = 'Dealer Claim';
templateColor = 'bg-blue-100 !text-blue-700 border-blue-200'; templateColor = 'bg-blue-100 !text-blue-700 border-blue-200';
} else if (templateTypeUpper === 'TEMPLATE') { } else if (templateTypeUpper === 'TEMPLATE') {
templateLabel = 'Template'; templateLabel = 'Template';

View File

@ -11,6 +11,7 @@ import {
setSearchTerm as setSearchTermAction, setSearchTerm as setSearchTermAction,
setStatusFilter as setStatusFilterAction, setStatusFilter as setStatusFilterAction,
setPriorityFilter as setPriorityFilterAction, setPriorityFilter as setPriorityFilterAction,
setTemplateTypeFilter as setTemplateTypeFilterAction,
setSlaComplianceFilter as setSlaComplianceFilterAction, setSlaComplianceFilter as setSlaComplianceFilterAction,
setDepartmentFilter as setDepartmentFilterAction, setDepartmentFilter as setDepartmentFilterAction,
setInitiatorFilter as setInitiatorFilterAction, setInitiatorFilter as setInitiatorFilterAction,
@ -32,6 +33,7 @@ export function useRequestsFilters() {
searchTerm, searchTerm,
statusFilter, statusFilter,
priorityFilter, priorityFilter,
templateTypeFilter,
slaComplianceFilter, slaComplianceFilter,
departmentFilter, departmentFilter,
initiatorFilter, initiatorFilter,
@ -48,6 +50,7 @@ export function useRequestsFilters() {
const setSearchTerm = useCallback((value: string) => dispatch(setSearchTermAction(value)), [dispatch]); const setSearchTerm = useCallback((value: string) => dispatch(setSearchTermAction(value)), [dispatch]);
const setStatusFilter = useCallback((value: string) => dispatch(setStatusFilterAction(value)), [dispatch]); const setStatusFilter = useCallback((value: string) => dispatch(setStatusFilterAction(value)), [dispatch]);
const setPriorityFilter = useCallback((value: string) => dispatch(setPriorityFilterAction(value)), [dispatch]); const setPriorityFilter = useCallback((value: string) => dispatch(setPriorityFilterAction(value)), [dispatch]);
const setTemplateTypeFilter = useCallback((value: string) => dispatch(setTemplateTypeFilterAction(value)), [dispatch]);
const setSlaComplianceFilter = useCallback((value: string) => dispatch(setSlaComplianceFilterAction(value)), [dispatch]); const setSlaComplianceFilter = useCallback((value: string) => dispatch(setSlaComplianceFilterAction(value)), [dispatch]);
const setDepartmentFilter = useCallback((value: string) => dispatch(setDepartmentFilterAction(value)), [dispatch]); const setDepartmentFilter = useCallback((value: string) => dispatch(setDepartmentFilterAction(value)), [dispatch]);
const setInitiatorFilter = useCallback((value: string) => dispatch(setInitiatorFilterAction(value)), [dispatch]); const setInitiatorFilter = useCallback((value: string) => dispatch(setInitiatorFilterAction(value)), [dispatch]);
@ -64,6 +67,7 @@ export function useRequestsFilters() {
search: searchTerm || undefined, search: searchTerm || undefined,
status: statusFilter !== 'all' ? statusFilter : undefined, status: statusFilter !== 'all' ? statusFilter : undefined,
priority: priorityFilter !== 'all' ? priorityFilter : undefined, priority: priorityFilter !== 'all' ? priorityFilter : undefined,
templateType: templateTypeFilter !== 'all' ? templateTypeFilter : undefined,
slaCompliance: slaComplianceFilter !== 'all' ? slaComplianceFilter : undefined, slaCompliance: slaComplianceFilter !== 'all' ? slaComplianceFilter : undefined,
department: departmentFilter !== 'all' ? departmentFilter : undefined, department: departmentFilter !== 'all' ? departmentFilter : undefined,
initiator: initiatorFilter !== 'all' ? initiatorFilter : undefined, initiator: initiatorFilter !== 'all' ? initiatorFilter : undefined,
@ -77,6 +81,7 @@ export function useRequestsFilters() {
searchTerm, searchTerm,
statusFilter, statusFilter,
priorityFilter, priorityFilter,
templateTypeFilter,
slaComplianceFilter, slaComplianceFilter,
departmentFilter, departmentFilter,
initiatorFilter, initiatorFilter,
@ -118,6 +123,7 @@ export function useRequestsFilters() {
searchTerm || searchTerm ||
statusFilter !== 'all' || statusFilter !== 'all' ||
priorityFilter !== 'all' || priorityFilter !== 'all' ||
templateTypeFilter !== 'all' ||
slaComplianceFilter !== 'all' || slaComplianceFilter !== 'all' ||
departmentFilter !== 'all' || departmentFilter !== 'all' ||
initiatorFilter !== 'all' || initiatorFilter !== 'all' ||
@ -132,6 +138,7 @@ export function useRequestsFilters() {
searchTerm, searchTerm,
statusFilter, statusFilter,
priorityFilter, priorityFilter,
templateTypeFilter,
slaComplianceFilter, slaComplianceFilter,
departmentFilter, departmentFilter,
initiatorFilter, initiatorFilter,
@ -147,6 +154,7 @@ export function useRequestsFilters() {
setSearchTerm, setSearchTerm,
setStatusFilter, setStatusFilter,
setPriorityFilter, setPriorityFilter,
setTemplateTypeFilter,
setSlaComplianceFilter, setSlaComplianceFilter,
setDepartmentFilter, setDepartmentFilter,
setInitiatorFilter, setInitiatorFilter,

View File

@ -5,6 +5,7 @@ export interface RequestsFiltersState {
searchTerm: string; searchTerm: string;
statusFilter: string; statusFilter: string;
priorityFilter: string; priorityFilter: string;
templateTypeFilter: string;
slaComplianceFilter: string; slaComplianceFilter: string;
departmentFilter: string; departmentFilter: string;
initiatorFilter: string; initiatorFilter: string;
@ -21,6 +22,7 @@ const initialState: RequestsFiltersState = {
searchTerm: '', searchTerm: '',
statusFilter: 'all', statusFilter: 'all',
priorityFilter: 'all', priorityFilter: 'all',
templateTypeFilter: 'all',
slaComplianceFilter: 'all', slaComplianceFilter: 'all',
departmentFilter: 'all', departmentFilter: 'all',
initiatorFilter: 'all', initiatorFilter: 'all',
@ -46,6 +48,9 @@ const requestsSlice = createSlice({
setPriorityFilter: (state, action: PayloadAction<string>) => { setPriorityFilter: (state, action: PayloadAction<string>) => {
state.priorityFilter = action.payload; state.priorityFilter = action.payload;
}, },
setTemplateTypeFilter: (state, action: PayloadAction<string>) => {
state.templateTypeFilter = action.payload;
},
setSlaComplianceFilter: (state, action: PayloadAction<string>) => { setSlaComplianceFilter: (state, action: PayloadAction<string>) => {
state.slaComplianceFilter = action.payload; state.slaComplianceFilter = action.payload;
}, },
@ -80,6 +85,7 @@ const requestsSlice = createSlice({
state.searchTerm = ''; state.searchTerm = '';
state.statusFilter = 'all'; state.statusFilter = 'all';
state.priorityFilter = 'all'; state.priorityFilter = 'all';
state.templateTypeFilter = 'all';
state.slaComplianceFilter = 'all'; state.slaComplianceFilter = 'all';
state.departmentFilter = 'all'; state.departmentFilter = 'all';
state.initiatorFilter = 'all'; state.initiatorFilter = 'all';
@ -98,6 +104,7 @@ export const {
setSearchTerm, setSearchTerm,
setStatusFilter, setStatusFilter,
setPriorityFilter, setPriorityFilter,
setTemplateTypeFilter,
setSlaComplianceFilter, setSlaComplianceFilter,
setDepartmentFilter, setDepartmentFilter,
setInitiatorFilter, setInitiatorFilter,

View File

@ -25,6 +25,7 @@ export async function fetchRequestsData({
if (filters?.search) backendFilters.search = filters.search; if (filters?.search) backendFilters.search = filters.search;
if (filters?.status && filters.status !== 'all') backendFilters.status = filters.status; if (filters?.status && filters.status !== 'all') backendFilters.status = filters.status;
if (filters?.priority && filters.priority !== 'all') backendFilters.priority = filters.priority; if (filters?.priority && filters.priority !== 'all') backendFilters.priority = filters.priority;
if (filters?.templateType && filters.templateType !== 'all') backendFilters.templateType = filters.templateType;
if (filters?.department && filters.department !== 'all') backendFilters.department = filters.department; if (filters?.department && filters.department !== 'all') backendFilters.department = filters.department;
if (filters?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator; if (filters?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator;
if (filters?.approver && filters.approver !== 'all') { if (filters?.approver && filters.approver !== 'all') {
@ -90,6 +91,7 @@ export async function fetchRequestsData({
if (filters?.search) backendFilters.search = filters.search; if (filters?.search) backendFilters.search = filters.search;
if (filters?.status && filters.status !== 'all') backendFilters.status = filters.status; if (filters?.status && filters.status !== 'all') backendFilters.status = filters.status;
if (filters?.priority && filters.priority !== 'all') backendFilters.priority = filters.priority; if (filters?.priority && filters.priority !== 'all') backendFilters.priority = filters.priority;
if (filters?.templateType && filters.templateType !== 'all') backendFilters.templateType = filters.templateType;
if (filters?.department && filters.department !== 'all') backendFilters.department = filters.department; if (filters?.department && filters.department !== 'all') backendFilters.department = filters.department;
if (filters?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator; if (filters?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator;
if (filters?.slaCompliance && filters.slaCompliance !== 'all') backendFilters.slaCompliance = filters.slaCompliance; if (filters?.slaCompliance && filters.slaCompliance !== 'all') backendFilters.slaCompliance = filters.slaCompliance;

View File

@ -30,6 +30,7 @@ export async function fetchUserParticipantRequestsData({
if (filters?.search) backendFilters.search = filters.search; if (filters?.search) backendFilters.search = filters.search;
if (filters?.status && filters.status !== 'all') backendFilters.status = filters.status; if (filters?.status && filters.status !== 'all') backendFilters.status = filters.status;
if (filters?.priority && filters.priority !== 'all') backendFilters.priority = filters.priority; if (filters?.priority && filters.priority !== 'all') backendFilters.priority = filters.priority;
if (filters?.templateType && filters.templateType !== 'all') backendFilters.templateType = filters.templateType;
if (filters?.department && filters.department !== 'all') backendFilters.department = filters.department; if (filters?.department && filters.department !== 'all') backendFilters.department = filters.department;
if (filters?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator; if (filters?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator;
if (filters?.approver && filters.approver !== 'all') { if (filters?.approver && filters.approver !== 'all') {
@ -101,6 +102,7 @@ export async function fetchAllRequestsForExport(filters?: RequestFilters): Promi
if (filters?.search) backendFilters.search = filters.search; if (filters?.search) backendFilters.search = filters.search;
if (filters?.status && filters.status !== 'all') backendFilters.status = filters.status; if (filters?.status && filters.status !== 'all') backendFilters.status = filters.status;
if (filters?.priority && filters.priority !== 'all') backendFilters.priority = filters.priority; if (filters?.priority && filters.priority !== 'all') backendFilters.priority = filters.priority;
if (filters?.templateType && filters.templateType !== 'all') backendFilters.templateType = filters.templateType;
if (filters?.department && filters.department !== 'all') backendFilters.department = filters.department; if (filters?.department && filters.department !== 'all') backendFilters.department = filters.department;
if (filters?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator; if (filters?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator;
if (filters?.approver && filters.approver !== 'all') { if (filters?.approver && filters.approver !== 'all') {

View File

@ -12,6 +12,7 @@ export interface RequestFilters {
search?: string; search?: string;
status?: string; status?: string;
priority?: string; priority?: string;
templateType?: string;
slaCompliance?: string; slaCompliance?: string;
department?: string; department?: string;
initiator?: string; initiator?: string;

View File

@ -196,6 +196,7 @@ class DashboardService {
endDate?: string, endDate?: string,
status?: string, status?: string,
priority?: string, priority?: string,
templateType?: string,
department?: string, department?: string,
initiator?: string, initiator?: string,
approver?: string, approver?: string,
@ -217,6 +218,9 @@ class DashboardService {
if (priority && priority !== 'all') { if (priority && priority !== 'all') {
params.priority = priority; params.priority = priority;
} }
if (templateType && templateType !== 'all') {
params.templateType = templateType;
}
if (department && department !== 'all') { if (department && department !== 'all') {
params.department = department; params.department = department;
} }

View File

@ -155,8 +155,8 @@ export async function createWorkflowMultipart(form: CreateWorkflowFromFormPayloa
return { id: data?.requestId } as any; return { id: data?.requestId } as any;
} }
export async function listWorkflows(params: { page?: number; limit?: number; search?: string; status?: string; priority?: string; department?: string; initiator?: string; approver?: string; slaCompliance?: string; dateRange?: string; startDate?: string; endDate?: string } = {}) { export async function listWorkflows(params: { page?: number; limit?: number; search?: string; status?: string; priority?: string; templateType?: string; department?: string; initiator?: string; approver?: string; slaCompliance?: string; dateRange?: string; startDate?: string; endDate?: string } = {}) {
const { page = 1, limit = 20, search, status, priority, department, initiator, approver, slaCompliance, dateRange, startDate, endDate } = params; const { page = 1, limit = 20, search, status, priority, templateType, department, initiator, approver, slaCompliance, dateRange, startDate, endDate } = params;
const res = await apiClient.get('/workflows', { const res = await apiClient.get('/workflows', {
params: { params: {
page, page,
@ -164,6 +164,7 @@ export async function listWorkflows(params: { page?: number; limit?: number; sea
search, search,
status, status,
priority, priority,
templateType,
department, department,
initiator, initiator,
approver, approver,
@ -178,8 +179,8 @@ export async function listWorkflows(params: { page?: number; limit?: number; sea
// List requests where user is a participant (not initiator) - for regular users' "All Requests" page // List requests where user is a participant (not initiator) - for regular users' "All Requests" page
// SEPARATE from listWorkflows (admin) to avoid interference // SEPARATE from listWorkflows (admin) to avoid interference
export async function listParticipantRequests(params: { page?: number; limit?: number; search?: string; status?: string; priority?: string; department?: string; initiator?: string; approver?: string; approverType?: string; slaCompliance?: string; dateRange?: string; startDate?: string; endDate?: string } = {}) { export async function listParticipantRequests(params: { page?: number; limit?: number; search?: string; status?: string; priority?: string; templateType?: string; department?: string; initiator?: string; approver?: string; approverType?: string; slaCompliance?: string; dateRange?: string; startDate?: string; endDate?: string } = {}) {
const { page = 1, limit = 20, search, status, priority, department, initiator, approver, approverType, slaCompliance, dateRange, startDate, endDate } = params; const { page = 1, limit = 20, search, status, priority, templateType, department, initiator, approver, approverType, slaCompliance, dateRange, startDate, endDate } = params;
const res = await apiClient.get('/workflows/participant-requests', { const res = await apiClient.get('/workflows/participant-requests', {
params: { params: {
page, page,
@ -187,6 +188,7 @@ export async function listParticipantRequests(params: { page?: number; limit?: n
search, search,
status, status,
priority, priority,
templateType,
department, department,
initiator, initiator,
approver, approver,
@ -232,8 +234,8 @@ export async function listMyWorkflows(params: { page?: number; limit?: number; s
} }
// List requests where user is the initiator - for "My Requests" page // List requests where user is the initiator - for "My Requests" page
export async function listMyInitiatedWorkflows(params: { page?: number; limit?: number; search?: string; status?: string; priority?: string; department?: string; slaCompliance?: string; dateRange?: string; startDate?: string; endDate?: string } = {}) { export async function listMyInitiatedWorkflows(params: { page?: number; limit?: number; search?: string; status?: string; priority?: string; templateType?: string; department?: string; slaCompliance?: string; dateRange?: string; startDate?: string; endDate?: string } = {}) {
const { page = 1, limit = 20, search, status, priority, department, slaCompliance, dateRange, startDate, endDate } = params; const { page = 1, limit = 20, search, status, priority, templateType, department, slaCompliance, dateRange, startDate, endDate } = params;
const res = await apiClient.get('/workflows/my-initiated', { const res = await apiClient.get('/workflows/my-initiated', {
params: { params: {
page, page,
@ -241,6 +243,7 @@ export async function listMyInitiatedWorkflows(params: { page?: number; limit?:
search, search,
status, status,
priority, priority,
templateType,
department, department,
slaCompliance, slaCompliance,
dateRange, dateRange,
@ -255,9 +258,9 @@ export async function listMyInitiatedWorkflows(params: { page?: number; limit?:
}; };
} }
export async function listOpenForMe(params: { page?: number; limit?: number; search?: string; status?: string; priority?: string; sortBy?: string; sortOrder?: string } = {}) { export async function listOpenForMe(params: { page?: number; limit?: number; search?: string; status?: string; priority?: string; templateType?: string; sortBy?: string; sortOrder?: string } = {}) {
const { page = 1, limit = 20, search, status, priority, sortBy, sortOrder } = params; const { page = 1, limit = 20, search, status, priority, templateType, sortBy, sortOrder } = params;
const res = await apiClient.get('/workflows/open-for-me', { params: { page, limit, search, status, priority, sortBy, sortOrder } }); const res = await apiClient.get('/workflows/open-for-me', { params: { page, limit, search, status, priority, templateType, sortBy, sortOrder } });
// Response structure: { success, data: { data: [...], pagination: {...} } } // Response structure: { success, data: { data: [...], pagination: {...} } }
return { return {
data: res.data?.data?.data || res.data?.data || [], data: res.data?.data?.data || res.data?.data || [],
@ -265,9 +268,9 @@ export async function listOpenForMe(params: { page?: number; limit?: number; sea
}; };
} }
export async function listClosedByMe(params: { page?: number; limit?: number; search?: string; status?: string; priority?: string; sortBy?: string; sortOrder?: string } = {}) { export async function listClosedByMe(params: { page?: number; limit?: number; search?: string; status?: string; priority?: string; templateType?: string; sortBy?: string; sortOrder?: string } = {}) {
const { page = 1, limit = 20, search, status, priority, sortBy, sortOrder } = params; const { page = 1, limit = 20, search, status, priority, templateType, sortBy, sortOrder } = params;
const res = await apiClient.get('/workflows/closed-by-me', { params: { page, limit, search, status, priority, sortBy, sortOrder } }); const res = await apiClient.get('/workflows/closed-by-me', { params: { page, limit, search, status, priority, templateType, sortBy, sortOrder } });
// Response structure: { success, data: { data: [...], pagination: {...} } } // Response structure: { success, data: { data: [...], pagination: {...} } }
return { return {
data: res.data?.data?.data || res.data?.data || [], data: res.data?.data?.data || res.data?.data || [],