added more pomidoro

This commit is contained in:
2026-06-09 11:54:32 +03:00
parent 244935e4ac
commit c8599b3d13
20 changed files with 817 additions and 91 deletions
+68 -19
View File
@@ -1,13 +1,9 @@
import { FormEvent, useEffect, useState } from "react";
import { api, PomodoroHistoryItem, PomodoroStatus } from "../api/client";
import { phaseLabel } from "../utils/pomodoro";
import { formatTime } from "../utils/time";
import "./Pomodoro.css";
function formatTime(seconds: number): string {
const m = Math.floor(seconds / 60);
const s = seconds % 60;
return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
}
export default function Pomodoro() {
const [status, setStatus] = useState<PomodoroStatus | null>(null);
const [history, setHistory] = useState<PomodoroHistoryItem[]>([]);
@@ -21,6 +17,12 @@ export default function Pomodoro() {
const [current, past] = await Promise.all([api.pomodoroStatus(), api.pomodoroHistory()]);
setStatus(current);
setHistory(past);
if (current.cycle?.work_duration_min) {
setDuration(current.cycle.work_duration_min);
}
if (current.cycle?.task_note) {
setTaskNote(current.cycle.task_note);
}
};
useEffect(() => {
@@ -31,7 +33,7 @@ export default function Pomodoro() {
return () => clearInterval(timer);
}, []);
const handleStart = async (e: FormEvent) => {
const handleStartWork = async (e: FormEvent) => {
e.preventDefault();
setError("");
try {
@@ -67,7 +69,6 @@ export default function Pomodoro() {
try {
await api.pomodoroStop(result, completed);
await refresh();
setTaskNote("");
setResult("");
setCompleted(false);
} catch (err) {
@@ -75,28 +76,62 @@ export default function Pomodoro() {
}
};
const handleSkip = async () => {
setError("");
try {
await api.pomodoroSkip();
await refresh();
} catch (err) {
setError(err instanceof Error ? err.message : "Ошибка");
}
};
const handleReset = async () => {
setError("");
try {
await api.pomodoroResetCycle(false);
await refresh();
} catch (err) {
setError(err instanceof Error ? err.message : "Ошибка");
}
};
const isActive = status?.status === "running" || status?.status === "paused";
const displaySeconds = isActive ? (status?.remaining_seconds ?? 0) : duration * 60;
const progress = status
const progress = status && isActive
? ((status.duration_min * 60 - status.remaining_seconds) / (status.duration_min * 60)) * 100
: 0;
const cycle = status?.cycle;
const ringColor = status?.phase === "work" ? "#4f7cff" : "#3dbf8f";
return (
<div className="pomodoro-page">
<section className="timer-card">
<div className="timer-ring" style={{ background: `conic-gradient(#4f7cff ${progress}%, #1f2633 0)` }}>
{cycle && (
<div className="cycle-badge">
Цикл {cycle.completed_work_sessions}/{cycle.sessions_until_long_break}
{cycle.auto_advance && " · авто"}
</div>
)}
<div
className="timer-ring"
style={{ background: `conic-gradient(${ringColor} ${progress}%, #1f2633 0)` }}
>
<div className="timer-inner">
<div className="timer-value">{formatTime(displaySeconds)}</div>
<div className="timer-status">{status?.status ?? "idle"}</div>
<div className="timer-status">
{isActive ? phaseLabel(status?.phase ?? "work") : status?.status ?? "idle"}
</div>
</div>
</div>
{status?.task_note && <p className="task-note">Задача: {status.task_note}</p>}
{!isActive ? (
<form className="timer-form" onSubmit={handleStart}>
<form className="timer-form" onSubmit={handleStartWork}>
<label>
Минут
Работа (мин)
<input
type="number"
min={1}
@@ -113,9 +148,17 @@ export default function Pomodoro() {
placeholder="Опишите задачу"
/>
</label>
<button type="submit" className="primary-btn">
Старт
</button>
<div className="timer-form-actions">
<button type="submit" className="primary-btn">
Старт работы
</button>
<button type="button" onClick={() => api.pomodoroStartShortBreak().then(setStatus)}>
Короткий перерыв
</button>
<button type="button" onClick={() => api.pomodoroStartLongBreak().then(setStatus)}>
Длинный перерыв
</button>
</div>
</form>
) : (
<div className="timer-controls">
@@ -124,6 +167,7 @@ export default function Pomodoro() {
) : (
<button onClick={handleResume}>Продолжить</button>
)}
<button onClick={handleSkip}>Пропустить фазу</button>
<div className="stop-form">
<input
value={result}
@@ -136,13 +180,17 @@ export default function Pomodoro() {
checked={completed}
onChange={(e) => setCompleted(e.target.checked)}
/>
Задача завершена
Фаза завершена
</label>
<button onClick={handleStop}>Стоп</button>
</div>
</div>
)}
<button type="button" className="reset-cycle-btn" onClick={handleReset}>
Сбросить цикл
</button>
{error && <p className="error">{error}</p>}
</section>
@@ -152,10 +200,11 @@ export default function Pomodoro() {
{history.map((item) => (
<li key={item.id}>
<div className="history-title">
{item.task_note || "Без описания"} {item.status}
{phaseLabel(item.phase)}: {item.task_note || "Без описания"} {item.status}
</div>
<div className="history-meta">
{item.duration_min} мин · {item.finished_at ? new Date(item.finished_at).toLocaleString("ru-RU") : ""}
{item.duration_min} мин ·{" "}
{item.finished_at ? new Date(item.finished_at).toLocaleString("ru-RU") : ""}
</div>
{item.result && <div className="history-result">{item.result}</div>}
</li>