chore: release v1.17.0 - Reorganized settings, added Localisation tab, and refined UI layout

This commit is contained in:
mARTin 2026-03-08 10:30:28 +01:00
parent 2ec09fdcca
commit 77daa6e30b
5 changed files with 362 additions and 237 deletions

View File

@ -1,6 +1,6 @@
{
"name": "my-weekly-todo-list",
"version": "1.16.1",
"version": "1.17.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": {

View File

@ -383,9 +383,18 @@ export async function GET(req: NextRequest) {
// Create new local tasks from remote
const somedayListInfo = synoListIdToSomedayList.get(listId);
if (somedayListInfo) {
// Also check ALL synology tasks in DB (not just this list) to avoid duplicates
const allSynoIds = await prisma.task.findMany({
where: { userId: user.id, externalProvider: 'synology', externalId: { not: null }, deletedAt: null },
select: { externalId: true }
});
const allExistingIds = new Set(allSynoIds.map(t => t.externalId!.replace(/^synology::/, '')));
// Merge with the per-list set
for (const id of existingExternalIds) { if (id) allExistingIds.add(id); }
const newRemoteTasks = remoteTasks.filter(rt => {
const cleanId = rt.id.replace(/^synology::/, '');
return !existingExternalIds.has(cleanId);
return !allExistingIds.has(cleanId);
});
for (const remote of newRemoteTasks) {

View File

@ -126,7 +126,7 @@ export default function SimpleDatePicker({ selected, onSelect, onClose, language
borderRadius: '0.5rem',
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
padding: '1rem',
zIndex: 50,
zIndex: 2000,
width: '18rem',
border: '1px solid #f3f4f6',
animation: 'fadeIn 0.15s ease-out'

View File

@ -58,6 +58,7 @@ import {
X,
Cable,
Link,
Globe,
} from "lucide-react";
// Types
@ -222,6 +223,7 @@ const translations: Record<string, any> = {
settings: "Settings",
general: "General",
calendar: "Connections",
localisation: "Localisation",
account: "Account",
runningList: "Running List (Auto-roll tasks to today)",
protectEventTimes: "Protect Event Times",
@ -303,11 +305,19 @@ const translations: Record<string, any> = {
noProjects: "No projects yet",
assignProject: "Assign project",
removeProject: "Remove project",
weekdayFormat: "Weekday Format",
weekdayFormatFull: "Full Name (Monday)",
weekdayFormatShort: "Short (Mon)",
weekdayFormatNarrow: "Narrow (M)",
weekdayFormatCustom: "Custom",
customWeekdayNamesMon: "Mo; Tu; We; Th; Fr; Sa; Su",
customWeekdayNamesSun: "Su; Mo; Tu; We; Th; Fr; Sa",
},
de: {
settings: "Einstellungen",
general: "Allgemein",
calendar: "Verbindungen",
localisation: "Lokalisierung",
account: "Konto",
runningList: "Laufende Liste (Aufgaben automatisch auf heute verschieben)",
protectEventTimes: "Ereigniszeiten schützen",
@ -390,6 +400,13 @@ const translations: Record<string, any> = {
noProjects: "Noch keine Projekte",
assignProject: "Projekt zuweisen",
removeProject: "Projekt entfernen",
weekdayFormat: "Wochentag-Format",
weekdayFormatFull: "Vollständiger Name (Montag)",
weekdayFormatShort: "Kurz (Mo)",
weekdayFormatNarrow: "Schmal (M)",
weekdayFormatCustom: "Benutzerdefiniert",
customWeekdayNamesMon: "Mo; Di; Mi; Do; Fr; Sa; So",
customWeekdayNamesSun: "So; Mo; Di; Mi; Do; Fr; Sa",
},
};
@ -421,8 +438,23 @@ 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"): string {
return date.toLocaleDateString(locale, { weekday: "long" }).toUpperCase();
function getDayName(date: Date, locale: string = "en-US", format?: string, customNames?: string, weekStartDay: number = 0): 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();
}
}
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();
}
}
function isSameDay(d1: Date, d2: Date): boolean {
@ -761,6 +793,8 @@ export default function WeeklyView() {
showTaskCheckboxes?: boolean;
quoteSourceUrls?: string[];
startDayOffset?: number;
weekdayFormat?: "long" | "short" | "narrow" | "custom";
customWeekdayNames?: string;
}>({
name: session?.user?.name || "",
email: session?.user?.email || "",
@ -874,6 +908,8 @@ export default function WeeklyView() {
const [showFocusMode, setShowFocusMode] = useState(false);
const [showSchedule, setShowSchedule] = useState(true);
const [focusBreakDuration, setFocusBreakDuration] = useState(5);
const [weekdayFormat, setWeekdayFormat] = useState<"long" | "short" | "narrow" | "custom">("long");
const [customWeekdayNames, setCustomWeekdayNames] = useState("");
// New UI State
const [isSearchOpen, setIsSearchOpen] = useState(false);
@ -1545,6 +1581,8 @@ export default function WeeklyView() {
setShowSomeday(newSettings.showSomeday);
setShowAllDay(newSettings.showAllDayEvents);
setShowSchedule(newSettings.showSchedule);
if (newSettings.weekdayFormat) setWeekdayFormat(newSettings.weekdayFormat);
if (newSettings.customWeekdayNames !== undefined) setCustomWeekdayNames(newSettings.customWeekdayNames);
setHeadlineFont(newSettings.headlineFont);
setHeadlineFontSize(newSettings.headlineFontSize);
setHeadlineFontWeight(newSettings.headlineFontWeight);
@ -1626,6 +1664,12 @@ export default function WeeklyView() {
else if (width <= 1024) setViewDays(Math.min(v, 5));
else setViewDays(v);
}
if (data.user.weekdayFormat) {
setProfile((prev: any) => ({ ...prev, weekdayFormat: data.user.weekdayFormat }));
}
if (data.user.customWeekdayNames) {
setProfile((prev: any) => ({ ...prev, customWeekdayNames: data.user.customWeekdayNames }));
}
const cookieCellDuration = getCookie("setting_cellDuration");
if (cookieCellDuration) setCellDuration(Number(cookieCellDuration) as CellDuration);
const cookieStartHour = getCookie("setting_startHour");
@ -1643,6 +1687,10 @@ export default function WeeklyView() {
setShowAllDay(data.user.showAllDayEvents);
if (data.user.showSchedule !== undefined)
setShowSchedule(data.user.showSchedule);
if (data.user.weekdayFormat)
setWeekdayFormat(data.user.weekdayFormat as any);
if (data.user.customWeekdayNames)
setCustomWeekdayNames(data.user.customWeekdayNames);
if (data.user.hourLabelFormat)
setHourLabelFormat(data.user.hourLabelFormat as "short" | "full");
if (data.user.showSubHourSlots !== undefined)
@ -4748,11 +4796,12 @@ export default function WeeklyView() {
style={{
visibility: "hidden", pointerEvents: "none",
display: "flex",
width: "100%",
alignItems:
activeDateLayout === "above" ||
activeDateLayout === "below"
? (profile.dateAlignment === "left" ? "flex-start" : profile.dateAlignment === "right" ? "flex-end" : "center")
: "baseline",
: "center",
justifyContent:
profile.dateAlignment === "left"
? "flex-start"
@ -4836,11 +4885,12 @@ export default function WeeklyView() {
<div
style={{
display: "flex",
width: "100%",
alignItems:
activeDateLayout === "above" ||
activeDateLayout === "below"
? (profile.dateAlignment === "left" ? "flex-start" : profile.dateAlignment === "right" ? "flex-end" : "center")
: "baseline",
: "center",
justifyContent:
profile.dateAlignment === "left"
? "flex-start"
@ -4857,21 +4907,21 @@ export default function WeeklyView() {
}}
>
{activeDateLayout === "left" && (
<span className="weekly-day-date">
<span className="weekly-day-date" style={{ flexShrink: 0 }}>
{formatDateHeader(date, language)}
</span>
)}
<h3
className={`weekly-day-name ${isSameDay(date, new Date()) ? "is-today" : ""}`}
style={{ marginBottom: 0 }}
style={{ marginBottom: 0, flexShrink: 0 }}
>
{getDayName(date, language)}
{getDayName(date, language, weekdayFormat, customWeekdayNames, weekStartDay)}
</h3>
{(activeDateLayout === "right" ||
activeDateLayout === "above" ||
activeDateLayout === "below" ||
activeDateLayout === undefined) && (
<span className="weekly-day-date">
<span className="weekly-day-date" style={{ flexShrink: 0 }}>
{formatDateHeader(date, language)}
</span>
)}
@ -7007,6 +7057,12 @@ function TaskItem({
>
{task.title}
</span>
{/* DEBUG: always show provider info */}
{!isSomeday && (
<span style={{ color: task.externalProvider ? "red" : "orange", fontWeight: "bold", fontSize: "11px", marginLeft: "4px", flexShrink: 0 }}>
[{task.externalProvider || "no-ext"}]
</span>
)}
{task.externalProvider && !isSomeday && (
<span
className="flex-shrink-0"
@ -7641,6 +7697,8 @@ interface SettingsSidebarProps {
setShowSchedule: (show: boolean) => void;
dateLayout?: "above" | "below" | "left" | "right" | "hidden";
mobileDateLayout?: "above" | "below" | "left" | "right" | "hidden";
weekdayFormat?: "long" | "short" | "narrow" | "custom";
customWeekdayNames?: string;
dateAlignment?: "left" | "center" | "right" | "tight";
hourLabelFormat: "short" | "full";
setHourLabelFormat: (fmt: "short" | "full") => void;
@ -7880,7 +7938,7 @@ function SettingsSidebar({
onProjectsChanged,
}: SettingsSidebarProps) {
const [activeTab, setActiveTab] = useState<
"calendar" | "general" | "account" | "styling" | "motivation" | "about"
"calendar" | "general" | "account" | "styling" | "motivation" | "about" | "localisation"
>(initialTab || "general");
const [isLoading, setIsLoading] = useState(true);
const [isSyncing, setIsSyncing] = useState(false);
@ -8013,6 +8071,8 @@ function SettingsSidebar({
quoteSourceUrls?: string[];
startDayOffset?: number;
id?: string;
weekdayFormat?: "long" | "short" | "narrow" | "custom";
customWeekdayNames?: string;
}>({
name: "",
email: "",
@ -8066,6 +8126,9 @@ function SettingsSidebar({
dateColor: "#888888",
taskColor: "#333333",
todayHighlightColor: "#f0fafa",
pastDayColor: "#a6a6a7",
weekdayFormat: "long",
customWeekdayNames: "",
});
const t = translations[profile.language || "en"] || translations["en"];
@ -8175,6 +8238,8 @@ function SettingsSidebar({
dayHeaderGap: profile.dayHeaderGap,
showTaskCheckboxes: profile.showTaskCheckboxes,
startDayOffset: profile.startDayOffset,
weekdayFormat: profile.weekdayFormat,
customWeekdayNames: profile.customWeekdayNames,
} as any);
}, [profile, showTimeGrid, cellDuration, viewStyle, fontSize, showNextTask, showSomeday, showAllDay, showSchedule]);
@ -8211,6 +8276,8 @@ function SettingsSidebar({
? data.user.showTimeGrid
: true,
cellDuration: data.user.cellDuration || 30,
weekdayFormat: data.user.weekdayFormat || "long",
customWeekdayNames: data.user.customWeekdayNames || "",
viewStyle: data.user.viewStyle || "list",
fontSize: data.user.fontSize || "M",
headlineFont: data.user.headlineFont || "Inter",
@ -8671,6 +8738,7 @@ function SettingsSidebar({
>
{([
{ key: "general", icon: <Settings size={18} />, label: t.general },
{ key: "localisation", icon: <Globe size={18} />, label: t.localisation },
{ key: "calendar", icon: <Link size={18} />, label: t.calendar },
{ key: "account", icon: <User size={18} />, label: t.account },
{ key: "styling", icon: <Palette size={18} />, label: "Styling" },
@ -8999,88 +9067,64 @@ function SettingsSidebar({
/>
</div>
</div>
{/* Hour Label Format */}
<div style={{ marginTop: "4px" }}>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
Hour Label Format
</label>
<select
value={hourLabelFormat}
onChange={(e) => {
const fmt = e.target.value as "short" | "full";
setHourLabelFormat(fmt);
saveSetting("hourLabelFormat", fmt);
}}
className="weekly-input"
style={{
width: "100%",
padding: "8px",
border: "1px solid #ddd",
borderRadius: "4px",
}}
>
<option value="short">Short (8, 9, 10)</option>
<option value="full">Full (8:00, 9:00, 10:00)</option>
</select>
</div>
{/* Sub-hour Slot Labels */}
<div
style={{ display: "flex", alignItems: "center", gap: "8px", marginTop: "8px" }}
>
<input
type="checkbox"
id="showSubHourSlots"
checked={showSubHourSlots}
onChange={(e) => {
setShowSubHourSlots(e.target.checked);
saveSetting("showSubHourSlots", e.target.checked);
}}
style={{ width: "16px", height: "16px" }}
/>
<label
htmlFor="showSubHourSlots"
style={{ fontSize: "0.9rem", fontWeight: 600 }}
>
Show Sub-hour Labels (:15, :30, :45)
</label>
</div>
</div>
)}
<div
style={{
borderTop: "1px solid #eee",
marginTop: "16px",
paddingTop: "16px",
}}
></div>
<h4 style={{ fontSize: "1rem", fontWeight: 600, margin: 0 }}>
{t.localization}
</h4>
{/* Start Week Setting + Start View On */}
<div style={{ display: "flex", gap: "24px", flexWrap: "wrap" }}>
<div>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
{profile.language === "de" ? "Woche beginnt am" : "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)}
>
{profile.language === "de" ? "Montag" : "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)}
>
{profile.language === "de" ? "Sonntag" : "Sunday"}
</button>
</div>
</div>
<div>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
{profile.language === "de" ? "Ansicht beginnt mit" : "Start view on"}
</label>
<div style={{ display: "flex", gap: "8px" }}>
<button
className={`px-3 py-2 rounded text-sm ${(profile.startDayOffset || 0) === 0 ? "bg-blue-600 text-white" : "bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300"}`}
onClick={() => {
setProfile({ ...profile, startDayOffset: 0 });
saveSetting("startDayOffset", 0);
const d = new Date();
d.setHours(0, 0, 0, 0);
setCurrentWeekStart(d);
}}
>
{profile.language === "de" ? "Heute" : "Today"}
</button>
<button
className={`px-3 py-2 rounded text-sm ${profile.startDayOffset === -1 ? "bg-blue-600 text-white" : "bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300"}`}
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);
}}
>
{profile.language === "de" ? "Gestern" : "Yesterday"}
</button>
</div>
</div>
</div>
<div style={{ marginTop: "16px" }}>
@ -9135,150 +9179,6 @@ function SettingsSidebar({
</div>
<div style={{ marginTop: "16px" }}>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
{t.language}
</label>
<select
value={profile.language}
onChange={(e) =>
setProfile({ ...profile, language: e.target.value })
}
className="weekly-input"
style={{
width: "100%",
padding: "8px",
border: "1px solid #ddd",
borderRadius: "4px",
}}
>
<option value="en">English</option>
<option value="de">German</option>
<option value="fr">French</option>
<option value="es">Spanish</option>
</select>
</div>
<div>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
{t.dateFormat}
</label>
<select
value={profile.dateFormat}
onChange={(e) =>
setProfile({ ...profile, dateFormat: e.target.value })
}
className="weekly-input"
style={{
width: "100%",
padding: "8px",
border: "1px solid #ddd",
borderRadius: "4px",
}}
>
<option value="MM/dd/yyyy">MM/DD/YYYY</option>
<option value="dd/MM/yyyy">DD/MM/YYYY</option>
<option value="yyyy-MM-dd">YYYY-MM-DD</option>
</select>
</div>
<div>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
{t.timeFormat}
</label>
<select
value={profile.timeFormat}
onChange={(e) =>
setProfile({ ...profile, timeFormat: e.target.value })
}
className="weekly-input"
style={{
width: "100%",
padding: "8px",
border: "1px solid #ddd",
borderRadius: "4px",
}}
>
<option value="12h">12h AM/PM</option>
<option value="24h">24H</option>
</select>
</div>
{/* Hour Label Format */}
<div>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
Hour Label Format
</label>
<select
value={hourLabelFormat}
onChange={(e) => {
const fmt = e.target.value as "short" | "full";
setHourLabelFormat(fmt);
saveSetting("hourLabelFormat", fmt);
}}
className="weekly-input"
style={{
width: "100%",
padding: "8px",
border: "1px solid #ddd",
borderRadius: "4px",
}}
>
<option value="short">Short (8, 9, 10)</option>
<option value="full">Full (8:00, 9:00, 10:00)</option>
</select>
</div>
{/* Sub-hour Slot Labels */}
<div
style={{ display: "flex", alignItems: "center", gap: "8px" }}
>
<input
type="checkbox"
id="showSubHourSlots"
checked={showSubHourSlots}
onChange={(e) => {
setShowSubHourSlots(e.target.checked);
saveSetting("showSubHourSlots", e.target.checked);
}}
style={{ width: "16px", height: "16px" }}
/>
<label
htmlFor="showSubHourSlots"
style={{ fontSize: "0.9rem", fontWeight: 600 }}
>
Show Sub-hour Labels (:15, :30, :45)
</label>
</div>
{/* Projects Section */}
<div style={{ marginTop: "24px", borderTop: "1px solid var(--border-color, #e5e7eb)", paddingTop: "16px" }}>
<h4 style={{ fontSize: "0.95rem", fontWeight: 700, marginBottom: "8px", display: "flex", alignItems: "center", gap: "6px" }}>
@ -9429,6 +9329,222 @@ function SettingsSidebar({
)}
</div>
</div>
) : activeTab === "localisation" ? (
<div
style={{ display: "flex", flexDirection: "column", gap: "20px" }}
>
<h4 style={{ fontSize: "1rem", fontWeight: 600, margin: 0 }}>
{t.localisation || "Localisation"}
</h4>
{/* Start Week Setting + Start View On */}
<div style={{ display: "flex", gap: "24px", flexWrap: "wrap" }}>
<div>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
{profile.language === "de" ? "Woche beginnt am" : "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)}
>
{profile.language === "de" ? "Montag" : "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)}
>
{profile.language === "de" ? "Sonntag" : "Sunday"}
</button>
</div>
</div>
<div>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
{profile.language === "de" ? "Ansicht beginnt mit" : "Start view on"}
</label>
<div style={{ display: "flex", gap: "8px" }}>
<button
className={`px-3 py-2 rounded text-sm ${(profile.startDayOffset || 0) === 0 ? "bg-blue-600 text-white" : "bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300"}`}
onClick={() => {
setProfile({ ...profile, startDayOffset: 0 });
saveSetting("startDayOffset", 0);
const d = new Date();
d.setHours(0, 0, 0, 0);
setCurrentWeekStart(d);
}}
>
{profile.language === "de" ? "Heute" : "Today"}
</button>
<button
className={`px-3 py-2 rounded text-sm ${profile.startDayOffset === -1 ? "bg-blue-600 text-white" : "bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300"}`}
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);
}}
>
{profile.language === "de" ? "Gestern" : "Yesterday"}
</button>
</div>
</div>
</div>
{/* Weekday Format */}
<div style={{ marginTop: "8px" }}>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "8px",
}}
>
{t.weekdayFormat || translations["en"].weekdayFormat}
</label>
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
<select
value={profile.weekdayFormat || "long"}
onChange={(e) =>
setProfile({
...profile,
weekdayFormat: e.target.value as any,
})
}
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="long">{t.weekdayFormatFull || translations["en"].weekdayFormatFull}</option>
<option value="short">{t.weekdayFormatShort || translations["en"].weekdayFormatShort}</option>
<option value="narrow">{t.weekdayFormatNarrow || translations["en"].weekdayFormatNarrow}</option>
<option value="custom">{t.weekdayFormatCustom || translations["en"].weekdayFormatCustom}</option>
</select>
{profile.weekdayFormat === "custom" && (
<input
type="text"
value={profile.customWeekdayNames || ""}
onChange={(e) => setProfile({ ...profile, customWeekdayNames: e.target.value })}
placeholder={
weekStartDay === 1
? (t.customWeekdayNamesMon || translations["en"].customWeekdayNamesMon)
: (t.customWeekdayNamesSun || translations["en"].customWeekdayNamesSun)
}
className="weekly-input"
style={{ width: "100%", padding: "8px", fontSize: "0.85rem", border: "1px solid var(--weekly-settings-input-border)", borderRadius: "4px", background: "var(--weekly-settings-input-bg)", color: "var(--weekly-settings-text)" }}
/>
)}
</div>
</div>
<div style={{ marginTop: "8px" }}>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
{t.language}
</label>
<select
value={profile.language}
onChange={(e) =>
setProfile({ ...profile, language: e.target.value })
}
className="weekly-input"
style={{
width: "100%",
padding: "8px",
border: "1px solid #ddd",
borderRadius: "4px",
}}
>
<option value="en">English</option>
<option value="de">German</option>
<option value="fr">French</option>
<option value="es">Spanish</option>
</select>
</div>
<div>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
{t.dateFormat}
</label>
<select
value={profile.dateFormat}
onChange={(e) =>
setProfile({ ...profile, dateFormat: e.target.value })
}
className="weekly-input"
style={{
width: "100%",
padding: "8px",
border: "1px solid #ddd",
borderRadius: "4px",
}}
>
<option value="MM/dd/yyyy">MM/DD/YYYY</option>
<option value="dd/MM/yyyy">DD/MM/YYYY</option>
<option value="yyyy-MM-dd">YYYY-MM-DD</option>
</select>
</div>
<div>
<label
style={{
display: "block",
fontSize: "0.9rem",
fontWeight: 600,
marginBottom: "4px",
}}
>
{t.timeFormat}
</label>
<select
value={profile.timeFormat}
onChange={(e) =>
setProfile({ ...profile, timeFormat: e.target.value })
}
className="weekly-input"
style={{
width: "100%",
padding: "8px",
border: "1px solid #ddd",
borderRadius: "4px",
}}
>
<option value="12h">12h AM/PM</option>
<option value="24h">24H</option>
</select>
</div>
<div />
</div>
) : activeTab === "calendar" ? (
isLoading ? (
<p>Loading connections...</p>
@ -10190,9 +10306,9 @@ function SettingsSidebar({
type="text"
value={profile.headlineFontSize || "1.25rem"}
onChange={(e) => setProfile({ ...profile, headlineFontSize: e.target.value })}
placeholder="1.25rem"
placeholder="Font size (e.g. 1.25rem)"
className="weekly-input"
style={{ flex: 1, 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)" }}
style={{ flex: 1, padding: "8px", fontSize: "0.85rem", border: "1px solid var(--weekly-settings-input-border)", borderRadius: "4px", background: "var(--weekly-settings-input-bg)", color: "var(--weekly-settings-text)" }}
/>
<select
value={profile.headlineFontWeight || "900"}

File diff suppressed because one or more lines are too long