From 674fad445d61577f483dfd05b6488da603dd42e7 Mon Sep 17 00:00:00 2001
From: Kevin <39208150+Kvrnn@users.noreply.github.com>
Date: Mon, 8 Apr 2024 00:02:48 -0700
Subject: [PATCH] Project and Filter Working
Implementation of Project and Custom Filter categories functional
---
src/widgets/todoist/agenda.jsx | 45 +++++----
src/widgets/todoist/categories/filter.jsx | 42 +++++++++
src/widgets/todoist/categories/label.jsx | 7 +-
src/widgets/todoist/categories/project.jsx | 103 +++++++++++++++++++--
src/widgets/todoist/event.jsx | 8 +-
src/widgets/todoist/widget.js | 14 +++
6 files changed, 178 insertions(+), 41 deletions(-)
diff --git a/src/widgets/todoist/agenda.jsx b/src/widgets/todoist/agenda.jsx
index cb59976fe..f0a82a3a9 100644
--- a/src/widgets/todoist/agenda.jsx
+++ b/src/widgets/todoist/agenda.jsx
@@ -3,30 +3,27 @@ import classNames from "classnames";
import Event from "./event";
const colorVariants = {
- // https://tailwindcss.com/docs/content-configuration#dynamic-class-names
- amber: "bg-amber-500",
- blue: "bg-blue-500",
- cyan: "bg-cyan-500",
- emerald: "bg-emerald-500",
- fuchsia: "bg-fuchsia-500",
- gray: "bg-gray-500",
- green: "bg-green-500",
- indigo: "bg-indigo-500",
- lime: "bg-lime-500",
- neutral: "bg-neutral-500",
- orange: "bg-orange-500",
- pink: "bg-pink-500",
- purple: "bg-purple-500",
- red: "bg-red-500",
- rose: "bg-rose-500",
- sky: "bg-sky-500",
- slate: "bg-slate-500",
- stone: "bg-stone-500",
- teal: "bg-teal-500",
- violet: "bg-violet-500",
- white: "bg-white-500",
- yellow: "bg-yellow-500",
- zinc: "bg-zinc-500",
+ // Custom colors for Todoist
+ "berry_red": "bg-pink-500",
+ "red": "bg-red-500",
+ "orange": "bg-orange-500",
+ "yellow": "bg-yellow-500",
+ "olive_green": "bg-green-500",
+ "lime_green": "bg-lime-500",
+ "green": "bg-green-500",
+ "mint_green": "bg-green-400",
+ "teal": "bg-teal-500",
+ "sky_blue": "bg-blue-300",
+ "light_blue": "bg-blue-200",
+ "blue": "bg-blue-500",
+ "grape": "bg-purple-500",
+ "violet": "bg-purple-700",
+ "lavender": "bg-purple-300",
+ "magenta": "bg-pink-500",
+ "salmon": "bg-red-300",
+ "charcoal": "bg-gray-700",
+ "grey": "bg-gray-500",
+ "taupe": "bg-gray-400",
};
export default function Agenda({ tasks }) {
diff --git a/src/widgets/todoist/categories/filter.jsx b/src/widgets/todoist/categories/filter.jsx
index e69de29bb..ec18f6ca5 100644
--- a/src/widgets/todoist/categories/filter.jsx
+++ b/src/widgets/todoist/categories/filter.jsx
@@ -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 ;
+ }
+
+ // Render the Agenda component if tasks is not empty
+ return ;
+}
diff --git a/src/widgets/todoist/categories/label.jsx b/src/widgets/todoist/categories/label.jsx
index e2a4947ef..bffe9dd2a 100644
--- a/src/widgets/todoist/categories/label.jsx
+++ b/src/widgets/todoist/categories/label.jsx
@@ -4,12 +4,13 @@ import { useTranslation } from "next-i18next";
import useWidgetAPI from "../../../utils/proxy/use-widget-api";
import Error from "../../../components/services/widget/error";
import Agenda from "../agenda";
+import { DateTime } from "luxon";
export default function Label({ widget }) {
const { t } = useTranslation();
const { data: tasksData, error: tasksError } = useWidgetAPI(widget, "getTasksWithLabel", {
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
@@ -19,8 +20,8 @@ export default function Label({ widget }) {
// 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 ? new Date(task.due.date) : null,
- color: task.color || "blue", // Adjust color based on your preference
+ 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,
diff --git a/src/widgets/todoist/categories/project.jsx b/src/widgets/todoist/categories/project.jsx
index b1e30b6fb..f98c8959f 100644
--- a/src/widgets/todoist/categories/project.jsx
+++ b/src/widgets/todoist/categories/project.jsx
@@ -1,10 +1,95 @@
-const groupTasksByProjectId = () => {
- const groupedTasks = {};
- tasks.forEach((task) => {
- if (!groupedTasks[task.project_id]) {
- groupedTasks[task.project_id] = [];
- }
- groupedTasks[task.project_id].push(task);
+import { useEffect, useState } from "react";
+import { useTranslation } from "next-i18next";
+
+import useWidgetAPI from "../../../utils/proxy/use-widget-api";
+import Error from "../../../components/services/widget/error";
+import Agenda from "../agenda";
+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 ;
+ }
+
+ // Check for projects error and display error component
+ if (projectsError) {
+ return ;
+ }
+
+ // 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 ;
+ }
+
+ // Render the Agenda component with tasks
+ return ;
+}
diff --git a/src/widgets/todoist/event.jsx b/src/widgets/todoist/event.jsx
index 52208fcfd..0a8756848 100644
--- a/src/widgets/todoist/event.jsx
+++ b/src/widgets/todoist/event.jsx
@@ -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 (
{task.date && (
-
- {DateTime.fromJSDate(task.date)
- .setLocale(i18n.language)
- .toLocaleString(DateTime.TIME_24_SIMPLE)}
-
+ {formatDate(task.date)}
)}
diff --git a/src/widgets/todoist/widget.js b/src/widgets/todoist/widget.js
index c4e3f2986..8bd48c5b1 100644
--- a/src/widgets/todoist/widget.js
+++ b/src/widgets/todoist/widget.js
@@ -9,11 +9,25 @@ const widget = {
method: "GET",
endpoint: "tasks",
},
+ getAllProjects: {
+ method: "GET",
+ endpoint: "projects",
+ },
+ getTasksWithCustomFilter: {
+ method: "GET",
+ endpoint: "tasks",
+ params: ["filter"]
+ },
getTasksWithLabel: {
method: "GET",
endpoint: "tasks",
params: ["label"]
},
+ getTasksWithProject: {
+ method: "GET",
+ endpoint: "tasks",
+ params: ["project_id"]
+ },
},
};