172 lines
7.8 KiB
TypeScript
172 lines
7.8 KiB
TypeScript
import { useRef, useState } from 'react';
|
|
import { Card, CardContent } from '@/components/ui/card';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Upload, X, FileText, Eye } from 'lucide-react';
|
|
import { RequestTemplate } from '@/hooks/useCreateRequestForm';
|
|
import { RichTextEditor } from '@/components/ui/rich-text-editor';
|
|
import { FilePreview } from '@/components/common/FilePreview/FilePreview';
|
|
|
|
interface AdminRequestDetailsStepProps {
|
|
template: RequestTemplate;
|
|
formData: any;
|
|
setFormData: (data: any) => void;
|
|
documents: File[];
|
|
setDocuments: (docs: File[]) => void;
|
|
}
|
|
|
|
export function AdminRequestDetailsStep({
|
|
template,
|
|
formData,
|
|
setFormData,
|
|
documents,
|
|
setDocuments
|
|
}: AdminRequestDetailsStepProps) {
|
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
const [previewFile, setPreviewFile] = useState<{ file: File; url: string } | null>(null);
|
|
|
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
if (e.target.files && e.target.files.length > 0) {
|
|
setDocuments([...documents, ...Array.from(e.target.files)]);
|
|
}
|
|
};
|
|
|
|
const removeDocument = (index: number) => {
|
|
const newDocs = [...documents];
|
|
newDocs.splice(index, 1);
|
|
setDocuments(newDocs);
|
|
};
|
|
|
|
const handlePreview = (file: File) => {
|
|
const url = URL.createObjectURL(file);
|
|
setPreviewFile({ file, url });
|
|
};
|
|
|
|
const closePreview = () => {
|
|
if (previewFile?.url) {
|
|
URL.revokeObjectURL(previewFile.url);
|
|
}
|
|
setPreviewFile(null);
|
|
};
|
|
|
|
const canPreview = (file: File) => {
|
|
return file.type.includes('image') || file.type.includes('pdf');
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6 max-w-4xl mx-auto">
|
|
<Card className="shadow-sm">
|
|
<CardContent className="pt-6">
|
|
<div className="mb-4">
|
|
<h2 className="text-xl font-bold text-gray-800">{template.name}</h2>
|
|
<p className="text-sm text-gray-500 mt-1">{template.description}</p>
|
|
</div>
|
|
<div className="flex gap-4 text-sm text-gray-600 bg-gray-50 p-3 rounded-lg">
|
|
<div className="flex items-center gap-1">
|
|
<span className="font-semibold">Category:</span> {template.category}
|
|
</div>
|
|
<div className="flex items-center gap-1">
|
|
<span className="font-semibold">Priority:</span>
|
|
<span className="capitalize">{template.priority}</span>
|
|
</div>
|
|
<div className="flex items-center gap-1">
|
|
<span className="font-semibold">SLA:</span> {template.suggestedSLA} Hours
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardContent className="pt-6 space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="requestTitle">Request Title *</Label>
|
|
<Input
|
|
id="requestTitle"
|
|
value={formData.title}
|
|
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
|
placeholder={`Request for ${template.name}`}
|
|
className="border-gray-200"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="justification" className="text-base font-semibold">Request Detail *</Label>
|
|
<p className="text-sm text-gray-600 mb-2">
|
|
Explain what you need approval for, why it's needed, and any relevant details.
|
|
</p>
|
|
<RichTextEditor
|
|
value={formData.description || ''}
|
|
onChange={(html) => setFormData({ ...formData, description: html })}
|
|
placeholder="Provide comprehensive details about your request..."
|
|
className="min-h-[120px] text-base border-gray-200 bg-white shadow-sm"
|
|
minHeight="120px"
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardContent className="pt-6 space-y-4">
|
|
<Label>Supporting Documents</Label>
|
|
|
|
<div
|
|
className="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center hover:bg-gray-50 transition-colors cursor-pointer"
|
|
onClick={() => fileInputRef.current?.click()}
|
|
>
|
|
<Upload className="w-10 h-10 text-gray-400 mx-auto mb-3" />
|
|
<p className="text-sm font-medium text-gray-700">Click to upload files</p>
|
|
<p className="text-xs text-gray-500 mt-1">PDF, Excel, Images (Max 10MB)</p>
|
|
<input
|
|
ref={fileInputRef}
|
|
type="file"
|
|
multiple
|
|
className="hidden"
|
|
onChange={handleFileChange}
|
|
/>
|
|
</div>
|
|
|
|
{documents.length > 0 && (
|
|
<div className="grid grid-cols-1 gap-2 mt-4">
|
|
{documents.map((file, index) => (
|
|
<div key={index} className="flex items-center justify-between p-3 bg-white border rounded-lg shadow-sm">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 bg-blue-50 rounded-lg flex items-center justify-center">
|
|
<FileText className="w-5 h-5 text-blue-600" />
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-medium text-gray-800 truncate max-w-[200px]">{file.name}</p>
|
|
<p className="text-xs text-gray-500">{(file.size / 1024 / 1024).toFixed(2)} MB</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-1">
|
|
{canPreview(file) && (
|
|
<Button variant="ghost" size="icon" onClick={() => handlePreview(file)}>
|
|
<Eye className="w-4 h-4 text-gray-500 hover:text-blue-600" />
|
|
</Button>
|
|
)}
|
|
<Button variant="ghost" size="icon" onClick={() => removeDocument(index)}>
|
|
<X className="w-4 h-4 text-gray-500 hover:text-red-500" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{previewFile && (
|
|
<FilePreview
|
|
fileName={previewFile.file.name}
|
|
fileType={previewFile.file.type}
|
|
fileUrl={previewFile.url}
|
|
fileSize={previewFile.file.size}
|
|
open={!!previewFile}
|
|
onClose={closePreview}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|