feat: UI improvements A-F — cookies, KW fix, mobile header, indicators
This commit is contained in:
parent
9be39b0b30
commit
b3503d9e4d
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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 ? (
|
||||
<AlertCircle size={14} className="text-red-500" />
|
||||
) : null}
|
||||
<span>KW {getWeekNumber(currentWeekStart).toString().padStart(2, "0")}</span>
|
||||
<span>KW {getWeekNumber((() => { const days = getVisibleDays(); const mid = days[Math.floor(days.length / 2)] || currentWeekStart; return mid; })()).toString().padStart(2, "0")}</span>
|
||||
<span className="text-gray-400">|</span>
|
||||
<span>{(() => { const days = getVisibleDays(); const mid = days[Math.floor(days.length / 2)] || currentWeekStart; return mid; })().getFullYear()}</span>
|
||||
</div>
|
||||
|
||||
{/* 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")}
|
||||
</span>
|
||||
<span className="text-gray-400">|</span>
|
||||
<span
|
||||
@ -6354,6 +6385,16 @@ function TaskItem({
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
style={{
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
overflow: "visible",
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
{task.title}
|
||||
</span>
|
||||
{task.subTasks && task.subTasks.length > 0 && !isSubTask && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
@ -6378,15 +6419,6 @@ function TaskItem({
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
style={{
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
overflow: "visible"
|
||||
}}
|
||||
>
|
||||
{task.title}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div className="task-actions z-20">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user