115 lines
4.9 KiB
TypeScript
115 lines
4.9 KiB
TypeScript
import React, {useEffect, useState} from 'react';
|
|
import axios from 'axios';
|
|
import AppLayout from '@/Layouts/AppLayout';
|
|
import {Typography} from "@material-tailwind/react";
|
|
|
|
interface StatsData {
|
|
transcription_duration_average: number;
|
|
vod_processing_average: number;
|
|
clip_average_per_day: number;
|
|
video_downloaded_true: number;
|
|
video_downloaded_false: number;
|
|
processed_video_count: number;
|
|
}
|
|
|
|
const formatDuration = (seconds: number): string => {
|
|
const minutes = Math.floor(seconds / 60);
|
|
const remainingSeconds = seconds % 60;
|
|
const hours = Math.floor(minutes / 60);
|
|
const remainingMinutes = minutes % 60;
|
|
|
|
if (hours >= 1) {
|
|
return `${hours}h ${remainingMinutes}m`;
|
|
}
|
|
return `${minutes}m`;
|
|
};
|
|
|
|
export default function Dashboard() {
|
|
const [stats, setStats] = useState<StatsData | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
axios.get('/api/dashboard/stats')
|
|
.then(response => {
|
|
setStats(response.data);
|
|
setLoading(false);
|
|
})
|
|
.catch(err => {
|
|
setError('Failed to load stats.');
|
|
setLoading(false);
|
|
});
|
|
}, []);
|
|
|
|
return (
|
|
<AppLayout
|
|
title="Dashboard"
|
|
renderHeader={() => (
|
|
<h2 className="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
|
Dashboard
|
|
</h2>
|
|
)}
|
|
>
|
|
<div className="py-12">
|
|
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
<div className="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-6">
|
|
<Typography className="text-[18px] pb-5 font-bold">
|
|
Last 30 days
|
|
</Typography>
|
|
{loading ? (
|
|
<div className="flex justify-center items-center">Loading...</div>
|
|
) : error ? (
|
|
<div className="text-red-500 text-center">{error}</div>
|
|
) : (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
<div className="card bg-base-100 shadow-md">
|
|
<div className="card-body">
|
|
<h2 className="card-title">Avg. Transcription Duration</h2>
|
|
<p>{formatDuration(stats?.transcription_duration_average ?? 0)}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="card bg-base-100 shadow-md">
|
|
<div className="card-body">
|
|
<h2 className="card-title">VOD processing delay</h2>
|
|
<p>{formatDuration(stats?.vod_processing_average ?? 0)}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="card bg-base-100 shadow-md">
|
|
<div className="card-body">
|
|
<h2 className="card-title">Avg. Clips per Day</h2>
|
|
<p>{stats?.clip_average_per_day} clips</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-6 mt-2">
|
|
<Typography className="text-[18px] pb-5 font-bold">
|
|
All time
|
|
</Typography>
|
|
{loading ? (
|
|
<div className="flex justify-center items-center">Loading...</div>
|
|
) : error ? (
|
|
<div className="text-red-500 text-center">{error}</div>
|
|
) : (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
<div className="card bg-base-100 shadow-md">
|
|
<div className="card-body">
|
|
<h2 className="card-title">VOD stats</h2>
|
|
<p>{stats?.video_downloaded_true} downloaded</p>
|
|
<p>{stats?.video_downloaded_false} not downloaded</p>
|
|
<p>{stats?.processed_video_count} transcripted</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AppLayout>
|
|
);
|
|
}
|