feat: visual updates and settings
This commit is contained in:
parent
dcd2e75537
commit
779181ddde
@ -41,8 +41,24 @@ model User {
|
||||
viewStyle String @default("grid")
|
||||
fontSize String @default("M") // "S", "M", "L"
|
||||
headlineFont String @default("Inter")
|
||||
headlineFontSize String? @default("1.25rem")
|
||||
headlineFontWeight String? @default("900")
|
||||
dateFontFamily String? @default("Inter")
|
||||
dateFontSize String? @default("0.65rem")
|
||||
dateFontWeight String? @default("400")
|
||||
timeTaskFontFamily String? @default("Inter")
|
||||
timeTaskFontSize String? @default("0.75rem")
|
||||
timeTaskFontWeight String? @default("500")
|
||||
bodyFont String @default("Inter")
|
||||
fontWeight String @default("400") // "300", "400", "500", "700"
|
||||
taskFontFamily String? @default("Inter")
|
||||
taskFontSize String? @default("0.9rem")
|
||||
taskFontWeight String? @default("400")
|
||||
eventFontFamily String? @default("Inter")
|
||||
eventFontSize String? @default("0.85rem")
|
||||
eventFontWeight String? @default("400")
|
||||
fontWeight String @default("400") // Legacy/Generic
|
||||
weekendColorSat String? @default("#666666")
|
||||
weekendColorSun String? @default("#dc2626")
|
||||
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
|
||||
@ -5,6 +5,7 @@ import { PrismaClient } from '@prisma/client';
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
// Prisma client re-initialized to pick up schema changes
|
||||
|
||||
// Get user profile
|
||||
export async function GET(request: NextRequest) {
|
||||
@ -36,8 +37,24 @@ export async function GET(request: NextRequest) {
|
||||
viewStyle: true,
|
||||
fontSize: true,
|
||||
headlineFont: true,
|
||||
headlineFontSize: true,
|
||||
headlineFontWeight: true,
|
||||
dateFontFamily: true,
|
||||
dateFontSize: true,
|
||||
dateFontWeight: true,
|
||||
timeTaskFontFamily: true,
|
||||
timeTaskFontSize: true,
|
||||
timeTaskFontWeight: true,
|
||||
bodyFont: true,
|
||||
taskFontFamily: true,
|
||||
taskFontSize: true,
|
||||
taskFontWeight: true,
|
||||
eventFontFamily: true,
|
||||
eventFontSize: true,
|
||||
eventFontWeight: true,
|
||||
fontWeight: true,
|
||||
weekendColorSat: true,
|
||||
weekendColorSun: true,
|
||||
createdAt: true
|
||||
}
|
||||
});
|
||||
@ -65,7 +82,12 @@ export async function PATCH(request: NextRequest) {
|
||||
showNextTask, calendarEditMode, focusTimerDuration, focusBreakDuration,
|
||||
showTimeGrid, showSomeday, showAllDayEvents, showSchedule,
|
||||
cellDuration, viewStyle, fontSize,
|
||||
headlineFont, bodyFont, fontWeight
|
||||
headlineFont, headlineFontSize, headlineFontWeight,
|
||||
dateFontFamily, dateFontSize, dateFontWeight,
|
||||
timeTaskFontFamily, timeTaskFontSize, timeTaskFontWeight,
|
||||
bodyFont, taskFontFamily, taskFontSize, taskFontWeight,
|
||||
eventFontFamily, eventFontSize, eventFontWeight,
|
||||
fontWeight, weekendColorSat, weekendColorSun
|
||||
} = body;
|
||||
|
||||
const updateData: any = {
|
||||
@ -90,8 +112,24 @@ export async function PATCH(request: NextRequest) {
|
||||
...(viewStyle !== undefined && { viewStyle }),
|
||||
...(fontSize !== undefined && { fontSize }),
|
||||
...(headlineFont !== undefined && { headlineFont }),
|
||||
...(headlineFontSize !== undefined && { headlineFontSize }),
|
||||
...(headlineFontWeight !== undefined && { headlineFontWeight }),
|
||||
...(dateFontFamily !== undefined && { dateFontFamily }),
|
||||
...(dateFontSize !== undefined && { dateFontSize }),
|
||||
...(dateFontWeight !== undefined && { dateFontWeight }),
|
||||
...(timeTaskFontFamily !== undefined && { timeTaskFontFamily }),
|
||||
...(timeTaskFontSize !== undefined && { timeTaskFontSize }),
|
||||
...(timeTaskFontWeight !== undefined && { timeTaskFontWeight }),
|
||||
...(bodyFont !== undefined && { bodyFont }),
|
||||
...(taskFontFamily !== undefined && { taskFontFamily }),
|
||||
...(taskFontSize !== undefined && { taskFontSize }),
|
||||
...(taskFontWeight !== undefined && { taskFontWeight }),
|
||||
...(eventFontFamily !== undefined && { eventFontFamily }),
|
||||
...(eventFontSize !== undefined && { eventFontSize }),
|
||||
...(eventFontWeight !== undefined && { eventFontWeight }),
|
||||
...(fontWeight !== undefined && { fontWeight }),
|
||||
...(weekendColorSat !== undefined && { weekendColorSat }),
|
||||
...(weekendColorSun !== undefined && { weekendColorSun }),
|
||||
};
|
||||
if (password && password.trim() !== "") {
|
||||
updateData.passwordHash = await bcrypt.hash(password, 10);
|
||||
@ -124,8 +162,24 @@ export async function PATCH(request: NextRequest) {
|
||||
viewStyle: true,
|
||||
fontSize: true,
|
||||
headlineFont: true,
|
||||
headlineFontSize: true,
|
||||
headlineFontWeight: true,
|
||||
dateFontFamily: true,
|
||||
dateFontSize: true,
|
||||
dateFontWeight: true,
|
||||
timeTaskFontFamily: true,
|
||||
timeTaskFontSize: true,
|
||||
timeTaskFontWeight: true,
|
||||
bodyFont: true,
|
||||
taskFontFamily: true,
|
||||
taskFontSize: true,
|
||||
taskFontWeight: true,
|
||||
eventFontFamily: true,
|
||||
eventFontSize: true,
|
||||
eventFontWeight: true,
|
||||
fontWeight: true,
|
||||
weekendColorSat: true,
|
||||
weekendColorSun: true,
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -221,6 +221,17 @@ h3 {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.weekly-btn-primary {
|
||||
background-color: var(--weekly-teal);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.nav-button:hover {
|
||||
background-color: #e2e8f0;
|
||||
transform: translateY(-1px);
|
||||
@ -617,6 +628,17 @@ h3 {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
/* Weekend Styling */
|
||||
.weekly-day-column.is-sat .weekly-day-name,
|
||||
.weekly-day-column.is-sat .weekly-day-date {
|
||||
color: var(--weekly-weekend-sat, #666);
|
||||
}
|
||||
|
||||
.weekly-day-column.is-sun .weekly-day-name,
|
||||
.weekly-day-column.is-sun .weekly-day-date {
|
||||
color: var(--weekly-weekend-sun, #dc2626);
|
||||
}
|
||||
|
||||
/* Day Header */
|
||||
.weekly-day-header {
|
||||
padding: 1rem 1rem 0.75rem;
|
||||
@ -626,20 +648,22 @@ h3 {
|
||||
}
|
||||
|
||||
.weekly-day-date {
|
||||
font-size: 0.65rem;
|
||||
font-size: var(--weekly-date-size, 0.65rem);
|
||||
font-weight: var(--weekly-date-weight, 400);
|
||||
color: var(--weekly-text-light);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
margin-bottom: 0.25rem;
|
||||
font-family: var(--weekly-date-font, var(--weekly-font-headline)) !important;
|
||||
}
|
||||
|
||||
.weekly-day-name {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 900;
|
||||
font-size: var(--weekly-headline-size, 1.25rem);
|
||||
font-weight: var(--weekly-headline-weight, 900);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--weekly-text);
|
||||
font-family: var(--weekly-font-headline);
|
||||
font-family: var(--weekly-font-headline) !important;
|
||||
}
|
||||
|
||||
.weekly-day-name.is-today {
|
||||
@ -685,13 +709,14 @@ h3 {
|
||||
|
||||
.weekly-task-text {
|
||||
flex: 1;
|
||||
font-size: var(--base-font-size);
|
||||
line-height: 1.5;
|
||||
font-family: var(--weekly-task-font, var(--weekly-font));
|
||||
font-size: var(--weekly-task-size, 0.9rem);
|
||||
font-weight: var(--weekly-task-weight, 400);
|
||||
line-height: var(--weekly-task-line-height, 1.5);
|
||||
color: var(--weekly-text);
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
resize: none;
|
||||
min-height: 1.5em;
|
||||
padding-top: 2px; /* Align with icons */
|
||||
@ -912,6 +937,10 @@ h3 {
|
||||
color: var(--weekly-text);
|
||||
}
|
||||
|
||||
.weekly-day-name {
|
||||
color: var(--weekly-text-light);
|
||||
}
|
||||
|
||||
.weekly-someday-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
@ -1296,6 +1325,7 @@ h3 {
|
||||
|
||||
.weekly-calendar-event-title {
|
||||
color: var(--weekly-text);
|
||||
font-family: var(--weekly-font);
|
||||
}
|
||||
|
||||
/* Drag and Drop States */
|
||||
@ -1311,7 +1341,9 @@ h3 {
|
||||
.weekly-settings-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
background: transparent !important; /* Changed from rgba(0, 0, 0, 0.4) for real-time preview */
|
||||
backdrop-filter: none !important;
|
||||
-webkit-backdrop-filter: none !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -1434,7 +1466,7 @@ h3 {
|
||||
|
||||
.time-slot-label {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
padding: 0 0.5rem;
|
||||
font-size: 0.65rem;
|
||||
@ -1516,7 +1548,9 @@ h3 {
|
||||
gap: 0.25rem;
|
||||
padding: 2px 0;
|
||||
background: transparent;
|
||||
font-size: 0.875rem;
|
||||
font-family: var(--weekly-time-task-font, var(--weekly-font));
|
||||
font-size: var(--weekly-time-task-size, 0.75rem);
|
||||
font-weight: var(--weekly-time-task-weight, 500);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@ -1573,18 +1607,25 @@ h3 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
font-family: var(--weekly-font);
|
||||
}
|
||||
|
||||
.time-slot-event .event-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
font-weight: 500;
|
||||
font-family: var(--weekly-font);
|
||||
}
|
||||
|
||||
.time-slot-event .event-time-row {
|
||||
font-size: 0.65rem;
|
||||
opacity: 0.8;
|
||||
white-space: nowrap;
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.9;
|
||||
font-family: var(--weekly-font);
|
||||
}
|
||||
|
||||
.time-slot-event:hover {
|
||||
@ -2407,3 +2448,16 @@ h3 {
|
||||
letter-spacing: 0.1em;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-family: var(--weekly-event-font);
|
||||
font-weight: var(--weekly-event-weight);
|
||||
font-size: var(--weekly-event-size);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.event-time-row {
|
||||
font-family: var(--weekly-event-font);
|
||||
font-size: 0.7rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
@ -343,11 +343,100 @@ export default function WeeklyView() {
|
||||
const [dateFormat, setDateFormat] = useState('yyyy-MM-dd');
|
||||
|
||||
const [somedayExpanded, setSomedayExpanded] = useState(true);
|
||||
const [draggingListId, setDraggingListId] = useState<string | null>(null);
|
||||
const [isAllDayExpanded, setIsAllDayExpanded] = useState(true);
|
||||
const [somedayLists, setSomedayLists] = useState<SomedayList[]>([]);
|
||||
const [editingTaskId, setEditingTaskId] = useState<string | null>(null);
|
||||
const [draggingListId, setDraggingListId] = useState<string | null>(null);
|
||||
|
||||
// Moved state definitions to the top
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<'general' | 'calendar' | 'account'>('general');
|
||||
const [exportStartDate, setExportStartDate] = useState('');
|
||||
const [exportEndDate, setExportEndDate] = useState('');
|
||||
const [passwords, setPasswords] = useState({ new: '', confirm: '' });
|
||||
const [accountMsg, setAccountMsg] = useState('');
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [profile, setProfile] = useState<{
|
||||
name: string;
|
||||
email: string;
|
||||
timezone: string;
|
||||
autoRolling?: boolean;
|
||||
protectEventTimes?: boolean;
|
||||
language?: string;
|
||||
dateFormat?: string;
|
||||
timeFormat?: string;
|
||||
startHour?: number;
|
||||
endHour?: number;
|
||||
focusTimerDuration?: number;
|
||||
focusBreakDuration?: number;
|
||||
showTimeGrid?: boolean;
|
||||
cellDuration?: number;
|
||||
viewStyle?: string;
|
||||
fontSize?: 'S' | 'M' | 'L';
|
||||
showNextTask?: boolean;
|
||||
showSomeday?: boolean;
|
||||
showAllDayEvents?: boolean;
|
||||
showSchedule?: boolean;
|
||||
headlineFont?: string;
|
||||
headlineFontSize?: string;
|
||||
headlineFontWeight?: string;
|
||||
dateFontFamily?: string;
|
||||
dateFontSize?: string;
|
||||
dateFontWeight?: string;
|
||||
timeTaskFontFamily?: string;
|
||||
timeTaskFontSize?: string;
|
||||
timeTaskFontWeight?: string;
|
||||
bodyFont?: string;
|
||||
taskFontFamily?: string;
|
||||
taskFontSize?: string;
|
||||
taskFontWeight?: string;
|
||||
eventFontFamily?: string;
|
||||
eventFontSize?: string;
|
||||
eventFontWeight?: string;
|
||||
fontWeight?: string;
|
||||
weekendColorSat?: string;
|
||||
weekendColorSun?: string;
|
||||
}>({
|
||||
name: session?.user?.name || '',
|
||||
email: session?.user?.email || '',
|
||||
timezone: 'UTC',
|
||||
language: 'de',
|
||||
dateFormat: 'yyyy-MM-dd',
|
||||
timeFormat: '24h',
|
||||
startHour: 8,
|
||||
endHour: 22,
|
||||
autoRolling: true,
|
||||
protectEventTimes: true,
|
||||
showTimeGrid: true,
|
||||
cellDuration: 60,
|
||||
viewStyle: 'list',
|
||||
fontSize: 'M',
|
||||
showNextTask: false,
|
||||
showSomeday: true,
|
||||
showAllDayEvents: true,
|
||||
showSchedule: true,
|
||||
headlineFont: 'Inter',
|
||||
headlineFontSize: '1.25rem',
|
||||
headlineFontWeight: '900',
|
||||
dateFontFamily: 'Inter',
|
||||
dateFontSize: '0.65rem',
|
||||
dateFontWeight: '400',
|
||||
timeTaskFontFamily: 'Inter',
|
||||
timeTaskFontSize: '0.75rem',
|
||||
timeTaskFontWeight: '500',
|
||||
bodyFont: 'Inter',
|
||||
taskFontFamily: 'Inter',
|
||||
taskFontSize: '0.9rem',
|
||||
taskFontWeight: '400',
|
||||
eventFontFamily: 'Inter',
|
||||
eventFontSize: '0.85rem',
|
||||
eventFontWeight: '400',
|
||||
fontWeight: '400',
|
||||
weekendColorSat: '#666666',
|
||||
weekendColorSun: '#dc2626',
|
||||
focusTimerDuration: 25,
|
||||
focusBreakDuration: 5
|
||||
});
|
||||
const [showSummary, setShowSummary] = useState(false);
|
||||
|
||||
const [isAddingSomedayList, setIsAddingSomedayList] = useState(false);
|
||||
@ -388,11 +477,36 @@ export default function WeeklyView() {
|
||||
const [focusTimerDuration, setFocusTimerDuration] = useState(25);
|
||||
const [fontSize, setFontSize] = useState<'S' | 'M' | 'L'>('M');
|
||||
const [headlineFont, setHeadlineFont] = useState('Inter');
|
||||
const [headlineFontSize, setHeadlineFontSize] = useState('1.25rem');
|
||||
const [headlineFontWeight, setHeadlineFontWeight] = useState('900');
|
||||
const [dateFontFamily, setDateFontFamily] = useState('Inter');
|
||||
const [dateFontSize, setDateFontSize] = useState('0.65rem');
|
||||
const [dateFontWeight, setDateFontWeight] = useState('400');
|
||||
const [timeTaskFontFamily, setTimeTaskFontFamily] = useState('Inter');
|
||||
const [timeTaskFontSize, setTimeTaskFontSize] = useState('0.75rem');
|
||||
const [timeTaskFontWeight, setTimeTaskFontWeight] = useState('500');
|
||||
const [bodyFont, setBodyFont] = useState('Inter');
|
||||
const [taskFontFamily, setTaskFontFamily] = useState('Inter');
|
||||
const [taskFontSize, setTaskFontSize] = useState('0.9rem');
|
||||
const [taskFontWeight, setTaskFontWeight] = useState('400');
|
||||
const [eventFontFamily, setEventFontFamily] = useState('Inter');
|
||||
const [eventFontSize, setEventFontSize] = useState('0.85rem');
|
||||
const [eventFontWeight, setEventFontWeight] = useState('400');
|
||||
const [fontWeight, setFontWeight] = useState('400');
|
||||
const [weekendColorSat, setWeekendColorSat] = useState('#666666');
|
||||
const [weekendColorSun, setWeekendColorSun] = useState('#dc2626');
|
||||
|
||||
// Load fonts
|
||||
useGoogleFonts([headlineFont, bodyFont, 'Dancing Script', 'Pacifico']);
|
||||
// Dynamic font loading is handled by the main useGoogleFonts hook call below
|
||||
|
||||
// Load ALL available fonts at the top level to ensure they are available
|
||||
// regardless of whether the settings modal is open or closed, and for
|
||||
// real-time preview usage.
|
||||
useGoogleFonts([
|
||||
...AVAILABLE_FONTS.map(f => f.value),
|
||||
'Dancing Script',
|
||||
'Pacifico'
|
||||
]);
|
||||
|
||||
// Dynamic font loading is handled by useGoogleFonts hook call above
|
||||
|
||||
@ -642,8 +756,24 @@ export default function WeeklyView() {
|
||||
showAllDayEvents: boolean;
|
||||
showSchedule: boolean;
|
||||
headlineFont: string;
|
||||
headlineFontSize: string;
|
||||
headlineFontWeight: string;
|
||||
dateFontFamily: string;
|
||||
dateFontSize: string;
|
||||
dateFontWeight: string;
|
||||
timeTaskFontFamily: string;
|
||||
timeTaskFontSize: string;
|
||||
timeTaskFontWeight: string;
|
||||
bodyFont: string;
|
||||
taskFontFamily: string;
|
||||
taskFontSize: string;
|
||||
taskFontWeight: string;
|
||||
eventFontFamily: string;
|
||||
eventFontSize: string;
|
||||
eventFontWeight: string;
|
||||
fontWeight: string;
|
||||
weekendColorSat: string;
|
||||
weekendColorSun: string;
|
||||
}) => {
|
||||
setShowTimeGrid(newSettings.showTimeGrid);
|
||||
setCellDuration(newSettings.cellDuration);
|
||||
@ -659,8 +789,24 @@ export default function WeeklyView() {
|
||||
setShowAllDay(newSettings.showAllDayEvents);
|
||||
setShowSchedule(newSettings.showSchedule);
|
||||
setHeadlineFont(newSettings.headlineFont);
|
||||
setHeadlineFontSize(newSettings.headlineFontSize);
|
||||
setHeadlineFontWeight(newSettings.headlineFontWeight);
|
||||
setDateFontFamily(newSettings.dateFontFamily);
|
||||
setDateFontSize(newSettings.dateFontSize);
|
||||
setDateFontWeight(newSettings.dateFontWeight);
|
||||
setTimeTaskFontFamily(newSettings.timeTaskFontFamily);
|
||||
setTimeTaskFontSize(newSettings.timeTaskFontSize);
|
||||
setTimeTaskFontWeight(newSettings.timeTaskFontWeight);
|
||||
setBodyFont(newSettings.bodyFont);
|
||||
setTaskFontFamily(newSettings.taskFontFamily);
|
||||
setTaskFontSize(newSettings.taskFontSize);
|
||||
setTaskFontWeight(newSettings.taskFontWeight);
|
||||
setEventFontFamily(newSettings.eventFontFamily);
|
||||
setEventFontSize(newSettings.eventFontSize);
|
||||
setEventFontWeight(newSettings.eventFontWeight);
|
||||
setFontWeight(newSettings.fontWeight);
|
||||
setWeekendColorSat(newSettings.weekendColorSat);
|
||||
setWeekendColorSun(newSettings.weekendColorSun);
|
||||
|
||||
// Custom start/end hours might affect task placement if we filter strictly
|
||||
fetchTasks();
|
||||
@ -686,8 +832,33 @@ export default function WeeklyView() {
|
||||
if (data.user.showAllDayEvents !== undefined) setShowAllDay(data.user.showAllDayEvents);
|
||||
if (data.user.showSchedule !== undefined) setShowSchedule(data.user.showSchedule);
|
||||
if (data.user.headlineFont) setHeadlineFont(data.user.headlineFont);
|
||||
if (data.user.headlineFontSize) setHeadlineFontSize(data.user.headlineFontSize);
|
||||
if (data.user.headlineFontWeight) setHeadlineFontWeight(data.user.headlineFontWeight);
|
||||
if (data.user.dateFontFamily) setDateFontFamily(data.user.dateFontFamily);
|
||||
if (data.user.dateFontSize) setDateFontSize(data.user.dateFontSize);
|
||||
if (data.user.dateFontWeight) setDateFontWeight(data.user.dateFontWeight);
|
||||
if (data.user.timeTaskFontFamily) setTimeTaskFontFamily(data.user.timeTaskFontFamily);
|
||||
if (data.user.timeTaskFontSize) setTimeTaskFontSize(data.user.timeTaskFontSize);
|
||||
if (data.user.timeTaskFontWeight) setTimeTaskFontWeight(data.user.timeTaskFontWeight);
|
||||
if (data.user.bodyFont) setBodyFont(data.user.bodyFont);
|
||||
if (data.user.taskFontFamily) setTaskFontFamily(data.user.taskFontFamily);
|
||||
if (data.user.taskFontSize) setTaskFontSize(data.user.taskFontSize);
|
||||
if (data.user.taskFontSize) setTaskFontSize(data.user.taskFontSize);
|
||||
if (data.user.taskFontWeight) setTaskFontWeight(data.user.taskFontWeight);
|
||||
if (data.user.eventFontFamily) setEventFontFamily(data.user.eventFontFamily);
|
||||
if (data.user.eventFontSize) setEventFontSize(data.user.eventFontSize);
|
||||
if (data.user.eventFontWeight) setEventFontWeight(data.user.eventFontWeight);
|
||||
if (data.user.fontWeight) setFontWeight(data.user.fontWeight);
|
||||
if (data.user.weekendColorSat) setWeekendColorSat(data.user.weekendColorSat);
|
||||
if (data.user.weekendColorSun) setWeekendColorSun(data.user.weekendColorSun);
|
||||
|
||||
setProfile(prev => ({
|
||||
...prev,
|
||||
...data.user,
|
||||
name: data.user.name || prev.name,
|
||||
email: data.user.email || prev.email,
|
||||
}));
|
||||
|
||||
if (data.user.focusTimerDuration) setFocusTimerDuration(data.user.focusTimerDuration);
|
||||
if (data.user.focusBreakDuration) setFocusBreakDuration(data.user.focusBreakDuration);
|
||||
if (data.user.showTimeGrid !== undefined) setShowTimeGrid(data.user.showTimeGrid);
|
||||
@ -1487,9 +1658,25 @@ export default function WeeklyView() {
|
||||
const visibleSlots = getTimeSlots(cellDuration, workingHoursStart, workingHoursEnd);
|
||||
|
||||
const containerStyle = {
|
||||
'--font-headline': `"${headlineFont}", sans-serif`,
|
||||
'--font-body': `"${bodyFont}", sans-serif`,
|
||||
'--font-weight-body': fontWeight || '400',
|
||||
'--weekly-font-headline': (profile.headlineFont || headlineFont) ? `"${profile.headlineFont || headlineFont}", sans-serif` : 'var(--font-headline)',
|
||||
'--weekly-headline-size': profile.headlineFontSize || '1.25rem',
|
||||
'--weekly-headline-weight': profile.headlineFontWeight || '900',
|
||||
'--weekly-date-font': (profile.dateFontFamily) ? `"${profile.dateFontFamily}", sans-serif` : 'var(--weekly-font-headline)',
|
||||
'--weekly-date-size': profile.dateFontSize || '0.65rem',
|
||||
'--weekly-date-weight': profile.dateFontWeight || '400',
|
||||
'--weekly-time-task-font': (profile.timeTaskFontFamily) ? `"${profile.timeTaskFontFamily}", sans-serif` : 'var(--weekly-font)',
|
||||
'--weekly-time-task-size': profile.timeTaskFontSize || '0.75rem',
|
||||
'--weekly-time-task-weight': profile.timeTaskFontWeight || '500',
|
||||
'--weekly-font': 'var(--font-body)', /* Force default body font as requested */
|
||||
'--weekly-task-font': (profile.taskFontFamily) ? `"${profile.taskFontFamily}", sans-serif` : 'var(--weekly-font)',
|
||||
'--weekly-task-size': profile.taskFontSize || '0.9rem',
|
||||
'--weekly-task-weight': profile.taskFontWeight || '400',
|
||||
'--weekly-event-font': (profile.eventFontFamily || eventFontFamily) ? `"${profile.eventFontFamily || eventFontFamily}", sans-serif` : 'var(--weekly-font)',
|
||||
'--weekly-event-size': profile.eventFontSize || eventFontSize || '0.85rem',
|
||||
'--weekly-event-weight': profile.eventFontWeight || eventFontWeight || '400',
|
||||
'--font-weight-body': profile.fontWeight || fontWeight || '400',
|
||||
'--weekly-weekend-sat': profile.weekendColorSat || '#666666',
|
||||
'--weekly-weekend-sun': profile.weekendColorSun || '#dc2626',
|
||||
} as React.CSSProperties;
|
||||
|
||||
if (isLoading) {
|
||||
@ -1773,14 +1960,14 @@ export default function WeeklyView() {
|
||||
{getVisibleDays().map((date, colIndex) => (
|
||||
<div
|
||||
key={date.toISOString()}
|
||||
className="weekly-day-column"
|
||||
className={`weekly-day-column ${date.getDay() === 6 ? 'is-sat' : ''} ${date.getDay() === 0 ? 'is-sun' : ''}`}
|
||||
style={{ viewTransitionName: `day-${date.getFullYear()}-${date.getMonth()}-${date.getDate()}` } as any}
|
||||
>
|
||||
{/* Day Header */}
|
||||
<header className="weekly-day-header">
|
||||
<div style={{ display: 'flex', alignItems: 'baseline', gap: '4px' }}>
|
||||
<h3 className={`weekly-day-name ${isSameDay(date, new Date()) ? 'is-today' : ''}`} style={{ marginBottom: 0 }}>
|
||||
{getDayName(date, language)}, <span style={{ fontWeight: 400 }}>{formatDateHeader(date, language)}</span>
|
||||
{getDayName(date, language)} <span className="weekly-day-date">{formatDateHeader(date, language)}</span>
|
||||
</h3>
|
||||
</div>
|
||||
</header>
|
||||
@ -1825,7 +2012,9 @@ export default function WeeklyView() {
|
||||
|
||||
// Calculate height based on event duration
|
||||
const durationMinutes = (eventEnd.getTime() - eventStart.getTime()) / (1000 * 60);
|
||||
const height = durationMinutes * pixelsPerMinute;
|
||||
const calculatedHeight = durationMinutes * pixelsPerMinute;
|
||||
// Ensure minimum height of 15px for visibility
|
||||
const height = Math.max(calculatedHeight, 15);
|
||||
|
||||
const isUnlocked = unlockedEvents.has(event.id);
|
||||
|
||||
@ -1982,24 +2171,29 @@ export default function WeeklyView() {
|
||||
</span>
|
||||
)}
|
||||
<div className="task-actions" style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(2, 1fr)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '2px',
|
||||
marginLeft: '4px',
|
||||
flexShrink: 0
|
||||
flexShrink: 0,
|
||||
flexWrap: 'nowrap',
|
||||
background: darkMode ? 'rgba(0,0,0,0.6)' : 'rgba(255,255,255,0.8)',
|
||||
padding: '1px 3px',
|
||||
borderRadius: '4px',
|
||||
boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
|
||||
}}>
|
||||
{/* Edit button */}
|
||||
<button className="task-action-btn" onClick={(e) => { e.stopPropagation(); setEditingTaskId(task.id); }} title="Edit">
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
|
||||
</button>
|
||||
{/* Notes button */}
|
||||
<button className="task-action-btn" onClick={(e) => { e.stopPropagation(); setSelectedTaskForNotes(task); }} title="Notes">
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
|
||||
</button>
|
||||
{/* Roll button */}
|
||||
{!task.completed && (
|
||||
<button className={`task-action-btn ${task.isRolling ? 'active' : ''}`} onClick={(e) => { e.stopPropagation(); toggleTaskRolling(task.id); }} title={task.isRolling ? "Disable rolling" : "Enable rolling"}>
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg>
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg>
|
||||
</button>
|
||||
)}
|
||||
{/* Recurrence button */}
|
||||
@ -2008,14 +2202,11 @@ export default function WeeklyView() {
|
||||
onClick={(e) => { e.stopPropagation(); setSelectedTaskForRecurrence(task); }}
|
||||
title={task.isRecurring ? "Edit recurrence" : "Make recurring"}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"></polyline>
|
||||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>
|
||||
</svg>
|
||||
<Repeat size={12} strokeWidth={2.5} />
|
||||
</button>
|
||||
{/* Delete button */}
|
||||
<button className="task-action-btn delete" onClick={(e) => { e.stopPropagation(); deleteTask(task.id); }} title="Delete">
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line></svg>
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -2050,8 +2241,8 @@ export default function WeeklyView() {
|
||||
className="time-slot-event"
|
||||
title={`${event.calendarTitle}: ${event.title}\n${timeStr}`}
|
||||
style={{
|
||||
height: `${eventHeight}px`,
|
||||
minHeight: `${eventHeight}px`,
|
||||
height: `${Math.max(eventHeight, 15)}px`,
|
||||
minHeight: `15px`,
|
||||
position: 'absolute',
|
||||
top: `${topOffset}px`,
|
||||
left: '4px',
|
||||
@ -2135,6 +2326,7 @@ export default function WeeklyView() {
|
||||
onRecurrence={() => setSelectedTaskForRecurrence(task)}
|
||||
onDragStart={(e, t) => handleDragStart(e, t)}
|
||||
onDragEnd={handleDragEnd}
|
||||
variant="minimal"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -2686,11 +2878,21 @@ export default function WeeklyView() {
|
||||
showNextTask={showNextTask}
|
||||
setShowNextTask={setShowNextTask}
|
||||
headlineFont={headlineFont}
|
||||
setHeadlineFont={setHeadlineFont}
|
||||
headlineFontSize={headlineFontSize}
|
||||
headlineFontWeight={headlineFontWeight}
|
||||
dateFontFamily={dateFontFamily}
|
||||
dateFontSize={dateFontSize}
|
||||
dateFontWeight={dateFontWeight}
|
||||
timeTaskFontFamily={timeTaskFontFamily}
|
||||
timeTaskFontSize={timeTaskFontSize}
|
||||
timeTaskFontWeight={timeTaskFontWeight}
|
||||
bodyFont={bodyFont}
|
||||
setBodyFont={setBodyFont}
|
||||
taskFontFamily={taskFontFamily}
|
||||
taskFontSize={taskFontSize}
|
||||
taskFontWeight={taskFontWeight}
|
||||
fontWeight={fontWeight}
|
||||
setFontWeight={setFontWeight}
|
||||
weekendColorSat={weekendColorSat}
|
||||
weekendColorSun={weekendColorSun}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -3222,8 +3424,21 @@ interface SettingsSidebarProps {
|
||||
showAllDayEvents: boolean;
|
||||
showSchedule: boolean;
|
||||
headlineFont: string;
|
||||
headlineFontSize: string;
|
||||
headlineFontWeight: string;
|
||||
dateFontFamily: string;
|
||||
dateFontSize: string;
|
||||
dateFontWeight: string;
|
||||
timeTaskFontFamily: string;
|
||||
timeTaskFontSize: string;
|
||||
timeTaskFontWeight: string;
|
||||
bodyFont: string;
|
||||
taskFontFamily: string;
|
||||
taskFontSize: string;
|
||||
taskFontWeight: string;
|
||||
fontWeight: string;
|
||||
weekendColorSat: string;
|
||||
weekendColorSun: string;
|
||||
}) => void;
|
||||
showTimeGrid: boolean;
|
||||
setShowTimeGrid: (show: boolean) => void;
|
||||
@ -3252,11 +3467,24 @@ interface SettingsSidebarProps {
|
||||
showNextTask: boolean;
|
||||
setShowNextTask: (show: boolean) => void;
|
||||
headlineFont: string;
|
||||
setHeadlineFont: (font: string) => void;
|
||||
headlineFontSize: string;
|
||||
headlineFontWeight: string;
|
||||
dateFontFamily: string;
|
||||
dateFontSize: string;
|
||||
dateFontWeight: string;
|
||||
timeTaskFontFamily: string;
|
||||
timeTaskFontSize: string;
|
||||
timeTaskFontWeight: string;
|
||||
bodyFont: string;
|
||||
setBodyFont: (font: string) => void;
|
||||
taskFontFamily: string;
|
||||
taskFontSize: string;
|
||||
taskFontWeight: string;
|
||||
eventFontFamily?: string;
|
||||
eventFontSize?: string;
|
||||
eventFontWeight?: string;
|
||||
fontWeight: string;
|
||||
setFontWeight: (weight: string) => void;
|
||||
weekendColorSat: string;
|
||||
weekendColorSun: string;
|
||||
}
|
||||
|
||||
function SettingsSidebar({
|
||||
@ -3289,11 +3517,24 @@ function SettingsSidebar({
|
||||
showNextTask,
|
||||
setShowNextTask,
|
||||
headlineFont,
|
||||
setHeadlineFont,
|
||||
headlineFontSize,
|
||||
headlineFontWeight,
|
||||
dateFontFamily,
|
||||
dateFontSize,
|
||||
dateFontWeight,
|
||||
timeTaskFontFamily,
|
||||
timeTaskFontSize,
|
||||
timeTaskFontWeight,
|
||||
bodyFont,
|
||||
setBodyFont,
|
||||
taskFontFamily,
|
||||
taskFontSize,
|
||||
taskFontWeight,
|
||||
eventFontFamily,
|
||||
eventFontSize,
|
||||
eventFontWeight,
|
||||
fontWeight,
|
||||
setFontWeight
|
||||
weekendColorSat,
|
||||
weekendColorSun
|
||||
}: SettingsSidebarProps) {
|
||||
const [activeTab, setActiveTab] = useState<'general' | 'calendar' | 'account'>('general');
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
@ -3325,8 +3566,24 @@ function SettingsSidebar({
|
||||
showAllDayEvents?: boolean;
|
||||
showSchedule?: boolean;
|
||||
headlineFont?: string;
|
||||
headlineFontSize?: string;
|
||||
headlineFontWeight?: string;
|
||||
dateFontFamily?: string;
|
||||
dateFontSize?: string;
|
||||
dateFontWeight?: string;
|
||||
timeTaskFontFamily?: string;
|
||||
timeTaskFontSize?: string;
|
||||
timeTaskFontWeight?: string;
|
||||
bodyFont?: string;
|
||||
taskFontFamily?: string;
|
||||
taskFontSize?: string;
|
||||
taskFontWeight?: string;
|
||||
eventFontFamily?: string;
|
||||
eventFontSize?: string;
|
||||
eventFontWeight?: string;
|
||||
fontWeight?: string;
|
||||
weekendColorSat?: string;
|
||||
weekendColorSun?: string;
|
||||
}>({
|
||||
name: '',
|
||||
email: '',
|
||||
@ -3349,14 +3606,27 @@ function SettingsSidebar({
|
||||
showAllDayEvents: true,
|
||||
showSchedule: true,
|
||||
headlineFont: 'Inter',
|
||||
headlineFontSize: '1.25rem',
|
||||
headlineFontWeight: '900',
|
||||
dateFontFamily: 'Inter',
|
||||
dateFontSize: '0.65rem',
|
||||
dateFontWeight: '400',
|
||||
timeTaskFontFamily: 'Inter',
|
||||
timeTaskFontSize: '0.75rem',
|
||||
timeTaskFontWeight: '500',
|
||||
bodyFont: 'Inter',
|
||||
fontWeight: '400'
|
||||
taskFontFamily: 'Inter',
|
||||
taskFontSize: '0.9rem',
|
||||
taskFontWeight: '400',
|
||||
fontWeight: '400',
|
||||
weekendColorSat: '#666666',
|
||||
weekendColorSun: '#dc2626'
|
||||
});
|
||||
|
||||
const t = translations[profile.language || 'en'] || translations['en'];
|
||||
|
||||
// Load fonts for preview
|
||||
useGoogleFonts([profile.headlineFont || '', profile.bodyFont || '']);
|
||||
// Font loading moved to top level WeeklyView component
|
||||
|
||||
useEffect(() => {
|
||||
fetchProfile();
|
||||
@ -3397,15 +3667,28 @@ function SettingsSidebar({
|
||||
fontWeight: data.user.fontWeight || '400',
|
||||
showSchedule: data.user.showSchedule !== undefined ? data.user.showSchedule : true,
|
||||
focusBreakDuration: data.user.focusBreakDuration || 5,
|
||||
headlineFontSize: data.user.headlineFontSize || '1.25rem',
|
||||
headlineFontWeight: data.user.headlineFontWeight || '900',
|
||||
dateFontFamily: data.user.dateFontFamily || 'Inter',
|
||||
dateFontSize: data.user.dateFontSize || '0.65rem',
|
||||
dateFontWeight: data.user.dateFontWeight || '400',
|
||||
timeTaskFontFamily: data.user.timeTaskFontFamily || 'Inter',
|
||||
timeTaskFontSize: data.user.timeTaskFontSize || '0.75rem',
|
||||
timeTaskFontWeight: data.user.timeTaskFontWeight || '500',
|
||||
taskFontFamily: data.user.taskFontFamily || 'Inter',
|
||||
taskFontSize: data.user.taskFontSize || '0.9rem',
|
||||
taskFontWeight: data.user.taskFontWeight || '400',
|
||||
eventFontFamily: data.user.eventFontFamily || 'Inter',
|
||||
eventFontSize: data.user.eventFontSize || '0.85rem',
|
||||
eventFontWeight: data.user.eventFontWeight || '400',
|
||||
weekendColorSat: data.user.weekendColorSat || '#666666',
|
||||
weekendColorSun: data.user.weekendColorSun || '#dc2626',
|
||||
});
|
||||
|
||||
if (data.user.showTimeGrid !== undefined) setShowTimeGrid(data.user.showTimeGrid);
|
||||
if (data.user.cellDuration) setCellDuration(data.user.cellDuration as CellDuration);
|
||||
if (data.user.viewStyle) setViewStyle(data.user.viewStyle as 'grid' | 'list');
|
||||
if (data.user.fontSize) setFontSize(data.user.fontSize as 'S' | 'M' | 'L');
|
||||
if (data.user.headlineFont) setHeadlineFont(data.user.headlineFont);
|
||||
if (data.user.bodyFont) setBodyFont(data.user.bodyFont);
|
||||
if (data.user.fontWeight) setFontWeight(data.user.fontWeight);
|
||||
if (data.user.focusTimerDuration) setFocusTimerDuration(data.user.focusTimerDuration);
|
||||
if (data.user.showSchedule !== undefined) setShowSchedule(data.user.showSchedule);
|
||||
if (data.user.focusBreakDuration) setFocusBreakDuration(data.user.focusBreakDuration);
|
||||
@ -3489,10 +3772,10 @@ function SettingsSidebar({
|
||||
showSomeday: showSomeday,
|
||||
showAllDayEvents: showAllDay,
|
||||
showSchedule: showSchedule,
|
||||
headlineFont: headlineFont,
|
||||
bodyFont: bodyFont,
|
||||
fontWeight: fontWeight,
|
||||
focusBreakDuration: focusBreakDuration,
|
||||
// The following will be taken from profile if present,
|
||||
// ensuring edited state is saved.
|
||||
focusBreakDuration: profile.focusBreakDuration || focusBreakDuration,
|
||||
focusTimerDuration: profile.focusTimerDuration || focusTimerDuration,
|
||||
password: (passwords.new && passwords.new.trim() !== "") ? passwords.new : undefined
|
||||
})
|
||||
});
|
||||
@ -3519,8 +3802,24 @@ function SettingsSidebar({
|
||||
showAllDayEvents: showAllDay,
|
||||
showSchedule: showSchedule,
|
||||
headlineFont: headlineFont,
|
||||
headlineFontSize: headlineFontSize,
|
||||
headlineFontWeight: headlineFontWeight,
|
||||
dateFontFamily: dateFontFamily,
|
||||
dateFontSize: dateFontSize,
|
||||
dateFontWeight: dateFontWeight,
|
||||
timeTaskFontFamily: timeTaskFontFamily,
|
||||
timeTaskFontSize: timeTaskFontSize,
|
||||
timeTaskFontWeight: timeTaskFontWeight,
|
||||
bodyFont: bodyFont,
|
||||
fontWeight: fontWeight
|
||||
taskFontFamily: taskFontFamily,
|
||||
taskFontSize: taskFontSize,
|
||||
taskFontWeight: profile.taskFontWeight || taskFontWeight,
|
||||
eventFontFamily: profile.eventFontFamily || eventFontFamily,
|
||||
eventFontSize: profile.eventFontSize || eventFontSize,
|
||||
eventFontWeight: profile.eventFontWeight || eventFontWeight,
|
||||
fontWeight: fontWeight,
|
||||
weekendColorSat: weekendColorSat,
|
||||
weekendColorSun: weekendColorSun,
|
||||
});
|
||||
}
|
||||
|
||||
@ -3597,12 +3896,6 @@ function SettingsSidebar({
|
||||
</div>
|
||||
|
||||
<div className="weekly-settings-content" style={{ flex: 1, overflowY: 'auto', padding: '24px' }}>
|
||||
{accountMsg && (
|
||||
<div className={`weekly-auth-message ${accountMsg.toLowerCase().includes('success') ? 'success' : 'error'}`}
|
||||
style={{ marginBottom: '20px', padding: '10px', borderRadius: '4px', textAlign: 'center', fontWeight: 600 }}>
|
||||
{accountMsg}
|
||||
</div>
|
||||
)}
|
||||
{activeTab === 'general' ? (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
|
||||
{/* Motto */}
|
||||
@ -3850,85 +4143,197 @@ function SettingsSidebar({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Font Settings */}
|
||||
<div style={{ marginBottom: '1.5rem' }}>
|
||||
<label style={{ display: 'block', fontSize: '0.9rem', fontWeight: 600, marginBottom: '8px' }}>Typography</label>
|
||||
{/* Typography Settings */}
|
||||
<div style={{ marginBottom: '1.5rem', borderBottom: '1px solid #eee', paddingBottom: '1rem' }}>
|
||||
<label style={{ display: 'block', fontSize: '1rem', fontWeight: 700, marginBottom: '12px', color: '#111827' }}>Font Customization</label>
|
||||
|
||||
{/* Headline Font */}
|
||||
<div style={{ marginBottom: '12px' }}>
|
||||
<label style={{ display: 'block', fontSize: '0.85rem', color: '#666', marginBottom: '4px' }}>Headline Font</label>
|
||||
{/* Day Names */}
|
||||
<div style={{ background: '#f8fafc', padding: '12px', borderRadius: '8px', marginBottom: '12px' }}>
|
||||
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '8px' }}>Weekday Font (e.g. MONTAG)</label>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 1fr', gap: '8px' }}>
|
||||
<select
|
||||
value={profile.headlineFont || 'Inter'}
|
||||
onChange={(e) => setProfile({ ...profile, headlineFont: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', border: '1px solid #ddd', borderRadius: '4px', fontFamily: profile.headlineFont || 'Inter' }}
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
{AVAILABLE_FONTS.map(font => (
|
||||
<option key={font.value} value={font.value} style={{ fontFamily: font.value }}>{font.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Body Font */}
|
||||
<div style={{ marginBottom: '12px' }}>
|
||||
<label style={{ display: 'block', fontSize: '0.85rem', color: '#666', marginBottom: '4px' }}>Body Font</label>
|
||||
<select
|
||||
value={profile.bodyFont || 'Inter'}
|
||||
onChange={(e) => setProfile({ ...profile, bodyFont: e.target.value })}
|
||||
value={profile.headlineFontSize || '1.25rem'}
|
||||
onChange={(e) => setProfile({ ...profile, headlineFontSize: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', border: '1px solid #ddd', borderRadius: '4px', fontFamily: profile.bodyFont || 'Inter' }}
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
<option value="1rem">Small 16px</option>
|
||||
<option value="1.25rem">Normal 20px</option>
|
||||
<option value="1.5rem">Large 24px</option>
|
||||
<option value="1.75rem">XL 28px</option>
|
||||
<option value="2rem">Huge 32px</option>
|
||||
</select>
|
||||
<select
|
||||
value={profile.headlineFontWeight || '900'}
|
||||
onChange={(e) => setProfile({ ...profile, headlineFontWeight: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
<option value="300">Light</option>
|
||||
<option value="400">Normal</option>
|
||||
<option value="500">Medium</option>
|
||||
<option value="600">Semi</option>
|
||||
<option value="700">Bold</option>
|
||||
<option value="900">Black</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Dates */}
|
||||
<div style={{ background: '#f8fafc', padding: '12px', borderRadius: '8px', marginBottom: '12px' }}>
|
||||
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '8px' }}>Date Font (e.g. 12. Feb.)</label>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 1fr', gap: '8px' }}>
|
||||
<select
|
||||
value={profile.dateFontFamily || 'Inter'}
|
||||
onChange={(e) => setProfile({ ...profile, dateFontFamily: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
{AVAILABLE_FONTS.map(font => (
|
||||
<option key={font.value} value={font.value} style={{ fontFamily: font.value }}>{font.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
value={profile.dateFontSize || '0.65rem'}
|
||||
onChange={(e) => setProfile({ ...profile, dateFontSize: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
<option value="0.55rem">XS 9px</option>
|
||||
<option value="0.65rem">Normal 10px</option>
|
||||
<option value="0.75rem">Small 12px</option>
|
||||
<option value="0.85rem">Medium 14px</option>
|
||||
<option value="1rem">Large 16px</option>
|
||||
</select>
|
||||
<select
|
||||
value={profile.dateFontWeight || '400'}
|
||||
onChange={(e) => setProfile({ ...profile, dateFontWeight: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
<option value="300">Light</option>
|
||||
<option value="400">Normal</option>
|
||||
<option value="500">Medium</option>
|
||||
<option value="600">Semi</option>
|
||||
<option value="700">Bold</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Font Weight */}
|
||||
<div>
|
||||
<label style={{ display: 'block', fontSize: '0.85rem', color: '#666', marginBottom: '4px' }}>Body Font Weight</label>
|
||||
<div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap' }}>
|
||||
{FONT_WEIGHTS.map(weight => (
|
||||
<div key={weight.value} style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={profile.fontWeight === weight.value}
|
||||
onChange={() => setProfile({ ...profile, fontWeight: weight.value })}
|
||||
style={{ width: '16px', height: '16px', cursor: 'pointer' }}
|
||||
/>
|
||||
<label onClick={() => setProfile({ ...profile, fontWeight: weight.value })} style={{ cursor: 'pointer', fontSize: '0.9rem' }}>
|
||||
{weight.name}
|
||||
</label>
|
||||
</div>
|
||||
{/* Tasks */}
|
||||
<div style={{ background: '#f8fafc', padding: '12px', borderRadius: '8px', marginBottom: '12px' }}>
|
||||
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '8px' }}>Task Font</label>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 1fr', gap: '8px' }}>
|
||||
<select
|
||||
value={profile.taskFontFamily || 'Inter'}
|
||||
onChange={(e) => setProfile({ ...profile, taskFontFamily: e.target.value, timeTaskFontFamily: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
{AVAILABLE_FONTS.map(font => (
|
||||
<option key={font.value} value={font.value} style={{ fontFamily: font.value }}>{font.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
value={profile.taskFontSize || '0.9rem'}
|
||||
onChange={(e) => setProfile({ ...profile, taskFontSize: e.target.value, timeTaskFontSize: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
<option value="0.75rem">Small 12px</option>
|
||||
<option value="0.9rem">Normal 14px</option>
|
||||
<option value="1rem">Medium 16px</option>
|
||||
<option value="1.1rem">Large 18px</option>
|
||||
<option value="1.25rem">XL 20px</option>
|
||||
</select>
|
||||
<select
|
||||
value={profile.taskFontWeight || '400'}
|
||||
onChange={(e) => setProfile({ ...profile, taskFontWeight: e.target.value, timeTaskFontWeight: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
<option value="300">Light</option>
|
||||
<option value="400">Normal</option>
|
||||
<option value="500">Medium</option>
|
||||
<option value="600">Semi</option>
|
||||
<option value="700">Bold</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Font Preview */}
|
||||
<div style={{
|
||||
marginTop: '1rem',
|
||||
padding: '1rem',
|
||||
border: '1px solid #e5e7eb',
|
||||
borderRadius: '8px',
|
||||
background: '#f9fafb'
|
||||
}}>
|
||||
<div style={{
|
||||
fontFamily: profile.headlineFont || 'Inter',
|
||||
fontSize: '1.25rem',
|
||||
fontWeight: 700,
|
||||
marginBottom: '0.5rem',
|
||||
color: '#111827'
|
||||
}}>
|
||||
Headline Preview
|
||||
{/* Calendar Event Font */}
|
||||
<div style={{ background: '#f8fafc', padding: '12px', borderRadius: '8px', marginBottom: '12px' }}>
|
||||
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '8px' }}>Calendar Event Font</label>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 1fr', gap: '8px' }}>
|
||||
<select
|
||||
value={profile.eventFontFamily || 'Inter'}
|
||||
onChange={(e) => setProfile({ ...profile, eventFontFamily: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
{AVAILABLE_FONTS.map(font => (
|
||||
<option key={font.value} value={font.value} style={{ fontFamily: font.value }}>{font.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
value={profile.eventFontSize || '0.85rem'}
|
||||
onChange={(e) => setProfile({ ...profile, eventFontSize: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
<option value="0.75rem">Small 12px</option>
|
||||
<option value="0.85rem">Normal 14px</option>
|
||||
<option value="1rem">Medium 16px</option>
|
||||
<option value="1.1rem">Large 18px</option>
|
||||
</select>
|
||||
<select
|
||||
value={profile.eventFontWeight || '400'}
|
||||
onChange={(e) => setProfile({ ...profile, eventFontWeight: e.target.value })}
|
||||
className="weekly-input"
|
||||
style={{ width: '100%', padding: '8px', fontSize: '0.9rem' }}
|
||||
>
|
||||
<option value="300">Light</option>
|
||||
<option value="400">Normal</option>
|
||||
<option value="500">Medium</option>
|
||||
<option value="600">Semi</option>
|
||||
<option value="700">Bold</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Weekend Colors */}
|
||||
<div style={{ background: '#f8fafc', padding: '12px', borderRadius: '8px', marginBottom: '12px' }}>
|
||||
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '8px' }}>Weekend Highlight Colors</label>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px' }}>
|
||||
<div>
|
||||
<label style={{ display: 'block', fontSize: '0.75rem', color: '#666', marginBottom: '4px' }}>Saturday</label>
|
||||
<input
|
||||
type="color"
|
||||
value={profile.weekendColorSat || '#666666'}
|
||||
onChange={(e) => setProfile({ ...profile, weekendColorSat: e.target.value })}
|
||||
style={{ width: '100%', height: '30px', cursor: 'pointer', border: 'none', background: 'transparent' }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ display: 'block', fontSize: '0.75rem', color: '#666', marginBottom: '4px' }}>Sunday</label>
|
||||
<input
|
||||
type="color"
|
||||
value={profile.weekendColorSun || '#dc2626'}
|
||||
onChange={(e) => setProfile({ ...profile, weekendColorSun: e.target.value })}
|
||||
style={{ width: '100%', height: '30px', cursor: 'pointer', border: 'none', background: 'transparent' }}
|
||||
/>
|
||||
</div>
|
||||
<div style={{
|
||||
fontFamily: profile.bodyFont || 'Inter',
|
||||
fontWeight: parseInt(profile.fontWeight || '400'),
|
||||
fontSize: '0.95rem',
|
||||
color: '#374151',
|
||||
lineHeight: 1.5
|
||||
}}>
|
||||
Body text preview. The quick brown fox jumps over the lazy dog. 1234567890.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -3971,13 +4376,24 @@ function SettingsSidebar({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||
<button
|
||||
onClick={handleUpdateProfile}
|
||||
className="weekly-btn-primary"
|
||||
style={{ marginTop: '8px', padding: '10px', alignSelf: 'flex-start' }}
|
||||
style={{ padding: '10px 20px' }}
|
||||
>
|
||||
{t.saveChanges}
|
||||
</button>
|
||||
{accountMsg && (
|
||||
<span style={{
|
||||
fontSize: '0.9rem',
|
||||
color: accountMsg.toLowerCase().includes('success') ? '#059669' : '#dc2626',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
{accountMsg}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : activeTab === 'calendar' ? (
|
||||
isLoading ? (
|
||||
@ -4112,13 +4528,24 @@ function SettingsSidebar({
|
||||
</div>
|
||||
|
||||
|
||||
<div style={{ marginTop: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||
<button
|
||||
type="submit"
|
||||
className="weekly-auth-button primary"
|
||||
style={{ marginTop: '8px' }}
|
||||
className="weekly-btn-primary"
|
||||
style={{ padding: '10px 20px' }}
|
||||
>
|
||||
{t.saveChanges}
|
||||
</button>
|
||||
{accountMsg && (
|
||||
<span style={{
|
||||
fontSize: '0.9rem',
|
||||
color: accountMsg.toLowerCase().includes('success') ? '#059669' : '#dc2626',
|
||||
fontWeight: 600
|
||||
}}>
|
||||
{accountMsg}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* Data Export Section */}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user