Fix learn 404, coin animation, full regions, and AI key errors.

Use numeric /learn/{num} routes, register Tailwind coin animations with方孔铜钱 UI, expand to 34 provinces, and surface missing OPENAI_API_KEY clearly.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-10 21:32:20 +08:00
parent 96b659fbe5
commit a487b17165
15 changed files with 931 additions and 159 deletions
+31 -36
View File
@@ -1,6 +1,6 @@
"use client";
import { useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { bool } from "aimless.js";
import Coin from "@/components/coin";
import Hexagram from "@/components/hexagram";
@@ -13,7 +13,7 @@ import {
YAO_LABELS,
} from "@/lib/calc/hexagram";
const AUTO_DELAY = 600;
const AUTO_DELAY = 800;
interface HexagramInputProps {
mode: "online" | "offline";
@@ -30,43 +30,30 @@ export default function HexagramInput({
}: HexagramInputProps) {
const [frontList, setFrontList] = useState([true, true, true]);
const [rotation, setRotation] = useState(false);
const [hexagramList, setHexagramList] = useState<
GuaResult["list"]
>([]);
const [count, setCount] = useState(0);
const [hexagramList, setHexagramList] = useState<GuaResult["list"]>([]);
const [offlineCounts, setOfflineCounts] = useState<(number | null)[]>(
Array(6).fill(null),
);
const complete = hexagramList.length === 6;
useEffect(() => {
setHexagramList([]);
setCount(0);
setOfflineCounts(Array(6).fill(null));
setRotation(false);
}, [mode]);
useEffect(() => {
if (
mode !== "online" ||
!enabled ||
rotation ||
hexagramList.length >= 6 ||
count >= 6
) {
return;
}
const timer = setTimeout(startOnlineCast, AUTO_DELAY);
return () => clearTimeout(timer);
}, [mode, enabled, rotation, count, hexagramList.length]);
const finishList = useCallback(
(list: GuaResult["list"]) => {
const result = computeGuaResult(list);
if (result) {
onResult({ list, result });
}
},
[onResult],
);
function finishList(list: GuaResult["list"]) {
const result = computeGuaResult(list);
if (result) {
onResult({ list, result });
}
}
function onTransitionEnd() {
const onTransitionEnd = useCallback(() => {
setRotation(false);
const frontCount = frontList.reduce(
(acc, val) => (val ? acc + 1 : acc),
@@ -86,16 +73,23 @@ export default function HexagramInput({
}
return newList;
});
}
}, [frontList, finishList]);
function startOnlineCast() {
const startOnlineCast = useCallback(() => {
if (rotation || !enabled || hexagramList.length >= 6) {
return;
}
setFrontList([bool(), bool(), bool()]);
setRotation(true);
setCount((c) => c + 1);
}
}, [rotation, enabled, hexagramList.length]);
useEffect(() => {
if (mode !== "online" || !enabled || rotation || complete) {
return;
}
const timer = setTimeout(startOnlineCast, AUTO_DELAY);
return () => clearTimeout(timer);
}, [mode, enabled, rotation, complete, hexagramList.length, startOnlineCast]);
function handleOfflineSelect(index: number, frontCount: number) {
const next = [...offlineCounts];
@@ -114,13 +108,11 @@ export default function HexagramInput({
function handleReset() {
setHexagramList([]);
setCount(0);
setOfflineCounts(Array(6).fill(null));
setRotation(false);
onClear();
}
const complete = hexagramList.length === 6;
const offlineReady = offlineCounts.every((v) => v !== null);
if (!enabled) {
@@ -133,7 +125,7 @@ export default function HexagramInput({
return (
<div className="flex w-full flex-col items-center gap-4">
{mode === "online" && !complete && (
{mode === "online" && (
<>
<Coin
onTransitionEnd={onTransitionEnd}
@@ -143,9 +135,12 @@ export default function HexagramInput({
<span className="text-lg font-medium">
🎲 {" "}
<span className="font-mono text-xl font-bold text-orange-500">
{count === 0 ? "-/-" : `${count}/6`}
{complete ? "6/6" : `${hexagramList.length + (rotation ? 1 : 0)}/6`}
</span>{" "}
{rotation && (
<span className="ml-2 text-sm text-muted-foreground"></span>
)}
</span>
</>
)}
+3 -3
View File
@@ -71,10 +71,10 @@ export default function RegionSelect({
))}
</select>
</div>
{cityOptional && provinceCode && (
{cityOptional && provinceCode && location && (
<p className="text-xs text-muted-foreground">
使
{location ? `${location.name}${location.longitude}°)` : "—"}
使
{location.name}{location.longitude}°
</p>
)}
</div>