Compare commits

..

No commits in common. "2028ba80e092310c2481c572c82406a5e6b7f693" and "9ce56646213e05d211357423b3bbebbfc323708a" have entirely different histories.

7 changed files with 182 additions and 140 deletions

View File

@ -2,37 +2,6 @@
All notable changes to this project are documented in this file.
## [1.114.2] - 2026-09-01
### Fixed
- Deploy script: copy `public/` and `.next/static/` into `.next/standalone/` after the build.
`next.config.js` sets `output: 'standalone'`, and Next.js does not copy those itself. Without
the copy every deploy left the old chunk hashes in the standalone tree, so the browser's
requests for the new chunks 404'd, React never hydrated, and the app hung on "Loading
session..." — while the API kept returning 200, which made it look like an auth problem.
## [1.114.1] - 2026-09-01
### Added
- `scripts/update-app.sh`: the production deploy script, now version-controlled instead of
living only on the server. Hardened with `set -euo pipefail` (a failed `git pull` used to
still trigger a rebuild and PM2 restart on unchanged code) and switched from `npm install`
to `npm ci` (npm install rewrote package-lock.json, leaving the tree dirty so the next
pull aborted).
## [1.114.0] - 2026-09-01
### Added
- Focus Mode: "SKIP" button to move on to the next task of the day without completing the
current one. Skipped tasks are remembered until Focus Mode is closed, and the timer
resets for each new task. The button is disabled when no other task is left for today.
### Changed
- Styling settings: removed the standalone "Element Colors" and "Weekend Colors" sections —
those controls now live only in the CSS Variables panel (added there in 1.113.1).
- Styling settings: larger, borderless color swatches and bigger labels in the CSS Variables
panel.
## [1.113.1] - 2026-07-08
### Changed

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "my-weekly-todo-list",
"version": "1.114.2",
"version": "1.104.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "my-weekly-todo-list",
"version": "1.114.2",
"version": "1.104.1",
"license": "MIT",
"dependencies": {
"@auth/prisma-adapter": "^2.11.1",

View File

@ -1,6 +1,6 @@
{
"name": "my-weekly-todo-list",
"version": "1.114.2",
"version": "1.113.1",
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
"main": "index.js",
"scripts": {

View File

@ -1,56 +0,0 @@
#!/bin/bash
#
# Deploy script for the production host (WebApp-Testing-live).
#
# The app runs there as a PM2 process ("my-weekly-todo") from /root/My-Weekly-ToDo-List.
# It is NOT run under Docker — deploy.sh and docker-compose.yml in this repo describe an
# older Portainer setup that is not what serves todo.martin-bierschenk.de.
#
# Usage on the host: /root/My-Weekly-ToDo-List/scripts/update-app.sh
#
# The whole body is wrapped in a function so bash parses it in one go: this script lives
# inside the very checkout it updates, and `git pull` rewriting it mid-run would otherwise
# make bash resume at a stale file offset.
main() {
set -euo pipefail
# Fail fast instead of hanging on a credential prompt when run without a terminal
# (e.g. from systemd). The host authenticates via a read-only SSH deploy key.
export GIT_TERMINAL_PROMPT=0
local APP_DIR="/root/My-Weekly-ToDo-List"
local PM2_APP="my-weekly-todo"
cd "$APP_DIR"
echo "--- Pulling latest changes from git ---"
git pull origin main
# npm ci, not npm install: it installs exactly what package-lock.json specifies and
# never rewrites it. npm install rewrites the lockfile, which leaves the working tree
# dirty and makes the *next* git pull abort.
echo "--- Installing dependencies ---"
npm ci
echo "--- Building application ---"
npm run build
# next.config.js sets output: 'standalone', and Next.js does NOT copy public/ or
# .next/static/ into .next/standalone — that is a documented manual step. Skipping it
# ships new server code whose HTML references new chunk hashes while the old chunks sit
# in the standalone tree: they 404, React never hydrates, and every page hangs on its
# server-rendered shell ("Loading session...") while the API still answers 200.
echo "--- Copying static assets into standalone output ---"
cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/
echo "--- Restarting PM2 process ---"
pm2 restart "$PM2_APP" --update-env
pm2 save
echo "--- Update Complete ---"
echo "Deployed version: $(node -p "require('$APP_DIR/package.json').version")"
}
main "$@"

View File

@ -11,11 +11,9 @@ interface FocusModeOverlayProps {
duration: number; // in minutes
onClose: () => void;
onComplete: (taskId: string) => void;
onSkip?: (taskId: string) => void;
hasNext?: boolean;
}
export default function FocusModeOverlay({ task, duration, onClose, onComplete, onSkip, hasNext }: FocusModeOverlayProps) {
export default function FocusModeOverlay({ task, duration, onClose, onComplete }: FocusModeOverlayProps) {
const [timeLeft, setTimeLeft] = useState(duration * 60);
const [isActive, setIsActive] = useState(false);
const [isCompleting, setIsCompleting] = useState(false);
@ -53,12 +51,6 @@ export default function FocusModeOverlay({ task, duration, onClose, onComplete,
setTimeLeft(duration * 60);
};
const handleSkip = () => {
if (!task || !onSkip) return;
onSkip(task.id);
handleReset(); // Fresh timer for the next task
};
const handleComplete = async () => {
if (!task) return;
setIsCompleting(true);
@ -135,16 +127,6 @@ export default function FocusModeOverlay({ task, duration, onClose, onComplete,
<button className="focus-btn secondary" onClick={handleReset}>
RESET
</button>
{task && onSkip && (
<button
className="focus-btn secondary"
onClick={handleSkip}
disabled={!hasNext || isCompleting}
title={hasNext ? 'Skip to the next task' : 'No other task left today'}
>
SKIP
</button>
)}
{task && (
<button className="focus-btn success" onClick={handleComplete} disabled={isCompleting}>
{isCompleting ? 'COMPLETING...' : 'COMPLETE TASK'}
@ -267,13 +249,6 @@ export default function FocusModeOverlay({ task, duration, onClose, onComplete,
.focus-btn.secondary:hover {
border-color: white;
}
.focus-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.focus-btn:disabled:hover {
transform: none;
}
.focus-btn.success {
background: #009a9a; /* Weekly Teal */
color: white;

View File

@ -358,9 +358,9 @@ function CssVariablesEditor({
onChange={onChange}
style={{
width: "32px",
height: "32px",
height: "22px",
cursor: "pointer",
border: "none",
border: "1px solid var(--weekly-settings-input-border)",
borderRadius: "4px",
background: "transparent",
flexShrink: 0,
@ -368,7 +368,7 @@ function CssVariablesEditor({
/>
<span
style={{
fontSize: "0.85rem",
fontSize: "0.72rem",
color: "var(--weekly-settings-label)",
overflow: "hidden",
textOverflow: "ellipsis",
@ -3790,7 +3790,171 @@ function SettingsSidebar({
</div>
</div>
{/* CSS Variables (includes Element Colors / Weekend Highlight Colors groups) */}
{/* Element Colors */}
<div
style={{
background: "var(--weekly-settings-item-bg)",
padding: "12px",
borderRadius: "8px",
marginBottom: "12px",
}}
>
<label
style={{
display: "block",
fontSize: "0.85rem",
fontWeight: 600,
color: "var(--weekly-settings-label)",
marginBottom: "8px",
}}
>
Element Colors
</label>
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: "12px",
}}
>
<div>
<label
style={{
display: "block",
fontSize: "0.75rem",
color: "var(--weekly-settings-label)",
marginBottom: "4px",
}}
>
Today Highlight
</label>
<input
type="color"
value={profile.todayHighlightColor || "#f0fafa"}
onChange={(e) =>
saveFieldDebounced("todayHighlightColor", e.target.value)
}
style={{
width: "100%",
height: "30px",
cursor: "pointer",
border: "none",
background: "transparent",
}}
/>
</div>
<div>
<label
style={{
display: "block",
fontSize: "0.75rem",
color: "var(--weekly-settings-label)",
marginBottom: "4px",
}}
>
Past Days
</label>
<input
type="color"
value={profile.pastDayColor || "#a6a6a7"}
onChange={(e) =>
saveFieldDebounced("pastDayColor", e.target.value)
}
style={{
width: "100%",
height: "30px",
cursor: "pointer",
border: "none",
background: "transparent",
}}
/>
</div>
</div>
</div>
{/* Weekend Colors */}
<div
style={{
background: "var(--weekly-settings-item-bg)",
padding: "12px",
borderRadius: "8px",
marginBottom: "12px",
}}
>
<label
style={{
display: "block",
fontSize: "0.85rem",
fontWeight: 600,
color: "var(--weekly-settings-label)",
marginBottom: "8px",
}}
>
Weekend Highlight Colors
</label>
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: "12px",
}}
>
<div>
<label
style={{
display: "block",
fontSize: "0.75rem",
color: "var(--weekly-settings-label)",
marginBottom: "4px",
}}
>
Saturday
</label>
<input
type="color"
value={profile.weekendColorSat || "#666666"}
onChange={(e) =>
saveFieldDebounced("weekendColorSat", e.target.value)
}
style={{
width: "100%",
height: "30px",
cursor: "pointer",
border: "none",
background: "transparent",
}}
/>
</div>
<div>
<label
style={{
display: "block",
fontSize: "0.75rem",
color: "var(--weekly-settings-label)",
marginBottom: "4px",
}}
>
Sunday
</label>
<input
type="color"
value={profile.weekendColorSun || "#dc2626"}
onChange={(e) =>
saveFieldDebounced("weekendColorSun", e.target.value)
}
style={{
width: "100%",
height: "30px",
cursor: "pointer",
border: "none",
background: "transparent",
}}
/>
</div>
</div>
</div>
{/* CSS Variables */}
<CssVariablesEditor profile={profile} setProfile={setProfile} saveSetting={saveSetting} />
{/* Custom CSS */}

View File

@ -1250,7 +1250,6 @@ export default function WeeklyView() {
const [selectedTaskForRecurrence, setSelectedTaskForRecurrence] =
useState<Task | null>(null);
const [showFocusMode, setShowFocusMode] = useState(false);
const [focusSkippedIds, setFocusSkippedIds] = useState<string[]>([]);
const [showSchedule, setShowSchedule] = useState(true);
const [focusBreakDuration, setFocusBreakDuration] = useState(5);
const [weekdayFormat, setWeekdayFormat] = useState<"long" | "short" | "narrow" | "custom">("long");
@ -9383,8 +9382,9 @@ export default function WeeklyView() {
)}
{/* Focus Mode Overlay */}
{
showFocusMode && (() => {
const focusQueue = (() => {
showFocusMode && (
<FocusModeOverlay
task={(() => {
// Logic to find the "Next Task"
// 1. Tasks for today with start time, sorted by time
// 2. Tasks for today without start time, sorted by order
@ -9424,23 +9424,13 @@ export default function WeeklyView() {
return a.order - b.order;
});
return activeTasks.filter((t) => !focusSkippedIds.includes(t.id));
})();
return (
<FocusModeOverlay
task={focusQueue.length > 0 ? focusQueue[0] : null}
hasNext={focusQueue.length > 1}
onSkip={(taskId) => setFocusSkippedIds((prev) => [...prev, taskId])}
return activeTasks.length > 0 ? activeTasks[0] : null;
})()}
duration={focusTimerDuration}
onClose={() => {
setShowFocusMode(false);
setFocusSkippedIds([]);
}}
onClose={() => setShowFocusMode(false)}
onComplete={(taskId) => toggleTask(taskId)}
/>
);
})()
)
}
{/* Settings Sidebar */}