import { useEffect, useState } from 'react'; import { Layout, Table, Button, Form, Input, InputNumber, Upload, Modal, Space, Tag, Typography, Switch, message, Image as AntImage, theme, Tabs } from 'antd'; import { UploadOutlined, DeleteOutlined } from '@ant-design/icons'; import type { UploadFile } from 'antd/es/upload/interface'; import MDEditor from '@uiw/react-md-editor'; import '@uiw/react-md-editor/markdown-editor.css'; import '@uiw/react-markdown-preview/markdown.css'; import { Item, fetchAdminItems, saveItem, deleteItem, deleteItemImage, markSold, markAvailable } from '../api'; import { useThemeMode } from '../theme/ThemeContext'; import Customers from './Customers'; import Settings from './Settings'; const { Header, Content } = Layout; const { Title } = Typography; function Inventory() { const [items, setItems] = useState([]); const [modalOpen, setModalOpen] = useState(false); const [editingItem, setEditingItem] = useState(null); const [form] = Form.useForm(); const [fileList, setFileList] = useState([]); const [description, setDescription] = useState(''); const { mode } = useThemeMode(); const load = () => fetchAdminItems().then(setItems); useEffect(() => { load(); }, []); function openNew() { setEditingItem(null); form.resetFields(); setFileList([]); setDescription(''); setModalOpen(true); } function openEdit(item: Item) { setEditingItem(item); form.setFieldsValue({ name: item.name, price: item.price_cents / 100 }); setFileList([]); setDescription(item.description || ''); setModalOpen(true); } async function handleOk() { const values = await form.validateFields(); const fd = new FormData(); fd.append('name', values.name); fd.append('description', description); fd.append('price', String(values.price)); fileList.forEach(f => { if (f.originFileObj) fd.append('images', f.originFileObj as File); }); await saveItem(editingItem?.id ?? null, fd); message.success(editingItem ? 'Item updated' : 'Item added'); setModalOpen(false); load(); } async function handleDelete(id: number) { await deleteItem(id); message.success('Item deleted'); load(); } async function handleDeleteImage(itemId: number, imageId: number) { await deleteItemImage(itemId, imageId); message.success('Image removed'); load(); setEditingItem(prev => prev && prev.id === itemId ? { ...prev, images: prev.images.filter(img => img.id !== imageId) } : prev); } const columns = [ { title: 'Image', dataIndex: 'images', render: (images: Item['images']) => images[0] ? ( {images.length > 1 && ( +{images.length - 1} )} ) : null }, { title: 'Name', dataIndex: 'name' }, { title: 'Price', dataIndex: 'price_cents', render: (v: number) => `$${(v / 100).toFixed(2)}` }, { title: 'Status', dataIndex: 'status', render: (status: string) => ( {status.toUpperCase()} ) }, { title: 'Actions', render: (_: unknown, item: Item) => ( {item.status !== 'sold' ? : } ) } ]; return (
Inventory
setModalOpen(false)} destroyOnClose width={720}>
setDescription(val || '')} height={220} preview="live" /> {editingItem && editingItem.images.length > 0 && ( {editingItem.images.map(img => (
))}
)} false} onChange={({ fileList }) => setFileList(fileList.slice(-6))} maxCount={6} multiple listType="picture-card">
Upload
); } export default function Admin() { const { mode, toggle } = useThemeMode(); const { token } = theme.useToken(); return (
Admin
}, { key: 'customers', label: 'Customers', children: }, { key: 'settings', label: 'Settings', children: } ]} />
); }