feat: Add Start Week setting, update date header format, and refine Someday styling

This commit is contained in:
mARTin 2026-02-13 07:16:59 +01:00
parent e9a0a706b7
commit bccadb34ce

View File

@ -174,12 +174,29 @@ const translations: Record<string, any> = {
function getStartOfWeek(date: Date, startDay: number = 0): Date { function getStartOfWeek(date: Date, startDay: number = 0): Date {
const d = new Date(date); const d = new Date(date);
const day = d.getDay(); const day = d.getDay();
const diff = d.getDate() - day + startDay; const diff = d.getDate() - day + (day < startDay ? -7 : 0) + startDay; // if today is sun(0) and start is mon(1), day < start (0 < 1) -> -7 + 1 = -6. 0 - 6 = -6. Correct.
return new Date(d.setDate(diff)); // Wait, let's re-verify:
// Start Mon(1). Today Sun(0). day=0. diff = date - 0 + (-7) + 1 = date - 6. Correct (last Monday).
// Start Mon(1). Today Mon(1). day=1. diff = date - 1 + (0) + 1 = date. Correct.
// Start Sun(0). Today Mon(1). day=1. diff = date - 1 + (0) + 0 = date - 1. Correct (last Sunday).
// Start Sun(0). Today Sun(0). day=0. diff = date - 0 + (0) + 0 = date. Correct.
// What if Start Mon(1), Today Tue(2). day=2. diff = date - 2 + 0 + 1 = date - 1. Correct.
// Better logic:
// const day = d.getDay();
// const diff = (day < startDay ? 7 : 0) + day - startDay;
// d.setDate(d.getDate() - diff);
//
// Let's stick to a robust one:
const currentDay = d.getDay();
const distance = (currentDay - startDay + 7) % 7;
d.setDate(d.getDate() - distance);
return d;
} }
function formatDateHeader(date: Date, locale: string = 'en-US'): string { function formatDateHeader(date: Date, locale: string = 'en-US'): string {
return date.toLocaleDateString(locale, { day: 'numeric', month: 'short', year: 'numeric' }); const datePart = date.toLocaleDateString(locale, { day: 'numeric', month: 'short' }); // e.g. 12. Feb.
return `| ${datePart}`;
} }
function getDayName(date: Date, locale: string = 'en-US'): string { function getDayName(date: Date, locale: string = 'en-US'): string {
@ -272,7 +289,7 @@ export default function WeeklyView() {
return { ...event, editable: isEditable }; return { ...event, editable: isEditable };
}); });
}, [rawCalendarEvents, connections]); }, [rawCalendarEvents, connections]);
const [currentWeekStart, setCurrentWeekStart] = useState(getStartOfWeek(new Date())); const [currentWeekStart, setCurrentWeekStart] = useState(getStartOfWeek(new Date(), 1)); // Default align to Monday initially
const [viewDays, setViewDays] = useState(7); const [viewDays, setViewDays] = useState(7);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [darkMode, setDarkMode] = useState(false); const [darkMode, setDarkMode] = useState(false);
@ -306,6 +323,7 @@ export default function WeeklyView() {
const [startHour, setStartHour] = useState(8); const [startHour, setStartHour] = useState(8);
const [endHour, setEndHour] = useState(22); const [endHour, setEndHour] = useState(22);
const [weekStartDay, setWeekStartDay] = useState(1); // 1 = Monday, 0 = Sunday
const [showSomeday, setShowSomeday] = useState(true); const [showSomeday, setShowSomeday] = useState(true);
const [showAllDay, setShowAllDay] = useState(true); const [showAllDay, setShowAllDay] = useState(true);
const [motto, setMotto] = useState('Focus and Execute'); const [motto, setMotto] = useState('Focus and Execute');
@ -339,6 +357,10 @@ export default function WeeklyView() {
if (savedDarkMode) { if (savedDarkMode) {
setDarkMode(JSON.parse(savedDarkMode)); setDarkMode(JSON.parse(savedDarkMode));
} }
const savedWeekStart = localStorage.getItem('weekly-week-start');
if (savedWeekStart) {
setWeekStartDay(Number(savedWeekStart));
}
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -351,6 +373,13 @@ export default function WeeklyView() {
} }
}, [darkMode, mounted]); }, [darkMode, mounted]);
useEffect(() => {
if (!mounted) return;
localStorage.setItem('weekly-week-start', String(weekStartDay));
// Re-align current week start when start day changes
setCurrentWeekStart(prev => getStartOfWeek(prev, weekStartDay));
}, [weekStartDay, mounted]);
// Translation helper // Translation helper
const t = translations[language] || translations['en']; const t = translations[language] || translations['en'];
@ -860,7 +889,7 @@ export default function WeeklyView() {
const goToNextWeek = () => navigate(new Date(currentWeekStart.getTime() + 7 * 24 * 60 * 60 * 1000), 'left', 'week'); const goToNextWeek = () => navigate(new Date(currentWeekStart.getTime() + 7 * 24 * 60 * 60 * 1000), 'left', 'week');
const goToPrevDay = () => navigate(new Date(currentWeekStart.getTime() - 24 * 60 * 60 * 1000), 'right', 'day'); const goToPrevDay = () => navigate(new Date(currentWeekStart.getTime() - 24 * 60 * 60 * 1000), 'right', 'day');
const goToNextDay = () => navigate(new Date(currentWeekStart.getTime() + 24 * 60 * 60 * 1000), 'left', 'day'); const goToNextDay = () => navigate(new Date(currentWeekStart.getTime() + 24 * 60 * 60 * 1000), 'left', 'day');
const goToToday = () => setCurrentWeekStart(getStartOfWeek(new Date())); const goToToday = () => setCurrentWeekStart(getStartOfWeek(new Date(), weekStartDay));
// Task CRUD operations // Task CRUD operations
const addTask = async (date: Date, title: string, startTime?: string) => { const addTask = async (date: Date, title: string, startTime?: string) => {
@ -1498,7 +1527,7 @@ export default function WeeklyView() {
<SimpleDatePicker <SimpleDatePicker
selected={currentWeekStart} selected={currentWeekStart}
onSelect={(date) => { onSelect={(date) => {
setCurrentWeekStart(getStartOfWeek(date)); setCurrentWeekStart(getStartOfWeek(date, weekStartDay));
setShowDatePicker(false); setShowDatePicker(false);
}} }}
onClose={() => setShowDatePicker(false)} onClose={() => setShowDatePicker(false)}
@ -2315,7 +2344,7 @@ export default function WeeklyView() {
}} /> }} />
{Array.from({ length: Math.max(0, 5 - list.tasks.length) }).map((_, i) => ( {Array.from({ length: Math.max(0, 5 - list.tasks.length) }).map((_, i) => (
<li key={`filler-${i}`} className="weekly-task-item minimal filler" style={{ <li key={`filler-${i}`} className="weekly-task-item minimal filler" style={{
borderBottom: '1px dashed #eee', borderBottom: '1px solid #eee',
height: '32px', height: '32px',
margin: '0 0.5rem', margin: '0 0.5rem',
pointerEvents: 'none' pointerEvents: 'none'
@ -2427,7 +2456,7 @@ export default function WeeklyView() {
(t.isRolling && (!t.scheduledDate || new Date(t.scheduledDate) <= now)) || (t.isRolling && (!t.scheduledDate || new Date(t.scheduledDate) <= now)) ||
// Or implicitly today if within current view logic (e.g. dayOfWeek match in current week) // Or implicitly today if within current view logic (e.g. dayOfWeek match in current week)
// But let's stick to explicit date or rolling for Focus Mode to be precise. // But let's stick to explicit date or rolling for Focus Mode to be precise.
(!t.scheduledDate && t.dayOfWeek === now.getDay() && isSameDay(currentWeekStart, getStartOfWeek(now))) (!t.scheduledDate && t.dayOfWeek === now.getDay() && isSameDay(currentWeekStart, getStartOfWeek(now, weekStartDay)))
) )
); );
@ -2458,6 +2487,8 @@ export default function WeeklyView() {
setShowTimeGrid={setShowTimeGrid} setShowTimeGrid={setShowTimeGrid}
cellDuration={cellDuration} cellDuration={cellDuration}
setCellDuration={setCellDuration} setCellDuration={setCellDuration}
weekStartDay={weekStartDay}
setWeekStartDay={setWeekStartDay}
viewStyle={viewStyle} viewStyle={viewStyle}
setViewStyle={setViewStyle} setViewStyle={setViewStyle}
showSomeday={showSomeday} showSomeday={showSomeday}
@ -2739,7 +2770,7 @@ function SomedayAddTask({ listId, onAdd }: { listId: string; onAdd: (title: stri
}; };
return ( return (
<li className="weekly-task-item minimal" style={{ borderBottom: '1px dashed #eee', margin: '0 0.5rem' }}> <li className="weekly-task-item minimal" style={{ borderBottom: '1px solid #eee', margin: '0 0.5rem' }}>
<form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }} style={{ width: '100%' }}> <form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }} style={{ width: '100%' }}>
<input <input
ref={inputRef} ref={inputRef}
@ -3111,6 +3142,8 @@ interface SettingsModalProps {
setShowTimeGrid: (show: boolean) => void; setShowTimeGrid: (show: boolean) => void;
cellDuration: CellDuration; cellDuration: CellDuration;
setCellDuration: (duration: CellDuration) => void; setCellDuration: (duration: CellDuration) => void;
weekStartDay: number;
setWeekStartDay: (day: number) => void;
viewStyle: 'grid' | 'list'; viewStyle: 'grid' | 'list';
setViewStyle: (style: 'grid' | 'list') => void; setViewStyle: (style: 'grid' | 'list') => void;
showSomeday: boolean; showSomeday: boolean;
@ -3136,6 +3169,8 @@ function SettingsModal({
setShowTimeGrid, setShowTimeGrid,
cellDuration, cellDuration,
setCellDuration, setCellDuration,
weekStartDay,
setWeekStartDay,
viewStyle, viewStyle,
setViewStyle, setViewStyle,
showSomeday, showSomeday,
@ -3620,6 +3655,25 @@ function SettingsModal({
<div style={{ borderTop: '1px solid #eee', marginTop: '16px', paddingTop: '16px' }}></div> <div style={{ borderTop: '1px solid #eee', marginTop: '16px', paddingTop: '16px' }}></div>
<h4 style={{ fontSize: '1rem', fontWeight: 600, margin: 0 }}>{t.localization}</h4> <h4 style={{ fontSize: '1rem', fontWeight: 600, margin: 0 }}>{t.localization}</h4>
{/* Start Week Setting */}
<div>
<label style={{ display: 'block', fontSize: '0.9rem', fontWeight: 600, marginBottom: '4px' }}>Start week on</label>
<div style={{ display: 'flex', gap: '8px' }}>
<button
className={`px-3 py-2 rounded text-sm ${weekStartDay === 1 ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300'}`}
onClick={() => setWeekStartDay(1)}
>
Monday
</button>
<button
className={`px-3 py-2 rounded text-sm ${weekStartDay === 0 ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300'}`}
onClick={() => setWeekStartDay(0)}
>
Sunday
</button>
</div>
</div>
<div> <div>
<label style={{ display: 'block', fontSize: '0.9rem', fontWeight: 600, marginBottom: '4px' }}>{t.language}</label> <label style={{ display: 'block', fontSize: '0.9rem', fontWeight: 600, marginBottom: '4px' }}>{t.language}</label>
<select <select