save draft an submit rquest adddd isDraft flag to support postman submit and dealer related code commented and made it completely non-templatized for production
This commit is contained in:
parent
1d205a4038
commit
c97053e0e3
@ -73,13 +73,14 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on
|
|||||||
{ 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 },
|
{ id: 'requests', label: 'All Requests', icon: List },
|
||||||
{ id: 'admin/templates', label: 'Admin Templates', icon: Plus, adminOnly: true },
|
{ id: 'my-requests', label: 'My Requests', icon: User, adminOnly: false },
|
||||||
|
// { 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 },
|
||||||
|
|||||||
@ -378,7 +378,7 @@ export function NewRequestModal({ open, onClose, onSubmit }: NewRequestModalProp
|
|||||||
<div className="border-2 border-dashed border-border rounded-lg p-6 text-center">
|
<div className="border-2 border-dashed border-border rounded-lg p-6 text-center">
|
||||||
<Upload className="h-8 w-8 mx-auto mb-2 text-muted-foreground" />
|
<Upload className="h-8 w-8 mx-auto mb-2 text-muted-foreground" />
|
||||||
<p className="text-sm text-muted-foreground mb-2">
|
<p className="text-sm text-muted-foreground mb-2">
|
||||||
Drag and drop files here, or click to browse
|
click to browse
|
||||||
</p>
|
</p>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
|
|||||||
@ -111,16 +111,16 @@ export function DocumentsStep({
|
|||||||
const type = (doc.fileType || doc.file_type || '').toLowerCase();
|
const type = (doc.fileType || doc.file_type || '').toLowerCase();
|
||||||
const name = (doc.originalFileName || doc.fileName || '').toLowerCase();
|
const name = (doc.originalFileName || doc.fileName || '').toLowerCase();
|
||||||
return type.includes('image') || type.includes('pdf') ||
|
return type.includes('image') || type.includes('pdf') ||
|
||||||
name.endsWith('.jpg') || name.endsWith('.jpeg') ||
|
name.endsWith('.jpg') || name.endsWith('.jpeg') ||
|
||||||
name.endsWith('.png') || name.endsWith('.gif') ||
|
name.endsWith('.png') || name.endsWith('.gif') ||
|
||||||
name.endsWith('.pdf');
|
name.endsWith('.pdf');
|
||||||
} else {
|
} else {
|
||||||
const type = (doc.type || '').toLowerCase();
|
const type = (doc.type || '').toLowerCase();
|
||||||
const name = (doc.name || '').toLowerCase();
|
const name = (doc.name || '').toLowerCase();
|
||||||
return type.includes('image') || type.includes('pdf') ||
|
return type.includes('image') || type.includes('pdf') ||
|
||||||
name.endsWith('.jpg') || name.endsWith('.jpeg') ||
|
name.endsWith('.jpg') || name.endsWith('.jpeg') ||
|
||||||
name.endsWith('.png') || name.endsWith('.gif') ||
|
name.endsWith('.png') || name.endsWith('.gif') ||
|
||||||
name.endsWith('.pdf');
|
name.endsWith('.pdf');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ export function DocumentsStep({
|
|||||||
<Upload className="h-12 w-12 mx-auto mb-4 text-gray-400" />
|
<Upload className="h-12 w-12 mx-auto mb-4 text-gray-400" />
|
||||||
<h3 className="text-lg font-semibold text-gray-900 mb-2">Upload Files</h3>
|
<h3 className="text-lg font-semibold text-gray-900 mb-2">Upload Files</h3>
|
||||||
<p className="text-gray-600 mb-4">
|
<p className="text-gray-600 mb-4">
|
||||||
Drag and drop files here, or click to browse
|
click to browse
|
||||||
</p>
|
</p>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
|
|||||||
@ -52,18 +52,18 @@ export function TemplateSelectionStep({
|
|||||||
const displayTemplates = viewMode === 'main'
|
const displayTemplates = viewMode === 'main'
|
||||||
? [
|
? [
|
||||||
...templates,
|
...templates,
|
||||||
{
|
// {
|
||||||
id: 'admin-templates-category',
|
// id: 'admin-templates-category',
|
||||||
name: 'Admin Templates',
|
// name: 'Admin Templates',
|
||||||
description: 'Browse standardized request workflows created by your organization administrators',
|
// description: 'Browse standardized request workflows created by your organization administrators',
|
||||||
category: 'Organization',
|
// category: 'Organization',
|
||||||
icon: FolderOpen,
|
// icon: FolderOpen,
|
||||||
estimatedTime: 'Variable',
|
// estimatedTime: 'Variable',
|
||||||
commonApprovers: [],
|
// commonApprovers: [],
|
||||||
suggestedSLA: 0,
|
// suggestedSLA: 0,
|
||||||
priority: 'medium',
|
// priority: 'medium',
|
||||||
fields: {}
|
// fields: {}
|
||||||
} as any
|
// } as any
|
||||||
]
|
]
|
||||||
: adminTemplates;
|
: adminTemplates;
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +1,12 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Plus, Pencil, Trash2, Search, FileText, AlertTriangle } from 'lucide-react';
|
import { Plus, Pencil, Search, FileText } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import {
|
import { getTemplates, WorkflowTemplate, getCachedTemplates } from '@/services/workflowTemplateApi';
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
} from '@/components/ui/alert-dialog';
|
|
||||||
import { getTemplates, deleteTemplate, WorkflowTemplate, getCachedTemplates } from '@/services/workflowTemplateApi';
|
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
export function AdminTemplatesList() {
|
export function AdminTemplatesList() {
|
||||||
@ -25,8 +15,6 @@ export function AdminTemplatesList() {
|
|||||||
// Only show full loading skeleton if we don't have any data yet
|
// Only show full loading skeleton if we don't have any data yet
|
||||||
const [loading, setLoading] = useState(() => !getCachedTemplates());
|
const [loading, setLoading] = useState(() => !getCachedTemplates());
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [deleteId, setDeleteId] = useState<string | null>(null);
|
|
||||||
const [deleting, setDeleting] = useState(false);
|
|
||||||
|
|
||||||
const fetchTemplates = async () => {
|
const fetchTemplates = async () => {
|
||||||
try {
|
try {
|
||||||
@ -49,22 +37,6 @@ export function AdminTemplatesList() {
|
|||||||
fetchTemplates();
|
fetchTemplates();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleDelete = async () => {
|
|
||||||
if (!deleteId) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
setDeleting(true);
|
|
||||||
await deleteTemplate(deleteId);
|
|
||||||
toast.success('Template deleted successfully');
|
|
||||||
setTemplates(prev => prev.filter(t => t.id !== deleteId));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to delete template:', error);
|
|
||||||
toast.error('Failed to delete template');
|
|
||||||
} finally {
|
|
||||||
setDeleting(false);
|
|
||||||
setDeleteId(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const filteredTemplates = templates.filter(template =>
|
const filteredTemplates = templates.filter(template =>
|
||||||
template.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
template.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
@ -152,7 +124,7 @@ export function AdminTemplatesList() {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<CardTitle className="line-clamp-1 text-lg">{template.name}</CardTitle>
|
<CardTitle className="line-clamp-1 text-lg">{template.name}</CardTitle>
|
||||||
<CardDescription className="line-clamp-2 h-10">
|
<CardDescription className="line-clamp-3 min-h-[4.5rem]">
|
||||||
{template.description}
|
{template.description}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@ -181,14 +153,6 @@ export function AdminTemplatesList() {
|
|||||||
<Pencil className="w-4 h-4 mr-2" />
|
<Pencil className="w-4 h-4 mr-2" />
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
className="flex-1 text-red-600 hover:text-red-700 hover:bg-red-50 border-red-100"
|
|
||||||
onClick={() => setDeleteId(template.id)}
|
|
||||||
>
|
|
||||||
<Trash2 className="w-4 h-4 mr-2" />
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@ -196,33 +160,6 @@ export function AdminTemplatesList() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<AlertDialog open={!!deleteId} onOpenChange={(open) => !open && setDeleteId(null)}>
|
|
||||||
<AlertDialogContent>
|
|
||||||
<AlertDialogHeader>
|
|
||||||
<AlertDialogTitle className="flex items-center gap-2">
|
|
||||||
<AlertTriangle className="w-5 h-5 text-red-600" />
|
|
||||||
Delete Template
|
|
||||||
</AlertDialogTitle>
|
|
||||||
<AlertDialogDescription>
|
|
||||||
Are you sure you want to delete this template? This action cannot be undone.
|
|
||||||
Active requests using this template will not be affected.
|
|
||||||
</AlertDialogDescription>
|
|
||||||
</AlertDialogHeader>
|
|
||||||
<AlertDialogFooter>
|
|
||||||
<AlertDialogCancel disabled={deleting}>Cancel</AlertDialogCancel>
|
|
||||||
<AlertDialogAction
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
handleDelete();
|
|
||||||
}}
|
|
||||||
className="bg-red-600 hover:bg-red-700"
|
|
||||||
disabled={deleting}
|
|
||||||
>
|
|
||||||
{deleting ? 'Deleting...' : 'Delete'}
|
|
||||||
</AlertDialogAction>
|
|
||||||
</AlertDialogFooter>
|
|
||||||
</AlertDialogContent>
|
|
||||||
</AlertDialog>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,8 +11,6 @@ import {
|
|||||||
validateApproversForSubmission,
|
validateApproversForSubmission,
|
||||||
} from '../utils/payloadBuilders';
|
} from '../utils/payloadBuilders';
|
||||||
import {
|
import {
|
||||||
createAndSubmitWorkflow,
|
|
||||||
updateAndSubmitWorkflow,
|
|
||||||
createWorkflow,
|
createWorkflow,
|
||||||
updateWorkflowRequest,
|
updateWorkflowRequest,
|
||||||
} from '../services/createRequestService';
|
} from '../services/createRequestService';
|
||||||
@ -59,14 +57,15 @@ export function useCreateRequestSubmission({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (isEditing && editRequestId) {
|
if (isEditing && editRequestId) {
|
||||||
// Update existing workflow
|
// Update existing workflow with isDraft: false (Submit)
|
||||||
const updatePayload = buildUpdatePayload(
|
const updatePayload = buildUpdatePayload(
|
||||||
formData,
|
formData,
|
||||||
user,
|
user,
|
||||||
documentsToDelete
|
documentsToDelete,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
await updateAndSubmitWorkflow(
|
await updateWorkflowRequest(
|
||||||
editRequestId,
|
editRequestId,
|
||||||
updatePayload,
|
updatePayload,
|
||||||
documents,
|
documents,
|
||||||
@ -85,14 +84,15 @@ export function useCreateRequestSubmission({
|
|||||||
template: selectedTemplate,
|
template: selectedTemplate,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Create new workflow
|
// Create new workflow with isDraft: false (Submit)
|
||||||
const createPayload = buildCreatePayload(
|
const createPayload = buildCreatePayload(
|
||||||
formData,
|
formData,
|
||||||
selectedTemplate,
|
selectedTemplate,
|
||||||
user
|
user,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await createAndSubmitWorkflow(createPayload, documents);
|
const result = await createWorkflow(createPayload, documents);
|
||||||
|
|
||||||
// Show toast after backend confirmation
|
// Show toast after backend confirmation
|
||||||
toast.success('Request Submitted Successfully!', {
|
toast.success('Request Submitted Successfully!', {
|
||||||
@ -133,11 +133,12 @@ export function useCreateRequestSubmission({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (isEditing && editRequestId) {
|
if (isEditing && editRequestId) {
|
||||||
// Update existing draft
|
// Update existing draft with isDraft: true
|
||||||
const updatePayload = buildUpdatePayload(
|
const updatePayload = buildUpdatePayload(
|
||||||
formData,
|
formData,
|
||||||
user,
|
user,
|
||||||
documentsToDelete
|
documentsToDelete,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
await updateWorkflowRequest(
|
await updateWorkflowRequest(
|
||||||
@ -158,11 +159,12 @@ export function useCreateRequestSubmission({
|
|||||||
template: selectedTemplate,
|
template: selectedTemplate,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Create new draft
|
// Create new draft with isDraft: true
|
||||||
const createPayload = buildCreatePayload(
|
const createPayload = buildCreatePayload(
|
||||||
formData,
|
formData,
|
||||||
selectedTemplate,
|
selectedTemplate,
|
||||||
user
|
user,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await createWorkflow(createPayload, documents);
|
const result = await createWorkflow(createPayload, documents);
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
createWorkflowMultipart,
|
createWorkflowMultipart,
|
||||||
submitWorkflow,
|
|
||||||
updateWorkflow,
|
updateWorkflow,
|
||||||
updateWorkflowMultipart,
|
updateWorkflowMultipart,
|
||||||
} from '@/services/workflowApi';
|
} from '@/services/workflowApi';
|
||||||
@ -14,7 +13,7 @@ import {
|
|||||||
} from '../types/createRequest.types';
|
} from '../types/createRequest.types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new workflow
|
* Create a new workflow (supports both draft and direct submission via isDraft flag)
|
||||||
*/
|
*/
|
||||||
export async function createWorkflow(
|
export async function createWorkflow(
|
||||||
payload: CreateWorkflowPayload,
|
payload: CreateWorkflowPayload,
|
||||||
@ -29,7 +28,7 @@ export async function createWorkflow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update an existing workflow
|
* Update an existing workflow (supports both draft and direct submission via isDraft flag)
|
||||||
*/
|
*/
|
||||||
export async function updateWorkflowRequest(
|
export async function updateWorkflowRequest(
|
||||||
requestId: string,
|
requestId: string,
|
||||||
@ -51,36 +50,3 @@ export async function updateWorkflowRequest(
|
|||||||
await updateWorkflow(requestId, payload);
|
await updateWorkflow(requestId, payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Submit a workflow
|
|
||||||
*/
|
|
||||||
export async function submitWorkflowRequest(requestId: string): Promise<void> {
|
|
||||||
await submitWorkflow(requestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and submit a workflow in one operation
|
|
||||||
*/
|
|
||||||
export async function createAndSubmitWorkflow(
|
|
||||||
payload: CreateWorkflowPayload,
|
|
||||||
documents: File[]
|
|
||||||
): Promise<{ id: string }> {
|
|
||||||
const result = await createWorkflow(payload, documents);
|
|
||||||
await submitWorkflowRequest(result.id);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update and submit a workflow in one operation
|
|
||||||
*/
|
|
||||||
export async function updateAndSubmitWorkflow(
|
|
||||||
requestId: string,
|
|
||||||
payload: UpdateWorkflowPayload,
|
|
||||||
documents: File[],
|
|
||||||
documentsToDelete: string[]
|
|
||||||
): Promise<void> {
|
|
||||||
await updateWorkflowRequest(requestId, payload, documents, documentsToDelete);
|
|
||||||
await submitWorkflowRequest(requestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@ -67,6 +67,7 @@ export interface CreateWorkflowPayload {
|
|||||||
email: string;
|
email: string;
|
||||||
}>;
|
}>;
|
||||||
participants: Participant[];
|
participants: Participant[];
|
||||||
|
isDraft?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateWorkflowPayload {
|
export interface UpdateWorkflowPayload {
|
||||||
@ -76,6 +77,7 @@ export interface UpdateWorkflowPayload {
|
|||||||
approvalLevels: ApprovalLevel[];
|
approvalLevels: ApprovalLevel[];
|
||||||
participants: Participant[];
|
participants: Participant[];
|
||||||
deleteDocumentIds?: string[];
|
deleteDocumentIds?: string[];
|
||||||
|
isDraft?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ValidationModalState {
|
export interface ValidationModalState {
|
||||||
|
|||||||
@ -17,16 +17,9 @@ import { buildApprovalLevels } from './approvalLevelBuilders';
|
|||||||
export function buildCreatePayload(
|
export function buildCreatePayload(
|
||||||
formData: FormData,
|
formData: FormData,
|
||||||
selectedTemplate: RequestTemplate | null,
|
selectedTemplate: RequestTemplate | null,
|
||||||
_user: any
|
_user: any,
|
||||||
|
isDraft: boolean = false
|
||||||
): CreateWorkflowPayload {
|
): CreateWorkflowPayload {
|
||||||
// Filter out spectators who are also approvers (backend will handle validation)
|
|
||||||
const approverEmails = new Set(
|
|
||||||
(formData.approvers || []).map((a: any) => a?.email?.toLowerCase()).filter(Boolean)
|
|
||||||
);
|
|
||||||
const filteredSpectators = (formData.spectators || []).filter(
|
|
||||||
(s: any) => s?.email && !approverEmails.has(s.email.toLowerCase())
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
templateId: selectedTemplate?.id || null,
|
templateId: selectedTemplate?.id || null,
|
||||||
templateType: selectedTemplate?.id === 'custom' ? 'CUSTOM' : 'TEMPLATE',
|
templateType: selectedTemplate?.id === 'custom' ? 'CUSTOM' : 'TEMPLATE',
|
||||||
@ -38,16 +31,17 @@ export function buildCreatePayload(
|
|||||||
userId: a?.userId || '',
|
userId: a?.userId || '',
|
||||||
email: a?.email || '',
|
email: a?.email || '',
|
||||||
name: a?.name,
|
name: a?.name,
|
||||||
tat: a?.tat || '',
|
tat: a?.tat || 24,
|
||||||
tatType: a?.tatType || 'hours',
|
tatType: a?.tatType || 'hours',
|
||||||
})),
|
})),
|
||||||
spectators: filteredSpectators.map((s: any) => ({
|
spectators: (formData.spectators || []).map((s: any) => ({
|
||||||
userId: s?.userId || '',
|
userId: s?.userId || '',
|
||||||
name: s?.name || '',
|
name: s?.name || '',
|
||||||
email: s?.email || '',
|
email: s?.email || '',
|
||||||
})),
|
})),
|
||||||
ccList: [], // Auto-generated by backend
|
ccList: [], // Auto-generated by backend
|
||||||
participants: [], // Auto-generated by backend from approvers and spectators
|
participants: [], // Auto-generated by backend from approvers and spectators
|
||||||
|
isDraft,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +52,8 @@ export function buildCreatePayload(
|
|||||||
export function buildUpdatePayload(
|
export function buildUpdatePayload(
|
||||||
formData: FormData,
|
formData: FormData,
|
||||||
_user: any,
|
_user: any,
|
||||||
documentsToDelete: string[]
|
documentsToDelete: string[],
|
||||||
|
isDraft: boolean = false
|
||||||
): UpdateWorkflowPayload {
|
): UpdateWorkflowPayload {
|
||||||
const approvalLevels = buildApprovalLevels(
|
const approvalLevels = buildApprovalLevels(
|
||||||
formData.approvers || [],
|
formData.approvers || [],
|
||||||
@ -72,6 +67,7 @@ export function buildUpdatePayload(
|
|||||||
approvalLevels,
|
approvalLevels,
|
||||||
participants: [], // Auto-generated by backend from approval levels
|
participants: [], // Auto-generated by backend from approval levels
|
||||||
deleteDocumentIds: documentsToDelete.length > 0 ? documentsToDelete : undefined,
|
deleteDocumentIds: documentsToDelete.length > 0 ? documentsToDelete : undefined,
|
||||||
|
isDraft,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,4 +108,3 @@ export function validateApproversForSubmission(
|
|||||||
|
|
||||||
return { valid: true };
|
return { valid: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -62,7 +62,7 @@ export function TATBreachReport({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="destructive" className="text-sm font-medium self-start sm:self-auto">
|
<Badge variant="destructive" className="text-sm font-medium self-start sm:self-auto">
|
||||||
{breachedRequests.length} {breachedRequests.length === 1 ? 'Breach' : 'Breaches'}
|
{pagination.totalRecords} {pagination.totalRecords === 1 ? 'Breach' : 'Breaches'}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@ -164,11 +164,10 @@ export function TATBreachReport({
|
|||||||
<td className="py-3 px-4">
|
<td className="py-3 px-4">
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={`text-xs font-medium ${
|
className={`text-xs font-medium ${req.priority === 'express'
|
||||||
req.priority === 'express'
|
|
||||||
? 'bg-orange-100 text-orange-800 border-orange-200'
|
? 'bg-orange-100 text-orange-800 border-orange-200'
|
||||||
: 'bg-blue-100 text-blue-800 border-blue-200'
|
: 'bg-blue-100 text-blue-800 border-blue-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{req.priority}
|
{req.priority}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|||||||
@ -25,6 +25,7 @@ export interface CreateWorkflowFromFormPayload {
|
|||||||
approvers: ApproverFormItem[];
|
approvers: ApproverFormItem[];
|
||||||
spectators?: ParticipantItem[];
|
spectators?: ParticipantItem[];
|
||||||
ccList?: ParticipantItem[];
|
ccList?: ParticipantItem[];
|
||||||
|
isDraft?: boolean; // Added isDraft to the payload interface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility to generate a RFC4122 v4 UUID (fallback if crypto.randomUUID not available)
|
// Utility to generate a RFC4122 v4 UUID (fallback if crypto.randomUUID not available)
|
||||||
@ -102,6 +103,7 @@ export async function createWorkflowFromForm(form: CreateWorkflowFromFormPayload
|
|||||||
priority, // STANDARD | EXPRESS
|
priority, // STANDARD | EXPRESS
|
||||||
approvalLevels,
|
approvalLevels,
|
||||||
participants: participants.length ? participants : undefined,
|
participants: participants.length ? participants : undefined,
|
||||||
|
isDraft: form.isDraft, // Added isDraft to the payload
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await apiClient.post('/workflows', payload);
|
const res = await apiClient.post('/workflows', payload);
|
||||||
@ -116,6 +118,7 @@ export async function createWorkflowMultipart(form: CreateWorkflowFromFormPayloa
|
|||||||
title: form.title,
|
title: form.title,
|
||||||
description: form.description,
|
description: form.description,
|
||||||
priority: form.priorityUi.toUpperCase() === 'EXPRESS' ? 'EXPRESS' : 'STANDARD',
|
priority: form.priorityUi.toUpperCase() === 'EXPRESS' ? 'EXPRESS' : 'STANDARD',
|
||||||
|
isDraft: form.isDraft, // Added isDraft to the payload
|
||||||
// Simplified approvers format - only email and tatHours required
|
// Simplified approvers format - only email and tatHours required
|
||||||
approvers: Array.from({ length: form.approverCount || 1 }, (_, i) => {
|
approvers: Array.from({ length: form.approverCount || 1 }, (_, i) => {
|
||||||
const a = form.approvers[i] || ({} as any);
|
const a = form.approvers[i] || ({} as any);
|
||||||
@ -417,7 +420,7 @@ export async function downloadDocument(documentId: string): Promise<void> {
|
|||||||
fetchOptions.headers = {
|
fetchOptions.headers = {
|
||||||
'Authorization': `Bearer ${token}`
|
'Authorization': `Bearer ${token}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(downloadUrl, fetchOptions);
|
const response = await fetch(downloadUrl, fetchOptions);
|
||||||
|
|
||||||
@ -462,7 +465,7 @@ export async function downloadWorkNoteAttachment(attachmentId: string): Promise<
|
|||||||
fetchOptions.headers = {
|
fetchOptions.headers = {
|
||||||
'Authorization': `Bearer ${token}`
|
'Authorization': `Bearer ${token}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(downloadUrl, fetchOptions);
|
const response = await fetch(downloadUrl, fetchOptions);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user