@@ -4,8 +4,12 @@ import {
44 CircularProgress ,
55 Container ,
66 Dialog ,
7+ FormControl ,
78 FormControlLabel ,
89 IconButton ,
10+ InputLabel ,
11+ MenuItem ,
12+ Select ,
913 Paper ,
1014 Stack ,
1115 Switch ,
@@ -19,9 +23,7 @@ import {
1923 TextField ,
2024 Typography ,
2125} from "@mui/material" ;
22- import {
23- Delete as DeleteIcon ,
24- } from "@mui/icons-material" ;
26+ import { Delete as DeleteIcon } from "@mui/icons-material" ;
2527import { useFormik } from "formik" ;
2628import React , { useState } from "react" ;
2729import * as Yup from "yup" ;
@@ -35,6 +37,8 @@ const Announcements = () => {
3537 const [ editAnnouncement , setEditAnnouncement ] = useState ( null ) ;
3638 const [ page , setPage ] = React . useState ( 0 ) ;
3739 const [ rowsPerPage , setRowsPerPage ] = React . useState ( 10 ) ;
40+ const [ sortBy , setSortBy ] = useState ( "created_at" ) ;
41+ const [ sortDirection , setSortDirection ] = useState ( "desc" ) ;
3842
3943 const {
4044 data : announcementsData ,
@@ -58,6 +62,7 @@ const Announcements = () => {
5862 title : announcement . title ,
5963 description : announcement . description ,
6064 is_enabled : isEnabled ,
65+ severity : announcement . severity ,
6166 } ) ;
6267 await announcementRefetch ( ) ;
6368 } catch ( error ) {
@@ -70,6 +75,7 @@ const Announcements = () => {
7075 title : "" ,
7176 description : "" ,
7277 is_enabled : false ,
78+ severity : "info" ,
7379 } ,
7480 validationSchema : Yup . object ( {
7581 title : Yup . string ( ) . trim ( ) . required ( "Title is required" ) ,
@@ -90,6 +96,7 @@ const Announcements = () => {
9096 title : editAnnouncement ?. title || "" ,
9197 description : editAnnouncement ?. description || "" ,
9298 is_enabled : editAnnouncement ?. is_enabled || false ,
99+ severity : editAnnouncement ?. severity || "info" ,
93100 } ,
94101 validationSchema : Yup . object ( {
95102 title : Yup . string ( ) . trim ( ) . required ( "Title is required" ) ,
@@ -110,6 +117,32 @@ const Announcements = () => {
110117 setPage ( 0 ) ;
111118 } ;
112119
120+ const sortedAnnouncements = React . useMemo ( ( ) => {
121+ if ( ! announcementsData ) return [ ] ;
122+ return [ ...announcementsData ] . sort ( ( a , b ) => {
123+ let aVal = a [ sortBy ] ;
124+ let bVal = b [ sortBy ] ;
125+
126+ if ( sortBy === "created_at" ) {
127+ aVal = new Date ( aVal ) ;
128+ bVal = new Date ( bVal ) ;
129+ } else if ( sortBy === "severity" ) {
130+ const severityMap = { info : 0 , warning : 1 , error : 2 , success : 3 } ;
131+ aVal = severityMap [ aVal ] ;
132+ bVal = severityMap [ bVal ] ;
133+ } else if ( typeof aVal === "string" ) {
134+ aVal = aVal . toLowerCase ( ) ;
135+ bVal = bVal . toLowerCase ( ) ;
136+ }
137+
138+ if ( sortDirection === "asc" ) {
139+ return aVal < bVal ? - 1 : aVal > bVal ? 1 : 0 ;
140+ } else {
141+ return aVal > bVal ? - 1 : aVal < bVal ? 1 : 0 ;
142+ }
143+ } ) ;
144+ } , [ announcementsData , sortBy , sortDirection ] ) ;
145+
113146 if ( announcementsLoading || ! announcementsData ) {
114147 return (
115148 < Stack
@@ -139,29 +172,58 @@ const Announcements = () => {
139172 < Typography variant = "h2" style = { { margin : 0 , fontWeight : "bold" } } >
140173 Announcements
141174 </ Typography >
142- < Button
143- variant = "contained"
144- type = "button"
145- onClick = { ( ) => setAnnouncementModalOpen ( true ) }
146- >
147- Add New Announcement
148- </ Button >
175+ < Box sx = { { display : "flex" , gap : 2 , marginTop : 1 } } >
176+ < FormControl size = "small" sx = { { minWidth : 150 } } >
177+ < InputLabel > Sort By</ InputLabel >
178+ < Select
179+ value = { sortBy }
180+ onChange = { ( e ) => setSortBy ( e . target . value ) }
181+ label = "Sort By"
182+ data-testid = "sort-by-select"
183+ >
184+ < MenuItem value = "created_at" > Created Date</ MenuItem >
185+ < MenuItem value = "severity" > Severity</ MenuItem >
186+ < MenuItem value = "title" > Title</ MenuItem >
187+ < MenuItem value = "announcementId" > ID</ MenuItem >
188+ </ Select >
189+ </ FormControl >
190+ < FormControl size = "small" sx = { { minWidth : 150 } } >
191+ < InputLabel > Order</ InputLabel >
192+ < Select
193+ value = { sortDirection }
194+ onChange = { ( e ) => setSortDirection ( e . target . value ) }
195+ label = "Order"
196+ >
197+ < MenuItem value = "asc" > Ascending</ MenuItem >
198+ < MenuItem value = "desc" > Descending</ MenuItem >
199+ </ Select >
200+ </ FormControl >
201+ < Button
202+ variant = "contained"
203+ type = "button"
204+ onClick = { ( ) => setAnnouncementModalOpen ( true ) }
205+ >
206+ Add New Announcement
207+ </ Button >
208+ </ Box >
149209 </ Box >
150210
151211 < TableContainer component = { Paper } elevation = { 3 } >
152212 < Table aria-label = "collapsible table" >
153213 < TableHead >
154214 < TableRow >
155215 < TableCell />
156- < TableCell align = "left" > Announcement ID </ TableCell >
216+ < TableCell align = "left" > Announcement ID</ TableCell >
157217 < TableCell align = "left" > Announcement Title</ TableCell >
158218 < TableCell align = "left" > Announcement Description</ TableCell >
219+ < TableCell align = "left" > Created Date</ TableCell >
220+ < TableCell align = "left" > Severity</ TableCell >
159221 < TableCell align = "left" > Enabled</ TableCell >
160222 < TableCell />
161223 </ TableRow >
162224 </ TableHead >
163225 < TableBody >
164- { ( announcementsData || [ ] )
226+ { ( sortedAnnouncements || [ ] )
165227 . slice ( page * rowsPerPage , page * rowsPerPage + rowsPerPage )
166228 . map ( ( row ) => (
167229 < TableRow
@@ -196,14 +258,23 @@ const Announcements = () => {
196258 < TableCell align = "left" component = "th" scope = "row" >
197259 { row . description }
198260 </ TableCell >
261+ < TableCell align = "left" component = "th" scope = "row" >
262+ { new Date ( row . created_at ) . toLocaleDateString ( ) }
263+ </ TableCell >
264+ < TableCell align = "left" component = "th" scope = "row" >
265+ { row . severity }
266+ </ TableCell >
199267 < TableCell >
200268 < FormControlLabel
201269 control = {
202270 < Switch
203271 color = "success"
204272 checked = { row . is_enabled }
205273 onChange = { ( e ) =>
206- handleIsEnabled ( row . announcementId , e . target . checked )
274+ handleIsEnabled (
275+ row . announcementId ,
276+ e . target . checked
277+ )
207278 }
208279 />
209280 }
@@ -282,6 +353,21 @@ const Announcements = () => {
282353 { announcementFormik . errors . description }
283354 </ Typography >
284355 ) }
356+ < FormControl fullWidth margin = "normal" >
357+ < InputLabel > Severity</ InputLabel >
358+ < Select
359+ name = "severity"
360+ value = { announcementFormik . values . severity }
361+ onChange = { announcementFormik . handleChange }
362+ label = "Severity"
363+ >
364+ < MenuItem value = "info" > Info</ MenuItem >
365+ < MenuItem value = "warning" > Warning</ MenuItem >
366+ < MenuItem value = "error" > Error</ MenuItem >
367+ < MenuItem value = "success" > Success</ MenuItem >
368+ </ Select >
369+ </ FormControl >
370+
285371 < FormControlLabel
286372 control = {
287373 < Switch
@@ -352,6 +438,20 @@ const Announcements = () => {
352438 { editFormik . errors . description }
353439 </ Typography >
354440 ) }
441+ < FormControl fullWidth margin = "normal" >
442+ < InputLabel > Severity</ InputLabel >
443+ < Select
444+ name = "severity"
445+ value = { editFormik . values . severity }
446+ onChange = { editFormik . handleChange }
447+ label = "Severity"
448+ >
449+ < MenuItem value = "info" > Info</ MenuItem >
450+ < MenuItem value = "warning" > Warning</ MenuItem >
451+ < MenuItem value = "error" > Error</ MenuItem >
452+ < MenuItem value = "success" > Success</ MenuItem >
453+ </ Select >
454+ </ FormControl >
355455 < FormControlLabel
356456 control = {
357457 < Switch
@@ -386,7 +486,7 @@ const Announcements = () => {
386486 < TablePagination
387487 rowsPerPageOptions = { [ 10 , 25 , 100 ] }
388488 component = "div"
389- count = { ( announcementsData || [ ] ) . length }
489+ count = { sortedAnnouncements . length }
390490 rowsPerPage = { rowsPerPage }
391491 page = { page }
392492 onPageChange = { ( event , newPage ) => setPage ( newPage ) }
0 commit comments