Project and Filter Working
Implementation of Project and Custom Filter categories functional
This commit is contained in:
parent
57e7aa6b2b
commit
674fad445d
@ -3,30 +3,27 @@ import classNames from "classnames";
|
|||||||
import Event from "./event";
|
import Event from "./event";
|
||||||
|
|
||||||
const colorVariants = {
|
const colorVariants = {
|
||||||
// https://tailwindcss.com/docs/content-configuration#dynamic-class-names
|
// Custom colors for Todoist
|
||||||
amber: "bg-amber-500",
|
"berry_red": "bg-pink-500",
|
||||||
blue: "bg-blue-500",
|
"red": "bg-red-500",
|
||||||
cyan: "bg-cyan-500",
|
"orange": "bg-orange-500",
|
||||||
emerald: "bg-emerald-500",
|
"yellow": "bg-yellow-500",
|
||||||
fuchsia: "bg-fuchsia-500",
|
"olive_green": "bg-green-500",
|
||||||
gray: "bg-gray-500",
|
"lime_green": "bg-lime-500",
|
||||||
green: "bg-green-500",
|
"green": "bg-green-500",
|
||||||
indigo: "bg-indigo-500",
|
"mint_green": "bg-green-400",
|
||||||
lime: "bg-lime-500",
|
"teal": "bg-teal-500",
|
||||||
neutral: "bg-neutral-500",
|
"sky_blue": "bg-blue-300",
|
||||||
orange: "bg-orange-500",
|
"light_blue": "bg-blue-200",
|
||||||
pink: "bg-pink-500",
|
"blue": "bg-blue-500",
|
||||||
purple: "bg-purple-500",
|
"grape": "bg-purple-500",
|
||||||
red: "bg-red-500",
|
"violet": "bg-purple-700",
|
||||||
rose: "bg-rose-500",
|
"lavender": "bg-purple-300",
|
||||||
sky: "bg-sky-500",
|
"magenta": "bg-pink-500",
|
||||||
slate: "bg-slate-500",
|
"salmon": "bg-red-300",
|
||||||
stone: "bg-stone-500",
|
"charcoal": "bg-gray-700",
|
||||||
teal: "bg-teal-500",
|
"grey": "bg-gray-500",
|
||||||
violet: "bg-violet-500",
|
"taupe": "bg-gray-400",
|
||||||
white: "bg-white-500",
|
|
||||||
yellow: "bg-yellow-500",
|
|
||||||
zinc: "bg-zinc-500",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Agenda({ tasks }) {
|
export default function Agenda({ tasks }) {
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "next-i18next";
|
||||||
|
import { DateTime } from "luxon"; // Import Luxon for timezone conversion
|
||||||
|
|
||||||
|
import useWidgetAPI from "../../../utils/proxy/use-widget-api";
|
||||||
|
import Error from "../../../components/services/widget/error";
|
||||||
|
import Agenda from "../agenda";
|
||||||
|
|
||||||
|
export default function Filter({ widget }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { data: tasksData, error: tasksError } = useWidgetAPI(widget, "getTasksWithCustomFilter", {
|
||||||
|
refreshInterval: widget.refreshInterval || 300000, // 5 minutes, use default if not provided
|
||||||
|
filter: widget.filter
|
||||||
|
});
|
||||||
|
|
||||||
|
const [tasks, setTasks] = useState([]); // State to hold tasks
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!tasksError && tasksData && tasksData.length > 0) {
|
||||||
|
// Process label data and set tasks
|
||||||
|
const tasksToAdd = tasksData.slice(0, widget.maxTasks || tasksData.length).map((task) => ({
|
||||||
|
title: task.content || t("Untitled Task by Label"),
|
||||||
|
date: task.due ? DateTime.fromISO(task.due.date, { zone: widget.timeZone }).toJSDate() : null,
|
||||||
|
color: widget.color || task.color || "blue", // Adjust color based on your preference
|
||||||
|
description: task.tags ? task.tags.join(", ") : "",
|
||||||
|
url: task.url,
|
||||||
|
id: task.id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Update the tasks state
|
||||||
|
setTasks(tasksToAdd);
|
||||||
|
}
|
||||||
|
}, [tasksData, tasksError, widget, t, setTasks]);
|
||||||
|
|
||||||
|
const error = tasksError ?? tasksData?.error;
|
||||||
|
if (error && !widget.hideErrors) {
|
||||||
|
return <Error error={{ message: `${widget.type}: ${error.message ?? error}` }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the Agenda component if tasks is not empty
|
||||||
|
return <Agenda tasks={tasks} />;
|
||||||
|
}
|
||||||
@ -4,12 +4,13 @@ import { useTranslation } from "next-i18next";
|
|||||||
import useWidgetAPI from "../../../utils/proxy/use-widget-api";
|
import useWidgetAPI from "../../../utils/proxy/use-widget-api";
|
||||||
import Error from "../../../components/services/widget/error";
|
import Error from "../../../components/services/widget/error";
|
||||||
import Agenda from "../agenda";
|
import Agenda from "../agenda";
|
||||||
|
import { DateTime } from "luxon";
|
||||||
|
|
||||||
export default function Label({ widget }) {
|
export default function Label({ widget }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { data: tasksData, error: tasksError } = useWidgetAPI(widget, "getTasksWithLabel", {
|
const { data: tasksData, error: tasksError } = useWidgetAPI(widget, "getTasksWithLabel", {
|
||||||
refreshInterval: widget.refreshInterval || 300000, // 5 minutes, use default if not provided
|
refreshInterval: widget.refreshInterval || 300000, // 5 minutes, use default if not provided
|
||||||
label: widget.name
|
label: widget.label
|
||||||
});
|
});
|
||||||
|
|
||||||
const [tasks, setEvents] = useState([]); // State to hold events
|
const [tasks, setEvents] = useState([]); // State to hold events
|
||||||
@ -19,8 +20,8 @@ export default function Label({ widget }) {
|
|||||||
// Process label data and set tasks
|
// Process label data and set tasks
|
||||||
const tasksToAdd = tasksData.slice(0, widget.maxTasks || tasksData.length).map((task) => ({
|
const tasksToAdd = tasksData.slice(0, widget.maxTasks || tasksData.length).map((task) => ({
|
||||||
title: task.content || t("Untitled Task by Label"),
|
title: task.content || t("Untitled Task by Label"),
|
||||||
date: task.due ? new Date(task.due.date) : null,
|
date: task.due ? DateTime.fromISO(task.due.date, { zone: widget.timeZone }).toJSDate() : null,
|
||||||
color: task.color || "blue", // Adjust color based on your preference
|
color: widget.color || task.color || "blue", // Adjust color based on your preference
|
||||||
description: task.tags ? task.tags.join(", ") : "",
|
description: task.tags ? task.tags.join(", ") : "",
|
||||||
url: task.url,
|
url: task.url,
|
||||||
id: task.id,
|
id: task.id,
|
||||||
|
|||||||
@ -1,10 +1,95 @@
|
|||||||
const groupTasksByProjectId = () => {
|
import { useEffect, useState } from "react";
|
||||||
const groupedTasks = {};
|
import { useTranslation } from "next-i18next";
|
||||||
tasks.forEach((task) => {
|
|
||||||
if (!groupedTasks[task.project_id]) {
|
import useWidgetAPI from "../../../utils/proxy/use-widget-api";
|
||||||
groupedTasks[task.project_id] = [];
|
import Error from "../../../components/services/widget/error";
|
||||||
}
|
import Agenda from "../agenda";
|
||||||
groupedTasks[task.project_id].push(task);
|
import { DateTime } from "luxon";
|
||||||
|
|
||||||
|
export default function Project({ widget }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
// Fetch projects data unconditionally
|
||||||
|
const { data: projectsData, error: projectsError } = useWidgetAPI(widget, "getAllProjects");
|
||||||
|
|
||||||
|
// Fetch tasks for the specific project unconditionally
|
||||||
|
const { data: tasksData, error: tasksError } = useWidgetAPI(widget, "getAllActiveTasks", {
|
||||||
|
refreshInterval: widget.refreshInterval || 300000, // 5 minutes, use default if not provided
|
||||||
});
|
});
|
||||||
return Object.values(groupedTasks);
|
|
||||||
};
|
// State to hold tasks
|
||||||
|
const [tasks, setTasks] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Check for errors
|
||||||
|
if (projectsError) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if projectsData is available
|
||||||
|
if (!projectsData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the project with the given name
|
||||||
|
const project = projectsData.find((project) => project.name === widget.project_name);
|
||||||
|
|
||||||
|
// Check if project exists
|
||||||
|
if (!project) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract project ID and color
|
||||||
|
const projectId = project.id;
|
||||||
|
const projectColor = widget.color || project.color || "blue"; // Default color if not provided
|
||||||
|
|
||||||
|
// Check for tasks error
|
||||||
|
if (tasksError) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process tasks data and set tasks
|
||||||
|
if (tasksData && tasksData.length > 0) {
|
||||||
|
const tasksToAdd = tasksData
|
||||||
|
.filter((task) => task.project_id === projectId) // Filter tasks by project ID
|
||||||
|
.slice(0, widget.maxTasks || tasksData.length)
|
||||||
|
.map((task) => ({
|
||||||
|
title: task.content || t("Untitled Task by Label"),
|
||||||
|
date: task.due ? DateTime.fromISO(task.due.date, { zone: widget.timeZone }).toJSDate() : null,
|
||||||
|
color: projectColor, // Assign project color to task
|
||||||
|
description: task.tags ? task.tags.join(", ") : "",
|
||||||
|
url: task.url,
|
||||||
|
id: task.id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Update the tasks state
|
||||||
|
setTasks(tasksToAdd);
|
||||||
|
}
|
||||||
|
}, [projectsData, projectsError, tasksData, tasksError, widget, t]);
|
||||||
|
|
||||||
|
// Check for tasks error and display error component if not hidden
|
||||||
|
if (tasksError && !widget.hideErrors) {
|
||||||
|
return <Error error={{ message: `${widget.type}: ${tasksError.message}` }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for projects error and display error component
|
||||||
|
if (projectsError) {
|
||||||
|
return <Error error={{ message: `${widget.type}: ${projectsError.message}` }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If projectsData is not yet available, return null or loading indicator
|
||||||
|
if (!projectsData) {
|
||||||
|
return null; // or return a loading indicator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the project with the given name
|
||||||
|
const project = projectsData.find((project) => project.name === widget.project_name);
|
||||||
|
|
||||||
|
// If project does not exist, display error component
|
||||||
|
if (!project) {
|
||||||
|
return <Error error={{ message: `${widget.type}: Project not found` }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the Agenda component with tasks
|
||||||
|
return <Agenda tasks={tasks} />;
|
||||||
|
}
|
||||||
|
|||||||
@ -24,6 +24,8 @@ export default function Event({ task, colorVariants }) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatDate = (date) => DateTime.fromJSDate(date).toFormat("LLL dd") // Format to month and date (e.g., Jan 01);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex flex-row text-theme-700 dark:text-theme-200 items-center text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1"
|
className="flex flex-row text-theme-700 dark:text-theme-200 items-center text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1"
|
||||||
@ -33,11 +35,7 @@ export default function Event({ task, colorVariants }) {
|
|||||||
>
|
>
|
||||||
<span className="ml-2 w-10">
|
<span className="ml-2 w-10">
|
||||||
{task.date && (
|
{task.date && (
|
||||||
<span>
|
<span>{formatDate(task.date)}</span>
|
||||||
{DateTime.fromJSDate(task.date)
|
|
||||||
.setLocale(i18n.language)
|
|
||||||
.toLocaleString(DateTime.TIME_24_SIMPLE)}
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="ml-2 h-2 w-2">
|
<span className="ml-2 h-2 w-2">
|
||||||
|
|||||||
@ -9,11 +9,25 @@ const widget = {
|
|||||||
method: "GET",
|
method: "GET",
|
||||||
endpoint: "tasks",
|
endpoint: "tasks",
|
||||||
},
|
},
|
||||||
|
getAllProjects: {
|
||||||
|
method: "GET",
|
||||||
|
endpoint: "projects",
|
||||||
|
},
|
||||||
|
getTasksWithCustomFilter: {
|
||||||
|
method: "GET",
|
||||||
|
endpoint: "tasks",
|
||||||
|
params: ["filter"]
|
||||||
|
},
|
||||||
getTasksWithLabel: {
|
getTasksWithLabel: {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
endpoint: "tasks",
|
endpoint: "tasks",
|
||||||
params: ["label"]
|
params: ["label"]
|
||||||
},
|
},
|
||||||
|
getTasksWithProject: {
|
||||||
|
method: "GET",
|
||||||
|
endpoint: "tasks",
|
||||||
|
params: ["project_id"]
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user