Files
nextjs-boilerplate/app/[locale]/admin/users/[id]/page.tsx
T

199 lines
8.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from 'next/link'
import { notFound } from 'next/navigation'
import { supabaseAdmin, listAuthUsers } from '@/lib/supabaseAdmin'
import { updateUserAction } from '../actions'
import DeleteUserButton from './DeleteUserButton'
interface ProfileRow {
id: string
full_name: string | null
bar_association_no: string | null
bar_name: string | null
phone: string | null
license_status: string | null
license_expires_at: string | null
license_started_at: string | null
trial_start_date: string | null
trial_end_date: string | null
org_id: string | null
created_at: string
}
interface LicenseRow {
id: string
plan: string
status: string
max_devices: number
expires_at: string | null
}
function toDateInputValue(iso: string | null) {
if (!iso) return ''
return new Date(iso).toISOString().slice(0, 10)
}
function formatDate(iso: string | null) {
if (!iso) return '—'
return new Date(iso).toLocaleDateString('tr-TR', { day: 'numeric', month: 'short', year: 'numeric' })
}
export default async function UserEditPage({
params,
searchParams,
}: {
params: Promise<{ id: string }>
searchParams: Promise<{ error?: string; success?: string }>
}) {
const { id } = await params
const { error, success } = await searchParams
const [{ data: profile }, authUsers, { data: licenses }] = await Promise.all([
supabaseAdmin
.from('profiles')
.select('id, full_name, bar_association_no, bar_name, phone, license_status, license_expires_at, license_started_at, trial_start_date, trial_end_date, org_id, created_at')
.eq('id', id)
.maybeSingle(),
listAuthUsers(),
supabaseAdmin.from('licenses').select('id, plan, status, max_devices, expires_at').eq('customer_id', id),
])
if (!profile) notFound()
const p = profile as ProfileRow
const email = authUsers.get(p.id)?.email || ''
const linkedLicenses = (licenses as LicenseRow[] | null) || []
return (
<div className="space-y-6 max-w-2xl">
<div>
<Link href="/admin/users" className="text-sm text-gray-500 hover:text-gray-900 dark:hover:text-white"> Kullanıcılar</Link>
<h2 className="text-2xl font-bold tracking-tight text-gray-900 dark:text-white mt-2">Kullanıcı Düzenle</h2>
<p className="text-gray-500 dark:text-gray-400 mt-1 text-sm">{email || p.id}</p>
</div>
{error && (
<div className="rounded-md bg-red-50 dark:bg-red-950/40 border border-red-200 dark:border-red-900 px-4 py-3 text-sm text-red-700 dark:text-red-400">
{error}
</div>
)}
{success && (
<div className="rounded-md bg-green-50 dark:bg-green-950/40 border border-green-200 dark:border-green-900 px-4 py-3 text-sm text-green-700 dark:text-green-400">
Değişiklikler kaydedildi.
</div>
)}
<form action={updateUserAction} className="rounded-lg bg-white dark:bg-gray-950 border border-gray-200 dark:border-gray-800 shadow-sm p-6 space-y-5">
<input type="hidden" name="id" value={p.id} />
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Ad Soyad</label>
<input
type="text"
name="full_name"
defaultValue={p.full_name || ''}
className="w-full rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 text-sm text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Baro</label>
<input
type="text"
name="bar_name"
defaultValue={p.bar_name || ''}
className="w-full rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 text-sm text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Baro Sicil No</label>
<input
type="text"
name="bar_association_no"
defaultValue={p.bar_association_no || ''}
className="w-full rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 text-sm text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Telefon (WhatsApp)</label>
<input
type="text"
name="phone"
placeholder="+905XXXXXXXXX"
defaultValue={p.phone || ''}
className="w-full rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 text-sm text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
<p className="mt-1 text-xs text-gray-400">Ülke koduyla (+90) girin WhatsApp AI hattının bu numarayı hesapla eşleştirmesi için kullanılır.</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Lisans Durumu</label>
<select
name="license_status"
defaultValue={p.license_status || 'active'}
className="w-full rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 text-sm text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500"
>
<option value="trial">trial</option>
<option value="active">active</option>
<option value="expired">expired</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Lisans Bitiş</label>
<input
type="date"
name="license_expires_at"
defaultValue={toDateInputValue(p.license_expires_at)}
className="w-full rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 text-sm text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Deneme Bitiş Tarihi</label>
<input
type="date"
name="trial_end_date"
defaultValue={toDateInputValue(p.trial_end_date)}
className="w-full rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 text-sm text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
</div>
</div>
<div className="flex items-center gap-3 pt-2">
<button
type="submit"
className="rounded-md bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-medium px-4 py-2 transition-colors"
>
Kaydet
</button>
<Link href="/admin/users" className="text-sm text-gray-500 hover:text-gray-900 dark:hover:text-white">
Vazgeç
</Link>
</div>
</form>
<div className="rounded-lg bg-white dark:bg-gray-950 border border-gray-200 dark:border-gray-800 shadow-sm p-6">
<h3 className="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide mb-3">Bağlı Lisanslar</h3>
{linkedLicenses.length === 0 ? (
<p className="text-sm text-gray-500">Bu kullanıcıya bağlı lisans yok.</p>
) : (
<ul className="divide-y divide-gray-100 dark:divide-gray-800">
{linkedLicenses.map((l) => (
<li key={l.id} className="py-2.5 flex items-center justify-between text-sm">
<span className="text-gray-700 dark:text-gray-300">{l.plan} · {l.status} · {l.max_devices} cihaz · bitiş {formatDate(l.expires_at)}</span>
<Link href={`/admin/licenses/${l.id}`} className="text-indigo-600 dark:text-indigo-400 hover:underline">Düzenle</Link>
</li>
))}
</ul>
)}
</div>
<div className="rounded-lg bg-white dark:bg-gray-950 border border-red-200 dark:border-red-900 shadow-sm p-6">
<h3 className="text-sm font-semibold text-red-700 dark:text-red-400 uppercase tracking-wide mb-3">Tehlikeli Bölge</h3>
<DeleteUserButton id={p.id} email={email || p.id} />
</div>
</div>
)
}