feat: weekday case setting, right-aligned icons, font matching, calendar fixes
- Add weekday case setting (Normal/Capitalize/Uppercase) with DB persistence - Move provider icons to end of line (right-aligned with marginLeft: auto) in both GridTaskBlock and TaskItem - Make time grid task font inherit from task font settings as fallback instead of separate hardcoded defaults (0.75rem/500 → task font values) - Fix Jump to Date calendar: center-aligned dropdown, dark mode support via CSS variables, proper border and shadow styling - Add weekdayCase to Prisma schema, profile API, and translations (EN/DE) v1.19.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
05dab0720b
commit
9863f8e28e
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "my-weekly-todo-list",
|
"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",
|
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -93,6 +93,7 @@ model User {
|
|||||||
showCompletedTasks Boolean @default(true)
|
showCompletedTasks Boolean @default(true)
|
||||||
showLines Boolean @default(true)
|
showLines Boolean @default(true)
|
||||||
weekdayFormat String? @default("long")
|
weekdayFormat String? @default("long")
|
||||||
|
weekdayCase String? @default("capitalize")
|
||||||
customWeekdayNames String? @default("")
|
customWeekdayNames String? @default("")
|
||||||
startDayOffset Int @default(-1)
|
startDayOffset Int @default(-1)
|
||||||
quoteSourceUrls String[] @default([])
|
quoteSourceUrls String[] @default([])
|
||||||
|
|||||||
@ -84,6 +84,7 @@ export async function GET(request: NextRequest) {
|
|||||||
showLines: true,
|
showLines: true,
|
||||||
startDayOffset: true,
|
startDayOffset: true,
|
||||||
weekdayFormat: true,
|
weekdayFormat: true,
|
||||||
|
weekdayCase: true,
|
||||||
customWeekdayNames: true,
|
customWeekdayNames: true,
|
||||||
quoteSourceUrls: true,
|
quoteSourceUrls: true,
|
||||||
accountNumber: true,
|
accountNumber: true,
|
||||||
@ -128,7 +129,7 @@ export async function PATCH(request: NextRequest) {
|
|||||||
cwFontFamily, cwFontSize, cwFontWeight, cwColor,
|
cwFontFamily, cwFontSize, cwFontWeight, cwColor,
|
||||||
yearFontFamily, yearFontSize, yearFontWeight, yearColor,
|
yearFontFamily, yearFontSize, yearFontWeight, yearColor,
|
||||||
showTaskCheckboxes, dayHeaderGap,
|
showTaskCheckboxes, dayHeaderGap,
|
||||||
showCompletedTasks, showLines, startDayOffset, weekdayFormat, customWeekdayNames, quoteSourceUrls
|
showCompletedTasks, showLines, startDayOffset, weekdayFormat, weekdayCase, customWeekdayNames, quoteSourceUrls
|
||||||
} = body;
|
} = body;
|
||||||
|
|
||||||
const updateData: any = {
|
const updateData: any = {
|
||||||
@ -202,6 +203,7 @@ export async function PATCH(request: NextRequest) {
|
|||||||
...(showLines !== undefined && { showLines }),
|
...(showLines !== undefined && { showLines }),
|
||||||
...(startDayOffset !== undefined && { startDayOffset }),
|
...(startDayOffset !== undefined && { startDayOffset }),
|
||||||
...(weekdayFormat !== undefined && { weekdayFormat }),
|
...(weekdayFormat !== undefined && { weekdayFormat }),
|
||||||
|
...(weekdayCase !== undefined && { weekdayCase }),
|
||||||
...(customWeekdayNames !== undefined && { customWeekdayNames }),
|
...(customWeekdayNames !== undefined && { customWeekdayNames }),
|
||||||
...(quoteSourceUrls !== undefined && { quoteSourceUrls }),
|
...(quoteSourceUrls !== undefined && { quoteSourceUrls }),
|
||||||
};
|
};
|
||||||
@ -285,6 +287,7 @@ export async function PATCH(request: NextRequest) {
|
|||||||
showLines: true,
|
showLines: true,
|
||||||
startDayOffset: true,
|
startDayOffset: true,
|
||||||
weekdayFormat: true,
|
weekdayFormat: true,
|
||||||
|
weekdayCase: true,
|
||||||
customWeekdayNames: true,
|
customWeekdayNames: true,
|
||||||
quoteSourceUrls: true,
|
quoteSourceUrls: true,
|
||||||
accountNumber: true,
|
accountNumber: true,
|
||||||
|
|||||||
@ -274,28 +274,6 @@ export function GridTaskBlock({
|
|||||||
)}
|
)}
|
||||||
<span style={task.completed && showTaskCheckboxes ? { opacity: 0.5, flex: 1, display: "flex", alignItems: "center", flexWrap: "wrap", rowGap: "2px" } : { flex: 1, display: "flex", alignItems: "center", flexWrap: "wrap", rowGap: "2px" }}>
|
<span style={task.completed && showTaskCheckboxes ? { opacity: 0.5, flex: 1, display: "flex", alignItems: "center", flexWrap: "wrap", rowGap: "2px" } : { flex: 1, display: "flex", alignItems: "center", flexWrap: "wrap", rowGap: "2px" }}>
|
||||||
<span style={{ marginRight: "4px" }}>{task.title}</span>
|
<span style={{ marginRight: "4px" }}>{task.title}</span>
|
||||||
{(() => {
|
|
||||||
const provider = task.externalProvider
|
|
||||||
|| (task.externalId?.startsWith("synology::") ? "synology" : null);
|
|
||||||
if (!provider) return null;
|
|
||||||
const iconMap: Record<string, { icon: any; color: string; label: string }> = {
|
|
||||||
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 (
|
|
||||||
<span
|
|
||||||
className="flex-shrink-0"
|
|
||||||
title={`Synced with ${info.label}`}
|
|
||||||
style={{ display: "inline-flex", alignItems: "center", opacity: 0.55 }}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={info.icon} style={{ width: 10, height: 10, color: info.color }} />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
{/* Subtask indicator */}
|
{/* Subtask indicator */}
|
||||||
{task.subTasks && task.subTasks.length > 0 && (() => {
|
{task.subTasks && task.subTasks.length > 0 && (() => {
|
||||||
const completed = task.subTasks.filter(s => s.completed).length;
|
const completed = task.subTasks.filter(s => s.completed).length;
|
||||||
@ -362,6 +340,28 @@ export function GridTaskBlock({
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{(() => {
|
||||||
|
const provider = task.externalProvider
|
||||||
|
|| (task.externalId?.startsWith("synology::") ? "synology" : null);
|
||||||
|
if (!provider) return null;
|
||||||
|
const iconMap: Record<string, { icon: any; color: string; label: string }> = {
|
||||||
|
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 (
|
||||||
|
<span
|
||||||
|
className="flex-shrink-0"
|
||||||
|
title={`Synced with ${info.label}`}
|
||||||
|
style={{ display: "inline-flex", alignItems: "center", opacity: 0.55, marginLeft: "auto", paddingLeft: "4px" }}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={info.icon} style={{ width: 10, height: 10, color: info.color }} />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -26,13 +26,13 @@ export default function SimpleDatePicker({ selected, onSelect, onClose, language
|
|||||||
|
|
||||||
const renderHeader = () => (
|
const renderHeader = () => (
|
||||||
<div className="datepicker-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1rem', padding: '0 0.5rem' }}>
|
<div className="datepicker-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1rem', padding: '0 0.5rem' }}>
|
||||||
<button onClick={() => setCurrentMonth(subMonths(currentMonth, 1))} style={{ padding: '0.25rem', background: 'none', border: 'none', cursor: 'pointer', color: '#9ca3af', transition: 'color 0.2s' }}>
|
<button onClick={() => setCurrentMonth(subMonths(currentMonth, 1))} style={{ padding: '0.25rem', background: 'none', border: 'none', cursor: 'pointer', color: 'var(--weekly-text-light, #9ca3af)', transition: 'color 0.2s' }}>
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
|
||||||
</button>
|
</button>
|
||||||
<div style={{ fontWeight: 'bold', fontSize: '0.875rem', letterSpacing: '0.05em', textTransform: 'uppercase', color: '#374151' }}>
|
<div style={{ fontWeight: 'bold', fontSize: '0.875rem', letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--weekly-text, #374151)' }}>
|
||||||
{format(currentMonth, 'MMMM yyyy', { locale })}
|
{format(currentMonth, 'MMMM yyyy', { locale })}
|
||||||
</div>
|
</div>
|
||||||
<button onClick={() => setCurrentMonth(addMonths(currentMonth, 1))} style={{ padding: '0.25rem', background: 'none', border: 'none', cursor: 'pointer', color: '#9ca3af', transition: 'color 0.2s' }}>
|
<button onClick={() => setCurrentMonth(addMonths(currentMonth, 1))} style={{ padding: '0.25rem', background: 'none', border: 'none', cursor: 'pointer', color: 'var(--weekly-text-light, #9ca3af)', transition: 'color 0.2s' }}>
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -43,7 +43,7 @@ export default function SimpleDatePicker({ selected, onSelect, onClose, language
|
|||||||
const startDate = startOfWeek(currentMonth, { weekStartsOn: 1 });
|
const startDate = startOfWeek(currentMonth, { weekStartsOn: 1 });
|
||||||
for (let i = 0; i < 7; i++) {
|
for (let i = 0; i < 7; i++) {
|
||||||
days.push(
|
days.push(
|
||||||
<div key={i} style={{ textAlign: 'center', fontSize: '0.75rem', fontWeight: 'bold', color: '#9ca3af', padding: '0.5rem 0' }}>
|
<div key={i} style={{ textAlign: 'center', fontSize: '0.75rem', fontWeight: 'bold', color: 'var(--weekly-text-light, #9ca3af)', padding: '0.5rem 0' }}>
|
||||||
{format(addDays(startDate, i), 'EEEEEE', { locale })}
|
{format(addDays(startDate, i), 'EEEEEE', { locale })}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -87,7 +87,7 @@ export default function SimpleDatePicker({ selected, onSelect, onClose, language
|
|||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
transition: 'all 0.2s',
|
transition: 'all 0.2s',
|
||||||
margin: '0 auto',
|
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',
|
background: isDaySelected ? 'black' : 'transparent',
|
||||||
fontWeight: isDaySelected ? 'bold' : 'normal',
|
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',
|
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' } : {})
|
...(isToday && !isDaySelected ? { color: '#ef4444', fontWeight: 'bold' } : {})
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
if (!isDaySelected) e.currentTarget.style.backgroundColor = '#f3f4f6';
|
if (!isDaySelected) e.currentTarget.style.backgroundColor = 'var(--weekly-hover-bg, #f3f4f6)';
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
if (!isDaySelected) e.currentTarget.style.backgroundColor = 'transparent';
|
if (!isDaySelected) e.currentTarget.style.backgroundColor = 'transparent';
|
||||||
@ -120,28 +120,30 @@ export default function SimpleDatePicker({ selected, onSelect, onClose, language
|
|||||||
<div ref={modalRef} style={{
|
<div ref={modalRef} style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '100%',
|
top: '100%',
|
||||||
right: 0,
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
marginTop: '0.5rem',
|
marginTop: '0.5rem',
|
||||||
backgroundColor: 'white',
|
backgroundColor: 'var(--weekly-bg, white)',
|
||||||
borderRadius: '0.5rem',
|
borderRadius: '0.5rem',
|
||||||
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
|
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.15), 0 10px 10px -5px rgba(0, 0, 0, 0.08)',
|
||||||
padding: '1rem',
|
padding: '1rem',
|
||||||
zIndex: 2000,
|
zIndex: 2000,
|
||||||
width: '18rem',
|
width: '18rem',
|
||||||
border: '1px solid #f3f4f6',
|
border: '1px solid var(--weekly-border, #e5e7eb)',
|
||||||
animation: 'fadeIn 0.15s ease-out'
|
animation: 'fadeIn 0.15s ease-out'
|
||||||
}}>
|
}}>
|
||||||
{/* Decorative triangle */}
|
{/* Decorative triangle */}
|
||||||
<div style={{
|
<div style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '-0.3rem',
|
top: '-0.3rem',
|
||||||
right: '1rem',
|
left: '50%',
|
||||||
|
marginLeft: '-0.375rem',
|
||||||
width: '0.75rem',
|
width: '0.75rem',
|
||||||
height: '0.75rem',
|
height: '0.75rem',
|
||||||
backgroundColor: 'white',
|
backgroundColor: 'var(--weekly-bg, white)',
|
||||||
transform: 'rotate(45deg)',
|
transform: 'rotate(45deg)',
|
||||||
borderTop: '1px solid #f3f4f6',
|
borderTop: '1px solid var(--weekly-border, #e5e7eb)',
|
||||||
borderLeft: '1px solid #f3f4f6'
|
borderLeft: '1px solid var(--weekly-border, #e5e7eb)'
|
||||||
}}></div>
|
}}></div>
|
||||||
{renderHeader()}
|
{renderHeader()}
|
||||||
{renderDays()}
|
{renderDays()}
|
||||||
|
|||||||
@ -317,6 +317,10 @@ const translations: Record<string, any> = {
|
|||||||
weekdayFormatCustom: "Custom",
|
weekdayFormatCustom: "Custom",
|
||||||
customWeekdayNamesMon: "Mo; Tu; We; Th; Fr; Sa; Su",
|
customWeekdayNamesMon: "Mo; Tu; We; Th; Fr; Sa; Su",
|
||||||
customWeekdayNamesSun: "Su; Mo; Tu; We; Th; Fr; Sa",
|
customWeekdayNamesSun: "Su; Mo; Tu; We; Th; Fr; Sa",
|
||||||
|
weekdayCase: "Weekday Case",
|
||||||
|
weekdayCaseNormal: "Normal (monday)",
|
||||||
|
weekdayCaseCapitalize: "Capitalize (Monday)",
|
||||||
|
weekdayCaseUppercase: "Uppercase (MONDAY)",
|
||||||
},
|
},
|
||||||
de: {
|
de: {
|
||||||
settings: "Einstellungen",
|
settings: "Einstellungen",
|
||||||
@ -417,6 +421,10 @@ const translations: Record<string, any> = {
|
|||||||
weekdayFormatCustom: "Benutzerdefiniert",
|
weekdayFormatCustom: "Benutzerdefiniert",
|
||||||
customWeekdayNamesMon: "Mo; Di; Mi; Do; Fr; Sa; So",
|
customWeekdayNamesMon: "Mo; Di; Mi; Do; Fr; Sa; So",
|
||||||
customWeekdayNamesSun: "So; Mo; Di; Mi; Do; Fr; Sa",
|
customWeekdayNamesSun: "So; Mo; Di; Mi; Do; Fr; Sa",
|
||||||
|
weekdayCase: "Groß-/Kleinschreibung",
|
||||||
|
weekdayCaseNormal: "Klein (montag)",
|
||||||
|
weekdayCaseCapitalize: "Großbuchstabe (Montag)",
|
||||||
|
weekdayCaseUppercase: "Großbuchstaben (MONTAG)",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -448,24 +456,31 @@ function formatDateHeader(date: Date, locale: string = "en-US"): string {
|
|||||||
return date.toLocaleDateString(locale, { day: "numeric", month: "short" }); // e.g. 12. Feb.
|
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) {
|
if (format === "custom" && customNames) {
|
||||||
// Split by comma or semicolon to allow spaces in names
|
// Split by comma or semicolon to allow spaces in names
|
||||||
const names = customNames.split(/[,;]+/).map(s => s.trim()).filter(Boolean);
|
const names = customNames.split(/[,;]+/).map(s => s.trim()).filter(Boolean);
|
||||||
if (names.length === 7) {
|
if (names.length === 7) {
|
||||||
// Adjust index based on weekStartDay (0=Sun, 1=Mon)
|
// Adjust index based on weekStartDay (0=Sun, 1=Mon)
|
||||||
const index = (date.getDay() - weekStartDay + 7) % 7;
|
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");
|
const weekdayOption = format === "narrow" ? "narrow" : (format === "short" ? "short" : "long");
|
||||||
try {
|
try {
|
||||||
return date.toLocaleDateString(locale, { weekday: weekdayOption }).toUpperCase();
|
name = date.toLocaleDateString(locale, { weekday: weekdayOption });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return date.toLocaleDateString("en-US", { weekday: weekdayOption }).toUpperCase();
|
name = date.toLocaleDateString("en-US", { weekday: weekdayOption });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 {
|
function isSameDay(d1: Date, d2: Date): boolean {
|
||||||
return d1.toDateString() === d2.toDateString();
|
return d1.toDateString() === d2.toDateString();
|
||||||
@ -804,6 +819,7 @@ export default function WeeklyView() {
|
|||||||
quoteSourceUrls?: string[];
|
quoteSourceUrls?: string[];
|
||||||
startDayOffset?: number;
|
startDayOffset?: number;
|
||||||
weekdayFormat?: "long" | "short" | "narrow" | "custom";
|
weekdayFormat?: "long" | "short" | "narrow" | "custom";
|
||||||
|
weekdayCase?: "normal" | "capitalize" | "uppercase";
|
||||||
customWeekdayNames?: string;
|
customWeekdayNames?: string;
|
||||||
dateVerticalAlign?: "top" | "middle" | "bottom";
|
dateVerticalAlign?: "top" | "middle" | "bottom";
|
||||||
}>({
|
}>({
|
||||||
@ -921,6 +937,7 @@ export default function WeeklyView() {
|
|||||||
const [showSchedule, setShowSchedule] = useState(true);
|
const [showSchedule, setShowSchedule] = useState(true);
|
||||||
const [focusBreakDuration, setFocusBreakDuration] = useState(5);
|
const [focusBreakDuration, setFocusBreakDuration] = useState(5);
|
||||||
const [weekdayFormat, setWeekdayFormat] = useState<"long" | "short" | "narrow" | "custom">("long");
|
const [weekdayFormat, setWeekdayFormat] = useState<"long" | "short" | "narrow" | "custom">("long");
|
||||||
|
const [weekdayCase, setWeekdayCase] = useState<"normal" | "capitalize" | "uppercase">("capitalize");
|
||||||
const [customWeekdayNames, setCustomWeekdayNames] = useState("");
|
const [customWeekdayNames, setCustomWeekdayNames] = useState("");
|
||||||
|
|
||||||
// New UI State
|
// New UI State
|
||||||
@ -1594,6 +1611,7 @@ export default function WeeklyView() {
|
|||||||
setShowAllDay(newSettings.showAllDayEvents);
|
setShowAllDay(newSettings.showAllDayEvents);
|
||||||
setShowSchedule(newSettings.showSchedule);
|
setShowSchedule(newSettings.showSchedule);
|
||||||
if (newSettings.weekdayFormat) setWeekdayFormat(newSettings.weekdayFormat);
|
if (newSettings.weekdayFormat) setWeekdayFormat(newSettings.weekdayFormat);
|
||||||
|
if (newSettings.weekdayCase) setWeekdayCase(newSettings.weekdayCase);
|
||||||
if (newSettings.customWeekdayNames !== undefined) setCustomWeekdayNames(newSettings.customWeekdayNames);
|
if (newSettings.customWeekdayNames !== undefined) setCustomWeekdayNames(newSettings.customWeekdayNames);
|
||||||
setHeadlineFont(newSettings.headlineFont);
|
setHeadlineFont(newSettings.headlineFont);
|
||||||
setHeadlineFontSize(newSettings.headlineFontSize);
|
setHeadlineFontSize(newSettings.headlineFontSize);
|
||||||
@ -1701,6 +1719,8 @@ export default function WeeklyView() {
|
|||||||
setShowSchedule(data.user.showSchedule);
|
setShowSchedule(data.user.showSchedule);
|
||||||
if (data.user.weekdayFormat)
|
if (data.user.weekdayFormat)
|
||||||
setWeekdayFormat(data.user.weekdayFormat as any);
|
setWeekdayFormat(data.user.weekdayFormat as any);
|
||||||
|
if (data.user.weekdayCase)
|
||||||
|
setWeekdayCase(data.user.weekdayCase as any);
|
||||||
if (data.user.customWeekdayNames)
|
if (data.user.customWeekdayNames)
|
||||||
setCustomWeekdayNames(data.user.customWeekdayNames);
|
setCustomWeekdayNames(data.user.customWeekdayNames);
|
||||||
if (data.user.hourLabelFormat)
|
if (data.user.hourLabelFormat)
|
||||||
@ -3779,9 +3799,11 @@ export default function WeeklyView() {
|
|||||||
"--weekly-date-weight": profile.dateFontWeight || "400",
|
"--weekly-date-weight": profile.dateFontWeight || "400",
|
||||||
"--weekly-time-task-font": fontVal(profile.timeTaskFontFamily)
|
"--weekly-time-task-font": fontVal(profile.timeTaskFontFamily)
|
||||||
? `"${fontVal(profile.timeTaskFontFamily)}", sans-serif`
|
? `"${fontVal(profile.timeTaskFontFamily)}", sans-serif`
|
||||||
|
: fontVal(profile.taskFontFamily)
|
||||||
|
? `"${fontVal(profile.taskFontFamily)}", sans-serif`
|
||||||
: "var(--weekly-font)",
|
: "var(--weekly-font)",
|
||||||
"--weekly-time-task-size": scaleRem(profile.timeTaskFontSize || "0.75rem"),
|
"--weekly-time-task-size": scaleRem(profile.timeTaskFontSize || profile.taskFontSize || "0.9rem"),
|
||||||
"--weekly-time-task-weight": profile.timeTaskFontWeight || "500",
|
"--weekly-time-task-weight": profile.timeTaskFontWeight || profile.taskFontWeight || "400",
|
||||||
"--weekly-font":
|
"--weekly-font":
|
||||||
"var(--font-body)" /* Force default body font as requested */,
|
"var(--font-body)" /* Force default body font as requested */,
|
||||||
"--weekly-task-font": fontVal(profile.taskFontFamily)
|
"--weekly-task-font": fontVal(profile.taskFontFamily)
|
||||||
@ -4927,7 +4949,7 @@ export default function WeeklyView() {
|
|||||||
className={`weekly-day-name ${isSameDay(date, new Date()) ? "is-today" : ""}`}
|
className={`weekly-day-name ${isSameDay(date, new Date()) ? "is-today" : ""}`}
|
||||||
style={{ marginBottom: 0, flexShrink: 0 }}
|
style={{ marginBottom: 0, flexShrink: 0 }}
|
||||||
>
|
>
|
||||||
{getDayName(date, language, weekdayFormat, customWeekdayNames, weekStartDay)}
|
{getDayName(date, language, weekdayFormat, customWeekdayNames, weekStartDay, weekdayCase)}
|
||||||
</h3>
|
</h3>
|
||||||
{(activeDateLayout === "right" ||
|
{(activeDateLayout === "right" ||
|
||||||
activeDateLayout === "above" ||
|
activeDateLayout === "above" ||
|
||||||
@ -7085,7 +7107,7 @@ function TaskItem({
|
|||||||
<span
|
<span
|
||||||
className="flex-shrink-0"
|
className="flex-shrink-0"
|
||||||
title={`Synced with ${info.label}`}
|
title={`Synced with ${info.label}`}
|
||||||
style={{ display: "inline-flex", alignItems: "center", opacity: 0.55, marginLeft: "4px" }}
|
style={{ display: "inline-flex", alignItems: "center", opacity: 0.55, marginLeft: "auto", paddingLeft: "4px" }}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={info.icon} style={{ width: 12, height: 12, color: info.color }} />
|
<FontAwesomeIcon icon={info.icon} style={{ width: 12, height: 12, color: info.color }} />
|
||||||
</span>
|
</span>
|
||||||
@ -7709,6 +7731,7 @@ interface SettingsSidebarProps {
|
|||||||
dateLayout?: "above" | "below" | "left" | "right" | "hidden";
|
dateLayout?: "above" | "below" | "left" | "right" | "hidden";
|
||||||
mobileDateLayout?: "above" | "below" | "left" | "right" | "hidden";
|
mobileDateLayout?: "above" | "below" | "left" | "right" | "hidden";
|
||||||
weekdayFormat?: "long" | "short" | "narrow" | "custom";
|
weekdayFormat?: "long" | "short" | "narrow" | "custom";
|
||||||
|
weekdayCase?: "normal" | "capitalize" | "uppercase";
|
||||||
customWeekdayNames?: string;
|
customWeekdayNames?: string;
|
||||||
dateAlignment?: "left" | "center" | "right" | "tight";
|
dateAlignment?: "left" | "center" | "right" | "tight";
|
||||||
hourLabelFormat: "short" | "full";
|
hourLabelFormat: "short" | "full";
|
||||||
@ -8083,6 +8106,7 @@ function SettingsSidebar({
|
|||||||
startDayOffset?: number;
|
startDayOffset?: number;
|
||||||
id?: string;
|
id?: string;
|
||||||
weekdayFormat?: "long" | "short" | "narrow" | "custom";
|
weekdayFormat?: "long" | "short" | "narrow" | "custom";
|
||||||
|
weekdayCase?: "normal" | "capitalize" | "uppercase";
|
||||||
customWeekdayNames?: string;
|
customWeekdayNames?: string;
|
||||||
dateVerticalAlign?: "top" | "middle" | "bottom";
|
dateVerticalAlign?: "top" | "middle" | "bottom";
|
||||||
}>({
|
}>({
|
||||||
@ -8141,6 +8165,7 @@ function SettingsSidebar({
|
|||||||
todayHighlightColor: "#f0fafa",
|
todayHighlightColor: "#f0fafa",
|
||||||
pastDayColor: "#a6a6a7",
|
pastDayColor: "#a6a6a7",
|
||||||
weekdayFormat: "long",
|
weekdayFormat: "long",
|
||||||
|
weekdayCase: "capitalize",
|
||||||
customWeekdayNames: "",
|
customWeekdayNames: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -8252,6 +8277,7 @@ function SettingsSidebar({
|
|||||||
showTaskCheckboxes: profile.showTaskCheckboxes,
|
showTaskCheckboxes: profile.showTaskCheckboxes,
|
||||||
startDayOffset: profile.startDayOffset,
|
startDayOffset: profile.startDayOffset,
|
||||||
weekdayFormat: profile.weekdayFormat,
|
weekdayFormat: profile.weekdayFormat,
|
||||||
|
weekdayCase: profile.weekdayCase,
|
||||||
customWeekdayNames: profile.customWeekdayNames,
|
customWeekdayNames: profile.customWeekdayNames,
|
||||||
} as any);
|
} as any);
|
||||||
}, [profile, showTimeGrid, cellDuration, viewStyle, fontSize, showNextTask, showSomeday, showAllDay, showSchedule]);
|
}, [profile, showTimeGrid, cellDuration, viewStyle, fontSize, showNextTask, showSomeday, showAllDay, showSchedule]);
|
||||||
@ -8290,6 +8316,7 @@ function SettingsSidebar({
|
|||||||
: true,
|
: true,
|
||||||
cellDuration: data.user.cellDuration || 30,
|
cellDuration: data.user.cellDuration || 30,
|
||||||
weekdayFormat: data.user.weekdayFormat || "long",
|
weekdayFormat: data.user.weekdayFormat || "long",
|
||||||
|
weekdayCase: data.user.weekdayCase || "capitalize",
|
||||||
customWeekdayNames: data.user.customWeekdayNames || "",
|
customWeekdayNames: data.user.customWeekdayNames || "",
|
||||||
viewStyle: data.user.viewStyle || "list",
|
viewStyle: data.user.viewStyle || "list",
|
||||||
fontSize: data.user.fontSize || "M",
|
fontSize: data.user.fontSize || "M",
|
||||||
@ -9466,6 +9493,35 @@ function SettingsSidebar({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Weekday Case */}
|
||||||
|
<div style={{ marginTop: "8px" }}>
|
||||||
|
<label
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
fontWeight: 600,
|
||||||
|
marginBottom: "8px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t.weekdayCase || "Weekday Case"}
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={profile.weekdayCase || "capitalize"}
|
||||||
|
onChange={(e) =>
|
||||||
|
setProfile({
|
||||||
|
...profile,
|
||||||
|
weekdayCase: e.target.value as "normal" | "capitalize" | "uppercase",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="weekly-input"
|
||||||
|
style={{ width: "100%", padding: "8px", fontSize: "0.9rem", border: "1px solid var(--weekly-settings-input-border)", borderRadius: "4px", background: "var(--weekly-settings-input-bg)", color: "var(--weekly-settings-text)" }}
|
||||||
|
>
|
||||||
|
<option value="normal">{t.weekdayCaseNormal || "Normal (monday)"}</option>
|
||||||
|
<option value="capitalize">{t.weekdayCaseCapitalize || "Capitalize (Monday)"}</option>
|
||||||
|
<option value="uppercase">{t.weekdayCaseUppercase || "Uppercase (MONDAY)"}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style={{ marginTop: "8px" }}>
|
<div style={{ marginTop: "8px" }}>
|
||||||
<label
|
<label
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user