| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- "use client";
- import { useState } from 'react';
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
- import { Button } from '@/components/ui/button';
- import { Upload, FileText, Database, BarChart3, CheckCircle, Loader2 } from 'lucide-react';
- import { UploadForm } from '@/app/components/uploadForm';
- import { createCintasImportRecord } from '@/app/actions/cintas-workflow';
- interface FileData {
- id: string;
- filename: string;
- mimetype: string;
- size: number;
- createdAt: string;
- updatedAt: string;
- }
- interface CintasSummary {
- id: number;
- week: string;
- trrTotal: number;
- fourWkAverages: number;
- trrPlus4Wk: number;
- powerAdds: number;
- weekId: number;
- }
- export default function CintasCalendarSummaryPage() {
- const [currentStep, setCurrentStep] = useState(1);
- const [uploadedFile, setUploadedFile] = useState<FileData | null>(null);
- const [isProcessing, setIsProcessing] = useState(false);
- const [importRecord, setImportRecord] = useState<any>(null);
- const [summaryData, setSummaryData] = useState<CintasSummary[]>([]);
- const [error, setError] = useState<string | null>(null);
- const handleFileUploaded = (file: FileData) => {
- setUploadedFile(file);
- setCurrentStep(2);
- setError(null);
- };
- const handleCreateImportRecord = async () => {
- if (!uploadedFile) return;
-
- setIsProcessing(true);
- setError(null);
-
- try {
- const result = await createCintasImportRecord(uploadedFile.id, uploadedFile.filename);
-
- if (result.success) {
- setImportRecord(result.data);
- setCurrentStep(3);
- } else {
- setError(result.error || 'Failed to create import record');
- }
- } catch (err) {
- setError(err instanceof Error ? err.message : 'Unknown error occurred');
- } finally {
- setIsProcessing(false);
- }
- };
- const steps = [
- {
- id: 1,
- title: 'Upload Excel File',
- description: 'Upload the Cintas Install Calendar Excel file to blob storage',
- icon: Upload,
- status: currentStep >= 1 ? (uploadedFile ? 'completed' : 'pending') : 'pending',
- },
- {
- id: 2,
- title: 'Create Import Record',
- description: 'Create an import record with Cintas Install Calendar layout configuration',
- icon: FileText,
- status: currentStep >= 2 ? (importRecord ? 'completed' : 'pending') : 'disabled',
- },
- {
- id: 3,
- title: 'Import Data',
- description: 'Read the Excel file and import data into PostgreSQL database',
- icon: Database,
- status: currentStep >= 3 ? 'pending' : 'disabled',
- },
- {
- id: 4,
- title: 'Generate Summary',
- description: 'Run summary calculations and display results',
- icon: BarChart3,
- status: currentStep >= 4 ? 'pending' : 'disabled',
- },
- ];
- const getStepStatusColor = (status: string) => {
- switch (status) {
- case 'completed':
- return 'text-green-600 bg-green-50 border-green-200';
- case 'pending':
- return 'text-blue-600 bg-blue-50 border-blue-200';
- case 'disabled':
- return 'text-gray-400 bg-gray-50 border-gray-200';
- default:
- return 'text-gray-600 bg-gray-50 border-gray-200';
- }
- };
- return (
- <div className="container mx-auto py-6 px-4 max-w-6xl">
- <div className="mb-8">
- <h1 className="text-3xl font-bold tracking-tight">Cintas Install Calendar Summary</h1>
- <p className="text-muted-foreground">
- Follow the workflow steps to upload and process the installation calendar data
- </p>
- </div>
- {error && (
- <div className="mb-4 p-4 bg-red-50 border border-red-200 rounded-lg">
- <p className="text-sm text-red-800">{error}</p>
- </div>
- )}
- {/* Workflow Steps */}
- <div className="mb-8">
- <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
- {steps.map((step) => {
- const Icon = step.icon;
- return (
- <Card
- key={step.id}
- className={`border-2 ${getStepStatusColor(step.status)}`}
- >
- <CardHeader className="pb-3">
- <div className="flex items-center space-x-2">
- <Icon className="h-5 w-5" />
- <div>
- <CardTitle className="text-sm font-medium">
- Step {step.id}: {step.title}
- </CardTitle>
- </div>
- </div>
- </CardHeader>
- <CardContent>
- <CardDescription className="text-xs">
- {step.description}
- </CardDescription>
- </CardContent>
- </Card>
- );
- })}
- </div>
- </div>
- {/* Step Content */}
- <div className="grid gap-6">
- {currentStep === 1 && (
- <Card>
- <CardHeader>
- <CardTitle>Step 1: Upload Excel File</CardTitle>
- <CardDescription>
- Upload your Cintas Install Calendar Excel file to begin processing
- </CardDescription>
- </CardHeader>
- <CardContent>
- <div className="space-y-4">
- <UploadForm onFileUploaded={handleFileUploaded} />
-
- {uploadedFile && (
- <div className="mt-4 p-4 bg-green-50 border border-green-200 rounded-lg">
- <div className="flex items-center space-x-2">
- <CheckCircle className="h-5 w-5 text-green-600" />
- <div>
- <p className="text-sm font-medium text-green-800">File uploaded successfully!</p>
- <p className="text-sm text-green-600">
- {uploadedFile.filename} ({(uploadedFile.size / 1024 / 1024).toFixed(2)} MB)
- </p>
- </div>
- </div>
- </div>
- )}
- </div>
- </CardContent>
- </Card>
- )}
- {currentStep === 2 && (
- <Card>
- <CardHeader>
- <CardTitle>Step 2: Create Import Record</CardTitle>
- <CardDescription>
- Creating import record with Cintas Install Calendar layout configuration
- </CardDescription>
- </CardHeader>
- <CardContent>
- <div className="space-y-4">
- <p className="text-sm text-muted-foreground">
- File: {uploadedFile?.filename}
- </p>
- <Button
- onClick={handleCreateImportRecord}
- disabled={isProcessing}
- className="w-full"
- >
- {isProcessing ? (
- <>
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
- Creating Import Record...
- </>
- ) : (
- 'Create Import Record'
- )}
- </Button>
- </div>
- </CardContent>
- </Card>
- )}
- {currentStep === 3 && (
- <Card>
- <CardHeader>
- <CardTitle>Step 3: Import Data</CardTitle>
- <CardDescription>
- Processing Excel file and importing data into database
- </CardDescription>
- </CardHeader>
- <CardContent>
- <div className="space-y-4">
- <p className="text-sm text-muted-foreground">
- Import ID: {importRecord?.id}
- </p>
- <Button
- onClick={() => setCurrentStep(4)}
- disabled={isProcessing}
- className="w-full"
- >
- {isProcessing ? (
- <>
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
- Processing Import...
- </>
- ) : (
- 'Process Import'
- )}
- </Button>
- </div>
- </CardContent>
- </Card>
- )}
- {currentStep === 4 && (
- <Card>
- <CardHeader>
- <CardTitle>Step 4: Generate Summary</CardTitle>
- <CardDescription>
- Running summary calculations and displaying results
- </CardDescription>
- </CardHeader>
- <CardContent>
- <div className="space-y-4">
- <Button
- onClick={() => setCurrentStep(4)}
- disabled={isProcessing}
- className="w-full"
- >
- {isProcessing ? (
- <>
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
- Generating Summary...
- </>
- ) : (
- 'Generate Summary'
- )}
- </Button>
- {summaryData.length > 0 && (
- <div className="mt-6">
- <h3 className="text-lg font-semibold mb-4">Summary Results</h3>
- <div className="overflow-x-auto">
- <table className="min-w-full divide-y divide-gray-200">
- <thead className="bg-gray-50">
- <tr>
- <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Week</th>
- <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">TRR Total</th>
- <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">4 Week Avg</th>
- <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">TRR + 4Wk</th>
- <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Power Adds</th>
- </tr>
- </thead>
- <tbody className="bg-white divide-y divide-gray-200">
- {summaryData.map((item) => (
- <tr key={item.id}>
- <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{item.week}</td>
- <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{item.trrTotal}</td>
- <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{item.fourWkAverages}</td>
- <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{item.trrPlus4Wk}</td>
- <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{item.powerAdds}</td>
- </tr>
- ))}
- </tbody>
- </table>
- </div>
- </div>
- )}
- </div>
- </CardContent>
- </Card>
- )}
- {/* Navigation */}
- <div className="flex justify-between">
- <Button
- variant="outline"
- onClick={() => setCurrentStep(Math.max(1, currentStep - 1))}
- disabled={currentStep === 1}
- >
- Previous
- </Button>
- <Button
- onClick={() => setCurrentStep(Math.min(4, currentStep + 1))}
- disabled={currentStep === 4 || (currentStep === 1 && !uploadedFile)}
- >
- {currentStep === 4 ? 'Complete' : 'Next'}
- </Button>
- </div>
- </div>
- </div>
- );
- }
|