diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..a405ba4 --- /dev/null +++ b/src/App.css @@ -0,0 +1,176 @@ +/* Simple styling for inventory app - no framework, just basic CSS */ + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; +} + +header { + text-align: center; + margin-bottom: 3rem; + border-bottom: 2px solid #646cff; + padding-bottom: 1rem; +} + +header h1 { + margin: 0; + color: #646cff; +} + +header p { + margin: 0.5rem 0 0 0; + color: #888; +} + +/* Form Section */ +.form-section { + background: #1a1a1a; + padding: 2rem; + border-radius: 8px; + margin-bottom: 2rem; +} + +.form-section h2 { + margin-top: 0; + color: #646cff; +} + +form { + display: flex; + flex-direction: column; + gap: 1rem; + max-width: 500px; +} + +.form-group { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; +} + +.form-group label { + font-weight: bold; + color: #fff; +} + +.form-group input { + width: 100%; + padding: 0.75rem; + border: 1px solid #444; + border-radius: 4px; + background: #2a2a2a; + color: #fff; + font-size: 1rem; +} + +.form-group input:focus { + outline: none; + border-color: #646cff; +} + +.form-group input:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +button[type="submit"] { + padding: 0.75rem 2rem; + background: #646cff; + color: white; + border: none; + border-radius: 4px; + font-size: 1rem; + font-weight: bold; + cursor: pointer; + transition: background 0.3s; +} + +button[type="submit"]:hover:not(:disabled) { + background: #535bf2; +} + +button[type="submit"]:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* List Section */ +.list-section { + background: #1a1a1a; + padding: 2rem; + border-radius: 8px; +} + +.list-section h2 { + margin-top: 0; + color: #646cff; +} + +.loading, .error, .empty { + text-align: center; + padding: 2rem; + font-size: 1.1rem; +} + +.loading { + color: #646cff; +} + +.error { + color: #ff6b6b; +} + +.empty { + color: #888; +} + +/* Table Styles */ +table { + width: 100%; + border-collapse: collapse; + margin-top: 1rem; +} + +thead { + background: #2a2a2a; +} + +th, td { + padding: 1rem; + text-align: left; + border-bottom: 1px solid #444; +} + +th { + font-weight: bold; + color: #646cff; +} + +td { + color: #fff; +} + +tbody tr:hover { + background: #2a2a2a; +} + +tbody tr:last-child td { + border-bottom: none; +} + +/* Responsive */ +@media (max-width: 768px) { + .container { + padding: 1rem; + } + + table { + font-size: 0.9rem; + } + + th, td { + padding: 0.5rem; + } +} diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..59c98a8 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,182 @@ +// Single component app - list + form (minimal design) +import { useState, useEffect } from 'react'; +import { getItems, createItem } from './api'; +import './App.css'; + +function App() { + const [items, setItems] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [formData, setFormData] = useState({ + name: '', + quantity: '', + price: '' + }); + const [submitting, setSubmitting] = useState(false); + + // Fetch items on mount + useEffect(() => { + fetchItems(); + }, []); + + const fetchItems = async () => { + try { + setLoading(true); + setError(null); + const data = await getItems(); + setItems(data); + } catch (err) { + setError('Failed to load items: ' + err.message); + console.error('Error fetching items:', err); + } finally { + setLoading(false); + } + }; + + const handleInputChange = (e) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value + })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + // Basic validation + if (!formData.name.trim()) { + alert('Name is required'); + return; + } + if (formData.quantity === '' || formData.quantity < 0) { + alert('Valid quantity is required'); + return; + } + if (formData.price === '' || formData.price < 0) { + alert('Valid price is required'); + return; + } + + try { + setSubmitting(true); + await createItem({ + name: formData.name.trim(), + quantity: parseInt(formData.quantity), + price: parseFloat(formData.price) + }); + + // Reset form + setFormData({ name: '', quantity: '', price: '' }); + + // Refresh list + await fetchItems(); + + alert('Item added successfully!'); + } catch (err) { + alert('Failed to add item: ' + err.message); + console.error('Error creating item:', err); + } finally { + setSubmitting(false); + } + }; + + return ( +
Simple 2-tier app for DevOps demo
+Loading items...
} + + {error &&{error}
} + + {!loading && !error && items.length === 0 && ( +No items found. Add one above!
+ )} + + {!loading && !error && items.length > 0 && ( +| ID | +Name | +Quantity | +Price | +
|---|---|---|---|
| {item.id} | +{item.name} | +{item.quantity} | +${parseFloat(item.price).toFixed(2)} | +