Files
mamad-app/app/role-admin/page.tsx
2025-06-22 00:01:22 +03:00

410 lines
16 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.
"use client"
import { useState, useEffect } from "react"
import { useRouter } from "next/navigation"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Alert, AlertDescription } from "@/components/ui/alert"
import { ArrowRight, Users, UserCog, MessageSquare, KeyRound, Trash2, Home } from "lucide-react"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { RoleManagementModal } from "@/components/role-management-modal"
import { ReportOnBehalfModal } from "@/components/report-on-behalf-modal"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { type User, type UserRole, ROLE_NAMES } from "@/types/user"
interface AdminUser {
national_id: string
name: string
role: UserRole
field?: string
department?: string
team?: string
}
export default function RoleAdminPage() {
const [admin, setAdmin] = useState<AdminUser | null>(null)
const [users, setUsers] = useState<User[]>([])
const [filteredUsers, setFilteredUsers] = useState<User[]>([])
const [searchTerm, setSearchTerm] = useState("")
const [message, setMessage] = useState("")
const [loading, setLoading] = useState(false)
const [roleModalOpen, setRoleModalOpen] = useState(false)
const [reportModalOpen, setReportModalOpen] = useState(false)
const [selectedUser, setSelectedUser] = useState<User | null>(null)
const router = useRouter()
useEffect(() => {
const userData = localStorage.getItem("user")
if (!userData) {
router.push("/login")
return
}
const parsedUser = JSON.parse(userData)
if (parsedUser.role === "user") {
router.push("/dashboard")
return
}
setAdmin(parsedUser)
fetchManageableUsers(parsedUser.national_id)
}, [router])
useEffect(() => {
if (searchTerm) {
setFilteredUsers(users.filter((user) => user.name.includes(searchTerm)))
} else {
setFilteredUsers(users)
}
}, [searchTerm, users])
const fetchManageableUsers = async (adminId: string) => {
setLoading(true)
try {
const response = await fetch("/api/admin/manageable-users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ adminId }),
})
const data = await response.json()
if (response.ok) {
setUsers(data.users)
} else {
setMessage(data.error || "שגיאה בטעינת משתמשים")
}
} catch (err) {
setMessage("שגיאה בחיבור לשרת")
} finally {
setLoading(false)
}
}
const handleRoleChange = async (userId: string, newRole: string) => {
try {
const response = await fetch("/api/admin/change-role", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
adminId: admin?.national_id,
targetUserId: userId,
newRole,
}),
})
const data = await response.json()
if (response.ok) {
setMessage(data.message)
fetchManageableUsers(admin?.national_id || "")
} else {
setMessage(data.error || "שגיאה בשינוי תפקיד")
}
} catch (err) {
setMessage("שגיאה בחיבור לשרת")
}
}
const handleReportOnBehalf = async (userId: string, status: string) => {
try {
const response = await fetch("/api/admin/report-on-behalf", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
adminId: admin?.national_id,
targetUserId: userId,
status,
}),
})
const data = await response.json()
if (response.ok) {
setMessage(data.message)
fetchManageableUsers(admin?.national_id || "")
} else {
setMessage(data.error || "שגיאה בדיווח")
}
} catch (err) {
setMessage("שגיאה בחיבור לשרת")
}
}
const handleDeleteUser = async (nationalId: string) => {
try {
const response = await fetch(`/api/admin/users/${nationalId}`, {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
adminId: admin?.national_id,
}),
})
if (response.ok) {
setMessage("משתמש נמחק בהצלחה")
fetchManageableUsers(admin?.national_id || "")
} else {
const data = await response.json()
setMessage(data.error || "שגיאה במחיקת משתמש")
}
} catch (err) {
setMessage("שגיאה בחיבור לשרת")
}
}
const handleResetPassword = async (nationalId: string, userName: string) => {
try {
const response = await fetch("/api/admin/reset-password", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
adminId: admin?.national_id,
targetUserId: nationalId,
}),
})
if (response.ok) {
setMessage(`סיסמה אופסה בהצלחה עבור ${userName}. הסיסמה החדשה: password123`)
fetchManageableUsers(admin?.national_id || "")
} else {
const data = await response.json()
setMessage(data.error || "שגיאה באיפוס סיסמה")
}
} catch (err) {
setMessage("שגיאה בחיבור לשרת")
}
}
const getStatusText = (status?: string) => {
switch (status) {
case "yes":
return { text: "במקלט/חדר מוגן", color: "text-green-600" }
case "no":
return { text: "לא במקלט", color: "text-orange-600" }
case "no_alarm":
return { text: "אין אזעקה", color: "text-blue-600" }
case "safe_after_exit":
return { text: "אני בטוח (אחרי יציאה מהמקלט)", color: "text-purple-600" }
default:
return { text: "לא דיווח", color: "text-gray-500" }
}
}
const getScopeText = () => {
if (!admin) return ""
switch (admin.role) {
case "global_admin":
return "כל המערכת"
case "field_admin":
return `תחום ${admin.field}`
case "department_admin":
return `מסגרת ${admin.department}`
case "team_admin":
return `צוות ${admin.team}`
default:
return ""
}
}
if (!admin) return null
return (
<div className="min-h-screen bg-gray-50 p-4" dir="rtl">
<div className="max-w-6xl mx-auto space-y-6">
<Card>
<CardHeader>
<div className="flex justify-between items-center">
<div>
<CardTitle className="text-xl">ניהול תפקידים ודיווחים</CardTitle>
<p className="text-sm text-gray-600 mt-1">
{ROLE_NAMES[admin.role]} - {getScopeText()}
</p>
</div>
<div className="flex gap-2">
<Button variant="outline" onClick={() => router.push("/admin")} className="flex items-center gap-2">
<Users className="h-4 w-4" />
</Button>
<Button variant="outline" onClick={() => router.push("/dashboard")} className="flex items-center gap-2">
<Home className="h-4 w-4" />
</Button>
</div>
</div>
</CardHeader>
</Card>
{message && (
<Alert>
<AlertDescription>{message}</AlertDescription>
</Alert>
)}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Users className="h-5 w-5" />
ניהול משתמשים ({filteredUsers.length})
</CardTitle>
<div className="flex gap-4">
<Input
placeholder="חיפוש לפי שם..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="max-w-md"
/>
</div>
</CardHeader>
<CardContent>
{loading ? (
<div className="text-center py-8">טוען משתמשים...</div>
) : (
<div className="overflow-x-auto">
<Table>
<TableHeader>
<TableRow>
<TableHead className="text-right">שם</TableHead>
<TableHead className="text-right">מזהה</TableHead>
<TableHead className="text-right">תפקיד</TableHead>
<TableHead className="text-right">תחום</TableHead>
<TableHead className="text-right">מסגרת</TableHead>
<TableHead className="text-right">צוות</TableHead>
<TableHead className="text-right">פעולות</TableHead>
</TableRow>
</TableHeader>
<TableBody className="text-right" dir="rtl">
{filteredUsers.map((user) => {
const status = getStatusText(user.in_shelter)
return (
<TableRow key={user.national_id}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell className="font-mono text-right" dir="ltr">
{user.national_id}
</TableCell>
<TableCell>
<span className="text-sm bg-indigo-100 text-indigo-800 px-2 py-1 rounded">
{ROLE_NAMES[user.role] || user.role}
</span>
</TableCell>
<TableCell>
<span className="text-sm bg-green-100 text-green-800 px-2 py-1 rounded">
{user.field || "לא הוגדר"}
</span>
</TableCell>
<TableCell>
<span className="text-sm bg-blue-100 text-blue-800 px-2 py-1 rounded">
{user.department || "לא הוגדר"}
</span>
</TableCell>
<TableCell>
<span className="text-sm bg-purple-100 text-purple-800 px-2 py-1 rounded">
{user.team || "לא הוגדר"}
</span>
</TableCell>
<TableCell className="text-right" dir="rtl">
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() => {
setSelectedUser(user)
setRoleModalOpen(true)
}}
className="text-blue-600 hover:text-blue-700"
>
<UserCog className="h-4 w-4" />
</Button>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline" size="sm" className="text-red-600 hover:text-red-700">
<Trash2 className="h-4 w-4" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent dir="rtl">
<AlertDialogHeader className="text-right" dir="rtl">
<AlertDialogTitle className="text-right" dir="rtl">האם אתה בטוח?</AlertDialogTitle>
<AlertDialogDescription className="text-right" dir="rtl">
פעולה זו בלתי הפיכה. האם אתה בטוח שברצונך למחוק את {user.name}?
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter dir="rtl" className="flex-row-reverse sm:justify-start">
<AlertDialogCancel>ביטול</AlertDialogCancel>
<AlertDialogAction
onClick={() => {
if (user.national_id === admin?.national_id) {
setMessage("לא ניתן למחוק את עצמך")
return
}
handleDeleteUser(user.national_id)
}}
>
מחיקה
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline" size="sm" className="text-orange-600 hover:text-orange-700">
<KeyRound className="h-4 w-4" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent dir="rtl">
<AlertDialogHeader>
<AlertDialogTitle>איפוס סיסמה</AlertDialogTitle>
<AlertDialogDescription>
האם אתה בטוח שברצונך לאפס את הסיסמה של {user.name}? הסיסמה החדשה תהיה: password123
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>ביטול</AlertDialogCancel>
<AlertDialogAction onClick={() => handleResetPassword(user.national_id, user.name)}>
איפוס סיסמה
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
{filteredUsers.length === 0 && (
<div className="text-center py-8 text-gray-500">
{searchTerm ? "לא נמצאו משתמשים התואמים לחיפוש" : "אין משתמשים"}
</div>
)}
</div>
)}
</CardContent>
</Card>
<RoleManagementModal
isOpen={roleModalOpen}
onClose={() => setRoleModalOpen(false)}
user={selectedUser}
adminRole={admin.role}
onRoleChange={handleRoleChange}
/>
<ReportOnBehalfModal
isOpen={reportModalOpen}
onClose={() => setReportModalOpen(false)}
user={selectedUser}
onReport={handleReportOnBehalf}
/>
</div>
</div>
)
}