'use client' import { useActionState, useCallback, useEffect, useState, useTransition } from 'react' import Swal, { type SweetAlertIcon } from 'sweetalert2' import { createUser, updateUser, deleteUser, changeUserStatus, type User, type UserFormState, type UsersResponse, } from './actions' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Checkbox } from '@/components/ui/checkbox' import { Badge } from '@/components/ui/badge' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription, } from '@/components/ui/dialog' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Loader2, Plus, Pencil, Trash2, AlertTriangle } from 'lucide-react' type Props = { initialData: UsersResponse } type ModalMode = 'create' | 'edit' | null function getSwalThemeOptions() { const isDark = document.documentElement.classList.contains('dark') return isDark ? { background: '#111827', color: '#f3f4f6' } : { background: '#ffffff', color: '#111827' } } function showToast(icon: SweetAlertIcon, title: string) { void Swal.fire({ ...getSwalThemeOptions(), toast: true, position: 'top-end', showConfirmButton: false, timer: 2200, timerProgressBar: true, icon, title, }) } /* ─── Field ─────────────────────────────────────────── */ function Field({ id, label, children }: { id?: string; label: string; children: React.ReactNode }) { return (
{children}
) } /* ─── UserModal ─────────────────────────────────────── */ function UserModal({ open, mode, user, onSaved, onClose, }: { open: boolean mode: 'create' | 'edit' user?: User onSaved: (savedUser: User, mode: 'create' | 'edit') => void onClose: () => void }) { const isEdit = mode === 'edit' && user != null const boundAction = isEdit ? updateUser.bind(null, user.id) : createUser const [state, formAction, pending] = useActionState(boundAction, {}) useEffect(() => { if (!state.success) return if (state.user) onSaved(state.user, mode) showToast('success', mode === 'edit' ? 'Kullanıcı güncellendi' : 'Kullanıcı oluşturuldu') onClose() }, [state.success, state.user, mode, onSaved, onClose]) useEffect(() => { if (!state.error) return showToast('error', state.error) }, [state.error]) return ( !o && onClose()}> {isEdit ? 'Kullanıcıyı Düzenle' : 'Yeni Kullanıcı Ekle'} {isEdit ? 'Kullanıcı bilgilerini güncelleyin.' : 'Yeni bir kullanıcı hesabı oluşturun.'}
{state.error && (
{state.error}
)}
) } /* ─── UsersClient ────────────────────────────────────── */ export default function UsersClient({ initialData }: Props) { const [data, setData] = useState(initialData) const [modal, setModal] = useState(null) const [selectedUser, setSelectedUser] = useState() const [, startTransition] = useTransition() const openCreate = useCallback(() => { setSelectedUser(undefined) setModal('create') }, []) const openEdit = useCallback((user: User) => { setSelectedUser(user) setModal('edit') }, []) const closeModal = useCallback(() => { setModal(null) setSelectedUser(undefined) }, []) const handleUserSaved = useCallback((savedUser: User, mode: 'create' | 'edit') => { setData((prev) => { const existingIndex = prev.items.findIndex((u) => u.id === savedUser.id) if (mode === 'edit' || existingIndex >= 0) { return { ...prev, items: prev.items.map((u) => (u.id === savedUser.id ? savedUser : u)), } } return { ...prev, items: [savedUser, ...prev.items], meta: { ...prev.meta, total: prev.meta.total + 1 }, } }) }, []) const handleUserDeleted = useCallback((id: number) => { setData((prev) => { const nextItems = prev.items.filter((u) => u.id !== id) const removed = nextItems.length !== prev.items.length return { ...prev, items: nextItems, meta: { ...prev.meta, total: removed ? Math.max(0, prev.meta.total - 1) : prev.meta.total, }, } }) }, []) const handleDelete = useCallback(async (user: User) => { const result = await Swal.fire({ ...getSwalThemeOptions(), icon: 'warning', title: 'Kullanıcı silinsin mi?', text: `${user.username} kalıcı olarak silinecek.`, showCancelButton: true, confirmButtonText: 'Evet, sil', cancelButtonText: 'İptal', confirmButtonColor: '#dc2626', reverseButtons: true, focusCancel: true, }) if (!result.isConfirmed) return const formData = new FormData() formData.set('id', String(user.id)) const deleted = await deleteUser({}, formData) if (!deleted.success) { showToast('error', deleted.error ?? 'Kullanıcı silinemedi') return } handleUserDeleted(deleted.deletedId ?? user.id) showToast('success', 'Kullanıcı silindi') }, [handleUserDeleted]) async function handleStatusToggle(user: User) { const nextStatus = !user.is_active const statusLabel = nextStatus ? 'aktif' : 'pasif' const decision = await Swal.fire({ ...getSwalThemeOptions(), icon: 'question', title: 'Durum değiştirilsin mi?', text: `${user.username} kullanıcısı ${statusLabel} yapılacak.`, showCancelButton: true, confirmButtonText: 'Evet', cancelButtonText: 'İptal', reverseButtons: true, focusCancel: true, }) if (!decision.isConfirmed) return startTransition(async () => { const result = await changeUserStatus(user.id, nextStatus) if (!result.success || !result.user) { showToast('error', result.error ?? 'Durum güncellenemedi') return } setData((prev) => ({ ...prev, items: prev.items.map((u) => (u.id === result.user!.id ? result.user! : u)), })) showToast('success', `Kullanıcı ${statusLabel} yapıldı`) }) } return (
{/* Header */}

Kullanıcılar

Toplam {data.meta.total} kullanıcı

{/* Table */}
ID Kullanıcı Adı E-posta Durum Rol Kayıt Tarihi İşlemler {data.items.map((user) => ( {user.id} {user.username} {user.email} {user.is_admin ? Admin : Kullanıcı } {new Date(user.created_at).toLocaleDateString('tr-TR')}
))} {data.items.length === 0 && ( Henüz kullanıcı yok )}
{/* Pagination */} {data.meta.total > data.meta.limit && (

Sayfa {data.meta.page} / {Math.ceil(data.meta.total / data.meta.limit)}

)} {/* Modals */}
) }