working leantime widget 19

This commit is contained in:
Alma 2025-04-12 13:47:51 +02:00
parent bdca2c6252
commit 9b7f0bc78a
2 changed files with 54 additions and 78 deletions

View File

@ -8,8 +8,14 @@ interface Task {
projectName: string;
status: string;
dueDate: string | null;
details?: string;
milestone?: string;
details?: string | null;
milestone?: string | null;
class: string;
}
interface Project {
id: number;
name: string;
}
// Cache for user IDs
@ -132,68 +138,53 @@ export async function GET() {
console.log('Projects response:', JSON.stringify(projectsData, null, 2));
const projectsMap = new Map(
projectsData.result.map((project: any) => [project.id, project.name])
projectsData.result.map((project: Project) => [project.id, project.name])
);
// Define status code mapping
const statusMapping: Record<string, string> = {
'-1': 'archived',
'0': 'new',
'1': 'in progress',
'2': 'waiting',
'3': 'in review',
'4': 'done'
};
// Transform the nested structure into a flat array of tasks
const tasks: Task[] = [];
Object.entries(data.result).forEach(([projectId, statusGroups]) => {
const projectName = projectsMap.get(Number(projectId)) || `Project ${projectId}`;
const project = projectsData.result.find((p: Project) => p.id === Number(projectId));
const projectName = project ? project.name : `Project ${projectId}`;
if (typeof statusGroups === 'object' && statusGroups !== null) {
// Iterate through each status group
Object.entries(statusGroups).forEach(([statusType, statusLabels]) => {
if (typeof statusLabels === 'object' && statusLabels !== null) {
// Each status label in the group
Object.values(statusLabels).forEach((label: any) => {
const statusName = statusMapping[statusType] || `status-${statusType}`;
const headline = String(label.title || label.name || statusName);
tasks.push({
id: label.id?.toString() || `${projectId}-${statusType}`,
headline,
projectName,
status: statusName,
dueDate: null,
details: label.description || null,
milestone: label.milestone || null
});
Object.entries(statusGroups).forEach(([_, label]) => {
if (typeof label === 'object' && label !== null && 'name' in label) {
const statusLabel = label as {
name: string;
class: string;
statusType: string;
kanbanCol: string;
sortKey: string;
};
tasks.push({
id: `${projectId}-${statusLabel.sortKey}`,
headline: statusLabel.name,
projectName,
status: statusLabel.statusType.toLowerCase(),
dueDate: null,
details: undefined,
milestone: undefined,
class: statusLabel.class
});
}
});
}
});
// Sort tasks by status type
const statusOrder: Record<string, number> = {
'new': 1,
'in progress': 2,
'waiting': 3,
'in review': 4,
'done': 5,
'archived': 6
};
// Sort tasks by their sortKey
tasks.sort((a, b) => {
const statusA = statusOrder[a.status.toLowerCase()] || 99;
const statusB = statusOrder[b.status.toLowerCase()] || 99;
return statusA - statusB;
const sortKeyA = Number(a.id.split('-')[1]) || 99;
const sortKeyB = Number(b.id.split('-')[1]) || 99;
return sortKeyA - sortKeyB;
});
// Deduplicate tasks based on project and status
// Deduplicate tasks based on headline and status
const uniqueTasks = tasks.reduce((acc: Task[], task) => {
const existingTask = acc.find(t =>
t.projectName === task.projectName &&
t.headline === task.headline &&
t.status === task.status
);

View File

@ -4,7 +4,6 @@ import { useEffect, useState } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { RefreshCw } from "lucide-react";
import { formatDate } from "@/lib/utils";
interface Task {
id: string;
@ -12,8 +11,9 @@ interface Task {
projectName: string;
status: string;
dueDate: string | null;
details?: string;
milestone?: string;
details?: string | null;
milestone?: string | null;
class: string;
}
export function Flow() {
@ -63,25 +63,6 @@ export function Flow() {
};
}, []);
const getStatusColor = (status: string) => {
switch (status.toLowerCase()) {
case 'new':
return 'text-blue-600';
case 'blocked':
return 'text-red-600';
case 'in progress':
case 'inprogress':
return 'text-orange-600';
case 'done':
case 'archived':
return 'text-green-600';
case 'waiting for approval':
return 'text-yellow-600';
default:
return 'text-gray-600';
}
};
// Group tasks by project
const groupedTasks = tasks.reduce((acc, task) => {
if (!acc[task.projectName]) {
@ -124,18 +105,8 @@ export function Flow() {
<div key={task.id} className="flex justify-between items-start text-sm border-b border-gray-100 pb-2">
<div className="flex-1">
<div className="font-medium">{task.headline}</div>
{task.milestone && (
<div className="text-xs text-gray-500 mt-1">
{task.milestone}
</div>
)}
{task.dueDate && (
<div className="text-xs text-gray-500 mt-1">
{formatDate(task.dueDate)}
</div>
)}
</div>
<div className={`ml-4 ${getStatusColor(task.status)}`}>
<div className={`ml-4 px-2 py-1 rounded text-xs ${getLabelClass(task.class)}`}>
{task.status.toUpperCase()}
</div>
</div>
@ -148,4 +119,18 @@ export function Flow() {
</CardContent>
</Card>
);
}
function getLabelClass(className: string): string {
const classMap: Record<string, string> = {
'label-default': 'bg-gray-100 text-gray-800',
'label-success': 'bg-green-100 text-green-800',
'label-warning': 'bg-yellow-100 text-yellow-800',
'label-info': 'bg-blue-100 text-blue-800',
'label-blue': 'bg-blue-100 text-blue-800',
'label-darker-blue': 'bg-indigo-100 text-indigo-800',
'label-dark-green': 'bg-emerald-100 text-emerald-800',
};
return classMap[className] || 'bg-gray-100 text-gray-800';
}