feat: add delete customer + fix auth form UX
- Backend: DELETE /api/customers/:id removes customer + extensions + logs - Frontend: delete button on customer detail with confirmation - Auth form: only sends espocrm_api_key when credentials are entered Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
-2
@@ -107,6 +107,12 @@ function CustomersTab() {
|
|||||||
const [selected, setSelected] = useState(null)
|
const [selected, setSelected] = useState(null)
|
||||||
const [showForm, setShowForm] = useState(false)
|
const [showForm, setShowForm] = useState(false)
|
||||||
|
|
||||||
|
const deleteCustomer = async (id) => {
|
||||||
|
await fetch(`${API}/customers/${id}`, { method: 'DELETE' })
|
||||||
|
if (selected === id) setSelected(null)
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
|
||||||
if (!customers) return <Loader />
|
if (!customers) return <Loader />
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-6">
|
<div className="flex gap-6">
|
||||||
@@ -143,7 +149,7 @@ function CustomersTab() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
{selected ? <CustomerDetail customerId={selected} /> : (
|
{selected ? <CustomerDetail customerId={selected} onDelete={() => deleteCustomer(selected)} /> : (
|
||||||
<div className="bg-white rounded-xl p-12 text-center text-slate-400">
|
<div className="bg-white rounded-xl p-12 text-center text-slate-400">
|
||||||
<Icon name="touch_app" className="text-5xl mb-4 block" />
|
<Icon name="touch_app" className="text-5xl mb-4 block" />
|
||||||
<p className="font-medium">בחר לקוח מהרשימה</p>
|
<p className="font-medium">בחר לקוח מהרשימה</p>
|
||||||
@@ -226,7 +232,7 @@ function CustomerForm({ onDone, onCancel, customer }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function CustomerDetail({ customerId }) {
|
function CustomerDetail({ customerId, onDelete }) {
|
||||||
const { data: customer, reload } = useFetch(`${API}/customers/${customerId}`)
|
const { data: customer, reload } = useFetch(`${API}/customers/${customerId}`)
|
||||||
const { data: allExtensions } = useFetch(`${API}/extensions`)
|
const { data: allExtensions } = useFetch(`${API}/extensions`)
|
||||||
const [deploying, setDeploying] = useState(null)
|
const [deploying, setDeploying] = useState(null)
|
||||||
@@ -366,6 +372,11 @@ function CustomerDetail({ customerId }) {
|
|||||||
title="עריכת לקוח">
|
title="עריכת לקוח">
|
||||||
<Icon name="edit" className="text-lg" />
|
<Icon name="edit" className="text-lg" />
|
||||||
</button>
|
</button>
|
||||||
|
<button onClick={() => { if (confirm('למחוק את הלקוח לצמיתות?')) onDelete() }}
|
||||||
|
className="p-2 text-slate-400 hover:text-red-600 hover:bg-red-50 rounded-lg transition-all"
|
||||||
|
title="מחיקת לקוח">
|
||||||
|
<Icon name="delete" className="text-lg" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -492,6 +492,9 @@ class APIHandler(BaseHTTPRequestHandler):
|
|||||||
if re.match(r'/api/customers/\d+/extensions/\d+$', path):
|
if re.match(r'/api/customers/\d+/extensions/\d+$', path):
|
||||||
m = re.search(r'/api/customers/(\d+)/extensions/(\d+)', path)
|
m = re.search(r'/api/customers/(\d+)/extensions/(\d+)', path)
|
||||||
self._remove_extension(int(m.group(1)), int(m.group(2)))
|
self._remove_extension(int(m.group(1)), int(m.group(2)))
|
||||||
|
elif re.match(r'/api/customers/\d+$', path):
|
||||||
|
cid = int(re.search(r'/api/customers/(\d+)', path).group(1))
|
||||||
|
self._delete_customer(cid)
|
||||||
else:
|
else:
|
||||||
self._respond(404, {'error': 'Not found'})
|
self._respond(404, {'error': 'Not found'})
|
||||||
|
|
||||||
@@ -560,6 +563,12 @@ class APIHandler(BaseHTTPRequestHandler):
|
|||||||
(cid, body['extension_id'], body.get('auto_update', True), body.get('license_status', 'active')), fetch=None)
|
(cid, body['extension_id'], body.get('auto_update', True), body.get('license_status', 'active')), fetch=None)
|
||||||
self._respond(201, {'status': 'ok'})
|
self._respond(201, {'status': 'ok'})
|
||||||
|
|
||||||
|
def _delete_customer(self, cid):
|
||||||
|
query("DELETE FROM customer_extensions WHERE customer_id = %s", (cid,), fetch=None)
|
||||||
|
query("DELETE FROM deployment_log WHERE customer_id = %s", (cid,), fetch=None)
|
||||||
|
query("DELETE FROM customers WHERE id = %s", (cid,), fetch=None)
|
||||||
|
self._respond(200, {'status': 'ok'})
|
||||||
|
|
||||||
def _remove_extension(self, cid, ext_id):
|
def _remove_extension(self, cid, ext_id):
|
||||||
query("DELETE FROM customer_extensions WHERE customer_id = %s AND extension_id = %s", (cid, ext_id), fetch=None)
|
query("DELETE FROM customer_extensions WHERE customer_id = %s AND extension_id = %s", (cid, ext_id), fetch=None)
|
||||||
self._respond(200, {'status': 'ok'})
|
self._respond(200, {'status': 'ok'})
|
||||||
|
|||||||
Reference in New Issue
Block a user