Compare commits
9 Commits
e05a7382c1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 077febdbe3 | |||
| 458a78795d | |||
| da156e9cd7 | |||
| a5e9072b9d | |||
| ca6d2d7abe | |||
| e8a7ed82a1 | |||
| 8a4b3bc396 | |||
| a75b8ec6af | |||
| 1607c8bc34 |
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"pages": {}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"polyfillFiles": [
|
|
||||||
"static/chunks/polyfills.js"
|
|
||||||
],
|
|
||||||
"devFiles": [],
|
|
||||||
"ampDevFiles": [],
|
|
||||||
"lowPriorityFiles": [
|
|
||||||
"static/development/_buildManifest.js",
|
|
||||||
"static/development/_ssgManifest.js"
|
|
||||||
],
|
|
||||||
"rootMainFiles": [],
|
|
||||||
"pages": {
|
|
||||||
"/_app": []
|
|
||||||
},
|
|
||||||
"ampFirstPages": []
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
BIN
.next/cache/webpack/client-development/0.pack.gz
vendored
BIN
.next/cache/webpack/client-development/0.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/1.pack.gz
vendored
BIN
.next/cache/webpack/client-development/1.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/10.pack.gz
vendored
BIN
.next/cache/webpack/client-development/10.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/11.pack.gz
vendored
BIN
.next/cache/webpack/client-development/11.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/12.pack.gz
vendored
BIN
.next/cache/webpack/client-development/12.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/2.pack.gz
vendored
BIN
.next/cache/webpack/client-development/2.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/3.pack.gz
vendored
BIN
.next/cache/webpack/client-development/3.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/4.pack.gz
vendored
BIN
.next/cache/webpack/client-development/4.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/5.pack.gz
vendored
BIN
.next/cache/webpack/client-development/5.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/6.pack.gz
vendored
BIN
.next/cache/webpack/client-development/6.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/7.pack.gz
vendored
BIN
.next/cache/webpack/client-development/7.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/8.pack.gz
vendored
BIN
.next/cache/webpack/client-development/8.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/9.pack.gz
vendored
BIN
.next/cache/webpack/client-development/9.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/index.pack.gz
vendored
BIN
.next/cache/webpack/client-development/index.pack.gz
vendored
Binary file not shown.
Binary file not shown.
BIN
.next/cache/webpack/server-development/0.pack.gz
vendored
BIN
.next/cache/webpack/server-development/0.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/1.pack.gz
vendored
BIN
.next/cache/webpack/server-development/1.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/10.pack.gz
vendored
BIN
.next/cache/webpack/server-development/10.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/11.pack.gz
vendored
BIN
.next/cache/webpack/server-development/11.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/12.pack.gz
vendored
BIN
.next/cache/webpack/server-development/12.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/13.pack.gz
vendored
BIN
.next/cache/webpack/server-development/13.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/14.pack.gz
vendored
BIN
.next/cache/webpack/server-development/14.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/2.pack.gz
vendored
BIN
.next/cache/webpack/server-development/2.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/3.pack.gz
vendored
BIN
.next/cache/webpack/server-development/3.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/4.pack.gz
vendored
BIN
.next/cache/webpack/server-development/4.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/5.pack.gz
vendored
BIN
.next/cache/webpack/server-development/5.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/6.pack.gz
vendored
BIN
.next/cache/webpack/server-development/6.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/7.pack.gz
vendored
BIN
.next/cache/webpack/server-development/7.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/8.pack.gz
vendored
BIN
.next/cache/webpack/server-development/8.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/9.pack.gz
vendored
BIN
.next/cache/webpack/server-development/9.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/index.pack.gz
vendored
BIN
.next/cache/webpack/server-development/index.pack.gz
vendored
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
{"type": "commonjs"}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
self.__INTERCEPTION_ROUTE_REWRITE_MANIFEST="[]"
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
self.__BUILD_MANIFEST = {
|
|
||||||
"polyfillFiles": [
|
|
||||||
"static/chunks/polyfills.js"
|
|
||||||
],
|
|
||||||
"devFiles": [],
|
|
||||||
"ampDevFiles": [],
|
|
||||||
"lowPriorityFiles": [],
|
|
||||||
"rootMainFiles": [],
|
|
||||||
"pages": {
|
|
||||||
"/_app": []
|
|
||||||
},
|
|
||||||
"ampFirstPages": []
|
|
||||||
};
|
|
||||||
self.__BUILD_MANIFEST.lowPriorityFiles = [
|
|
||||||
"/static/" + process.env.__NEXT_BUILD_ID + "/_buildManifest.js",
|
|
||||||
,"/static/" + process.env.__NEXT_BUILD_ID + "/_ssgManifest.js",
|
|
||||||
|
|
||||||
];
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 3,
|
|
||||||
"middleware": {},
|
|
||||||
"functions": {},
|
|
||||||
"sortedMiddleware": []
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
self.__REACT_LOADABLE_MANIFEST="{}"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
self.__NEXT_FONT_MANIFEST="{\"pages\":{},\"app\":{},\"appUsingSizeAdjust\":false,\"pagesUsingSizeAdjust\":false}"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"pages":{},"app":{},"appUsingSizeAdjust":false,"pagesUsingSizeAdjust":false}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY\"\n}"
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"node": {},
|
|
||||||
"edge": {},
|
|
||||||
"encryptionKey": "4wbouw6RrCkiRBuhNA0RdY1ZJ0IOxBnrfMXrawt4S9g="
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
self.__BUILD_MANIFEST = {__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},sortedPages:["\u002F_app"]};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
self.__SSG_MANIFEST=new Set;self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
{"type": "module"}
|
|
||||||
@@ -83,6 +83,7 @@ interface Stats {
|
|||||||
interface UserData {
|
interface UserData {
|
||||||
national_id: string
|
national_id: string
|
||||||
name: string
|
name: string
|
||||||
|
role?: string
|
||||||
in_shelter?: string
|
in_shelter?: string
|
||||||
last_updated?: string
|
last_updated?: string
|
||||||
is_admin: boolean
|
is_admin: boolean
|
||||||
@@ -650,35 +651,64 @@ export default function AdminPage() {
|
|||||||
const handleAddUser = async (e: React.FormEvent) => {
|
const handleAddUser = async (e: React.FormEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (!newUser.field || !newUser.department || !newUser.team || !newUser.role) {
|
const trimmedField = newUser.field.trim()
|
||||||
setMessage("יש לבחור תפקיד, תחום, מסגרת וצוות")
|
const trimmedDepartment = newUser.department.trim()
|
||||||
|
const trimmedTeam = newUser.team.trim()
|
||||||
|
const selectedRole = newUser.role as UserRole
|
||||||
|
|
||||||
|
if (!trimmedField || !selectedRole) {
|
||||||
|
setMessage("Please fill in the required fields.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedRole === "department_admin") {
|
||||||
|
if (!trimmedDepartment) {
|
||||||
|
setMessage("Please fill in the required fields.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if (selectedRole === "team_admin" || selectedRole === "user") {
|
||||||
|
if (!trimmedDepartment || !trimmedTeam) {
|
||||||
|
setMessage("Please fill in the required fields.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const payload = {
|
||||||
|
...newUser,
|
||||||
|
field: trimmedField,
|
||||||
|
department: trimmedDepartment,
|
||||||
|
team: trimmedTeam,
|
||||||
|
adminId: user?.national_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedRole === "field_admin") {
|
||||||
|
payload.department = ""
|
||||||
|
payload.team = ""
|
||||||
|
} else if (selectedRole === "department_admin") {
|
||||||
|
payload.team = ""
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch("/api/admin/add-user", {
|
const response = await fetch("/api/admin/add-user", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(payload),
|
||||||
...newUser,
|
|
||||||
adminId: user?.national_id,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
setMessage(`${data.message}. הסיסמה הזמנית: password123`)
|
setMessage(`${data.message}. Default password: password123`)
|
||||||
setNewUser({ name: "", isAdmin: false, field: "", department: "", team: "", role: "" })
|
setNewUser({ name: "", isAdmin: false, field: "", department: "", team: "", role: "" })
|
||||||
refetchGlobal()
|
refetchGlobal()
|
||||||
refetchTeam()
|
refetchTeam()
|
||||||
refetchDepartment()
|
refetchDepartment()
|
||||||
refetchField()
|
refetchField()
|
||||||
} else {
|
} else {
|
||||||
setMessage(data.error || "שגיאה בהוספת משתמש")
|
setMessage(data.error || "Failed to add user.")
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setMessage("שגיאה בחיבור לשרת")
|
setMessage("Failed to add user.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1130,6 +1160,34 @@ export default function AdminPage() {
|
|||||||
}
|
}
|
||||||
}, [allowedManagedTabs, managedTypeTab])
|
}, [allowedManagedTabs, managedTypeTab])
|
||||||
|
|
||||||
|
const canShowTeamTab =
|
||||||
|
(user?.role === "team_admin" && !!user?.team) || (user?.role === "global_admin" && !!user?.team)
|
||||||
|
const canShowDepartmentTab =
|
||||||
|
(user?.role === "department_admin" && !!user?.department) ||
|
||||||
|
(user?.role === "global_admin" && !!user?.department)
|
||||||
|
const canShowFieldTab =
|
||||||
|
user?.role === "field_admin" || (user?.role === "global_admin" && !!user?.field)
|
||||||
|
const visibleTabs = [
|
||||||
|
canShowTeamTab ? "team" : null,
|
||||||
|
canShowDepartmentTab ? "department" : null,
|
||||||
|
canShowFieldTab ? "field" : null,
|
||||||
|
"global",
|
||||||
|
].filter(Boolean) as string[]
|
||||||
|
const tabColumns =
|
||||||
|
visibleTabs.length === 1
|
||||||
|
? "grid-cols-1"
|
||||||
|
: visibleTabs.length === 2
|
||||||
|
? "grid-cols-2"
|
||||||
|
: visibleTabs.length === 3
|
||||||
|
? "grid-cols-3"
|
||||||
|
: "grid-cols-4"
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visibleTabs.length > 0 && !visibleTabs.includes(activeTab)) {
|
||||||
|
setActiveTab(visibleTabs[0])
|
||||||
|
}
|
||||||
|
}, [activeTab, visibleTabs])
|
||||||
|
|
||||||
if (!user) return null
|
if (!user) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -1204,25 +1262,32 @@ export default function AdminPage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full" dir="rtl">
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full" dir="rtl">
|
||||||
<TabsList className="grid w-full grid-cols-4">
|
<TabsList className={`grid w-full ${tabColumns}`}>
|
||||||
<TabsTrigger value="team" className="flex items-center gap-2">
|
{canShowTeamTab && (
|
||||||
<UsersIcon className="h-4 w-4" />
|
<TabsTrigger value="team" className="flex items-center gap-2">
|
||||||
צוות
|
<UsersIcon className="h-4 w-4" />
|
||||||
</TabsTrigger>
|
צוות
|
||||||
<TabsTrigger value="department" className="flex items-center gap-2">
|
</TabsTrigger>
|
||||||
<Building2 className="h-4 w-4" />
|
)}
|
||||||
מסגרת
|
{canShowDepartmentTab && (
|
||||||
</TabsTrigger>
|
<TabsTrigger value="department" className="flex items-center gap-2">
|
||||||
<TabsTrigger value="field" className="flex items-center gap-2">
|
<Building2 className="h-4 w-4" />
|
||||||
<Globe className="h-4 w-4" />
|
מסגרת
|
||||||
תחום
|
</TabsTrigger>
|
||||||
</TabsTrigger>
|
)}
|
||||||
|
{canShowFieldTab && (
|
||||||
|
<TabsTrigger value="field" className="flex items-center gap-2">
|
||||||
|
<Globe className="h-4 w-4" />
|
||||||
|
תחום
|
||||||
|
</TabsTrigger>
|
||||||
|
)}
|
||||||
<TabsTrigger value="global" className="flex items-center gap-2">
|
<TabsTrigger value="global" className="flex items-center gap-2">
|
||||||
<Globe className="h-4 w-4" />
|
<Globe className="h-4 w-4" />
|
||||||
כללי
|
כללי
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
|
{canShowTeamTab && (
|
||||||
<TabsContent value="team" className="space-y-6">
|
<TabsContent value="team" className="space-y-6">
|
||||||
<div className="grid md:grid-cols-2 gap-6">
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -1278,7 +1343,9 @@ export default function AdminPage() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{canShowDepartmentTab && (
|
||||||
<TabsContent value="department" className="space-y-6">
|
<TabsContent value="department" className="space-y-6">
|
||||||
<div className="grid md:grid-cols-2 gap-6">
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -1343,7 +1410,9 @@ export default function AdminPage() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{canShowFieldTab && (
|
||||||
<TabsContent value="field" className="space-y-6">
|
<TabsContent value="field" className="space-y-6">
|
||||||
<div className="grid md:grid-cols-2 gap-6">
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -1408,6 +1477,7 @@ export default function AdminPage() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
)}
|
||||||
|
|
||||||
<TabsContent value="global" className="space-y-6">
|
<TabsContent value="global" className="space-y-6">
|
||||||
<div className="grid md:grid-cols-2 gap-6">
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
@@ -1481,6 +1551,8 @@ export default function AdminPage() {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
{newUser.role !== "field_admin" && (
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="department">מסגרת</Label>
|
<Label htmlFor="department">מסגרת</Label>
|
||||||
<Select
|
<Select
|
||||||
@@ -1499,6 +1571,9 @@ export default function AdminPage() {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
{newUser.role !== "field_admin" && newUser.role !== "department_admin" && (
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="team">צוות</Label>
|
<Label htmlFor="team">צוות</Label>
|
||||||
<Select value={newUser.team} onValueChange={(value) => setNewUser({ ...newUser, team: value })}>
|
<Select value={newUser.team} onValueChange={(value) => setNewUser({ ...newUser, team: value })}>
|
||||||
@@ -1514,15 +1589,21 @@ export default function AdminPage() {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="role">הרשאה</Label>
|
<Label htmlFor="role">הרשאה</Label>
|
||||||
<Select
|
<Select
|
||||||
value={newUser.role}
|
value={newUser.role}
|
||||||
onValueChange={(value) =>
|
onValueChange={(value) =>
|
||||||
setNewUser({
|
setNewUser((prev) => {
|
||||||
...newUser,
|
const next = { ...prev, role: value, isAdmin: value !== "user" }
|
||||||
role: value,
|
if (value === "field_admin") {
|
||||||
isAdmin: value !== "user", // Automatically set isAdmin
|
next.department = ""
|
||||||
|
next.team = ""
|
||||||
|
} else if (value === "department_admin") {
|
||||||
|
next.team = ""
|
||||||
|
}
|
||||||
|
return next
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -1858,7 +1939,7 @@ export default function AdminPage() {
|
|||||||
<br/>
|
<br/>
|
||||||
גרסה: {process.env.APPVERSION || "לא הוצהר ב-Dockerfile!"}
|
גרסה: {process.env.APPVERSION || "לא הוצהר ב-Dockerfile!"}
|
||||||
<br/>
|
<br/>
|
||||||
2025 COPYRIGHT TR-WEB
|
2026 COPYRIGHT TR-WEB
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -21,44 +21,52 @@ export async function POST(request: NextRequest) {
|
|||||||
const { name, isAdmin, field, department, team, role } = await request.json()
|
const { name, isAdmin, field, department, team, role } = await request.json()
|
||||||
|
|
||||||
// Input validation
|
// Input validation
|
||||||
if (!name || !field || !department || !team) {
|
if (!name || !field) {
|
||||||
return NextResponse.json({ error: "נתונים חסרים" }, { status: 400 })
|
return NextResponse.json({ error: "Invalid name or field." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate department, team, and field against managed types
|
// Validate field against managed types
|
||||||
if (!(await hasManagedType("field", field))) {
|
if (!(await hasManagedType("field", field))) {
|
||||||
return NextResponse.json({ error: "Invalid field." }, { status: 400 })
|
return NextResponse.json({ error: "Invalid field." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(await hasManagedType("department", department))) {
|
const normalizedDepartment = typeof department === "string" && department.trim() ? department.trim() : null
|
||||||
|
|
||||||
|
if (normalizedDepartment && !(await hasManagedType("department", normalizedDepartment))) {
|
||||||
return NextResponse.json({ error: "Invalid department." }, { status: 400 })
|
return NextResponse.json({ error: "Invalid department." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(await hasManagedType("team", team))) {
|
const normalizedTeam = typeof team === "string" && team.trim() ? team.trim() : null
|
||||||
|
|
||||||
|
if (normalizedTeam && !(await hasManagedType("team", normalizedTeam))) {
|
||||||
return NextResponse.json({ error: "Invalid team." }, { status: 400 })
|
return NextResponse.json({ error: "Invalid team." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldRows = (await safeQuery("SELECT id FROM managed_types WHERE type = 'field' AND name = ?", [
|
const fieldRows = (await safeQuery("SELECT id FROM managed_types WHERE type = 'field' AND name = ?", [
|
||||||
field,
|
field,
|
||||||
])) as Array<{ id: number }>
|
])) as Array<{ id: number }>
|
||||||
const departmentRows = (await safeQuery(
|
const departmentRows = normalizedDepartment
|
||||||
"SELECT id, parent_id AS parentId FROM managed_types WHERE type = 'department' AND name = ?",
|
? ((await safeQuery(
|
||||||
[department],
|
"SELECT id, parent_id AS parentId FROM managed_types WHERE type = 'department' AND name = ?",
|
||||||
)) as Array<{ id: number; parentId: number | null }>
|
[normalizedDepartment],
|
||||||
const teamRows = (await safeQuery(
|
)) as Array<{ id: number; parentId: number | null }>)
|
||||||
"SELECT id, parent_id AS parentId FROM managed_types WHERE type = 'team' AND name = ?",
|
: []
|
||||||
[team],
|
const teamRows = normalizedTeam
|
||||||
)) as Array<{ id: number; parentId: number | null }>
|
? ((await safeQuery(
|
||||||
|
"SELECT id, parent_id AS parentId FROM managed_types WHERE type = 'team' AND name = ?",
|
||||||
|
[normalizedTeam],
|
||||||
|
)) as Array<{ id: number; parentId: number | null }>)
|
||||||
|
: []
|
||||||
|
|
||||||
if (fieldRows.length === 0 || departmentRows.length === 0 || teamRows.length === 0) {
|
if (fieldRows.length === 0 || (normalizedDepartment && departmentRows.length === 0) || (normalizedTeam && teamRows.length === 0)) {
|
||||||
return NextResponse.json({ error: "Invalid field, department, or team." }, { status: 400 })
|
return NextResponse.json({ error: "Invalid field, department, or team." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (departmentRows[0].parentId !== fieldRows[0].id) {
|
if (normalizedDepartment && departmentRows[0].parentId !== fieldRows[0].id) {
|
||||||
return NextResponse.json({ error: "Department does not belong to field." }, { status: 400 })
|
return NextResponse.json({ error: "Department does not belong to field." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (teamRows[0].parentId !== departmentRows[0].id) {
|
if (normalizedTeam && normalizedDepartment && teamRows[0].parentId !== departmentRows[0].id) {
|
||||||
return NextResponse.json({ error: "Team does not belong to department." }, { status: 400 })
|
return NextResponse.json({ error: "Team does not belong to department." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +76,21 @@ export async function POST(request: NextRequest) {
|
|||||||
const userRole: UserRole = (role as UserRole) || (isAdmin ? "global_admin" : "user")
|
const userRole: UserRole = (role as UserRole) || (isAdmin ? "global_admin" : "user")
|
||||||
|
|
||||||
if (!validRoles.includes(userRole)) {
|
if (!validRoles.includes(userRole)) {
|
||||||
return NextResponse.json({ error: "תפקיד לא תקין" }, { status: 400 })
|
return NextResponse.json({ error: "Invalid role." }, { status: 400 })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userRole === "field_admin") {
|
||||||
|
if (!field || normalizedDepartment || normalizedTeam) {
|
||||||
|
return NextResponse.json({ error: "Field admins must have a field only." }, { status: 400 })
|
||||||
|
}
|
||||||
|
} else if (userRole === "department_admin") {
|
||||||
|
if (!field || !normalizedDepartment || normalizedTeam) {
|
||||||
|
return NextResponse.json({ error: "Department admins must have field + department only." }, { status: 400 })
|
||||||
|
}
|
||||||
|
} else if (userRole === "team_admin" || userRole === "user") {
|
||||||
|
if (!field || !normalizedDepartment || !normalizedTeam) {
|
||||||
|
return NextResponse.json({ error: "Team/users require field, department, and team." }, { status: 400 })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate unique Login ID
|
// Generate unique Login ID
|
||||||
@@ -80,7 +102,7 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
await safeQuery(
|
await safeQuery(
|
||||||
"INSERT INTO users (national_id, password, name, is_admin, role, must_change_password, field, department, team) VALUES (?, ?, ?, ?, ?, TRUE, ?, ?, ?)",
|
"INSERT INTO users (national_id, password, name, is_admin, role, must_change_password, field, department, team) VALUES (?, ?, ?, ?, ?, TRUE, ?, ?, ?)",
|
||||||
[nationalId, hashedPassword, name, isAdmin, userRole, field, department, team],
|
[nationalId, hashedPassword, name, isAdmin, userRole, field, normalizedDepartment, normalizedTeam],
|
||||||
)
|
)
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
// Get admin's field and department
|
// Get admin's field and department
|
||||||
const adminData = (await safeQuery(
|
const adminData = (await safeQuery(
|
||||||
"SELECT field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
"SELECT role, field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||||
[adminId],
|
[adminId],
|
||||||
)) as any[]
|
)) as any[]
|
||||||
|
|
||||||
@@ -19,10 +19,14 @@ export async function POST(request: NextRequest) {
|
|||||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { field: adminField, department: adminDepartment } = adminData[0]
|
const { role: adminRole, field: adminField, department: adminDepartment } = adminData[0]
|
||||||
|
|
||||||
|
if (adminRole !== "department_admin" && adminRole !== "global_admin") {
|
||||||
|
return NextResponse.json({ error: "Insufficient permissions." }, { status: 403 })
|
||||||
|
}
|
||||||
|
|
||||||
if (!adminField || !adminDepartment) {
|
if (!adminField || !adminDepartment) {
|
||||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום ומסגרת" }, { status: 400 })
|
return NextResponse.json({ error: "Department is not assigned." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cooldown for department resets
|
// Check cooldown for department resets
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
// Get admin's field and department
|
// Get admin's field and department
|
||||||
const adminData = (await executeQuery(
|
const adminData = (await executeQuery(
|
||||||
"SELECT field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
"SELECT role, field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||||
[adminId],
|
[adminId],
|
||||||
)) as any[]
|
)) as any[]
|
||||||
|
|
||||||
@@ -19,10 +19,18 @@ export async function POST(request: Request) {
|
|||||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { field: adminField, department: adminDepartment } = adminData[0]
|
const { role: adminRole, field: adminField, department: adminDepartment } = adminData[0]
|
||||||
|
|
||||||
if (!adminField || !adminDepartment) {
|
if (adminRole !== "department_admin" && adminRole !== "global_admin" || !adminField || !adminDepartment) {
|
||||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום ומסגרת" }, { status: 400 })
|
return NextResponse.json({
|
||||||
|
no_report: 0,
|
||||||
|
in_shelter: 0,
|
||||||
|
not_in_shelter: 0,
|
||||||
|
no_alarm: 0,
|
||||||
|
safe_after_exit: 0,
|
||||||
|
field: adminField,
|
||||||
|
department: adminDepartment,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get department stats with field and department context
|
// Get department stats with field and department context
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
// Get admin's field and department
|
// Get admin's field and department
|
||||||
const adminData = (await executeQuery(
|
const adminData = (await executeQuery(
|
||||||
"SELECT field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
"SELECT role, field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||||
[adminId],
|
[adminId],
|
||||||
)) as any[]
|
)) as any[]
|
||||||
|
|
||||||
@@ -19,10 +19,10 @@ export async function POST(request: NextRequest) {
|
|||||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { field: adminField, department: adminDepartment } = adminData[0]
|
const { role: adminRole, field: adminField, department: adminDepartment } = adminData[0]
|
||||||
|
|
||||||
if (!adminField || !adminDepartment) {
|
if (adminRole !== "department_admin" && adminRole !== "global_admin" || !adminField || !adminDepartment) {
|
||||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום ומסגרת" }, { status: 400 })
|
return NextResponse.json([])
|
||||||
}
|
}
|
||||||
|
|
||||||
let query = ""
|
let query = ""
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
// Get admin's field and department
|
// Get admin's field and department
|
||||||
const adminData = (await executeQuery(
|
const adminData = (await executeQuery(
|
||||||
"SELECT field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
"SELECT role, field, department FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||||
[adminId],
|
[adminId],
|
||||||
)) as any[]
|
)) as any[]
|
||||||
|
|
||||||
@@ -19,10 +19,10 @@ export async function POST(request: Request) {
|
|||||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { field: adminField, department: adminDepartment } = adminData[0]
|
const { role: adminRole, field: adminField, department: adminDepartment } = adminData[0]
|
||||||
|
|
||||||
if (!adminField || !adminDepartment) {
|
if (adminRole !== "department_admin" && adminRole !== "global_admin" || !adminField || !adminDepartment) {
|
||||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום ומסגרת" }, { status: 400 })
|
return NextResponse.json({ users: [], field: adminField, department: adminDepartment })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get department users with field and department context
|
// Get department users with field and department context
|
||||||
@@ -31,6 +31,7 @@ export async function POST(request: Request) {
|
|||||||
SELECT
|
SELECT
|
||||||
national_id,
|
national_id,
|
||||||
name,
|
name,
|
||||||
|
role,
|
||||||
in_shelter,
|
in_shelter,
|
||||||
last_updated,
|
last_updated,
|
||||||
is_admin,
|
is_admin,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export async function POST(request: Request) {
|
|||||||
SELECT
|
SELECT
|
||||||
national_id,
|
national_id,
|
||||||
name,
|
name,
|
||||||
|
role,
|
||||||
in_shelter,
|
in_shelter,
|
||||||
last_updated,
|
last_updated,
|
||||||
is_admin,
|
is_admin,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
// Get admin's field, department, and team
|
// Get admin's field, department, and team
|
||||||
const adminData = (await safeQuery(
|
const adminData = (await safeQuery(
|
||||||
"SELECT field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
"SELECT role, field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||||
[adminId],
|
[adminId],
|
||||||
)) as any[]
|
)) as any[]
|
||||||
|
|
||||||
@@ -20,10 +20,14 @@ export async function POST(request: NextRequest) {
|
|||||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
const { role: adminRole, field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
||||||
|
|
||||||
|
if (adminRole !== "team_admin" && adminRole !== "global_admin") {
|
||||||
|
return NextResponse.json({ error: "Insufficient permissions." }, { status: 403 })
|
||||||
|
}
|
||||||
|
|
||||||
if (!adminField || !adminDepartment || !adminTeam) {
|
if (!adminField || !adminDepartment || !adminTeam) {
|
||||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום, מסגרת וצוות" }, { status: 400 })
|
return NextResponse.json({ error: "Team is not assigned." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
await safeQuery(
|
await safeQuery(
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
// Get admin's field, department, and team
|
// Get admin's field, department, and team
|
||||||
const adminData = (await executeQuery(
|
const adminData = (await executeQuery(
|
||||||
"SELECT field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
"SELECT role, field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||||
[adminId],
|
[adminId],
|
||||||
)) as any[]
|
)) as any[]
|
||||||
|
|
||||||
@@ -19,10 +19,19 @@ export async function POST(request: Request) {
|
|||||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
const { role: adminRole, field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
||||||
|
|
||||||
if (!adminField || !adminDepartment || !adminTeam) {
|
if (adminRole !== "team_admin" && adminRole !== "global_admin" || !adminField || !adminDepartment || !adminTeam) {
|
||||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום, מסגרת וצוות" }, { status: 400 })
|
return NextResponse.json({
|
||||||
|
no_report: 0,
|
||||||
|
in_shelter: 0,
|
||||||
|
not_in_shelter: 0,
|
||||||
|
no_alarm: 0,
|
||||||
|
safe_after_exit: 0,
|
||||||
|
field: adminField,
|
||||||
|
department: adminDepartment,
|
||||||
|
team: adminTeam,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get team stats with full context (field + department + team)
|
// Get team stats with full context (field + department + team)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
// Get admin's field, department, and team
|
// Get admin's field, department, and team
|
||||||
const adminData = (await executeQuery(
|
const adminData = (await executeQuery(
|
||||||
"SELECT field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
"SELECT role, field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'",
|
||||||
[adminId],
|
[adminId],
|
||||||
)) as any[]
|
)) as any[]
|
||||||
|
|
||||||
@@ -19,10 +19,10 @@ export async function POST(request: NextRequest) {
|
|||||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
const { role: adminRole, field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
||||||
|
|
||||||
if (!adminField || !adminDepartment || !adminTeam) {
|
if (adminRole !== "team_admin" && adminRole !== "global_admin" || !adminField || !adminDepartment || !adminTeam) {
|
||||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום, מסגרת וצוות" }, { status: 400 })
|
return NextResponse.json([])
|
||||||
}
|
}
|
||||||
|
|
||||||
let query = ""
|
let query = ""
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export async function POST(request: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get admin's field, department, and team
|
// Get admin's field, department, and team
|
||||||
const adminData = (await executeQuery("SELECT field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'", [
|
const adminData = (await executeQuery("SELECT role, field, department, team FROM users WHERE national_id = ? AND role IS NOT NULL AND role != 'user'", [
|
||||||
adminId,
|
adminId,
|
||||||
])) as any[]
|
])) as any[]
|
||||||
|
|
||||||
@@ -18,10 +18,10 @@ export async function POST(request: Request) {
|
|||||||
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
return NextResponse.json({ error: "מנהל לא נמצא" }, { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
const { role: adminRole, field: adminField, department: adminDepartment, team: adminTeam } = adminData[0]
|
||||||
|
|
||||||
if (!adminField || !adminDepartment || !adminTeam) {
|
if (adminRole !== "team_admin" && adminRole !== "global_admin" || !adminField || !adminDepartment || !adminTeam) {
|
||||||
return NextResponse.json({ error: "למנהל לא הוגדרו תחום, מסגרת וצוות" }, { status: 400 })
|
return NextResponse.json({ users: [], field: adminField, department: adminDepartment, team: adminTeam })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get team users with full context (field + department + team)
|
// Get team users with full context (field + department + team)
|
||||||
@@ -30,6 +30,7 @@ export async function POST(request: Request) {
|
|||||||
SELECT
|
SELECT
|
||||||
national_id,
|
national_id,
|
||||||
name,
|
name,
|
||||||
|
role,
|
||||||
in_shelter,
|
in_shelter,
|
||||||
last_updated,
|
last_updated,
|
||||||
is_admin,
|
is_admin,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export async function POST(request: NextRequest) {
|
|||||||
try {
|
try {
|
||||||
const { adminId, targetUserId, field, department, team } = await request.json()
|
const { adminId, targetUserId, field, department, team } = await request.json()
|
||||||
|
|
||||||
if (!adminId || !targetUserId || !field || !department || !team) {
|
if (!adminId || !targetUserId || !field ) {
|
||||||
return NextResponse.json({ error: "חסרים שדות חובה." }, { status: 400 })
|
return NextResponse.json({ error: "חסרים שדות חובה." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
const admin = adminRows[0]
|
const admin = adminRows[0]
|
||||||
|
|
||||||
const userRows = (await safeQuery("SELECT national_id, field, department, team FROM users WHERE national_id = ?", [
|
const userRows = (await safeQuery("SELECT national_id, role, field, department, team FROM users WHERE national_id = ?", [
|
||||||
targetUserId,
|
targetUserId,
|
||||||
])) as any[]
|
])) as any[]
|
||||||
if (userRows.length === 0) {
|
if (userRows.length === 0) {
|
||||||
@@ -39,10 +39,25 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
const targetUser = userRows[0]
|
const targetUser = userRows[0]
|
||||||
|
|
||||||
|
const normalizedDepartment = typeof department === "string" && department.trim() ? department.trim() : null
|
||||||
|
const normalizedTeam = typeof team === "string" && team.trim() ? team.trim() : null
|
||||||
|
|
||||||
|
if (targetUser.role !== "field_admin" && !normalizedDepartment) {
|
||||||
|
return NextResponse.json({ error: "Missing required fields." }, { status: 400 })
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((targetUser.role === "team_admin" || targetUser.role === "user") && !normalizedTeam) {
|
||||||
|
return NextResponse.json({ error: "Missing required fields." }, { status: 400 })
|
||||||
|
}
|
||||||
|
|
||||||
|
const effectiveDepartment = targetUser.role === "field_admin" ? null : normalizedDepartment
|
||||||
|
const effectiveTeam = targetUser.role === "field_admin" ? null : normalizedTeam
|
||||||
|
|
||||||
|
|
||||||
const [fieldOk, departmentOk, teamOk] = await Promise.all([
|
const [fieldOk, departmentOk, teamOk] = await Promise.all([
|
||||||
hasManagedType("field", field),
|
hasManagedType("field", field),
|
||||||
hasManagedType("department", department),
|
effectiveDepartment ? hasManagedType("department", effectiveDepartment) : Promise.resolve(true),
|
||||||
hasManagedType("team", team),
|
effectiveTeam ? hasManagedType("team", effectiveTeam) : Promise.resolve(true),
|
||||||
])
|
])
|
||||||
|
|
||||||
if (!fieldOk || !departmentOk || !teamOk) {
|
if (!fieldOk || !departmentOk || !teamOk) {
|
||||||
@@ -52,24 +67,27 @@ export async function POST(request: NextRequest) {
|
|||||||
const fieldRows = (await safeQuery("SELECT id FROM managed_types WHERE type = 'field' AND name = ?", [
|
const fieldRows = (await safeQuery("SELECT id FROM managed_types WHERE type = 'field' AND name = ?", [
|
||||||
field,
|
field,
|
||||||
])) as Array<{ id: number }>
|
])) as Array<{ id: number }>
|
||||||
const departmentRows = (await safeQuery(
|
const departmentRows = effectiveDepartment
|
||||||
"SELECT id, parent_id AS parentId FROM managed_types WHERE type = 'department' AND name = ?",
|
? ((await safeQuery(
|
||||||
[department],
|
"SELECT id, parent_id AS parentId FROM managed_types WHERE type = 'department' AND name = ?",
|
||||||
)) as Array<{ id: number; parentId: number | null }>
|
[effectiveDepartment],
|
||||||
const teamRows = (await safeQuery(
|
)) as Array<{ id: number; parentId: number | null }>)
|
||||||
"SELECT id, parent_id AS parentId FROM managed_types WHERE type = 'team' AND name = ?",
|
: []
|
||||||
[team],
|
const teamRows = effectiveTeam
|
||||||
)) as Array<{ id: number; parentId: number | null }>
|
? ((await safeQuery("SELECT id, parent_id AS parentId FROM managed_types WHERE type = 'team' AND name = ?", [
|
||||||
|
effectiveTeam,
|
||||||
|
])) as Array<{ id: number; parentId: number | null }>)
|
||||||
|
: []
|
||||||
|
|
||||||
if (fieldRows.length === 0 || departmentRows.length === 0 || teamRows.length === 0) {
|
if (fieldRows.length === 0 || (effectiveDepartment && departmentRows.length === 0) || (effectiveTeam && teamRows.length === 0)) {
|
||||||
return NextResponse.json({ error: "תחום, מסגרת או צוות שגויים." }, { status: 400 })
|
return NextResponse.json({ error: "תחום, מסגרת או צוות שגויים." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (departmentRows[0].parentId !== fieldRows[0].id) {
|
if (effectiveDepartment && departmentRows[0].parentId !== fieldRows[0].id) {
|
||||||
return NextResponse.json({ error: "מסגרת לא משוייכת לתחום הנבחר." }, { status: 400 })
|
return NextResponse.json({ error: "מסגרת לא משוייכת לתחום הנבחר." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (teamRows[0].parentId !== departmentRows[0].id) {
|
if (effectiveTeam && teamRows[0].parentId !== departmentRows[0].id) {
|
||||||
return NextResponse.json({ error: "צוות לא משוייך למסגרת הנבחרת." }, { status: 400 })
|
return NextResponse.json({ error: "צוות לא משוייך למסגרת הנבחרת." }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +112,7 @@ export async function POST(request: NextRequest) {
|
|||||||
if (targetUser.team !== admin.team) {
|
if (targetUser.team !== admin.team) {
|
||||||
return NextResponse.json({ error: "המשתמש הנבחר לא משוייך לצוות שלך." }, { status: 403 })
|
return NextResponse.json({ error: "המשתמש הנבחר לא משוייך לצוות שלך." }, { status: 403 })
|
||||||
}
|
}
|
||||||
if (team !== admin.team) {
|
if (effectiveTeam !== admin.team) {
|
||||||
return NextResponse.json({ error: "מנהלי צוותים רשאים לנהל אך ורק את הצוות שלהם." }, { status: 403 })
|
return NextResponse.json({ error: "מנהלי צוותים רשאים לנהל אך ורק את הצוות שלהם." }, { status: 403 })
|
||||||
}
|
}
|
||||||
if (admin.department && department !== admin.department) {
|
if (admin.department && department !== admin.department) {
|
||||||
@@ -109,8 +127,8 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
await safeQuery("UPDATE users SET field = ?, department = ?, team = ? WHERE national_id = ?", [
|
await safeQuery("UPDATE users SET field = ?, department = ?, team = ? WHERE national_id = ?", [
|
||||||
field,
|
field,
|
||||||
department,
|
effectiveDepartment,
|
||||||
team,
|
effectiveTeam,
|
||||||
targetUserId,
|
targetUserId,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export async function POST(request: Request) { // Changed to POST, similar to te
|
|||||||
SELECT
|
SELECT
|
||||||
national_id,
|
national_id,
|
||||||
name,
|
name,
|
||||||
|
role,
|
||||||
in_shelter,
|
in_shelter,
|
||||||
last_updated,
|
last_updated,
|
||||||
is_admin,
|
is_admin,
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
import { NextResponse } from "next/server"
|
import { NextResponse } from "next/server"
|
||||||
import os from "os"
|
import os from "os"
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic"
|
||||||
|
export const revalidate = 0
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
return NextResponse.json({ instance: os.hostname() })
|
return NextResponse.json(
|
||||||
|
{ instance: os.hostname() },
|
||||||
|
{ headers: { "Cache-Control": "no-store" } },
|
||||||
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Instance fetch error:", error)
|
console.error("Instance fetch error:", error)
|
||||||
return NextResponse.json({ error: "Failed to load instance name." }, { status: 500 })
|
return NextResponse.json({ error: "Failed to load instance name." }, { status: 500 })
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ export default function DashboardPage() {
|
|||||||
<br/>
|
<br/>
|
||||||
גרסה: {process.env.APPVERSION || "לא הוגדרה גרסה ב-Dockerfile!"}
|
גרסה: {process.env.APPVERSION || "לא הוגדרה גרסה ב-Dockerfile!"}
|
||||||
<br/>
|
<br/>
|
||||||
2025 COPYRIGHT TR-WEB
|
2026 COPYRIGHT TR-WEB
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import type React from "react"
|
import type React from "react"
|
||||||
import type { Metadata } from "next"
|
import type { Metadata } from "next"
|
||||||
import { Inter } from "next/font/google"
|
import { IBM_Plex_Sans_Hebrew } from "next/font/google"
|
||||||
import "./globals.css"
|
import "./globals.css"
|
||||||
|
|
||||||
const inter = Inter({ subsets: ["latin"] })
|
const ibmPlexSansHebrew = IBM_Plex_Sans_Hebrew({
|
||||||
|
subsets: ["hebrew"],
|
||||||
|
weight: ["100", "200", "300", "400", "500", "600", "700"],
|
||||||
|
})
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'ממ"ד',
|
title: 'ממ"ד',
|
||||||
@@ -22,7 +25,7 @@ export default function RootLayout({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="he" dir="rtl">
|
<html lang="he" dir="rtl">
|
||||||
<body className={inter.className}>{children}</body>
|
<body className={ibmPlexSansHebrew.className}>{children}</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface ManagedTypeOption {
|
|||||||
interface UserScopeModalUser {
|
interface UserScopeModalUser {
|
||||||
national_id: string
|
national_id: string
|
||||||
name: string
|
name: string
|
||||||
|
role?: string
|
||||||
field?: string
|
field?: string
|
||||||
department?: string
|
department?: string
|
||||||
team?: string
|
team?: string
|
||||||
@@ -44,12 +45,18 @@ export function UserScopeModal({
|
|||||||
const [field, setField] = useState("")
|
const [field, setField] = useState("")
|
||||||
const [department, setDepartment] = useState("")
|
const [department, setDepartment] = useState("")
|
||||||
const [team, setTeam] = useState("")
|
const [team, setTeam] = useState("")
|
||||||
|
const noDepartmentValue = "__no_department__"
|
||||||
|
const noTeamValue = "__no_team__"
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user && isOpen) {
|
if (user && isOpen) {
|
||||||
setField(user.field || "")
|
setField(user.field || "")
|
||||||
setDepartment(user.department || "")
|
if (user.role === "field_admin") {
|
||||||
setTeam(user.team || "")
|
setDepartment(user.department || noDepartmentValue)
|
||||||
|
} else {
|
||||||
|
setDepartment(user.department || "")
|
||||||
|
}
|
||||||
|
setTeam(user.team || noTeamValue)
|
||||||
}
|
}
|
||||||
}, [user, isOpen])
|
}, [user, isOpen])
|
||||||
|
|
||||||
@@ -58,25 +65,30 @@ export function UserScopeModal({
|
|||||||
? departments.filter((item) => item.parentId === selectedFieldId)
|
? departments.filter((item) => item.parentId === selectedFieldId)
|
||||||
: departments
|
: departments
|
||||||
const selectedDepartmentId = departments.find((item) => item.name === department)?.id
|
const selectedDepartmentId = departments.find((item) => item.name === department)?.id
|
||||||
const availableTeams = selectedDepartmentId
|
const availableTeams =
|
||||||
? teams.filter((item) => item.parentId === selectedDepartmentId)
|
selectedDepartmentId && department !== noDepartmentValue
|
||||||
: teams
|
? teams.filter((item) => item.parentId === selectedDepartmentId)
|
||||||
|
: []
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (field && availableDepartments.length > 0) {
|
if (field && availableDepartments.length > 0) {
|
||||||
const hasDepartment = availableDepartments.some((item) => item.name === department)
|
const hasDepartment = availableDepartments.some((item) => item.name === department)
|
||||||
if (!hasDepartment) {
|
if (!hasDepartment) {
|
||||||
setDepartment("")
|
setDepartment(user?.role === "field_admin" ? noDepartmentValue : "")
|
||||||
setTeam("")
|
setTeam(noTeamValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [field, availableDepartments, department])
|
}, [field, availableDepartments, department])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (department === noDepartmentValue) {
|
||||||
|
setTeam(noTeamValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
if (department && availableTeams.length > 0) {
|
if (department && availableTeams.length > 0) {
|
||||||
const hasTeam = availableTeams.some((item) => item.name === team)
|
const hasTeam = availableTeams.some((item) => item.name === team)
|
||||||
if (!hasTeam) {
|
if (!hasTeam) {
|
||||||
setTeam("")
|
setTeam(noTeamValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [department, availableTeams, team])
|
}, [department, availableTeams, team])
|
||||||
@@ -84,8 +96,16 @@ export function UserScopeModal({
|
|||||||
if (!user) return null
|
if (!user) return null
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
if (!field || !department || !team) return
|
if (!field) return
|
||||||
await onSave({ userId: user.national_id, field, department, team })
|
const normalizedDepartment = department === noDepartmentValue ? "" : department
|
||||||
|
const normalizedTeam = team === noTeamValue ? "" : team
|
||||||
|
const scopedDepartment = user.role === "field_admin" ? "" : normalizedDepartment
|
||||||
|
const scopedTeam = user.role === "field_admin" ? "" : normalizedTeam
|
||||||
|
|
||||||
|
if (user.role !== "field_admin" && !scopedDepartment) return
|
||||||
|
if ((user.role === "team_admin" || user.role === "user") && !scopedTeam) return
|
||||||
|
|
||||||
|
await onSave({ userId: user.national_id, field, department: scopedDepartment, team: scopedTeam })
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -104,7 +124,7 @@ export function UserScopeModal({
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Select value={field} onValueChange={setField}>
|
<Select value={field} onValueChange={setField}>
|
||||||
<SelectTrigger dir="rtl">
|
<SelectTrigger dir="rtl">
|
||||||
<SelectValue placeholder="בחרו תחום" />
|
<SelectValue placeholder="??? ????" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent dir="rtl">
|
<SelectContent dir="rtl">
|
||||||
{fields.map((item) => (
|
{fields.map((item) => (
|
||||||
@@ -119,7 +139,7 @@ export function UserScopeModal({
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Select value={department} onValueChange={setDepartment}>
|
<Select value={department} onValueChange={setDepartment}>
|
||||||
<SelectTrigger dir="rtl">
|
<SelectTrigger dir="rtl">
|
||||||
<SelectValue placeholder="בחרו מסגרת" />
|
<SelectValue placeholder="??? ?????" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent dir="rtl">
|
<SelectContent dir="rtl">
|
||||||
{availableDepartments.map((item) => (
|
{availableDepartments.map((item) => (
|
||||||
@@ -127,6 +147,9 @@ export function UserScopeModal({
|
|||||||
{item.name}
|
{item.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
{user.role === "field_admin" && (
|
||||||
|
<SelectItem value={noDepartmentValue}>??? ?????</SelectItem>
|
||||||
|
)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
@@ -134,7 +157,7 @@ export function UserScopeModal({
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Select value={team} onValueChange={setTeam}>
|
<Select value={team} onValueChange={setTeam}>
|
||||||
<SelectTrigger dir="rtl">
|
<SelectTrigger dir="rtl">
|
||||||
<SelectValue placeholder="בחרו צוות" />
|
<SelectValue placeholder="??? ????" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent dir="rtl">
|
<SelectContent dir="rtl">
|
||||||
{availableTeams.map((item) => (
|
{availableTeams.map((item) => (
|
||||||
@@ -142,6 +165,7 @@ export function UserScopeModal({
|
|||||||
{item.name}
|
{item.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
<SelectItem value={noTeamValue}>ללא צוות</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
@@ -153,7 +177,12 @@ export function UserScopeModal({
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
disabled={isSaving || !field || !department || !team}
|
disabled={
|
||||||
|
isSaving ||
|
||||||
|
!field ||
|
||||||
|
(user.role !== "field_admin" && !department) ||
|
||||||
|
((user.role === "team_admin" || user.role === "user") && !team)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{isSaving ? "שומר..." : "שמירה"}
|
{isSaving ? "שומר..." : "שמירה"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
env: {
|
env: {
|
||||||
APPVERSION: '1.1.0'
|
APPVERSION: '1.1.4'
|
||||||
},
|
},
|
||||||
experimental: {
|
experimental: {
|
||||||
serverComponentsExternalPackages: ['mysql2']
|
serverComponentsExternalPackages: ['mysql2']
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mamad-app",
|
"name": "mamad-app",
|
||||||
"version": "1.0.4",
|
"version": "1.1.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
.text-balance {
|
.text-balance {
|
||||||
|
|||||||
Reference in New Issue
Block a user