refactor: Phase 2 — consolidate profile state reads
Replace all individual useState reads (viewStyle, timeFormat, language, cellDuration, showTimeGrid, showNextTask, showSomeday, showAllDay, etc.) with profile.fieldName reads. Individual useState declarations remain; only read sites are migrated. No UI logic changed. - getEffective() calls use profile.fieldName as global fallback - workingHoursStart/End derived from profile.startHour/endHour - All JSX reads (className, conditionals, child props) use profile.* - SettingsSidebar value props use profile.* (setters unchanged) - Add weekStartDay: 1 to profile defaults - ProjectsSidebar function body reverted (uses language prop, not profile) - darkMode excluded: localStorage-only, not an API profile field v1.80.1
This commit is contained in:
parent
a5f6edd82d
commit
bfb749a8a6
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "my-weekly-todo-list",
|
||||
"version": "1.80.0",
|
||||
"version": "1.80.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": {
|
||||
|
||||
@ -849,6 +849,7 @@ export default function WeeklyView() {
|
||||
dayHeaderGap: "0.75em",
|
||||
dateVerticalAlign: "middle",
|
||||
allDayPosition: "above",
|
||||
weekStartDay: 1,
|
||||
focusTimerDuration: 25,
|
||||
focusBreakDuration: 5,
|
||||
headlineFont: "Oswald",
|
||||
@ -951,18 +952,18 @@ export default function WeeklyView() {
|
||||
viewSettingsRef.current = viewSettings;
|
||||
|
||||
const getEffective = <K extends keyof PerViewOverrides>(key: K, globalVal: PerViewOverrides[K]): PerViewOverrides[K] => {
|
||||
const vs = viewSettingsRef.current[viewStyle];
|
||||
const vs = viewSettingsRef.current[profile.viewStyle];
|
||||
if (vs && vs[key] !== undefined) return vs[key] as PerViewOverrides[K];
|
||||
return globalVal;
|
||||
};
|
||||
const isPerView = (key: keyof PerViewOverrides): boolean => {
|
||||
const vs = viewSettingsRef.current[viewStyle];
|
||||
const vs = viewSettingsRef.current[profile.viewStyle];
|
||||
return !!(vs && vs[key] !== undefined);
|
||||
};
|
||||
const saveViewSetting = async <K extends keyof PerViewOverrides>(key: K, value: PerViewOverrides[K], perView: boolean) => {
|
||||
const updated = { ...viewSettingsRef.current };
|
||||
if (perView) {
|
||||
updated[viewStyle] = { ...(updated[viewStyle] || {}), [key]: value };
|
||||
updated[profile.viewStyle] = { ...(updated[profile.viewStyle] || {}), [key]: value };
|
||||
} else {
|
||||
// Remove per-view overrides for this key from ALL views and set globally
|
||||
for (const v of Object.keys(updated)) {
|
||||
@ -987,9 +988,9 @@ export default function WeeklyView() {
|
||||
if (isPerView(key)) {
|
||||
// Remove per-view override (revert to global)
|
||||
const updated = { ...viewSettingsRef.current };
|
||||
if (updated[viewStyle]) {
|
||||
const { [key]: _, ...rest } = updated[viewStyle] as any;
|
||||
updated[viewStyle] = rest;
|
||||
if (updated[profile.viewStyle]) {
|
||||
const { [key]: _, ...rest } = updated[profile.viewStyle] as any;
|
||||
updated[profile.viewStyle] = rest;
|
||||
}
|
||||
viewSettingsRef.current = updated;
|
||||
setViewSettings(updated);
|
||||
@ -1011,17 +1012,17 @@ export default function WeeklyView() {
|
||||
const [showAllDay, setShowAllDay] = useState(true);
|
||||
|
||||
// Effective per-view values (override if set for current view, else global)
|
||||
const effectiveHourLabelFormat = getEffective("hourLabelFormat", hourLabelFormat);
|
||||
const effectiveShowSubHourSlots = getEffective("showSubHourSlots", showSubHourSlots);
|
||||
const effectiveHourLabelFormat = getEffective("hourLabelFormat", profile.hourLabelFormat ?? "short");
|
||||
const effectiveShowSubHourSlots = getEffective("showSubHourSlots", profile.showSubHourSlots ?? true);
|
||||
const effectiveWeatherEnabled = getEffective("weatherEnabled", profile.weatherEnabled);
|
||||
const effectiveWeatherDisplay = (getEffective("weatherDisplay", WEATHER_DISPLAY_DEFAULTS) || WEATHER_DISPLAY_DEFAULTS) as WeatherDisplayKey[];
|
||||
const effectiveShowTaskCheckboxes = getEffective("showTaskCheckboxes", profile.showTaskCheckboxes);
|
||||
const effectiveShowProjectIcons = getEffective("showProjectIcons", profile.showProjectIcons);
|
||||
const effectiveShowSomeday = getEffective("showSomeday", showSomeday);
|
||||
const effectiveShowAllDay = getEffective("showAllDayEvents", showAllDay);
|
||||
const effectiveAllDayPosition = getEffective("allDayPosition", allDayPosition) || "above";
|
||||
const effectiveShowSomeday = getEffective("showSomeday", profile.showSomeday ?? true);
|
||||
const effectiveShowAllDay = getEffective("showAllDayEvents", profile.showAllDayEvents ?? true);
|
||||
const effectiveAllDayPosition = getEffective("allDayPosition", profile.allDayPosition ?? "above") || "above";
|
||||
const effectiveShowCompletedTasks = getEffective("showCompletedTasks", profile.showCompletedTasks !== false);
|
||||
const effectiveCellDuration = getEffective("cellDuration", cellDuration) as CellDuration;
|
||||
const effectiveCellDuration = getEffective("cellDuration", profile.cellDuration ?? 30) as CellDuration;
|
||||
const effectiveStartHour = getEffective("startHour", profile.startHour ?? 8) ?? 8;
|
||||
const effectiveEndHour = getEffective("endHour", profile.endHour ?? 18) ?? 18;
|
||||
|
||||
@ -1321,14 +1322,14 @@ export default function WeeklyView() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!mounted) return;
|
||||
localStorage.setItem("weekly-week-start", String(weekStartDay));
|
||||
localStorage.setItem("weekly-week-start", String(profile.weekStartDay ?? 1));
|
||||
// REMOVED: Re-align current week start when start day changes
|
||||
// This was forcing the view to snap to Monday, breaking the "Yesterday as first column" setting.
|
||||
// setCurrentWeekStart(prev => getStartOfWeek(prev, weekStartDay));
|
||||
}, [weekStartDay, mounted]);
|
||||
}, [profile.weekStartDay, mounted]);
|
||||
|
||||
// Translation helper
|
||||
const t = translations[language] || translations["en"];
|
||||
const t = translations[profile.language] || translations["en"];
|
||||
|
||||
// Refs for scroll
|
||||
const dayColumnsRef = useRef<HTMLDivElement[]>([]);
|
||||
@ -1371,7 +1372,7 @@ export default function WeeklyView() {
|
||||
window.removeEventListener('resize', updateHeight);
|
||||
if (resizeObserver) resizeObserver.disconnect();
|
||||
};
|
||||
}, [dayHeaderRef.current, cellDuration, viewDays, isMobile, profile.mobileDateLayout, profile.dateLayout, profile.dateAlignment, profile.dayHeaderGap, profile.headlineFontSize, profile.headlineFontWeight, profile.dateFontSize, profile.dateVerticalAlign, profile.headerDisplay, profile.weekdayFormat, viewStyle]);
|
||||
}, [dayHeaderRef.current, profile.cellDuration, viewDays, isMobile, profile.mobileDateLayout, profile.dateLayout, profile.dateAlignment, profile.dayHeaderGap, profile.headlineFontSize, profile.headlineFontWeight, profile.dateFontSize, profile.dateVerticalAlign, profile.headerDisplay, profile.weekdayFormat, profile.viewStyle]);
|
||||
const somedaySectionRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
// Unified scroll sync handlers
|
||||
@ -1383,8 +1384,8 @@ export default function WeeklyView() {
|
||||
};
|
||||
|
||||
const jumpToHour = (hour: number) => {
|
||||
const slotsPerHour = 60 / cellDuration;
|
||||
const slotHeight = getSlotHeight(cellDuration);
|
||||
const slotsPerHour = 60 / effectiveCellDuration;
|
||||
const slotHeight = getSlotHeight(effectiveCellDuration);
|
||||
const scrollOffset = hour * slotsPerHour * slotHeight;
|
||||
|
||||
console.log(`[SCROLL] Jumping to hour ${hour} (offset ${scrollOffset}px)`);
|
||||
@ -1449,8 +1450,8 @@ export default function WeeklyView() {
|
||||
};
|
||||
|
||||
// Working hours range (configurable)
|
||||
const workingHoursStart = startHour;
|
||||
const workingHoursEnd = endHour;
|
||||
const workingHoursStart = profile.startHour ?? 8;
|
||||
const workingHoursEnd = profile.endHour ?? 18;
|
||||
|
||||
// Fetch calendar events
|
||||
// Find connectionId for a given calendarId
|
||||
@ -1984,7 +1985,7 @@ export default function WeeklyView() {
|
||||
|
||||
// Mobile: show a sticky day bar by reading scroll position on the actual grid scroll container
|
||||
useEffect(() => {
|
||||
if (!isMobile || !showTimeGrid) return;
|
||||
if (!isMobile || !profile.showTimeGrid) return;
|
||||
const grid = gridRef.current;
|
||||
if (!grid) return;
|
||||
|
||||
@ -2009,7 +2010,7 @@ export default function WeeklyView() {
|
||||
const dateStr = (currentCol as Element).getAttribute('data-date');
|
||||
if (dateStr) {
|
||||
const d = new Date(dateStr + 'T00:00:00');
|
||||
const dayNames = language === 'de'
|
||||
const dayNames = profile.language === 'de'
|
||||
? ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']
|
||||
: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
const label = `${dayNames[d.getDay()]} ${d.getDate()}.${d.getMonth() + 1}.`;
|
||||
@ -2021,7 +2022,7 @@ export default function WeeklyView() {
|
||||
grid.addEventListener('scroll', updateStickyDay, { passive: true });
|
||||
updateStickyDay();
|
||||
return () => grid.removeEventListener('scroll', updateStickyDay);
|
||||
}, [isMobile, showTimeGrid, currentWeekStart, viewDays, language]);
|
||||
}, [isMobile, profile.showTimeGrid, currentWeekStart, viewDays, profile.language]);
|
||||
|
||||
// Compute the goal date key: for "week" scope, normalize to Monday of that week; for "day", use the exact date
|
||||
const getGoalDateKey = useCallback(
|
||||
@ -2212,7 +2213,7 @@ export default function WeeklyView() {
|
||||
setStartHour(data.user.startHour);
|
||||
if (data.user.endHour !== undefined)
|
||||
setEndHour(data.user.endHour);
|
||||
if (data.user.viewStyle !== undefined) {
|
||||
if (data.user.profile.viewStyle !== undefined) {
|
||||
setViewStyle(data.user.viewStyle as ViewStyle);
|
||||
setShowTimeGrid(data.user.showTimeGrid ?? true);
|
||||
}
|
||||
@ -5029,7 +5030,7 @@ export default function WeeklyView() {
|
||||
24,
|
||||
);
|
||||
|
||||
const fontSizeScale = fontSize === "S" ? 0.85 : fontSize === "L" ? 1.15 : 1;
|
||||
const fontSizeScale = profile.fontSize === "S" ? 0.85 : profile.fontSize === "L" ? 1.15 : 1;
|
||||
const mobileScale = isMobile ? (profile.mobileFontScale || 1.0) : 1.0;
|
||||
const fontVal = (v: string | undefined) => v && v !== "__custom__" ? v : "";
|
||||
const scaleRem = (base: string) => {
|
||||
@ -5049,8 +5050,8 @@ export default function WeeklyView() {
|
||||
"--weekly-item-hover": activeTheme.color0,
|
||||
} : {}),
|
||||
"--weekly-font-headline":
|
||||
fontVal(profile.headlineFont) || headlineFont
|
||||
? `"${fontVal(profile.headlineFont) || headlineFont}", sans-serif`
|
||||
fontVal(profile.headlineFont)
|
||||
? `"${fontVal(profile.headlineFont)}", sans-serif`
|
||||
: "var(--font-headline)",
|
||||
"--weekly-headline-size": scaleRem(profile.headlineFontSize || "1.25rem"),
|
||||
"--weekly-headline-weight": profile.headlineFontWeight || "900",
|
||||
@ -5088,13 +5089,12 @@ export default function WeeklyView() {
|
||||
"--weekly-task-size": scaleRem(profile.taskFontSize || "0.9rem"),
|
||||
"--weekly-task-weight": profile.taskFontWeight || "400",
|
||||
"--weekly-event-font":
|
||||
fontVal(profile.eventFontFamily) || eventFontFamily
|
||||
? `"${fontVal(profile.eventFontFamily) || eventFontFamily}", sans-serif`
|
||||
fontVal(profile.eventFontFamily)
|
||||
? `"${fontVal(profile.eventFontFamily)}", sans-serif`
|
||||
: "var(--weekly-font)",
|
||||
"--weekly-event-size": scaleRem(profile.eventFontSize || eventFontSize || "0.85rem"),
|
||||
"--weekly-event-weight":
|
||||
profile.eventFontWeight || eventFontWeight || "400",
|
||||
"--font-weight-body": profile.fontWeight || fontWeight || "400",
|
||||
"--weekly-event-size": scaleRem(profile.eventFontSize || "0.85rem"),
|
||||
"--weekly-event-weight": profile.eventFontWeight || "400",
|
||||
"--font-weight-body": profile.fontWeight || "400",
|
||||
"--weekly-weekend-sat": activeTheme?.color3 || (darkMode
|
||||
? invertColor(profile.weekendColorSat || "#666666")
|
||||
: profile.weekendColorSat || "#666666"),
|
||||
@ -5125,7 +5125,7 @@ export default function WeeklyView() {
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<div style={{ color: "var(--weekly-text-light)" }}>
|
||||
{translations[language]?.loading || translations["en"].loading}
|
||||
{translations[profile.language]?.loading || translations["en"].loading}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -5172,7 +5172,7 @@ export default function WeeklyView() {
|
||||
style={isAllDayExpanded && allDayHeight ? { height: `${allDayHeight}px`, overflowY: 'auto' } : undefined}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
{showTimeGrid && (
|
||||
{profile.showTimeGrid && (
|
||||
<div
|
||||
className="all-day-label-column"
|
||||
onClick={() => setIsAllDayExpanded(!isAllDayExpanded)}
|
||||
@ -5293,7 +5293,7 @@ export default function WeeklyView() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`weekly-container ${darkMode ? "dark-mode" : ""} font-size-${fontSize.toLowerCase()} ${viewStyle}-view ${showTimeGrid ? "time-grid-on" : "time-grid-off"}`}
|
||||
className={`weekly-container ${darkMode ? "dark-mode" : ""} font-size-${(profile.fontSize ?? "M").toLowerCase()} ${profile.viewStyle}-view ${profile.showTimeGrid ? "time-grid-on" : "time-grid-off"}`}
|
||||
style={containerStyle}
|
||||
>
|
||||
{/* Mobile sticky day indicator */}
|
||||
@ -5349,30 +5349,30 @@ export default function WeeklyView() {
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: "0.85rem", fontWeight: 700, color: darkMode ? "#e5e7eb" : "#333", marginBottom: "2px" }}>
|
||||
{language === "de" ? "Einstellungen" : "Preferences"}
|
||||
{profile.language === "de" ? "Einstellungen" : "Preferences"}
|
||||
</div>
|
||||
|
||||
{/* Navigation Row */}
|
||||
<div style={{ display: "flex", justifyContent: "center", gap: "4px", padding: "4px 0", borderBottom: `1px solid ${darkMode ? "#333" : "#e5e7eb"}`, paddingBottom: "10px" }}>
|
||||
<button onClick={() => { goToPrevWeek(); setShowQuickSettings(false); }} style={{ ...qsBtnStyle(darkMode), minWidth: "36px" }} title="Previous Week"><ChevronsLeft size={16} /></button>
|
||||
<button onClick={() => { goToPrevDay(); setShowQuickSettings(false); }} style={{ ...qsBtnStyle(darkMode), minWidth: "36px" }} title="Previous Day"><ChevronLeft size={16} /></button>
|
||||
<button onClick={() => { goToToday(); setShowQuickSettings(false); }} style={{ ...qsBtnStyle(darkMode), minWidth: "56px", fontWeight: 700, fontSize: "0.7rem" }} title="Today">{language === "de" ? "Heute" : "Today"}</button>
|
||||
<button onClick={() => { goToToday(); setShowQuickSettings(false); }} style={{ ...qsBtnStyle(darkMode), minWidth: "56px", fontWeight: 700, fontSize: "0.7rem" }} title="Today">{profile.language === "de" ? "Heute" : "Today"}</button>
|
||||
<button onClick={() => { goToNextDay(); setShowQuickSettings(false); }} style={{ ...qsBtnStyle(darkMode), minWidth: "36px" }} title="Next Day"><ChevronRight size={16} /></button>
|
||||
<button onClick={() => { goToNextWeek(); setShowQuickSettings(false); }} style={{ ...qsBtnStyle(darkMode), minWidth: "36px" }} title="Next Week"><ChevronsRight size={16} /></button>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "1px" }}>
|
||||
<button onClick={() => { setIsSearchOpen(true); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><Search size={16} /> <span>{language === "de" ? "Suche" : "Search"}</span></button>
|
||||
<button onClick={() => { setShowDatePicker(true); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><Calendar size={16} /> <span>{language === "de" ? "Datum wählen" : "Jump to date"}</span></button>
|
||||
<button onClick={() => { const now = new Date(); setCalendarEventModal({ isOpen: true, event: undefined, initialDate: now, initialStartTime: `${String(now.getHours()).padStart(2, "0")}:00` }); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><Plus size={16} /> <span>{language === "de" ? "Kalendereintrag" : "Calendar Event"}</span></button>
|
||||
<button onClick={() => { setShowProjectsSidebar(true); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><FolderPlus size={16} /> <span>{language === "de" ? "Projekte" : "Projects"}</span></button>
|
||||
<button onClick={() => { setIsRecurringTasksOpen(true); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><Repeat size={16} /> <span>{language === "de" ? "Wiederkehrend" : "Recurring Tasks"}</span></button>
|
||||
<button onClick={() => { const nv = !showNextTask; setShowNextTask(nv); saveSetting("showNextTask", nv); }} style={qsActionStyle(darkMode)}>
|
||||
{showNextTask ? <Play size={16} /> : <Target size={16} />}
|
||||
<span>{showNextTask ? (language === "de" ? "Nächste Aufgabe" : "Next Task") : (language === "de" ? "Ziel" : "Goal")}</span>
|
||||
<button onClick={() => { setIsSearchOpen(true); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><Search size={16} /> <span>{profile.language === "de" ? "Suche" : "Search"}</span></button>
|
||||
<button onClick={() => { setShowDatePicker(true); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><Calendar size={16} /> <span>{profile.language === "de" ? "Datum wählen" : "Jump to date"}</span></button>
|
||||
<button onClick={() => { const now = new Date(); setCalendarEventModal({ isOpen: true, event: undefined, initialDate: now, initialStartTime: `${String(now.getHours()).padStart(2, "0")}:00` }); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><Plus size={16} /> <span>{profile.language === "de" ? "Kalendereintrag" : "Calendar Event"}</span></button>
|
||||
<button onClick={() => { setShowProjectsSidebar(true); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><FolderPlus size={16} /> <span>{profile.language === "de" ? "Projekte" : "Projects"}</span></button>
|
||||
<button onClick={() => { setIsRecurringTasksOpen(true); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><Repeat size={16} /> <span>{profile.language === "de" ? "Wiederkehrend" : "Recurring Tasks"}</span></button>
|
||||
<button onClick={() => { const nv = !profile.showNextTask; setShowNextTask(nv); saveSetting("showNextTask", nv); }} style={qsActionStyle(darkMode)}>
|
||||
{profile.showNextTask ? <Play size={16} /> : <Target size={16} />}
|
||||
<span>{profile.showNextTask ? (profile.language === "de" ? "Nächste Aufgabe" : "Next Task") : (profile.language === "de" ? "Ziel" : "Goal")}</span>
|
||||
</button>
|
||||
<button onClick={() => { setShowFocusMode(true); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><Zap size={16} /> <span>{language === "de" ? "Fokus" : "Focus"}</span></button>
|
||||
<button onClick={() => { setShowFocusMode(true); setShowQuickSettings(false); }} style={qsActionStyle(darkMode)}><Zap size={16} /> <span>{profile.language === "de" ? "Fokus" : "Focus"}</span></button>
|
||||
</div>
|
||||
|
||||
{/* Separator */}
|
||||
@ -5381,7 +5381,7 @@ export default function WeeklyView() {
|
||||
{/* View Style */}
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
|
||||
<span style={{ fontSize: "0.7rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
{language === "de" ? "Ansicht" : "View"}
|
||||
{profile.language === "de" ? "Ansicht" : "View"}
|
||||
</span>
|
||||
<div style={{ display: "flex", gap: "4px" }}>
|
||||
{[
|
||||
@ -5391,7 +5391,7 @@ export default function WeeklyView() {
|
||||
{ key: "kanban", icon: <Kanban size={13} /> },
|
||||
].map((v) => (
|
||||
<button key={v.key} onClick={() => { setViewStyle(v.key as any); saveSetting("viewStyle", v.key); }}
|
||||
style={{ flex: 1, padding: "5px 0", borderRadius: "6px", border: "none", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: viewStyle === v.key ? 700 : 400, background: viewStyle === v.key ? "#0ea5e9" : (darkMode ? "#1f2937" : "#e5e7eb"), color: viewStyle === v.key ? "#fff" : (darkMode ? "#9ca3af" : "#6b7280") }}>
|
||||
style={{ flex: 1, padding: "5px 0", borderRadius: "6px", border: "none", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: profile.viewStyle ===v.key ? 700 : 400, background: profile.viewStyle ===v.key ? "#0ea5e9" : (darkMode ? "#1f2937" : "#e5e7eb"), color: profile.viewStyle ===v.key ? "#fff" : (darkMode ? "#9ca3af" : "#6b7280") }}>
|
||||
{v.icon}
|
||||
</button>
|
||||
))}
|
||||
@ -5401,7 +5401,7 @@ export default function WeeklyView() {
|
||||
{/* Columns / Days */}
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
|
||||
<span style={{ fontSize: "0.7rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
{language === "de" ? "Spalten" : "Days"}
|
||||
{profile.language === "de" ? "Spalten" : "Days"}
|
||||
</span>
|
||||
<div style={{ display: "flex", gap: "4px" }}>
|
||||
{[1, 2, 3, 5, 7].map((num) => (
|
||||
@ -5414,10 +5414,10 @@ export default function WeeklyView() {
|
||||
</div>
|
||||
|
||||
{/* Slot Duration (only with time grid) */}
|
||||
{showTimeGrid && (
|
||||
{profile.showTimeGrid && (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
|
||||
<span style={{ fontSize: "0.7rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
{language === "de" ? "Zeitfenster" : "Slot"}
|
||||
{profile.language === "de" ? "Zeitfenster" : "Slot"}
|
||||
</span>
|
||||
<div style={{ display: "flex", gap: "4px" }}>
|
||||
{[15, 30, 60].map((d) => (
|
||||
@ -5433,12 +5433,12 @@ export default function WeeklyView() {
|
||||
{/* Text size */}
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
|
||||
<span style={{ fontSize: "0.7rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
{language === "de" ? "Textgröße" : "Text size"}
|
||||
{profile.language === "de" ? "Textgröße" : "Text size"}
|
||||
</span>
|
||||
<div style={{ display: "flex", gap: "4px" }}>
|
||||
{(["S", "M", "L"] as const).map((size) => (
|
||||
<button key={size} onClick={() => { setFontSize(size); saveSetting("fontSize", size); }}
|
||||
style={{ padding: "4px 10px", fontSize: "0.8rem", borderRadius: "6px", border: "none", cursor: "pointer", fontWeight: fontSize === size ? 700 : 400, background: fontSize === size ? (darkMode ? "#374151" : "#333") : (darkMode ? "#1f2937" : "#e5e7eb"), color: fontSize === size ? "#fff" : (darkMode ? "#9ca3af" : "#6b7280") }}>
|
||||
style={{ padding: "4px 10px", fontSize: "0.8rem", borderRadius: "6px", border: "none", cursor: "pointer", fontWeight: profile.fontSize === size ? 700 : 400, background: profile.fontSize === size ? (darkMode ? "#374151" : "#333") : (darkMode ? "#1f2937" : "#e5e7eb"), color: profile.fontSize === size ? "#fff" : (darkMode ? "#9ca3af" : "#6b7280") }}>
|
||||
{size}
|
||||
</button>
|
||||
))}
|
||||
@ -5450,31 +5450,31 @@ export default function WeeklyView() {
|
||||
|
||||
{/* Toggle switches */}
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{language === "de" ? "Irgendwann" : "Someday"}</span>
|
||||
<button onClick={() => { const v = !showSomeday; setShowSomeday(v); saveSetting("showSomeday", v); }} style={{ background: "none", border: "none", cursor: "pointer", padding: "2px", color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{profile.language === "de" ? "Irgendwann" : "Someday"}</span>
|
||||
<button onClick={() => { const v = !profile.showSomeday; setShowSomeday(v); saveSetting("showSomeday", v); }} style={{ background: "none", border: "none", cursor: "pointer", padding: "2px", color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
{effectiveShowSomeday ? <Eye size={16} /> : <EyeOff size={16} />}
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{language === "de" ? "Zeitplan" : "Schedule"}</span>
|
||||
<button onClick={() => { const v = !showTimeGrid; setShowTimeGrid(v); saveSetting("showTimeGrid", v); }} style={{ background: "none", border: "none", cursor: "pointer", padding: "2px", color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
{showTimeGrid ? <Eye size={16} /> : <EyeOff size={16} />}
|
||||
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{profile.language === "de" ? "Zeitplan" : "Schedule"}</span>
|
||||
<button onClick={() => { const v = !profile.showTimeGrid; setShowTimeGrid(v); saveSetting("showTimeGrid", v); }} style={{ background: "none", border: "none", cursor: "pointer", padding: "2px", color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
{profile.showTimeGrid ? <Eye size={16} /> : <EyeOff size={16} />}
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{language === "de" ? "Ganztägig" : "All-day"}</span>
|
||||
<button onClick={() => { const v = !showAllDay; setShowAllDay(v); saveSetting("showAllDayEvents", v); }} style={{ background: "none", border: "none", cursor: "pointer", padding: "2px", color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{profile.language === "de" ? "Ganztägig" : "All-day"}</span>
|
||||
<button onClick={() => { const v = !profile.showAllDayEvents; setShowAllDay(v); saveSetting("showAllDayEvents", v); }} style={{ background: "none", border: "none", cursor: "pointer", padding: "2px", color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
{effectiveShowAllDay ? <Eye size={16} /> : <EyeOff size={16} />}
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{language === "de" ? "Checkboxen" : "Checkboxes"}</span>
|
||||
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{profile.language === "de" ? "Checkboxen" : "Checkboxes"}</span>
|
||||
<button onClick={() => { const v = !profile.showTaskCheckboxes; setProfile({ ...profile, showTaskCheckboxes: v }); saveSetting("showTaskCheckboxes", v); }} style={{ background: "none", border: "none", cursor: "pointer", padding: "2px", color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
{effectiveShowTaskCheckboxes ? <Eye size={16} /> : <EyeOff size={16} />}
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{language === "de" ? "Projekt-Icons" : "Project Icons"}</span>
|
||||
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{profile.language === "de" ? "Projekt-Icons" : "Project Icons"}</span>
|
||||
<button onClick={() => { const v = !profile.showProjectIcons; setProfile({ ...profile, showProjectIcons: v }); saveSetting("showProjectIcons", v); }} style={{ background: "none", border: "none", cursor: "pointer", padding: "2px", color: darkMode ? "#9ca3af" : "#6b7280" }}>
|
||||
{effectiveShowProjectIcons ? <Eye size={16} /> : <EyeOff size={16} />}
|
||||
</button>
|
||||
@ -5482,30 +5482,30 @@ export default function WeeklyView() {
|
||||
|
||||
{/* Start on */}
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
|
||||
<span style={{ fontSize: "0.7rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{language === "de" ? "Starten mit" : "Start on"}</span>
|
||||
<span style={{ fontSize: "0.7rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{profile.language === "de" ? "Starten mit" : "Start on"}</span>
|
||||
<div style={{ display: "flex", gap: "4px" }}>
|
||||
<button onClick={() => { setProfile({ ...profile, startDayOffset: 0 }); saveSetting("startDayOffset", 0); const d = new Date(); d.setHours(0, 0, 0, 0); setCurrentWeekStart(d); }}
|
||||
style={{ padding: "4px 10px", fontSize: "0.75rem", borderRadius: "6px", border: "none", cursor: "pointer", fontWeight: (profile.startDayOffset || 0) === 0 ? 700 : 400, background: (profile.startDayOffset || 0) === 0 ? (darkMode ? "#374151" : "#333") : (darkMode ? "#1f2937" : "#e5e7eb"), color: (profile.startDayOffset || 0) === 0 ? "#fff" : (darkMode ? "#9ca3af" : "#6b7280") }}>
|
||||
{language === "de" ? "Heute" : "Today"}
|
||||
{profile.language === "de" ? "Heute" : "Today"}
|
||||
</button>
|
||||
<button onClick={() => { setProfile({ ...profile, startDayOffset: -1 }); saveSetting("startDayOffset", -1); const d = new Date(); d.setHours(0, 0, 0, 0); d.setDate(d.getDate() - 1); setCurrentWeekStart(d); }}
|
||||
style={{ padding: "4px 10px", fontSize: "0.75rem", borderRadius: "6px", border: "none", cursor: "pointer", fontWeight: profile.startDayOffset === -1 ? 700 : 400, background: profile.startDayOffset === -1 ? (darkMode ? "#374151" : "#333") : (darkMode ? "#1f2937" : "#e5e7eb"), color: profile.startDayOffset === -1 ? "#fff" : (darkMode ? "#9ca3af" : "#6b7280") }}>
|
||||
{language === "de" ? "Gestern" : "Yesterday"}
|
||||
{profile.language === "de" ? "Gestern" : "Yesterday"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Display mode */}
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
|
||||
<span style={{ fontSize: "0.7rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{language === "de" ? "Anzeige" : "Display"}</span>
|
||||
<span style={{ fontSize: "0.7rem", fontWeight: 600, color: darkMode ? "#9ca3af" : "#6b7280" }}>{profile.language === "de" ? "Anzeige" : "Display"}</span>
|
||||
<div style={{ display: "flex", gap: "4px" }}>
|
||||
<button onClick={() => setDarkMode(false)}
|
||||
style={{ padding: "4px 10px", fontSize: "0.75rem", borderRadius: "6px", border: "none", cursor: "pointer", display: "flex", alignItems: "center", gap: "4px", fontWeight: !darkMode ? 700 : 400, background: !darkMode ? "#333" : (darkMode ? "#1f2937" : "#e5e7eb"), color: !darkMode ? "#fff" : (darkMode ? "#9ca3af" : "#6b7280") }}>
|
||||
<Sun size={12} /> {language === "de" ? "Hell" : "Light"}
|
||||
<Sun size={12} /> {profile.language === "de" ? "Hell" : "Light"}
|
||||
</button>
|
||||
<button onClick={() => setDarkMode(true)}
|
||||
style={{ padding: "4px 10px", fontSize: "0.75rem", borderRadius: "6px", border: "none", cursor: "pointer", display: "flex", alignItems: "center", gap: "4px", fontWeight: darkMode ? 700 : 400, background: darkMode ? "#374151" : "#e5e7eb", color: darkMode ? "#fff" : "#6b7280" }}>
|
||||
<Moon size={12} /> {language === "de" ? "Dunkel" : "Dark"}
|
||||
<Moon size={12} /> {profile.language === "de" ? "Dunkel" : "Dark"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -5529,7 +5529,7 @@ export default function WeeklyView() {
|
||||
style={{ display: "flex", alignItems: "center", gap: "6px", background: "none", border: "none", cursor: "pointer", fontSize: "0.75rem", color: darkMode ? "#6b7280" : "#9ca3af", padding: "4px 0" }}
|
||||
>
|
||||
<PanelLeftClose size={16} />
|
||||
{language === "de" ? "Ausblenden" : "Hide"}
|
||||
{profile.language === "de" ? "Ausblenden" : "Hide"}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
@ -5539,7 +5539,7 @@ export default function WeeklyView() {
|
||||
{showProjectsSidebar && (
|
||||
<ProjectsSidebar
|
||||
darkMode={darkMode}
|
||||
language={language}
|
||||
language={profile.language}
|
||||
projects={projects}
|
||||
onProjectsChanged={fetchProjects}
|
||||
onClose={() => setShowProjectsSidebar(false)}
|
||||
@ -5608,14 +5608,14 @@ export default function WeeklyView() {
|
||||
d.getFullYear() === today.getFullYear()
|
||||
);
|
||||
const refDate = isTodayInWeek ? today : getCWReferenceDate(getVisibleDays());
|
||||
return refDate.toLocaleDateString(language, { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||
return refDate.toLocaleDateString(profile.language, { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||
})() :
|
||||
profile.headerDisplay === "month_year" ?
|
||||
getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { month: 'long', year: 'numeric' }) :
|
||||
getCWReferenceDate(getVisibleDays()).toLocaleDateString(profile.language, { month: 'long', year: 'numeric' }) :
|
||||
profile.headerDisplay === "custom" ?
|
||||
formatCustomHeader(profile.headerCustomFormat || "KW WW | YYYY", getVisibleDays(), language, t) :
|
||||
formatCustomHeader(profile.headerCustomFormat || "KW WW | YYYY", getVisibleDays(), profile.language, t) :
|
||||
profile.headerDisplay === "month" ?
|
||||
getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { month: "long" }) :
|
||||
getCWReferenceDate(getVisibleDays()).toLocaleDateString(profile.language, { month: "long" }) :
|
||||
`KW ${getWeekNumber(getCWReferenceDate(getVisibleDays())).toString().padStart(2, "0")} | ${getCWReferenceDate(getVisibleDays()).getFullYear()}`
|
||||
}
|
||||
</span>
|
||||
@ -5638,7 +5638,7 @@ export default function WeeklyView() {
|
||||
<button
|
||||
onClick={() => setShowQuickSettings(true)}
|
||||
className="p-1.5 rounded text-gray-400 hover:text-gray-700 hover:bg-gray-100 dark:hover:text-white dark:hover:bg-gray-700 transition-colors"
|
||||
title={language === "de" ? "Schnelleinstellungen" : "Quick Settings"}
|
||||
title={profile.language === "de" ? "Schnelleinstellungen" : "Quick Settings"}
|
||||
>
|
||||
<PanelLeftOpen size={16} />
|
||||
</button>
|
||||
@ -5647,28 +5647,28 @@ export default function WeeklyView() {
|
||||
<div className="flex items-center gap-0.5 bg-gray-100 dark:bg-gray-800 rounded p-0.5" title={t.viewStyle}>
|
||||
<button
|
||||
onClick={() => { setViewStyle("simple"); setShowTimeGrid(true); saveSetting("viewStyle", "simple"); saveSetting("showTimeGrid", true); }}
|
||||
className={`p-1.5 rounded transition-colors ${viewStyle === "simple" ? "bg-white shadow-sm text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}
|
||||
className={`p-1.5 rounded transition-colors ${profile.viewStyle ==="simple" ? "bg-white shadow-sm text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}
|
||||
title={t.simpleView}
|
||||
>
|
||||
<Calendar size={16} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setViewStyle("calendar"); setShowTimeGrid(true); saveSetting("viewStyle", "calendar"); saveSetting("showTimeGrid", true); }}
|
||||
className={`p-1.5 rounded transition-colors ${viewStyle === "calendar" ? "bg-white shadow-sm text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}
|
||||
className={`p-1.5 rounded transition-colors ${profile.viewStyle ==="calendar" ? "bg-white shadow-sm text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}
|
||||
title={t.calendarView}
|
||||
>
|
||||
<CalendarDays size={16} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setViewStyle("list"); setShowTimeGrid(false); saveSetting("viewStyle", "list"); saveSetting("showTimeGrid", false); }}
|
||||
className={`p-1.5 rounded transition-colors ${viewStyle === "list" ? "bg-white shadow-sm text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}
|
||||
className={`p-1.5 rounded transition-colors ${profile.viewStyle ==="list" ? "bg-white shadow-sm text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}
|
||||
title={t.listView}
|
||||
>
|
||||
<ListTodo size={16} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setViewStyle("kanban"); saveSetting("viewStyle", "kanban"); }}
|
||||
className={`p-1.5 rounded transition-colors ${viewStyle === "kanban" ? "bg-white shadow-sm text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}
|
||||
className={`p-1.5 rounded transition-colors ${profile.viewStyle ==="kanban" ? "bg-white shadow-sm text-black dark:bg-gray-700 dark:text-white" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}
|
||||
title={t.kanbanView}
|
||||
>
|
||||
<Kanban size={16} />
|
||||
@ -5697,7 +5697,7 @@ export default function WeeklyView() {
|
||||
</div>
|
||||
|
||||
{/* Slot Duration — desktop only (available in sidebar on tablet) */}
|
||||
{showTimeGrid && (
|
||||
{profile.showTimeGrid && (
|
||||
<div
|
||||
className="header-desktop-only items-center gap-1 bg-gray-100 dark:bg-gray-800 rounded p-1"
|
||||
title="Slot Duration"
|
||||
@ -5739,7 +5739,7 @@ export default function WeeklyView() {
|
||||
setShowDatePicker(false);
|
||||
}}
|
||||
onClose={() => setShowDatePicker(false)}
|
||||
language={language}
|
||||
language={profile.language}
|
||||
anchorRef={datePickerBtnRef}
|
||||
/>
|
||||
)}
|
||||
@ -5754,9 +5754,9 @@ export default function WeeklyView() {
|
||||
profile.headerDisplay === "none"
|
||||
? ""
|
||||
: profile.headerDisplay === "month"
|
||||
? getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { month: "long" })
|
||||
? getCWReferenceDate(getVisibleDays()).toLocaleDateString(profile.language, { month: "long" })
|
||||
: profile.headerDisplay === "month_year"
|
||||
? getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { month: "long", year: "numeric" })
|
||||
? getCWReferenceDate(getVisibleDays()).toLocaleDateString(profile.language, { month: "long", year: "numeric" })
|
||||
: profile.headerDisplay === "date"
|
||||
? (() => {
|
||||
const today = new Date();
|
||||
@ -5766,10 +5766,10 @@ export default function WeeklyView() {
|
||||
d.getFullYear() === today.getFullYear()
|
||||
);
|
||||
const refDate = isTodayInWeek ? today : getCWReferenceDate(getVisibleDays());
|
||||
return refDate.toLocaleDateString(language, { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||
return refDate.toLocaleDateString(profile.language, { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||
})()
|
||||
: profile.headerDisplay === "custom"
|
||||
? formatCustomHeader(profile.headerCustomFormat || "KW WW | YYYY", getVisibleDays(), language, t)
|
||||
? formatCustomHeader(profile.headerCustomFormat || "KW WW | YYYY", getVisibleDays(), profile.language, t)
|
||||
: `KW ${getWeekNumber(getCWReferenceDate(getVisibleDays())).toString().padStart(2, "0")}`
|
||||
}
|
||||
</span>
|
||||
@ -5833,9 +5833,9 @@ export default function WeeklyView() {
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
onClick={() => !showNextTask && setIsEditingGoal(true)}
|
||||
className={`cursor-pointer font-medium italic transition-colors ${showNextTask ? "cursor-default text-gray-600 dark:text-white hover:text-black dark:hover:text-gray-100" : "text-gray-600 dark:text-yellow-400 hover:text-black dark:hover:text-yellow-300"}`}
|
||||
title={showNextTask ? "Next task" : "Edit goal"}
|
||||
onClick={() => !profile.showNextTask && setIsEditingGoal(true)}
|
||||
className={`cursor-pointer font-medium italic transition-colors ${profile.showNextTask ? "cursor-default text-gray-600 dark:text-white hover:text-black dark:hover:text-gray-100" : "text-gray-600 dark:text-yellow-400 hover:text-black dark:hover:text-yellow-300"}`}
|
||||
title={profile.showNextTask ? "Next task" : "Edit goal"}
|
||||
style={{
|
||||
fontFamily: profile.goalFontFamily
|
||||
? `"${profile.goalFontFamily}", sans-serif`
|
||||
@ -5852,7 +5852,7 @@ export default function WeeklyView() {
|
||||
WebkitBoxOrient: "vertical" as const,
|
||||
}}
|
||||
>
|
||||
{showNextTask
|
||||
{profile.showNextTask
|
||||
? (() => {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
@ -5900,8 +5900,8 @@ export default function WeeklyView() {
|
||||
<button onClick={() => { const now = new Date(); setCalendarEventModal({ isOpen: true, event: undefined, initialDate: now, initialStartTime: `${String(now.getHours()).padStart(2, "0")}:00` }); }} className="weekly-btn-icon" title="Add Calendar Event">
|
||||
<Plus size={17} className="text-gray-600 hover:text-black transition-colors" />
|
||||
</button>
|
||||
<button onClick={() => { const newVal = !showNextTask; setShowNextTask(newVal); saveSetting("showNextTask", newVal); }} className={`weekly-btn-icon ${showNextTask ? "active" : ""}`} title={showNextTask ? "Showing Next Task" : "Showing Goal"}>
|
||||
{showNextTask ? <Play size={17} className="text-teal-600" /> : <Target size={17} className="text-gray-400" />}
|
||||
<button onClick={() => { const newVal = !profile.showNextTask; setShowNextTask(newVal); saveSetting("showNextTask", newVal); }} className={`weekly-btn-icon ${profile.showNextTask ? "active" : ""}`} title={profile.showNextTask ? "Showing Next Task" : "Showing Goal"}>
|
||||
{profile.showNextTask ? <Play size={17} className="text-teal-600" /> : <Target size={17} className="text-gray-400" />}
|
||||
</button>
|
||||
<button onClick={() => setShowFocusMode(true)} className="weekly-btn-icon" title="Enter Focus Mode">
|
||||
<Zap size={17} className="text-gray-600 hover:text-yellow-500 transition-colors" />
|
||||
@ -5935,15 +5935,15 @@ export default function WeeklyView() {
|
||||
<div className="header-overflow-menu">
|
||||
<button onClick={() => { handleUndo(); setShowHeaderMore(false); }} disabled={undoCount === 0}><Undo2 size={16} /> <span>Undo</span></button>
|
||||
<button onClick={() => { handleRedo(); setShowHeaderMore(false); }} disabled={redoCount === 0}><Redo2 size={16} /> <span>Redo</span></button>
|
||||
<button onClick={() => { const now = new Date(); setCalendarEventModal({ isOpen: true, event: undefined, initialDate: now, initialStartTime: `${String(now.getHours()).padStart(2, "0")}:00` }); setShowHeaderMore(false); }}><Plus size={16} /> <span>{language === "de" ? "Termin erstellen" : "Add Event"}</span></button>
|
||||
<button onClick={() => { setIsRecurringTasksOpen(true); setShowHeaderMore(false); }}><Repeat size={16} /> <span>{language === "de" ? "Wiederkehrend" : "Recurring"}</span></button>
|
||||
<button onClick={() => { const now = new Date(); setCalendarEventModal({ isOpen: true, event: undefined, initialDate: now, initialStartTime: `${String(now.getHours()).padStart(2, "0")}:00` }); setShowHeaderMore(false); }}><Plus size={16} /> <span>{profile.language === "de" ? "Termin erstellen" : "Add Event"}</span></button>
|
||||
<button onClick={() => { setIsRecurringTasksOpen(true); setShowHeaderMore(false); }}><Repeat size={16} /> <span>{profile.language === "de" ? "Wiederkehrend" : "Recurring"}</span></button>
|
||||
<button onClick={() => { const nv = !showNextTask; setShowNextTask(nv); saveSetting("showNextTask", nv); setShowHeaderMore(false); }}>{showNextTask ? <Play size={16} /> : <Target size={16} />} <span>{showNextTask ? "Next Task" : "Goal"}</span></button>
|
||||
<button onClick={() => { setShowFocusMode(true); setShowHeaderMore(false); }}><Zap size={16} /> <span>{language === "de" ? "Fokus" : "Focus"}</span></button>
|
||||
<button onClick={() => { setShowFocusMode(true); setShowHeaderMore(false); }}><Zap size={16} /> <span>{profile.language === "de" ? "Fokus" : "Focus"}</span></button>
|
||||
<button onClick={() => { setDarkMode(!darkMode); setShowHeaderMore(false); }}>{darkMode ? <Sun size={16} /> : <Moon size={16} />} <span>{darkMode ? "Light" : "Dark"}</span></button>
|
||||
<button onClick={() => { setShowProjectsSidebar(true); setShowHeaderMore(false); }}><FolderPlus size={16} /> <span>{language === "de" ? "Neues Projekt" : "New Project"}</span></button>
|
||||
<button onClick={() => { setShowProjectsSidebar(true); setShowHeaderMore(false); }}><FolderPlus size={16} /> <span>{profile.language === "de" ? "Neues Projekt" : "New Project"}</span></button>
|
||||
<div style={{ borderTop: "1px solid var(--border-color, #e5e7eb)", margin: "2px 0" }} />
|
||||
<div style={{ padding: "6px 12px", display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" }}>
|
||||
<span style={{ fontSize: "12px", color: "#888", marginRight: "4px" }}>{language === "de" ? "Tage" : "Days"}:</span>
|
||||
<span style={{ fontSize: "12px", color: "#888", marginRight: "4px" }}>{profile.language === "de" ? "Tage" : "Days"}:</span>
|
||||
{[1, 2, 3, 5, 7].map((num) => (
|
||||
<button
|
||||
key={num}
|
||||
@ -5954,9 +5954,9 @@ export default function WeeklyView() {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{showTimeGrid && (
|
||||
{profile.showTimeGrid && (
|
||||
<div style={{ padding: "6px 12px", display: "flex", alignItems: "center", gap: "6px" }}>
|
||||
<span style={{ fontSize: "12px", color: "#888", marginRight: "4px" }}>{language === "de" ? "Slot" : "Slot"}:</span>
|
||||
<span style={{ fontSize: "12px", color: "#888", marginRight: "4px" }}>{profile.language === "de" ? "Slot" : "Slot"}:</span>
|
||||
{[15, 30, 60].map((duration) => (
|
||||
<button
|
||||
key={duration}
|
||||
@ -5988,10 +5988,10 @@ export default function WeeklyView() {
|
||||
</header>
|
||||
|
||||
{/* All-Day Events Section (above position) — hidden in kanban */}
|
||||
{viewStyle !== "kanban" && effectiveAllDayPosition === "above" && allDaySection}
|
||||
{profile.viewStyle !== "kanban" && effectiveAllDayPosition === "above" && allDaySection}
|
||||
|
||||
{/* Kanban Board View */}
|
||||
{viewStyle === "kanban" && (() => {
|
||||
{profile.viewStyle ==="kanban" && (() => {
|
||||
// Combine all tasks (weekly + someday) for kanban
|
||||
const allSomedayTasks = somedayLists.flatMap(l => l.tasks);
|
||||
const allKanbanTasks = [...tasks, ...allSomedayTasks];
|
||||
@ -6007,7 +6007,7 @@ export default function WeeklyView() {
|
||||
if (!weeks.has(key)) {
|
||||
const weekEnd = new Date(weekStart);
|
||||
weekEnd.setDate(weekEnd.getDate() + 6);
|
||||
weeks.set(key, `${weekStart.toLocaleDateString(language, { month: "short", day: "numeric" })} - ${weekEnd.toLocaleDateString(language, { month: "short", day: "numeric" })}`);
|
||||
weeks.set(key, `${weekStart.toLocaleDateString(profile.language, { month: "short", day: "numeric" })} - ${weekEnd.toLocaleDateString(profile.language, { month: "short", day: "numeric" })}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -6125,12 +6125,12 @@ export default function WeeklyView() {
|
||||
)}
|
||||
{task.markdownContent && (
|
||||
<div className="kanban-card-has-notes" style={{ fontSize: "0.65rem", color: "#9ca3af", marginTop: "2px", display: "flex", alignItems: "center", gap: "3px" }}>
|
||||
<FileText size={10} /> {language === "de" ? "Notizen" : "Notes"}
|
||||
<FileText size={10} /> {profile.language === "de" ? "Notizen" : "Notes"}
|
||||
</div>
|
||||
)}
|
||||
{(task.scheduledDate || task.startTime) && (
|
||||
<div className="kanban-card-date">
|
||||
{task.scheduledDate && new Date(task.scheduledDate).toLocaleDateString(language, { weekday: "short", month: "short", day: "numeric" })}
|
||||
{task.scheduledDate && new Date(task.scheduledDate).toLocaleDateString(profile.language, { weekday: "short", month: "short", day: "numeric" })}
|
||||
{task.startTime && <span className="kanban-card-time"> {task.startTime}</span>}
|
||||
</div>
|
||||
)}
|
||||
@ -6213,7 +6213,7 @@ export default function WeeklyView() {
|
||||
{projects.length > 0 && (
|
||||
<div className="kanban-project-chip" style={{ background: darkMode ? "#1f2937" : "#f3f4f6", color: "#9ca3af" }} {...kanbanProjectDrop(null)}>
|
||||
<X size={12} />
|
||||
<span>{language === "de" ? "Kein Projekt" : "No project"}</span>
|
||||
<span>{profile.language === "de" ? "Kein Projekt" : "No project"}</span>
|
||||
</div>
|
||||
)}
|
||||
{/* Spacer pushes filters to right */}
|
||||
@ -6286,7 +6286,7 @@ export default function WeeklyView() {
|
||||
const updated = kanbanStages.map((s, i) => i === idx ? { ...s, color: e.target.value } : s);
|
||||
saveKanbanStages(updated);
|
||||
}}
|
||||
title={language === "de" ? "Farbe ändern" : "Change color"}
|
||||
title={profile.language === "de" ? "Farbe ändern" : "Change color"}
|
||||
style={{ width: "14px", height: "14px", border: "none", cursor: "pointer", padding: 0, borderRadius: "50%", flexShrink: 0 }}
|
||||
/>
|
||||
<span
|
||||
@ -6319,12 +6319,12 @@ export default function WeeklyView() {
|
||||
}}
|
||||
style={{ padding: "2px 6px", fontSize: "0.65rem", borderRadius: "4px", border: "none", cursor: "pointer", background: "#ef4444", color: "#fff", fontWeight: 600, whiteSpace: "nowrap" }}
|
||||
>
|
||||
{language === "de" ? "Ja, löschen" : "Confirm"}
|
||||
{profile.language === "de" ? "Ja, löschen" : "Confirm"}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setKanbanDeleteStageId(null)}
|
||||
style={{ padding: "2px", background: "none", border: "none", cursor: "pointer", color: "#9ca3af", display: "flex" }}
|
||||
title={language === "de" ? "Abbrechen" : "Cancel"}
|
||||
title={profile.language === "de" ? "Abbrechen" : "Cancel"}
|
||||
>
|
||||
<X size={12} />
|
||||
</button>
|
||||
@ -6333,7 +6333,7 @@ export default function WeeklyView() {
|
||||
<button
|
||||
onClick={() => setKanbanDeleteStageId(stage.id)}
|
||||
className="kanban-column-delete"
|
||||
title={language === "de" ? "Phase löschen" : "Delete stage"}
|
||||
title={profile.language === "de" ? "Phase löschen" : "Delete stage"}
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
</button>
|
||||
@ -6346,7 +6346,7 @@ export default function WeeklyView() {
|
||||
<input
|
||||
type="text"
|
||||
autoFocus
|
||||
placeholder={language === "de" ? "Aufgabe eingeben..." : "Enter task..."}
|
||||
placeholder={profile.language === "de" ? "Aufgabe eingeben..." : "Enter task..."}
|
||||
value={kanbanNewTaskTitle}
|
||||
onChange={(e) => setKanbanNewTaskTitle(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
@ -6401,7 +6401,7 @@ export default function WeeklyView() {
|
||||
}}
|
||||
>
|
||||
<Plus size={12} />
|
||||
{language === "de" ? "Aufgabe" : "Add task"}
|
||||
{profile.language === "de" ? "Aufgabe" : "Add task"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@ -6438,7 +6438,7 @@ export default function WeeklyView() {
|
||||
<input
|
||||
type="text"
|
||||
autoFocus
|
||||
placeholder={language === "de" ? "Aufgabe eingeben..." : "Enter task..."}
|
||||
placeholder={profile.language === "de" ? "Aufgabe eingeben..." : "Enter task..."}
|
||||
value={kanbanNewTaskTitle}
|
||||
onChange={(e) => setKanbanNewTaskTitle(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
@ -6493,7 +6493,7 @@ export default function WeeklyView() {
|
||||
}}
|
||||
>
|
||||
<Plus size={12} />
|
||||
{language === "de" ? "Aufgabe" : "Add task"}
|
||||
{profile.language === "de" ? "Aufgabe" : "Add task"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@ -6506,15 +6506,15 @@ export default function WeeklyView() {
|
||||
})()}
|
||||
|
||||
{/* Main Grid with Time Column */}
|
||||
{viewStyle !== "kanban" && <div style={{ position: "relative", flex: 1, minHeight: 0, overflow: 'hidden' }}>
|
||||
<div className="time-grid-wrapper" ref={gridRef as React.Ref<HTMLDivElement>} onScroll={handleGridScroll} style={showTimeGrid ? {
|
||||
{profile.viewStyle !== "kanban" && <div style={{ position: "relative", flex: 1, minHeight: 0, overflow: 'hidden' }}>
|
||||
<div className="time-grid-wrapper" ref={gridRef as React.Ref<HTMLDivElement>} onScroll={handleGridScroll} style={profile.showTimeGrid ? {
|
||||
height: '100%',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
WebkitOverflowScrolling: 'touch' as any,
|
||||
} : undefined}>
|
||||
{/* Time Column */}
|
||||
{showTimeGrid && (
|
||||
{profile.showTimeGrid && (
|
||||
<div
|
||||
className="time-column"
|
||||
style={{
|
||||
@ -6611,14 +6611,14 @@ export default function WeeklyView() {
|
||||
title={isHourStart ? `Jump to ${hour}:00` : undefined}
|
||||
>
|
||||
{(isHourStart || effectiveShowSubHourSlots) && (
|
||||
<span>{formatHour(hour, parseInt(minutes), (isHourStart ? effectiveHourLabelFormat : 'full') as "short" | "full", timeFormat)}</span>
|
||||
<span>{formatHour(hour, parseInt(minutes), (isHourStart ? effectiveHourLabelFormat : 'full') as "short" | "full", profile.timeFormat)}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{/* End-of-day 24:00 label */}
|
||||
<div className="time-slot-label hour-start" style={{ height: '0px', lineHeight: 0 }}>
|
||||
<span>{timeFormat === '24h' ? '24' : '12 AM'}</span>
|
||||
<span>{profile.timeFormat === '24h' ? '24' : '12 AM'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -6629,7 +6629,7 @@ export default function WeeklyView() {
|
||||
className={`weekly-days-grid cols-${viewDays}`}
|
||||
data-slide-direction={slideDirection}
|
||||
data-nav-type={viewDays > 1 ? "week" : "day"}
|
||||
style={showTimeGrid ? {
|
||||
style={profile.showTimeGrid ? {
|
||||
height: `${24 * (60 / effectiveCellDuration) * getSlotHeight(effectiveCellDuration) + getHeaderHeight(effectiveCellDuration)}px`,
|
||||
flex: 1,
|
||||
alignSelf: "flex-start",
|
||||
@ -6693,21 +6693,21 @@ export default function WeeklyView() {
|
||||
<>
|
||||
{activeDateLayout === "left" && (
|
||||
<span className="weekly-day-date" style={dateStyle}>
|
||||
{formatDateHeader(date, language)}
|
||||
{formatDateHeader(date, profile.language)}
|
||||
</span>
|
||||
)}
|
||||
<h3
|
||||
className={`weekly-day-name ${isSameDay(date, new Date()) ? "is-today" : ""}`}
|
||||
style={{ marginBottom: 0, flexShrink: 0 }}
|
||||
>
|
||||
{getDayName(date, language, weekdayFormat, customWeekdayNames, weekStartDay, weekdayCase)}
|
||||
{getDayName(date, profile.language, profile.weekdayFormat, profile.customWeekdayNames, profile.weekStartDay, profile.weekdayCase)}
|
||||
</h3>
|
||||
{(activeDateLayout === "right" ||
|
||||
activeDateLayout === "above" ||
|
||||
activeDateLayout === "below" ||
|
||||
activeDateLayout === undefined) && (
|
||||
<span className="weekly-day-date" style={dateStyle}>
|
||||
{formatDateHeader(date, language)}
|
||||
{formatDateHeader(date, profile.language)}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
@ -6717,14 +6717,14 @@ export default function WeeklyView() {
|
||||
</header>
|
||||
|
||||
{/* Time Grid or Simple List */}
|
||||
{showTimeGrid ? (
|
||||
{profile.showTimeGrid ? (
|
||||
<div
|
||||
className="time-slots-container"
|
||||
onDragLeave={handleDragLeave}
|
||||
style={{ position: "relative" }}
|
||||
>
|
||||
{/* Calendar Fetching Indicator */}
|
||||
{showTimeGrid && colIndex === 0 && isFetchingCalendar && (
|
||||
{profile.showTimeGrid && colIndex === 0 && isFetchingCalendar && (
|
||||
<div className="absolute top-2 left-2 z-[60] flex items-center gap-2 bg-white/90 dark:bg-zinc-800/90 px-3 py-1.5 rounded-full shadow-sm border border-zinc-200 dark:border-zinc-700 text-xs text-zinc-600 dark:text-zinc-300 pointer-events-none">
|
||||
<div className="w-3 h-3 border-2 border-zinc-400 border-t-transparent rounded-full animate-spin"></div>
|
||||
Syncing Calendar...
|
||||
@ -7319,7 +7319,7 @@ export default function WeeklyView() {
|
||||
</div>}
|
||||
|
||||
{/* All-Day Events Section (below position) — hidden in kanban */}
|
||||
{viewStyle !== "kanban" && effectiveAllDayPosition === "below" && allDaySection}
|
||||
{profile.viewStyle !== "kanban" && effectiveAllDayPosition === "below" && allDaySection}
|
||||
|
||||
{/* Someday Section */}
|
||||
{effectiveShowSomeday && (<>
|
||||
@ -8383,8 +8383,8 @@ export default function WeeklyView() {
|
||||
initialStartTime={calendarEventModal.initialStartTime}
|
||||
initialEndTime={calendarEventModal.initialEndTime}
|
||||
connections={connections}
|
||||
weekStartDay={weekStartDay}
|
||||
language={language}
|
||||
weekStartDay={profile.weekStartDay ?? 1}
|
||||
language={profile.language}
|
||||
onClose={() =>
|
||||
setCalendarEventModal({ ...calendarEventModal, isOpen: false })
|
||||
}
|
||||
@ -8411,13 +8411,13 @@ export default function WeeklyView() {
|
||||
padding: '16px 20px 12px', fontWeight: 700, fontSize: '0.95rem',
|
||||
borderBottom: '2px solid #3b82f6',
|
||||
}}>
|
||||
{language === 'de' ? 'Wiederkehrendes Ereignis bearbeiten' : 'Edit recurring event'}
|
||||
{profile.language === 'de' ? 'Wiederkehrendes Ereignis bearbeiten' : 'Edit recurring event'}
|
||||
</div>
|
||||
<div style={{ padding: '12px 20px' }}>
|
||||
{([
|
||||
{ value: 'this' as const, title: language === 'de' ? 'Nur dieses Ereignis' : 'This event', desc: language === 'de' ? 'Alle anderen Ereignisse der Serie bleiben unverändert.' : 'All other events in the series stay the same.' },
|
||||
{ value: 'future' as const, title: language === 'de' ? 'Dieses und folgende Ereignisse' : 'This and following events', desc: language === 'de' ? 'Dieses und alle zukünftigen Ereignisse der Serie werden geändert.' : 'This and all future events in the series will be changed.' },
|
||||
{ value: 'all' as const, title: language === 'de' ? 'Alle Ereignisse' : 'All events', desc: language === 'de' ? 'Alle Ereignisse der Serie werden geändert.' : 'All events in the series will be changed.' },
|
||||
{ value: 'this' as const, title: profile.language === 'de' ? 'Nur dieses Ereignis' : 'This event', desc: profile.language === 'de' ? 'Alle anderen Ereignisse der Serie bleiben unverändert.' : 'All other events in the series stay the same.' },
|
||||
{ value: 'future' as const, title: profile.language === 'de' ? 'Dieses und folgende Ereignisse' : 'This and following events', desc: profile.language === 'de' ? 'Dieses und alle zukünftigen Ereignisse der Serie werden geändert.' : 'This and all future events in the series will be changed.' },
|
||||
{ value: 'all' as const, title: profile.language === 'de' ? 'Alle Ereignisse' : 'All events', desc: profile.language === 'de' ? 'Alle Ereignisse der Serie werden geändert.' : 'All events in the series will be changed.' },
|
||||
]).map(opt => (
|
||||
<label key={opt.value} style={{
|
||||
display: 'flex', gap: 12, padding: '10px 4px', cursor: 'pointer',
|
||||
@ -8444,12 +8444,12 @@ export default function WeeklyView() {
|
||||
padding: '8px 16px', fontSize: '0.85rem', fontWeight: 600,
|
||||
background: 'none', border: 'none', cursor: 'pointer',
|
||||
color: darkMode ? '#aaa' : '#666',
|
||||
}}>{language === 'de' ? 'Abbrechen' : 'Cancel'}</button>
|
||||
}}>{profile.language === 'de' ? 'Abbrechen' : 'Cancel'}</button>
|
||||
<button onClick={() => handleRecurringDragConfirm(recurringDragEditMode)} style={{
|
||||
padding: '8px 20px', fontSize: '0.85rem', fontWeight: 600,
|
||||
background: '#3b82f6', color: 'white', border: 'none',
|
||||
borderRadius: 8, cursor: 'pointer',
|
||||
}}>{language === 'de' ? 'Speichern' : 'Save'}</button>
|
||||
}}>{profile.language === 'de' ? 'Speichern' : 'Save'}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -8485,7 +8485,7 @@ export default function WeeklyView() {
|
||||
t.dayOfWeek === now.getDay() &&
|
||||
isSameDay(
|
||||
currentWeekStart,
|
||||
getStartOfWeek(now, weekStartDay),
|
||||
getStartOfWeek(now, profile.weekStartDay ?? 1),
|
||||
))),
|
||||
);
|
||||
|
||||
@ -8520,19 +8520,19 @@ export default function WeeklyView() {
|
||||
}
|
||||
}}
|
||||
onSettingsChanged={handleSettingsChanged}
|
||||
showTimeGrid={showTimeGrid}
|
||||
showTimeGrid={profile.showTimeGrid ?? true}
|
||||
setShowTimeGrid={setShowTimeGrid}
|
||||
cellDuration={cellDuration}
|
||||
cellDuration={profile.cellDuration ?? 30}
|
||||
setCellDuration={setCellDuration}
|
||||
weekStartDay={weekStartDay}
|
||||
weekStartDay={profile.weekStartDay ?? 1}
|
||||
setWeekStartDay={setWeekStartDay}
|
||||
viewStyle={viewStyle}
|
||||
viewStyle={profile.viewStyle ?? "simple"}
|
||||
setViewStyle={setViewStyle}
|
||||
showSomeday={showSomeday}
|
||||
showSomeday={profile.showSomeday ?? true}
|
||||
setShowSomeday={setShowSomeday}
|
||||
showAllDay={showAllDay}
|
||||
showAllDay={profile.showAllDayEvents ?? true}
|
||||
setShowAllDay={setShowAllDay}
|
||||
showSchedule={showSchedule}
|
||||
showSchedule={profile.showSchedule ?? true}
|
||||
setShowSchedule={setShowSchedule}
|
||||
goal={goal}
|
||||
setGoal={setGoal}
|
||||
@ -8543,26 +8543,26 @@ export default function WeeklyView() {
|
||||
setFocusTimerDuration={setFocusTimerDuration}
|
||||
focusBreakDuration={focusBreakDuration}
|
||||
setFocusBreakDuration={setFocusBreakDuration}
|
||||
fontSize={fontSize}
|
||||
fontSize={profile.fontSize ?? "M"}
|
||||
setFontSize={setFontSize}
|
||||
showNextTask={showNextTask}
|
||||
showNextTask={profile.showNextTask ?? false}
|
||||
setShowNextTask={setShowNextTask}
|
||||
headlineFont={headlineFont}
|
||||
headlineFontSize={headlineFontSize}
|
||||
headlineFontWeight={headlineFontWeight}
|
||||
dateFontFamily={dateFontFamily}
|
||||
dateFontSize={dateFontSize}
|
||||
dateFontWeight={dateFontWeight}
|
||||
timeTaskFontFamily={timeTaskFontFamily}
|
||||
timeTaskFontSize={timeTaskFontSize}
|
||||
timeTaskFontWeight={timeTaskFontWeight}
|
||||
bodyFont={bodyFont}
|
||||
taskFontFamily={taskFontFamily}
|
||||
taskFontSize={taskFontSize}
|
||||
taskFontWeight={taskFontWeight}
|
||||
fontWeight={fontWeight}
|
||||
weekendColorSat={weekendColorSat}
|
||||
weekendColorSun={weekendColorSun}
|
||||
headlineFont={profile.headlineFont ?? "Inter"}
|
||||
headlineFontSize={profile.headlineFontSize ?? "1.25rem"}
|
||||
headlineFontWeight={profile.headlineFontWeight ?? "900"}
|
||||
dateFontFamily={profile.dateFontFamily ?? "Inter"}
|
||||
dateFontSize={profile.dateFontSize ?? "0.65rem"}
|
||||
dateFontWeight={profile.dateFontWeight ?? "400"}
|
||||
timeTaskFontFamily={profile.timeTaskFontFamily ?? "Inter"}
|
||||
timeTaskFontSize={profile.timeTaskFontSize ?? "0.75rem"}
|
||||
timeTaskFontWeight={profile.timeTaskFontWeight ?? "500"}
|
||||
bodyFont={profile.bodyFont ?? "Inter"}
|
||||
taskFontFamily={profile.taskFontFamily ?? "Inter"}
|
||||
taskFontSize={profile.taskFontSize ?? "0.9rem"}
|
||||
taskFontWeight={profile.taskFontWeight ?? "400"}
|
||||
fontWeight={profile.fontWeight ?? "400"}
|
||||
weekendColorSat={profile.weekendColorSat ?? "#666666"}
|
||||
weekendColorSun={profile.weekendColorSun ?? "#dc2626"}
|
||||
protectEventTimes={protectEventTimes}
|
||||
setProtectEventTimes={setProtectEventTimes}
|
||||
goalFontWeight={profile.goalFontWeight || "500"}
|
||||
@ -8572,11 +8572,11 @@ export default function WeeklyView() {
|
||||
executeImport={executeImport}
|
||||
onImportLists={(lists) => doImport("apple", lists)}
|
||||
importStatusMsg={importStatusMsg}
|
||||
hourLabelFormat={hourLabelFormat}
|
||||
hourLabelFormat={profile.hourLabelFormat ?? "short"}
|
||||
setHourLabelFormat={setHourLabelFormat}
|
||||
showSubHourSlots={showSubHourSlots}
|
||||
showSubHourSlots={profile.showSubHourSlots ?? true}
|
||||
setShowSubHourSlots={setShowSubHourSlots}
|
||||
allDayPosition={allDayPosition}
|
||||
allDayPosition={profile.allDayPosition ?? "above"}
|
||||
setAllDayPosition={setAllDayPosition}
|
||||
saveSetting={saveSetting}
|
||||
availableTaskLists={availableTaskLists}
|
||||
@ -8609,7 +8609,7 @@ export default function WeeklyView() {
|
||||
},
|
||||
onAddProject: () => { setShowProjectsSidebar(true); },
|
||||
onRecurringTasks: () => setIsRecurringTasksOpen(true),
|
||||
onToggleNextTask: () => { const newVal = !showNextTask; setShowNextTask(newVal); saveSetting("showNextTask", newVal); },
|
||||
onToggleNextTask: () => { const newVal = !profile.showNextTask; setShowNextTask(newVal); saveSetting("showNextTask", newVal); },
|
||||
onFocusMode: () => setShowFocusMode(true),
|
||||
onToggleDarkMode: () => setDarkMode(!darkMode),
|
||||
onSearch: () => setIsSearchOpen(true),
|
||||
@ -8617,18 +8617,18 @@ export default function WeeklyView() {
|
||||
onRedo: handleRedo,
|
||||
onRefresh: () => { fetchCalendarEvents(true); fetchTasks(); },
|
||||
darkMode,
|
||||
showNextTask,
|
||||
showNextTask: profile.showNextTask ?? false,
|
||||
undoCount,
|
||||
redoCount,
|
||||
viewDays,
|
||||
onViewDaysChange: (num: number) => { setViewDays(num); savedViewDaysRef.current = num; saveSetting("viewDays", num); },
|
||||
showTimeGrid,
|
||||
cellDuration,
|
||||
showTimeGrid: profile.showTimeGrid ?? true,
|
||||
cellDuration: profile.cellDuration ?? 30,
|
||||
onCellDurationChange: (d: CellDuration) => { setCellDuration(d); saveSetting("cellDuration", d); },
|
||||
viewStyle,
|
||||
viewStyle: profile.viewStyle ?? "simple",
|
||||
onViewStyleChange: (s: string) => { setViewStyle(s as any); saveSetting("viewStyle", s); },
|
||||
startHour,
|
||||
endHour,
|
||||
startHour: profile.startHour ?? 8,
|
||||
endHour: profile.endHour ?? 18,
|
||||
onStartHourChange: (h: number) => { setStartHour(h); saveSetting("startHour", h); },
|
||||
onEndHourChange: (h: number) => { setEndHour(h); saveSetting("endHour", h); },
|
||||
}}
|
||||
@ -8647,7 +8647,7 @@ export default function WeeklyView() {
|
||||
task={selectedTaskForRecurrence}
|
||||
onClose={() => setSelectedTaskForRecurrence(null)}
|
||||
onSave={handleRecurrenceSave}
|
||||
language={language}
|
||||
language={profile.language}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -8657,7 +8657,7 @@ export default function WeeklyView() {
|
||||
<OnboardingWizard
|
||||
profile={profile}
|
||||
darkMode={darkMode}
|
||||
language={language}
|
||||
language={profile.language}
|
||||
connections={connections}
|
||||
onComplete={async () => { await saveSetting("hasCompletedOnboarding", true); setShowOnboarding(false); await fetchProfile(); }}
|
||||
onSkip={async () => { await saveSetting("hasCompletedOnboarding", true); setShowOnboarding(false); await fetchProfile(); }}
|
||||
@ -8808,7 +8808,7 @@ export default function WeeklyView() {
|
||||
setKanbanDetailTask({ ...liveTask, markdownContent: val });
|
||||
}
|
||||
}}
|
||||
placeholder={language === "de" ? "Notizen hinzufügen..." : "Add notes..."}
|
||||
placeholder={profile.language === "de" ? "Notizen hinzufügen..." : "Add notes..."}
|
||||
className="kanban-detail-notes"
|
||||
rows={4}
|
||||
/>
|
||||
@ -8863,7 +8863,7 @@ export default function WeeklyView() {
|
||||
<input
|
||||
name="newSub"
|
||||
type="text"
|
||||
placeholder={language === "de" ? "Unteraufgabe hinzufügen..." : "Add subtask..."}
|
||||
placeholder={profile.language === "de" ? "Unteraufgabe hinzufügen..." : "Add subtask..."}
|
||||
className="kanban-detail-subtask-input"
|
||||
/>
|
||||
</form>
|
||||
@ -8876,7 +8876,7 @@ export default function WeeklyView() {
|
||||
<button
|
||||
onClick={() => { deleteTask(liveTask.id); setKanbanDetailTask(null); }}
|
||||
className="kanban-detail-delete"
|
||||
title={language === "de" ? "Löschen" : "Delete"}
|
||||
title={profile.language === "de" ? "Löschen" : "Delete"}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
@ -8885,7 +8885,7 @@ export default function WeeklyView() {
|
||||
onClick={() => setKanbanDetailTask(null)}
|
||||
className="kanban-detail-btn kanban-detail-btn-cancel"
|
||||
>
|
||||
{language === "de" ? "Abbrechen" : "Cancel"}
|
||||
{profile.language === "de" ? "Abbrechen" : "Cancel"}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setKanbanDetailTask(null)}
|
||||
@ -8919,7 +8919,7 @@ export default function WeeklyView() {
|
||||
<div className="mobile-fab-menu">
|
||||
<button className="mobile-fab-menu-item" onClick={() => { setShowMobileFabMenu(false); setShowMobileFabSheet(true); }}>
|
||||
<div className="mobile-fab-menu-icon" style={{ background: "#0ea5e9" }}><Plus size={18} /></div>
|
||||
<span>{language === "de" ? "Aufgabe" : "Task"}</span>
|
||||
<span>{profile.language === "de" ? "Aufgabe" : "Task"}</span>
|
||||
</button>
|
||||
<button className="mobile-fab-menu-item" onClick={() => {
|
||||
setShowMobileFabMenu(false);
|
||||
@ -8927,19 +8927,19 @@ export default function WeeklyView() {
|
||||
setCalendarEventModal({ isOpen: true, event: undefined, initialDate: now, initialStartTime: `${String(now.getHours()).padStart(2, "0")}:00` });
|
||||
}}>
|
||||
<div className="mobile-fab-menu-icon" style={{ background: "#8b5cf6" }}><Calendar size={18} /></div>
|
||||
<span>{language === "de" ? "Termin" : "Event"}</span>
|
||||
<span>{profile.language === "de" ? "Termin" : "Event"}</span>
|
||||
</button>
|
||||
<button className="mobile-fab-menu-item" onClick={() => { setShowMobileFabMenu(false); setShowProjectsSidebar(true); }}>
|
||||
<div className="mobile-fab-menu-icon" style={{ background: "#6366f1" }}><FolderPlus size={18} /></div>
|
||||
<span>{language === "de" ? "Projekt" : "Project"}</span>
|
||||
<span>{profile.language === "de" ? "Projekt" : "Project"}</span>
|
||||
</button>
|
||||
<button className="mobile-fab-menu-item" onClick={() => { setShowMobileFabMenu(false); setShowFocusMode(true); }}>
|
||||
<div className="mobile-fab-menu-icon" style={{ background: "#f59e0b" }}><Zap size={18} /></div>
|
||||
<span>{language === "de" ? "Fokus" : "Focus"}</span>
|
||||
<span>{profile.language === "de" ? "Fokus" : "Focus"}</span>
|
||||
</button>
|
||||
<button className="mobile-fab-menu-item" onClick={() => { setShowMobileFabMenu(false); setIsSearchOpen(true); }}>
|
||||
<div className="mobile-fab-menu-icon" style={{ background: "#10b981" }}><Search size={18} /></div>
|
||||
<span>{language === "de" ? "Suchen" : "Search"}</span>
|
||||
<span>{profile.language === "de" ? "Suchen" : "Search"}</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@ -9026,7 +9026,7 @@ export default function WeeklyView() {
|
||||
setShowDatePicker(false);
|
||||
}}
|
||||
onClose={() => setShowDatePicker(false)}
|
||||
language={language}
|
||||
language={profile.language}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user