-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
99faa1a
commit 72db26b
Showing
28 changed files
with
1,177 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import * as React from 'react'; | ||
import * as SeparatorPrimitive from '@radix-ui/react-separator'; | ||
|
||
import { cn } from '@/lib/utils'; | ||
|
||
const Separator = React.forwardRef< | ||
React.ElementRef<typeof SeparatorPrimitive.Root>, | ||
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> | ||
>(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => ( | ||
<SeparatorPrimitive.Root | ||
ref={ref} | ||
decorative={decorative} | ||
orientation={orientation} | ||
className={cn( | ||
'shrink-0 bg-border', | ||
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]', | ||
className, | ||
)} | ||
{...props} | ||
/> | ||
)); | ||
Separator.displayName = SeparatorPrimitive.Root.displayName; | ||
|
||
export { Separator }; |
139 changes: 139 additions & 0 deletions
139
client/src/containers/analysis-eudr-detail/deforestation-alerts/chart/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import { UTCDate } from '@date-fns/utc'; | ||
import { format } from 'date-fns'; | ||
import { | ||
LineChart, | ||
Line, | ||
XAxis, | ||
YAxis, | ||
CartesianGrid, | ||
Tooltip, | ||
ResponsiveContainer, | ||
} from 'recharts'; | ||
import { useParams } from 'next/navigation'; | ||
import { useMemo, useState } from 'react'; | ||
|
||
import { EUDR_COLOR_RAMP } from '@/utils/colors'; | ||
import { useEUDRSupplier } from '@/hooks/eudr'; | ||
import { useAppSelector } from '@/store/hooks'; | ||
import { eudrDetail } from '@/store/features/eudr-detail'; | ||
import { Badge } from '@/components/ui/badge'; | ||
import { cn } from '@/lib/utils'; | ||
|
||
const DeforestationAlertsChart = (): JSX.Element => { | ||
const [selectedPlots, setSelectedPlots] = useState<string[]>([]); | ||
const { supplierId }: { supplierId: string } = useParams(); | ||
const { | ||
filters: { dates }, | ||
} = useAppSelector(eudrDetail); | ||
const { data } = useEUDRSupplier( | ||
supplierId, | ||
{ | ||
startAlertDate: dates.from, | ||
endAlertDate: dates.to, | ||
}, | ||
{ | ||
select: (data) => data?.alerts?.values, | ||
}, | ||
); | ||
|
||
const parsedData = data | ||
?.map((item) => { | ||
return { | ||
...item, | ||
...Object.fromEntries(item.plots.map((plot) => [plot.plotName, plot.alertCount])), | ||
alertDate: new UTCDate(item.alertDate).getTime(), | ||
}; | ||
}) | ||
?.sort((a, b) => new UTCDate(a.alertDate).getTime() - new UTCDate(b.alertDate).getTime()); | ||
|
||
const plotConfig = useMemo(() => { | ||
if (!parsedData?.[0]) return []; | ||
|
||
return Array.from( | ||
new Set(parsedData.map((item) => item.plots.map((plot) => plot.plotName)).flat()), | ||
).map((key, index) => ({ | ||
name: key, | ||
color: EUDR_COLOR_RAMP[index] || '#000', | ||
})); | ||
}, [parsedData]); | ||
|
||
return ( | ||
<> | ||
<div className="flex flex-wrap gap-2"> | ||
{plotConfig.map(({ name, color }) => ( | ||
<Badge | ||
key={name} | ||
variant="secondary" | ||
className={cn( | ||
'flex cursor-pointer items-center space-x-1 rounded border border-gray-200 bg-white p-1 text-gray-900', | ||
{ | ||
'bg-secondary/80': selectedPlots.includes(name), | ||
}, | ||
)} | ||
onClick={() => { | ||
setSelectedPlots((prev) => { | ||
if (prev.includes(name)) { | ||
return prev.filter((item) => item !== name); | ||
} | ||
return [...prev, name]; | ||
}); | ||
}} | ||
> | ||
<span | ||
className="inline-block h-[12px] w-[5px] rounded-[18px]" | ||
style={{ | ||
background: color, | ||
}} | ||
/> | ||
<span>{name}</span> | ||
</Badge> | ||
))} | ||
</div> | ||
<ResponsiveContainer width="100%" height={285}> | ||
<LineChart | ||
data={parsedData} | ||
margin={{ | ||
top: 20, | ||
bottom: 15, | ||
}} | ||
> | ||
<CartesianGrid strokeDasharray="3 3" vertical={false} /> | ||
<XAxis | ||
type="number" | ||
scale="time" | ||
dataKey="alertDate" | ||
domain={[ | ||
new UTCDate(parsedData?.[0].alertDate).getTime(), | ||
new UTCDate(parsedData?.[parsedData?.length - 1].alertDate).getTime(), | ||
]} | ||
tickFormatter={(value: string | number, x) => { | ||
if (x === 0) return format(new UTCDate(value), 'LLL yyyy'); | ||
return format(new UTCDate(value), 'LLL'); | ||
}} | ||
tickLine={false} | ||
padding={{ left: 20, right: 20 }} | ||
axisLine={false} | ||
className="text-xs" | ||
tickMargin={15} | ||
/> | ||
<YAxis tickLine={false} axisLine={false} label="(nº)" className="text-xs" /> | ||
<Tooltip labelFormatter={(v) => format(new UTCDate(v), 'dd/MM/yyyy')} /> | ||
{plotConfig?.map(({ name, color }) => { | ||
return ( | ||
<Line | ||
key={name} | ||
dataKey={name} | ||
stroke={color} | ||
strokeWidth={3} | ||
strokeOpacity={selectedPlots.length ? (selectedPlots.includes(name) ? 1 : 0.2) : 1} | ||
connectNulls | ||
/> | ||
); | ||
})} | ||
</LineChart> | ||
</ResponsiveContainer> | ||
</> | ||
); | ||
}; | ||
|
||
export default DeforestationAlertsChart; |
49 changes: 49 additions & 0 deletions
49
client/src/containers/analysis-eudr-detail/deforestation-alerts/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { useParams } from 'next/navigation'; | ||
import { format } from 'date-fns'; | ||
import { UTCDate } from '@date-fns/utc'; | ||
import { BellRing } from 'lucide-react'; | ||
|
||
import DeforestationAlertsChart from './chart'; | ||
|
||
import { useEUDRSupplier } from '@/hooks/eudr'; | ||
import { eudrDetail } from '@/store/features/eudr-detail'; | ||
import { useAppSelector } from '@/store/hooks'; | ||
|
||
const dateFormatter = (date: string) => format(new UTCDate(date), "do 'of' MMMM yyyy"); | ||
|
||
const DeforestationAlerts = (): JSX.Element => { | ||
const { supplierId }: { supplierId: string } = useParams(); | ||
const { | ||
filters: { dates }, | ||
} = useAppSelector(eudrDetail); | ||
const { data } = useEUDRSupplier( | ||
supplierId, | ||
{ | ||
startAlertDate: dates.from, | ||
endAlertDate: dates.to, | ||
}, | ||
{ | ||
select: (data) => data?.alerts, | ||
}, | ||
); | ||
|
||
return ( | ||
<section className="space-y-4 rounded-xl border border-gray-100 p-7 shadow-md"> | ||
<h4 className="font-medium">Deforestation alerts detected within the smallholders</h4> | ||
{data?.totalAlerts && ( | ||
<div className="rounded-xl bg-orange-100 px-6 py-4 text-xs"> | ||
There were <span className="font-bold">{data?.totalAlerts}</span> deforestation alerts | ||
reported for the supplier between the{' '} | ||
<span className="font-bold">{dateFormatter(data.startAlertDate)}</span> and the{' '} | ||
<div className="flex items-center space-x-2"> | ||
<span className="font-bold">{dateFormatter(data.endAlertDate)}</span>. | ||
<BellRing className="h-5 w-5 fill-black" /> | ||
</div> | ||
</div> | ||
)} | ||
<DeforestationAlertsChart /> | ||
</section> | ||
); | ||
}; | ||
|
||
export default DeforestationAlerts; |
11 changes: 11 additions & 0 deletions
11
client/src/containers/analysis-eudr-detail/filters/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import YearsRange from './years-range'; | ||
|
||
const EUDRDetailFilters = () => { | ||
return ( | ||
<div className="flex space-x-2"> | ||
<YearsRange /> | ||
</div> | ||
); | ||
}; | ||
|
||
export default EUDRDetailFilters; |
77 changes: 77 additions & 0 deletions
77
client/src/containers/analysis-eudr-detail/filters/years-range/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import React, { useCallback, useMemo } from 'react'; | ||
import { UTCDate } from '@date-fns/utc'; | ||
import { ChevronDown } from 'lucide-react'; | ||
import { format } from 'date-fns'; | ||
|
||
import { useAppDispatch, useAppSelector } from 'store/hooks'; | ||
import { eudrDetail, setFilters } from 'store/features/eudr-detail'; | ||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; | ||
import { Button } from '@/components/ui/button'; | ||
import { Calendar } from '@/components/ui/calendar'; | ||
|
||
import type { DateRange } from 'react-day-picker'; | ||
const dateFormatter = (date: Date) => format(date, 'yyyy-MM-dd'); | ||
|
||
// ! the date range is hardcoded for now | ||
export const DATES_RANGE = ['2020-12-31', dateFormatter(new Date())]; | ||
|
||
const DatesRange = (): JSX.Element => { | ||
const dispatch = useAppDispatch(); | ||
const { | ||
filters: { dates }, | ||
} = useAppSelector(eudrDetail); | ||
|
||
const handleDatesChange = useCallback( | ||
(dates: DateRange) => { | ||
if (dates) { | ||
dispatch( | ||
setFilters({ | ||
dates: { | ||
from: dateFormatter(dates.from), | ||
to: dateFormatter(dates.to), | ||
}, | ||
}), | ||
); | ||
} | ||
}, | ||
[dispatch], | ||
); | ||
|
||
const datesToDate = useMemo(() => { | ||
return { | ||
from: dates.from ? new UTCDate(dates.from) : undefined, | ||
to: dates.to ? new UTCDate(dates.to) : undefined, | ||
}; | ||
}, [dates]); | ||
|
||
return ( | ||
<Popover> | ||
<PopoverTrigger asChild> | ||
<Button | ||
variant="ghost" | ||
className="h-auto space-x-1 border border-gray-200 bg-white shadow-sm" | ||
> | ||
<span className="text-gray-500"> | ||
Deforestation alerts from <span className="text-gray-900">{dates.from || '-'}</span> to{' '} | ||
<span className="text-gray-900">{dates.to || '-'}</span> | ||
</span> | ||
<ChevronDown className="h-4 w-4" /> | ||
</Button> | ||
</PopoverTrigger> | ||
<PopoverContent className="flex w-auto space-x-2" align="start"> | ||
<Calendar | ||
mode="range" | ||
numberOfMonths={2} | ||
disabled={{ | ||
before: new UTCDate(DATES_RANGE[0]), | ||
after: new UTCDate(DATES_RANGE[1]), | ||
}} | ||
selected={datesToDate} | ||
onSelect={handleDatesChange} | ||
/> | ||
</PopoverContent> | ||
</Popover> | ||
); | ||
}; | ||
|
||
export default DatesRange; |
Oops, something went wrong.