File: /var/www/indoadvisory_new/webapp/src/routes/clients.tsx
import { Hono } from 'hono'
import { getCookie } from 'hono/cookie'
import { type Client } from '../utils/database'
const clients = new Hono<{ Bindings: { DB: D1Database } }>()
// Middleware to check authentication
async function requireAuth(c: any, next: any) {
const sessionId = getCookie(c, 'admin_session')
if (!sessionId) {
return c.redirect('/admin/login')
}
// Verify session
const session = await c.env.DB.prepare(
'SELECT * FROM admin_sessions WHERE id = ? AND expires_at > datetime("now")'
).bind(sessionId).first()
if (!session) {
return c.redirect('/admin/login')
}
return next()
}
clients.use('*', requireAuth)
// Client list page
clients.get('/', async (c) => {
try {
const page = parseInt(c.req.query('page') || '1')
const limit = 10
const offset = (page - 1) * limit
const search = c.req.query('search') || ''
const status = c.req.query('status') || 'all'
let query = 'SELECT * FROM clients'
let countQuery = 'SELECT COUNT(*) as total FROM clients'
const params: any[] = []
const conditions: string[] = []
if (search) {
conditions.push('(company_name LIKE ? OR industry LIKE ? OR project_type LIKE ?)')
const searchPattern = `%${search}%`
params.push(searchPattern, searchPattern, searchPattern)
}
if (status !== 'all') {
if (status === 'active') {
conditions.push('is_active = 1')
} else if (status === 'featured') {
conditions.push('is_featured = 1')
}
}
if (conditions.length > 0) {
const whereClause = ' WHERE ' + conditions.join(' AND ')
query += whereClause
countQuery += whereClause
}
query += ' ORDER BY sort_order ASC, created_at DESC LIMIT ? OFFSET ?'
params.push(limit, offset)
const [clientsResult, countResult] = await Promise.all([
c.env.DB.prepare(query).bind(...params).all() as Promise<{ results: Client[] }>,
c.env.DB.prepare(countQuery).bind(...(params.slice(0, -2))).first() as Promise<{ total: number }>
])
const totalPages = Math.ceil(countResult.total / limit)
return c.render(
<div class="min-h-screen bg-gray-50">
{/* Header */}
<div class="bg-white shadow-sm border-b border-gray-200">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<div class="flex items-center space-x-4">
<a href="/admin" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-arrow-left mr-2"></i>
Kembali ke Dashboard
</a>
<div class="text-gray-300">•</div>
<h1 class="text-xl font-semibold text-gray-900">Manajemen Klien</h1>
</div>
<a
href="/admin/clients/create"
class="mckinsey-btn px-4 py-2 rounded-lg text-sm font-medium"
>
<i class="fas fa-plus mr-2"></i>
Tambah Klien
</a>
</div>
</div>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Filters and Search */}
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
<form method="GET" class="flex flex-wrap gap-4">
<div class="flex-1 min-w-64">
<label class="block text-sm font-medium text-gray-700 mb-2">Pencarian</label>
<input
type="text"
name="search"
value={search}
placeholder="Cari nama perusahaan, industri, atau jenis proyek..."
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Status</label>
<select
name="status"
class="px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
>
<option value="all" selected={status === 'all'}>Semua</option>
<option value="active" selected={status === 'active'}>Aktif</option>
<option value="featured" selected={status === 'featured'}>Featured</option>
</select>
</div>
<div class="flex items-end">
<button
type="submit"
class="mckinsey-btn px-4 py-2 rounded-md text-sm font-medium"
>
<i class="fas fa-search mr-2"></i>
Cari
</button>
</div>
</form>
</div>
{/* Client Table */}
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Perusahaan
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Industri
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Proyek
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Nilai
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Aksi
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{clientsResult.results.length > 0 ? clientsResult.results.map(client => (
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="w-10 h-10 bg-mckinsey-blue rounded-lg flex items-center justify-center mr-3">
<i class="fas fa-building text-white text-sm"></i>
</div>
<div>
<div class="text-sm font-medium text-gray-900">{client.company_name}</div>
<div class="text-sm text-gray-500">Urutan: {client.sort_order}</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
{client.industry}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">{client.project_type}</div>
{client.completion_date && (
<div class="text-sm text-gray-500">
{new Date(client.completion_date).toLocaleDateString('id-ID')}
</div>
)}
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">
{client.project_value || '-'}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex flex-col space-y-1">
<span class={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
client.is_active
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}>
{client.is_active ? 'Aktif' : 'Nonaktif'}
</span>
{client.is_featured && (
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">
Featured
</span>
)}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
<a
href={`/admin/clients/${client.id}/edit`}
class="text-mckinsey-blue hover:text-mckinsey-darkblue"
>
<i class="fas fa-edit mr-1"></i>
Edit
</a>
<button
onclick={`deleteClient(${client.id})`}
class="text-red-600 hover:text-red-900 ml-2"
>
<i class="fas fa-trash mr-1"></i>
Hapus
</button>
</td>
</tr>
)) : (
<tr>
<td colspan="6" class="px-6 py-12 text-center text-gray-500">
<i class="fas fa-building text-gray-400 text-4xl mb-4"></i>
<div class="text-lg font-medium text-gray-400 mb-2">Belum ada klien</div>
<p class="text-gray-400">Klik "Tambah Klien" untuk menambah klien pertama</p>
</td>
</tr>
)}
</tbody>
</table>
</div>
{/* Pagination */}
{totalPages > 1 && (
<div class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6 mt-6 rounded-lg">
<div class="flex-1 flex justify-between sm:hidden">
{page > 1 && (
<a
href={`/admin/clients?page=${page - 1}&search=${search}&status=${status}`}
class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
>
Previous
</a>
)}
{page < totalPages && (
<a
href={`/admin/clients?page=${page + 1}&search=${search}&status=${status}`}
class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
>
Next
</a>
)}
</div>
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
<div>
<p class="text-sm text-gray-700">
Menampilkan{' '}
<span class="font-medium">{Math.min((page - 1) * limit + 1, countResult.total)}</span>
{' '}sampai{' '}
<span class="font-medium">{Math.min(page * limit, countResult.total)}</span>
{' '}dari{' '}
<span class="font-medium">{countResult.total}</span>
{' '}hasil
</p>
</div>
<div>
<nav class="relative z-0 inline-flex rounded-md shadow-sm -space-x-px">
{Array.from({ length: totalPages }, (_, i) => i + 1).map(pageNum => (
<a
href={`/admin/clients?page=${pageNum}&search=${search}&status=${status}`}
class={`relative inline-flex items-center px-4 py-2 border text-sm font-medium ${
pageNum === page
? 'z-10 bg-mckinsey-blue border-mckinsey-blue text-white'
: 'bg-white border-gray-300 text-gray-500 hover:bg-gray-50'
}`}
>
{pageNum}
</a>
))}
</nav>
</div>
</div>
</div>
)}
</div>
{/* Delete confirmation script */}
<script dangerouslySetInnerHTML={{__html: `
async function deleteClient(clientId) {
if (!confirm('Apakah Anda yakin ingin menghapus klien ini?')) {
return;
}
try {
const response = await fetch('/admin/clients/' + clientId, {
method: 'DELETE'
});
if (response.ok) {
location.reload();
} else {
alert('Gagal menghapus klien. Silakan coba lagi.');
}
} catch (error) {
alert('Terjadi kesalahan. Silakan coba lagi.');
}
}
`}} />
</div>,
{
title: 'Manajemen Klien - IndoAdvisory Admin'
}
)
} catch (error) {
console.error('Clients list error:', error)
return c.render(<div>Error loading clients</div>)
}
})
// Create client form
clients.get('/create', async (c) => {
return c.render(
<div class="min-h-screen bg-gray-50">
<div class="bg-white shadow-sm border-b border-gray-200">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center space-x-4 h-16">
<a href="/admin/clients" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-arrow-left mr-2"></i>
Kembali ke Daftar Klien
</a>
<div class="text-gray-300">•</div>
<h1 class="text-xl font-semibold text-gray-900">Tambah Klien Baru</h1>
</div>
</div>
</div>
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<form method="POST" action="/admin/clients" class="space-y-6">
<div class="bg-white rounded-lg shadow-sm p-6">
<h3 class="text-lg font-medium text-gray-900 mb-6">Informasi Dasar</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Nama Perusahaan *</label>
<input
type="text"
name="company_name"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
placeholder="Contoh: PT Teknologi Nusantara"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Industri *</label>
<select
name="industry"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
>
<option value="">Pilih industri</option>
<option value="Technology">Technology</option>
<option value="Finance">Finance</option>
<option value="Healthcare">Healthcare</option>
<option value="Manufacturing">Manufacturing</option>
<option value="Retail">Retail</option>
<option value="Energy">Energy</option>
<option value="Agriculture">Agriculture</option>
<option value="Real Estate">Real Estate</option>
<option value="E-commerce">E-commerce</option>
<option value="Other">Other</option>
</select>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Jenis Proyek *</label>
<select
name="project_type"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
>
<option value="">Pilih jenis proyek</option>
<option value="valuation">Valuasi Perusahaan</option>
<option value="ipo">Persiapan IPO</option>
<option value="ma">M&A Advisory</option>
<option value="fundraising">Fundraising</option>
<option value="restructuring">Restructuring</option>
<option value="dd">Due Diligence</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Nilai Proyek</label>
<input
type="text"
name="project_value"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
placeholder="Contoh: $25M Series B, Rp 2.1T IPO"
/>
</div>
</div>
<div class="mt-6">
<label class="block text-sm font-medium text-gray-700 mb-2">Tanggal Penyelesaian</label>
<input
type="date"
name="completion_date"
class="px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
/>
</div>
</div>
<div class="bg-white rounded-lg shadow-sm p-6">
<h3 class="text-lg font-medium text-gray-900 mb-6">Deskripsi Proyek</h3>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Deskripsi (Bahasa Indonesia) *</label>
<textarea
name="description_id"
rows="4"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
placeholder="Jelaskan proyek dalam bahasa Indonesia..."
></textarea>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Deskripsi (English) *</label>
<textarea
name="description_en"
rows="4"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
placeholder="Describe the project in English..."
></textarea>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-sm p-6">
<h3 class="text-lg font-medium text-gray-900 mb-6">Pengaturan Tampilan</h3>
<div class="space-y-4">
<div class="flex items-center">
<input
type="checkbox"
name="is_featured"
id="is_featured"
class="h-4 w-4 text-mckinsey-blue focus:ring-mckinsey-blue border-gray-300 rounded"
/>
<label for="is_featured" class="ml-2 block text-sm text-gray-700">
Tampilkan sebagai featured client
</label>
</div>
<div class="flex items-center">
<input
type="checkbox"
name="is_active"
id="is_active"
checked
class="h-4 w-4 text-mckinsey-blue focus:ring-mckinsey-blue border-gray-300 rounded"
/>
<label for="is_active" class="ml-2 block text-sm text-gray-700">
Aktif (tampil di website)
</label>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Urutan Tampil</label>
<input
type="number"
name="sort_order"
min="0"
value="0"
class="px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent w-32"
placeholder="0"
/>
<p class="text-sm text-gray-500 mt-1">Semakin kecil angka, semakin awal tampil</p>
</div>
</div>
</div>
<div class="flex justify-end space-x-4">
<a
href="/admin/clients"
class="mckinsey-btn-outline px-6 py-2 rounded-md text-sm font-medium"
>
Batal
</a>
<button
type="submit"
class="mckinsey-btn px-6 py-2 rounded-md text-sm font-medium"
>
<i class="fas fa-save mr-2"></i>
Simpan Klien
</button>
</div>
</form>
</div>
</div>,
{
title: 'Tambah Klien - IndoAdvisory Admin'
}
)
})
// Handle client creation
clients.post('/', async (c) => {
try {
const body = await c.req.parseBody()
const {
company_name,
industry,
project_type,
project_value,
completion_date,
description_id,
description_en,
is_featured,
is_active,
sort_order
} = body
await c.env.DB.prepare(
`INSERT INTO clients (
company_name, industry, project_type, project_value, completion_date,
description_id, description_en, is_featured, is_active, sort_order
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
).bind(
company_name,
industry,
project_type,
project_value || null,
completion_date || null,
description_id,
description_en,
is_featured ? 1 : 0,
is_active ? 1 : 0,
parseInt(sort_order as string) || 0
).run()
return c.redirect('/admin/clients')
} catch (error) {
console.error('Create client error:', error)
return c.render(<div>Error creating client</div>)
}
})
// Edit client form
clients.get('/:id/edit', async (c) => {
try {
const id = c.req.param('id')
const client = await c.env.DB.prepare(
'SELECT * FROM clients WHERE id = ?'
).bind(id).first() as Client
if (!client) {
return c.notFound()
}
return c.render(
<div class="min-h-screen bg-gray-50">
<div class="bg-white shadow-sm border-b border-gray-200">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center space-x-4 h-16">
<a href="/admin/clients" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-arrow-left mr-2"></i>
Kembali ke Daftar Klien
</a>
<div class="text-gray-300">•</div>
<h1 class="text-xl font-semibold text-gray-900">Edit Klien: {client.company_name}</h1>
</div>
</div>
</div>
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<form method="POST" action={`/admin/clients/${client.id}`} class="space-y-6">
<div class="bg-white rounded-lg shadow-sm p-6">
<h3 class="text-lg font-medium text-gray-900 mb-6">Informasi Dasar</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Nama Perusahaan *</label>
<input
type="text"
name="company_name"
required
value={client.company_name}
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Industri *</label>
<select
name="industry"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
>
<option value="">Pilih industri</option>
{['Technology', 'Finance', 'Healthcare', 'Manufacturing', 'Retail', 'Energy', 'Agriculture', 'Real Estate', 'E-commerce', 'Other'].map(ind => (
<option value={ind} selected={client.industry === ind}>{ind}</option>
))}
</select>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Jenis Proyek *</label>
<select
name="project_type"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
>
<option value="">Pilih jenis proyek</option>
{[
{ value: 'valuation', label: 'Valuasi Perusahaan' },
{ value: 'ipo', label: 'Persiapan IPO' },
{ value: 'ma', label: 'M&A Advisory' },
{ value: 'fundraising', label: 'Fundraising' },
{ value: 'restructuring', label: 'Restructuring' },
{ value: 'dd', label: 'Due Diligence' }
].map(proj => (
<option value={proj.value} selected={client.project_type === proj.value}>
{proj.label}
</option>
))}
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Nilai Proyek</label>
<input
type="text"
name="project_value"
value={client.project_value || ''}
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
placeholder="Contoh: $25M Series B, Rp 2.1T IPO"
/>
</div>
</div>
<div class="mt-6">
<label class="block text-sm font-medium text-gray-700 mb-2">Tanggal Penyelesaian</label>
<input
type="date"
name="completion_date"
value={client.completion_date || ''}
class="px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
/>
</div>
</div>
<div class="bg-white rounded-lg shadow-sm p-6">
<h3 class="text-lg font-medium text-gray-900 mb-6">Deskripsi Proyek</h3>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Deskripsi (Bahasa Indonesia) *</label>
<textarea
name="description_id"
rows="4"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
placeholder="Jelaskan proyek dalam bahasa Indonesia..."
>{client.description_id}</textarea>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Deskripsi (English) *</label>
<textarea
name="description_en"
rows="4"
required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent"
placeholder="Describe the project in English..."
>{client.description_en}</textarea>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-sm p-6">
<h3 class="text-lg font-medium text-gray-900 mb-6">Pengaturan Tampilan</h3>
<div class="space-y-4">
<div class="flex items-center">
<input
type="checkbox"
name="is_featured"
id="is_featured"
checked={client.is_featured}
class="h-4 w-4 text-mckinsey-blue focus:ring-mckinsey-blue border-gray-300 rounded"
/>
<label for="is_featured" class="ml-2 block text-sm text-gray-700">
Tampilkan sebagai featured client
</label>
</div>
<div class="flex items-center">
<input
type="checkbox"
name="is_active"
id="is_active"
checked={client.is_active}
class="h-4 w-4 text-mckinsey-blue focus:ring-mckinsey-blue border-gray-300 rounded"
/>
<label for="is_active" class="ml-2 block text-sm text-gray-700">
Aktif (tampil di website)
</label>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Urutan Tampil</label>
<input
type="number"
name="sort_order"
min="0"
value={client.sort_order}
class="px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-mckinsey-blue focus:border-transparent w-32"
/>
<p class="text-sm text-gray-500 mt-1">Semakin kecil angka, semakin awal tampil</p>
</div>
</div>
</div>
<div class="flex justify-end space-x-4">
<a
href="/admin/clients"
class="mckinsey-btn-outline px-6 py-2 rounded-md text-sm font-medium"
>
Batal
</a>
<button
type="submit"
class="mckinsey-btn px-6 py-2 rounded-md text-sm font-medium"
>
<i class="fas fa-save mr-2"></i>
Simpan Perubahan
</button>
</div>
</form>
</div>
</div>,
{
title: `Edit ${client.company_name} - IndoAdvisory Admin`
}
)
} catch (error) {
console.error('Edit client error:', error)
return c.notFound()
}
})
// Handle client update
clients.post('/:id', async (c) => {
try {
const id = c.req.param('id')
const body = await c.req.parseBody()
const {
company_name,
industry,
project_type,
project_value,
completion_date,
description_id,
description_en,
is_featured,
is_active,
sort_order
} = body
await c.env.DB.prepare(
`UPDATE clients SET
company_name = ?, industry = ?, project_type = ?, project_value = ?,
completion_date = ?, description_id = ?, description_en = ?,
is_featured = ?, is_active = ?, sort_order = ?, updated_at = datetime('now')
WHERE id = ?`
).bind(
company_name,
industry,
project_type,
project_value || null,
completion_date || null,
description_id,
description_en,
is_featured ? 1 : 0,
is_active ? 1 : 0,
parseInt(sort_order as string) || 0,
id
).run()
return c.redirect('/admin/clients')
} catch (error) {
console.error('Update client error:', error)
return c.render(<div>Error updating client</div>)
}
})
// Handle client deletion
clients.delete('/:id', async (c) => {
try {
const id = c.req.param('id')
await c.env.DB.prepare('DELETE FROM clients WHERE id = ?').bind(id).run()
return c.json({ success: true })
} catch (error) {
console.error('Delete client error:', error)
return c.json({ success: false }, 500)
}
})
export default clients