import Giscus from "@giscus/react"; import { GitHub } from "@mui/icons-material"; import { Avatar, Box, Container, Divider, List, ListItem, ListItemAvatar, ListItemButton, ListItemIcon, ListItemText, Paper, Skeleton, styled, Typography, } from "@mui/material"; import { blue, green, red, yellow } from "@mui/material/colors"; import Grid from "@mui/material/Grid"; import { useTheme } from "@mui/material/styles"; import dayjs from "dayjs"; import i18next from "i18next"; import { useCallback, useEffect, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { CSSTransition, SwitchTransition } from "react-transition-group"; import { CartesianGrid, Legend, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; import { getDashboardSummary } from "../../../api/api.ts"; import { HomepageSummary } from "../../../api/dashboard.ts"; import { useAppDispatch } from "../../../redux/hooks.ts"; import FacebookCircularProgress from "../../Common/CircularProgress.tsx"; import { SecondaryButton, SquareChip } from "../../Common/StyledComponents.tsx"; import TimeBadge from "../../Common/TimeBadge.tsx"; import Book from "../../Icons/Book.tsx"; import BoxMultipleFilled from "../../Icons/BoxMultipleFilled.tsx"; import Discord from "../../Icons/Discord.tsx"; import DocumentCopyFilled from "../../Icons/DocumentCopyFilled.tsx"; import HomeIcon from "../../Icons/Home.tsx"; import OpenFilled from "../../Icons/OpenFilled.tsx"; import PeopleFilled from "../../Icons/PeopleFilled.tsx"; import ShareFilled from "../../Icons/ShareFilled.tsx"; import SparkleFilled from "../../Icons/SparkleFilled.tsx"; import Telegram from "../../Icons/Telegram.tsx"; import PageContainer from "../../Pages/PageContainer.tsx"; import PageHeader from "../../Pages/PageHeader.tsx"; import ProDialog from "../../Admin/Common/ProDialog.tsx"; import SiteUrlWarning from "./SiteUrlWarning.tsx"; import CommentMultiple from "../../Icons/CommentMultiple.tsx"; import LinearProgress from "@mui/material/LinearProgress"; const StyledPaper = styled(Paper)(({ theme }) => ({ padding: theme.spacing(3), boxShadow: "initial", border: "1px solid " + theme.palette.divider, })); const StyledListItemIcon = styled(ListItemIcon)(() => ({ minWidth: 0, })); const Home = () => { const { t } = useTranslation("dashboard"); const theme = useTheme(); const dispatch = useAppDispatch(); const [summary, setSummary] = useState(); const [chartLoading, setChartLoading] = useState(false); const [siteUrlWarning, setSiteUrlWarning] = useState(false); const [proDialogOpen, setProDialogOpen] = useState(false); useEffect(() => { loadSummary(false); }, []); const loadSummary = useCallback((loadChart?: boolean) => { if (loadChart) { setChartLoading(true); } dispatch(getDashboardSummary(loadChart)) .then((r) => { setSummary(r); if (!loadChart) { const target = r.site_urls.find((site) => site == window.location.origin); if (!target) { setSiteUrlWarning(true); } } }) .finally(() => { setChartLoading(false); }); }, []); return ( setProDialogOpen(false)} /> setSiteUrlWarning(false)} existingUrls={summary?.site_urls ?? []} /> {t("summary.trend")} {summary?.metrics_summary?.generated_at && ( ]} /> )} node.addEventListener("transitionend", done, false)} classNames="fade" key={`${!!summary?.metrics_summary}-${!!chartLoading}`} > {summary?.metrics_summary && ( ({ name: dayjs(i).format("MM-DD"), user: summary?.metrics_summary?.users[d] ?? 0, file: summary?.metrics_summary?.files[d] ?? 0, share: summary?.metrics_summary?.shares[d] ?? 0, }))} > { const yAxisValue = [ ...(summary?.metrics_summary?.users ?? []), ...(summary?.metrics_summary?.files ?? []), ...(summary?.metrics_summary?.shares ?? []), ]; const yAxisUpperLimit = yAxisValue.length ? Math.max(...yAxisValue) / 0.8 - 1 : 0; const yAxisDigits = yAxisUpperLimit > 1 ? Math.floor(Math.log10(yAxisUpperLimit)) + 1 : 1; return 3 + yAxisDigits * 9; })()} /> )} {chartLoading && ( )} {!summary?.metrics_summary?.generated_at && !chartLoading && ( loadSummary(true)}> {t("application:fileManager.calculate")} )} {t("summary.summary")} node.addEventListener("transitionend", done, false)} classNames="fade" key={`${!!summary?.metrics_summary}-${chartLoading}`} > {summary?.metrics_summary && ( )} {chartLoading && ( )} {!summary?.metrics_summary?.generated_at && !chartLoading && ( loadSummary(true)}> {t("application:fileManager.calculate")} )} {t("summary.storagePolicies")} {summary?.storage_policies && summary.storage_policies.length > 0 ? ( {summary.storage_policies.map((policy) => { const percentage = policy.total > 0 ? (policy.used / policy.total) * 100 : 0; const formatSize = (bytes: number) => { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB", "TB", "PB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; }; return ( {policy.name} {formatSize(policy.used)} / {formatSize(policy.total)} {policy.type} {percentage.toFixed(1)}% 90 ? red[500] : percentage > 70 ? yellow[500] : green[500], }, }} /> ); })} ) : ( {t("summary.noStoragePolicies")} )} Cloudreve {summary && summary.version.pro && ( )} {summary ? summary.version.version : } {summary && ( t.palette.action.disabled }}> #{summary.version.commit} )} window.open("https://cloudreve.org")}> window.open("https://github.com/cloudreve/cloudreve")}> window.open("https://docs.cloudreve.org/")}> window.open("https://discord.gg/WTpMFpZT76")}> window.open("https://t.me/cloudreve_official")}> window.open("https://github.com/cloudreve/cloudreve/discussions")}> {summary && !summary.version.pro && ( setProDialogOpen(true)}> )} 公告 ); }; export default Home;