🚀 Elite Accountant Hub: Foundation & Trojan Horse deployment
This commit is contained in:
155
frontend/src/pages/Public/TrojanHorseConverter.tsx
Normal file
155
frontend/src/pages/Public/TrojanHorseConverter.tsx
Normal file
@@ -0,0 +1,155 @@
|
||||
import React, { useState } from 'react';
|
||||
import { UploadCloud, FileType, CheckCircle2, ArrowRight } from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export const TrojanHorseConverter = () => {
|
||||
const [dragActive, setDragActive] = useState(false);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [status, setStatus] = useState<'idle' | 'uploading' | 'success'>('idle');
|
||||
|
||||
const handleDrag = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (e.type === "dragenter" || e.type === "dragover") {
|
||||
setDragActive(true);
|
||||
} else if (e.type === "dragleave") {
|
||||
setDragActive(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDrop = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setDragActive(false);
|
||||
if (e.dataTransfer.files && e.dataTransfer.files[0]) {
|
||||
setFile(e.dataTransfer.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConvert = () => {
|
||||
if (!file) return;
|
||||
setStatus('uploading');
|
||||
|
||||
// Simulate API call to the backend Trojan endpoint
|
||||
setTimeout(() => {
|
||||
setStatus('success');
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 text-slate-900 font-sans selection:bg-emerald-200">
|
||||
|
||||
{/* Top Banner */}
|
||||
<div className="bg-slate-900 text-center py-3 text-sm text-slate-300">
|
||||
هل تدير عشرات الشركات؟ <span className="text-white font-medium ml-2 cursor-pointer hover:text-emerald-400 transition-colors">اكتشف لوحة تحكم محاسبي إيليت <ArrowRight className="inline w-4 h-4" /></span>
|
||||
</div>
|
||||
|
||||
<div className="max-w-4xl mx-auto px-6 py-20">
|
||||
|
||||
{/* Header */}
|
||||
<div className="text-center mb-16">
|
||||
<div className="inline-block px-4 py-1.5 rounded-full bg-emerald-100 text-emerald-800 text-xs font-bold mb-6">
|
||||
أداة مجانية 100%
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl font-extrabold tracking-tight mb-6 text-slate-900">
|
||||
حوّل فواتيرك إلى صيغة <span className="text-emerald-600">جو فوترة</span> في ثوانٍ
|
||||
</h1>
|
||||
<p className="text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
قم برفع فاتورة الـ PDF الخاصة بك وسيقوم الذكاء الاصطناعي باستخراج البيانات وتحويلها إلى صيغة XML المتوافقة تماماً مع نظام دائرة ضريبة الدخل والمبيعات الأردنية.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Upload Area */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="bg-white rounded-3xl shadow-xl p-8 border border-slate-100"
|
||||
>
|
||||
{status === 'idle' && (
|
||||
<div
|
||||
onDragEnter={handleDrag}
|
||||
onDragLeave={handleDrag}
|
||||
onDragOver={handleDrag}
|
||||
onDrop={handleDrop}
|
||||
className={`border-2 border-dashed rounded-2xl p-12 text-center transition-all ${
|
||||
dragActive ? 'border-emerald-500 bg-emerald-50/50' : 'border-slate-200 hover:border-slate-300'
|
||||
}`}
|
||||
>
|
||||
<UploadCloud className={`w-16 h-16 mx-auto mb-6 ${dragActive ? 'text-emerald-500' : 'text-slate-400'}`} />
|
||||
|
||||
{file ? (
|
||||
<div>
|
||||
<p className="text-lg font-medium text-slate-900 mb-2">{file.name}</p>
|
||||
<p className="text-sm text-slate-500 mb-8">{(file.size / 1024 / 1024).toFixed(2)} MB</p>
|
||||
<button
|
||||
onClick={handleConvert}
|
||||
className="px-8 py-3 bg-slate-900 hover:bg-slate-800 text-white rounded-xl font-medium shadow-lg transition-all w-full md:w-auto"
|
||||
>
|
||||
بدء التحويل إلى XML
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<p className="text-xl font-medium text-slate-900 mb-2">اسحب وأفلت الفاتورة هنا</p>
|
||||
<p className="text-sm text-slate-500 mb-8">يدعم PDF, PNG, JPG</p>
|
||||
<label className="px-8 py-3 bg-emerald-500 hover:bg-emerald-600 text-white rounded-xl font-medium shadow-lg shadow-emerald-500/30 transition-all cursor-pointer inline-block">
|
||||
اختر ملف
|
||||
<input type="file" className="hidden" onChange={(e) => e.target.files && setFile(e.target.files[0])} accept=".pdf,.png,.jpg" />
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === 'uploading' && (
|
||||
<div className="py-20 text-center">
|
||||
<div className="w-16 h-16 border-4 border-slate-200 border-t-emerald-500 rounded-full animate-spin mx-auto mb-6"></div>
|
||||
<h3 className="text-xl font-medium text-slate-900 mb-2">جاري استخراج البيانات...</h3>
|
||||
<p className="text-slate-500">يقوم الذكاء الاصطناعي بقراءة الفاتورة وتنسيقها</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === 'success' && (
|
||||
<motion.div
|
||||
initial={{ scale: 0.9, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
className="py-12 text-center"
|
||||
>
|
||||
<div className="w-20 h-20 bg-emerald-100 rounded-full flex items-center justify-center mx-auto mb-6">
|
||||
<CheckCircle2 className="w-10 h-10 text-emerald-600" />
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-slate-900 mb-4">تم التحويل بنجاح!</h3>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center mt-8">
|
||||
<button className="px-6 py-3 bg-emerald-500 hover:bg-emerald-600 text-white rounded-xl font-medium flex items-center justify-center gap-2">
|
||||
<FileType className="w-5 h-5" /> تحميل ملف XML
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setStatus('idle'); setFile(null); }}
|
||||
className="px-6 py-3 bg-white border border-slate-200 hover:bg-slate-50 text-slate-700 rounded-xl font-medium"
|
||||
>
|
||||
تحويل فاتورة أخرى
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Upsell Banner for Accountants */}
|
||||
<div className="mt-12 bg-slate-900 rounded-2xl p-6 text-left flex flex-col md:flex-row items-center justify-between gap-6">
|
||||
<div>
|
||||
<h4 className="text-white font-bold mb-1 flex items-center gap-2">
|
||||
<span className="text-emerald-400">مُصادَق</span> | هل أنت محاسب؟
|
||||
</h4>
|
||||
<p className="text-slate-400 text-sm">توقف عن تحويل الفواتير واحدة تلو الأخرى. جرب لوحة تحكم إيليت وأتمت عملك لـ 50 شركة بنقرة واحدة.</p>
|
||||
</div>
|
||||
<button className="whitespace-nowrap px-6 py-2.5 bg-white text-slate-900 rounded-lg font-medium hover:bg-slate-100">
|
||||
اكتشف إيليت
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
</motion.div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
145
frontend/src/pages/dashboard/MultiEntityDashboard.tsx
Normal file
145
frontend/src/pages/dashboard/MultiEntityDashboard.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import React from 'react';
|
||||
import { Building2, TrendingUp, AlertTriangle, FileText, ChevronDown } from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
// Mock data matching the API structure we built in the backend
|
||||
const mockCompanies = [
|
||||
{ id: '1', name: 'Apex Innovations', totalInvoices: 24, pendingAmount: 78450, totalTax: 12500, failedCount: 2, riskScore: 85, status: 'High' },
|
||||
{ id: '2', name: 'Quantum Solutions', totalInvoices: 26, pendingAmount: 112000, totalTax: 18000, failedCount: 0, riskScore: 32, status: 'Low' },
|
||||
{ id: '3', name: 'Nomad Ventures', totalInvoices: 45, pendingAmount: 319000, totalTax: 45000, failedCount: 5, riskScore: 68, status: 'Medium' },
|
||||
{ id: '4', name: 'Elevate Tech', totalInvoices: 12, pendingAmount: 188000, totalTax: 30000, failedCount: 1, riskScore: 85, status: 'High' },
|
||||
{ id: '5', name: 'Horizon Group', totalInvoices: 33, pendingAmount: 95000, totalTax: 14000, failedCount: 3, riskScore: 68, status: 'Medium' },
|
||||
];
|
||||
|
||||
const RiskGauge = ({ score, status }: { score: number, status: string }) => {
|
||||
const getColor = () => {
|
||||
if (status === 'High') return 'text-red-500';
|
||||
if (status === 'Medium') return 'text-orange-500';
|
||||
return 'text-emerald-500';
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="relative w-16 h-16">
|
||||
<svg className="w-full h-full transform -rotate-90" viewBox="0 0 36 36">
|
||||
<path
|
||||
className="text-slate-700"
|
||||
strokeWidth="3"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"
|
||||
/>
|
||||
<path
|
||||
className={getColor()}
|
||||
strokeWidth="3"
|
||||
strokeDasharray={`${score}, 100`}
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"
|
||||
/>
|
||||
</svg>
|
||||
<div className="absolute inset-0 flex items-center justify-center text-lg font-bold text-white">
|
||||
{score}
|
||||
</div>
|
||||
</div>
|
||||
<span className={`text-xs mt-1 ${getColor()}`}>{status}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const MultiEntityDashboard = () => {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-950 text-slate-300 font-sans p-8 selection:bg-emerald-500/30">
|
||||
|
||||
{/* Header */}
|
||||
<header className="flex justify-between items-center mb-10">
|
||||
<div>
|
||||
<h1 className="text-3xl font-light tracking-tight text-white flex items-center gap-3">
|
||||
<span className="font-semibold text-emerald-400">مُصادَق</span> | لوحة تحكم الشركات
|
||||
</h1>
|
||||
<p className="text-slate-400 mt-2 text-sm">نظرة عامة على الموقف الضريبي لجميع عملائك (Elite View)</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<button className="px-4 py-2 bg-slate-800 hover:bg-slate-700 text-white rounded-lg text-sm border border-slate-700 transition-colors flex items-center gap-2">
|
||||
آخر 30 يوم <ChevronDown className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="px-5 py-2 bg-emerald-500 hover:bg-emerald-600 text-slate-950 font-medium rounded-lg text-sm shadow-[0_0_15px_rgba(16,185,129,0.3)] transition-all">
|
||||
+ إضافة شركة جديدة
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
{mockCompanies.map((company, index) => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
key={company.id}
|
||||
className="bg-slate-900/50 backdrop-blur-xl border border-slate-800 rounded-2xl p-6 relative overflow-hidden hover:border-slate-700 transition-colors group"
|
||||
>
|
||||
{/* Ambient glow */}
|
||||
<div className={`absolute -inset-20 opacity-0 group-hover:opacity-20 blur-3xl transition-opacity duration-500 rounded-full
|
||||
${company.status === 'High' ? 'bg-red-500' : company.status === 'Medium' ? 'bg-orange-500' : 'bg-emerald-500'}
|
||||
`} />
|
||||
|
||||
<div className="relative z-10">
|
||||
<div className="flex justify-between items-start mb-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-slate-800 flex items-center justify-center border border-slate-700">
|
||||
<Building2 className="w-5 h-5 text-emerald-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-white font-medium text-lg">{company.name}</h3>
|
||||
<p className="text-xs text-slate-500">الرقم الضريبي: 00{company.id}829471</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 mb-6">
|
||||
<div>
|
||||
<p className="text-xs text-slate-400 mb-1">التدفق النقدي (المبيعات)</p>
|
||||
<p className="text-2xl font-light text-white">${(company.pendingAmount / 1000).toFixed(1)}k</p>
|
||||
<div className="flex items-center gap-1 mt-1 text-xs text-emerald-400">
|
||||
<TrendingUp className="w-3 h-3" /> مستقر
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<div>
|
||||
<p className="text-xs text-slate-400 mb-1 text-center">درجة الخطر الضريبي</p>
|
||||
<RiskGauge score={company.riskScore} status={company.status} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pt-4 border-t border-slate-800/50">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<p className="text-sm text-slate-300">الفواتير المعلقة</p>
|
||||
<span className="text-xs text-slate-500">{company.totalInvoices} فاتورة | ${company.totalTax} ضرائب</span>
|
||||
</div>
|
||||
|
||||
{/* Minimal Progress Bar */}
|
||||
<div className="h-1.5 w-full bg-slate-800 rounded-full overflow-hidden flex">
|
||||
<div className="h-full bg-emerald-500" style={{ width: '60%' }}></div>
|
||||
<div className="h-full bg-orange-500" style={{ width: '25%' }}></div>
|
||||
<div className="h-full bg-red-500" style={{ width: '15%' }}></div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between mt-2 text-[10px] text-slate-500">
|
||||
<span>تم الرفع (60%)</span>
|
||||
{company.failedCount > 0 && (
|
||||
<span className="text-red-400 flex items-center gap-1">
|
||||
<AlertTriangle className="w-3 h-3" /> {company.failedCount} مرفوضة
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user