From f82a184c97dda1a276a3abd3f01a270843bb8f62 Mon Sep 17 00:00:00 2001 From: Alma Date: Fri, 11 Apr 2025 11:02:10 +0200 Subject: [PATCH] widget flow --- app/api/leantime/tasks/route.ts | 62 ++++++++++ app/page.tsx | 4 + components/flow.tsx | 165 +++++++++++++++++++++++++++ components/parole.tsx | 10 +- components/users/add-user-button.tsx | 11 +- 5 files changed, 245 insertions(+), 7 deletions(-) create mode 100644 app/api/leantime/tasks/route.ts create mode 100644 components/flow.tsx diff --git a/app/api/leantime/tasks/route.ts b/app/api/leantime/tasks/route.ts new file mode 100644 index 00000000..4608ad2c --- /dev/null +++ b/app/api/leantime/tasks/route.ts @@ -0,0 +1,62 @@ +import { getServerSession } from "next-auth/next"; +import { authOptions } from "@/app/api/auth/[...nextauth]/route"; +import { NextResponse } from "next/server"; + +export async function GET() { + const session = await getServerSession(authOptions); + + if (!session) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + try { + // Get user's tasks from Leantime + const response = await fetch('https://agilite.slm-lab.net/api/jsonrpc', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': process.env.LEANTIME_TOKEN || '', + }, + body: JSON.stringify({ + method: 'leantime.rpc.Tasks.Tasks.getAll', + jsonrpc: '2.0', + id: 1, + params: { + userId: session.user.id, + status: ['not_started', 'in_progress'], + limit: 10, + sort: 'dueDate', + order: 'ASC' + } + }) + }); + + if (!response.ok) { + throw new Error('Failed to fetch tasks from Leantime'); + } + + const data = await response.json(); + + if (!data.result) { + return NextResponse.json({ tasks: [] }); + } + + // Transform the tasks to match our interface + const tasks = data.result.map((task: any) => ({ + id: task.id, + headline: task.headline, + description: task.description, + status: task.status, + dueDate: task.dueDate, + priority: task.priority + })); + + return NextResponse.json({ tasks }); + } catch (error) { + console.error('Error fetching tasks:', error); + return NextResponse.json( + { error: "Failed to fetch tasks" }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 62bc8e55..f6f9ed3f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,5 +1,6 @@ import { QuoteCard } from "@/components/quote-card"; import { Parole } from "@/components/parole"; +import { Flow } from "@/components/flow"; export const metadata = { title: "Enkun - Dashboard", @@ -15,6 +16,9 @@ export default function DashboardPage() {
+
+ +
); diff --git a/components/flow.tsx b/components/flow.tsx new file mode 100644 index 00000000..573e6eef --- /dev/null +++ b/components/flow.tsx @@ -0,0 +1,165 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { RefreshCw, Calendar, CheckCircle2, Clock } from "lucide-react"; +import { useRouter } from "next/navigation"; +import { useSession } from "next-auth/react"; + +interface Task { + id: string; + headline: string; + description: string; + status: string; + dueDate: string; + priority: number; +} + +export function Flow() { + const [tasks, setTasks] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [refreshing, setRefreshing] = useState(false); + const router = useRouter(); + const { data: session } = useSession(); + + const fetchTasks = async (isRefresh = false) => { + try { + if (isRefresh) { + setRefreshing(true); + } + const response = await fetch('/api/leantime/tasks', { + cache: 'no-store', + next: { revalidate: 0 }, + }); + + if (response.status === 401) { + setError('Session expired. Please sign in again.'); + return; + } + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || 'Failed to fetch tasks'); + } + + const data = await response.json(); + setTasks(data.tasks); + setError(null); + } catch (err) { + console.error('Error fetching tasks:', err); + const errorMessage = err instanceof Error ? err.message : 'Failed to fetch tasks'; + setError(errorMessage); + } finally { + setLoading(false); + setRefreshing(false); + } + }; + + useEffect(() => { + if (session) { + fetchTasks(); + // Set up polling every 5 minutes + const interval = setInterval(() => fetchTasks(), 300000); + return () => clearInterval(interval); + } + }, [session]); + + const getPriorityColor = (priority: number) => { + switch (priority) { + case 1: + return 'text-red-500'; + case 2: + return 'text-yellow-500'; + case 3: + return 'text-green-500'; + default: + return 'text-gray-500'; + } + }; + + const getStatusIcon = (status: string) => { + switch (status.toLowerCase()) { + case 'done': + return ; + case 'in progress': + return ; + default: + return ; + } + }; + + return ( + router.push('/flow')} + > + + My Tasks + + + + {loading &&

Loading tasks...

} + {error && ( +
+

Error: {error}

+ +
+ )} + {!loading && !error && ( +
+ {tasks.length === 0 ? ( +

No tasks found

+ ) : ( + tasks.map((task) => ( +
+
+ {getStatusIcon(task.status)} +
+
+
+

{task.headline}

+ + {task.priority === 1 ? 'High' : task.priority === 2 ? 'Medium' : 'Low'} + +
+

{task.description}

+ {task.dueDate && ( +

+ Due: {new Date(task.dueDate).toLocaleDateString()} +

+ )} +
+
+ )) + )} +
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/components/parole.tsx b/components/parole.tsx index 93a390b4..90c66afd 100644 --- a/components/parole.tsx +++ b/components/parole.tsx @@ -115,7 +115,7 @@ export function Parole() { return ( router.push('/parole')} > @@ -133,7 +133,7 @@ export function Parole() { - + {loading &&

Loading messages...

} {error && (
@@ -151,13 +151,13 @@ export function Parole() {
)} {!loading && !error && ( -
+
{messages.length === 0 ? (

No messages found

) : ( messages.map((message) => ( -
- +
+ {(message.u.name || message.u.username).substring(0, 2).toUpperCase()} diff --git a/components/users/add-user-button.tsx b/components/users/add-user-button.tsx index 131c5778..d45d8806 100644 --- a/components/users/add-user-button.tsx +++ b/components/users/add-user-button.tsx @@ -137,8 +137,15 @@ export function AddUserButton({ userRole, handleAddUser }: AddUserButtonProps) { Admin - Enseignant - Étudiant + Apprentices + Coding + Data Intelligence + Entrepreneurship + Expression + Investigation + Lean + Mediation + Mentors