Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"dotenv": "^17.2.1",
"input-otp": "^1.4.2",
"lucide-react": "^0.510.0",
"moment": "^2.30.1",
"next-themes": "^0.4.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
32 changes: 24 additions & 8 deletions src/components/academic/courses/create-course.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import axios from "axios";
import { Plus } from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
import type { CourseData } from "types";
import type { CourseData, DepartmentData } from "@/types";
import { courses } from "../../../store/atoms/academics/courses";
import { useRecoilRefresher_UNSTABLE } from "recoil";
import { useDepartmentData } from "@/store/hooks/academics/use-department-data";

const base_api = import.meta.env.VITE_SERVER_BASE_URL;

const CreateCourse = () => {
const { departmentInfo, loading } = useDepartmentData();
const refreshCourse = useRecoilRefresher_UNSTABLE(courses);
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
Expand Down Expand Up @@ -67,6 +69,10 @@ const CreateCourse = () => {
}
};

if (loading) {
return <div>Loading department...</div>;
}

return (
<UseModel
isOpen={isCreateDialogOpen}
Expand Down Expand Up @@ -104,14 +110,24 @@ const CreateCourse = () => {
/>
</div>
<div className="grid gap-3">
<Label htmlFor="create-department">Department</Label>
<Input
id="create-department"
<Label htmlFor="department">Department</Label>
<Select
value={createCourse.department}
onChange={(e) => InputHandler(e, setCreateCourse)}
name="department"
placeholder="Enter department"
/>
onValueChange={(value) =>
SelectHandler("department", value, setCreateCourse)
}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select Department" />
</SelectTrigger>
<SelectContent>
{departmentInfo.Departments.map((department: DepartmentData) => (
<SelectItem key={department.id} value={department.id!}>
{department.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="grid gap-3">
<Label htmlFor="create-startdate">Start Date</Label>
Expand Down
40 changes: 25 additions & 15 deletions src/components/academic/courses/edit-course.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,20 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { InputHandler, SelectHandler } from "@/lib/utils";
import { FormateDate, InputHandler, SelectHandler } from "@/lib/utils";
import axios from "axios";
import { Edit } from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
import type { CourseData } from "types";
import type { CourseData, DepartmentData, EditCourseProps } from "@/types";
import { courses } from "../../../store/atoms/academics/courses";
import { useRecoilRefresher_UNSTABLE } from "recoil";
import { useDepartmentData } from "@/store/hooks/academics/use-department-data";

const base_api = import.meta.env.VITE_SERVER_BASE_URL;

interface EditCourseProps {
course: CourseData;
}

const EditCourse = ({ course }: EditCourseProps) => {
const { departmentInfo } = useDepartmentData();
const refreshCourse = useRecoilRefresher_UNSTABLE(courses);

const [editingCourse, setEditingCourse] = useState<CourseData | null>(null);
Expand Down Expand Up @@ -108,22 +106,34 @@ const EditCourse = ({ course }: EditCourseProps) => {
/>
</div>
<div className="grid gap-3">
<Label htmlFor="update-department">Department</Label>
<Input
id="update-department"
<Label htmlFor="department">Department</Label>
<Select
value={editingCourse.department}
onChange={(e) => InputHandler(e, setEditingCourse)}
name="department"
placeholder="Enter department"
/>
onValueChange={(value) =>
SelectHandler("department", value, setEditingCourse)
}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select Department" />
</SelectTrigger>
<SelectContent>
{departmentInfo.Departments.map(
(department: DepartmentData) => (
<SelectItem key={department.id} value={department.id!}>
{department.name}
</SelectItem>
)
)}
</SelectContent>
</Select>
</div>
<div className="grid gap-3">
<Label htmlFor="update-startdate">Start Date</Label>
<Input
id="update-startdate"
name="startdate"
type="date"
value={editingCourse.startdate}
value={FormateDate(editingCourse.startdate)}
onChange={(e) => InputHandler(e, setEditingCourse)}
/>
</div>
Expand All @@ -133,7 +143,7 @@ const EditCourse = ({ course }: EditCourseProps) => {
id="update-enddate"
name="enddate"
type="date"
value={editingCourse.enddate}
value={FormateDate(editingCourse.enddate)}
onChange={(e) => InputHandler(e, setEditingCourse)}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Badge } from "@/components/ui/badge";
import { Card, CardContent } from "../../ui/card";
import { Card, CardContent } from "@/components/ui/card";
import { useCoursesData } from "@/store/hooks/academics/use-courses-data";
import { FormateDate } from "@/lib/utils";
import { Skeleton } from "../../ui/skeleton";
import type { CourseData } from "../../../../types/index";
import { Skeleton } from "@/components/ui/skeleton";
import type { CourseData } from "@/types";
import EditCourse from "./edit-course";
import CreateCourse from "./create-course";
import { Button } from "@/components/ui/button";
Expand All @@ -12,7 +12,7 @@ import { ConfirmDialog } from "@/components/confirm-dialog";
import axios from "axios";
import { useState } from "react";
import { toast } from "sonner";
import { courses } from "../../../store/atoms/academics/courses";
import { courses } from "@/store/atoms/academics/courses";
import { useRecoilRefresher_UNSTABLE } from "recoil";

const base_api = import.meta.env.VITE_SERVER_BASE_URL;
Expand Down Expand Up @@ -64,7 +64,6 @@ const AcademinCoursesTab = () => {
<div className="space-y-4">
<div className="flex justify-between items-center">
<h3 className="text-lg font-medium">Courses Data</h3>

{/* CREATE COURSE MODAL */}
<CreateCourse />
</div>
Expand Down Expand Up @@ -110,8 +109,10 @@ const AcademinCoursesTab = () => {
</div>
</div>
<div className="flex gap-2 flex-wrap ">
{/* UPDATE COURSE MODAL */}
{/* update course */}
<EditCourse course={course} />

{/* delete course */}
<div>
<ConfirmDialog
title="Delete Course"
Expand Down
107 changes: 107 additions & 0 deletions src/components/academic/departments/create-department.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import UseModel from "@/components/models/useModel";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { InputHandler } from "@/lib/utils";
import axios from "axios";
import { Plus } from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
import type { DepartmentData } from "@/types";
import { department } from "../../../store/atoms/academics/department";
import { useRecoilRefresher_UNSTABLE } from "recoil";

const base_api = import.meta.env.VITE_SERVER_BASE_URL;

const CreateDepartment = () => {
const refreshDepartment = useRecoilRefresher_UNSTABLE(department);
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
const [isLoading, setIsLoading] = useState<boolean>(false);

const [createDepartment, setCreateDepartment] = useState<DepartmentData>({
name: "",
school: "",
established: "",
});

// Create department
const handleCreateSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);

try {
const response = await axios.post(
`${base_api}/api/v1/create-department`,
createDepartment,
{ withCredentials: true }
);
if (response.status === 200) {
toast.success("Department created successfully");
setCreateDepartment({
name: "",
school: "",
established: "",
});
refreshDepartment();
setIsCreateDialogOpen(false);
setIsLoading(false);
}
} catch (error: any) {
console.error(error?.message);
toast.error("Failed to create Department");
setIsCreateDialogOpen(false);
setIsLoading(false);
}
};

return (
<UseModel
isOpen={isCreateDialogOpen}
onOpenChange={setIsCreateDialogOpen}
handleSubmit={handleCreateSubmit}
DialogHeading="Add New Department"
DialogDescriptionText="Fill in the department details below. Click save when you're done."
dialogFooterCancel="Cancel"
dailogSubmitButtonText={isLoading ? "Loading..." : "Save Department"}
trigger={
<Button className="gap-2 cursor-pointer">
<Plus className="w-4 h-4" />
Add new
</Button>
}
>
<div className="grid gap-3">
<Label htmlFor="department-name">Department Name</Label>
<Input
id="department-name"
value={createDepartment.name}
onChange={(e) => InputHandler(e, setCreateDepartment)}
name="name"
placeholder="Enter department name"
/>
</div>
<div className="grid gap-3">
<Label htmlFor="create-school">School Name</Label>
<Input
id="create-school"
value={createDepartment.school}
onChange={(e) => InputHandler(e, setCreateDepartment)}
name="school"
placeholder="Enter school name"
/>
</div>
<div className="grid gap-3">
<Label htmlFor="established-at">Established At</Label>
<Input
id="established-at"
value={createDepartment.established}
onChange={(e) => InputHandler(e, setCreateDepartment)}
name="established"
placeholder="Enter established year"
/>
</div>
</UseModel>
);
};

export default CreateDepartment;
Loading