mirror of
https://github.com/arkorty/B.Tech-Project-III.git
synced 2026-04-19 20:51:49 +00:00
init
This commit is contained in:
366
dmtp/client/app/submissions/[submissionId]/page.tsx
Normal file
366
dmtp/client/app/submissions/[submissionId]/page.tsx
Normal file
@@ -0,0 +1,366 @@
|
||||
'use client';
|
||||
|
||||
import { api } from '@/lib/api';
|
||||
import { formatCurrency } from '@/lib/utils';
|
||||
import { VerificationStatus } from '@/types';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { motion } from 'framer-motion';
|
||||
import { AlertCircle, CheckCircle2, Clock, ExternalLink, FileText, TrendingUp, Wallet, XCircle } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
|
||||
|
||||
export default function SubmissionStatusPage() {
|
||||
const params = useParams();
|
||||
const submissionId = params.submissionId as string;
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['submission', submissionId],
|
||||
queryFn: () => api.submissions.getStatus(submissionId),
|
||||
refetchInterval: 5000, // Poll every 5 seconds
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<LoadingSpinner size="lg" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const submission = data?.data?.submission;
|
||||
const task = data?.data?.task;
|
||||
const payment = data?.data?.payment;
|
||||
|
||||
if (!submission) {
|
||||
return (
|
||||
<div className="relative min-h-screen overflow-hidden">
|
||||
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<motion.div
|
||||
className="absolute w-96 h-96 bg-linear-to-br from-green-500/20 to-green-600/10 blur-3xl"
|
||||
animate={{ scale: [1, 1.2, 1], opacity: [0.3, 0.5, 0.3] }}
|
||||
transition={{ duration: 8, repeat: Infinity }}
|
||||
style={{ top: "20%", left: "10%" }}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative z-10 text-center py-32">
|
||||
<h2 className="text-2xl font-bold text-foreground">Submission not found</h2>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const statusConfig = {
|
||||
[VerificationStatus.PENDING]: {
|
||||
gradient: 'from-green-500 to-green-600',
|
||||
bgGradient: 'from-green-500/10 to-green-600/5',
|
||||
border: 'border-green-500/30',
|
||||
icon: Clock,
|
||||
iconColor: 'text-green-500',
|
||||
title: 'Verification in Progress',
|
||||
description: 'AI is verifying your submission. This usually takes 1-2 minutes.',
|
||||
},
|
||||
[VerificationStatus.APPROVED]: {
|
||||
gradient: 'from-green-500 to-green-600',
|
||||
bgGradient: 'from-green-500/10 to-green-600/5',
|
||||
border: 'border-green-500/30',
|
||||
icon: CheckCircle2,
|
||||
iconColor: 'text-green-500',
|
||||
title: 'Submission Approved!',
|
||||
description: 'Your submission has been approved and payment has been sent to your wallet.',
|
||||
},
|
||||
[VerificationStatus.REJECTED]: {
|
||||
gradient: 'from-red-500 to-red-600',
|
||||
bgGradient: 'from-red-500/10 to-red-600/5',
|
||||
border: 'border-red-500/30',
|
||||
icon: XCircle,
|
||||
iconColor: 'text-red-500',
|
||||
title: 'Submission Rejected',
|
||||
description: 'Your submission did not meet the verification criteria.',
|
||||
},
|
||||
};
|
||||
|
||||
const config = statusConfig[submission.status as VerificationStatus];
|
||||
const StatusIcon = config.icon;
|
||||
|
||||
return (
|
||||
<div className="relative min-h-screen overflow-hidden">
|
||||
{/* Animated Background */}
|
||||
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<motion.div
|
||||
className="absolute w-96 h-96 bg-linear-to-br from-green-500/20 to-green-600/10 blur-3xl"
|
||||
animate={{
|
||||
x: [0, 100, 0],
|
||||
y: [0, 50, 0],
|
||||
}}
|
||||
transition={{ duration: 20, repeat: Infinity }}
|
||||
style={{ top: "10%", left: "-10%" }}
|
||||
/>
|
||||
<motion.div
|
||||
className="absolute w-96 h-96 bg-linear-to-br from-green-600/10 to-green-500/20 blur-3xl"
|
||||
animate={{
|
||||
x: [0, -100, 0],
|
||||
y: [0, -50, 0],
|
||||
}}
|
||||
transition={{ duration: 25, repeat: Infinity }}
|
||||
style={{ bottom: "10%", right: "-10%" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 max-w-4xl mx-auto px-4 py-12 pt-32">
|
||||
{/* Status Card */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className={`bg-linear-to-br ${config.bgGradient} backdrop-blur-md border ${config.border} p-8 mb-8`}
|
||||
>
|
||||
<div className="flex items-start gap-6">
|
||||
<motion.div
|
||||
initial={{ scale: 0 }}
|
||||
animate={{ scale: 1 }}
|
||||
transition={{ type: "spring", delay: 0.3 }}
|
||||
className={`w-20 h-20 bg-linear-to-r ${config.gradient} flex items-center justify-center flex-shrink-0`}
|
||||
>
|
||||
<StatusIcon className="w-10 h-10 text-white" />
|
||||
</motion.div>
|
||||
<div className="flex-1">
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
className="text-3xl font-bold text-foreground mb-2"
|
||||
>
|
||||
{config.title}
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="text-foreground/70 text-lg"
|
||||
>
|
||||
{config.description}
|
||||
</motion.p>
|
||||
|
||||
{/* Progress Bar for Pending */}
|
||||
{submission.status === VerificationStatus.PENDING && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.6 }}
|
||||
className="mt-6"
|
||||
>
|
||||
<div className="w-full bg-foreground/10 h-2 overflow-hidden">
|
||||
<motion.div
|
||||
className="h-full bg-linear-to-r from-green-500 to-green-600 "
|
||||
animate={{ width: ["40%", "70%", "40%"] }}
|
||||
transition={{ duration: 2, repeat: Infinity }}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-foreground/60 mt-2 flex items-center gap-2">
|
||||
<LoadingSpinner />
|
||||
Estimated time: 1-2 minutes
|
||||
</p>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Task Info */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
className="bg-background/80 backdrop-blur-md border border-green-500/20 p-6 mb-6"
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 bg-linear-to-r from-green-500 to-green-600 flex items-center justify-center">
|
||||
<FileText className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<h2 className="text-xl font-bold text-foreground">Task Details</h2>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center p-3 bg-foreground/5 ">
|
||||
<span className="text-foreground/60">Task:</span>
|
||||
<span className="font-semibold text-foreground">{task?.title}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center p-3 bg-foreground/5 ">
|
||||
<span className="text-foreground/60">Payment:</span>
|
||||
<span className="font-bold bg-linear-to-r from-green-400 to-green-600 bg-clip-text text-transparent">
|
||||
{formatCurrency(task?.paymentAmount || 0)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center p-3 bg-foreground/5 ">
|
||||
<span className="text-foreground/60">Submitted:</span>
|
||||
<span className="font-medium text-foreground">
|
||||
{new Date(submission.submittedAt).toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Verification Results */}
|
||||
{submission.verificationResult && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.3 }}
|
||||
className="bg-background/80 backdrop-blur-md border border-green-500/20 p-6 mb-6"
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 bg-linear-to-r from-green-500 to-green-600 flex items-center justify-center">
|
||||
<TrendingUp className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<h2 className="text-xl font-bold text-foreground">Verification Results</h2>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center p-4 bg-linear-to-br from-green-500/10 to-green-600/5 border border-green-500/20">
|
||||
<span className="text-foreground/60 font-medium">Score:</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<motion.span
|
||||
initial={{ scale: 0 }}
|
||||
animate={{ scale: 1 }}
|
||||
transition={{ type: "spring", delay: 0.5 }}
|
||||
className="text-2xl font-bold bg-linear-to-r from-green-400 to-green-600 bg-clip-text text-transparent"
|
||||
>
|
||||
{submission.verificationResult.score}
|
||||
</motion.span>
|
||||
<span className="text-foreground/60">/100</span>
|
||||
</div>
|
||||
</div>
|
||||
{submission.verificationResult.reasoning && (
|
||||
<div>
|
||||
<span className="text-foreground/60 block mb-2 font-medium">AI Reasoning:</span>
|
||||
<div className="bg-foreground/5 border border-green-500/20 p-4 ">
|
||||
<p className="text-sm text-foreground/70 leading-relaxed">
|
||||
{submission.verificationResult.reasoning}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{submission.verificationResult.error && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
className="bg-linear-to-br from-red-500/10 to-red-600/5 border border-red-500/30 p-4"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<AlertCircle className="w-5 h-5 text-red-500 flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-red-600 mb-1">Error:</p>
|
||||
<p className="text-sm text-red-600/80">
|
||||
{submission.verificationResult.error}
|
||||
</p>
|
||||
{submission.verificationResult.blockchainError && (
|
||||
<p className="text-xs text-red-600/70 mt-2">
|
||||
Blockchain: {submission.verificationResult.blockchainError}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Payment Info */}
|
||||
{payment && payment.transactionHash && payment.transactionHash !== 'pending' && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.4 }}
|
||||
className="bg-linear-to-br from-green-500/10 to-green-600/5 backdrop-blur-md border border-green-500/30 p-6 mb-6"
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<motion.div
|
||||
animate={{ scale: [1, 1.1, 1] }}
|
||||
transition={{ duration: 2, repeat: Infinity }}
|
||||
className="w-10 h-10 bg-linear-to-r from-green-500 to-green-600 flex items-center justify-center"
|
||||
>
|
||||
<Wallet className="w-5 h-5 text-white" />
|
||||
</motion.div>
|
||||
<h2 className="text-xl font-bold text-foreground">💰 Payment Details</h2>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center p-3 bg-background/50 ">
|
||||
<span className="text-foreground/60">Amount:</span>
|
||||
<span className="font-bold text-green-600 text-lg">
|
||||
{formatCurrency(payment.amount)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="p-3 bg-background/50 ">
|
||||
<span className="text-foreground/60 block mb-2">Transaction Hash:</span>
|
||||
<a
|
||||
href={`https://sepolia.celoscan.io/tx/${payment.transactionHash}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-mono text-sm text-green-500 hover:text-green-600 break-all flex items-center gap-2 transition-colors"
|
||||
>
|
||||
{payment.transactionHash}
|
||||
<ExternalLink className="w-4 h-4 flex-shrink-0" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Pending Payment */}
|
||||
{submission.status === VerificationStatus.APPROVED && (!payment || !payment.transactionHash || payment.transactionHash === 'pending') && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.4 }}
|
||||
className="bg-linear-to-br from-green-500/10 to-green-600/5 backdrop-blur-md border border-green-500/30 p-6 mb-6"
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-10 h-10 bg-linear-to-r from-green-500 to-green-600 flex items-center justify-center">
|
||||
<Wallet className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<h2 className="text-xl font-bold text-foreground">💰 Payment Processing</h2>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-4 bg-background/50 ">
|
||||
<motion.div
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
|
||||
>
|
||||
<LoadingSpinner />
|
||||
</motion.div>
|
||||
<p className="text-sm text-foreground/70">
|
||||
Payment is being processed on the blockchain. This may take a few moments...
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Actions */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.5 }}
|
||||
className="flex flex-col sm:flex-row gap-4"
|
||||
>
|
||||
<Link href="/tasks" className="flex-1">
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.02 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
className="w-full py-4 bg-linear-to-r from-green-500 to-green-600 text-white hover:from-green-600 hover:to-green-700 transition-all font-bold shadow-lg shadow-green-500/25"
|
||||
>
|
||||
Browse More Tasks
|
||||
</motion.button>
|
||||
</Link>
|
||||
<Link href="/dashboard" className="flex-1">
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.02 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
className="w-full py-4 bg-background/80 backdrop-blur-sm text-green-500 border-2 border-green-500/50 hover:border-green-500 hover:bg-green-500/10 transition-all font-bold"
|
||||
>
|
||||
View Dashboard
|
||||
</motion.button>
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user