Skip to content

Commit 74f7040

Browse files
committed
department details
1 parent 6f6e006 commit 74f7040

3 files changed

Lines changed: 259 additions & 0 deletions

File tree

src/App.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { authProvider } from "./providers/auth";
2626
import { Login } from "./pages/login";
2727
import { Register } from "./pages/register";
2828
import DepartmentsList from "./pages/departments/list";
29+
import DepartmentShow from "./pages/departments/show";
2930
import FacultyList from "./pages/faculty/list";
3031

3132
function App() {
@@ -65,6 +66,7 @@ function App() {
6566
{
6667
name: "departments",
6768
list: "/departments",
69+
show: "/departments/show/:id",
6870
meta: {
6971
label: "Departments",
7072
icon: <Building2 />,
@@ -120,6 +122,7 @@ function App() {
120122

121123
<Route path="departments">
122124
<Route index element={<DepartmentsList />} />
125+
<Route path="show/:id" element={<DepartmentShow />} />
123126
</Route>
124127

125128
<Route path="faculty">

src/pages/departments/list.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Input } from "@/components/ui/input";
88
import { ListView } from "@/components/refine-ui/views/list-view";
99
import { Breadcrumb } from "@/components/refine-ui/layout/breadcrumb";
1010
import { DataTable } from "@/components/refine-ui/data-table/data-table";
11+
import { ShowButton } from "@/components/refine-ui/buttons/show";
1112

1213
type DepartmentListItem = {
1314
id: number;
@@ -72,6 +73,21 @@ const DepartmentsList = () => {
7273
);
7374
},
7475
},
76+
{
77+
id: "details",
78+
size: 140,
79+
header: () => <p className="column-title">Details</p>,
80+
cell: ({ row }) => (
81+
<ShowButton
82+
resource="departments"
83+
recordItemId={row.original.id}
84+
variant="outline"
85+
size="sm"
86+
>
87+
View
88+
</ShowButton>
89+
),
90+
},
7591
],
7692
[]
7793
);

src/pages/departments/show.tsx

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
import { useLink, useShow } from "@refinedev/core";
2+
3+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
4+
import { Badge } from "@/components/ui/badge";
5+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
6+
import {
7+
Table,
8+
TableBody,
9+
TableCell,
10+
TableHead,
11+
TableHeader,
12+
TableRow,
13+
} from "@/components/ui/table";
14+
import {
15+
ShowView,
16+
ShowViewHeader,
17+
} from "@/components/refine-ui/views/show-view";
18+
import type { Department, Subject, User } from "@/types";
19+
20+
type DepartmentDetails = {
21+
department: Department;
22+
subjects: Array<Subject & { totalClasses: number }>;
23+
classes: Array<{
24+
id: number;
25+
name: string;
26+
status?: "active" | "inactive";
27+
subject?: { name: string };
28+
teacher?: Pick<User, "id" | "name" | "email" | "image">;
29+
}>;
30+
enrolledStudents: Array<Pick<User, "id" | "name" | "email" | "image">>;
31+
totals: {
32+
subjects: number;
33+
classes: number;
34+
enrolledStudents: number;
35+
};
36+
};
37+
38+
const DepartmentShow = () => {
39+
const Link = useLink();
40+
const { query } = useShow<DepartmentDetails>({
41+
resource: "departments",
42+
});
43+
44+
const details = query.data?.data;
45+
46+
if (query.isLoading || query.isError || !details) {
47+
return (
48+
<ShowView className="class-view">
49+
<ShowViewHeader resource="departments" title="Department Details" />
50+
<p className="text-sm text-muted-foreground">
51+
{query.isLoading
52+
? "Loading department details..."
53+
: query.isError
54+
? "Failed to load department details."
55+
: "Department details not found."}
56+
</p>
57+
</ShowView>
58+
);
59+
}
60+
61+
return (
62+
<ShowView className="class-view space-y-6">
63+
<ShowViewHeader resource="departments" title={details.department.name} />
64+
65+
<div>
66+
<p className="text-sm text-muted-foreground">
67+
{details.department.description ?? "No description provided."}
68+
</p>
69+
</div>
70+
71+
<div className="grid gap-4 sm:grid-cols-3">
72+
<Card>
73+
<CardHeader>
74+
<CardTitle className="text-sm text-muted-foreground">
75+
Total Subjects
76+
</CardTitle>
77+
</CardHeader>
78+
<CardContent>
79+
<div className="text-2xl font-semibold">
80+
{details.totals.subjects}
81+
</div>
82+
</CardContent>
83+
</Card>
84+
<Card>
85+
<CardHeader>
86+
<CardTitle className="text-sm text-muted-foreground">
87+
Total Classes
88+
</CardTitle>
89+
</CardHeader>
90+
<CardContent>
91+
<div className="text-2xl font-semibold">
92+
{details.totals.classes}
93+
</div>
94+
</CardContent>
95+
</Card>
96+
<Card>
97+
<CardHeader>
98+
<CardTitle className="text-sm text-muted-foreground">
99+
Enrolled Students
100+
</CardTitle>
101+
</CardHeader>
102+
<CardContent>
103+
<div className="text-2xl font-semibold">
104+
{details.totals.enrolledStudents}
105+
</div>
106+
</CardContent>
107+
</Card>
108+
</div>
109+
110+
<Card>
111+
<CardHeader>
112+
<CardTitle>Subjects</CardTitle>
113+
</CardHeader>
114+
<CardContent>
115+
<Table>
116+
<TableHeader>
117+
<TableRow>
118+
<TableHead>Code</TableHead>
119+
<TableHead>Name</TableHead>
120+
<TableHead>Description</TableHead>
121+
<TableHead>Classes</TableHead>
122+
</TableRow>
123+
</TableHeader>
124+
<TableBody>
125+
{details.subjects.length === 0 && (
126+
<TableRow>
127+
<TableCell colSpan={4} className="text-muted-foreground">
128+
No subjects assigned.
129+
</TableCell>
130+
</TableRow>
131+
)}
132+
{details.subjects.map((subject) => (
133+
<TableRow key={subject.id}>
134+
<TableCell>
135+
<Badge variant="secondary">{subject.code}</Badge>
136+
</TableCell>
137+
<TableCell>{subject.name}</TableCell>
138+
<TableCell className="text-muted-foreground">
139+
{subject.description ?? "No description"}
140+
</TableCell>
141+
<TableCell>
142+
<Badge>{subject.totalClasses}</Badge>
143+
</TableCell>
144+
</TableRow>
145+
))}
146+
</TableBody>
147+
</Table>
148+
</CardContent>
149+
</Card>
150+
151+
<Card>
152+
<CardHeader>
153+
<CardTitle>Classes</CardTitle>
154+
</CardHeader>
155+
<CardContent>
156+
<Table>
157+
<TableHeader>
158+
<TableRow>
159+
<TableHead>Name</TableHead>
160+
<TableHead>Subject</TableHead>
161+
<TableHead>Teacher</TableHead>
162+
<TableHead>Status</TableHead>
163+
</TableRow>
164+
</TableHeader>
165+
<TableBody>
166+
{details.classes.length === 0 && (
167+
<TableRow>
168+
<TableCell colSpan={4} className="text-muted-foreground">
169+
No classes available.
170+
</TableCell>
171+
</TableRow>
172+
)}
173+
{details.classes.map((item) => (
174+
<TableRow key={item.id}>
175+
<TableCell>
176+
<Link to={`/classes/show/${item.id}`}>{item.name}</Link>
177+
</TableCell>
178+
<TableCell>{item.subject?.name ?? "Unassigned"}</TableCell>
179+
<TableCell>{item.teacher?.name ?? "Unassigned"}</TableCell>
180+
<TableCell>
181+
<Badge
182+
variant={
183+
item.status === "active" ? "default" : "secondary"
184+
}
185+
>
186+
{item.status ?? "unknown"}
187+
</Badge>
188+
</TableCell>
189+
</TableRow>
190+
))}
191+
</TableBody>
192+
</Table>
193+
</CardContent>
194+
</Card>
195+
196+
<Card>
197+
<CardHeader>
198+
<CardTitle>Enrolled Students</CardTitle>
199+
</CardHeader>
200+
<CardContent>
201+
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
202+
{details.enrolledStudents.length === 0 && (
203+
<p className="text-sm text-muted-foreground">
204+
No enrolled students yet.
205+
</p>
206+
)}
207+
{details.enrolledStudents.map((student) => (
208+
<div
209+
key={student.id}
210+
className="flex items-center gap-3 rounded-md border border-border p-3"
211+
>
212+
<Avatar>
213+
{student.image && (
214+
<AvatarImage src={student.image} alt={student.name} />
215+
)}
216+
<AvatarFallback>{getInitials(student.name)}</AvatarFallback>
217+
</Avatar>
218+
<div>
219+
<p className="text-sm font-medium">{student.name}</p>
220+
<p className="text-xs text-muted-foreground">
221+
{student.email}
222+
</p>
223+
</div>
224+
</div>
225+
))}
226+
</div>
227+
</CardContent>
228+
</Card>
229+
</ShowView>
230+
);
231+
};
232+
233+
const getInitials = (name = "") => {
234+
const parts = name.trim().split(" ").filter(Boolean);
235+
if (parts.length === 0) return "";
236+
if (parts.length === 1) return parts[0][0]?.toUpperCase() ?? "";
237+
return `${parts[0][0] ?? ""}${parts[parts.length - 1][0] ?? ""}`.toUpperCase();
238+
};
239+
240+
export default DepartmentShow;

0 commit comments

Comments
 (0)