diff --git a/.importlinter b/.importlinter index d964dc3c..e45331be 100644 --- a/.importlinter +++ b/.importlinter @@ -15,6 +15,7 @@ source_modules = modules.placement modules.directory modules.accesscontrol + modules.elm forbidden_modules = modules.placement.models modules.placement.services @@ -25,6 +26,10 @@ forbidden_modules = modules.directory.services modules.directory.api modules.accesscontrol.models + modules.elm.models + modules.elm.services + modules.elm.api + modules.elm.domain # DIRECT imports only. contracts.py legitimately imports its OWN module's # models and services, so following the chain transitively would make this # contract impossible to satisfy. What we forbid is a module reaching straight @@ -38,6 +43,7 @@ type = forbidden source_modules = # Every module's domain, not just the first one written. modules.placement.domain + modules.elm.domain forbidden_modules = django rest_framework diff --git a/client/src/app/registry.ts b/client/src/app/registry.ts index e76d32bf..62a76ddf 100644 --- a/client/src/app/registry.ts +++ b/client/src/app/registry.ts @@ -17,4 +17,9 @@ export const MODULE_REGISTRY: Record = { basePath: "/placement", load: () => import("../modules/placement/routes"), }, + elm: { + code: "elm", + basePath: "/elm", + load: () => import("../modules/elm/routes"), + }, }; diff --git a/client/src/modules/elm/LeaveApplication.tsx b/client/src/modules/elm/LeaveApplication.tsx new file mode 100644 index 00000000..6d88a800 --- /dev/null +++ b/client/src/modules/elm/LeaveApplication.tsx @@ -0,0 +1,128 @@ +import { type FormEvent, useState } from "react"; +import { submitLeaveApplication } from "./api"; + +const LeaveApplication = () => { + const [leaveType, setLeaveType] = useState("1"); + const [startDate, setStartDate] = useState(""); + const [endDate, setEndDate] = useState(""); + const [reason, setReason] = useState(""); + const [loading, setLoading] = useState(false); + const [feedback, setFeedback] = useState<{ type: "success" | "error"; message: string } | null>(null); + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + setLoading(true); + setFeedback(null); + try { + await submitLeaveApplication({ + leave_type: parseInt(leaveType, 10), + start_date: startDate, + end_date: endDate, + reason, + }); + setFeedback({ type: "success", message: "Leave application submitted successfully!" }); + setStartDate(""); + setEndDate(""); + setReason(""); + } catch (err: any) { + setFeedback({ + type: "error", + message: err.response?.data?.detail || "Failed to submit leave application.", + }); + } finally { + setLoading(false); + } + }; + + return ( +
+

Apply for Leave

+ + {feedback && ( +
+ {feedback.message} +
+ )} + +
+
+ + +
+ +
+ + setStartDate(e.target.value)} + style={{ width: "100%", padding: "0.5rem", marginTop: "0.25rem" }} + /> +
+ +
+ + setEndDate(e.target.value)} + style={{ width: "100%", padding: "0.5rem", marginTop: "0.25rem" }} + /> +
+ +
+ +