fix: cancel button on form, edit customer, show installed/total count
This commit is contained in:
+48
-24
@@ -119,7 +119,7 @@ function CustomersTab() {
|
||||
<span>חדש</span>
|
||||
</button>
|
||||
</div>
|
||||
{showForm && <CustomerForm onDone={() => { setShowForm(false); reload() }} />}
|
||||
{showForm && <CustomerForm onDone={() => { setShowForm(false); reload() }} onCancel={() => setShowForm(false)} />}
|
||||
<div className="space-y-2">
|
||||
{customers.map(c => (
|
||||
<div key={c.id} onClick={() => setSelected(c.id)}
|
||||
@@ -134,7 +134,7 @@ function CustomersTab() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 mt-3 text-xs text-slate-400">
|
||||
<span>{c.extension_count} הרחבות</span>
|
||||
<span>{c.installed_count || 0}/{c.extension_count} מותקנות</span>
|
||||
<span>•</span>
|
||||
<span className={c.is_active ? 'text-emerald-600' : 'text-red-500'}>{c.is_active ? 'פעיל' : 'לא פעיל'}</span>
|
||||
</div>
|
||||
@@ -154,29 +154,43 @@ function CustomersTab() {
|
||||
)
|
||||
}
|
||||
|
||||
function CustomerForm({ onDone }) {
|
||||
const [form, setForm] = useState({ name: '', email: '', espocrm_url: '', espocrm_api_key: '' })
|
||||
function CustomerForm({ onDone, onCancel, customer }) {
|
||||
const isEdit = !!customer
|
||||
const [form, setForm] = useState({
|
||||
name: customer?.name || '', email: customer?.email || '',
|
||||
espocrm_url: customer?.espocrm_url || '', espocrm_api_key: ''
|
||||
})
|
||||
const submit = async () => {
|
||||
await fetch(`${API}/customers`, {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form)
|
||||
const url = isEdit ? `${API}/customers/${customer.id}` : `${API}/customers`
|
||||
await fetch(url, {
|
||||
method: isEdit ? 'PUT' : 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(form)
|
||||
})
|
||||
onDone()
|
||||
}
|
||||
const inputClass = "w-full bg-slate-50 border-none rounded-xl px-4 py-2.5 text-sm focus:ring-2 focus:ring-blue-500"
|
||||
return (
|
||||
<div className="bg-white rounded-xl p-5 mb-4 space-y-3">
|
||||
<input placeholder="שם לקוח" value={form.name} onChange={e => setForm({ ...form, name: e.target.value })}
|
||||
className="w-full bg-slate-50 border-none rounded-xl px-4 py-2.5 text-sm focus:ring-2 focus:ring-blue-500 text-right" />
|
||||
className={`${inputClass} text-right`} />
|
||||
<input placeholder="אימייל" value={form.email} onChange={e => setForm({ ...form, email: e.target.value })}
|
||||
className="w-full bg-slate-50 border-none rounded-xl px-4 py-2.5 text-sm focus:ring-2 focus:ring-blue-500" dir="ltr" />
|
||||
className={inputClass} dir="ltr" />
|
||||
<input placeholder="EspoCRM URL" value={form.espocrm_url} onChange={e => setForm({ ...form, espocrm_url: e.target.value })}
|
||||
className="w-full bg-slate-50 border-none rounded-xl px-4 py-2.5 text-sm focus:ring-2 focus:ring-blue-500" dir="ltr" />
|
||||
<input placeholder="Espo-Authorization (base64)" value={form.espocrm_api_key}
|
||||
onChange={e => setForm({ ...form, espocrm_api_key: e.target.value })}
|
||||
className="w-full bg-slate-50 border-none rounded-xl px-4 py-2.5 text-sm font-mono focus:ring-2 focus:ring-blue-500" dir="ltr" />
|
||||
<button onClick={submit}
|
||||
className="deep-action-gradient text-white px-4 py-2.5 rounded-xl font-bold w-full shadow-lg shadow-blue-500/20 active:scale-95 transition-all">
|
||||
שמור
|
||||
</button>
|
||||
className={inputClass} dir="ltr" />
|
||||
<input placeholder={isEdit ? "Espo-Authorization (השאר ריק אם לא משתנה)" : "Espo-Authorization (base64)"}
|
||||
value={form.espocrm_api_key} onChange={e => setForm({ ...form, espocrm_api_key: e.target.value })}
|
||||
className={`${inputClass} font-mono`} dir="ltr" />
|
||||
<div className="flex gap-2">
|
||||
<button onClick={submit}
|
||||
className="deep-action-gradient text-white px-4 py-2.5 rounded-xl font-bold flex-1 shadow-lg shadow-blue-500/20 active:scale-95 transition-all">
|
||||
{isEdit ? 'עדכון' : 'שמור'}
|
||||
</button>
|
||||
<button onClick={onCancel}
|
||||
className="px-4 py-2.5 rounded-xl font-bold text-slate-500 hover:bg-slate-100 transition-all">
|
||||
ביטול
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -186,6 +200,7 @@ function CustomerDetail({ customerId }) {
|
||||
const { data: allExtensions } = useFetch(`${API}/extensions`)
|
||||
const [deploying, setDeploying] = useState(null)
|
||||
const [deployResult, setDeployResult] = useState(null)
|
||||
const [editing, setEditing] = useState(false)
|
||||
|
||||
if (!customer) return <Loader />
|
||||
|
||||
@@ -224,15 +239,24 @@ function CustomerDetail({ customerId }) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center gap-4 mb-6">
|
||||
<div className="w-12 h-12 rounded-xl bg-blue-100 flex items-center justify-center text-blue-700">
|
||||
<Icon name="domain" className="text-2xl" />
|
||||
{editing ? (
|
||||
<CustomerForm customer={customer} onDone={() => { setEditing(false); reload() }} onCancel={() => setEditing(false)} />
|
||||
) : (
|
||||
<div className="flex items-center gap-4 mb-6">
|
||||
<div className="w-12 h-12 rounded-xl bg-blue-100 flex items-center justify-center text-blue-700">
|
||||
<Icon name="domain" className="text-2xl" />
|
||||
</div>
|
||||
<div className="text-right flex-1">
|
||||
<h3 className="text-xl font-black text-slate-900">{customer.name}</h3>
|
||||
<p className="text-sm text-slate-500" dir="ltr">{customer.espocrm_url}</p>
|
||||
</div>
|
||||
<button onClick={() => setEditing(true)}
|
||||
className="p-2 text-slate-400 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all"
|
||||
title="עריכת לקוח">
|
||||
<Icon name="edit" className="text-lg" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<h3 className="text-xl font-black text-slate-900">{customer.name}</h3>
|
||||
<p className="text-sm text-slate-500" dir="ltr">{customer.espocrm_url}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{deployResult && (
|
||||
<div className={`p-4 rounded-xl mb-6 text-sm font-medium flex items-center gap-3 ${deployResult.status === 'ok' ? 'bg-emerald-50 text-emerald-800' : 'bg-red-50 text-red-800'}`}>
|
||||
|
||||
@@ -394,6 +394,7 @@ class APIHandler(BaseHTTPRequestHandler):
|
||||
rows = query("""SELECT c.id, c.name, c.email, c.espocrm_url, c.is_active, c.notes, c.created_at, c.updated_at,
|
||||
c.coolify_url, c.coolify_app_uuid,
|
||||
(SELECT COUNT(*) FROM customer_extensions ce WHERE ce.customer_id = c.id AND ce.license_status = 'active') as extension_count,
|
||||
(SELECT COUNT(*) FROM customer_extensions ce WHERE ce.customer_id = c.id AND ce.license_status = 'active' AND ce.installed_version IS NOT NULL) as installed_count,
|
||||
(SELECT MAX(dl.started_at) FROM deployment_log dl WHERE dl.customer_id = c.id) as last_deploy
|
||||
FROM customers c ORDER BY c.name""")
|
||||
self._respond(200, rows)
|
||||
|
||||
Reference in New Issue
Block a user