Skip to content

Commit a826125

Browse files
committed
feat: enhance History component with date filtering and service selection
1 parent fde40cd commit a826125

1 file changed

Lines changed: 195 additions & 3 deletions

File tree

src/Pages/History.tsx

Lines changed: 195 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,116 @@
1+
import { ScaleButton, ScaleDropdownSelect, ScaleDropdownSelectItem, ScaleTextField } from "@telekom/scale-components-react";
2+
import dayjs from "dayjs";
13
import { chain } from "lodash";
4+
import { useState } from "react";
25
import { Helmet } from "react-helmet";
6+
import { EventType } from "~/Components/Event/Enums";
37
import { EventItem } from "~/Components/History/EventItem";
48
import { 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
*/
1115
export 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

Comments
 (0)