"use client" //export const dynamic = 'force-dynamic' //export const revalidate = 0 import type React from "react" import { useState, useEffect } from "react" import { useRouter } from "next/navigation" // app/admin/page.tsx // ... (other imports) // Updated import to get all necessary types and constants from types/user.ts import { type User, type UserRole, ROLE_NAMES } from "@/types/user" // ... (rest of your component code) import { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, } from "@/components/ui/alert-dialog" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Checkbox } from "@/components/ui/checkbox" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Alert, AlertDescription } from "@/components/ui/alert" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { ArrowRight, RotateCcw, Users, UserPlus, Clock, Trash2, Eye, KeyRound, RefreshCw, WifiOff, Zap, BarChart3, PieChart, UsersIcon, Globe, Building2, UserCog, MessageSquare, Lock, LockOpen, Pencil, ArrowLeft, Home, } from "lucide-react" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { UserCategoryModal } from "@/components/user-category-modal" import { TeamUserCategoryModal } from "@/components/team-user-category-modal" import { StatsPieChart } from "@/components/stats-pie-chart" import { SimplePieChart } from "@/components/simple-pie-chart" import { useRealTimeUpdates } from "@/hooks/useRealTimeUpdates" import { useTeamRealTimeUpdates } from "@/hooks/useTeamRealTimeUpdates" import { DepartmentUserCategoryModal } from "@/components/department-user-category-modal" import { useDepartmentRealTimeUpdates } from "@/hooks/useDepartmentRealTimeUpdates" import { FieldUserCategoryModal } from "@/components/field-user-category-modal" import { useFieldRealTimeUpdates } from "@/hooks/useFieldRealTimeUpdates" import { ReportOnBehalfModal } from "@/components/report-on-behalf-modal" import { UserScopeModal } from "@/components/user-scope-modal" interface Stats { no_report: number in_shelter: number not_in_shelter: number no_alarm: number safe_after_exit: number } interface UserData { national_id: string name: string in_shelter?: string last_updated?: string is_admin: boolean must_change_password?: boolean field?: string department?: string team?: string lock_status?: boolean } interface ManagedTypeOption { id?: number name: string managed: boolean parentId?: number | null } export default function AdminPage() { const [user, setUser] = useState(null) const [activeTab, setActiveTab] = useState("team") // Global stats and data const [globalStats, setGlobalStats] = useState(null) const [globalUsers, setGlobalUsers] = useState([]) const [globalLastReset, setGlobalLastReset] = useState(null) const [globalResetCooldown, setGlobalResetCooldown] = useState(0) // Team stats and data const [teamStats, setTeamStats] = useState(null) const [teamUsers, setTeamUsers] = useState([]) const [teamName, setTeamName] = useState("") const [teamResetCooldown, setTeamResetCooldown] = useState(0) // Department stats and data const [departmentStats, setDepartmentStats] = useState(null) const [departmentUsers, setDepartmentUsers] = useState([]) const [departmentName, setDepartmentName] = useState("") const [departmentResetCooldown, setDepartmentResetCooldown] = useState(0) const [departmentChangedRows, setDepartmentChangedRows] = useState>(new Set()) const [departmentModalOpen, setDepartmentModalOpen] = useState(false) // Field stats and data const [fieldStats, setFieldStats] = useState(null) const [fieldUsers, setFieldUsers] = useState([]) const [fieldName, setFieldName] = useState("") const [fieldResetCooldown, setFieldResetCooldown] = useState(0) const [fieldChangedRows, setFieldChangedRows] = useState>(new Set()) const [fieldModalOpen, setFieldModalOpen] = useState(false) const [newUser, setNewUser] = useState({ name: "", isAdmin: false, field: "", department: "", team: "", role: "", }) const [managedTypes, setManagedTypes] = useState<{ fields: ManagedTypeOption[] departments: ManagedTypeOption[] teams: ManagedTypeOption[] }>({ fields: [], departments: [], teams: [], }) const [managedTypesLoading, setManagedTypesLoading] = useState(false) const [managedTypeTab, setManagedTypeTab] = useState<"field" | "department" | "team">("field") const [newFieldName, setNewFieldName] = useState("") const [newDepartmentName, setNewDepartmentName] = useState("") const [newTeamName, setNewTeamName] = useState("") const [newDepartmentParentId, setNewDepartmentParentId] = useState("") const [newTeamParentId, setNewTeamParentId] = useState("") const [scopeModalOpen, setScopeModalOpen] = useState(false) const [scopeUser, setScopeUser] = useState(null) const [scopeSaving, setScopeSaving] = useState(false) const [message, setMessage] = useState("") const [loadingUsers, setLoadingUsers] = useState(false) const [modalOpen, setModalOpen] = useState(false) const [teamModalOpen, setTeamModalOpen] = useState(false) const [selectedCategory, setSelectedCategory] = useState("") const [selectedCategoryName, setSelectedCategoryName] = useState("") const [isRefreshing, setIsRefreshing] = useState(false) const [changedRows, setChangedRows] = useState>(new Set()) const [teamChangedRows, setTeamChangedRows] = useState>(new Set()) const [viewMode, setViewMode] = useState<"list" | "pie">("list") const [debugMode, setDebugMode] = useState(false) const [useSimpleChart, setUseSimpleChart] = useState(false) const router = useRouter() const [reportModalOpen, setReportModalOpen] = useState(false) const [selectedUserForReport, setSelectedUserForReport] = useState(null) const [instanceName, setInstanceName] = useState("") // Global real-time updates const { isConnected: globalConnected, refetch: refetchGlobal } = useRealTimeUpdates((data) => { if (data.stats) { setGlobalStats(data.stats) } if (data.users) { const newChangedRows = new Set() data.users.forEach((newUser: UserData) => { const existingUser = globalUsers.find((u) => u.national_id === newUser.national_id) if ( existingUser && (existingUser.in_shelter !== newUser.in_shelter || existingUser.last_updated !== newUser.last_updated) ) { newChangedRows.add(newUser.national_id) } }) setGlobalUsers(data.users) setChangedRows(newChangedRows) if (newChangedRows.size > 0) { setTimeout(() => setChangedRows(new Set()), 3000) } } if (data.lastReset?.lastReset) { setGlobalLastReset(data.lastReset.lastReset) if (data.lastReset.timestamp) { const resetTime = new Date(data.lastReset.timestamp).getTime() const now = new Date().getTime() const cooldownMs = 2 * 60 // 2 minutes const remaining = Math.max(0, cooldownMs - (now - resetTime)) setGlobalResetCooldown(Math.ceil(remaining / 1000)) } } }) // Team real-time updates const { isConnected: teamConnected, refetch: refetchTeam } = useTeamRealTimeUpdates( user?.national_id || "", (data) => { if (data.stats) { setTeamStats(data.stats) } if (data.users) { const newChangedRows = new Set() data.users.forEach((newUser: UserData) => { const existingUser = teamUsers.find((u) => u.national_id === newUser.national_id) if ( existingUser && (existingUser.in_shelter !== newUser.in_shelter || existingUser.last_updated !== newUser.last_updated) ) { newChangedRows.add(newUser.national_id) } }) setTeamUsers(data.users) setTeamChangedRows(newChangedRows) if (newChangedRows.size > 0) { setTimeout(() => setTeamChangedRows(new Set()), 3000) } } if (data.team) { setTeamName(data.team) } }, ) // Department real-time updates const { isConnected: departmentConnected, refetch: refetchDepartment } = useDepartmentRealTimeUpdates( user?.national_id || "", (data) => { if (data.stats) { setDepartmentStats(data.stats) } if (data.users) { const newChangedRows = new Set() data.users.forEach((newUser: UserData) => { const existingUser = departmentUsers.find((u) => u.national_id === newUser.national_id) if ( existingUser && (existingUser.in_shelter !== newUser.in_shelter || existingUser.last_updated !== newUser.last_updated) ) { newChangedRows.add(newUser.national_id) } }) setDepartmentUsers(data.users) setDepartmentChangedRows(newChangedRows) if (newChangedRows.size > 0) { setTimeout(() => setDepartmentChangedRows(new Set()), 3000) } } if (data.department) { setDepartmentName(data.department) } }, ) // Field real-time updates const { isConnected: fieldConnected, refetch: refetchField } = useFieldRealTimeUpdates( user?.national_id || "", (data) => { if (data.stats) { setFieldStats(data.stats) } if (data.users) { const newChangedRows = new Set() data.users.forEach((newUser: UserData) => { const existingUser = fieldUsers.find((u) => u.national_id === newUser.national_id) if ( existingUser && (existingUser.in_shelter !== newUser.in_shelter || existingUser.last_updated !== newUser.last_updated) ) { newChangedRows.add(newUser.national_id) } }) setFieldUsers(data.users) setFieldChangedRows(newChangedRows) if (newChangedRows.size > 0) { setTimeout(() => setFieldChangedRows(new Set()), 3000) } } if (data.field) { setFieldName(data.field) } }, ) useEffect(() => { const userData = localStorage.getItem("user") if (!userData) { router.push("/login") return } const parsedUser = JSON.parse(userData) if (!["global_admin", "field_admin", "department_admin", "team_admin"].includes(parsedUser.role)) { router.push("/dashboard") return } setUser(parsedUser) }, [router]) useEffect(() => { const loadInstance = async () => { try { const response = await fetch("/api/instance") const data = await response.json() if (response.ok && data.instance) { setInstanceName(data.instance) } } catch (error) { console.error("Failed to load instance name:", error) } } loadInstance() }, []) const fetchManagedTypes = async () => { setManagedTypesLoading(true) try { const response = await fetch(`/api/admin/managed-types?adminId=${encodeURIComponent(user?.national_id || "")}`) const data = await response.json() if (response.ok) { setManagedTypes({ fields: data.fields || [], departments: data.departments || [], teams: data.teams || [], }) } else { setMessage(data.error || "Failed to load managed types.") } } catch (error) { console.error("Managed types fetch error:", error) setMessage("Failed to load managed types.") } finally { setManagedTypesLoading(false) } } useEffect(() => { if (user?.national_id) { fetchManagedTypes() } }, [user?.national_id]) useEffect(() => { if (!newDepartmentParentId && managedTypes.fields.length === 1 && managedTypes.fields[0].id) { setNewDepartmentParentId(String(managedTypes.fields[0].id)) } if (!newTeamParentId && managedTypes.departments.length === 1 && managedTypes.departments[0].id) { setNewTeamParentId(String(managedTypes.departments[0].id)) } }, [managedTypes, newDepartmentParentId, newTeamParentId]) useEffect(() => { if (globalResetCooldown > 0) { const timer = setTimeout(() => setGlobalResetCooldown(globalResetCooldown - 1), 1000) return () => clearTimeout(timer) } }, [globalResetCooldown]) useEffect(() => { if (teamResetCooldown > 0) { const timer = setTimeout(() => setTeamResetCooldown(teamResetCooldown - 1), 1000) return () => clearTimeout(timer) } }, [teamResetCooldown]) useEffect(() => { if (departmentResetCooldown > 0) { const timer = setTimeout(() => setDepartmentResetCooldown(departmentResetCooldown - 1), 1000) return () => clearTimeout(timer) } }, [departmentResetCooldown]) useEffect(() => { if (fieldResetCooldown > 0) { const timer = setTimeout(() => setFieldResetCooldown(fieldResetCooldown - 1), 1000) return () => clearTimeout(timer) } }, [fieldResetCooldown]) const handleGlobalResetAll = async () => { if (globalResetCooldown > 0) return try { const response = await fetch("/api/admin/reset-all", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ adminId: user?.national_id }), }) const data = await response.json() if (response.ok) { setMessage(data.message || "כל הסטטוסים אופסו בהצלחה") setGlobalResetCooldown(30) // 2 minutes setGlobalLastReset(`${user?.name} - ${new Date().toLocaleString("he-IL")}`) refetchGlobal() refetchTeam() // Also refresh team data refetchDepartment() refetchField() } else { // Handle cooldown error specifically if (response.status === 429 && data.remainingSeconds) { setGlobalResetCooldown(data.remainingSeconds) setMessage(`יש להמתין ${data.remainingSeconds} שניות לפני איפוס נוסף`) } else { setMessage(data.error || "שגיאה באיפוס הסטטוסים") } } } catch (err) { setMessage("שגיאה באיפוס הסטטוסים") } } const handleTeamReset = async () => { if (teamResetCooldown > 0) return try { const response = await fetch("/api/admin/team-reset", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ adminId: user?.national_id }), }) const data = await response.json() if (response.ok) { setMessage(data.message || `כל הסטטוסים של צוות ${data.team} אופסו בהצלחה`) setTeamResetCooldown(60) // 1 minute refetchTeam() refetchGlobal() // Also refresh global data refetchDepartment() refetchField() } else { if (response.status === 429 && data.remainingSeconds) { setTeamResetCooldown(data.remainingSeconds) setMessage(`יש להמתין ${data.remainingSeconds} שניות לפני איפוס צוות נוסף`) } else { setMessage(data.error || "שגיאה באיפוס הצוות") } } } catch (err) { setMessage("שגיאה באיפוס הצוות") } } const handleDepartmentReset = async () => { if (departmentResetCooldown > 0) return try { const response = await fetch("/api/admin/department-reset", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ adminId: user?.national_id }), }) const data = await response.json() if (response.ok) { setMessage(data.message || `כל הסטטוסים של מסגרת ${data.department} אופסו בהצלחה`) setDepartmentResetCooldown(90) // 1.5 minutes refetchDepartment() refetchGlobal() // Also refresh global data refetchTeam() refetchField() } else { if (response.status === 429 && data.remainingSeconds) { setDepartmentResetCooldown(data.remainingSeconds) setMessage(`יש להמתין ${data.remainingSeconds} שניות לפני איפוס מסגרת נוסף`) } else { setMessage(data.error || "שגיאה באיפוס המסגרת") } } } catch (err) { setMessage("שגיאה באיפוס המסגרת") } } const handleFieldReset = async () => { if (fieldResetCooldown > 0) return try { const response = await fetch("/api/admin/field-reset", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ adminId: user?.national_id }), }) const data = await response.json() if (response.ok) { setMessage(data.message || `כל הסטטוסים של תחום ${data.field} אופסו בהצלחה`) setFieldResetCooldown(120) // 2 minutes refetchField() refetchGlobal() // Also refresh global data refetchTeam() refetchDepartment() } else { if (response.status === 429 && data.remainingSeconds) { setFieldResetCooldown(data.remainingSeconds) setMessage(`יש להמתין ${data.remainingSeconds} שניות לפני איפוס תחום נוסף`) } else { setMessage(data.error || "שגיאה באיפוס התחום") } } } catch (err) { setMessage("שגיאה באיפוס התחום") } } const handleAddManagedType = async (type: "field" | "department" | "team") => { const name = type === "field" ? newFieldName : type === "department" ? newDepartmentName : newTeamName if (!name.trim()) { return } const parentId = type === "department" ? newDepartmentParentId : type === "team" ? newTeamParentId : "" if ((type === "department" || type === "team") && !parentId) { setMessage("Select a parent before adding.") return } try { const response = await fetch("/api/admin/managed-types", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ adminId: user?.national_id, type, name: name.trim(), parentId: parentId ? Number(parentId) : undefined, }), }) const data = await response.json() if (response.ok) { if (type === "field") setNewFieldName("") if (type === "department") setNewDepartmentName("") if (type === "team") setNewTeamName("") if (type === "department") setNewDepartmentParentId("") if (type === "team") setNewTeamParentId("") await fetchManagedTypes() } else { setMessage(data.error || "שגיאה בהוספת Type מנוהל") } } catch (error) { console.error("Managed type add error:", error) setMessage("שגיאה בהוספת Type מנוהל") } } const handleDeleteManagedType = async (id?: number) => { if (!id || !user?.national_id) return if (!confirm("למחוק ערך זה? יש לשייך לפני המחיקה את כלל המשתמשים תחת ערך זה מחדש.")) { return } try { const response = await fetch(`/api/admin/managed-types/${id}`, { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ adminId: user.national_id }), }) const data = await response.json() if (response.ok) { await fetchManagedTypes() } else { setMessage(data.error || "Failed to delete managed type.") } } catch (error) { console.error("Managed type delete error:", error) setMessage("Failed to delete managed type.") } } const handleRenameManagedType = async (id?: number, currentName?: string) => { if (!id || !user?.national_id || !currentName) return const nextName = prompt("Rename value:", currentName) if (!nextName || nextName.trim() === currentName) return try { const response = await fetch(`/api/admin/managed-types/${id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ adminId: user.national_id, name: nextName.trim() }), }) const data = await response.json() if (response.ok) { await fetchManagedTypes() await Promise.all([refetchGlobal(), refetchTeam(), refetchDepartment(), refetchField()]) } else { setMessage(data.error || "Failed to rename managed type.") } } catch (error) { console.error("Managed type rename error:", error) setMessage("Failed to rename managed type.") } } const handleAddUser = async (e: React.FormEvent) => { e.preventDefault() if (!newUser.field || !newUser.department || !newUser.team || !newUser.role) { setMessage("יש לבחור תפקיד, תחום, מסגרת וצוות") return } try { const response = await fetch("/api/admin/add-user", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...newUser, adminId: user?.national_id, }), }) const data = await response.json() if (response.ok) { setMessage(`${data.message}. הסיסמה הזמנית: password123`) setNewUser({ name: "", isAdmin: false, field: "", department: "", team: "", role: "" }) refetchGlobal() refetchTeam() refetchDepartment() refetchField() } else { setMessage(data.error || "שגיאה בהוספת משתמש") } } catch (err) { setMessage("שגיאה בחיבור לשרת") } } const handleDeleteUser = async (nationalId: string) => { try { const response = await fetch(`/api/admin/users/${nationalId}`, { method: "DELETE", }) if (response.ok) { setMessage("משתמש נמחק בהצלחה") refetchGlobal() refetchTeam() refetchDepartment() refetchField() } 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: user?.national_id, targetUserId: nationalId, }), }) if (response.ok) { setMessage(`סיסמה אופסה בהצלחה עבור ${userName}. הסיסמה החדשה: password123`) refetchGlobal() refetchTeam() refetchDepartment() refetchField() } else { const data = await response.json() setMessage(data.error || "שגיאה באיפוס סיסמה") } } catch (err) { setMessage("שגיאה בחיבור לשרת") } } const handleToggleUserLock = async (nationalId: string, currentLockStatus: boolean, userName: string) => { try { const response = await fetch("/api/admin/toggle-user-lock", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ adminId: user?.national_id, targetUserId: nationalId, lockStatus: !currentLockStatus, }), }) const data = await response.json() if (response.ok) { setMessage(data.message) // Immediately update the local state to reflect the change const updateUserLockStatus = (users: UserData[]) => users.map((u) => (u.national_id === nationalId ? { ...u, lock_status: !currentLockStatus } : u)) setGlobalUsers((prev) => updateUserLockStatus(prev)) setTeamUsers((prev) => updateUserLockStatus(prev)) setDepartmentUsers((prev) => updateUserLockStatus(prev)) setFieldUsers((prev) => updateUserLockStatus(prev)) // Also refresh from server to ensure consistency setTimeout(() => { refetchGlobal() refetchTeam() refetchDepartment() refetchField() }, 100) } else { setMessage(data.error || "שגיאה בשינוי סטטוס נעילה") } } catch (err) { setMessage("שגיאה בחיבור לשרת") } } const handleUpdateUserScope = async (payload: { userId: string field: string department: string team: string }) => { if (!user?.national_id) return setScopeSaving(true) try { const response = await fetch("/api/admin/update-user-scope", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ adminId: user.national_id, targetUserId: payload.userId, field: payload.field, department: payload.department, team: payload.team, }), }) const data = await response.json() if (response.ok) { setMessage(data.message || "User updated.") setScopeModalOpen(false) setScopeUser(null) await Promise.all([refetchGlobal(), refetchTeam(), refetchDepartment(), refetchField()]) } else { setMessage(data.error || "Failed to update user.") } } catch (error) { console.error("User scope update error:", error) setMessage("Failed to update user.") } finally { setScopeSaving(false) } } 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-emerald-600" } default: return { text: "דיווח חסר", color: "text-gray-500" } } } const handleGlobalCategoryClick = (category: string, categoryName: string) => { setSelectedCategory(category) setSelectedCategoryName(categoryName) setModalOpen(true) } const handleTeamCategoryClick = (category: string, categoryName: string) => { setSelectedCategory(category) setSelectedCategoryName(categoryName) setTeamModalOpen(true) } const handleDepartmentCategoryClick = (category: string, categoryName: string) => { setSelectedCategory(category) setSelectedCategoryName(categoryName) setDepartmentModalOpen(true) } const handleFieldCategoryClick = (category: string, categoryName: string) => { setSelectedCategory(category) setSelectedCategoryName(categoryName) setFieldModalOpen(true) } const handleManualRefresh = async () => { setIsRefreshing(true) try { await Promise.all([refetchGlobal(), refetchTeam(), refetchDepartment(), refetchField()]) } catch (error) { console.error("Manual refresh failed:", error) setMessage("שגיאה ברענון הנתונים") } finally { setTimeout(() => setIsRefreshing(false), 500) } } 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: user?.national_id, targetUserId: userId, status, }), }) const data = await response.json() if (response.ok) { setMessage(data.message) refetchGlobal() refetchTeam() refetchDepartment() refetchField() } else { setMessage(data.error || "שגיאה בדיווח") } } catch (err) { setMessage("שגיאה בחיבור לשרת") } } const formatCooldownTime = (seconds: number) => { if (seconds <= 0) return "" const minutes = Math.floor(seconds / 60) const remainingSeconds = seconds % 60 if (minutes > 0) { return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}` } return `${remainingSeconds} שניות` } const renderStatsSection = ( stats: Stats | null, onCategoryClick: (category: string, categoryName: string) => void, isTeam = false, customName?: string, ) => { const displayName = customName || (isTeam ? `צוות ${teamName}` : "כלליות") return (
{isTeam ? `צוות ${teamName}` : customName ? `מסגרת ${customName}` : "כללי"}{" "} {isRefreshing && } {(isTeam ? teamConnected : departmentConnected ? departmentConnected : fieldConnected ? fieldConnected : globalConnected) &&
}
{stats ? ( <> {viewMode === "list" ? (
onCategoryClick("no_report", "לא דיווחו")} > לא דיווחו: {stats.no_report}
onCategoryClick("in_shelter", "במקלט/חדר מוגן")} > במקלט: {stats.in_shelter}
onCategoryClick("not_in_shelter", "לא במקלט - אין מקלט בקרבת מקום")} > לא במקלט: {stats.not_in_shelter}
onCategoryClick("no_alarm", "אין אזעקה באזור")} > אין אזעקה: {stats.no_alarm}
onCategoryClick("safe_after_exit", "אני בטוח.ה (סוף אירוע)")} > אני בטוח.ה (סוף אירוע) {stats.safe_after_exit}
) : useSimpleChart ? ( ) : ( )} ) : (
טוען סטטיסטיקות...
)}
{( isTeam ? teamConnected : departmentConnected ? departmentConnected : fieldConnected ? fieldConnected : globalConnected ) ? ( ) : ( "מנסה להתחבר לעדכונים..." )}
) } const renderUsersTable = (users: UserData[], changedRows: Set, isReadOnly = false) => { return (
שם דיווח {!isReadOnly && פעולות} תחום מסגרת צוות {users.map((userData) => { const status = getStatusText(userData.in_shelter) const isChanged = changedRows.has(userData.national_id) const isLocked = userData.lock_status || false return ( {userData.name} {isChanged && 🔄} {status.text} {!isReadOnly && (
{user?.role !== "user" && ( )}
)} {userData.field || "לא הוגדר"} {userData.department || "לא הוגדר"} {userData.team || "לא הוגדר"}
) })}
{users.length === 0 &&
אין משתמשים
}
) } const fieldNameById = new Map(managedTypes.fields.map((field) => [field.id, field.name])) const departmentNameById = new Map(managedTypes.departments.map((dept) => [dept.id, dept.name])) const allowedManagedTabs = user?.role === "global_admin" || user?.role === "field_admin" ? ["field", "department", "team"] : user?.role === "department_admin" ? ["department", "team"] : [] useEffect(() => { if (allowedManagedTabs.length > 0 && !allowedManagedTabs.includes(managedTypeTab)) { setManagedTypeTab(allowedManagedTabs[0] as "field" | "department" | "team") } }, [allowedManagedTabs, managedTypeTab]) if (!user) return null return (
ניהול
{globalConnected || teamConnected || departmentConnected || fieldConnected ? ( <> מקוון ) : ( <> מתחבר... )}
{user?.role !== "user" && ( )}
{message && ( {message} )} {debugMode && ( Debug Info:
User: {user?.name} ({user?.role})
Team: {teamName}
Global Users: {globalUsers.length}
Global Connected: {globalConnected ? "Yes" : "No"}
Global Reset Cooldown: {globalResetCooldown} seconds
Team Reset Cooldown: {teamResetCooldown} seconds
Department Reset Cooldown: {departmentResetCooldown} seconds
Field Reset Cooldown: {fieldResetCooldown} seconds
)} צוות מסגרת תחום כללי
איפוס סטטוסי הצוות
הערה: איפוס יאפס רק את המשתמשים מהצוות שלך ({teamName}) שאינם נעולים
{renderStatsSection(teamStats, handleTeamCategoryClick, true)}
ניהול משתמשי צוות {teamName} {teamChangedRows.size > 0 && ( {teamChangedRows.size} עדכונים חדשים )} {loadingUsers ? (
טוען משתמשים...
) : ( renderUsersTable(teamUsers, teamChangedRows) )}
{user?.role === "team_admin" ? "צפייה במסגרת" : "איפוס סטטוסי המסגרת"} {user?.role === "team_admin" ? (

צפייה בלבד

כמנהל צוות, אתה יכול לראות את המסגרת שלך אך לא לאפס אותה

) : ( <>
הערה: איפוס יאפס את כל המשתמשים מהמסגרת שלך ({departmentName}) שאינם נעולים
)}
{renderStatsSection(departmentStats, handleDepartmentCategoryClick, false, departmentName)}
{user?.role === "team_admin" ? "צפייה במשתמשי מסגרת" : "ניהול משתמשי מסגרת"} {departmentName} {departmentChangedRows.size > 0 && ( {departmentChangedRows.size} עדכונים חדשים )} {loadingUsers ? (
טוען משתמשים...
) : ( renderUsersTable(departmentUsers, departmentChangedRows, user?.role === "team_admin") )}
איפוס סטטוסי התחום {(user?.role === "department_admin" || user?.role === "team_admin") ? (

צפייה בלבד

כ{ROLE_NAMES[user.role]}, אתה יכול לראות את התחום שלך אך לא לאפס אותו

) : ( <>
הערה: איפוס יאפס את כל המשתמשים מהתחום שלך ({fieldName}) שאינם נעולים
)}
{renderStatsSection(fieldStats, handleFieldCategoryClick, false, fieldName)}
{(user?.role === "team_admin" || user?.role === "department_admin") ? "צפייה במשתמשי תחום" : "ניהול משתמשי תחום"} {fieldName} {fieldChangedRows.size > 0 && ( {fieldChangedRows.size} עדכונים חדשים )} {loadingUsers ? (
טוען משתמשים...
) : ( renderUsersTable(fieldUsers, fieldChangedRows, (user?.role === "team_admin" || user?.role === "department_admin")) )}
איפוס סטטוסים כללי {globalLastReset &&

איפוס אחרון: {globalLastReset}

}
הערה: איפוס יאפס את כל המשתמשים במערכת (כולל מנהלים) שאינם נעולים
{renderStatsSection(globalStats, handleGlobalCategoryClick, false)}
הוספת משתמש חדש
setNewUser({ ...newUser, name: e.target.value })} placeholder="שם" required />
הערה: המשתמש יקבל את הסיסמה הזמנית "password123" ויידרש לשנותה בכניסה הראשונה
{allowedManagedTabs.length > 0 && ( ניהול תחומים, מסגרות וצוותים setManagedTypeTab(value as "field" | "department" | "team")} className="w-full" dir="rtl" > {allowedManagedTabs.includes("field") && תחומים} {allowedManagedTabs.includes("department") && ( מסגרות )} {allowedManagedTabs.includes("team") && צוותים} {allowedManagedTabs.includes("field") && (
setNewFieldName(e.target.value)} placeholder="תחום חדש" disabled={managedTypesLoading} />
יש לשייך משתמשים לתחום אחר לפני מחיקה!
{managedTypes.fields.length === 0 ? (
No fields yet.
) : ( managedTypes.fields.map((item) => (
{item.name}
{item.managed ? (
) : ( In use )}
)) )}
)} {allowedManagedTabs.includes("department") && (
setNewDepartmentName(e.target.value)} placeholder="מסגרת חדשה" disabled={managedTypesLoading} />
יש לשייך משתמשים למסגרת אחרת לפני מחיקה!
{managedTypes.departments.length === 0 ? (
No departments yet.
) : ( managedTypes.departments.map((item) => (
{item.name} {item.parentId && ( תחום: {fieldNameById.get(item.parentId) || "לא ידוע"} )}
{item.managed ? (
) : ( In use )}
)) )}
)} {allowedManagedTabs.includes("team") && (
setNewTeamName(e.target.value)} placeholder="צוות חדש" disabled={managedTypesLoading} />
יש לשייך משתמשים לצוות אחר לפני מחיקה!
{managedTypes.teams.length === 0 ? (
No teams yet.
) : ( managedTypes.teams.map((item) => (
{item.name} {item.parentId && ( מסגרת: {departmentNameById.get(item.parentId) || "לא ידועה"} )}
{item.managed ? (
) : ( In use )}
)) )}
)}
)} ניהול כל המשתמשים {changedRows.size > 0 && ( {changedRows.size} עדכונים חדשים )} {loadingUsers ? (
טוען משתמשים...
) : ( renderUsersTable(globalUsers, changedRows) )}
setModalOpen(false)} category={selectedCategory} categoryName={selectedCategoryName} /> setTeamModalOpen(false)} category={selectedCategory} categoryName={selectedCategoryName} adminId={user?.national_id || ""} teamName={teamName} /> setDepartmentModalOpen(false)} category={selectedCategory} categoryName={selectedCategoryName} adminId={user?.national_id || ""} departmentName={departmentName} /> setFieldModalOpen(false)} category={selectedCategory} categoryName={selectedCategoryName} adminId={user?.national_id || ""} fieldName={fieldName} /> setScopeModalOpen(false)} user={scopeUser} fields={managedTypes.fields} departments={managedTypes.departments} teams={managedTypes.teams} onSave={handleUpdateUserScope} isSaving={scopeSaving} /> setReportModalOpen(false)} user={selectedUserForReport} onReport={handleReportOnBehalf} /> {/* Hostname Footer */}
סביבה: {instanceName || "לא זוהה"}
גרסה: {process.env.APPVERSION || "לא הוצהר ב-Dockerfile!"}
2025 COPYRIGHT TR-WEB
) }