fix: match webapp style — solid task blocks, fix all-day events, tab-sorted someday
- Fix all-day events: remove externalProvider distinction — ALL tasks with scheduledDate but no startTime belong in the GANZ-TAG strip regardless of source; was incorrectly routing them to a separate noTime strip that didn't show external-provider=null events - Remove noTime calendar strip: it was a wrong concept; tasks outside the time window are simply not shown; someday page handles unscheduled tasks - Solid task blocks: revert from light+border back to solid fill (matching the webapp); use contrastColor() helper to pick white or dark text automatically based on background luminance - All-day strip also uses solid blocks (same style as webapp's ALL DAY bars) - Someday page: group lists by their tab with a tab header row; query now sorts by [tab, order]; lists without a tab get a General/Allgemein header - Show up to 5 all-day tasks per column (was 4) v1.95.4
This commit is contained in:
parent
3fd738b185
commit
da31128121
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "my-weekly-todo-list",
|
"name": "my-weekly-todo-list",
|
||||||
"version": "1.95.3",
|
"version": "1.95.4",
|
||||||
"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": {
|
||||||
|
|||||||
@ -167,16 +167,24 @@ type TaskItem = {
|
|||||||
startTime: string | null;
|
startTime: string | null;
|
||||||
endTime: string | null;
|
endTime: string | null;
|
||||||
completed: boolean;
|
completed: boolean;
|
||||||
externalProvider: string | null;
|
|
||||||
project: { name: string; color: string | null } | null;
|
project: { name: string; color: string | null } | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DayData = {
|
type DayData = {
|
||||||
allDay: TaskItem[]; // external all-day calendar events (showAllDay)
|
allDay: TaskItem[]; // all tasks without a startTime (shown in GANZ-TAG strip)
|
||||||
noTime: TaskItem[]; // user tasks with no time (showNoTime)
|
timed: TaskItem[]; // tasks with startTime within [startHour, endHour)
|
||||||
timed: TaskItem[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Compute a contrasting text color (white or near-black) for a given bg hex
|
||||||
|
function contrastColor(hex: string): string {
|
||||||
|
const h = hex.replace('#', '');
|
||||||
|
const r = parseInt(h.slice(0, 2), 16) / 255;
|
||||||
|
const g = parseInt(h.slice(2, 4), 16) / 255;
|
||||||
|
const b = parseInt(h.slice(4, 6), 16) / 255;
|
||||||
|
const lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||||
|
return lum > 0.42 ? '#1e293b' : '#ffffff';
|
||||||
|
}
|
||||||
|
|
||||||
type WeatherDay = { maxTemp: number | null; code: number | null; };
|
type WeatherDay = { maxTemp: number | null; code: number | null; };
|
||||||
|
|
||||||
// User-specific style settings passed to the PDF
|
// User-specific style settings passed to the PDF
|
||||||
@ -203,6 +211,7 @@ function dayColor(d: Date, isToday: boolean, us: UserStyle): string {
|
|||||||
type SomedayList = {
|
type SomedayList = {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
tab: string | null;
|
||||||
tasks: { id: string; title: string; completed: boolean; project: { name: string; color: string | null } | null }[];
|
tasks: { id: string; title: string; completed: boolean; project: { name: string; color: string | null } | null }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -244,10 +253,8 @@ function WeekCalendarPDF({
|
|||||||
? (parseInt(userStyle.headlineFontWeight) >= 600 ? 700 : 400)
|
? (parseInt(userStyle.headlineFontWeight) >= 600 ? 700 : 400)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
// GRID_H = remaining space after headers and optional strips
|
// GRID_H = remaining space after day header and optional all-day strip
|
||||||
const GRID_H = CONTENT_H - DAY_HDR_H
|
const GRID_H = CONTENT_H - DAY_HDR_H - (showAllDay ? ALL_DAY_H : 0);
|
||||||
- (showAllDay ? ALL_DAY_H : 0)
|
|
||||||
- (showNoTime ? NO_TIME_H : 0);
|
|
||||||
|
|
||||||
function minutesToY(mins: number): number {
|
function minutesToY(mins: number): number {
|
||||||
return Math.max(0, (mins / totalMins) * GRID_H);
|
return Math.max(0, (mins / totalMins) * GRID_H);
|
||||||
@ -267,7 +274,7 @@ function WeekCalendarPDF({
|
|||||||
// Adaptive font size — full names always, size scales with available column width
|
// Adaptive font size — full names always, size scales with available column width
|
||||||
const dayNameSize = numDays <= 2 ? 16 : numDays <= 3 ? 13 : numDays <= 5 ? 10 : 8.5;
|
const dayNameSize = numDays <= 2 ? 16 : numDays <= 3 ? 13 : numDays <= 5 ? 10 : 8.5;
|
||||||
|
|
||||||
const calendarH = DAY_HDR_H + (showAllDay ? ALL_DAY_H : 0) + (showNoTime ? NO_TIME_H : 0) + GRID_H;
|
const calendarH = DAY_HDR_H + (showAllDay ? ALL_DAY_H : 0) + GRID_H;
|
||||||
|
|
||||||
const firstDay = days[0];
|
const firstDay = days[0];
|
||||||
const lastDay = days[days.length - 1];
|
const lastDay = days[days.length - 1];
|
||||||
@ -358,32 +365,6 @@ function WeekCalendarPDF({
|
|||||||
),
|
),
|
||||||
] : []),
|
] : []),
|
||||||
|
|
||||||
// Anyday label spacer
|
|
||||||
...(showNoTime ? [
|
|
||||||
React.createElement(View, {
|
|
||||||
style: {
|
|
||||||
height: NO_TIME_H,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: BORDER,
|
|
||||||
backgroundColor: NOTIME_BG,
|
|
||||||
alignItems: 'flex-end',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
paddingRight: 4,
|
|
||||||
paddingTop: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
React.createElement(Text, {
|
|
||||||
style: {
|
|
||||||
fontSize: 5.5,
|
|
||||||
fontFamily: 'Helvetica-Bold',
|
|
||||||
color: MUTED,
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
letterSpacing: 0.3,
|
|
||||||
},
|
|
||||||
}, de ? 'Jederzeit' : 'Any Day'),
|
|
||||||
),
|
|
||||||
] : []),
|
|
||||||
|
|
||||||
// Hour labels (absolute within container)
|
// Hour labels (absolute within container)
|
||||||
React.createElement(View, { style: { height: GRID_H, position: 'relative' } },
|
React.createElement(View, { style: { height: GRID_H, position: 'relative' } },
|
||||||
...Array.from({ length: numHours + 1 }, (_, i) => {
|
...Array.from({ length: numHours + 1 }, (_, i) => {
|
||||||
@ -416,7 +397,6 @@ function WeekCalendarPDF({
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
const allDayTasks = dayData?.allDay || [];
|
const allDayTasks = dayData?.allDay || [];
|
||||||
const noTimeTasks = dayData?.noTime || [];
|
|
||||||
const timedTasks = dayData?.timed || [];
|
const timedTasks = dayData?.timed || [];
|
||||||
|
|
||||||
const dayName = weekdayLabel(d, de);
|
const dayName = weekdayLabel(d, de);
|
||||||
@ -484,8 +464,9 @@ function WeekCalendarPDF({
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...allDayTasks.slice(0, 4).map(t => {
|
...allDayTasks.slice(0, 5).map(t => {
|
||||||
const c = t.completed ? DONE_BG : (t.project?.color || '#3b82f6');
|
const bg = t.completed ? DONE_BG : (t.project?.color || userStyle.taskColor);
|
||||||
|
const fg = t.completed ? DONE_TEXT : contrastColor(bg);
|
||||||
return React.createElement(View, {
|
return React.createElement(View, {
|
||||||
key: t.id,
|
key: t.id,
|
||||||
style: {
|
style: {
|
||||||
@ -493,69 +474,25 @@ function WeekCalendarPDF({
|
|||||||
paddingVertical: 1.5,
|
paddingVertical: 1.5,
|
||||||
paddingHorizontal: 4,
|
paddingHorizontal: 4,
|
||||||
marginBottom: 2,
|
marginBottom: 2,
|
||||||
backgroundColor: t.completed ? DONE_BG : lighten(c, 0.7),
|
backgroundColor: bg,
|
||||||
borderLeftWidth: 3,
|
|
||||||
borderLeftColor: c,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
React.createElement(Text, {
|
React.createElement(Text, {
|
||||||
style: {
|
style: {
|
||||||
fontFamily: 'Helvetica-Bold',
|
fontFamily: 'Helvetica-Bold',
|
||||||
fontSize: 6.5,
|
fontSize: 6.5,
|
||||||
color: t.completed ? DONE_TEXT : '#1e293b',
|
color: fg,
|
||||||
},
|
},
|
||||||
}, (t.title || '').slice(0, 36)),
|
}, (t.title || '').slice(0, 36)),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
allDayTasks.length > 4 ? React.createElement(Text, {
|
allDayTasks.length > 5 ? React.createElement(Text, {
|
||||||
style: { fontSize: 6, color: '#3b82f6', marginTop: 1 },
|
style: { fontSize: 5.5, color: '#3b82f6', marginTop: 1 },
|
||||||
}, `+${allDayTasks.length - 4} ${de ? 'weitere' : 'more'}`) : null,
|
}, `+${allDayTasks.length - 5} ${de ? 'weitere' : 'more'}`) : null,
|
||||||
),
|
),
|
||||||
] : []),
|
] : []),
|
||||||
|
|
||||||
// ── No-time cell (anyday tasks)
|
// ── Time grid (solid colored blocks, matching webapp style)
|
||||||
...(showNoTime ? [
|
|
||||||
React.createElement(View, {
|
|
||||||
style: {
|
|
||||||
height: NO_TIME_H,
|
|
||||||
backgroundColor: NOTIME_BG,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: BORDER,
|
|
||||||
paddingHorizontal: 3,
|
|
||||||
paddingTop: 3,
|
|
||||||
overflow: 'hidden',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...noTimeTasks.slice(0, 4).map(t => {
|
|
||||||
const c = t.completed ? DONE_BG : (t.project?.color || userStyle.taskColor);
|
|
||||||
return React.createElement(View, {
|
|
||||||
key: t.id,
|
|
||||||
style: {
|
|
||||||
borderRadius: 2,
|
|
||||||
paddingVertical: 1.5,
|
|
||||||
paddingHorizontal: 4,
|
|
||||||
marginBottom: 2,
|
|
||||||
backgroundColor: t.completed ? DONE_BG : lighten(c, 0.75),
|
|
||||||
borderLeftWidth: 3,
|
|
||||||
borderLeftColor: c,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
React.createElement(Text, {
|
|
||||||
style: {
|
|
||||||
fontFamily: 'Helvetica-Bold',
|
|
||||||
fontSize: 6.5,
|
|
||||||
color: t.completed ? DONE_TEXT : '#1e293b',
|
|
||||||
},
|
|
||||||
}, (t.title || '').slice(0, 36)),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
noTimeTasks.length > 4 ? React.createElement(Text, {
|
|
||||||
style: { fontSize: 6, color: MUTED, marginTop: 1 },
|
|
||||||
}, `+${noTimeTasks.length - 4} ${de ? 'weitere' : 'more'}`) : null,
|
|
||||||
),
|
|
||||||
] : []),
|
|
||||||
|
|
||||||
// ── Time grid
|
|
||||||
React.createElement(View, {
|
React.createElement(View, {
|
||||||
style: {
|
style: {
|
||||||
height: GRID_H,
|
height: GRID_H,
|
||||||
@ -593,7 +530,7 @@ function WeekCalendarPDF({
|
|||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
|
||||||
// Half-hour tick lines
|
// Half-hour dashes
|
||||||
...Array.from({ length: numHours }, (_, i) =>
|
...Array.from({ length: numHours }, (_, i) =>
|
||||||
React.createElement(View, {
|
React.createElement(View, {
|
||||||
key: `hh${i}`,
|
key: `hh${i}`,
|
||||||
@ -607,8 +544,7 @@ function WeekCalendarPDF({
|
|||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
|
||||||
// Task blocks (absolute, spanning actual duration)
|
// Task blocks — solid fill matching webapp style
|
||||||
// Style: light tinted background + colored left border (print-friendly)
|
|
||||||
...timedTasks.map(t => {
|
...timedTasks.map(t => {
|
||||||
const startMins = parseTimeMinutes(t.startTime)!;
|
const startMins = parseTimeMinutes(t.startTime)!;
|
||||||
const endMins = t.endTime
|
const endMins = t.endTime
|
||||||
@ -625,14 +561,10 @@ function WeekCalendarPDF({
|
|||||||
const rawH = minutesToY(ceMin - csMin);
|
const rawH = minutesToY(ceMin - csMin);
|
||||||
const taskH = Math.max(rawH, 14);
|
const taskH = Math.max(rawH, 14);
|
||||||
|
|
||||||
const accentColor = t.completed
|
const bgColor = t.completed ? DONE_BG : (t.project?.color || userStyle.taskColor);
|
||||||
? DONE_BG
|
const textColor = t.completed ? DONE_TEXT : contrastColor(bgColor);
|
||||||
: (t.project?.color || userStyle.taskColor);
|
|
||||||
const bgColor = t.completed ? DONE_BG : lighten(accentColor, 0.78);
|
|
||||||
const textColor = t.completed ? DONE_TEXT : '#1e293b';
|
|
||||||
const subColor = t.completed ? DONE_TEXT : accentColor;
|
|
||||||
const hasMinutes = t.startTime && !t.startTime.endsWith(':00');
|
|
||||||
const isShort = (ceMin - csMin) <= 30;
|
const isShort = (ceMin - csMin) <= 30;
|
||||||
|
const showTime = t.startTime && (!t.startTime.endsWith(':00') || isShort);
|
||||||
|
|
||||||
return React.createElement(View, {
|
return React.createElement(View, {
|
||||||
key: t.id,
|
key: t.id,
|
||||||
@ -644,10 +576,7 @@ function WeekCalendarPDF({
|
|||||||
height: taskH - 2,
|
height: taskH - 2,
|
||||||
backgroundColor: bgColor,
|
backgroundColor: bgColor,
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
borderLeftWidth: 3,
|
paddingHorizontal: 4,
|
||||||
borderLeftColor: accentColor,
|
|
||||||
paddingLeft: 4,
|
|
||||||
paddingRight: 3,
|
|
||||||
paddingVertical: 2,
|
paddingVertical: 2,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
@ -660,10 +589,10 @@ function WeekCalendarPDF({
|
|||||||
lineHeight: 1.25,
|
lineHeight: 1.25,
|
||||||
},
|
},
|
||||||
}, (t.title || '').slice(0, 44)),
|
}, (t.title || '').slice(0, 44)),
|
||||||
(hasMinutes || isShort) && t.startTime
|
showTime
|
||||||
? React.createElement(Text, {
|
? React.createElement(Text, {
|
||||||
style: { fontSize: 6, color: subColor, marginTop: 0.5 },
|
style: { fontSize: 6, color: textColor, opacity: 0.8, marginTop: 0.5 },
|
||||||
}, fmtTime(t.startTime))
|
}, fmtTime(t.startTime!))
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
}).filter(Boolean),
|
}).filter(Boolean),
|
||||||
@ -743,77 +672,72 @@ function WeekCalendarPDF({
|
|||||||
}, userName),
|
}, userName),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Lists in columns (flex wrap)
|
// Group lists by tab, then render tab sections
|
||||||
React.createElement(View, {
|
...((() => {
|
||||||
style: {
|
// Build ordered tab groups
|
||||||
flexDirection: 'row',
|
const tabOrder: string[] = [];
|
||||||
flexWrap: 'wrap',
|
const tabMap = new Map<string, SomedayList[]>();
|
||||||
gap: 12,
|
for (const list of somedayLists) {
|
||||||
},
|
const tab = list.tab || (de ? 'Allgemein' : 'General');
|
||||||
},
|
if (!tabMap.has(tab)) { tabMap.set(tab, []); tabOrder.push(tab); }
|
||||||
...somedayLists.map(list =>
|
tabMap.get(tab)!.push(list);
|
||||||
React.createElement(View, {
|
}
|
||||||
|
|
||||||
|
const renderList = (list: SomedayList) => React.createElement(View, {
|
||||||
key: list.id,
|
key: list.id,
|
||||||
style: {
|
style: { width: '31%', marginBottom: 10 },
|
||||||
width: '30%',
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
// List name
|
|
||||||
React.createElement(View, {
|
React.createElement(View, {
|
||||||
style: {
|
style: {
|
||||||
borderLeftWidth: 3,
|
borderLeftWidth: 3,
|
||||||
borderLeftColor: list.tasks[0]?.project?.color || userStyle.weekdayColor,
|
borderLeftColor: list.tasks[0]?.project?.color || userStyle.weekdayColor,
|
||||||
paddingLeft: 6,
|
paddingLeft: 5, marginBottom: 4,
|
||||||
marginBottom: 5,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
React.createElement(Text, {
|
React.createElement(Text, {
|
||||||
style: {
|
style: { fontFamily: 'Helvetica-Bold', fontSize: 7.5, color: '#374151', textTransform: 'uppercase', letterSpacing: 0.3 },
|
||||||
fontFamily: 'Helvetica-Bold',
|
|
||||||
fontSize: 8,
|
|
||||||
color: '#374151',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
letterSpacing: 0.4,
|
|
||||||
},
|
|
||||||
}, list.title),
|
}, list.title),
|
||||||
),
|
),
|
||||||
|
...list.tasks.slice(0, 18).map(t =>
|
||||||
// Task rows
|
|
||||||
...list.tasks.slice(0, 20).map(t =>
|
|
||||||
React.createElement(View, {
|
React.createElement(View, {
|
||||||
key: t.id,
|
key: t.id,
|
||||||
style: {
|
style: { flexDirection: 'row', alignItems: 'flex-start', paddingVertical: 2, paddingHorizontal: 3, marginBottom: 1, borderRadius: 2, backgroundColor: '#f8fafc' },
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
paddingVertical: 2.5,
|
|
||||||
paddingHorizontal: 4,
|
|
||||||
marginBottom: 1,
|
|
||||||
borderRadius: 2,
|
|
||||||
backgroundColor: '#f8fafc',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
React.createElement(View, {
|
React.createElement(View, {
|
||||||
style: {
|
style: { width: 5, height: 5, borderRadius: 3, borderWidth: 1, borderColor: t.project?.color || MUTED, marginRight: 4, marginTop: 1.5, flexShrink: 0 },
|
||||||
width: 6, height: 6, borderRadius: 3,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: t.project?.color || MUTED,
|
|
||||||
marginRight: 5,
|
|
||||||
marginTop: 0.5,
|
|
||||||
flexShrink: 0,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
React.createElement(Text, {
|
React.createElement(Text, {
|
||||||
style: { fontSize: 7.5, color: '#374151', lineHeight: 1.3, flex: 1 },
|
style: { fontSize: 7, color: '#374151', lineHeight: 1.3, flex: 1 },
|
||||||
}, (t.title || '').slice(0, 55)),
|
}, (t.title || '').slice(0, 52)),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
list.tasks.length > 20 ? React.createElement(Text, {
|
list.tasks.length > 18 ? React.createElement(Text, {
|
||||||
style: { fontSize: 6.5, color: MUTED, marginTop: 2, paddingLeft: 4 },
|
style: { fontSize: 6, color: MUTED, marginTop: 1, paddingLeft: 3 },
|
||||||
}, `+${list.tasks.length - 20} ${de ? 'weitere' : 'more'}`) : null,
|
}, `+${list.tasks.length - 18} ${de ? 'weitere' : 'more'}`) : null,
|
||||||
|
);
|
||||||
|
|
||||||
|
return tabOrder.map(tab =>
|
||||||
|
React.createElement(View, { key: tab },
|
||||||
|
// Tab header
|
||||||
|
React.createElement(View, {
|
||||||
|
style: {
|
||||||
|
borderBottomWidth: 1, borderBottomColor: BORDER,
|
||||||
|
marginBottom: 8, marginTop: 4, paddingBottom: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
React.createElement(Text, {
|
||||||
|
style: { fontFamily: 'Helvetica-Bold', fontSize: 9, color: userStyle.weekdayColor, textTransform: 'uppercase', letterSpacing: 0.5 },
|
||||||
|
}, tab),
|
||||||
|
),
|
||||||
|
// Lists in row
|
||||||
|
React.createElement(View, {
|
||||||
|
style: { flexDirection: 'row', flexWrap: 'wrap', gap: 10, marginBottom: 6 },
|
||||||
|
},
|
||||||
|
...tabMap.get(tab)!.map(renderList),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
),
|
);
|
||||||
),
|
})()),
|
||||||
|
|
||||||
// Footer
|
// Footer
|
||||||
React.createElement(View, {
|
React.createElement(View, {
|
||||||
@ -919,29 +843,23 @@ export async function GET(request: Request) {
|
|||||||
const dk = localDateStr(task.scheduledDate, userTz);
|
const dk = localDateStr(task.scheduledDate, userTz);
|
||||||
if (dk < startDate || dk > endDate) continue;
|
if (dk < startDate || dk > endDate) continue;
|
||||||
|
|
||||||
if (!tasksByDay.has(dk)) tasksByDay.set(dk, { allDay: [], noTime: [], timed: [] });
|
if (!tasksByDay.has(dk)) tasksByDay.set(dk, { allDay: [], timed: [] });
|
||||||
const d = tasksByDay.get(dk)!;
|
const d = tasksByDay.get(dk)!;
|
||||||
const item: TaskItem = {
|
const item: TaskItem = {
|
||||||
id: task.id, title: task.title, startTime: task.startTime,
|
id: task.id, title: task.title, startTime: task.startTime,
|
||||||
endTime: task.endTime ?? null, completed: task.completed,
|
endTime: task.endTime ?? null, completed: task.completed,
|
||||||
externalProvider: (task as any).externalProvider ?? null,
|
|
||||||
project: task.project,
|
project: task.project,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!task.startTime) {
|
if (!task.startTime) {
|
||||||
// External calendar all-day events → allDay strip; user tasks → noTime strip
|
// All date-scheduled tasks with no time → all-day strip
|
||||||
if (item.externalProvider) {
|
|
||||||
if (showAllDay) d.allDay.push(item);
|
if (showAllDay) d.allDay.push(item);
|
||||||
} else {
|
|
||||||
if (showNoTime) d.noTime.push(item);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const h = parseInt(task.startTime.split(':')[0], 10);
|
const h = parseInt(task.startTime.split(':')[0], 10);
|
||||||
if (h >= startHour && h < endHour) {
|
if (h >= startHour && h < endHour) {
|
||||||
d.timed.push(item);
|
d.timed.push(item);
|
||||||
} else if (showNoTime) {
|
|
||||||
d.noTime.push(item); // tasks outside the time range → anyday strip
|
|
||||||
}
|
}
|
||||||
|
// Tasks outside the time window are simply not shown in the grid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -965,16 +883,18 @@ export async function GET(request: Request) {
|
|||||||
type SomedayListWithTasks = {
|
type SomedayListWithTasks = {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
tab: string | null;
|
||||||
tasks: { id: string; title: string; completed: boolean; project: { name: string; color: string | null } | null }[];
|
tasks: { id: string; title: string; completed: boolean; project: { name: string; color: string | null } | null }[];
|
||||||
};
|
};
|
||||||
let somedayLists: SomedayListWithTasks[] = [];
|
let somedayLists: SomedayListWithTasks[] = [];
|
||||||
if (showNoTime) {
|
if (showNoTime) {
|
||||||
const raw = await prisma.somedayList.findMany({
|
const raw = await prisma.somedayList.findMany({
|
||||||
where: { userId: user.id },
|
where: { userId: user.id },
|
||||||
orderBy: { order: 'asc' },
|
orderBy: [{ tab: 'asc' }, { order: 'asc' }],
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
title: true,
|
title: true,
|
||||||
|
tab: true,
|
||||||
tasks: {
|
tasks: {
|
||||||
where: { deletedAt: null, completed: false },
|
where: { deletedAt: null, completed: false },
|
||||||
orderBy: { order: 'asc' },
|
orderBy: { order: 'asc' },
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user