Fix: Show Projects with due dates in Upcoming view (#928)

* Add projects with due dates to upcoming view

Fetch and include projects with due_date_at in the upcoming range
when type=upcoming is requested. Projects are filtered by ownership
or permissions and exclude completed/archived projects.

Part of #925

* Display upcoming projects in Upcoming view

Add UI to show projects with due dates in the Upcoming view.
Projects are displayed in a separate section below tasks with
links to project details and formatted due dates.

Fixes #925

* Fix prettier formatting errors
This commit is contained in:
Chris 2026-03-09 23:36:14 +02:00 committed by GitHub
parent 358f577576
commit 8fea7020bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 98 additions and 16 deletions

View file

@ -44,6 +44,7 @@ const Tasks: React.FC = () => {
const [tasks, setTasks] = useState<Task[]>([]);
const projects = useStore((state: any) => state.projectsStore.projects);
const [groupedTasks, setGroupedTasks] = useState<GroupedTasks | null>(null);
const [upcomingProjects, setUpcomingProjects] = useState<any[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const [dropdownOpen, setDropdownOpen] = useState<boolean>(false);
@ -226,6 +227,7 @@ const Tasks: React.FC = () => {
if (resetPagination) {
setTasks(tasksData.tasks || []);
setGroupedTasks(tasksData.groupedTasks || null);
setUpcomingProjects(tasksData.projects || []);
if (!options?.disablePagination) {
const limitToUse = options?.limitOverride ?? limit;
setOffset(limitToUse);
@ -241,6 +243,9 @@ const Tasks: React.FC = () => {
};
});
}
if (tasksData.projects) {
setUpcomingProjects((prev) => [...prev, ...(tasksData.projects || [])]);
}
if (!options?.disablePagination) {
const limitToUse = options?.limitOverride ?? limit;
setOffset((prev) => prev + limitToUse);
@ -896,22 +901,54 @@ const Tasks: React.FC = () => {
Object.keys(groupedTasks).length > 0) ? (
<>
{query.get('type') === 'upcoming' ? (
<GroupedTaskList
tasks={displayTasks}
groupedTasks={groupedTasks}
groupBy="none"
onTaskCreate={handleTaskCreate}
onTaskUpdate={handleTaskUpdate}
onTaskCompletionToggle={
handleTaskCompletionToggle
}
onTaskDelete={handleTaskDelete}
projects={projects}
hideProjectName={false}
onToggleToday={undefined}
showCompletedTasks={showCompleted}
searchQuery={taskSearchQuery}
/>
<>
<GroupedTaskList
tasks={displayTasks}
groupedTasks={groupedTasks}
groupBy="none"
onTaskCreate={handleTaskCreate}
onTaskUpdate={handleTaskUpdate}
onTaskCompletionToggle={
handleTaskCompletionToggle
}
onTaskDelete={handleTaskDelete}
projects={projects}
hideProjectName={false}
onToggleToday={undefined}
showCompletedTasks={showCompleted}
searchQuery={taskSearchQuery}
/>
{upcomingProjects.length > 0 && (
<div className="mt-8">
<h3 className="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">
{t('projects.upcomingProjects', 'Upcoming Projects')}
</h3>
<div className="space-y-2">
{upcomingProjects.map((project) => (
<div
key={project.uid}
className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 hover:shadow-md transition-shadow"
>
<div className="flex items-center justify-between">
<a
href={`/project/${project.uid}-${project.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')}`}
className="text-blue-600 dark:text-blue-400 hover:underline font-medium"
>
{project.name}
</a>
<div className="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400">
<span>{t('common.due', 'Due')}: </span>
<span className="font-medium">
{new Date(project.due_date_at).toLocaleDateString()}
</span>
</div>
</div>
</div>
))}
</div>
</div>
)}
</>
) : groupBy === 'project' ? (
<GroupedTaskList
tasks={displayTasks}