diff --git a/src/app/globals.css b/src/app/globals.css
index 6b1e3d8..43a6d27 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -2757,7 +2757,7 @@ h3 {
/* Week Grid Transition Group */
::view-transition-group(week-grid) {
- animation-duration: 0.3s;
+ animation-duration: 0.9s;
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden;
}
@@ -3202,6 +3202,19 @@ h3 {
.weekly-logo {
font-size: 1rem;
}
+
+ /* D) Mobile: sticky day header at top so user always knows which day */
+ .weekly-day-header {
+ position: sticky;
+ top: 0;
+ z-index: 10;
+ background: var(--weekly-bg, white);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
+ }
+
+ .dark-mode .weekly-day-header {
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
+ }
}
diff --git a/src/components/WeeklyView.tsx b/src/components/WeeklyView.tsx
index d484174..479a477 100644
--- a/src/components/WeeklyView.tsx
+++ b/src/components/WeeklyView.tsx
@@ -55,6 +55,21 @@ import RecurringTasksManager from "./RecurringTasksManager";
export interface RecurringTaskException { id: string; taskId: string; originalDate: string; newDate?: string | null; isCancelled: boolean; createdAt: Date; updatedAt: Date; }
import { ImportListModal } from "./ImportListModal";
+// Cookie helpers for per-device settings
+const DEVICE_SETTINGS_KEYS = ["viewDays", "cellDuration", "startHour", "endHour"];
+
+function getCookie(name: string): string | null {
+ if (typeof document === "undefined") return null;
+ const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
+ return match ? decodeURIComponent(match[1]) : null;
+}
+
+function setCookie(name: string, value: string, days: number = 365) {
+ if (typeof document === "undefined") return;
+ const expires = new Date(Date.now() + days * 864e5).toUTCString();
+ document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/; SameSite=Lax`;
+}
+
export type ViewStyle = "simple" | "calendar" | "list" | "grid";
export interface Task {
@@ -1393,6 +1408,10 @@ export default function WeeklyView() {
return () => el.removeEventListener("wheel", handler);
}, [showSomeday, somedayExpanded]);
const saveSetting = async (key: string, value: any) => {
+ // Save per-device settings to cookie as well
+ if (DEVICE_SETTINGS_KEYS.includes(key)) {
+ setCookie(`setting_${key}`, String(value));
+ }
try {
await fetch("/api/user/profile", {
method: "PATCH",
@@ -1469,24 +1488,34 @@ export default function WeeklyView() {
setTimeFormat(data.user.timeFormat || "12h");
setDateFormat(data.user.dateFormat || "MM/dd/yyyy");
setLanguage(data.user.language || "en");
- if (data.user.startHour !== undefined)
- setStartHour(data.user.startHour);
- if (data.user.endHour !== undefined) setEndHour(data.user.endHour);
+ if (data.user.startHour !== undefined) {
+ const cookieStartHour = getCookie("setting_startHour");
+ setStartHour(cookieStartHour ? Number(cookieStartHour) : data.user.startHour);
+ }
+ if (data.user.endHour !== undefined) {
+ const cookieEndHour = getCookie("setting_endHour");
+ setEndHour(cookieEndHour ? Number(cookieEndHour) : data.user.endHour);
+ }
if (data.user.viewStyle !== undefined) {
setViewStyle(data.user.viewStyle as ViewStyle);
setShowTimeGrid(data.user.showTimeGrid ?? true);
}
if (data.user.viewDays !== undefined) {
- savedViewDaysRef.current = data.user.viewDays;
+ // Cookie override for per-device viewDays
+ const cookieViewDays = getCookie("setting_viewDays");
+ const effectiveViewDays = cookieViewDays ? Number(cookieViewDays) : data.user.viewDays;
+ savedViewDaysRef.current = effectiveViewDays;
// Re-apply responsive constraints after loading saved preference
const width = window.innerWidth;
if (width <= 480) setViewDays(1);
else if (width <= 768) setViewDays(3);
- else if (width <= 1024) setViewDays(Math.min(data.user.viewDays, 5));
- else setViewDays(data.user.viewDays);
+ else if (width <= 1024) setViewDays(Math.min(effectiveViewDays, 5));
+ else setViewDays(effectiveViewDays);
+ }
+ if (data.user.cellDuration !== undefined) {
+ const cookieCellDuration = getCookie("setting_cellDuration");
+ setCellDuration((cookieCellDuration ? Number(cookieCellDuration) : data.user.cellDuration) as CellDuration);
}
- if (data.user.cellDuration !== undefined)
- setCellDuration(data.user.cellDuration as CellDuration);
setShowNextTask(data.user.showNextTask || false);
setCalendarEditMode(data.user.calendarEditMode || false);
if (data.user.fontSize)
@@ -3709,7 +3738,9 @@ export default function WeeklyView() {
) : syncError ? (
) : null}
- KW {getWeekNumber(currentWeekStart).toString().padStart(2, "0")}
+ KW {getWeekNumber((() => { const days = getVisibleDays(); const mid = days[Math.floor(days.length / 2)] || currentWeekStart; return mid; })()).toString().padStart(2, "0")}
+ |
+ {(() => { const days = getVisibleDays(); const mid = days[Math.floor(days.length / 2)] || currentWeekStart; return mid; })().getFullYear()}
{/* Right: Settings + Overflow */}
@@ -3920,7 +3951,7 @@ export default function WeeklyView() {
color: adjustColorForDarkMode(profile.cwColor || "#333333", darkMode),
filter: "brightness(var(--weekly-header-brightness, 1))"
}}>
- KW {getWeekNumber(currentWeekStart).toString().padStart(2, "0")}
+ KW {getWeekNumber((() => { const days = getVisibleDays(); const mid = days[Math.floor(days.length / 2)] || currentWeekStart; return mid; })()).toString().padStart(2, "0")}
|
)}
+
+ {task.title}
+
{task.subTasks && task.subTasks.length > 0 && !isSubTask && (