-
Dashboard
-
-
Blog
-
-
Department
+
+
+
GG Dashboard
+
+
+
+
);
}
diff --git a/frontend/src/components/ui/dialog.tsx b/frontend/src/components/ui/dialog.tsx
new file mode 100644
index 0000000..f4cbece
--- /dev/null
+++ b/frontend/src/components/ui/dialog.tsx
@@ -0,0 +1,163 @@
+import * as React from "react"
+import { Dialog as DialogPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import { XIcon } from "lucide-react"
+
+function Dialog({
+ ...props
+}: React.ComponentProps
) {
+ return
+}
+
+function DialogTrigger({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DialogPortal({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DialogClose({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DialogOverlay({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DialogContent({
+ className,
+ children,
+ showCloseButton = true,
+ ...props
+}: React.ComponentProps & {
+ showCloseButton?: boolean
+}) {
+ return (
+
+
+
+ {children}
+ {showCloseButton && (
+
+
+
+ )}
+
+
+ )
+}
+
+function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function DialogFooter({
+ className,
+ showCloseButton = false,
+ children,
+ ...props
+}: React.ComponentProps<"div"> & {
+ showCloseButton?: boolean
+}) {
+ return (
+
+ {children}
+ {showCloseButton && (
+
+
+
+ )}
+
+ )
+}
+
+function DialogTitle({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DialogDescription({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogOverlay,
+ DialogPortal,
+ DialogTitle,
+ DialogTrigger,
+}
diff --git a/frontend/src/components/ui/switch.tsx b/frontend/src/components/ui/switch.tsx
new file mode 100644
index 0000000..fcd7310
--- /dev/null
+++ b/frontend/src/components/ui/switch.tsx
@@ -0,0 +1,31 @@
+import * as React from "react";
+import {Switch as SwitchPrimitive} from "radix-ui";
+
+import {cn} from "@/lib/utils";
+
+function Switch({
+ className,
+ size = "default",
+ ...props
+}: React.ComponentProps & {
+ size?: "sm" | "default";
+}) {
+ return (
+
+
+
+ );
+}
+
+export {Switch};
diff --git a/frontend/src/components/ui/table.tsx b/frontend/src/components/ui/table.tsx
new file mode 100644
index 0000000..1017c00
--- /dev/null
+++ b/frontend/src/components/ui/table.tsx
@@ -0,0 +1,114 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Table({ className, ...props }: React.ComponentProps<"table">) {
+ return (
+
+ )
+}
+
+function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
+ return (
+
+ )
+}
+
+function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
+ return (
+
+ )
+}
+
+function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
+ return (
+ tr]:last:border-b-0",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
+ return (
+
+ )
+}
+
+function TableHead({ className, ...props }: React.ComponentProps<"th">) {
+ return (
+ |
+ )
+}
+
+function TableCell({ className, ...props }: React.ComponentProps<"td">) {
+ return (
+ |
+ )
+}
+
+function TableCaption({
+ className,
+ ...props
+}: React.ComponentProps<"caption">) {
+ return (
+
+ )
+}
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}
diff --git a/frontend/src/components/ui/textarea.tsx b/frontend/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..04d27f7
--- /dev/null
+++ b/frontend/src/components/ui/textarea.tsx
@@ -0,0 +1,18 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
+ return (
+
+ )
+}
+
+export { Textarea }
diff --git a/frontend/src/index.css b/frontend/src/index.css
index b5c61c9..9451a9c 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -1,3 +1,124 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
+@import "tailwindcss";
+@import "tw-animate-css";
+@import "shadcn/tailwind.css";
+
+@custom-variant dark (&:is(.dark *));
+
+@theme inline {
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+ --radius-2xl: calc(var(--radius) + 8px);
+ --radius-3xl: calc(var(--radius) + 12px);
+ --radius-4xl: calc(var(--radius) + 16px);
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-popover: var(--popover);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+ --color-chart-1: var(--chart-1);
+ --color-chart-2: var(--chart-2);
+ --color-chart-3: var(--chart-3);
+ --color-chart-4: var(--chart-4);
+ --color-chart-5: var(--chart-5);
+ --color-sidebar: var(--sidebar);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-ring: var(--sidebar-ring);
+}
+
+:root {
+ --radius: 0.625rem;
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.141 0.005 285.823);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.141 0.005 285.823);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.141 0.005 285.823);
+ --primary: oklch(0.21 0.006 285.885);
+ --primary-foreground: oklch(0.985 0 0);
+ --secondary: oklch(0.967 0.001 286.375);
+ --secondary-foreground: oklch(0.21 0.006 285.885);
+ --muted: oklch(0.967 0.001 286.375);
+ --muted-foreground: oklch(0.552 0.016 285.938);
+ --accent: oklch(0.967 0.001 286.375);
+ --accent-foreground: oklch(0.21 0.006 285.885);
+ --destructive: oklch(0.577 0.245 27.325);
+ --border: oklch(0.92 0.004 286.32);
+ --input: oklch(0.92 0.004 286.32);
+ --ring: oklch(0.705 0.015 286.067);
+ --chart-1: oklch(0.646 0.222 41.116);
+ --chart-2: oklch(0.6 0.118 184.704);
+ --chart-3: oklch(0.398 0.07 227.392);
+ --chart-4: oklch(0.828 0.189 84.429);
+ --chart-5: oklch(0.769 0.188 70.08);
+ --sidebar: oklch(0.985 0 0);
+ --sidebar-foreground: oklch(0.141 0.005 285.823);
+ --sidebar-primary: oklch(0.21 0.006 285.885);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.967 0.001 286.375);
+ --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
+ --sidebar-border: oklch(0.92 0.004 286.32);
+ --sidebar-ring: oklch(0.705 0.015 286.067);
+}
+
+.dark {
+ --background: oklch(0.141 0.005 285.823);
+ --foreground: oklch(0.985 0 0);
+ --card: oklch(0.21 0.006 285.885);
+ --card-foreground: oklch(0.985 0 0);
+ --popover: oklch(0.21 0.006 285.885);
+ --popover-foreground: oklch(0.985 0 0);
+ --primary: oklch(0.92 0.004 286.32);
+ --primary-foreground: oklch(0.21 0.006 285.885);
+ --secondary: oklch(0.274 0.006 286.033);
+ --secondary-foreground: oklch(0.985 0 0);
+ --muted: oklch(0.274 0.006 286.033);
+ --muted-foreground: oklch(0.705 0.015 286.067);
+ --accent: oklch(0.274 0.006 286.033);
+ --accent-foreground: oklch(0.985 0 0);
+ --destructive: oklch(0.704 0.191 22.216);
+ --border: oklch(1 0 0 / 10%);
+ --input: oklch(1 0 0 / 15%);
+ --ring: oklch(0.552 0.016 285.938);
+ --chart-1: oklch(0.488 0.243 264.376);
+ --chart-2: oklch(0.696 0.17 162.48);
+ --chart-3: oklch(0.769 0.188 70.08);
+ --chart-4: oklch(0.627 0.265 303.9);
+ --chart-5: oklch(0.645 0.246 16.439);
+ --sidebar: oklch(0.21 0.006 285.885);
+ --sidebar-foreground: oklch(0.985 0 0);
+ --sidebar-primary: oklch(0.488 0.243 264.376);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.274 0.006 286.033);
+ --sidebar-accent-foreground: oklch(0.985 0 0);
+ --sidebar-border: oklch(1 0 0 / 10%);
+ --sidebar-ring: oklch(0.552 0.016 285.938);
+}
+
+@layer base {
+ * {
+ @apply border-border outline-ring/50;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
diff --git a/frontend/src/layouts/DashboardLayout.tsx b/frontend/src/layouts/DashboardLayout.tsx
new file mode 100644
index 0000000..2a75c56
--- /dev/null
+++ b/frontend/src/layouts/DashboardLayout.tsx
@@ -0,0 +1,18 @@
+import {Outlet} from "react-router-dom";
+
+import Sidebar from "@/components/layout/Sidebar";
+import Header from "@/components/layout/Header";
+
+export default function DashboardLayout() {
+ return (
+
+ );
+}
diff --git a/frontend/src/pages/Blog.tsx b/frontend/src/pages/Blog.tsx
index 684de10..14b3994 100644
--- a/frontend/src/pages/Blog.tsx
+++ b/frontend/src/pages/Blog.tsx
@@ -1,13 +1,7 @@
-import DashboardLayout from "@/components/layout/DashboardLayout";
+import React from "react";
-export default function Blog() {
- return (
-
- Blog Management
-
-
-
- );
+function Blog() {
+ return Blog
;
}
+
+export default Blog;
diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx
deleted file mode 100644
index 39d8383..0000000
--- a/frontend/src/pages/Dashboard.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import DashboardLayout from "@/components/layout/DashboardLayout";
-
-export default function Dashboard() {
- return (
-
- Dashboard
-
-
-
Blogs
-
-
Departments
-
-
Users
-
-
- );
-}
diff --git a/frontend/src/pages/Department.jsx b/frontend/src/pages/Department.jsx
deleted file mode 100644
index 0151e4c..0000000
--- a/frontend/src/pages/Department.jsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import DashboardLayout from "@/components/layout/DashboardLayout";
-
-export default function Department() {
- return (
-
- Departments
-
- );
-}
diff --git a/frontend/src/pages/Department.tsx b/frontend/src/pages/Department.tsx
new file mode 100644
index 0000000..c6c93dc
--- /dev/null
+++ b/frontend/src/pages/Department.tsx
@@ -0,0 +1,349 @@
+import {useState, useEffect, useCallback} from "react";
+import {AxiosError} from "axios";
+
+import {
+ getDepartmentsApi,
+ createDepartmentApi,
+ updateDepartmentApi,
+ deleteDepartmentApi,
+} from "@/api/department";
+
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+
+import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card";
+import {Button} from "@/components/ui/button";
+
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogFooter,
+} from "@/components/ui/dialog";
+
+import {Input} from "@/components/ui/input";
+import {Textarea} from "@/components/ui/textarea";
+
+import {Loader2, RefreshCw, Plus, Pencil, Trash} from "lucide-react";
+
+interface Department {
+ id?: number;
+ departmentId: string;
+ name: string;
+ para1: string;
+ para2: string;
+ para3: string;
+ facilities: string;
+ services: string;
+}
+
+export default function DepartmentPage() {
+ const [departments, setDepartments] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState("");
+
+ const [openModal, setOpenModal] = useState(false);
+ const [editing, setEditing] = useState(null);
+
+ const [form, setForm] = useState({
+ departmentId: "",
+ name: "",
+ para1: "",
+ para2: "",
+ para3: "",
+ facilities: "",
+ services: "",
+ });
+
+ /* ---------------- FETCH ---------------- */
+
+ const fetchDepartments = useCallback(async () => {
+ setLoading(true);
+ setError("");
+
+ try {
+ const res = await getDepartmentsApi();
+ setDepartments(res?.data || []);
+ } catch (err) {
+ if (err instanceof AxiosError) {
+ setError(err.response?.data?.message || "Failed to load departments");
+ } else {
+ setError("Something went wrong");
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ fetchDepartments();
+ }, [fetchDepartments]);
+
+ /* ---------------- FORM ---------------- */
+
+ function handleChange(
+ e: React.ChangeEvent,
+ ) {
+ setForm({
+ ...form,
+ [e.target.name]: e.target.value,
+ });
+ }
+
+ function openAdd() {
+ setEditing(null);
+
+ setForm({
+ departmentId: "",
+ name: "",
+ para1: "",
+ para2: "",
+ para3: "",
+ facilities: "",
+ services: "",
+ });
+
+ setOpenModal(true);
+ }
+
+ function openEdit(dep: Department) {
+ setEditing(dep);
+ setForm(dep);
+ setOpenModal(true);
+ }
+
+ async function handleSubmit() {
+ try {
+ if (editing) {
+ await updateDepartmentApi(editing.id!, form);
+ } else {
+ await createDepartmentApi(form);
+ }
+
+ setOpenModal(false);
+ fetchDepartments();
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ /* ---------------- DELETE ---------------- */
+
+ async function handleDelete(id: number) {
+ const confirmDelete = confirm("Delete this department?");
+ if (!confirmDelete) return;
+
+ try {
+ await deleteDepartmentApi(id);
+ fetchDepartments();
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ /* ---------------- UI ---------------- */
+
+ return (
+
+ {/* Header */}
+
+
+
Departments
+
+
+
+
+
+
+
+
+ {/* Error */}
+
+ {error && (
+
+ {error}
+
+ )}
+
+ {/* Table */}
+
+
+
+ Department List
+
+
+
+
+
+
+
+ ID
+ Name
+ Para1
+ Para2
+ Para3
+ Facilities
+ Services
+ Actions
+
+
+
+
+ {loading ? (
+
+
+
+
+
+ ) : departments.length === 0 ? (
+
+
+ No departments found
+
+
+ ) : (
+ departments.map((dep) => (
+
+ {dep.departmentId}
+
+ {dep.name}
+
+
+ {dep.para1}
+
+
+
+ {dep.para2}
+
+
+
+ {dep.para3}
+
+
+
+ {dep.facilities}
+
+
+
+ {dep.services}
+
+
+
+
+
+
+
+
+ ))
+ )}
+
+
+
+
+
+
+ {/* MODAL */}
+
+
+
+ );
+}
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index f7de5e9..50eb8a2 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -1,8 +1,4 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
- theme: {
- extend: {},
- },
- plugins: [],
};