TT / src /App.jsx
Karmashek's picture
Upload 21 files
4d76deb verified
import React, { useState, useEffect } from 'react';
import { createClient } from '@supabase/supabase-js';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTachometerAlt, faCalendarAlt, faTools, faWrench, faClipboardCheck, faChartLine, faUsersCog, faBell, faBars } from '@fortawesome/free-solid-svg-icons';
import Swal from 'sweetalert2';
import Dashboard from './components/Dashboard';
import Equipment from './components/Equipment';
// Initialize Supabase client
const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY
);
function App() {
const [currentTab, setCurrentTab] = useState('dashboard');
const [equipment, setEquipment] = useState([]);
const [maintenanceEvents, setMaintenanceEvents] = useState([]);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
useEffect(() => {
const checkUser = async () => {
const { data: { user } } = await supabase.auth.getUser();
if (user) {
setIsLoggedIn(true);
loadData();
}
};
checkUser();
}, []);
const loadData = async () => {
try {
const [equipmentResult, eventsResult] = await Promise.all([
supabase.from('equipment').select('*').order('created_at', { ascending: false }),
supabase.from('maintenance_events').select('*').order('date', { ascending: false })
]);
if (equipmentResult.error) throw equipmentResult.error;
if (eventsResult.error) throw eventsResult.error;
setEquipment(equipmentResult.data || []);
setMaintenanceEvents(eventsResult.data || []);
} catch (error) {
console.error('Error loading data:', error);
Swal.fire({
title: 'Ошибка',
text: 'Не удалось загрузить данные',
icon: 'error'
});
}
};
const handleLogin = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const email = formData.get('email');
const password = formData.get('password');
try {
const { error } = await supabase.auth.signInWithPassword({
email,
password
});
if (error) throw error;
setIsLoggedIn(true);
loadData();
} catch (error) {
Swal.fire({
title: 'Ошибка входа',
text: error.message,
icon: 'error'
});
}
};
const handleLogout = async () => {
await supabase.auth.signOut();
setIsLoggedIn(false);
};
const handleAddEquipment = async (formData) => {
try {
const { error } = await supabase.from('equipment').insert([{
name: formData.get('name'),
type: formData.get('type'),
serial: formData.get('serial'),
inventory: formData.get('inventory'),
commission_date: formData.get('commissionDate'),
maintenance_interval: parseInt(formData.get('interval')),
description: formData.get('description'),
status: 'active'
}]);
if (error) throw error;
loadData();
Swal.fire({
title: 'Успешно',
text: 'Оборудование добавлено',
icon: 'success'
});
} catch (error) {
Swal.fire({
title: 'Ошибка',
text: error.message,
icon: 'error'
});
}
};
if (!isLoggedIn) {
return (
<div className="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center">
<div className="bg-white rounded-lg shadow-lg w-full max-w-md p-6">
<h3 className="text-xl font-bold mb-4">Вход в систему</h3>
<form onSubmit={handleLogin} className="space-y-4">
<input
type="email"
name="email"
placeholder="Email"
required
className="w-full px-4 py-2 border rounded-lg"
/>
<input
type="password"
name="password"
placeholder="Пароль"
required
className="w-full px-4 py-2 border rounded-lg"
/>
<button
type="submit"
className="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 rounded-lg"
>
Войти
</button>
</form>
</div>
</div>
);
}
return (
<div className="flex min-h-screen bg-gray-100">
{/* Sidebar */}
<div className={`fixed top-0 left-0 h-full bg-blue-800 text-white ${sidebarCollapsed ? 'w-16' : 'w-64'} p-4 transition-all duration-300`}>
<div className="flex items-center justify-between mb-6">
{!sidebarCollapsed && <h1 className="text-xl font-bold">MES System</h1>}
<button onClick={() => setSidebarCollapsed(!sidebarCollapsed)} className="text-white hover:text-blue-200">
<FontAwesomeIcon icon={faBars} />
</button>
</div>
<nav>
<ul className="space-y-2">
{[
{ id: 'dashboard', icon: faTachometerAlt, label: 'Главная' },
{ id: 'schedule', icon: faCalendarAlt, label: 'График ТО' },
{ id: 'equipment', icon: faTools, label: 'Оборудование' },
{ id: 'maintenance', icon: faClipboardCheck, label: 'Техобслуживание' },
{ id: 'reports', icon: faChartLine, label: 'Отчеты' },
{ id: 'users', icon: faUsersCog, label: 'Пользователи' }
].map(item => (
<li key={item.id}>
<button
onClick={() => setCurrentTab(item.id)}
className={`w-full text-left px-4 py-2 rounded-lg hover:bg-blue-700 flex items-center ${currentTab === item.id ? 'bg-blue-700' : ''}`}
>
<FontAwesomeIcon icon={item.icon} className="mr-2" />
{!sidebarCollapsed && item.label}
</button>
</li>
))}
</ul>
</nav>
</div>
{/* Main Content */}
<div className={`flex-1 ${sidebarCollapsed ? 'ml-16' : 'ml-64'} p-6 transition-all duration-300`}>
{/* Header */}
<header className="bg-white shadow-sm p-4 rounded-lg mb-6 flex justify-between items-center">
<h1 className="text-2xl font-semibold">
{currentTab === 'dashboard' ? 'Главная панель' :
currentTab === 'schedule' ? 'График ТО' :
currentTab === 'equipment' ? 'Оборудование' :
currentTab === 'maintenance' ? 'Техобслуживание' :
currentTab === 'reports' ? 'Отчеты' : 'Пользователи'}
</h1>
<div className="flex space-x-4">
<button className="p-2 rounded-full hover:bg-gray-100 relative">
<FontAwesomeIcon icon={faBell} className="text-gray-600" />
<span className="absolute top-0 right-0 bg-red-500 text-white text-xs w-5 h-5 flex items-center justify-center rounded-full">3</span>
</button>
<button
onClick={handleLogout}
className="bg-red-500 text-white px-4 py-2 rounded-lg hover:bg-red-600"
>
Выход
</button>
</div>
</header>
{/* Content based on current tab */}
<div className="bg-white rounded-lg shadow p-6">
{currentTab === 'dashboard' && (
<Dashboard equipment={equipment} maintenanceEvents={maintenanceEvents} />
)}
{currentTab === 'equipment' && (
<Equipment
equipment={equipment}
onAdd={handleAddEquipment}
onEdit={(item) => {
// Handle edit
}}
onDelete={async (id) => {
try {
const { error } = await supabase
.from('equipment')
.delete()
.eq('id', id);
if (error) throw error;
loadData();
Swal.fire({
title: 'Успешно',
text: 'Оборудование удалено',
icon: 'success'
});
} catch (error) {
Swal.fire({
title: 'Ошибка',
text: error.message,
icon: 'error'
});
}
}}
/>
)}
{/* Add other tab components here */}
</div>
</div>
</div>
);
}
export default App;