Initial commit: React app prototype
Add test.html UI component and app.js entry point. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,294 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import {
|
||||||
|
Package,
|
||||||
|
ArrowRightLeft,
|
||||||
|
Clock,
|
||||||
|
FileText,
|
||||||
|
CheckCircle2,
|
||||||
|
ClipboardList,
|
||||||
|
Bot,
|
||||||
|
Info,
|
||||||
|
Layers,
|
||||||
|
ChevronRight,
|
||||||
|
Smartphone
|
||||||
|
} from 'lucide-react';
|
||||||
|
|
||||||
|
// Корпоративные цвета Koblik Group
|
||||||
|
const COLORS = {
|
||||||
|
primary: '#FF9E00', // Фирменный оранжевый
|
||||||
|
dark: '#414449', // Корпоративный графитовый
|
||||||
|
background: '#F8F9FA',
|
||||||
|
text: '#2D2F33',
|
||||||
|
white: '#FFFFFF'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const [currentTime, setCurrentTime] = useState(new Date());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setInterval(() => setCurrentTime(new Date()), 1000);
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Список активных приоритетных документов в работе
|
||||||
|
const priorityDocuments = [
|
||||||
|
{
|
||||||
|
id: "Заказ № 1042-88",
|
||||||
|
client: 'ООО "Вектор"',
|
||||||
|
total: 1250,
|
||||||
|
picked: 875,
|
||||||
|
type: 'Отгрузка',
|
||||||
|
priority: 'Высокий'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "Заказ № 1042-95",
|
||||||
|
client: 'АО "АгроТех"',
|
||||||
|
total: 400,
|
||||||
|
picked: 380,
|
||||||
|
type: 'Кросс-докинг',
|
||||||
|
priority: 'Критический'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "Приемка № 884-П",
|
||||||
|
client: 'Завод "Деталь"',
|
||||||
|
total: 2000,
|
||||||
|
picked: 450,
|
||||||
|
type: 'Приемка',
|
||||||
|
priority: 'Средний'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Данные активности сотрудников: ТСД заменено на Номер заказа
|
||||||
|
const tsdActivity = [
|
||||||
|
{ orderId: 'ЗКЗ-1042-88', worker: 'Иванов И.А.', item: 'Вал карданный L=1200', quantity: 12, unit: 'шт', itemStatus: 'arrived' },
|
||||||
|
{ orderId: 'ЗКЗ-1042-95', worker: 'Смирнов А.В.', item: 'Подшипник 206 (закрытый)', quantity: 48, unit: 'шт', itemStatus: 'planned' },
|
||||||
|
{ orderId: 'ЗКЗ-1042-92', worker: 'Петрова Е.С.', item: 'Редуктор цилиндрический Ц2У', quantity: 2, unit: 'шт', itemStatus: 'arrived' },
|
||||||
|
{ orderId: 'ЗКЗ-1042-89', worker: 'Сидоров К.М.', item: 'Звездочка Z-24 t=19.05', quantity: 120, unit: 'шт', itemStatus: 'planned' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const containerMoves = [
|
||||||
|
{ id: 1, container: 'CONT-8892', robot: 'RBT-14', from: 'Приемка', to: 'A-12-04', status: 'В пути', statusType: 'moving' },
|
||||||
|
{ id: 2, container: 'CONT-1024', robot: 'RBT-02', from: 'B-05-01', to: 'Отгрузка', status: 'Ожидает ТС', statusType: 'waiting' },
|
||||||
|
{ id: 3, container: 'CONT-5531', robot: 'RBT-09', from: 'Консолидация', to: 'Буфер', status: 'В пути', statusType: 'moving' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getStatusBadge = (type, text) => {
|
||||||
|
const styles = {
|
||||||
|
moving: "bg-orange-50 text-orange-700 border-orange-200",
|
||||||
|
waiting: "bg-slate-100 text-slate-600 border-slate-200",
|
||||||
|
error: "bg-red-50 text-red-700 border-red-200",
|
||||||
|
processing: "bg-[#FF9E00] text-white border-[#E68E00]",
|
||||||
|
new: "bg-slate-50 text-slate-500 border-slate-200",
|
||||||
|
done: "bg-green-50 text-green-700 border-green-200"
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<span className={`px-2.5 py-0.5 text-[10px] font-bold rounded uppercase tracking-wider border ${styles[type] || 'bg-gray-100'}`}>
|
||||||
|
{text}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-[#F8F9FA] text-[#2D2F33] font-sans">
|
||||||
|
|
||||||
|
{/* Шапка KOBLIK */}
|
||||||
|
<header className="bg-[#414449] text-white px-8 py-4 shadow-lg flex justify-between items-center border-b-4 border-[#FF9E00]">
|
||||||
|
<div className="flex items-center gap-6">
|
||||||
|
<div className="bg-white px-4 py-2 rounded flex items-center">
|
||||||
|
<svg width="130" height="32" viewBox="0 0 130 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0 2V30H6.5V18.5L13 30H20.5L12 15.5L19.5 2H12.5L6.5 13V2H0Z" fill="#414449"/>
|
||||||
|
<path d="M23 16C23 8 28.5 2 36.5 2C44.5 2 50 8 50 16C50 24 44.5 30 36.5 30C28.5 30 23 24 23 16ZM43.5 16C43.5 11 41 8 36.5 8C32 8 29.5 11 29.5 16C29.5 21 32 24 36.5 24C41 24 43.5 21 43.5 16Z" fill="#414449"/>
|
||||||
|
<path d="M54 2H65C69 2 72 4 72 8C72 10.5 70.5 12.5 68.5 13.5C71 14.5 73 17 73 20C73 25 69.5 30 64 30H54V2ZM66.5 11.5C66.5 10 65.5 9 64 9H60.5V14H64C65.5 14 66.5 13 66.5 11.5ZM66.5 20.5C66.5 19 65.5 18 64 18H60.5V23H64C65.5 23 66.5 22 66.5 20.5Z" fill="#414449"/>
|
||||||
|
<path d="M78 2H84.5V23H97V30H78V2Z" fill="#414449"/>
|
||||||
|
<rect x="101" y="2" width="6.5" height="6.5" fill="#FF9E00"/>
|
||||||
|
<path d="M101 11H107.5V30H101V11Z" fill="#414449"/>
|
||||||
|
<path d="M112 2V30H118.5V18.5L124 30H130L124.5 18.5L130 2H123L118.5 11.5V2H112Z" fill="#414449"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-xl font-black tracking-widest uppercase hidden md:block">WMS Dashboard</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="flex flex-col items-end mr-4">
|
||||||
|
<span className="text-[10px] uppercase text-slate-400 font-bold tracking-widest text-right">Текущее время</span>
|
||||||
|
<div className="text-xl font-mono font-bold text-[#FF9E00]">
|
||||||
|
{currentTime.toLocaleTimeString('ru-RU')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-[#56595F] p-2 rounded-full">
|
||||||
|
<Clock className="w-5 h-5 text-[#FF9E00]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main className="p-6 max-w-[1600px] mx-auto space-y-6">
|
||||||
|
|
||||||
|
{/* СЕКЦИЯ: ПРИОРИТЕТНЫЕ ЗАДАНИЯ В РАБОТЕ */}
|
||||||
|
<section className="space-y-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Layers className="w-5 h-5 text-[#414449]" />
|
||||||
|
<h2 className="text-sm font-black uppercase tracking-[0.2em] text-[#414449]">Приоритетные задания</h2>
|
||||||
|
</div>
|
||||||
|
<span className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Всего в работе: {priorityDocuments.length}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{priorityDocuments.map((doc, index) => {
|
||||||
|
const progress = Math.round((doc.picked / doc.total) * 100);
|
||||||
|
return (
|
||||||
|
<div key={index} className="bg-white rounded-xl shadow-md border-l-4 border-[#FF9E00] p-4 flex flex-col justify-between hover:shadow-lg transition-shadow">
|
||||||
|
<div className="flex justify-between items-start mb-3">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2 mb-1">
|
||||||
|
<span className={`text-[9px] font-black px-1.5 py-0.5 rounded uppercase ${
|
||||||
|
doc.priority === 'Критический' ? 'bg-red-100 text-red-600' : 'bg-orange-100 text-orange-700'
|
||||||
|
}`}>
|
||||||
|
{doc.priority}
|
||||||
|
</span>
|
||||||
|
<span className="text-slate-400 text-[9px] font-bold uppercase tracking-tighter">{doc.type}</span>
|
||||||
|
</div>
|
||||||
|
<h3 className="font-black text-[#414449] text-lg">{doc.id}</h3>
|
||||||
|
<p className="text-slate-500 text-xs truncate max-w-[150px]">{doc.client}</p>
|
||||||
|
</div>
|
||||||
|
<FileText className="w-5 h-5 text-slate-300" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex justify-between items-end">
|
||||||
|
<span className="text-xs font-bold text-slate-400">{doc.picked} / {doc.total} ед.</span>
|
||||||
|
<span className="text-[#FF9E00] font-black text-sm">{progress}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-1.5 w-full bg-slate-100 rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full bg-[#FF9E00] transition-all duration-500"
|
||||||
|
style={{ width: `${progress}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
|
||||||
|
{/* Операторы ТСД (Активность по заказам) */}
|
||||||
|
<section className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden flex flex-col">
|
||||||
|
<div className="px-6 py-4 border-b border-slate-100 bg-[#414449] flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Smartphone className="w-5 h-5 text-[#FF9E00]" />
|
||||||
|
<h3 className="font-black text-white uppercase text-xs tracking-[0.2em]">Операторы ТСД</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full text-left">
|
||||||
|
<thead className="bg-slate-50">
|
||||||
|
<tr className="text-[10px] uppercase tracking-wider text-slate-400 border-b border-slate-200">
|
||||||
|
<th className="px-6 py-4 font-black">Сотрудник / Заказ</th>
|
||||||
|
<th className="px-6 py-4 font-black">Номенклатура и план</th>
|
||||||
|
<th className="px-6 py-4 font-black text-right">Наличие в зоне</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-slate-100">
|
||||||
|
{tsdActivity.map((task, index) => (
|
||||||
|
<tr key={index} className="hover:bg-slate-50 transition-colors">
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<div className="font-bold text-[#414449] text-sm">{task.worker}</div>
|
||||||
|
<div className="text-[10px] font-mono font-bold text-[#FF9E00] mt-0.5">{task.orderId}</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className={`text-xs font-bold ${task.itemStatus === 'arrived' ? 'text-slate-800' : 'text-slate-500'}`}>
|
||||||
|
{task.item}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1 mt-1">
|
||||||
|
<span className="text-[10px] text-slate-400 uppercase font-bold">Потребность:</span>
|
||||||
|
<span className={`text-xs font-black ${task.itemStatus === 'arrived' ? 'text-[#FF9E00]' : 'text-slate-400'}`}>
|
||||||
|
{task.quantity} {task.unit}.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 text-right">
|
||||||
|
{task.itemStatus === 'arrived' ? (
|
||||||
|
<span className="inline-flex items-center px-2 py-1 rounded bg-emerald-50 text-emerald-700 border border-emerald-200 text-[9px] font-black uppercase tracking-wider">
|
||||||
|
В зоне отбора
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="inline-flex items-center px-2 py-1 rounded bg-slate-100 text-slate-500 border border-slate-200 text-[9px] font-black uppercase tracking-wider">
|
||||||
|
Ожидает подвоза
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Логистика роботов */}
|
||||||
|
<section className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden flex flex-col">
|
||||||
|
<div className="px-6 py-4 border-b border-slate-100 bg-[#414449] flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Bot className="w-5 h-5 text-[#FF9E00]" />
|
||||||
|
<h3 className="font-black text-white uppercase text-xs tracking-[0.2em]">Логистика роботов</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full text-left">
|
||||||
|
<thead className="bg-slate-50">
|
||||||
|
<tr className="text-[10px] uppercase tracking-wider text-slate-400 border-b border-slate-200">
|
||||||
|
<th className="px-6 py-4 font-black">ID Тары</th>
|
||||||
|
<th className="px-6 py-4 font-black">Маршрут</th>
|
||||||
|
<th className="px-6 py-4 font-black">Юнит</th>
|
||||||
|
<th className="px-6 py-4 font-black text-right">Статус</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-slate-100">
|
||||||
|
{containerMoves.map((move) => (
|
||||||
|
<tr key={move.id} className="hover:bg-slate-50 transition-colors">
|
||||||
|
<td className="px-6 py-4 font-mono font-black text-[#414449] text-sm">
|
||||||
|
{move.container}
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<div className="flex items-center gap-2 text-[11px]">
|
||||||
|
<span className="text-slate-400 font-medium">{move.from}</span>
|
||||||
|
<ArrowRightLeft className="w-3 h-3 text-[#FF9E00]" />
|
||||||
|
<span className="font-bold text-slate-800">{move.to}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<div className="inline-flex items-center gap-1.5 px-2 py-1 rounded bg-[#414449] text-[#FF9E00] font-mono text-[10px] font-black">
|
||||||
|
{move.robot}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 text-right">
|
||||||
|
{getStatusBadge(move.statusType, move.status)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer className="mt-8 py-6 bg-white border-t border-slate-200 text-center">
|
||||||
|
<div className="text-slate-400 text-[10px] font-black uppercase tracking-[0.3em] flex justify-center items-center gap-2">
|
||||||
|
<span>KOBLIK GROUP</span>
|
||||||
|
<span className="text-[#FF9E00]">•</span>
|
||||||
|
<span>WMS Logistics Control System</span>
|
||||||
|
<span className="text-[#FF9E00]">•</span>
|
||||||
|
<span>{new Date().getFullYear()}</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user