feat(tutorials): add video training academy management with YouTube parser and live preview

This commit is contained in:
mstfyldz
2026-08-22 13:49:00 +03:00
parent 2d149f1178
commit 7f690cc6d3
29 changed files with 2476 additions and 1034 deletions
+104
View File
@@ -0,0 +1,104 @@
import Link from 'next/link'
import { supabaseAdmin, listAuthUsers } from '@/lib/supabaseAdmin'
interface ProfileRow {
id: string
full_name: string | null
bar_association_no: string | null
bar_name: string | null
license_status: string | null
license_expires_at: string | null
trial_end_date: string | null
org_id: string | null
created_at: string
}
function statusBadge(status: string | null) {
const map: Record<string, string> = {
active: 'bg-green-50 text-green-700 border-green-200 dark:bg-green-950/40 dark:text-green-400 dark:border-green-900',
trial: 'bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-950/40 dark:text-amber-400 dark:border-amber-900',
expired: 'bg-red-50 text-red-700 border-red-200 dark:bg-red-950/40 dark:text-red-400 dark:border-red-900',
}
const cls = map[status || ''] || 'bg-gray-50 text-gray-600 border-gray-200 dark:bg-gray-900 dark:text-gray-400 dark:border-gray-800'
return (
<span className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium border ${cls}`}>
{status || 'bilinmiyor'}
</span>
)
}
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 UsersPage({ searchParams }: { searchParams: Promise<{ deleted?: string }> }) {
const { deleted } = await searchParams
const [{ data: profiles }, authUsers] = await Promise.all([
supabaseAdmin
.from('profiles')
.select('id, full_name, bar_association_no, bar_name, license_status, license_expires_at, trial_end_date, org_id, created_at')
.order('created_at', { ascending: false }),
listAuthUsers(),
])
const rows = (profiles as ProfileRow[] | null) || []
return (
<div className="space-y-6">
{deleted && (
<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">
Kullanıcı silindi.
</div>
)}
<div>
<h2 className="text-2xl font-bold tracking-tight text-gray-900 dark:text-white">Kullanıcılar</h2>
<p className="text-gray-500 dark:text-gray-400 mt-2">Toplam {rows.length} kullanıcı.</p>
</div>
<div className="rounded-lg bg-white dark:bg-gray-950 border border-gray-200 dark:border-gray-800 shadow-sm overflow-hidden">
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-800">
<thead className="bg-gray-50 dark:bg-gray-900">
<tr>
{['Kullanıcı', 'Baro', 'Durum', 'Bitiş Tarihi', 'Kayıt', ''].map((h) => (
<th key={h} className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">{h}</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-gray-100 dark:divide-gray-800">
{rows.map((p) => {
const authUser = authUsers.get(p.id)
const expiryDate = p.license_status === 'trial' ? p.trial_end_date : p.license_expires_at
return (
<tr key={p.id} className="hover:bg-gray-50 dark:hover:bg-gray-900/50">
<td className="px-4 py-3 whitespace-nowrap">
<div className="text-sm font-medium text-gray-900 dark:text-white">{p.full_name || '(isimsiz)'}</div>
<div className="text-xs text-gray-500">{authUser?.email || p.id}</div>
</td>
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">
{p.bar_name || '—'}{p.bar_association_no ? ` · ${p.bar_association_no}` : ''}
</td>
<td className="px-4 py-3 whitespace-nowrap">{statusBadge(p.license_status)}</td>
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">{formatDate(expiryDate)}</td>
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-500">{formatDate(p.created_at)}</td>
<td className="px-4 py-3 whitespace-nowrap text-right text-sm">
<Link href={`/admin/users/${p.id}`} className="text-indigo-600 dark:text-indigo-400 hover:underline">
Düzenle
</Link>
</td>
</tr>
)
})}
{rows.length === 0 && (
<tr>
<td colSpan={6} className="px-4 py-8 text-center text-sm text-gray-500">Henüz kullanıcı yok.</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
)
}