ميزة: إضافة مكوني ProcedureSelection و StepProgress لأداة مخططات التدفق بصيغة PDF
- تنفيذ مكون ProcedureSelection لتمكين المستخدمين من اختيار الإجراءات من قائمة، وإدارة الاختيارات، ومعالجة الإجراءات المرفوضة. - إنشاء مكون StepProgress لعرض تقدم معالج متعدد الخطوات بشكل مرئي. - تعريف أنواع مشتركة للإجراءات، وخطوات التدفق، ورسائل الدردشة في ملف types.ts. - إضافة اختبارات وحدة لخطافات useFileUpload و useTaskPolling لضمان الأداء السليم ومعالجة الأخطاء. - تنفيذ اختبارات واجهة برمجة التطبيقات (API) للتحقق من تنسيقات نقاط النهاية وضمان اتساق ربط الواجهة الأمامية بالخلفية.
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Upload, Sparkles } from 'lucide-react';
|
||||
import { Upload, Sparkles, PenLine } from 'lucide-react';
|
||||
import ToolSelectorModal from '@/components/shared/ToolSelectorModal';
|
||||
import { useFileStore } from '@/stores/fileStore';
|
||||
import { getToolsForFile, detectFileCategory, getCategoryLabel } from '@/utils/fileRouting';
|
||||
import type { ToolOption } from '@/utils/fileRouting';
|
||||
|
||||
@@ -23,6 +25,8 @@ const ACCEPTED_TYPES = {
|
||||
|
||||
export default function HeroUploadZone() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const setStoreFile = useFileStore((s) => s.setFile);
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const [matchedTools, setMatchedTools] = useState<ToolOption[]>([]);
|
||||
const [fileTypeLabel, setFileTypeLabel] = useState('');
|
||||
@@ -102,11 +106,50 @@ export default function HeroUploadZone() {
|
||||
</div>
|
||||
|
||||
{/* CTA Text */}
|
||||
<p className="mb-1 text-lg font-semibold text-slate-800 dark:text-slate-200">
|
||||
{t('home.uploadCta')}
|
||||
</p>
|
||||
<div className="mb-6 flex gap-3 justify-center z-10 relative">
|
||||
<button
|
||||
type="button"
|
||||
className="px-6 py-3 bg-red-600 hover:bg-red-700 text-white font-bold rounded-xl shadow-md transition-colors"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = Object.values(ACCEPTED_TYPES).flat().join(',');
|
||||
input.onchange = (ev) => {
|
||||
const fileInput = ev.target as HTMLInputElement;
|
||||
const f = fileInput.files?.[0];
|
||||
if (f) onDrop([f]);
|
||||
};
|
||||
input.click();
|
||||
}}
|
||||
>
|
||||
{t('home.uploadCta', 'Choose File')}
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.pdf';
|
||||
input.onchange = (ev) => {
|
||||
const fileInput = ev.target as HTMLInputElement;
|
||||
const f = fileInput.files?.[0];
|
||||
if (f) {
|
||||
setStoreFile(f);
|
||||
navigate('/tools/pdf-editor');
|
||||
}
|
||||
};
|
||||
input.click();
|
||||
}}
|
||||
className="px-6 py-3 bg-slate-900 hover:bg-slate-800 text-white font-bold rounded-xl shadow-md transition-colors flex items-center gap-2"
|
||||
>
|
||||
<PenLine className="h-5 w-5" />
|
||||
{t('home.editNow')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p className="mb-3 text-sm text-slate-500 dark:text-slate-400">
|
||||
{t('home.uploadOr')}
|
||||
{t('common.dragDrop', 'or drop files here')}
|
||||
</p>
|
||||
|
||||
{/* Supported formats */}
|
||||
|
||||
@@ -22,21 +22,21 @@ export default function ToolCard({
|
||||
bgColor,
|
||||
}: ToolCardProps) {
|
||||
return (
|
||||
<Link to={to} className="tool-card group block">
|
||||
<div className="flex items-start gap-4">
|
||||
<div
|
||||
className={`flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-xl ${bgColor}`}
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h3 className="text-base font-semibold text-slate-900 group-hover:text-primary-600 transition-colors dark:text-slate-100 dark:group-hover:text-primary-400">
|
||||
<Link to={to} className="group block h-full">
|
||||
<div className="flex h-full flex-col gap-3 rounded-2xl bg-white p-5 shadow-sm ring-1 ring-slate-200 transition-all duration-200 hover:-translate-y-1 hover:shadow-md hover:ring-primary-300 dark:bg-slate-800 dark:ring-slate-700 dark:hover:ring-primary-500">
|
||||
<div className="flex items-center gap-4">
|
||||
<div
|
||||
className={`flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-xl transition-colors ${bgColor} dark:bg-slate-700 dark:group-hover:bg-slate-600`}
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-slate-900 transition-colors group-hover:text-primary-600 dark:text-slate-100 dark:group-hover:text-primary-400">
|
||||
{title}
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-slate-500 line-clamp-2 dark:text-slate-400">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 line-clamp-2 dark:text-slate-400 mt-1">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user