mirror of
https://github.com/prdlk/leetcode.git
synced 2026-11-03 13:27:36 +00:00
feat(api): enforce REVIEW_CAP, level overflow reviews, and display difficulty in digest
This commit is contained in:
1 parent
f697a986b8
commit
1b4ad8ca5b
4 files changed
+110
-26
No files matched your search
+63
-19
@@ -35,6 +35,7 @@ import {
|
||||
} from "@react-email/components";
|
||||
import { render } from "@react-email/render";
|
||||
import type { CSSProperties } from "react";
|
||||
import { REVIEW_CAP } from "./srs.ts";
|
||||
|
||||
// ── palette (GitHub dark) ────────────────────────────────────────
|
||||
const CANVAS = "#010409";
|
||||
@@ -82,6 +83,14 @@ export interface DigestRow {
|
||||
failUrl: string;
|
||||
}
|
||||
|
||||
/** A learning-day core problem — the only rows allowed a link. */
|
||||
export interface TopicRow {
|
||||
lc: number;
|
||||
difficulty: string;
|
||||
url: string;
|
||||
solved: boolean;
|
||||
}
|
||||
|
||||
export interface DigestData {
|
||||
/** "Tuesday, August 25" */
|
||||
day: string;
|
||||
@@ -89,9 +98,9 @@ export interface DigestData {
|
||||
progress: string;
|
||||
streak: number;
|
||||
rest: boolean;
|
||||
topic?: { name: string; core: { lc: number; url: string; solved: boolean }[] };
|
||||
topic?: { name: string; core: TopicRow[] };
|
||||
reviews: DigestRow[];
|
||||
/** Reviews past the daily cap; they simply stay due. */
|
||||
/** Reviews past the daily cap; levelled onto the coming days. */
|
||||
carried: number;
|
||||
drills: DigestRow[];
|
||||
gate?: { week: number; url: string };
|
||||
@@ -196,6 +205,48 @@ function ProblemTable({ rows, staged }: { rows: DigestRow[]; staged: boolean })
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The learning day's core list as the same table shape as reviews — this is
|
||||
* the one section allowed to link problems, and ticks replace tap buttons.
|
||||
*/
|
||||
function TopicTable({ rows }: { rows: TopicRow[] }) {
|
||||
return (
|
||||
<table
|
||||
width="100%"
|
||||
border={0}
|
||||
cellPadding={0}
|
||||
cellSpacing={0}
|
||||
style={{ borderCollapse: "collapse", width: "100%" }}
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={th}>Problem</th>
|
||||
<th style={th}>Level</th>
|
||||
<th style={{ ...th, textAlign: "right" }}>Solved</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((p) => {
|
||||
const difficulty = DIFFICULTY[p.difficulty];
|
||||
return (
|
||||
<tr key={p.lc}>
|
||||
<td style={{ ...td, fontWeight: 600 }}>
|
||||
<Link href={p.url} style={{ color: p.solved ? MUTED : LINK, textDecoration: "none" }}>
|
||||
LC {p.lc}
|
||||
</Link>
|
||||
</td>
|
||||
<td style={{ ...td, color: difficulty?.color ?? INK }}>{difficulty?.label ?? p.difficulty}</td>
|
||||
<td style={{ ...td, color: p.solved ? GREEN : MUTED, textAlign: "right" }}>
|
||||
{p.solved ? "✓" : "—"}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
function Block({
|
||||
title,
|
||||
note,
|
||||
@@ -265,18 +316,8 @@ function DigestEmail({ data }: { data: DigestData }) {
|
||||
<Text style={{ color: INK, fontSize: "16px", fontWeight: 600, margin: "0 0 8px" }}>
|
||||
{data.topic.name}
|
||||
</Text>
|
||||
<Text style={{ ...hint, margin: 0 }}>
|
||||
Work through these — ticked ones you have already solved:{" "}
|
||||
{data.topic.core.map((p, i) => (
|
||||
<span key={p.lc}>
|
||||
{i > 0 ? " · " : ""}
|
||||
<Link href={p.url} style={{ color: p.solved ? MUTED : LINK, textDecoration: "none" }}>
|
||||
LC {p.lc}
|
||||
</Link>
|
||||
{p.solved ? <span style={{ color: GREEN }}> ✓</span> : null}
|
||||
</span>
|
||||
))}
|
||||
</Text>
|
||||
<Text style={hint}>Work through these from the top — a tick means it is already solved.</Text>
|
||||
<TopicTable rows={data.topic.core} />
|
||||
</Section>
|
||||
) : null}
|
||||
|
||||
@@ -289,8 +330,8 @@ function DigestEmail({ data }: { data: DigestData }) {
|
||||
/>
|
||||
{data.carried > 0 ? (
|
||||
<Text style={{ ...hint, margin: "12px 0 0" }}>
|
||||
{data.carried} more {data.carried === 1 ? "is" : "are"} waiting — they come back
|
||||
tomorrow, oldest first.
|
||||
{data.carried} more {data.carried === 1 ? "is" : "are"} waiting — spread over the
|
||||
coming days, oldest first, never more than {REVIEW_CAP} a day.
|
||||
</Text>
|
||||
) : null}
|
||||
|
||||
@@ -373,8 +414,11 @@ function plainDigest(data: DigestData): string {
|
||||
out.push(
|
||||
"",
|
||||
`SOMETHING NEW TODAY — ${data.topic.name}`,
|
||||
"Work through these; the ones marked done you have already solved.",
|
||||
...data.topic.core.map((p) => ` LC ${p.lc}${p.solved ? " (done)" : ""} — ${p.url}`),
|
||||
"Work through these from the top; the ones marked done you have already solved.",
|
||||
...data.topic.core.map(
|
||||
(p) =>
|
||||
` LC ${p.lc} — ${DIFFICULTY[p.difficulty]?.label ?? p.difficulty}${p.solved ? " (done)" : ""} — ${p.url}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -411,7 +455,7 @@ function plainDigest(data: DigestData): string {
|
||||
if (data.carried > 0) {
|
||||
out.push(
|
||||
"",
|
||||
`${data.carried} more ${data.carried === 1 ? "is" : "are"} waiting — they come back tomorrow, oldest first.`,
|
||||
`${data.carried} more ${data.carried === 1 ? "is" : "are"} waiting — spread over the coming days, oldest first, never more than ${REVIEW_CAP} a day.`,
|
||||
);
|
||||
}
|
||||
if (data.gate) {
|
||||
|
||||
Reference in new issue
Block a user