1+ import { ScaleButton , ScaleDropdownSelect , ScaleDropdownSelectItem , ScaleTextField } from "@telekom/scale-components-react" ;
2+ import dayjs from "dayjs" ;
13import { chain } from "lodash" ;
4+ import { useState } from "react" ;
25import { Helmet } from "react-helmet" ;
6+ import { EventType } from "~/Components/Event/Enums" ;
37import { EventItem } from "~/Components/History/EventItem" ;
48import { useStatus } from "~/Services/Status" ;
59
610/**
711 * @author Aloento
812 * @since 1.0.0
9- * @version 0.1 .0
13+ * @version 1.0 .0
1014 */
1115export function History ( ) {
1216 const { DB } = useStatus ( ) ;
1317
18+ const [ filters , setFilters ] = useState ( {
19+ startDate : dayjs ( ) . add ( - 6 , 'month' ) . format ( 'YYYY-MM-DD' ) ,
20+ endDate : "" ,
21+ serviceName : "" ,
22+ region : "" ,
23+ eventType : "" ,
24+ } ) ;
25+
26+ const [ validation , setValidation ] = useState ( {
27+ startDate : "" ,
28+ endDate : "" ,
29+ } ) ;
30+
31+ function validateDates ( startDate : string , endDate : string ) {
32+ const errors = { startDate : "" , endDate : "" } ;
33+
34+ if ( startDate && endDate ) {
35+ const start = dayjs ( startDate ) ;
36+ const end = dayjs ( endDate ) ;
37+
38+ if ( start . isAfter ( end ) ) {
39+ errors . startDate = "Start date cannot be later than end date." ;
40+ }
41+ }
42+
43+ if ( endDate ) {
44+ const end = dayjs ( endDate ) ;
45+ const now = dayjs ( ) ;
46+
47+ if ( end . isAfter ( now ) ) {
48+ errors . endDate = "End date cannot be in the future." ;
49+ }
50+ }
51+
52+ setValidation ( errors ) ;
53+ return ! errors . startDate && ! errors . endDate ;
54+ }
55+
56+ function clearFilters ( ) {
57+ setFilters ( {
58+ startDate : dayjs ( ) . add ( - 6 , 'month' ) . format ( 'YYYY-MM-DD' ) ,
59+ endDate : "" ,
60+ serviceName : "" ,
61+ region : "" ,
62+ eventType : "" ,
63+ } ) ;
64+ setValidation ( {
65+ startDate : "" ,
66+ endDate : "" ,
67+ } ) ;
68+ }
69+
70+ const filteredEvents = chain ( DB . Events )
71+ . filter ( event => {
72+ if ( filters . startDate ) {
73+ const filterStart = dayjs ( filters . startDate ) . startOf ( 'day' ) ;
74+ const eventStart = dayjs ( event . Start ) ;
75+ if ( eventStart . isBefore ( filterStart ) ) return false ;
76+ }
77+
78+ if ( filters . endDate ) {
79+ const filterEnd = dayjs ( filters . endDate ) . endOf ( 'day' ) ;
80+ const eventStart = dayjs ( event . Start ) ;
81+ const eventEnd = event . End ? dayjs ( event . End ) : eventStart ;
82+ if ( eventEnd . isAfter ( filterEnd ) ) return false ;
83+ }
84+
85+ if ( filters . serviceName ) {
86+ const serviceNames = Array . from ( event . RegionServices )
87+ . map ( rs => ( {
88+ name : rs . Service . Name . toLowerCase ( ) ,
89+ abbr : rs . Service . Abbr . toLowerCase ( )
90+ } ) ) ;
91+ const searchTerm = filters . serviceName . toLowerCase ( ) ;
92+ const hasService = serviceNames . some ( service =>
93+ service . name . includes ( searchTerm ) || service . abbr . includes ( searchTerm )
94+ ) ;
95+ if ( ! hasService ) return false ;
96+ }
97+
98+ if ( filters . region ) {
99+ const regionNames = Array . from ( event . RegionServices )
100+ . map ( rs => rs . Region . Name ) ;
101+ const hasRegion = regionNames . includes ( filters . region ) ;
102+ if ( ! hasRegion ) return false ;
103+ }
104+
105+ if ( filters . eventType && event . Type !== filters . eventType ) {
106+ return false ;
107+ }
108+
109+ return true ;
110+ } )
111+ . orderBy ( x => x . Start , "desc" )
112+ . value ( ) ;
113+
14114 return < >
15115 < Helmet >
16116 < title > Timeline - OTC Status Dashboard</ title >
@@ -22,9 +122,101 @@ export function History() {
22122 </ h3 >
23123 </ section >
24124
125+ < section className = "flex flex-col rounded-lg bg-white shadow-md p-5 gap-y-1.5" >
126+ < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 gap-4" >
127+ < ScaleTextField
128+ type = "date"
129+ label = "Start Date"
130+ placeholder = "Select start date"
131+ value = { filters . startDate }
132+ invalid = { ! ! validation . startDate }
133+ helperText = { validation . startDate }
134+ onScale-input = { ( e ) => {
135+ const newStartDate = e . target . value as string ;
136+ setFilters ( prev => ( {
137+ ...prev ,
138+ startDate : newStartDate
139+ } ) ) ;
140+ validateDates ( newStartDate , filters . endDate ) ;
141+ } }
142+ />
143+
144+ < ScaleTextField
145+ type = "date"
146+ label = "End Date"
147+ placeholder = "Select end date"
148+ value = { filters . endDate }
149+ invalid = { ! ! validation . endDate }
150+ helperText = { validation . endDate }
151+ onScale-input = { ( e ) => {
152+ const newEndDate = e . target . value as string ;
153+ setFilters ( prev => ( {
154+ ...prev ,
155+ endDate : newEndDate
156+ } ) ) ;
157+ validateDates ( filters . startDate , newEndDate ) ;
158+ } }
159+ />
160+
161+ < ScaleTextField
162+ label = "Service Name"
163+ placeholder = "Search service name"
164+ value = { filters . serviceName }
165+ onScale-input = { ( e ) => setFilters ( prev => ( {
166+ ...prev ,
167+ serviceName : e . target . value as string
168+ } ) ) }
169+ />
170+
171+ < ScaleDropdownSelect
172+ label = "Region"
173+ value = { filters . region }
174+ onScale-change = { ( e ) => setFilters ( prev => ( {
175+ ...prev ,
176+ region : e . target . value as string
177+ } ) ) }
178+ >
179+ < ScaleDropdownSelectItem value = "" > All Regions</ ScaleDropdownSelectItem >
180+ { DB . Regions . map ( ( region , i ) => (
181+ < ScaleDropdownSelectItem value = { region . Name } key = { i } >
182+ { region . Name }
183+ </ ScaleDropdownSelectItem >
184+ ) ) }
185+ </ ScaleDropdownSelect >
186+
187+ < ScaleDropdownSelect
188+ label = "Event Type"
189+ value = { filters . eventType }
190+ onScale-change = { ( e ) => setFilters ( prev => ( {
191+ ...prev ,
192+ eventType : e . target . value as string
193+ } ) ) }
194+ >
195+ < ScaleDropdownSelectItem value = "" > All Types</ ScaleDropdownSelectItem >
196+ { Object . values ( EventType ) . slice ( 1 ) . map ( ( type , i ) => (
197+ < ScaleDropdownSelectItem value = { type } key = { i } >
198+ { type }
199+ </ ScaleDropdownSelectItem >
200+ ) ) }
201+ </ ScaleDropdownSelect >
202+ </ div >
203+
204+ < div className = "flex justify-between items-center mt-4" >
205+ < span className = "text-sm text-gray-600" >
206+ Found { filteredEvents . length } events, { DB . Events . length - filteredEvents . length } filtered out
207+ </ span >
208+ < ScaleButton
209+ variant = "secondary"
210+ size = "small"
211+ onClick = { clearFilters }
212+ >
213+ Clear Filters
214+ </ ScaleButton >
215+ </ div >
216+ </ section >
217+
25218 < ol className = "flex flex-col pl-3 xl:pl-0" >
26- { chain ( DB . Events )
27- . orderBy ( x => x . Start , "desc" )
219+ { chain ( filteredEvents )
28220 . map ( ( event , index , events ) => [ events [ index - 1 ] , event ] )
29221 . map ( ( [ prev , curr ] ) => (
30222 < EventItem key = { curr . Id } Prev = { prev } Curr = { curr } />
0 commit comments