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,
statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
sortBy: filters.sortBy,
sortOrder: filters.sortOrder,
});
@ -39,6 +40,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy,
sortOrder: filters.sortOrder,
});
@ -55,6 +57,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
prev.searchTerm !== filters.searchTerm ||
prev.statusFilter !== filters.statusFilter ||
prev.priorityFilter !== filters.priorityFilter ||
prev.templateTypeFilter !== filters.templateTypeFilter ||
prev.sortBy !== filters.sortBy ||
prev.sortOrder !== filters.sortOrder;
@ -67,6 +70,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy,
sortOrder: filters.sortOrder,
});
@ -76,6 +80,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
sortBy: filters.sortBy,
sortOrder: filters.sortOrder,
};
@ -83,7 +88,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
return () => clearTimeout(timeoutId);
// 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
const handlePageChange = useCallback(
@ -94,6 +99,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy,
sortOrder: filters.sortOrder,
});
@ -108,6 +114,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy,
sortOrder: filters.sortOrder,
});
@ -128,12 +135,14 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
searchTerm={filters.searchTerm}
priorityFilter={filters.priorityFilter}
statusFilter={filters.statusFilter}
templateTypeFilter={filters.templateTypeFilter}
sortBy={filters.sortBy}
sortOrder={filters.sortOrder}
activeFiltersCount={filters.activeFiltersCount}
onSearchChange={filters.setSearchTerm}
onPriorityChange={filters.setPriorityFilter}
onStatusChange={filters.setStatusFilter}
onTemplateTypeChange={filters.setTemplateTypeFilter}
onSortByChange={filters.setSortBy}
onSortOrderChange={() => filters.setSortOrder(filters.sortOrder === 'asc' ? 'desc' : 'asc')}
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';
if (templateTypeUpper === 'DEALER CLAIM') {
templateLabel = 'Claim Management';
templateLabel = 'Dealer Claim';
templateColor = 'bg-blue-100 !text-blue-700 border-blue-200';
} else if (templateTypeUpper === 'TEMPLATE') {
templateLabel = 'Template';

View File

@ -12,12 +12,14 @@ interface ClosedRequestsFiltersProps {
searchTerm: string;
priorityFilter: string;
statusFilter: string;
templateTypeFilter: string;
sortBy: 'created' | 'due' | 'priority';
sortOrder: 'asc' | 'desc';
activeFiltersCount: number;
onSearchChange: (value: string) => void;
onPriorityChange: (value: string) => void;
onStatusChange: (value: string) => void;
onTemplateTypeChange: (value: string) => void;
onSortByChange: (value: 'created' | 'due' | 'priority') => void;
onSortOrderChange: () => void;
onClearFilters: () => void;
@ -27,12 +29,14 @@ export function ClosedRequestsFilters({
searchTerm,
priorityFilter,
statusFilter,
templateTypeFilter,
sortBy,
sortOrder,
activeFiltersCount,
onSearchChange,
onPriorityChange,
onStatusChange,
onTemplateTypeChange,
onSortByChange,
onSortOrderChange,
onClearFilters,
@ -71,7 +75,7 @@ export function ClosedRequestsFilters({
</div>
</CardHeader>
<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">
<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
@ -125,6 +129,17 @@ export function ClosedRequestsFilters({
</SelectContent>
</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">
<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">

View File

@ -30,7 +30,7 @@ export function useClosedRequests({ itemsPerPage = 10 }: UseClosedRequestsOption
});
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 {
if (page === 1) {
setLoading(true);
@ -51,6 +51,7 @@ export function useClosedRequests({ itemsPerPage = 10 }: UseClosedRequestsOption
search: filters?.search,
status: filters?.status && filters.status !== 'all' ? filters.status : undefined,
priority: filters?.priority,
templateType: filters?.templateType,
sortBy: filters?.sortBy,
sortOrder: filters?.sortOrder
});
@ -90,7 +91,7 @@ export function useClosedRequests({ itemsPerPage = 10 }: UseClosedRequestsOption
// Initial fetch removed - component handles initial fetch using Redux stored page
// 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);
fetchRequests(pagination.currentPage, filters);
}, [fetchRequests, pagination.currentPage]);

View File

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

View File

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

View File

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

View File

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

View File

@ -37,6 +37,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
});
const hasInitialFetchRun = useRef(false);
@ -47,6 +48,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
});
hasInitialFetchRun.current = true;
// eslint-disable-next-line react-hooks/exhaustive-deps
@ -60,7 +62,8 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
const hasChanged =
prev.searchTerm !== filters.searchTerm ||
prev.statusFilter !== filters.statusFilter ||
prev.priorityFilter !== filters.priorityFilter;
prev.priorityFilter !== filters.priorityFilter ||
prev.templateTypeFilter !== filters.templateTypeFilter;
if (!hasChanged) return; // No actual change, skip
@ -71,6 +74,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
});
// Update previous values
@ -78,12 +82,13 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
};
}, filters.searchTerm !== prev.searchTerm ? 500 : 0);
return () => clearTimeout(timeoutId);
// 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)
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"
// 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: 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(
undefined, // dateRange - no date filter to match the list
'all', // dateRange - 'all' means no date filter to match the list
undefined, // startDate
undefined, // endDate
undefined, // status - My Requests stats show all statuses, not filtered by status (same as All Requests)
filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined, // templateType
undefined, // department
user.userId, // initiator - explicitly filter by user's own userId to ensure only their requests
undefined, // approver
undefined, // approverType
filters.searchTerm || undefined,
undefined // slaCompliance
undefined, // slaCompliance
true // viewAsUser - treat as normal user even if admin
);
setBackendStats({
@ -142,7 +149,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
} finally {
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)
// 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);
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)
const convertedDynamicRequests = transformRequests(dynamicRequests);
@ -196,6 +203,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : 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}
statusFilter={filters.statusFilter}
priorityFilter={filters.priorityFilter}
templateTypeFilter={filters.templateTypeFilter}
onSearchChange={filters.setSearchTerm}
onStatusChange={filters.setStatusFilter}
onPriorityChange={filters.setPriorityFilter}
onTemplateTypeChange={filters.setTemplateTypeFilter}
/>
{/* Requests List */}

View File

@ -11,18 +11,22 @@ interface MyRequestsFiltersProps {
searchTerm: string;
statusFilter: string;
priorityFilter: string;
templateTypeFilter: string;
onSearchChange: (value: string) => void;
onStatusChange: (value: string) => void;
onPriorityChange: (value: string) => void;
onTemplateTypeChange: (value: string) => void;
}
export function MyRequestsFilters({
searchTerm,
statusFilter,
priorityFilter,
templateTypeFilter,
onSearchChange,
onStatusChange,
onPriorityChange,
onTemplateTypeChange,
}: MyRequestsFiltersProps) {
return (
<Card className="border-gray-200" data-testid="my-requests-filters">
@ -71,6 +75,20 @@ export function MyRequestsFilters({
<SelectItem value="standard">Standard</SelectItem>
</SelectContent>
</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>
</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';
if (templateTypeUpper === 'DEALER CLAIM') {
templateLabel = 'Claim Management';
templateLabel = 'Dealer Claim';
templateColor = 'bg-blue-100 !text-blue-700 border-blue-200';
} else if (templateTypeUpper === 'TEMPLATE') {
templateLabel = 'Template';

View File

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

View File

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

View File

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

View File

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

View File

@ -122,7 +122,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
// - An initiator (for approved requests awaiting closure)
// This applies to ALL users including regular users, MANAGEMENT, and ADMIN roles
// 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 {
if (page === 1) {
setLoading(true);
@ -138,6 +138,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
search: filterParams?.search,
status: filterParams?.status,
priority: filterParams?.priority,
templateType: filterParams?.templateType,
sortBy: filterParams?.sortBy,
sortOrder: filterParams?.sortOrder
});
@ -197,6 +198,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy,
sortOrder: filters.sortOrder
});
@ -209,6 +211,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy,
sortOrder: filters.sortOrder
});
@ -244,6 +247,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy,
sortOrder: filters.sortOrder,
});
@ -263,6 +267,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
search: filters.searchTerm || undefined,
status: filters.statusFilter !== 'all' ? filters.statusFilter : undefined,
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
sortBy: filters.sortBy,
sortOrder: filters.sortOrder,
});
@ -270,7 +275,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
return () => clearTimeout(timeoutId);
// 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
// No client-side sorting needed anymore
@ -345,7 +350,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
</CardHeader>
<CardContent className="space-y-3 sm:space-y-4 px-3 sm:px-6">
{/* 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">
<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
@ -388,6 +393,17 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
</SelectContent>
</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">
<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">
@ -470,7 +486,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
let templateColor = 'bg-purple-100 !text-purple-600 border-purple-200';
if (templateTypeUpper === 'DEALER CLAIM') {
templateLabel = 'Claim Management';
templateLabel = 'Dealer Claim';
templateColor = 'bg-blue-100 !text-blue-700 border-blue-200';
} else if (templateTypeUpper === 'TEMPLATE') {
templateLabel = 'Template';

View File

@ -8,6 +8,7 @@ import {
setSearchTerm as setSearchTermAction,
setStatusFilter as setStatusFilterAction,
setPriorityFilter as setPriorityFilterAction,
setTemplateTypeFilter as setTemplateTypeFilterAction,
setSortBy as setSortByAction,
setSortOrder as setSortOrderAction,
setCurrentPage as setCurrentPageAction,
@ -18,12 +19,13 @@ export function useOpenRequestsFilters() {
const dispatch = useAppDispatch();
// 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
const setSearchTerm = useCallback((value: string) => dispatch(setSearchTermAction(value)), [dispatch]);
const setStatusFilter = useCallback((value: string) => dispatch(setStatusFilterAction(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 setSortOrder = useCallback((value: 'asc' | 'desc') => dispatch(setSortOrderAction(value)), [dispatch]);
const setCurrentPage = useCallback((value: number) => dispatch(setCurrentPageAction(value)), [dispatch]);
@ -32,19 +34,22 @@ export function useOpenRequestsFilters() {
const activeFiltersCount = [
searchTerm,
priorityFilter !== 'all' ? priorityFilter : null,
statusFilter !== 'all' ? statusFilter : null
statusFilter !== 'all' ? statusFilter : null,
templateTypeFilter !== 'all' ? templateTypeFilter : null
].filter(Boolean).length;
return {
searchTerm,
statusFilter,
priorityFilter,
templateTypeFilter,
sortBy,
sortOrder,
currentPage,
setSearchTerm,
setStatusFilter,
setPriorityFilter,
setTemplateTypeFilter,
setSortBy,
setSortOrder,
setCurrentPage,

View File

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

View File

@ -310,6 +310,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
const timeoutId = setTimeout(() => {
const filtersWithoutStatus = {
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
department: filters.departmentFilter !== 'all' ? filters.departmentFilter : undefined,
initiator: filters.initiatorFilter !== 'all' ? filters.initiatorFilter : undefined,
approver: filters.approverFilter !== 'all' ? filters.approverFilter : undefined,
@ -337,6 +338,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
filters.customStartDate,
filters.customEndDate,
filters.priorityFilter,
filters.templateTypeFilter,
filters.departmentFilter,
filters.initiatorFilter,
filters.approverFilter,
@ -351,6 +353,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
slaComplianceFilter: filters.slaComplianceFilter,
departmentFilter: filters.departmentFilter,
initiatorFilter: filters.initiatorFilter,
@ -380,6 +383,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
prev.searchTerm !== filters.searchTerm ||
prev.statusFilter !== filters.statusFilter ||
prev.priorityFilter !== filters.priorityFilter ||
prev.templateTypeFilter !== filters.templateTypeFilter ||
prev.slaComplianceFilter !== filters.slaComplianceFilter ||
prev.departmentFilter !== filters.departmentFilter ||
prev.initiatorFilter !== filters.initiatorFilter ||
@ -400,6 +404,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
slaComplianceFilter: filters.slaComplianceFilter,
departmentFilter: filters.departmentFilter,
initiatorFilter: filters.initiatorFilter,
@ -419,6 +424,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
filters.searchTerm,
filters.statusFilter,
filters.priorityFilter,
filters.templateTypeFilter,
filters.slaComplianceFilter,
filters.departmentFilter,
filters.initiatorFilter,
@ -513,7 +519,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
<Separator />
{/* 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">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<Input
@ -550,6 +556,17 @@ export function Requests({ onViewRequest }: RequestsProps) {
</SelectContent>
</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
value={filters.departmentFilter}
onValueChange={filters.setDepartmentFilter}

View File

@ -84,6 +84,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
statsEndDate?: Date,
filtersWithoutStatus?: {
priority?: string;
templateType?: string;
department?: string;
initiator?: string;
approver?: string;
@ -100,6 +101,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
statsEndDate ? statsEndDate.toISOString() : undefined,
undefined, // status - stats should show all statuses
filtersWithoutStatus?.priority,
filtersWithoutStatus?.templateType,
filtersWithoutStatus?.department,
filtersWithoutStatus?.initiator,
filtersWithoutStatus?.approver,
@ -216,6 +218,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
const timeoutId = setTimeout(() => {
const filtersWithoutStatus = {
priority: filters.priorityFilter !== 'all' ? filters.priorityFilter : undefined,
templateType: filters.templateTypeFilter !== 'all' ? filters.templateTypeFilter : undefined,
department: filters.departmentFilter !== 'all' ? filters.departmentFilter : undefined,
initiator: filters.initiatorFilter !== 'all' ? filters.initiatorFilter : undefined,
approver: filters.approverFilter !== 'all' ? filters.approverFilter : undefined,
@ -223,7 +226,8 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
search: filters.searchTerm || 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(
statsDateRange,
@ -245,7 +249,8 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
filters.approverFilterType,
filters.dateRange,
filters.customStartDate,
filters.customEndDate
filters.customEndDate,
filters.templateTypeFilter
// 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,
statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
slaComplianceFilter: filters.slaComplianceFilter,
departmentFilter: filters.departmentFilter,
initiatorFilter: filters.initiatorFilter,
@ -282,6 +288,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
prev.searchTerm !== filters.searchTerm ||
prev.statusFilter !== filters.statusFilter ||
prev.priorityFilter !== filters.priorityFilter ||
prev.templateTypeFilter !== filters.templateTypeFilter ||
prev.slaComplianceFilter !== filters.slaComplianceFilter ||
prev.departmentFilter !== filters.departmentFilter ||
prev.initiatorFilter !== filters.initiatorFilter ||
@ -301,6 +308,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
searchTerm: filters.searchTerm,
statusFilter: filters.statusFilter,
priorityFilter: filters.priorityFilter,
templateTypeFilter: filters.templateTypeFilter,
slaComplianceFilter: filters.slaComplianceFilter,
departmentFilter: filters.departmentFilter,
initiatorFilter: filters.initiatorFilter,
@ -318,6 +326,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
filters.searchTerm,
filters.statusFilter,
filters.priorityFilter,
filters.templateTypeFilter,
filters.slaComplianceFilter,
filters.departmentFilter,
filters.initiatorFilter,
@ -431,7 +440,7 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
<Separator />
{/* 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">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<Input
@ -468,6 +477,17 @@ export function UserAllRequests({ onViewRequest }: RequestsProps) {
</SelectContent>
</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
value={filters.departmentFilter}
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';
if (templateTypeUpper === 'DEALER CLAIM') {
templateLabel = 'Claim Management';
templateLabel = 'Dealer Claim';
templateColor = 'bg-blue-100 !text-blue-700 border-blue-200';
} else if (templateTypeUpper === 'TEMPLATE') {
templateLabel = 'Template';

View File

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

View File

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

View File

@ -25,6 +25,7 @@ export async function fetchRequestsData({
if (filters?.search) backendFilters.search = filters.search;
if (filters?.status && filters.status !== 'all') backendFilters.status = filters.status;
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?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator;
if (filters?.approver && filters.approver !== 'all') {
@ -90,6 +91,7 @@ export async function fetchRequestsData({
if (filters?.search) backendFilters.search = filters.search;
if (filters?.status && filters.status !== 'all') backendFilters.status = filters.status;
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?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator;
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?.status && filters.status !== 'all') backendFilters.status = filters.status;
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?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator;
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?.status && filters.status !== 'all') backendFilters.status = filters.status;
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?.initiator && filters.initiator !== 'all') backendFilters.initiator = filters.initiator;
if (filters?.approver && filters.approver !== 'all') {

View File

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

View File

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

View File

@ -155,8 +155,8 @@ export async function createWorkflowMultipart(form: CreateWorkflowFromFormPayloa
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 } = {}) {
const { page = 1, limit = 20, search, status, priority, department, initiator, approver, slaCompliance, dateRange, startDate, endDate } = params;
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, templateType, department, initiator, approver, slaCompliance, dateRange, startDate, endDate } = params;
const res = await apiClient.get('/workflows', {
params: {
page,
@ -164,6 +164,7 @@ export async function listWorkflows(params: { page?: number; limit?: number; sea
search,
status,
priority,
templateType,
department,
initiator,
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
// 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 } = {}) {
const { page = 1, limit = 20, search, status, priority, department, initiator, approver, approverType, slaCompliance, dateRange, startDate, endDate } = params;
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, templateType, department, initiator, approver, approverType, slaCompliance, dateRange, startDate, endDate } = params;
const res = await apiClient.get('/workflows/participant-requests', {
params: {
page,
@ -187,6 +188,7 @@ export async function listParticipantRequests(params: { page?: number; limit?: n
search,
status,
priority,
templateType,
department,
initiator,
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
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 } = {}) {
const { page = 1, limit = 20, search, status, priority, department, slaCompliance, dateRange, startDate, endDate } = params;
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, templateType, department, slaCompliance, dateRange, startDate, endDate } = params;
const res = await apiClient.get('/workflows/my-initiated', {
params: {
page,
@ -241,6 +243,7 @@ export async function listMyInitiatedWorkflows(params: { page?: number; limit?:
search,
status,
priority,
templateType,
department,
slaCompliance,
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 } = {}) {
const { page = 1, limit = 20, search, status, priority, sortBy, sortOrder } = params;
const res = await apiClient.get('/workflows/open-for-me', { params: { page, limit, search, status, priority, sortBy, sortOrder } });
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, templateType, sortBy, sortOrder } = params;
const res = await apiClient.get('/workflows/open-for-me', { params: { page, limit, search, status, priority, templateType, sortBy, sortOrder } });
// Response structure: { success, data: { data: [...], pagination: {...} } }
return {
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 } = {}) {
const { page = 1, limit = 20, search, status, priority, sortBy, sortOrder } = params;
const res = await apiClient.get('/workflows/closed-by-me', { params: { page, limit, search, status, priority, sortBy, sortOrder } });
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, templateType, sortBy, sortOrder } = params;
const res = await apiClient.get('/workflows/closed-by-me', { params: { page, limit, search, status, priority, templateType, sortBy, sortOrder } });
// Response structure: { success, data: { data: [...], pagination: {...} } }
return {
data: res.data?.data?.data || res.data?.data || [],