This commit is contained in:
Michael Zhang 2023-06-08 18:52:29 -05:00
parent 4a0ec0391d
commit 16c154471f
6 changed files with 115 additions and 18 deletions

21
package-lock.json generated
View file

@ -8,6 +8,7 @@
"name": "houhou",
"version": "0.1.0",
"dependencies": {
"@chakra-ui/icons": "^2.0.19",
"@chakra-ui/react": "^2.7.0",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
@ -682,6 +683,18 @@
"react": ">=18"
}
},
"node_modules/@chakra-ui/icons": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/@chakra-ui/icons/-/icons-2.0.19.tgz",
"integrity": "sha512-0A6U1ZBZhLIxh3QgdjuvIEhAZi3B9v8g6Qvlfa3mu6vSnXQn2CHBZXmJwxpXxO40NK/2gj/gKXrLeUaFR6H/Qw==",
"dependencies": {
"@chakra-ui/icon": "3.0.16"
},
"peerDependencies": {
"@chakra-ui/system": ">=2.0.0",
"react": ">=18"
}
},
"node_modules/@chakra-ui/image": {
"version": "2.0.16",
"resolved": "https://registry.npmjs.org/@chakra-ui/image/-/image-2.0.16.tgz",
@ -4475,6 +4488,14 @@
"@chakra-ui/shared-utils": "2.0.5"
}
},
"@chakra-ui/icons": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/@chakra-ui/icons/-/icons-2.0.19.tgz",
"integrity": "sha512-0A6U1ZBZhLIxh3QgdjuvIEhAZi3B9v8g6Qvlfa3mu6vSnXQn2CHBZXmJwxpXxO40NK/2gj/gKXrLeUaFR6H/Qw==",
"requires": {
"@chakra-ui/icon": "3.0.16"
}
},
"@chakra-ui/image": {
"version": "2.0.16",
"resolved": "https://registry.npmjs.org/@chakra-ui/image/-/image-2.0.16.tgz",

View file

@ -10,6 +10,7 @@
"tauri": "tauri"
},
"dependencies": {
"@chakra-ui/icons": "^2.0.19",
"@chakra-ui/react": "^2.7.0",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",

View file

@ -1,3 +1,8 @@
use std::{
ops::Add,
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
};
use sqlx::{Row, SqlitePool};
use tauri::State;
@ -18,6 +23,7 @@ pub struct SrsStats {
#[tauri::command]
pub async fn get_srs_stats(db: State<'_, SrsDb>) -> Result<SrsStats, String> {
// counts query
let row = sqlx::query(
r#"
SELECT
@ -31,12 +37,45 @@ pub async fn get_srs_stats(db: State<'_, SrsDb>) -> Result<SrsStats, String> {
.await
.map_err(|err| err.to_string())?;
// reviews query
let utc_now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|err| err.to_string())?
.as_secs() as i64
* 1000000000;
let utc_tomorrow = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|err| err.to_string())?
.add(Duration::from_secs(60 * 60 * 24))
.as_secs() as i64
* 1000000000;
let row2 = sqlx::query(
r#"
SELECT COUNT(*) AS reviews
FROM SrsEntrySet
WHERE
SuspensionDate IS NULL
AND NextAnswerDate <= ?
UNION ALL
SELECT COUNT(*) AS reviews
FROM SrsEntrySet
WHERE
SuspensionDate IS NULL
AND NextAnswerDate <= ?
"#,
)
.bind(&utc_now)
.bind(&utc_tomorrow)
.fetch_all(&db.0)
.await
.map_err(|err| err.to_string())?;
Ok(SrsStats {
reviews_available: 0,
reviews_today: 0,
total_items: row.get("total_items"),
reviews_available: row2[0].get("reviews"),
reviews_today: row2[1].get("reviews"),
total_items: row.try_get("total_items").unwrap_or(0),
total_reviews: 0,
num_success: row.get("num_success"),
num_failure: row.get("num_failure"),
num_success: row.try_get("num_success").unwrap_or(0),
num_failure: row.try_get("num_failure").unwrap_or(0),
})
}

View file

@ -11,6 +11,7 @@ import SettingsPane from "./panes/SettingsPane";
import { StrictMode } from "react";
import styles from "./App.module.scss";
import SrsReviewPane from "./panes/SrsReviewPane";
function Layout() {
const location = useLocation();
@ -45,18 +46,19 @@ export default function App() {
<Route path="/" element={<Layout />}>
{navLinks.flatMap((route, idx) => {
if (route.subPaths) {
return route.subPaths.map((subRoute, idx) => {
return route.subPaths.map((subRoute, idx2) => {
return (
<Route
key={`${route.key}-${subRoute.key}`}
index={idx + idx2 == 0}
path={subRoute.path}
element={route.element}
element={subRoute.element ?? route.element}
/>
);
});
} else {
return (
<Route key={route.path} index={idx === 0} path={route.path} element={route.element} />
<Route key={route.path} index={idx == 0} path={route.path} element={route.element} />
);
}
})}
@ -74,8 +76,16 @@ export default function App() {
}
const navLinks = [
{ key: "home", path: "/", title: "Home", element: <HomePane /> },
{ key: "srs", path: "/srs", title: "SRS", element: <SrsPane /> },
// { key: "home", path: "/", title: "Home", element: <HomePane /> },
{
key: "srs",
title: "SRS",
element: <SrsPane />,
subPaths: [
{ key: "index", path: "/srs" },
{ key: "review", path: "/srs/review", element: <SrsReviewPane /> },
],
},
{
key: "kanji",
title: "Kanji",

View file

@ -1,4 +1,5 @@
import {
Button,
Grid,
GridItem,
Stat,
@ -8,9 +9,11 @@ import {
StatLabel,
StatNumber,
} from "@chakra-ui/react";
import { ArrowRightIcon } from "@chakra-ui/icons";
import styles from "./DashboardReviewStats.module.scss";
import useSWR from "swr";
import { invoke } from "@tauri-apps/api/tauri";
import { Link } from "react-router-dom";
interface SrsStats {
reviews_available: number;
@ -31,28 +34,48 @@ export default function DashboardReviewStats() {
isLoading,
} = useSWR(["get_srs_stats"], ([command]) => invoke<SrsStats>(command));
if (!srsStats) return <>Loading...</>;
if (!srsStats)
return (
<>
{JSON.stringify([srsStats, error, isLoading])}
Loading...
</>
);
const averageSuccess = srsStats.num_success / (srsStats.num_success + srsStats.num_failure);
const averageSuccessStr = `${Math.round(averageSuccess * 10000) / 100}%`;
const generateStat = (stat) => {
return (
<Stat>
<StatLabel>{stat.label}</StatLabel>
<StatNumber>{stat.value}</StatNumber>
</Stat>
<GridItem>
<Stat>
<StatLabel>{stat.label}</StatLabel>
<StatNumber>{stat.value}</StatNumber>
</Stat>
</GridItem>
);
};
return (
<>
{/* JSON.stringify([srsStats, error, isLoading]) */}
<Grid templateColumns="2fr 1fr 1fr" templateRows="1fr 1fr">
<GridItem rowSpan={2}>
<Stat>
<StatLabel>reviews available</StatLabel>
<StatNumber>{srsStats.reviews_available}</StatNumber>
<Link to="/srs/review">
<Button disabled={srsStats.reviews_available == 0} colorScheme="blue">
Start reviewing <ArrowRightIcon marginLeft={3} />
</Button>
</Link>
</Stat>
</GridItem>
<StatGroup>
{generateStat({ label: "reviews available", value: srsStats.reviews_available })}
{generateStat({ label: "reviews today", value: srsStats.reviews_today })}
{generateStat({ label: "total items", value: srsStats.total_items })}
{generateStat({ label: "average success", value: averageSuccessStr })}
</StatGroup>
</Grid>
</>
);
}

View file

@ -0,0 +1,3 @@
export default function SrsReviewPane() {
return <>review</>;
}