@@ -48,6 +48,34 @@ const CustomChart = styled(Chart)`
4848 }
4949` ;
5050
51+ const CustomOfflineChart = styled ( Chart ) `
52+ svg {
53+ // Background Color
54+ g:nth-child(2) {
55+ rect {
56+ fill: var(--gray-a2);
57+ }
58+ path {
59+ stroke: var(--gray-4);
60+ }
61+ }
62+
63+ // Text
64+ g:nth-child(3) {
65+ text {
66+ fill: var(--gray-12);
67+ }
68+ }
69+
70+ // Fill
71+ g:nth-child(5) {
72+ rect {
73+ fill: var(--gray-8);
74+ }
75+ }
76+ }
77+ ` ;
78+
5179function getFilteredDischarges (
5280 dischargeData : DischargeHistoricalData ,
5381 period : DischargeHistoryPeriod ,
@@ -312,6 +340,108 @@ function DischargeTimeline({ locationName }: { locationName: string }) {
312340 ) ;
313341}
314342
343+ function OfflineTimeline ( { locationName } : { locationName : string } ) {
344+ const [ selectedPeriod , setSelectedPeriod ] = React . useState < DischargeHistoryPeriod > (
345+ DischargeHistoryPeriod . Last6Months ,
346+ ) ;
347+
348+ const {
349+ data : historicOfflineDataJSON ,
350+ isLoading,
351+ error,
352+ } = useSWR (
353+ 'https://d1kmd884co9q6x.cloudfront.net/discharges_to_date/up_to_now_offline.json' ,
354+ fetchHistoricDischargeData ,
355+ ) ;
356+
357+ const { data : lastUpdatedDate , isLoading : lastUpdatedLoading } = useSWR (
358+ 'https://d1kmd884co9q6x.cloudfront.net/discharges_to_date/timestamp.txt' ,
359+ fetchTimeStamp ,
360+ ) ;
361+
362+ if ( isLoading || lastUpdatedLoading ) {
363+ return < p > Loading...</ p > ;
364+ }
365+
366+ if ( error ) {
367+ return < p > Failed to load offline monitor data.</ p > ;
368+ }
369+
370+ const locationData = processDataForLocation ( historicOfflineDataJSON , locationName , selectedPeriod ) ;
371+ const { dischargeChartData, totalDischarge : totalOfflineDuration } = locationData ;
372+
373+ const offlineStartDate = getDischargeDateObject (
374+ getStartDateOfInterest ( selectedPeriod ) ?? new Date ( ) ,
375+ ) ;
376+
377+ return (
378+ < TimeLineWrapper >
379+ < Text size = { '2' } >
380+ Offline periods from the:{ ' ' }
381+ < InLineSelect
382+ options = { [
383+ { value : DischargeHistoryPeriod . Last3Months , label : 'Last 3 months' } ,
384+ { value : DischargeHistoryPeriod . Last6Months , label : 'Last 6 months' } ,
385+ { value : DischargeHistoryPeriod . Last12Months , label : 'Last 12 months' } ,
386+ { value : DischargeHistoryPeriod . StartOf2023 , label : 'Entirety of 2023' } ,
387+ { value : DischargeHistoryPeriod . StartOf2024 , label : 'Entirety of 2024' } ,
388+ { value : DischargeHistoryPeriod . StartOf2025 , label : 'Start of the Year' } ,
389+ ] }
390+ value = { selectedPeriod }
391+ onChange = { ( selectedPeriod ) => {
392+ setSelectedPeriod ( selectedPeriod as DischargeHistoryPeriod ) ;
393+ } }
394+ />
395+ </ Text >
396+ { dischargeChartData . length === 1 ? (
397+ < Text size = { '2' } >
398+ No Recorded Offline Periods since { offlineStartDate . day } { offlineStartDate . month } { ' ' }
399+ { offlineStartDate . year }
400+ </ Text >
401+ ) : (
402+ < CustomOfflineChart
403+ chartType = "Timeline"
404+ data = { dischargeChartData }
405+ width = "100%"
406+ height = "120px"
407+ options = { {
408+ timeline : {
409+ showRowLabels : false ,
410+ singleColor : '#808080' ,
411+ barLabelStyle : {
412+ fontSize : 20 ,
413+ } ,
414+ } ,
415+ hAxis : {
416+ minValue : getStartDateOfInterest ( selectedPeriod ) ,
417+ maxValue : getEndDateOfInterest ( selectedPeriod ) ,
418+ } ,
419+ tooltip : { html : true } ,
420+ avoidOverlappingGridLines : false ,
421+ } }
422+ > </ CustomOfflineChart >
423+ ) }
424+ < Flex
425+ direction = { 'row' }
426+ justify = { 'between' }
427+ style = { {
428+ containerType : 'inline-size' ,
429+ } }
430+ mt = { '-4' }
431+ >
432+ { lastUpdatedDate && (
433+ < SubText >
434+ Last Updated < b > { formatDate ( lastUpdatedDate , 'full' ) } </ b >
435+ </ SubText >
436+ ) }
437+ < SubText >
438+ Total Duration < b > { formatTime ( totalOfflineDuration , false ) } </ b >
439+ </ SubText >
440+ </ Flex >
441+ </ TimeLineWrapper >
442+ ) ;
443+ }
444+
315445const BulletPoint = styled . li `
316446 font-size: 14px;
317447` ;
@@ -358,4 +488,51 @@ function HistoricDischarges({ company, locationName }: { company: string; locati
358488 ) ;
359489}
360490
491+ export function HistoricOfflinePeriods ( {
492+ company,
493+ locationName,
494+ } : {
495+ company : string ;
496+ locationName : string ;
497+ } ) {
498+ if ( company === 'Thames Water' ) {
499+ return < OfflineTimeline locationName = { locationName } /> ;
500+ }
501+
502+ const message =
503+ company === 'Southern Water'
504+ ? `${ company } provides live historical sewage spill data on their <a href="https://www.southernwater.co.uk/our-region/clean-rivers-and-seas-task-force/rivers-and-seas-watch/" target="_blank" rel="noopener noreferrer">website</a> but does not make it available via an API for third-party applications. 🔒`
505+ : `${ company } has chosen not to share live historical sewage spill data with the public. 🙄` ;
506+
507+ return (
508+ < >
509+ < Blockquote mb = { '2' } size = { '2' } >
510+ < span dangerouslySetInnerHTML = { { __html : message } } />
511+ < br />
512+ Find information from last year at:{ ' ' }
513+ < Link href = "https://top-of-the-poops.org" > top-of-the-poops.org</ Link >
514+ </ Blockquote >
515+
516+ < Heading as = "h4" size = "2" >
517+ Want to help? You can:
518+ </ Heading >
519+ < ul >
520+ < BulletPoint >
521+ < Text size = { '2' } > Contact { company } to request data transparency</ Text >
522+ </ BulletPoint >
523+ < BulletPoint >
524+ < Link href = "https://writetothem.com" size = { '2' } >
525+ Tell your local MP why this data matters to you
526+ </ Link >
527+ </ BulletPoint >
528+ < BulletPoint >
529+ < Link href = "https://theconversation.com/water-companies-now-have-to-release-live-sewage-spill-data-heres-why-more-transparency-is-the-key-to-cleaner-rivers-239444" >
530+ Learn more about the importance of open environmental data
531+ </ Link >
532+ </ BulletPoint >
533+ </ ul >
534+ </ >
535+ ) ;
536+ }
537+
361538export default HistoricDischarges ;
0 commit comments