diff --git a/package.json b/package.json index 61fae9a..f65ab67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.18.0", + "version": "1.19.0", "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": { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d341302..5e4b292 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -93,6 +93,7 @@ model User { showCompletedTasks Boolean @default(true) showLines Boolean @default(true) weekdayFormat String? @default("long") + weekdayCase String? @default("capitalize") customWeekdayNames String? @default("") startDayOffset Int @default(-1) quoteSourceUrls String[] @default([]) diff --git a/src/app/api/user/profile/route.ts b/src/app/api/user/profile/route.ts index 02331cf..863408b 100644 --- a/src/app/api/user/profile/route.ts +++ b/src/app/api/user/profile/route.ts @@ -84,6 +84,7 @@ export async function GET(request: NextRequest) { showLines: true, startDayOffset: true, weekdayFormat: true, + weekdayCase: true, customWeekdayNames: true, quoteSourceUrls: true, accountNumber: true, @@ -128,7 +129,7 @@ export async function PATCH(request: NextRequest) { cwFontFamily, cwFontSize, cwFontWeight, cwColor, yearFontFamily, yearFontSize, yearFontWeight, yearColor, showTaskCheckboxes, dayHeaderGap, - showCompletedTasks, showLines, startDayOffset, weekdayFormat, customWeekdayNames, quoteSourceUrls + showCompletedTasks, showLines, startDayOffset, weekdayFormat, weekdayCase, customWeekdayNames, quoteSourceUrls } = body; const updateData: any = { @@ -202,6 +203,7 @@ export async function PATCH(request: NextRequest) { ...(showLines !== undefined && { showLines }), ...(startDayOffset !== undefined && { startDayOffset }), ...(weekdayFormat !== undefined && { weekdayFormat }), + ...(weekdayCase !== undefined && { weekdayCase }), ...(customWeekdayNames !== undefined && { customWeekdayNames }), ...(quoteSourceUrls !== undefined && { quoteSourceUrls }), }; @@ -285,6 +287,7 @@ export async function PATCH(request: NextRequest) { showLines: true, startDayOffset: true, weekdayFormat: true, + weekdayCase: true, customWeekdayNames: true, quoteSourceUrls: true, accountNumber: true, diff --git a/src/components/GridTaskBlock.tsx b/src/components/GridTaskBlock.tsx index f7a9b99..a022d41 100644 --- a/src/components/GridTaskBlock.tsx +++ b/src/components/GridTaskBlock.tsx @@ -274,28 +274,6 @@ export function GridTaskBlock({ )} {task.title} - {(() => { - const provider = task.externalProvider - || (task.externalId?.startsWith("synology::") ? "synology" : null); - if (!provider) return null; - const iconMap: Record = { - google: { icon: faGoogle, color: "#4285F4", label: "Google" }, - outlook: { icon: faMicrosoft, color: "#0078D4", label: "Microsoft" }, - apple: { icon: faApple, color: "#555", label: "Apple" }, - synology: { icon: faServer, color: "#007AFF", label: "Synology" }, - }; - const info = iconMap[provider]; - if (!info) return null; - return ( - - - - ); - })()} {/* Subtask indicator */} {task.subTasks && task.subTasks.length > 0 && (() => { const completed = task.subTasks.filter(s => s.completed).length; @@ -362,6 +340,28 @@ export function GridTaskBlock({ )} + {(() => { + const provider = task.externalProvider + || (task.externalId?.startsWith("synology::") ? "synology" : null); + if (!provider) return null; + const iconMap: Record = { + google: { icon: faGoogle, color: "#4285F4", label: "Google" }, + outlook: { icon: faMicrosoft, color: "#0078D4", label: "Microsoft" }, + apple: { icon: faApple, color: "#555", label: "Apple" }, + synology: { icon: faServer, color: "#007AFF", label: "Synology" }, + }; + const info = iconMap[provider]; + if (!info) return null; + return ( + + + + ); + })()}
(
- -
+
{format(currentMonth, 'MMMM yyyy', { locale })}
-
@@ -43,7 +43,7 @@ export default function SimpleDatePicker({ selected, onSelect, onClose, language const startDate = startOfWeek(currentMonth, { weekStartsOn: 1 }); for (let i = 0; i < 7; i++) { days.push( -
+
{format(addDays(startDate, i), 'EEEEEE', { locale })}
); @@ -87,7 +87,7 @@ export default function SimpleDatePicker({ selected, onSelect, onClose, language cursor: 'pointer', transition: 'all 0.2s', margin: '0 auto', - color: !isCurrentMonth ? '#d1d5db' : isDaySelected ? 'white' : '#374151', + color: !isCurrentMonth ? 'var(--weekly-text-light, #d1d5db)' : isDaySelected ? 'white' : 'var(--weekly-text, #374151)', background: isDaySelected ? 'black' : 'transparent', fontWeight: isDaySelected ? 'bold' : 'normal', boxShadow: isDaySelected ? '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)' : 'none', @@ -95,7 +95,7 @@ export default function SimpleDatePicker({ selected, onSelect, onClose, language ...(isToday && !isDaySelected ? { color: '#ef4444', fontWeight: 'bold' } : {}) }} onMouseEnter={(e) => { - if (!isDaySelected) e.currentTarget.style.backgroundColor = '#f3f4f6'; + if (!isDaySelected) e.currentTarget.style.backgroundColor = 'var(--weekly-hover-bg, #f3f4f6)'; }} onMouseLeave={(e) => { if (!isDaySelected) e.currentTarget.style.backgroundColor = 'transparent'; @@ -120,28 +120,30 @@ export default function SimpleDatePicker({ selected, onSelect, onClose, language
{/* Decorative triangle */}
{renderHeader()} {renderDays()} diff --git a/src/components/WeeklyView.tsx b/src/components/WeeklyView.tsx index f9f34c2..3ef932c 100644 --- a/src/components/WeeklyView.tsx +++ b/src/components/WeeklyView.tsx @@ -317,6 +317,10 @@ const translations: Record = { weekdayFormatCustom: "Custom", customWeekdayNamesMon: "Mo; Tu; We; Th; Fr; Sa; Su", customWeekdayNamesSun: "Su; Mo; Tu; We; Th; Fr; Sa", + weekdayCase: "Weekday Case", + weekdayCaseNormal: "Normal (monday)", + weekdayCaseCapitalize: "Capitalize (Monday)", + weekdayCaseUppercase: "Uppercase (MONDAY)", }, de: { settings: "Einstellungen", @@ -417,6 +421,10 @@ const translations: Record = { weekdayFormatCustom: "Benutzerdefiniert", customWeekdayNamesMon: "Mo; Di; Mi; Do; Fr; Sa; So", customWeekdayNamesSun: "So; Mo; Di; Mi; Do; Fr; Sa", + weekdayCase: "Groß-/Kleinschreibung", + weekdayCaseNormal: "Klein (montag)", + weekdayCaseCapitalize: "Großbuchstabe (Montag)", + weekdayCaseUppercase: "Großbuchstaben (MONTAG)", }, }; @@ -448,23 +456,30 @@ function formatDateHeader(date: Date, locale: string = "en-US"): string { return date.toLocaleDateString(locale, { day: "numeric", month: "short" }); // e.g. 12. Feb. } -function getDayName(date: Date, locale: string = "en-US", format?: string, customNames?: string, weekStartDay: number = 0): string { +function getDayName(date: Date, locale: string = "en-US", format?: string, customNames?: string, weekStartDay: number = 0, dayCase: string = "capitalize"): string { + let name: string; if (format === "custom" && customNames) { // Split by comma or semicolon to allow spaces in names const names = customNames.split(/[,;]+/).map(s => s.trim()).filter(Boolean); if (names.length === 7) { // Adjust index based on weekStartDay (0=Sun, 1=Mon) const index = (date.getDay() - weekStartDay + 7) % 7; - return names[index].toUpperCase(); + name = names[index]; + } else { + name = date.toLocaleDateString(locale, { weekday: "long" }); + } + } else { + const weekdayOption = format === "narrow" ? "narrow" : (format === "short" ? "short" : "long"); + try { + name = date.toLocaleDateString(locale, { weekday: weekdayOption }); + } catch (e) { + name = date.toLocaleDateString("en-US", { weekday: weekdayOption }); } } - - const weekdayOption = format === "narrow" ? "narrow" : (format === "short" ? "short" : "long"); - try { - return date.toLocaleDateString(locale, { weekday: weekdayOption }).toUpperCase(); - } catch (e) { - return date.toLocaleDateString("en-US", { weekday: weekdayOption }).toUpperCase(); - } + if (dayCase === "uppercase") return name.toUpperCase(); + if (dayCase === "normal") return name.toLowerCase(); + // capitalize: first letter uppercase, rest lowercase + return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase(); } function isSameDay(d1: Date, d2: Date): boolean { @@ -804,6 +819,7 @@ export default function WeeklyView() { quoteSourceUrls?: string[]; startDayOffset?: number; weekdayFormat?: "long" | "short" | "narrow" | "custom"; + weekdayCase?: "normal" | "capitalize" | "uppercase"; customWeekdayNames?: string; dateVerticalAlign?: "top" | "middle" | "bottom"; }>({ @@ -921,6 +937,7 @@ export default function WeeklyView() { const [showSchedule, setShowSchedule] = useState(true); const [focusBreakDuration, setFocusBreakDuration] = useState(5); const [weekdayFormat, setWeekdayFormat] = useState<"long" | "short" | "narrow" | "custom">("long"); + const [weekdayCase, setWeekdayCase] = useState<"normal" | "capitalize" | "uppercase">("capitalize"); const [customWeekdayNames, setCustomWeekdayNames] = useState(""); // New UI State @@ -1594,6 +1611,7 @@ export default function WeeklyView() { setShowAllDay(newSettings.showAllDayEvents); setShowSchedule(newSettings.showSchedule); if (newSettings.weekdayFormat) setWeekdayFormat(newSettings.weekdayFormat); + if (newSettings.weekdayCase) setWeekdayCase(newSettings.weekdayCase); if (newSettings.customWeekdayNames !== undefined) setCustomWeekdayNames(newSettings.customWeekdayNames); setHeadlineFont(newSettings.headlineFont); setHeadlineFontSize(newSettings.headlineFontSize); @@ -1701,6 +1719,8 @@ export default function WeeklyView() { setShowSchedule(data.user.showSchedule); if (data.user.weekdayFormat) setWeekdayFormat(data.user.weekdayFormat as any); + if (data.user.weekdayCase) + setWeekdayCase(data.user.weekdayCase as any); if (data.user.customWeekdayNames) setCustomWeekdayNames(data.user.customWeekdayNames); if (data.user.hourLabelFormat) @@ -3779,9 +3799,11 @@ export default function WeeklyView() { "--weekly-date-weight": profile.dateFontWeight || "400", "--weekly-time-task-font": fontVal(profile.timeTaskFontFamily) ? `"${fontVal(profile.timeTaskFontFamily)}", sans-serif` - : "var(--weekly-font)", - "--weekly-time-task-size": scaleRem(profile.timeTaskFontSize || "0.75rem"), - "--weekly-time-task-weight": profile.timeTaskFontWeight || "500", + : fontVal(profile.taskFontFamily) + ? `"${fontVal(profile.taskFontFamily)}", sans-serif` + : "var(--weekly-font)", + "--weekly-time-task-size": scaleRem(profile.timeTaskFontSize || profile.taskFontSize || "0.9rem"), + "--weekly-time-task-weight": profile.timeTaskFontWeight || profile.taskFontWeight || "400", "--weekly-font": "var(--font-body)" /* Force default body font as requested */, "--weekly-task-font": fontVal(profile.taskFontFamily) @@ -4927,7 +4949,7 @@ export default function WeeklyView() { className={`weekly-day-name ${isSameDay(date, new Date()) ? "is-today" : ""}`} style={{ marginBottom: 0, flexShrink: 0 }} > - {getDayName(date, language, weekdayFormat, customWeekdayNames, weekStartDay)} + {getDayName(date, language, weekdayFormat, customWeekdayNames, weekStartDay, weekdayCase)} {(activeDateLayout === "right" || activeDateLayout === "above" || @@ -7085,7 +7107,7 @@ function TaskItem({ @@ -7709,6 +7731,7 @@ interface SettingsSidebarProps { dateLayout?: "above" | "below" | "left" | "right" | "hidden"; mobileDateLayout?: "above" | "below" | "left" | "right" | "hidden"; weekdayFormat?: "long" | "short" | "narrow" | "custom"; + weekdayCase?: "normal" | "capitalize" | "uppercase"; customWeekdayNames?: string; dateAlignment?: "left" | "center" | "right" | "tight"; hourLabelFormat: "short" | "full"; @@ -8083,6 +8106,7 @@ function SettingsSidebar({ startDayOffset?: number; id?: string; weekdayFormat?: "long" | "short" | "narrow" | "custom"; + weekdayCase?: "normal" | "capitalize" | "uppercase"; customWeekdayNames?: string; dateVerticalAlign?: "top" | "middle" | "bottom"; }>({ @@ -8141,6 +8165,7 @@ function SettingsSidebar({ todayHighlightColor: "#f0fafa", pastDayColor: "#a6a6a7", weekdayFormat: "long", + weekdayCase: "capitalize", customWeekdayNames: "", }); @@ -8252,6 +8277,7 @@ function SettingsSidebar({ showTaskCheckboxes: profile.showTaskCheckboxes, startDayOffset: profile.startDayOffset, weekdayFormat: profile.weekdayFormat, + weekdayCase: profile.weekdayCase, customWeekdayNames: profile.customWeekdayNames, } as any); }, [profile, showTimeGrid, cellDuration, viewStyle, fontSize, showNextTask, showSomeday, showAllDay, showSchedule]); @@ -8290,6 +8316,7 @@ function SettingsSidebar({ : true, cellDuration: data.user.cellDuration || 30, weekdayFormat: data.user.weekdayFormat || "long", + weekdayCase: data.user.weekdayCase || "capitalize", customWeekdayNames: data.user.customWeekdayNames || "", viewStyle: data.user.viewStyle || "list", fontSize: data.user.fontSize || "M", @@ -9466,6 +9493,35 @@ function SettingsSidebar({
+ {/* Weekday Case */} +
+ + +
+