Files
Beyhan Oğur 6d95e27114 first commit
2026-04-26 22:16:43 +03:00

224 lines
9.1 KiB
TypeScript
Raw Permalink 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.
"use client";
import { useEffect, useState } from "react";
import { useAppDispatch, useAppSelector } from "@/lib/hooks";
import { fetchUsers, fetchDeletedUsers, createUser, updateUser, deleteUser, restoreUser, CreateUserRequest, UpdateUserRequest } from "@/lib/features/users/usersSlice";
import { UserTable } from "@/components/users/user-table";
import { DeletedUserTable } from "@/components/users/deleted-user-table";
import { UserDialog } from "@/components/users/user-dialog";
import { Button } from "@/components/ui/button";
import { Plus, Trash, Trash2 } from "lucide-react";
import Swal from "sweetalert2";
import { User } from "@/lib/features/auth/authSlice";
export default function UsersPage() {
const dispatch = useAppDispatch();
const { users, deletedUsers, isLoading, error } = useAppSelector((state) => state.users);
const [dialogOpen, setDialogOpen] = useState(false);
const [selectedUser, setSelectedUser] = useState<User | null>(null);
useEffect(() => {
dispatch(fetchUsers());
dispatch(fetchDeletedUsers());
}, [dispatch]);
const handleCreate = () => {
setSelectedUser(null);
setDialogOpen(true);
};
const handleEdit = (user: User) => {
setSelectedUser(user);
setDialogOpen(true);
};
const handleDelete = (id: string) => {
Swal.fire({
title: "Soft Delete",
text: "Bu kullanıcı 'silindi' olarak işaretlenecek (Soft Delete). Geri alınabilir.",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#f97316", // Orange
cancelButtonColor: "#3085d6",
confirmButtonText: "Evet, Sil",
cancelButtonText: "İptal",
}).then((result) => {
if (result.isConfirmed) {
dispatch(deleteUser(id))
.unwrap()
.then(() => {
Swal.fire("Silindi!", "Kullanıcı başarıyla silindi (Soft).", "success");
// Refresh both lists because a user moved from active to deleted
dispatch(fetchUsers());
dispatch(fetchDeletedUsers());
})
.catch((err) => {
Swal.fire("Hata!", err || "Silme işlemi başarısız.", "error");
});
}
});
};
const handleHardDelete = (id: string) => {
Swal.fire({
title: "KALICI OLARAK SİL?",
text: "Bu işlem GERİ ALINAMAZ! Kullanıcı ve tüm verileri veritabanından tamamen silinecek (Hard Delete).",
icon: "error", // Red
showCancelButton: true,
confirmButtonColor: "#d33", // Red
cancelButtonColor: "#3085d6",
confirmButtonText: "Evet, KALICI SİL!",
cancelButtonText: "İptal",
}).then((result) => {
if (result.isConfirmed) {
const isSoftDeleted = deletedUsers.some(u => u.id === id);
if (isSoftDeleted) {
// WORKAROUND: Backend soft-deleted kullanıcıları bulamıyor olabilir.
// Önce restore et, sonra hard delete yap.
dispatch(restoreUser(id))
.unwrap()
.then(() => {
return dispatch(deleteUser({ id, hard: true })).unwrap();
})
.then(() => {
Swal.fire("Silindi!", "Kullanıcı kalıcı olarak silindi.", "success");
})
.catch((err) => {
Swal.fire("Hata!", err || "Silme işlemi başarısız.", "error");
});
} else {
// Normal Hard Delete (Aktif kullanıcı)
dispatch(deleteUser({ id, hard: true }))
.unwrap()
.then(() => {
Swal.fire("Silindi!", "Kullanıcı kalıcı olarak silindi.", "success");
})
.catch((err) => {
Swal.fire("Hata!", err || "Silme işlemi başarısız.", "error");
});
}
}
});
};
const handleRestore = (id: string) => {
Swal.fire({
title: "Geri Yükle?",
text: "Kullanıcı tekrar aktif edilecek.",
icon: "question",
showCancelButton: true,
confirmButtonColor: "#16a34a", // Green
cancelButtonColor: "#d33",
confirmButtonText: "Evet, Geri Yükle",
cancelButtonText: "İptal",
}).then((result) => {
if (result.isConfirmed) {
dispatch(restoreUser(id))
.unwrap()
.then(() => {
Swal.fire("Başarılı!", "Kullanıcı geri yüklendi.", "success");
dispatch(fetchUsers());
dispatch(fetchDeletedUsers());
})
.catch((err) => {
Swal.fire("Hata!", err || "Geri yükleme başarısız.", "error");
});
}
});
};
const handleSubmit = (data: CreateUserRequest | UpdateUserRequest) => {
if ("id" in data) {
dispatch(updateUser(data as UpdateUserRequest))
.unwrap()
.then(() => {
setDialogOpen(false);
Swal.fire("Başarılı", "Kullanıcı güncellendi.", "success");
dispatch(fetchUsers());
})
.catch((err) => {
Swal.fire("Hata", err || "Güncelleme başarısız.", "error");
});
} else {
dispatch(createUser(data as CreateUserRequest))
.unwrap()
.then(() => {
setDialogOpen(false);
Swal.fire("Başarılı", "Kullanıcı oluşturuldu.", "success");
dispatch(fetchUsers());
})
.catch((err) => {
Swal.fire("Hata", err || "Oluşturma başarısız.", "error");
});
}
};
return (
<div className="space-y-12">
{/* Active Users Section */}
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h2 className="text-md font-bold tracking-tight">Kullanıcılar</h2>
<p className="text-muted-foreground">
Sistemdeki aktif kullanıcıları yönetin.
</p>
</div>
<Button onClick={() => { setSelectedUser(null); setDialogOpen(true); }}>
<Plus className="mr-2 h-4 w-4" /> Yeni Kullanıcı
</Button>
</div>
{error && (
<div className="rounded-md bg-destructive/15 p-4 text-destructive">
{error}
</div>
)}
<UserTable
users={users}
onEdit={(u) => { setSelectedUser(u); setDialogOpen(true); }}
onDelete={handleDelete}
onHardDelete={handleHardDelete}
/>
</div>
{/* Deleted Users Section */}
<div className="space-y-6">
<div>
<h2 className="text-2xl font-bold tracking-tight text-destructive flex items-center gap-2">
<Trash2 className="h-6 w-6" /> Çöp Kutusu
</h2>
<p className="text-muted-foreground">
Silinmiş kullanıcıları geri yükleyin veya kalıcı olarak silin.
</p>
</div>
<DeletedUserTable
users={deletedUsers}
onRestore={handleRestore}
onHardDelete={handleHardDelete}
/>
</div>
<UserDialog
open={dialogOpen}
onOpenChange={setDialogOpen}
user={selectedUser}
onSubmit={(data) => {
// Re-implementing handleSubmit logic inline or keep it separate as before if preferred,
// but for brevity I'll assume the previous handleSubmit function is available in scope
// or I should include it in replacement if I am replacing the whole return block.
// Ideally, I should keep the existing handleSubmit and just pass it.
// Since I am replacing the whole return, I must ensure handleSubmit is defined above or passed correctly.
// Wait, I am replacing a range that includes imports and component setup? No, just the function body?
// Let's modify the instruction to be safer.
handleSubmit(data);
}}
isLoading={isLoading}
/>
</div>
);
}