@@ -10,163 +10,163 @@ import { Skeleton } from '@/components/ui/skeleton';
1010import { adminApi } from '@/features/admin/api/admin-api' ;
1111
1212export function AdminCoursesPage ( ) {
13- const queryClient = useQueryClient ( ) ;
13+ const queryClient = useQueryClient ( ) ;
1414
15- const { data : courses , isLoading } = useQuery ( {
15+ const { data : courses , isLoading } = useQuery ( {
16+ queryKey : [ 'admin-pending-courses' ] ,
17+ queryFn : adminApi . getPendingCourses ,
18+ } ) ;
19+
20+ const approveMutation = useMutation ( {
21+ mutationFn : adminApi . approveCourse ,
22+ onSuccess : ( ) => {
23+ toast . success ( 'Course approved' ) ;
24+ void queryClient . invalidateQueries ( {
1625 queryKey : [ 'admin-pending-courses' ] ,
17- queryFn : adminApi . getPendingCourses ,
18- } ) ;
26+ } ) ;
27+ } ,
28+ } ) ;
1929
20- const approveMutation = useMutation ( {
21- mutationFn : adminApi . approveCourse ,
22- onSuccess : ( ) => {
23- toast . success ( 'Course approved ' ) ;
24- void queryClient . invalidateQueries ( {
25- queryKey : [ 'admin-pending-courses' ] ,
26- } ) ;
27- } ,
28- } ) ;
30+ const rejectMutation = useMutation ( {
31+ mutationFn : adminApi . rejectCourse ,
32+ onSuccess : ( ) => {
33+ toast . success ( 'Course rejected ' ) ;
34+ void queryClient . invalidateQueries ( {
35+ queryKey : [ 'admin-pending-courses' ] ,
36+ } ) ;
37+ } ,
38+ } ) ;
2939
30- const rejectMutation = useMutation ( {
31- mutationFn : adminApi . rejectCourse ,
32- onSuccess : ( ) => {
33- toast . success ( 'Course rejected' ) ;
34- void queryClient . invalidateQueries ( {
35- queryKey : [ 'admin-pending-courses' ] ,
36- } ) ;
37- } ,
38- } ) ;
40+ const handleApprove = ( id : string ) => {
41+ // eslint-disable-next-line no-alert
42+ if ( window . confirm ( 'Approve this course and publish it?' ) ) {
43+ approveMutation . mutate ( id ) ;
44+ }
45+ } ;
3946
40- const handleApprove = ( id : string ) => {
41- // eslint-disable-next-line no-alert
42- if ( window . confirm ( 'Approve this course and publish it ?' ) ) {
43- approveMutation . mutate ( id ) ;
44- }
45- } ;
47+ const handleReject = ( id : string ) => {
48+ // eslint-disable-next-line no-alert
49+ if ( window . confirm ( 'Reject this course?' ) ) {
50+ rejectMutation . mutate ( id ) ;
51+ }
52+ } ;
4653
47- const handleReject = ( id : string ) => {
48- // eslint-disable-next-line no-alert
49- if ( window . confirm ( 'Reject this course?' ) ) {
50- rejectMutation . mutate ( id ) ;
51- }
52- } ;
54+ return (
55+ < PageContainer >
56+ < div className = "space-y-8 animate-fade-in" >
57+ < div className = "flex items-center justify-between" >
58+ < div >
59+ < div className = "flex items-center gap-2 text-muted-foreground mb-2" >
60+ < Shield className = "w-5 h-5" />
61+ < span className = "text-sm font-medium" > Administration</ span >
62+ </ div >
63+ < h1 className = "text-3xl font-bold" > Course Moderation</ h1 >
64+ < p className = "text-muted-foreground" >
65+ Review incoming courses from instructors
66+ </ p >
67+ </ div >
68+ </ div >
5369
54- return (
55- < PageContainer >
56- < div className = "space-y-8 animate-fade-in" >
57- < div className = "flex items-center justify-between" >
58- < div >
59- < div className = "flex items-center gap-2 text-muted-foreground mb-2" >
60- < Shield className = "w-5 h-5" />
61- < span className = "text-sm font-medium" > Administration</ span >
62- </ div >
63- < h1 className = "text-3xl font-bold" > Course Moderation</ h1 >
64- < p className = "text-muted-foreground" >
65- Review incoming courses from instructors
66- </ p >
67- </ div >
68- </ div >
70+ < div className = "grid gap-6" >
71+ { isLoading && (
72+ < div className = "space-y-4" >
73+ { [ 1 , 2 ] . map ( ( i ) => (
74+ < Skeleton key = { i } className = "h-24 w-full rounded-xl" />
75+ ) ) }
76+ </ div >
77+ ) }
6978
70- < div className = "grid gap-6" >
71- { isLoading && (
72- < div className = "space-y-4" >
73- { [ 1 , 2 ] . map ( ( i ) => (
74- < Skeleton key = { i } className = "h-24 w-full rounded-xl" />
75- ) ) }
76- </ div >
77- ) }
79+ { ! isLoading && ( ! courses || courses . length === 0 ) && (
80+ < Card className = "p-12 text-center bg-muted/30 border-dashed" >
81+ < div className = "max-w-sm mx-auto space-y-4" >
82+ < div className = "w-16 h-16 mx-auto rounded-full bg-muted flex items-center justify-center" >
83+ < Check className = "w-8 h-8 text-green-500" />
84+ </ div >
85+ < h3 className = "text-xl font-semibold" > All Caught Up!</ h3 >
86+ < p className = "text-muted-foreground" >
87+ There are no pending courses to review at the moment.
88+ </ p >
89+ </ div >
90+ </ Card >
91+ ) }
7892
79- { ! isLoading && ( ! courses || courses . length === 0 ) && (
80- < Card className = "p-12 text-center bg-muted/30 border-dashed" >
81- < div className = "max-w-sm mx-auto space-y-4" >
82- < div className = "w-16 h-16 mx-auto rounded-full bg-muted flex items-center justify-center" >
83- < Check className = "w-8 h-8 text-green-500" />
84- </ div >
85- < h3 className = "text-xl font-semibold" > All Caught Up!</ h3 >
86- < p className = "text-muted-foreground" >
87- There are no pending courses to review at the moment.
88- </ p >
89- </ div >
90- </ Card >
93+ { ! isLoading &&
94+ courses ?. map ( ( course ) => (
95+ < Card
96+ key = { course . id }
97+ className = "overflow-hidden hover:shadow-md transition-shadow"
98+ >
99+ < CardContent className = "p-6 flex flex-col md:flex-row gap-6 items-start md:items-center" >
100+ < div className = "w-full md:w-48 h-32 bg-muted rounded-lg shrink-0 overflow-hidden" >
101+ { course . thumbnailUrl ? (
102+ < img
103+ src = { course . thumbnailUrl }
104+ alt = { course . title }
105+ className = "w-full h-full object-cover"
106+ />
107+ ) : (
108+ < div className = "w-full h-full flex items-center justify-center text-muted-foreground bg-muted" >
109+ No Image
110+ </ div >
91111 ) }
112+ </ div >
92113
93- { ! isLoading &&
94- courses ?. map ( ( course ) => (
95- < Card
96- key = { course . id }
97- className = "overflow-hidden hover:shadow-md transition-shadow"
98- >
99- < CardContent className = "p-6 flex flex-col md:flex-row gap-6 items-start md:items-center" >
100- < div className = "w-full md:w-48 h-32 bg-muted rounded-lg shrink-0 overflow-hidden" >
101- { course . thumbnailUrl ? (
102- < img
103- src = { course . thumbnailUrl }
104- alt = { course . title }
105- className = "w-full h-full object-cover"
106- />
107- ) : (
108- < div className = "w-full h-full flex items-center justify-center text-muted-foreground bg-muted" >
109- No Image
110- </ div >
111- ) }
112- </ div >
113-
114- < div className = "flex-1 min-w-0 space-y-2" >
115- < h3 className = "text-xl font-bold truncate" >
116- { course . title }
117- </ h3 >
118- < div className = "flex items-center gap-2 text-sm text-muted-foreground" >
119- < span >
120- By { course . instructor ?. fullName ?? 'Unknown Instructor' }
121- </ span >
122- < span > •</ span >
123- < span className = "capitalize" > { course . level } </ span >
124- < span > •</ span >
125- { /* Date formatting could go here */ }
126- < span > Submitted Recently</ span >
127- </ div >
128- < p className = "text-sm text-muted-foreground line-clamp-2" >
129- { course . description }
130- </ p >
131- </ div >
114+ < div className = "flex-1 min-w-0 space-y-2" >
115+ < h3 className = "text-xl font-bold truncate" >
116+ { course . title }
117+ </ h3 >
118+ < div className = "flex items-center gap-2 text-sm text-muted-foreground" >
119+ < span >
120+ By { course . instructor ?. fullName ?? 'Unknown Instructor' }
121+ </ span >
122+ < span > •</ span >
123+ < span className = "capitalize" > { course . level } </ span >
124+ < span > •</ span >
125+ { /* Date formatting could go here */ }
126+ < span > Submitted Recently</ span >
127+ </ div >
128+ < p className = "text-sm text-muted-foreground line-clamp-2" >
129+ { course . description }
130+ </ p >
131+ </ div >
132132
133- < div className = "flex flex-row md:flex-col gap-2 shrink-0 w-full md:w-auto" >
134- < Button
135- variant = "outline"
136- size = "sm"
137- asChild
138- className = "w-full justify-start"
139- >
140- < Link to = { `/courses/${ course . id } ` } target = "_blank" >
141- < Eye className = "w-4 h-4 mr-2" /> Preview
142- </ Link >
143- </ Button >
144- < div className = "flex gap-2 w-full" >
145- < Button
146- variant = "primary"
147- size = "sm"
148- className = "flex-1 bg-green-600 hover:bg-green-700"
149- onClick = { ( ) => handleApprove ( course . id ) }
150- >
151- < Check className = "w-4 h-4 mr-2" /> Approve
152- </ Button >
153- < Button
154- variant = "destructive"
155- size = "sm"
156- className = "flex-1"
157- onClick = { ( ) => handleReject ( course . id ) }
158- >
159- < X className = "w-4 h-4 mr-2" /> Reject
160- </ Button >
161- </ div >
162- </ div >
163- </ CardContent >
164- </ Card >
165- ) ) }
166- </ div >
167- </ div >
168- </ PageContainer >
169- ) ;
133+ < div className = "flex flex-row md:flex-col gap-2 shrink-0 w-full md:w-auto" >
134+ < Button
135+ variant = "outline"
136+ size = "sm"
137+ asChild
138+ className = "w-full justify-start"
139+ >
140+ < Link to = { `/courses/${ course . id } ` } target = "_blank" >
141+ < Eye className = "w-4 h-4 mr-2" /> Preview
142+ </ Link >
143+ </ Button >
144+ < div className = "flex gap-2 w-full" >
145+ < Button
146+ variant = "primary"
147+ size = "sm"
148+ className = "flex-1 bg-green-600 hover:bg-green-700"
149+ onClick = { ( ) => handleApprove ( course . id ) }
150+ >
151+ < Check className = "w-4 h-4 mr-2" /> Approve
152+ </ Button >
153+ < Button
154+ variant = "destructive"
155+ size = "sm"
156+ className = "flex-1"
157+ onClick = { ( ) => handleReject ( course . id ) }
158+ >
159+ < X className = "w-4 h-4 mr-2" /> Reject
160+ </ Button >
161+ </ div >
162+ </ div >
163+ </ CardContent >
164+ </ Card >
165+ ) ) }
166+ </ div >
167+ </ div >
168+ </ PageContainer >
169+ ) ;
170170}
171171
172172export default AdminCoursesPage ;
0 commit comments