Add manual customer creation and fix color edit modal
All checks were successful
Deploy / detect (push) Successful in 4s
Deploy / deploy-frontend (push) Successful in 2m24s
Deploy / tag-deploy (push) Successful in 5s
Deploy / deploy-api (push) Has been skipped

- Add "Dodaj kupca" button and modal form to customers page
- Fix color edit modal dark mode styling and add backdrop blur
This commit is contained in:
DaX
2026-03-05 02:35:13 +01:00
parent f564f944f7
commit cff73c1381
2 changed files with 161 additions and 6 deletions

View File

@@ -370,10 +370,24 @@ export default function ColorsManagement() {
{/* Edit Modal */}
{editingColor && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full max-h-[90vh] overflow-auto">
<div
className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4"
onClick={() => setEditingColor(null)}
>
<div
className="bg-white dark:bg-gray-800 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-700 max-w-md w-full max-h-[90vh] overflow-auto"
onClick={(e) => e.stopPropagation()}
>
<div className="flex justify-between items-center p-6 pb-0">
<h3 className="text-xl font-bold text-gray-900 dark:text-white">Izmeni boju</h3>
<button
onClick={() => setEditingColor(null)}
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-2xl leading-none"
>
&times;
</button>
</div>
<div className="p-6">
<h3 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">Izmeni boju</h3>
<ColorForm
color={editingColor}
onSave={(color) => {
@@ -477,7 +491,7 @@ function ColorForm({
readOnly={isBambuLabColor}
pattern="^#[0-9A-Fa-f]{6}$"
placeholder="#000000"
className={`flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 ${isBambuLabColor ? 'bg-gray-100 dark:bg-gray-900 cursor-not-allowed' : ''}`}
className={`flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 ${isBambuLabColor ? 'bg-gray-100 dark:bg-gray-900 cursor-not-allowed' : 'bg-white dark:bg-gray-700'}`}
/>
</div>
{isBambuLabColor && (

View File

@@ -25,6 +25,10 @@ export default function CustomersManagement() {
const [saving, setSaving] = useState(false);
const [darkMode, setDarkMode] = useState(false);
const [mounted, setMounted] = useState(false);
const [showAddModal, setShowAddModal] = useState(false);
const [addForm, setAddForm] = useState({ name: '', phone: '', city: '', notes: '' });
const [addError, setAddError] = useState('');
const [addSaving, setAddSaving] = useState(false);
// Initialize dark mode
useEffect(() => {
@@ -139,6 +143,35 @@ export default function CustomersManagement() {
}
};
const handleAddCustomer = async () => {
if (!addForm.name.trim()) {
setAddError('Ime je obavezno');
return;
}
setAddSaving(true);
setAddError('');
try {
await customerService.create({
name: addForm.name.trim(),
phone: addForm.phone.trim() || undefined,
city: addForm.city.trim() || undefined,
notes: addForm.notes.trim() || undefined,
});
setShowAddModal(false);
setAddForm({ name: '', phone: '', city: '', notes: '' });
await fetchCustomers();
} catch (err: unknown) {
const error = err as { response?: { data?: { code?: string } } };
if (error.response?.data?.code === '23505') {
setAddError('Kupac sa ovim brojem telefona vec postoji');
} else {
setAddError('Greska pri dodavanju kupca');
}
} finally {
setAddSaving(false);
}
};
const handleLogout = () => {
localStorage.removeItem('authToken');
localStorage.removeItem('tokenExpiry');
@@ -230,8 +263,8 @@ export default function CustomersManagement() {
</div>
)}
{/* Search bar */}
<div className="mb-6">
{/* Search bar + Add button */}
<div className="mb-6 flex items-center gap-4">
<input
type="text"
placeholder="Pretrazi kupce..."
@@ -239,6 +272,12 @@ export default function CustomersManagement() {
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full max-w-md px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
<button
onClick={() => setShowAddModal(true)}
className="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 whitespace-nowrap font-medium"
>
+ Dodaj kupca
</button>
</div>
{/* Customer count */}
@@ -488,6 +527,108 @@ export default function CustomersManagement() {
</main>
</div>
</div>
{/* Add Customer Modal */}
{showAddModal && (
<div
className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
onClick={() => setShowAddModal(false)}
>
<div
className="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-md mx-4 p-6"
onClick={(e) => e.stopPropagation()}
>
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-bold text-gray-900 dark:text-white">
Dodaj kupca
</h2>
<button
onClick={() => setShowAddModal(false)}
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-2xl leading-none"
>
&times;
</button>
</div>
{addError && (
<div className="mb-4 p-3 bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-400 rounded text-sm">
{addError}
</div>
)}
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Ime *
</label>
<input
type="text"
value={addForm.name}
onChange={(e) => setAddForm({ ...addForm, name: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-green-500 focus:border-transparent"
placeholder="Ime i prezime"
autoFocus
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Telefon
</label>
<input
type="text"
value={addForm.phone}
onChange={(e) => setAddForm({ ...addForm, phone: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-green-500 focus:border-transparent"
placeholder="Broj telefona"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Grad
</label>
<input
type="text"
value={addForm.city}
onChange={(e) => setAddForm({ ...addForm, city: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-green-500 focus:border-transparent"
placeholder="Grad"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Napomena
</label>
<textarea
value={addForm.notes}
onChange={(e) => setAddForm({ ...addForm, notes: e.target.value })}
rows={3}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-green-500 focus:border-transparent"
placeholder="npr. stampa figurice, obicno crna..."
/>
</div>
</div>
<div className="flex justify-end gap-3 mt-6">
<button
onClick={() => setShowAddModal(false)}
className="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600"
>
Otkazi
</button>
<button
onClick={handleAddCustomer}
disabled={addSaving}
className="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 disabled:opacity-50 font-medium"
>
{addSaving ? 'Cuvanje...' : 'Dodaj'}
</button>
</div>
</div>
</div>
)}
</div>
);
}