Overview v1
This commit is contained in:
parent
9ee76a0742
commit
b0b177c6d8
@ -1,14 +1,17 @@
|
|||||||
import * as icon from '@mdi/js';
|
import * as icon from '@mdi/js';
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import React from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import type { ReactElement } from 'react'
|
import type { ReactElement } from 'react'
|
||||||
import LayoutAuthenticated from '../layouts/Authenticated'
|
import LayoutAuthenticated from '../layouts/Authenticated'
|
||||||
import SectionMain from '../components/SectionMain'
|
import SectionMain from '../components/SectionMain'
|
||||||
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'
|
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'
|
||||||
import { getPageTitle } from '../config'
|
import { getPageTitle } from '../config'
|
||||||
import { useAppSelector, useAppDispatch } from '../stores/hooks';
|
import { useAppSelector, useAppDispatch } from '../stores/hooks';
|
||||||
|
import axios from 'axios';
|
||||||
|
import CardBox from '../components/CardBox';
|
||||||
|
import BaseIcon from '../components/BaseIcon';
|
||||||
|
import { mdiAccountMultiple, mdiCartOutline, mdiCalendarCheck } from '@mdi/js';
|
||||||
|
import ChartLineSample from '../components/ChartLineSample/index';
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@ -16,16 +19,89 @@ const Dashboard = () => {
|
|||||||
const corners = useAppSelector((state) => state.style.corners);
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
const cardsStyle = useAppSelector((state) => state.style.cardsStyle);
|
const cardsStyle = useAppSelector((state) => state.style.cardsStyle);
|
||||||
|
|
||||||
|
const [stats, setStats] = useState({
|
||||||
|
customers: 0,
|
||||||
|
menuItems: 0,
|
||||||
|
reservations: 0,
|
||||||
|
});
|
||||||
|
const [chartData, setChartData] = useState(null);
|
||||||
|
const [recentReservations, setRecentReservations] = useState([]);
|
||||||
|
const [lowStockItems, setLowStockItems] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadData = async () => {
|
||||||
|
try {
|
||||||
|
const statsPromises = [
|
||||||
|
axios.get('/customers', { params: { limit: 1 } }),
|
||||||
|
axios.get('/menu_items', { params: { limit: 1 } }),
|
||||||
|
axios.get('/reservations', { params: { limit: 1 } }),
|
||||||
|
];
|
||||||
|
|
||||||
|
const chartPromise = axios.get('/payments');
|
||||||
|
const reservationsPromise = axios.get('/reservations?limit=5&orderBy=createdAt_DESC');
|
||||||
|
const inventoryPromise = axios.get('/inventory_items');
|
||||||
|
|
||||||
|
const [customersResponse, menuItemsResponse, reservationsResponse] = await Promise.all(statsPromises);
|
||||||
|
|
||||||
|
setStats({
|
||||||
|
customers: customersResponse.data.count,
|
||||||
|
menuItems: menuItemsResponse.data.count,
|
||||||
|
reservations: reservationsResponse.data.count,
|
||||||
|
});
|
||||||
|
|
||||||
|
const chartResponse = await chartPromise;
|
||||||
|
const payments = chartResponse.data.rows;
|
||||||
|
|
||||||
|
if (payments) {
|
||||||
|
const groupedData = payments.reduce((acc, payment) => {
|
||||||
|
const date = new Date(payment.paid_at).toLocaleDateString();
|
||||||
|
if (!acc[date]) {
|
||||||
|
acc[date] = 0;
|
||||||
|
}
|
||||||
|
acc[date] += parseFloat(payment.amount);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const labels = Object.keys(groupedData);
|
||||||
|
const data = Object.values(groupedData);
|
||||||
|
|
||||||
|
setChartData({
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Revenue',
|
||||||
|
data,
|
||||||
|
fill: true,
|
||||||
|
borderColor: '#4A5568',
|
||||||
|
backgroundColor: '#4A556820',
|
||||||
|
tension: 0.2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const recentReservationsResponse = await reservationsPromise;
|
||||||
|
setRecentReservations(recentReservationsResponse.data.rows);
|
||||||
|
|
||||||
|
const inventoryResponse = await inventoryPromise;
|
||||||
|
const inventoryItems = inventoryResponse.data.rows;
|
||||||
|
if (inventoryItems) {
|
||||||
|
const lowStock = inventoryItems.filter(
|
||||||
|
(item) => item.quantity <= (item.reorder_level || 10)
|
||||||
|
);
|
||||||
|
setLowStockItems(lowStock);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load dashboard data', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
@ -40,6 +116,55 @@ const Dashboard = () => {
|
|||||||
main>
|
main>
|
||||||
{''}
|
{''}
|
||||||
</SectionTitleLineWithButton>
|
</SectionTitleLineWithButton>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3 mb-6">
|
||||||
|
<CardBox
|
||||||
|
title="Clients"
|
||||||
|
number={stats.customers}
|
||||||
|
icon={<BaseIcon path={mdiAccountMultiple} w="24" h="24" />}
|
||||||
|
/>
|
||||||
|
<CardBox
|
||||||
|
title="Menu Items"
|
||||||
|
number={stats.menuItems}
|
||||||
|
icon={<BaseIcon path={mdiCartOutline} w="24" h="24" />}
|
||||||
|
/>
|
||||||
|
<CardBox
|
||||||
|
title="Reservations"
|
||||||
|
number={stats.reservations}
|
||||||
|
icon={<BaseIcon path={mdiCalendarCheck} w="24" h="24" />}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{chartData && (
|
||||||
|
<CardBox className="mb-6">
|
||||||
|
<ChartLineSample data={chartData} />
|
||||||
|
</CardBox>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
||||||
|
<CardBox>
|
||||||
|
<h2 className="text-xl font-semibold mb-4">Recent Reservations</h2>
|
||||||
|
<ul>
|
||||||
|
{recentReservations.map((reservation) => (
|
||||||
|
<li key={reservation.id} className="flex justify-between items-center py-2 border-b">
|
||||||
|
<span>{reservation.code}</span>
|
||||||
|
<span>{new Date(reservation.start_at).toLocaleString()}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</CardBox>
|
||||||
|
<CardBox>
|
||||||
|
<h2 className="text-xl font-semibold mb-4">Low Stock Items</h2>
|
||||||
|
<ul>
|
||||||
|
{lowStockItems.map((item) => (
|
||||||
|
<li key={item.id} className="flex justify-between items-center py-2 border-b">
|
||||||
|
<span>{item.name}</span>
|
||||||
|
<span className="text-red-500">{item.quantity}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</CardBox>
|
||||||
|
</div>
|
||||||
</SectionMain>
|
</SectionMain>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user