Files
fredy/ui/src/views/dashboard/Dashboard.jsx
Christian Kellner 8c039f0026 UI improvements (#283)
* ui-improvements

* improving dashboard and settings

* improve job overview

* improving job card

* improving grid view of listings+

* restructuring settings

* next release version
2026-03-23 13:22:34 +01:00

155 lines
4.9 KiB
JavaScript

/*
* Copyright (c) 2026 by Christian Kellner.
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
*/
import React from 'react';
import { Button, Col, Row, Toast, Typography } from '@douyinfe/semi-ui-19';
import {
IconTerminal,
IconStar,
IconClock,
IconDoubleChevronLeft,
IconDoubleChevronRight,
IconStarStroked,
IconNoteMoney,
IconSearch,
IconPlayCircle,
} from '@douyinfe/semi-icons';
import { useSelector, useActions } from '../../services/state/store';
import KpiCard from '../../components/cards/KpiCard.jsx';
import PieChartCard from '../../components/cards/PieChartCard.jsx';
import './Dashboard.less';
import { xhrPost } from '../../services/xhr.js';
import { format } from '../../services/time/timeService.js';
export default function Dashboard() {
const actions = useActions();
const dashboard = useSelector((state) => state.dashboard.data);
React.useEffect(() => {
actions.dashboard.getDashboard();
}, []);
const kpis = dashboard?.kpis || { totalJobs: 0, totalListings: 0, providersUsed: 0 };
const pieData = dashboard?.pie || [];
const { Text } = Typography;
return (
<div className="dashboard">
<Text className="dashboard__section-label">General</Text>
<Row gutter={[16, 16]} className="dashboard__row">
<Col xs={24} sm={12} md={12} lg={6} xl={6}>
<KpiCard
title="Search Interval"
value={`${dashboard?.general?.interval} min`}
icon={<IconClock />}
description="Time interval for job execution"
/>
</Col>
<Col xs={24} sm={12} md={12} lg={6} xl={6}>
<KpiCard
title="Last Search"
valueFontSize="14px"
value={
dashboard?.general?.lastRun == null || dashboard?.general?.lastRun === 0
? '---'
: format(dashboard?.general?.lastRun)
}
icon={<IconDoubleChevronLeft />}
description="Last execution timestamp"
/>
</Col>
<Col xs={24} sm={12} md={12} lg={6} xl={6}>
<KpiCard
title="Next Search"
value={
dashboard?.general?.nextRun == null || dashboard?.general?.nextRun === 0
? '---'
: format(dashboard?.general?.nextRun)
}
valueFontSize="14px"
icon={<IconDoubleChevronRight />}
description="Next execution timestamp"
/>
</Col>
<Col xs={24} sm={12} md={12} lg={6} xl={6}>
<KpiCard title="Search Now" icon={<IconSearch />} description="Run a search now">
<Button
size="small"
style={{ marginTop: '.2rem' }}
icon={<IconPlayCircle />}
aria-label="Start now"
onClick={async () => {
try {
await xhrPost('/api/jobs/startAll', null);
Toast.success('Successfully triggered Fredy search.');
} catch {
Toast.error('Failed to trigger search');
}
}}
>
Search now
</Button>
</KpiCard>
</Col>
</Row>
<Text className="dashboard__section-label">Overview</Text>
<Row gutter={[16, 16]} className="dashboard__row">
<Col xs={24} sm={12} md={12} lg={6} xl={6}>
<KpiCard
title="Jobs"
color="blue"
value={!kpis.totalJobs ? '---' : kpis.totalJobs}
icon={<IconTerminal />}
description="Total number of jobs"
/>
</Col>
<Col xs={24} sm={12} md={12} lg={6} xl={6}>
<KpiCard
title="Listings"
color="orange"
value={!kpis.totalListings ? '---' : kpis.totalListings}
icon={<IconStarStroked />}
description="Total listings found"
/>
</Col>
<Col xs={24} sm={12} md={12} lg={6} xl={6}>
<KpiCard
title="Active Listings"
color="green"
value={!kpis.numberOfActiveListings ? '---' : kpis.numberOfActiveListings}
icon={<IconStar />}
description="Total active listings"
/>
</Col>
<Col xs={24} sm={12} md={12} lg={6} xl={6}>
<KpiCard
title="Avg. Price"
color="purple"
value={`${
!kpis.avgPriceOfListings
? '---'
: new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR',
}).format(kpis.avgPriceOfListings)
}`}
icon={<IconNoteMoney />}
description="Avg. Price of listings"
/>
</Col>
</Row>
<Text className="dashboard__section-label">Provider Insights</Text>
<div className="dashboard__pie-wrapper">
<PieChartCard data={pieData} />
</div>
</div>
);
}
Dashboard.displayName = 'Dashboard';