Compare commits
No commits in common. "custom_production" and "main" have entirely different histories.
custom_pro
...
main
@ -10,7 +10,7 @@ import { SharedSummaryDetail } from '@/pages/SharedSummaries/SharedSummaryDetail
|
|||||||
import { WorkNotes } from '@/pages/WorkNotes';
|
import { WorkNotes } from '@/pages/WorkNotes';
|
||||||
import { CreateRequest } from '@/pages/CreateRequest';
|
import { CreateRequest } from '@/pages/CreateRequest';
|
||||||
import { ClaimManagementWizard } from '@/dealer-claim/components/request-creation/ClaimManagementWizard';
|
import { ClaimManagementWizard } from '@/dealer-claim/components/request-creation/ClaimManagementWizard';
|
||||||
// import { DealerDashboard } from '@/dealer-claim/pages/Dashboard';
|
import { DealerDashboard } from '@/dealer-claim/pages/Dashboard';
|
||||||
import { MyRequests } from '@/pages/MyRequests';
|
import { MyRequests } from '@/pages/MyRequests';
|
||||||
import { Requests } from '@/pages/Requests/Requests';
|
import { Requests } from '@/pages/Requests/Requests';
|
||||||
import { UserAllRequests } from '@/pages/Requests/UserAllRequests';
|
import { UserAllRequests } from '@/pages/Requests/UserAllRequests';
|
||||||
@ -84,8 +84,7 @@ function DashboardRoute({ onNavigate, onNewRequest }: { onNavigate?: (page: stri
|
|||||||
|
|
||||||
// Render dealer-specific dashboard if user is a dealer
|
// Render dealer-specific dashboard if user is a dealer
|
||||||
if (isDealer) {
|
if (isDealer) {
|
||||||
console.log("isDealer", isDealer)
|
return <DealerDashboard onNavigate={onNavigate} onNewRequest={onNewRequest} />;
|
||||||
// return <DealerDashboard onNavigate={onNavigate} onNewRequest={onNewRequest} />;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render regular dashboard for all other users
|
// Render regular dashboard for all other users
|
||||||
|
|||||||
@ -72,15 +72,14 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on
|
|||||||
const items = [
|
const items = [
|
||||||
{ id: 'dashboard', label: 'Dashboard', icon: Home },
|
{ id: 'dashboard', label: 'Dashboard', icon: Home },
|
||||||
// Add "All Requests" for all users (admin sees org-level, regular users see their participant requests)
|
// Add "All Requests" for all users (admin sees org-level, regular users see their participant requests)
|
||||||
{ id: 'requests', label: 'All Requests', icon: List, adminOnly: false },
|
{ id: 'requests', label: 'All Requests', icon: List, adminOnly: false }
|
||||||
{ id: 'my-requests', label: 'My Requests', icon: User }
|
|
||||||
// { id: 'admin/templates', label: 'Admin Templates', icon: Plus, adminOnly: true },
|
// { id: 'admin/templates', label: 'Admin Templates', icon: Plus, adminOnly: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Add remaining menu items (exclude "My Requests" for dealers)
|
// Add remaining menu items (exclude "My Requests" for dealers)
|
||||||
// if (!isDealer) {
|
if (!isDealer) {
|
||||||
// items.push({ id: 'my-requests', label: 'My Requests', icon: User });
|
items.push({ id: 'my-requests', label: 'My Requests', icon: User });
|
||||||
// }
|
}
|
||||||
|
|
||||||
items.push(
|
items.push(
|
||||||
{ id: 'open-requests', label: 'Open Requests', icon: FileText },
|
{ id: 'open-requests', label: 'Open Requests', icon: FileText },
|
||||||
@ -108,20 +107,6 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on
|
|||||||
|
|
||||||
// Navigate to the request if URL provided
|
// Navigate to the request if URL provided
|
||||||
if (notification.actionUrl && onNavigate) {
|
if (notification.actionUrl && onNavigate) {
|
||||||
// PRIORITY: For shared summaries or specific action URLs, use them directly
|
|
||||||
if (notification.actionUrl && (
|
|
||||||
notification.notificationType === 'summary_shared' ||
|
|
||||||
notification.actionUrl.includes('shared-summaries')
|
|
||||||
)) {
|
|
||||||
console.log('[PageLayout] Navigating to shared summary:', notification.actionUrl);
|
|
||||||
const targetUrl = notification.actionUrl.startsWith('/')
|
|
||||||
? notification.actionUrl.substring(1)
|
|
||||||
: notification.actionUrl;
|
|
||||||
onNavigate(targetUrl);
|
|
||||||
setNotificationsOpen(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract request number from URL (e.g., /request/REQ-2025-12345)
|
// Extract request number from URL (e.g., /request/REQ-2025-12345)
|
||||||
const requestNumber = notification.metadata?.requestNumber;
|
const requestNumber = notification.metadata?.requestNumber;
|
||||||
if (requestNumber) {
|
if (requestNumber) {
|
||||||
@ -290,7 +275,7 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quick Action in Sidebar - Right below menu items */}
|
{/* Quick Action in Sidebar - Right below menu items */}
|
||||||
{/* {!isDealer && ( */}
|
{!isDealer && (
|
||||||
<div className="mt-6 pt-6 border-t border-gray-800 px-3">
|
<div className="mt-6 pt-6 border-t border-gray-800 px-3">
|
||||||
<Button
|
<Button
|
||||||
onClick={onNewRequest}
|
onClick={onNewRequest}
|
||||||
@ -301,7 +286,7 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on
|
|||||||
Raise New Request
|
Raise New Request
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{/* )} */}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@ -79,10 +79,10 @@ interface WorkNoteChatProps {
|
|||||||
skipSocketJoin?: boolean; // Set to true when embedded in RequestDetail (to avoid double join)
|
skipSocketJoin?: boolean; // Set to true when embedded in RequestDetail (to avoid double join)
|
||||||
requestTitle?: string; // Optional title for display
|
requestTitle?: string; // Optional title for display
|
||||||
onAttachmentsExtracted?: (attachments: any[]) => void; // Callback to pass attachments to parent
|
onAttachmentsExtracted?: (attachments: any[]) => void; // Callback to pass attachments to parent
|
||||||
|
isInitiator?: boolean; // Whether current user is the initiator
|
||||||
isSpectator?: boolean; // Whether current user is a spectator (view-only)
|
isSpectator?: boolean; // Whether current user is a spectator (view-only)
|
||||||
currentLevels?: any[]; // Current approval levels for add approver modal
|
currentLevels?: any[]; // Current approval levels for add approver modal
|
||||||
onAddApprover?: (email: string, tatHours: number, level: number) => Promise<void>; // Callback to add approver
|
onAddApprover?: (email: string, tatHours: number, level: number) => Promise<void>; // Callback to add approver
|
||||||
canAddApprover?: boolean; // Whether the current user can add approvers
|
|
||||||
maxApprovalLevels?: number; // Maximum allowed approval levels from system policy
|
maxApprovalLevels?: number; // Maximum allowed approval levels from system policy
|
||||||
onPolicyViolation?: (violations: Array<{ type: string; message: string; currentValue?: number; maxValue?: number }>) => void; // Callback for policy violations
|
onPolicyViolation?: (violations: Array<{ type: string; message: string; currentValue?: number; maxValue?: number }>) => void; // Callback for policy violations
|
||||||
}
|
}
|
||||||
@ -145,7 +145,7 @@ const FileIcon = ({ type }: { type: string }) => {
|
|||||||
return <Paperclip className={`${iconClass} text-gray-600`} />;
|
return <Paperclip className={`${iconClass} text-gray-600`} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function WorkNoteChat({ requestId, messages: externalMessages, onSend, skipSocketJoin = false, requestTitle, onAttachmentsExtracted, isSpectator = false, currentLevels = [], onAddApprover, canAddApprover, maxApprovalLevels, onPolicyViolation }: WorkNoteChatProps) {
|
export function WorkNoteChat({ requestId, messages: externalMessages, onSend, skipSocketJoin = false, requestTitle, onAttachmentsExtracted, isInitiator = false, isSpectator = false, currentLevels = [], onAddApprover, maxApprovalLevels, onPolicyViolation }: WorkNoteChatProps) {
|
||||||
const routeParams = useParams<{ requestId: string }>();
|
const routeParams = useParams<{ requestId: string }>();
|
||||||
const effectiveRequestId = requestId || routeParams.requestId || '';
|
const effectiveRequestId = requestId || routeParams.requestId || '';
|
||||||
const [message, setMessage] = useState('');
|
const [message, setMessage] = useState('');
|
||||||
@ -1743,8 +1743,8 @@ export function WorkNoteChat({ requestId, messages: externalMessages, onSend, sk
|
|||||||
<div className="p-4 sm:p-6 flex-shrink-0">
|
<div className="p-4 sm:p-6 flex-shrink-0">
|
||||||
<h4 className="font-semibold text-gray-900 mb-3 text-sm sm:text-base">Quick Actions</h4>
|
<h4 className="font-semibold text-gray-900 mb-3 text-sm sm:text-base">Quick Actions</h4>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{/* Only initiator or current approver can add approvers in custom flows */}
|
{/* Only initiator can add approvers */}
|
||||||
{canAddApprover && (
|
{isInitiator && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -1805,7 +1805,7 @@ export function WorkNoteChat({ requestId, messages: externalMessages, onSend, sk
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Add Approver Modal - Hide for spectators */}
|
{/* Add Approver Modal - Hide for spectators */}
|
||||||
{!effectiveIsSpectator && canAddApprover && (
|
{!effectiveIsSpectator && isInitiator && (
|
||||||
<AddApproverModal
|
<AddApproverModal
|
||||||
open={showAddApproverModal}
|
open={showAddApproverModal}
|
||||||
onClose={() => setShowAddApproverModal(false)}
|
onClose={() => setShowAddApproverModal(false)}
|
||||||
|
|||||||
@ -56,8 +56,7 @@ export function ApprovalWorkflowStep({
|
|||||||
email: '',
|
email: '',
|
||||||
name: '',
|
name: '',
|
||||||
level: i + 1,
|
level: i + 1,
|
||||||
tat: 8 as any,
|
tat: '' as any
|
||||||
tatType: 'hours'
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,9 +373,9 @@ export function ApprovalWorkflowStep({
|
|||||||
<Input
|
<Input
|
||||||
id={`tat-${level}`}
|
id={`tat-${level}`}
|
||||||
type="number"
|
type="number"
|
||||||
placeholder={approver.tatType === 'days' ? '1' : '8'}
|
placeholder={approver.tatType === 'days' ? '7' : '24'}
|
||||||
min="1"
|
min="1"
|
||||||
max={approver.tatType === 'days' ? '90' : '720'}
|
max={approver.tatType === 'days' ? '30' : '720'}
|
||||||
value={approver.tat || ''}
|
value={approver.tat || ''}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const newApprovers = [...formData.approvers];
|
const newApprovers = [...formData.approvers];
|
||||||
@ -447,7 +446,28 @@ export function ApprovalWorkflowStep({
|
|||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<h4 className="font-semibold text-emerald-900">TAT Summary</h4>
|
<h4 className="font-semibold text-emerald-900">TAT Summary</h4>
|
||||||
<div>
|
<div className="text-right">
|
||||||
|
{(() => {
|
||||||
|
// Calculate total calendar days (for display)
|
||||||
|
// Days: count as calendar days
|
||||||
|
// Hours: convert to calendar days (hours / 24)
|
||||||
|
const totalCalendarDays = formData.approvers?.reduce((sum: number, a: any) => {
|
||||||
|
const tat = Number(a.tat || 0);
|
||||||
|
const tatType = a.tatType || 'hours';
|
||||||
|
if (tatType === 'days') {
|
||||||
|
return sum + tat; // Calendar days
|
||||||
|
} else {
|
||||||
|
return sum + (tat / 24); // Convert hours to calendar days
|
||||||
|
}
|
||||||
|
}, 0) || 0;
|
||||||
|
const displayDays = Math.ceil(totalCalendarDays);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="text-lg font-bold text-emerald-800">{displayDays} {displayDays === 1 ? 'Day' : 'Days'}</div>
|
||||||
|
<div className="text-xs text-emerald-600">Total Duration</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
@ -455,7 +475,8 @@ export function ApprovalWorkflowStep({
|
|||||||
{formData.approvers?.map((approver: any, idx: number) => {
|
{formData.approvers?.map((approver: any, idx: number) => {
|
||||||
const tat = Number(approver.tat || 0);
|
const tat = Number(approver.tat || 0);
|
||||||
const tatType = approver.tatType || 'hours';
|
const tatType = approver.tatType || 'hours';
|
||||||
const hours = tatType === 'days' ? tat * 8 : tat;
|
// Convert days to hours: 1 day = 24 hours
|
||||||
|
const hours = tatType === 'days' ? tat * 24 : tat;
|
||||||
if (!tat) return null;
|
if (!tat) return null;
|
||||||
return (
|
return (
|
||||||
<div key={idx} className="bg-white/60 p-2 rounded border border-emerald-100">
|
<div key={idx} className="bg-white/60 p-2 rounded border border-emerald-100">
|
||||||
@ -468,12 +489,15 @@ export function ApprovalWorkflowStep({
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
{(() => {
|
{(() => {
|
||||||
|
// Convert all TAT to hours first
|
||||||
|
// Days: 1 day = 24 hours
|
||||||
|
// Hours: already in hours
|
||||||
const totalHours = formData.approvers?.reduce((sum: number, a: any) => {
|
const totalHours = formData.approvers?.reduce((sum: number, a: any) => {
|
||||||
const tat = Number(a.tat || 0);
|
const tat = Number(a.tat || 0);
|
||||||
const tatType = a.tatType || 'hours';
|
const tatType = a.tatType || 'hours';
|
||||||
if (tatType === 'days') {
|
if (tatType === 'days') {
|
||||||
// 1 day = 8 working hours
|
// 1 day = 24 hours
|
||||||
return sum + (tat * 8);
|
return sum + (tat * 24);
|
||||||
} else {
|
} else {
|
||||||
return sum + tat;
|
return sum + tat;
|
||||||
}
|
}
|
||||||
@ -485,12 +509,12 @@ export function ApprovalWorkflowStep({
|
|||||||
<div className="bg-white/80 p-3 rounded border border-emerald-200">
|
<div className="bg-white/80 p-3 rounded border border-emerald-200">
|
||||||
<div className="grid grid-cols-2 gap-4 text-center">
|
<div className="grid grid-cols-2 gap-4 text-center">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-lg font-bold text-emerald-800">{totalHours}h</div>
|
<div className="text-lg font-bold text-emerald-800">{totalHours}{totalHours === 1 ? 'h' : 'h'}</div>
|
||||||
<div className="text-xs text-emerald-600">Total Hours</div>
|
<div className="text-xs text-emerald-600">Total Hours</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-lg font-bold text-emerald-800">{workingDays} {workingDays === 1 ? 'Day' : 'Days'}</div>
|
<div className="text-lg font-bold text-emerald-800">{workingDays}</div>
|
||||||
<div className="text-xs text-emerald-600">Total Days*</div>
|
<div className="text-xs text-emerald-600">Working Days*</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-emerald-600 mt-2 text-center">*Based on 8-hour working days</p>
|
<p className="text-xs text-emerald-600 mt-2 text-center">*Based on 8-hour working days</p>
|
||||||
|
|||||||
@ -107,8 +107,8 @@ export function TemplateSelectionStep({
|
|||||||
<p>No admin templates available yet.</p>
|
<p>No admin templates available yet.</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
displayTemplates.map((template, index) => {
|
displayTemplates.map((template) => {
|
||||||
const isComingSoon = index === 1;
|
const isComingSoon = false;
|
||||||
const isDisabled = isComingSoon;
|
const isDisabled = isComingSoon;
|
||||||
const isCategoryCard = template.id === 'admin-templates-category';
|
const isCategoryCard = template.id === 'admin-templates-category';
|
||||||
// const isCustomCard = template.id === 'custom';
|
// const isCustomCard = template.id === 'custom';
|
||||||
@ -124,7 +124,7 @@ export function TemplateSelectionStep({
|
|||||||
>
|
>
|
||||||
<Card
|
<Card
|
||||||
className={`h-full transition-all duration-300 border-2 ${isDisabled
|
className={`h-full transition-all duration-300 border-2 ${isDisabled
|
||||||
? 'border-gray-200 bg-gray-50/50 opacity-50 cursor-not-allowed'
|
? 'border-gray-200 bg-gray-50/50 opacity-85 cursor-not-allowed'
|
||||||
: isSelected
|
: isSelected
|
||||||
? 'border-blue-500 shadow-xl bg-blue-50/50 ring-2 ring-blue-200 cursor-pointer'
|
? 'border-blue-500 shadow-xl bg-blue-50/50 ring-2 ring-blue-200 cursor-pointer'
|
||||||
: isCategoryCard
|
: isCategoryCard
|
||||||
|
|||||||
@ -31,14 +31,14 @@ export function StandardClosedRequestsFilters({
|
|||||||
searchTerm,
|
searchTerm,
|
||||||
priorityFilter,
|
priorityFilter,
|
||||||
statusFilter,
|
statusFilter,
|
||||||
templateTypeFilter: _templateTypeFilter,
|
templateTypeFilter,
|
||||||
sortBy,
|
sortBy,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
activeFiltersCount,
|
activeFiltersCount,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
onPriorityChange,
|
onPriorityChange,
|
||||||
onStatusChange,
|
onStatusChange,
|
||||||
onTemplateTypeChange: _onTemplateTypeChange,
|
onTemplateTypeChange,
|
||||||
onSortByChange,
|
onSortByChange,
|
||||||
onSortOrderChange,
|
onSortOrderChange,
|
||||||
onClearFilters,
|
onClearFilters,
|
||||||
@ -131,7 +131,7 @@ export function StandardClosedRequestsFilters({
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
{/* <Select value={_templateTypeFilter} onValueChange={_onTemplateTypeChange}>
|
<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">
|
<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" />
|
<SelectValue placeholder="All Templates" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@ -140,7 +140,7 @@ export function StandardClosedRequestsFilters({
|
|||||||
<SelectItem value="CUSTOM">Non-Templatized</SelectItem>
|
<SelectItem value="CUSTOM">Non-Templatized</SelectItem>
|
||||||
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
|
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select> */}
|
</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')}>
|
||||||
|
|||||||
@ -31,13 +31,13 @@ export function StandardRequestsFilters({
|
|||||||
searchTerm,
|
searchTerm,
|
||||||
statusFilter,
|
statusFilter,
|
||||||
priorityFilter,
|
priorityFilter,
|
||||||
templateTypeFilter: _templateTypeFilter,
|
templateTypeFilter,
|
||||||
sortBy,
|
sortBy,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
onStatusFilterChange,
|
onStatusFilterChange,
|
||||||
onPriorityFilterChange,
|
onPriorityFilterChange,
|
||||||
onTemplateTypeFilterChange: _onTemplateTypeFilterChange,
|
onTemplateTypeFilterChange,
|
||||||
onSortByChange,
|
onSortByChange,
|
||||||
onSortOrderChange,
|
onSortOrderChange,
|
||||||
onClearFilters,
|
onClearFilters,
|
||||||
@ -120,7 +120,7 @@ export function StandardRequestsFilters({
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
{/* <Select value={templateTypeFilter} onValueChange={onTemplateTypeFilterChange}>
|
<Select value={templateTypeFilter} onValueChange={onTemplateTypeFilterChange}>
|
||||||
<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">
|
||||||
<SelectValue placeholder="All Templates" />
|
<SelectValue placeholder="All Templates" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@ -129,7 +129,7 @@ export function StandardRequestsFilters({
|
|||||||
<SelectItem value="CUSTOM">Non-Templatized</SelectItem>
|
<SelectItem value="CUSTOM">Non-Templatized</SelectItem>
|
||||||
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
|
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select> */}
|
</Select>
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Select value={sortBy} onValueChange={(value: any) => onSortByChange(value)}>
|
<Select value={sortBy} onValueChange={(value: any) => onSortByChange(value)}>
|
||||||
|
|||||||
@ -87,7 +87,7 @@ export function StandardUserAllRequestsFilters({
|
|||||||
searchTerm,
|
searchTerm,
|
||||||
statusFilter,
|
statusFilter,
|
||||||
priorityFilter,
|
priorityFilter,
|
||||||
templateTypeFilter: _templateTypeFilter,
|
templateTypeFilter,
|
||||||
departmentFilter,
|
departmentFilter,
|
||||||
slaComplianceFilter,
|
slaComplianceFilter,
|
||||||
initiatorFilter: _initiatorFilter,
|
initiatorFilter: _initiatorFilter,
|
||||||
@ -104,7 +104,7 @@ export function StandardUserAllRequestsFilters({
|
|||||||
onSearchChange,
|
onSearchChange,
|
||||||
onStatusChange,
|
onStatusChange,
|
||||||
onPriorityChange,
|
onPriorityChange,
|
||||||
onTemplateTypeChange: _onTemplateTypeChange,
|
onTemplateTypeChange,
|
||||||
onDepartmentChange,
|
onDepartmentChange,
|
||||||
onSlaComplianceChange,
|
onSlaComplianceChange,
|
||||||
onInitiatorChange: _onInitiatorChange,
|
onInitiatorChange: _onInitiatorChange,
|
||||||
@ -180,7 +180,7 @@ export function StandardUserAllRequestsFilters({
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
{/* <Select value={_templateTypeFilter} onValueChange={_onTemplateTypeChange}>
|
<Select value={templateTypeFilter} onValueChange={onTemplateTypeChange}>
|
||||||
<SelectTrigger className="h-10" data-testid="template-type-filter">
|
<SelectTrigger className="h-10" data-testid="template-type-filter">
|
||||||
<SelectValue placeholder="All Templates" />
|
<SelectValue placeholder="All Templates" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@ -189,7 +189,7 @@ export function StandardUserAllRequestsFilters({
|
|||||||
<SelectItem value="CUSTOM">Custom</SelectItem>
|
<SelectItem value="CUSTOM">Custom</SelectItem>
|
||||||
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
|
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select> */}
|
</Select>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
value={departmentFilter}
|
value={departmentFilter}
|
||||||
|
|||||||
@ -47,9 +47,6 @@ import { CustomOverviewTab, CustomWorkflowTab } from '../index';
|
|||||||
import { SharedComponents } from '@/shared/components';
|
import { SharedComponents } from '@/shared/components';
|
||||||
const { DocumentsTab, ActivityTab, WorkNotesTab, SummaryTab, RequestDetailHeader, QuickActionsSidebar, RequestDetailModals } = SharedComponents;
|
const { DocumentsTab, ActivityTab, WorkNotesTab, SummaryTab, RequestDetailHeader, QuickActionsSidebar, RequestDetailModals } = SharedComponents;
|
||||||
|
|
||||||
// Utilities
|
|
||||||
import { isCustomRequest } from '@/utils/requestTypeUtils';
|
|
||||||
|
|
||||||
// Other components
|
// Other components
|
||||||
import { ShareSummaryModal } from '@/components/modals/ShareSummaryModal';
|
import { ShareSummaryModal } from '@/components/modals/ShareSummaryModal';
|
||||||
import { getSummaryDetails, getSummaryByRequestId, type SummaryDetails } from '@/services/summaryApi';
|
import { getSummaryDetails, getSummaryByRequestId, type SummaryDetails } from '@/services/summaryApi';
|
||||||
@ -572,10 +569,10 @@ function CustomRequestDetailInner({ requestId: propRequestId, onBack, dynamicReq
|
|||||||
requestTitle={request.title}
|
requestTitle={request.title}
|
||||||
mergedMessages={mergedMessages}
|
mergedMessages={mergedMessages}
|
||||||
setWorkNoteAttachments={setWorkNoteAttachments}
|
setWorkNoteAttachments={setWorkNoteAttachments}
|
||||||
|
isInitiator={isInitiator}
|
||||||
isSpectator={isSpectator}
|
isSpectator={isSpectator}
|
||||||
currentLevels={currentLevels}
|
currentLevels={currentLevels}
|
||||||
onAddApprover={handleAddApprover}
|
onAddApprover={handleAddApprover}
|
||||||
canAddApprover={(isInitiator || !!currentApprovalLevel) && isCustomRequest(apiRequest)}
|
|
||||||
maxApprovalLevels={systemPolicy.maxApprovalLevels}
|
maxApprovalLevels={systemPolicy.maxApprovalLevels}
|
||||||
onPolicyViolation={(violations) => setPolicyViolationModal({ open: true, violations })}
|
onPolicyViolation={(violations) => setPolicyViolationModal({ open: true, violations })}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -661,10 +661,10 @@ function DealerClaimRequestDetailInner({ requestId: propRequestId, onBack, dynam
|
|||||||
requestTitle={request.title}
|
requestTitle={request.title}
|
||||||
mergedMessages={mergedMessages}
|
mergedMessages={mergedMessages}
|
||||||
setWorkNoteAttachments={setWorkNoteAttachments}
|
setWorkNoteAttachments={setWorkNoteAttachments}
|
||||||
|
isInitiator={isInitiator}
|
||||||
isSpectator={isSpectator}
|
isSpectator={isSpectator}
|
||||||
currentLevels={currentLevels}
|
currentLevels={currentLevels}
|
||||||
onAddApprover={handleAddApprover}
|
onAddApprover={handleAddApprover}
|
||||||
canAddApprover={false} // Explicitly disabled for Dealer Claims
|
|
||||||
maxApprovalLevels={systemPolicy.maxApprovalLevels}
|
maxApprovalLevels={systemPolicy.maxApprovalLevels}
|
||||||
onPolicyViolation={(violations) => setPolicyViolationModal({ open: true, violations })}
|
onPolicyViolation={(violations) => setPolicyViolationModal({ open: true, violations })}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -2,13 +2,13 @@ import { useState, useEffect } from 'react';
|
|||||||
import { useAuth } from '@/contexts/AuthContext';
|
import { useAuth } from '@/contexts/AuthContext';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||||
import { LogIn } from 'lucide-react';
|
import { LogIn, Shield } from 'lucide-react';
|
||||||
import { ReLogo, LandingPageImage } from '@/assets';
|
import { ReLogo, LandingPageImage } from '@/assets';
|
||||||
// import { initiateTanflowLogin } from '@/services/tanflowAuth';
|
import { initiateTanflowLogin } from '@/services/tanflowAuth';
|
||||||
|
|
||||||
export function Auth() {
|
export function Auth() {
|
||||||
const { login, isLoading, error } = useAuth();
|
const { login, isLoading, error } = useAuth();
|
||||||
const [tanflowLoading] = useState(false);
|
const [tanflowLoading, setTanflowLoading] = useState(false);
|
||||||
const [imageLoaded, setImageLoaded] = useState(false);
|
const [imageLoaded, setImageLoaded] = useState(false);
|
||||||
|
|
||||||
// Preload the background image
|
// Preload the background image
|
||||||
@ -41,21 +41,21 @@ export function Auth() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// const handleTanflowLogin = () => {
|
const handleTanflowLogin = () => {
|
||||||
// // Clear any existing session data
|
// Clear any existing session data
|
||||||
// localStorage.clear();
|
localStorage.clear();
|
||||||
// sessionStorage.clear();
|
sessionStorage.clear();
|
||||||
|
|
||||||
// setTanflowLoading(true);
|
setTanflowLoading(true);
|
||||||
// try {
|
try {
|
||||||
// initiateTanflowLogin();
|
initiateTanflowLogin();
|
||||||
// } catch (loginError) {
|
} catch (loginError) {
|
||||||
// console.error('========================================');
|
console.error('========================================');
|
||||||
// console.error('TANFLOW LOGIN ERROR');
|
console.error('TANFLOW LOGIN ERROR');
|
||||||
// console.error('Error details:', loginError);
|
console.error('Error details:', loginError);
|
||||||
// setTanflowLoading(false);
|
setTanflowLoading(false);
|
||||||
// }
|
}
|
||||||
// };
|
};
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Auth Error in Auth Component:', {
|
console.error('Auth Error in Auth Component:', {
|
||||||
@ -124,7 +124,7 @@ export function Auth() {
|
|||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* <div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute inset-0 flex items-center">
|
<div className="absolute inset-0 flex items-center">
|
||||||
<span className="w-full border-t border-gray-700"></span>
|
<span className="w-full border-t border-gray-700"></span>
|
||||||
</div>
|
</div>
|
||||||
@ -152,7 +152,7 @@ export function Auth() {
|
|||||||
Dealer Login
|
Dealer Login
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Button> */}
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-center text-sm text-gray-400 mt-4">
|
<div className="text-center text-sm text-gray-400 mt-4">
|
||||||
|
|||||||
@ -22,11 +22,11 @@ export function MyRequestsFilters({
|
|||||||
searchTerm,
|
searchTerm,
|
||||||
statusFilter,
|
statusFilter,
|
||||||
priorityFilter,
|
priorityFilter,
|
||||||
templateTypeFilter: _templateTypeFilter,
|
templateTypeFilter,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
onStatusChange,
|
onStatusChange,
|
||||||
onPriorityChange,
|
onPriorityChange,
|
||||||
onTemplateTypeChange: _onTemplateTypeChange,
|
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">
|
||||||
@ -76,7 +76,7 @@ export function MyRequestsFilters({
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
{/* <Select value={_templateTypeFilter} onValueChange={_onTemplateTypeChange}>
|
<Select value={templateTypeFilter} onValueChange={onTemplateTypeChange}>
|
||||||
<SelectTrigger
|
<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"
|
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"
|
data-testid="template-type-filter"
|
||||||
@ -88,7 +88,7 @@ export function MyRequestsFilters({
|
|||||||
<SelectItem value="CUSTOM">Non-Templatized</SelectItem>
|
<SelectItem value="CUSTOM">Non-Templatized</SelectItem>
|
||||||
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
|
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select> */}
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@ -76,19 +76,6 @@ export function Notifications({ onNavigate }: NotificationsProps) {
|
|||||||
|
|
||||||
// Navigate to the request if URL provided
|
// Navigate to the request if URL provided
|
||||||
if (notification.actionUrl && onNavigate) {
|
if (notification.actionUrl && onNavigate) {
|
||||||
// PRIORITY: For shared summaries or specific action URLs, use them directly
|
|
||||||
if (notification.actionUrl && (
|
|
||||||
notification.notificationType === 'summary_shared' ||
|
|
||||||
notification.actionUrl.includes('shared-summaries')
|
|
||||||
)) {
|
|
||||||
console.log('[Notifications] Navigating to shared summary:', notification.actionUrl);
|
|
||||||
const targetUrl = notification.actionUrl.startsWith('/')
|
|
||||||
? notification.actionUrl.substring(1)
|
|
||||||
: notification.actionUrl;
|
|
||||||
onNavigate(targetUrl);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestNumber = notification.metadata?.requestNumber;
|
const requestNumber = notification.metadata?.requestNumber;
|
||||||
if (requestNumber) {
|
if (requestNumber) {
|
||||||
let navigationUrl = `request/${requestNumber}`;
|
let navigationUrl = `request/${requestNumber}`;
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import notificationApi, { type Notification } from '@/services/notificationApi';
|
|||||||
import { ProcessDetailsCard } from '@/dealer-claim/components/request-detail/claim-cards';
|
import { ProcessDetailsCard } from '@/dealer-claim/components/request-detail/claim-cards';
|
||||||
import { isClaimManagementRequest } from '@/utils/claimRequestUtils';
|
import { isClaimManagementRequest } from '@/utils/claimRequestUtils';
|
||||||
import { determineUserRole, getRoleBasedVisibility, mapToClaimManagementRequest } from '@/utils/claimDataMapper';
|
import { determineUserRole, getRoleBasedVisibility, mapToClaimManagementRequest } from '@/utils/claimDataMapper';
|
||||||
import { isCustomRequest } from '@/utils/requestTypeUtils';
|
|
||||||
|
|
||||||
interface QuickActionsSidebarProps {
|
interface QuickActionsSidebarProps {
|
||||||
request: any;
|
request: any;
|
||||||
@ -146,8 +145,8 @@ export function QuickActionsSidebar({
|
|||||||
<CardTitle className="text-sm sm:text-base">Quick Actions</CardTitle>
|
<CardTitle className="text-sm sm:text-base">Quick Actions</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-2">
|
<CardContent className="space-y-2">
|
||||||
{/* Add Approver - Only for Initiator or Current Approver in Custom Workflows */}
|
{/* Add Approver */}
|
||||||
{(isInitiator || currentApprovalLevel) && isCustomRequest(request) && request.status !== 'closed' && (
|
{isInitiator && request.status !== 'closed' && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="w-full justify-start gap-2 bg-white text-gray-700 border-gray-300 hover:bg-gray-50 hover:text-gray-900 h-9 sm:h-10 text-xs sm:text-sm"
|
className="w-full justify-start gap-2 bg-white text-gray-700 border-gray-300 hover:bg-gray-50 hover:text-gray-900 h-9 sm:h-10 text-xs sm:text-sm"
|
||||||
|
|||||||
@ -9,10 +9,10 @@ interface WorkNotesTabProps {
|
|||||||
requestTitle: string;
|
requestTitle: string;
|
||||||
mergedMessages: any[];
|
mergedMessages: any[];
|
||||||
setWorkNoteAttachments: (attachments: any[]) => void;
|
setWorkNoteAttachments: (attachments: any[]) => void;
|
||||||
|
isInitiator: boolean;
|
||||||
isSpectator: boolean;
|
isSpectator: boolean;
|
||||||
currentLevels: any[];
|
currentLevels: any[];
|
||||||
onAddApprover: (email: string, tatHours: number, level: number) => Promise<void>;
|
onAddApprover: (email: string, tatHours: number, level: number) => Promise<void>;
|
||||||
canAddApprover?: boolean;
|
|
||||||
maxApprovalLevels?: number;
|
maxApprovalLevels?: number;
|
||||||
onPolicyViolation?: (violations: Array<{ type: string; message: string; currentValue?: number; maxValue?: number }>) => void;
|
onPolicyViolation?: (violations: Array<{ type: string; message: string; currentValue?: number; maxValue?: number }>) => void;
|
||||||
}
|
}
|
||||||
@ -22,10 +22,10 @@ export function WorkNotesTab({
|
|||||||
requestTitle,
|
requestTitle,
|
||||||
mergedMessages,
|
mergedMessages,
|
||||||
setWorkNoteAttachments,
|
setWorkNoteAttachments,
|
||||||
|
isInitiator,
|
||||||
isSpectator,
|
isSpectator,
|
||||||
currentLevels,
|
currentLevels,
|
||||||
onAddApprover,
|
onAddApprover,
|
||||||
canAddApprover,
|
|
||||||
maxApprovalLevels,
|
maxApprovalLevels,
|
||||||
onPolicyViolation,
|
onPolicyViolation,
|
||||||
}: WorkNotesTabProps) {
|
}: WorkNotesTabProps) {
|
||||||
@ -37,10 +37,10 @@ export function WorkNotesTab({
|
|||||||
skipSocketJoin={true}
|
skipSocketJoin={true}
|
||||||
messages={mergedMessages}
|
messages={mergedMessages}
|
||||||
onAttachmentsExtracted={setWorkNoteAttachments}
|
onAttachmentsExtracted={setWorkNoteAttachments}
|
||||||
|
isInitiator={isInitiator}
|
||||||
isSpectator={isSpectator}
|
isSpectator={isSpectator}
|
||||||
currentLevels={currentLevels}
|
currentLevels={currentLevels}
|
||||||
onAddApprover={onAddApprover}
|
onAddApprover={onAddApprover}
|
||||||
canAddApprover={canAddApprover}
|
|
||||||
maxApprovalLevels={maxApprovalLevels}
|
maxApprovalLevels={maxApprovalLevels}
|
||||||
onPolicyViolation={onPolicyViolation}
|
onPolicyViolation={onPolicyViolation}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -574,7 +574,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
{/* <Select value={filters.templateTypeFilter} onValueChange={filters.setTemplateTypeFilter}>
|
<Select value={filters.templateTypeFilter} onValueChange={filters.setTemplateTypeFilter}>
|
||||||
<SelectTrigger className="h-10" data-testid="template-type-filter">
|
<SelectTrigger className="h-10" data-testid="template-type-filter">
|
||||||
<SelectValue placeholder="All Templates" />
|
<SelectValue placeholder="All Templates" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@ -583,7 +583,7 @@ export function Requests({ onViewRequest }: RequestsProps) {
|
|||||||
<SelectItem value="CUSTOM">Non-Templatized</SelectItem>
|
<SelectItem value="CUSTOM">Non-Templatized</SelectItem>
|
||||||
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
|
<SelectItem value="DEALER CLAIM">Dealer Claim</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select> */}
|
</Select>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
value={filters.departmentFilter}
|
value={filters.departmentFilter}
|
||||||
|
|||||||
@ -200,14 +200,14 @@ export function Settings() {
|
|||||||
<span className="hidden sm:inline">Holidays</span>
|
<span className="hidden sm:inline">Holidays</span>
|
||||||
<span className="sm:hidden">Holidays</span>
|
<span className="sm:hidden">Holidays</span>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
{/* <TabsTrigger
|
<TabsTrigger
|
||||||
value="templates"
|
value="templates"
|
||||||
className="flex items-center justify-center gap-2 py-3 rounded-lg data-[state=active]:bg-white data-[state=active]:shadow-md transition-all"
|
className="flex items-center justify-center gap-2 py-3 rounded-lg data-[state=active]:bg-white data-[state=active]:shadow-md transition-all"
|
||||||
>
|
>
|
||||||
<FileText className="w-4 h-4" />
|
<FileText className="w-4 h-4" />
|
||||||
<span className="hidden sm:inline">Templates</span>
|
<span className="hidden sm:inline">Templates</span>
|
||||||
<span className="sm:hidden">Templates</span>
|
<span className="sm:hidden">Templates</span>
|
||||||
</TabsTrigger> */}
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
{/* Fixed width container to prevent layout shifts */}
|
{/* Fixed width container to prevent layout shifts */}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user