feature/syasya/testlayout #7
@ -25,6 +25,11 @@ const AdminDashboard = () => {
 | 
				
			|||||||
  const router = useRouter();
 | 
					  const router = useRouter();
 | 
				
			||||||
  const pathname = usePathname();
 | 
					  const pathname = usePathname();
 | 
				
			||||||
  const searchParams = useSearchParams();
 | 
					  const searchParams = useSearchParams();
 | 
				
			||||||
 | 
					  const siteIdMap: Record<SiteName, string> = {
 | 
				
			||||||
 | 
					  'Site A': 'site_01',
 | 
				
			||||||
 | 
					  'Site B': 'site_02',
 | 
				
			||||||
 | 
					  'Site C': 'site_03',
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const siteParam = searchParams?.get('site');
 | 
					  const siteParam = searchParams?.get('site');
 | 
				
			||||||
  const validSiteNames: SiteName[] = ['Site A', 'Site B', 'Site C'];
 | 
					  const validSiteNames: SiteName[] = ['Site A', 'Site B', 'Site C'];
 | 
				
			||||||
@ -54,11 +59,6 @@ const AdminDashboard = () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
  const fetchData = async () => {
 | 
					  const fetchData = async () => {
 | 
				
			||||||
    const siteIdMap: Record<SiteName, string> = {
 | 
					 | 
				
			||||||
  'Site A': 'site_01',
 | 
					 | 
				
			||||||
  'Site B': 'site_02',
 | 
					 | 
				
			||||||
  'Site C': 'site_03',
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const siteId = siteIdMap[selectedSite];
 | 
					  const siteId = siteIdMap[selectedSite];
 | 
				
			||||||
  const today = new Date();
 | 
					  const today = new Date();
 | 
				
			||||||
@ -190,11 +190,7 @@ const AdminDashboard = () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <div className="grid md:grid-cols-2 gap-6 lg:flex-col justify-center">
 | 
					        <div className="grid md:grid-cols-2 gap-6 lg:flex-col justify-center">
 | 
				
			||||||
          <div ref={energyChartRef} className="pb-5">
 | 
					          <div ref={energyChartRef} className="pb-5">
 | 
				
			||||||
            <EnergyLineChart
 | 
					            <EnergyLineChart siteId={siteIdMap[selectedSite]} />
 | 
				
			||||||
  consumption={timeSeriesData.consumption}
 | 
					 | 
				
			||||||
  generation={timeSeriesData.generation}
 | 
					 | 
				
			||||||
/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div ref={monthlyChartRef} className="pb-5">
 | 
					          <div ref={monthlyChartRef} className="pb-5">
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,18 @@ import React, { useRef, useEffect, useState } from 'react';
 | 
				
			|||||||
import { Line } from 'react-chartjs-2';
 | 
					import { Line } from 'react-chartjs-2';
 | 
				
			||||||
import ChartJS from 'chart.js/auto';
 | 
					import ChartJS from 'chart.js/auto';
 | 
				
			||||||
import zoomPlugin from 'chartjs-plugin-zoom';
 | 
					import zoomPlugin from 'chartjs-plugin-zoom';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  getISOWeek,
 | 
				
			||||||
 | 
					  startOfDay,
 | 
				
			||||||
 | 
					  endOfDay,
 | 
				
			||||||
 | 
					  startOfWeek,
 | 
				
			||||||
 | 
					  endOfWeek,
 | 
				
			||||||
 | 
					  startOfMonth,
 | 
				
			||||||
 | 
					  endOfMonth,
 | 
				
			||||||
 | 
					  startOfYear,
 | 
				
			||||||
 | 
					  endOfYear,
 | 
				
			||||||
 | 
					} from 'date-fns';
 | 
				
			||||||
 | 
					import { fetchPowerTimeseries } from '@/app/utils/api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ChartJS.register(zoomPlugin);
 | 
					ChartJS.register(zoomPlugin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,22 +23,116 @@ interface TimeSeriesEntry {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface EnergyLineChartProps {
 | 
					interface EnergyLineChartProps {
 | 
				
			||||||
  consumption: TimeSeriesEntry[];
 | 
					  siteId: string;
 | 
				
			||||||
  generation: TimeSeriesEntry[];
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const EnergyLineChart = ({ consumption, generation }: EnergyLineChartProps) => {
 | 
					function groupTimeSeries(
 | 
				
			||||||
 | 
					  data: TimeSeriesEntry[],
 | 
				
			||||||
 | 
					  mode: 'day' | 'daily' | 'weekly' | 'monthly' | 'yearly'
 | 
				
			||||||
 | 
					): TimeSeriesEntry[] {
 | 
				
			||||||
 | 
					  const groupMap = new Map<string, number[]>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const entry of data) {
 | 
				
			||||||
 | 
					    const date = new Date(entry.time);
 | 
				
			||||||
 | 
					    let key = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (mode) {
 | 
				
			||||||
 | 
					      case 'day':
 | 
				
			||||||
 | 
					        const local = new Date(date.toLocaleString('en-US', { timeZone: 'Asia/Kuala_Lumpur' }));
 | 
				
			||||||
 | 
					        const hour = local.getHours();
 | 
				
			||||||
 | 
					        const minute = local.getMinutes() < 30 ? '00' : '30';
 | 
				
			||||||
 | 
					        key = `${hour.toString().padStart(2, '0')}:${minute}`;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'daily':
 | 
				
			||||||
 | 
					        key = date.toLocaleDateString('en-MY', {
 | 
				
			||||||
 | 
					          timeZone: 'Asia/Kuala_Lumpur',
 | 
				
			||||||
 | 
					          weekday: 'short',
 | 
				
			||||||
 | 
					          day: '2-digit',
 | 
				
			||||||
 | 
					          month: 'short',
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'weekly':
 | 
				
			||||||
 | 
					        key = `${date.getFullYear()}-W${String(getISOWeek(date)).padStart(2, '0')}`;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'monthly':
 | 
				
			||||||
 | 
					        key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'yearly':
 | 
				
			||||||
 | 
					        key = date.getFullYear().toString();
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!groupMap.has(key)) groupMap.set(key, []);
 | 
				
			||||||
 | 
					    groupMap.get(key)!.push(entry.value);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Array.from(groupMap.entries()).map(([time, values]) => ({
 | 
				
			||||||
 | 
					    time,
 | 
				
			||||||
 | 
					    value: values.reduce((sum, v) => sum + v, 0),
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const EnergyLineChart = ({ siteId }: EnergyLineChartProps) => {
 | 
				
			||||||
  const chartRef = useRef<any>(null);
 | 
					  const chartRef = useRef<any>(null);
 | 
				
			||||||
 | 
					  const [viewMode, setViewMode] = useState<'day' | 'daily' | 'weekly' | 'monthly' | 'yearly'>('day');
 | 
				
			||||||
 | 
					  const [consumption, setConsumption] = useState<TimeSeriesEntry[]>([]);
 | 
				
			||||||
 | 
					  const [generation, setGeneration] = useState<TimeSeriesEntry[]>([]);
 | 
				
			||||||
 | 
					  const [selectedDate, setSelectedDate] = useState(new Date());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    const now = new Date();
 | 
				
			||||||
 | 
					    let start: Date;
 | 
				
			||||||
 | 
					    let end: Date;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (viewMode) {
 | 
				
			||||||
 | 
					      case 'day':
 | 
				
			||||||
 | 
					        start = startOfDay(selectedDate);
 | 
				
			||||||
 | 
					        end = endOfDay(selectedDate);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'daily':
 | 
				
			||||||
 | 
					        start = startOfWeek(now, { weekStartsOn: 1 });
 | 
				
			||||||
 | 
					        end = endOfWeek(now, { weekStartsOn: 1 });
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'weekly':
 | 
				
			||||||
 | 
					        start = startOfMonth(now);
 | 
				
			||||||
 | 
					        end = endOfMonth(now);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'monthly':
 | 
				
			||||||
 | 
					        start = startOfYear(now);
 | 
				
			||||||
 | 
					        end = endOfYear(now);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'yearly':
 | 
				
			||||||
 | 
					        start = new Date('2020-01-01');
 | 
				
			||||||
 | 
					        end = now;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const isoStart = start.toISOString();
 | 
				
			||||||
 | 
					    const isoEnd = end.toISOString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const fetchData = async () => {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        const res = await fetchPowerTimeseries(siteId, isoStart, isoEnd);
 | 
				
			||||||
 | 
					        setConsumption(res.consumption);
 | 
				
			||||||
 | 
					        setGeneration(res.generation);
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        console.error('Failed to fetch energy timeseries:', error);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fetchData();
 | 
				
			||||||
 | 
					  }, [siteId, viewMode, selectedDate]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const groupedConsumption = groupTimeSeries(consumption, viewMode);
 | 
				
			||||||
 | 
					  const groupedGeneration = groupTimeSeries(generation, viewMode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Generate sorted unique time labels from both series
 | 
					 | 
				
			||||||
  const allTimes = Array.from(new Set([
 | 
					  const allTimes = Array.from(new Set([
 | 
				
			||||||
    ...consumption.map(d => d.time),
 | 
					    ...groupedConsumption.map(d => d.time),
 | 
				
			||||||
    ...generation.map(d => d.time),
 | 
					    ...groupedGeneration.map(d => d.time),
 | 
				
			||||||
  ])).sort(); // e.g., ["00:00", "00:30", "01:00", ...]
 | 
					  ])).sort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Map times to values
 | 
					  const consumptionMap = Object.fromEntries(groupedConsumption.map(d => [d.time, d.value]));
 | 
				
			||||||
  const consumptionMap = Object.fromEntries(consumption.map(d => [d.time, d.value]));
 | 
					  const generationMap = Object.fromEntries(groupedGeneration.map(d => [d.time, d.value]));
 | 
				
			||||||
  const generationMap = Object.fromEntries(generation.map(d => [d.time, d.value]));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [startIndex, setStartIndex] = useState(0);
 | 
					  const [startIndex, setStartIndex] = useState(0);
 | 
				
			||||||
  const [endIndex, setEndIndex] = useState(allTimes.length - 1);
 | 
					  const [endIndex, setEndIndex] = useState(allTimes.length - 1);
 | 
				
			||||||
@ -37,6 +143,22 @@ const EnergyLineChart = ({ consumption, generation }: EnergyLineChartProps) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }, []);
 | 
					  }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    setStartIndex(0);
 | 
				
			||||||
 | 
					    setEndIndex(allTimes.length - 1);
 | 
				
			||||||
 | 
					  }, [viewMode, allTimes.length]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const formatLabel = (key: string) => {
 | 
				
			||||||
 | 
					    switch (viewMode) {
 | 
				
			||||||
 | 
					      case 'monthly':
 | 
				
			||||||
 | 
					        return new Date(`${key}-01`).toLocaleString('en-GB', { month: 'short', year: 'numeric' });
 | 
				
			||||||
 | 
					      case 'weekly':
 | 
				
			||||||
 | 
					        return key.replace('-', ' ');
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        return key;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const filteredLabels = allTimes.slice(startIndex, endIndex + 1);
 | 
					  const filteredLabels = allTimes.slice(startIndex, endIndex + 1);
 | 
				
			||||||
  const filteredConsumption = filteredLabels.map(t => consumptionMap[t] ?? null);
 | 
					  const filteredConsumption = filteredLabels.map(t => consumptionMap[t] ?? null);
 | 
				
			||||||
  const filteredGeneration = filteredLabels.map(t => generationMap[t] ?? null);
 | 
					  const filteredGeneration = filteredLabels.map(t => generationMap[t] ?? null);
 | 
				
			||||||
@ -46,7 +168,7 @@ const EnergyLineChart = ({ consumption, generation }: EnergyLineChartProps) => {
 | 
				
			|||||||
  const yAxisSuggestedMax = maxValue * 1.15;
 | 
					  const yAxisSuggestedMax = maxValue * 1.15;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const data = {
 | 
					  const data = {
 | 
				
			||||||
    labels: filteredLabels,
 | 
					    labels: filteredLabels.map(formatLabel),
 | 
				
			||||||
    datasets: [
 | 
					    datasets: [
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        label: 'Consumption',
 | 
					        label: 'Consumption',
 | 
				
			||||||
@ -84,10 +206,17 @@ const EnergyLineChart = ({ consumption, generation }: EnergyLineChartProps) => {
 | 
				
			|||||||
      x: {
 | 
					      x: {
 | 
				
			||||||
        title: {
 | 
					        title: {
 | 
				
			||||||
          display: true,
 | 
					          display: true,
 | 
				
			||||||
        text: 'Time (HH:MM)',
 | 
					          text:
 | 
				
			||||||
        font: {
 | 
					            viewMode === 'day'
 | 
				
			||||||
          weight: 'bold' as const, // ✅ FIX: cast as 'const'
 | 
					              ? 'Time (HH:MM)'
 | 
				
			||||||
        },
 | 
					              : viewMode === 'daily'
 | 
				
			||||||
 | 
					              ? 'Day'
 | 
				
			||||||
 | 
					              : viewMode === 'weekly'
 | 
				
			||||||
 | 
					              ? 'Week'
 | 
				
			||||||
 | 
					              : viewMode === 'monthly'
 | 
				
			||||||
 | 
					              ? 'Month'
 | 
				
			||||||
 | 
					              : 'Year',
 | 
				
			||||||
 | 
					          font: { weight: 'bold' as const },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      y: {
 | 
					      y: {
 | 
				
			||||||
@ -96,14 +225,11 @@ const EnergyLineChart = ({ consumption, generation }: EnergyLineChartProps) => {
 | 
				
			|||||||
        title: {
 | 
					        title: {
 | 
				
			||||||
          display: true,
 | 
					          display: true,
 | 
				
			||||||
          text: 'Power (kW)',
 | 
					          text: 'Power (kW)',
 | 
				
			||||||
        font: {
 | 
					          font: { weight: 'bold' as const },
 | 
				
			||||||
          weight: 'bold' as const, // ✅ FIX: cast as 'const'
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  } as const;
 | 
				
			||||||
} as const; // ✅ Ensures compatibility with chart.js types
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleResetZoom = () => {
 | 
					  const handleResetZoom = () => {
 | 
				
			||||||
    chartRef.current?.resetZoom();
 | 
					    chartRef.current?.resetZoom();
 | 
				
			||||||
@ -119,9 +245,20 @@ const EnergyLineChart = ({ consumption, generation }: EnergyLineChartProps) => {
 | 
				
			|||||||
          </button>
 | 
					          </button>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {/* Time range selectors */}
 | 
					 | 
				
			||||||
        <div className="mb-4 flex gap-4 items-center">
 | 
					        <div className="mb-4 flex gap-4 items-center">
 | 
				
			||||||
          <label className='font-medium'>
 | 
					          {viewMode === 'day' && (
 | 
				
			||||||
 | 
					            <label className="font-medium">
 | 
				
			||||||
 | 
					              Date:{' '}
 | 
				
			||||||
 | 
					              <input
 | 
				
			||||||
 | 
					                type="date"
 | 
				
			||||||
 | 
					                value={selectedDate.toISOString().split('T')[0]}
 | 
				
			||||||
 | 
					                onChange={(e) => setSelectedDate(new Date(e.target.value))}
 | 
				
			||||||
 | 
					                className="border rounded p-1"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <label className="font-medium">
 | 
				
			||||||
            From:{' '}
 | 
					            From:{' '}
 | 
				
			||||||
            <select
 | 
					            <select
 | 
				
			||||||
              value={startIndex}
 | 
					              value={startIndex}
 | 
				
			||||||
@ -132,11 +269,11 @@ const EnergyLineChart = ({ consumption, generation }: EnergyLineChartProps) => {
 | 
				
			|||||||
              className="border rounded p-1"
 | 
					              className="border rounded p-1"
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              {allTimes.map((label, idx) => (
 | 
					              {allTimes.map((label, idx) => (
 | 
				
			||||||
                <option key={idx} value={idx}>{label}</option>
 | 
					                <option key={idx} value={idx}>{formatLabel(label)}</option>
 | 
				
			||||||
              ))}
 | 
					              ))}
 | 
				
			||||||
            </select>
 | 
					            </select>
 | 
				
			||||||
          </label>
 | 
					          </label>
 | 
				
			||||||
          <label className='font-medium'>
 | 
					          <label className="font-medium">
 | 
				
			||||||
            To:{' '}
 | 
					            To:{' '}
 | 
				
			||||||
            <select
 | 
					            <select
 | 
				
			||||||
              value={endIndex}
 | 
					              value={endIndex}
 | 
				
			||||||
@ -147,10 +284,24 @@ const EnergyLineChart = ({ consumption, generation }: EnergyLineChartProps) => {
 | 
				
			|||||||
              className="border rounded p-1"
 | 
					              className="border rounded p-1"
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              {allTimes.map((label, idx) => (
 | 
					              {allTimes.map((label, idx) => (
 | 
				
			||||||
                <option key={idx} value={idx}>{label}</option>
 | 
					                <option key={idx} value={idx}>{formatLabel(label)}</option>
 | 
				
			||||||
              ))}
 | 
					              ))}
 | 
				
			||||||
            </select>
 | 
					            </select>
 | 
				
			||||||
          </label>
 | 
					          </label>
 | 
				
			||||||
 | 
					          <label className="font-medium">
 | 
				
			||||||
 | 
					            View:{' '}
 | 
				
			||||||
 | 
					            <select
 | 
				
			||||||
 | 
					              value={viewMode}
 | 
				
			||||||
 | 
					              onChange={(e) => setViewMode(e.target.value as typeof viewMode)}
 | 
				
			||||||
 | 
					              className="border rounded p-1"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              <option value="day">Day</option>
 | 
				
			||||||
 | 
					              <option value="daily">Daily</option>
 | 
				
			||||||
 | 
					              <option value="weekly">Weekly</option>
 | 
				
			||||||
 | 
					              <option value="monthly">Monthly</option>
 | 
				
			||||||
 | 
					              <option value="yearly">Yearly</option>
 | 
				
			||||||
 | 
					            </select>
 | 
				
			||||||
 | 
					          </label>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div className="h-96 w-full">
 | 
					        <div className="h-96 w-full">
 | 
				
			||||||
@ -164,3 +315,8 @@ const EnergyLineChart = ({ consumption, generation }: EnergyLineChartProps) => {
 | 
				
			|||||||
export default EnergyLineChart;
 | 
					export default EnergyLineChart;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										16
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										16
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -23,6 +23,7 @@
 | 
				
			|||||||
                "chart.js": "^4.4.9",
 | 
					                "chart.js": "^4.4.9",
 | 
				
			||||||
                "chartjs-plugin-zoom": "^2.2.0",
 | 
					                "chartjs-plugin-zoom": "^2.2.0",
 | 
				
			||||||
                "cookie": "^1.0.2",
 | 
					                "cookie": "^1.0.2",
 | 
				
			||||||
 | 
					                "date-fns": "^4.1.0",
 | 
				
			||||||
                "eslint": "8.32.0",
 | 
					                "eslint": "8.32.0",
 | 
				
			||||||
                "eslint-config-next": "13.1.2",
 | 
					                "eslint-config-next": "13.1.2",
 | 
				
			||||||
                "html2canvas": "^1.4.1",
 | 
					                "html2canvas": "^1.4.1",
 | 
				
			||||||
@ -1974,6 +1975,16 @@
 | 
				
			|||||||
            "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
 | 
				
			||||||
            "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="
 | 
					            "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/date-fns": {
 | 
				
			||||||
 | 
					            "version": "4.1.0",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
 | 
				
			||||||
 | 
					            "license": "MIT",
 | 
				
			||||||
 | 
					            "funding": {
 | 
				
			||||||
 | 
					                "type": "github",
 | 
				
			||||||
 | 
					                "url": "https://github.com/sponsors/kossnocorp"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/debug": {
 | 
					        "node_modules/debug": {
 | 
				
			||||||
            "version": "4.3.4",
 | 
					            "version": "4.3.4",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
 | 
				
			||||||
@ -7633,6 +7644,11 @@
 | 
				
			|||||||
            "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
 | 
				
			||||||
            "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="
 | 
					            "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "date-fns": {
 | 
				
			||||||
 | 
					            "version": "4.1.0",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "debug": {
 | 
					        "debug": {
 | 
				
			||||||
            "version": "4.3.4",
 | 
					            "version": "4.3.4",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@
 | 
				
			|||||||
        "chart.js": "^4.4.9",
 | 
					        "chart.js": "^4.4.9",
 | 
				
			||||||
        "chartjs-plugin-zoom": "^2.2.0",
 | 
					        "chartjs-plugin-zoom": "^2.2.0",
 | 
				
			||||||
        "cookie": "^1.0.2",
 | 
					        "cookie": "^1.0.2",
 | 
				
			||||||
 | 
					        "date-fns": "^4.1.0",
 | 
				
			||||||
        "eslint": "8.32.0",
 | 
					        "eslint": "8.32.0",
 | 
				
			||||||
        "eslint-config-next": "13.1.2",
 | 
					        "eslint-config-next": "13.1.2",
 | 
				
			||||||
        "html2canvas": "^1.4.1",
 | 
					        "html2canvas": "^1.4.1",
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user