8000 display all classes · JavaScript-Mastery-Pro/classroom@0253a3d · GitHub
[go: up one dir, main page]

Skip to content

Commit 0253a3d

Browse files
committed
display all classes
1 parent 0be32ba commit 0253a3d

File tree

2 files changed

+274
-1
lines changed

2 files changed

+274
-1
lines changed

src/pages/classes/list.tsx

Lines changed: 268 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,272 @@
1+
import { Search } from "lucide-react";
2+
import { useMemo, useState } from "react";
3+
import { ColumnDef } from "@tanstack/react-table";
4+
import { useTable } from "@refinedev/react-table";
5+
import { useList } from "@refinedev/core";
6+
7+
import {
8+
Select,
9+
SelectContent,
10+
SelectItem,
11+
SelectTrigger,
12+
SelectValue,
13+
} from "@/components/ui/select";
14+
import { Badge } from "@/components/ui/badge";
15+
import { Input } from "@/components/ui/input";
16+
import { ListView } from "@/components/refine-ui/views/list-view";
17+
import { CreateButton } from "@/components/refine-ui/buttons/create";
18+
import { Breadcrumb } from "@/components/refine-ui/layout/breadcrumb";
19+
import { DataTable } from "@/components/refine-ui/data-table/data-table";
20+
21+
import { Subject, User } from "@/types";
22+
23+
type ClassListItem = {
24+
id: number;
25+
name: string;
26+
status: "active" | "inactive";
27+
bannerUrl?: string;
28+
subject?: {
29+
name: string;
30+
};
31+
teacher?: {
32+
name: string;
33+
};
34+
capacity: number;
35+
};
36+
137
const ClassesList = () => {
2-
return <div>ClassesList</div>;
38+
const [searchQuery, setSearchQuery] = useState("");
39+
const [selectedSubject, setSelectedSubject] = useState<string>("all");
40+
const [selectedTeacher, setSelectedTeacher] = useState<string>("all");
41+
42+
const classColumns = useMemo<ColumnDef<ClassListItem>[]>(
43+
() => [
44+
{
45+
id: "banner",
46+
accessorKey: "bannerUrl",
47+
size: 120,
48+
header: () => <p className="column-title ml-2">Banner</p>,
49+
cell: ({ getValue }) => {
50+
const bannerUrl = getValue<string>();
51+
52+
return bannerUrl ? (
53+
<img
54+
src={bannerUrl}
55+
alt="Class banner"
56+
className="ml-2 h-10 w-10 rounded-md object-cover"
57+
loading="lazy"
58+
/>
59+
) : (
60+
<span className="text-muted-foreground ml-2">No image</span>
61+
);
62+
},
63+
},
64+
{
65+
id: "name",
66+
accessorKey: "name",
67+
size: 220,
68+
header: () => <p className="column-title">Class Name</p>,
69+
cell: ({ getValue }) => {
70+
const className = getValue<string>();
71+
72+
return <span className="text-foreground">{className}</span>;
73+
},
74+
},
75+
{
76+
id: "status",
77+
accessorKey: "status",
78+
size: 140,
79+
header: () => <p className="column-title">Status</p>,
80+
cell: ({ getValue }) => {
81+
const status = getValue<"active" | "inactive">();
82+
const variant = status === "active" ? "default" : "secondary";
83+
84+
return <Badge variant={variant}>{status}</Badge>;
85+
},
86+
},
87+
{
88+
id: "subject",
89+
accessorKey: "subject.name",
90+
size: 200,
91+
header: () => <p className="column-title">Subject</p>,
92+
cell: ({ getValue }) => {
93+
const subjectName = getValue<string>();
94+
95+
return subjectName ? (
96+
<Badge variant="secondary">{subjectName}</Badge>
97+
) : (
98+
<span className="text-muted-foreground">Not set</span>
99+
);
100+
},
101+
},
102+
{
103+
id: "teacher",
104+
accessorKey: "teacher.name",
105+
size: 200,
106+
header: () => <p className="column-title">Teacher</p>,
107+
cell: ({ getValue }) => {
108+
const teacherName = getValue<string>();
109+
110+
return teacherName ? (
111+
<span className="text-foreground">{teacherName}</span>
112+
) : (
113+
<span className="text-muted-foreground">Not assigned</span>
114+
);
115+
},
116+
},
117+
{
118+
id: "capacity",
119+
accessorKey: "capacity",
120+
size: 120,
121+
header: () => <p className="column-title">Capacity</p>,
122+
cell: ({ getValue }) => {
123+
const capacity = getValue<number>();
124+
125+
return <span className="text-foreground">{capacity}</span>;
126+
},
127+
},
128+
],
129+
[]
130+
);
131+
132+
const { query: subjectsQuery } = useList<Subject>({
133+
resource: "subjects",
134+
pagination: {
135+
pageSize: 100,
136+
},
137+
});
138+
139+
const { query: teachersQuery } = useList<User>({
140+
resource: "users",
141+
filters: [
142+
{
143+
field: "role",
144+
operator: "eq",
145+
value: "teacher",
146+
},
147+
],
148+
pagination: {
149+
pageSize: 100,
150+
},
151+
});
152+
153+
const subjects = subjectsQuery.data?.data || [];
154+
const teachers = teachersQuery.data?.data || [];
155+
156+
const subjectFilters =
157+
selectedSubject === "all"
158+
? []
159+
: [
160+
{
161+
field: "subject",
162+
operator: "eq" as const,
163+
value: selectedSubject,
164+
},
165+
];
166+
167+
const teacherFilters =
168+
selectedTeacher === "all"
169+
? []
170+
: [
171+
{
172+
field: "teacher",
173+
operator: "eq" as const,
174+
value: selectedTeacher,
175+
},
176+
];
177+
178+
const searchFilters = searchQuery
179+
? [
180+
{
181+
field: "name",
182+
operator: "contains" as const,
183+
value: searchQuery,
184+
},
185+
]
186+
: [];
187+
188+
const classesTable = useTable<ClassListItem>({
189+
columns: classColumns,
190+
refineCoreProps: {
191+
resource: "classes",
192+
pagination: {
193+
pageSize: 10,
194+
mode: "server",
195+
},
196+
filters: {
197+
// Compose refine filters from the current UI selections.
198+
permanent: [...subjectFilters, ...teacherFilters, ...searchFilters],
199+
},
200+
sorters: {
201+
initial: [
202+
{
203+
field: "id",
204+
order: "desc",
205+
},
206+
],
207+
},
208+
},
209+
});
210+
211+
return (
212+
<ListView>
213+
<Breadcrumb />
214+
<h1 className="page-title">Classes</h1>
215+
216+
<div className="intro-row">
217+
<p>Quick access to essential metrics and management tools.</p>
218+
219+
<div className="actions-row">
220+
<div className="search-field">
221+
<Search className="search-icon" />
222+
<Input
223+
type="text"
224+
placeholder="Search by name..."
225+
className="pl-10 w-full"
226+
value={searchQuery}
227+
onChange={(event) => setSearchQuery(event.target.value)}
228+
/>
229+
</div>
230+
231+
<div className="flex gap-2 w-full sm:w-auto">
232+
<Select value={selectedSubject} onValueChange={setSelectedSubject}>
233+
<SelectTrigger>
234+
<SelectValue placeholder="Filter by subject" />
235+
</SelectTrigger>
236+
237+
<SelectContent>
238+
<SelectItem value="all">All Subjects</SelectItem>
239+
{subjects.map((subject) => (
240+
<SelectItem key={subject.id} value={subject.name}>
241+
{subject.name}
242+
</SelectItem>
243+
))}
244+
</SelectContent>
245+
</Select>
246+
247+
<Select value={selectedTeacher} onValueChange={setSelectedTeacher}>
248+
<SelectTrigger>
249+
<SelectValue placeholder="Filter by teacher" />
250+
</SelectTrigger>
251+
252+
<SelectContent>
253+
<SelectItem value="all">All Teachers</SelectItem>
254+
{teachers.map((teacher) => (
255+
<SelectItem key={teacher.id} value={teacher.name}>
256+
{teacher.name}
257+
</SelectItem>
258+
))}
259+
</SelectContent>
260+
</Select>
261+
262+
<CreateButton resource="classes" />
263+
</div>
264+
</div>
265+
</div>
266+
267+
<DataTable table={classesTable} />
268+
</ListView>
269+
);
3270
};
4271

5272
export default ClassesList;

src/providers/data.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ const options: CreateDataProviderOptions = {
2424
if (field === "department") params.department = value;
2525
if (field === "name" || field === "code") params.search = value;
2626
}
27+
28+
if (resource === "classes") {
29+< 5CC4 div class="diff-text-inner"> if (field === "name") params.search = value;
30+
if (field === "subject") params.subject = value;
31+
if (field === "teacher") params.teacher = value;
32+
}
2733
});
2834

2935
return params;

0 commit comments

Comments
 (0)
0