From f2374857073d993aef9f67097feb78860e51730e Mon Sep 17 00:00:00 2001 From: Carsten Koch Date: Tue, 4 Jun 2024 22:18:34 +0200 Subject: [PATCH 1/3] feat: set default date when adding a new day plan --- components/dayplan/dayplan-form.tsx | 7 +++++-- docs/releases/next.md | 22 ++++++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/components/dayplan/dayplan-form.tsx b/components/dayplan/dayplan-form.tsx index 2517a22e8..ea5a9a32e 100644 --- a/components/dayplan/dayplan-form.tsx +++ b/components/dayplan/dayplan-form.tsx @@ -1,7 +1,7 @@ import { addDaysToDate } from "@/helpers/functional"; import { cn } from "@/lib/utils"; import { zodResolver } from "@hookform/resolvers/zod"; -import { format } from "date-fns"; +import { addDays, format, getHours } from "date-fns"; import { CalendarIcon } from "lucide-react"; import { FC } from "react"; import { useForm } from "react-hook-form"; @@ -44,7 +44,10 @@ const DayPlanForm: FC = ({ }) => { const form = useForm>({ resolver: zodResolver(FormSchema), - defaultValues: { goal: "" }, + defaultValues: { + goal: "", + date: getHours(new Date()) < 12 ? new Date() : addDays(new Date(), 1), + }, }); const { toast } = useToast(); diff --git a/docs/releases/next.md b/docs/releases/next.md index 81e1baec6..12d34fb28 100644 --- a/docs/releases/next.md +++ b/docs/releases/next.md @@ -1,5 +1,19 @@ -# Kleinere Veränderungen (Version :VERSION) +# Optimierungen beim Arbeiten mit Tagesplänen (Version :VERSION) -- Bei Suchen nach Personen, Accounts und Projekten musste auf Groß- und Kleinschreibung geachtet werden. Das ist nun nicht mehr so. -- Actions mit Bullet Points sahen in der Vorschau bei einem geschlossenen Accordion etwas komisch aus und waren mit doppelten Kommas durchsät. Das ist jetzt nicht mehr so. -- Das Erstellen eines neuen Eintrags in der Inbox ist in das Navigationsmenü und in den Header verlegt worden. +- Beim Einrichten eines neuen Tagesplans den aktuellen Tag als Standard anbieten, wenn es vormittags ist, ansonsten den nächsten Tag + +In Arbeit: + +- Editieren von Einträgen in Aufgabenliste ermöglichen + +- Löschen von Einträgen ermöglichen (in Today's Tasks, Meetings und bei Notizen; immer zur Sicherheit abfragen) + +- Aufgabenliste umstellen auf Checkbox + +- Hinweise bei den Tagesplänen, wenn offene Pläne in anderen Kontexten existieren + +- Nochmal prüfen, was bei der Migration passiert, ob ich dem Alert traue; dann kann ich migrieren und entsprechend die Logik für die Legacy Tasks endlich entfernen + +- Die Bestätigung, dass eine Aufgabe im Tagesplan angelegt wurde, ist unnötig + +- Aufgaben im Tagesplan sortieren (erledigte nach unten, ansonsten nach Projekt Prio sortieren, dann nach Erstelldatum) From b24e58d98ffa1bb8c592e829eb42d4c453e214a5 Mon Sep 17 00:00:00 2001 From: Carsten Koch Date: Wed, 5 Jun 2024 00:58:50 +0200 Subject: [PATCH 2/3] feat: allow editing and deletion of todos in DayPlan --- api/useDayplans.ts | 46 +++++++++++ components/dayplan/task.tsx | 134 +++++++++++++++++++++++++++---- components/ui/alert-dialog.tsx | 139 +++++++++++++++++++++++++++++++++ docs/releases/next.md | 9 +-- package-lock.json | 29 +++++++ package.json | 1 + pages/today/index.tsx | 84 +++++++++++--------- 7 files changed, 384 insertions(+), 58 deletions(-) create mode 100644 components/ui/alert-dialog.tsx diff --git a/api/useDayplans.ts b/api/useDayplans.ts index 93366d914..9a3ce8439 100644 --- a/api/useDayplans.ts +++ b/api/useDayplans.ts @@ -149,6 +149,13 @@ type CreateTodoFn = (props: { projectId?: string; }) => Promise; +type UpdateTodoFn = ( + todo: string, + todoId: string +) => Promise; + +type DeleteTodoFn = (todoId: string) => Promise; + type SwitchTodoDoneFn = (todoId: string, done: boolean) => Promise; const checkForLegacyTasks = async (setCount: (count: number) => void) => { @@ -335,6 +342,43 @@ const useDayPlans = (context?: Context) => { return data || undefined; }; + const updateTodo: UpdateTodoFn = async (todo, todoId) => { + const updated: DayPlan[] | undefined = dayPlans?.map((d) => + !d.todos.some((t) => t.id === todoId) + ? d + : { + ...d, + todos: d.todos.map((t) => (t.id !== todoId ? t : { ...t, todo })), + } + ); + if (updated) mutate(updated, false); + const { data, errors } = await client.models.DayPlanTodo.update({ + id: todoId, + todo, + }); + if (errors) handleApiErrors(errors, "Error updating todo"); + if (updated) mutate(updated); + return data?.id; + }; + + const deleteTodo: DeleteTodoFn = async (todoId) => { + const updated: DayPlan[] | undefined = dayPlans?.map((d) => + !d.todos.some((t) => t.id === todoId) + ? d + : { + ...d, + todos: d.todos.filter((t) => t.id !== todoId), + } + ); + if (updated) mutate(updated, false); + const { data, errors } = await client.models.DayPlanTodo.delete({ + id: todoId, + }); + if (errors) handleApiErrors(errors, "Error deleting task from day plan."); + if (updated) mutate(updated); + return data?.id; + }; + const switchTodoDone: SwitchTodoDoneFn = async (todoId, done) => { const updated = dayPlans?.map(({ todos, ...rest }) => ({ ...rest, @@ -359,6 +403,8 @@ const useDayPlans = (context?: Context) => { completeDayPlan, undoDayplanCompletion, createTodo, + updateTodo, + deleteTodo, switchTodoDone, migrateLegacyTasks, countLegacyTasks, diff --git a/components/dayplan/task.tsx b/components/dayplan/task.tsx index 122b0a727..0b2a8824e 100644 --- a/components/dayplan/task.tsx +++ b/components/dayplan/task.tsx @@ -1,35 +1,137 @@ import { DayPlanTodo } from "@/api/useDayplans"; import { cn } from "@/lib/utils"; -import { FC } from "react"; -import { IoCheckboxSharp, IoSquareOutline } from "react-icons/io5"; +import { Edit, Trash } from "lucide-react"; +import { FC, useState } from "react"; import ProjectName from "../ui-elements/tokens/project-name"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "../ui/alert-dialog"; +import { Button } from "../ui/button"; +import { Checkbox } from "../ui/checkbox"; +import { Input } from "../ui/input"; type TaskProps = { todo: DayPlanTodo; switchTodoDone: (taskId: string, done: boolean) => void; + updateTodo: (todo: string, todoId: string) => void; + deleteTodo: (todoId: string) => void; + editable?: boolean; }; const Task: FC = ({ todo: { id, todo, done, projectId }, switchTodoDone, + updateTodo, + deleteTodo, + editable, }) => { - const Icon = done ? IoCheckboxSharp : IoSquareOutline; + const [isEditing, setIsEditing] = useState(false); + const [todoValue, setTodoValue] = useState(todo); + const [alertOpen, setAlertOpen] = useState(false); return ( -
- switchTodoDone(id, done)} - /> -
-
{todo}
- {projectId && ( -
- -
- )} +
+
+ switchTodoDone(id, done)} + className="mt-[0.2rem]" + /> +
+ + {projectId && ( +
+ +
+ )} +
-
+ ); }; diff --git a/components/ui/alert-dialog.tsx b/components/ui/alert-dialog.tsx new file mode 100644 index 000000000..47f60f2ce --- /dev/null +++ b/components/ui/alert-dialog.tsx @@ -0,0 +1,139 @@ +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; +import * as React from "react"; + +import { buttonVariants } from "@/components/ui/button"; +import { cn } from "@/lib/utils"; + +const AlertDialog = AlertDialogPrimitive.Root; + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger; + +const AlertDialogPortal = AlertDialogPrimitive.Portal; + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)); +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +AlertDialogHeader.displayName = "AlertDialogHeader"; + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +AlertDialogFooter.displayName = "AlertDialogFooter"; + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName; + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; + +export { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogOverlay, + AlertDialogPortal, + AlertDialogTitle, + AlertDialogTrigger, +}; diff --git a/docs/releases/next.md b/docs/releases/next.md index 12d34fb28..063a4a414 100644 --- a/docs/releases/next.md +++ b/docs/releases/next.md @@ -1,14 +1,13 @@ # Optimierungen beim Arbeiten mit Tagesplänen (Version :VERSION) - Beim Einrichten eines neuen Tagesplans den aktuellen Tag als Standard anbieten, wenn es vormittags ist, ansonsten den nächsten Tag - -In Arbeit: - +- Aufgabenliste umstellen auf Checkbox - Editieren von Einträgen in Aufgabenliste ermöglichen +- Löschen von Einträgen ermöglichen (in Today's Tasks; immer zur Sicherheit abfragen) -- Löschen von Einträgen ermöglichen (in Today's Tasks, Meetings und bei Notizen; immer zur Sicherheit abfragen) +In Arbeit: -- Aufgabenliste umstellen auf Checkbox +- Löschen von Einträgen ermöglichen (Meetings und bei Notizen; immer zur Sicherheit abfragen) - Hinweise bei den Tagesplänen, wenn offene Pläne in anderen Kontexten existieren diff --git a/package-lock.json b/package-lock.json index 20028ac22..2841aa29d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@dnd-kit/sortable": "^8.0.0", "@hookform/resolvers": "^3.4.2", "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", @@ -14826,6 +14827,34 @@ } } }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.0.5.tgz", + "integrity": "sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dialog": "1.0.5", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", diff --git a/package.json b/package.json index 49dd83494..c15c5f164 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@dnd-kit/sortable": "^8.0.0", "@hookform/resolvers": "^3.4.2", "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", diff --git a/pages/today/index.tsx b/pages/today/index.tsx index 84ddbba7d..7074f0888 100644 --- a/pages/today/index.tsx +++ b/pages/today/index.tsx @@ -6,13 +6,14 @@ import TaskForm from "@/components/dayplan/task-form"; import MainLayout from "@/components/layouts/MainLayout"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; import { ToastAction } from "@/components/ui/toast"; import { useToast } from "@/components/ui/use-toast"; import { useContextContext } from "@/contexts/ContextContext"; import { isTodayOrFuture } from "@/helpers/functional"; +import { format } from "date-fns"; import { FileWarning } from "lucide-react"; import { useState } from "react"; -import { IoSquareOutline } from "react-icons/io5"; const TodayPage = () => { const { context } = useContextContext(); @@ -22,6 +23,8 @@ const TodayPage = () => { completeDayPlan, undoDayplanCompletion, createTodo, + updateTodo, + deleteTodo, switchTodoDone, migrateLegacyTasks, countLegacyTasks, @@ -118,44 +121,51 @@ const TodayPage = () => {
- {dayPlans.map(({ id: dayplanId, day, dayGoal, todos }) => ( -
-
- handleCompleteDayPlan(dayplanId)} +
+ {dayPlans.map(({ id: dayplanId, day, dayGoal, todos }) => ( +
+ handleCompleteDayPlan(dayplanId)} + className="mt-[0.05rem] sticky top-[7rem] md:top-[8rem] z-30 bg-bgTransparent " /> -

- {dayGoal} – {day.toLocaleDateString()} -

+
+ + {todos.length === 0 ? ( +

No open todos

+ ) : ( + todos.map((todo) => ( + + )) + )} + {isTodayOrFuture(day) && ( +
+ + createTodo({ todo, projectId, dayplanId }) + } + /> +
+ )} +
- -
- {todos.length === 0 ? ( -
- No open todos -
- ) : ( - todos.map((todo) => ( - - )) - )} - {isTodayOrFuture(day) && ( -
- - - createTodo({ todo, projectId, dayplanId }) - } - /> -
- )} -
-
- ))} + ))} +
)} From a9e6831435e034f04649ab843b1a1289a7be9407 Mon Sep 17 00:00:00 2001 From: Carsten Koch Date: Wed, 5 Jun 2024 01:13:26 +0200 Subject: [PATCH 3/3] feat: deletion of day plan todos is now possible --- api/useDayplans.ts | 6 +++++- components/dayplan/dayplan-form.tsx | 6 ------ components/dayplan/task-form.tsx | 3 --- docs/releases/next.md | 12 +----------- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/api/useDayplans.ts b/api/useDayplans.ts index 9a3ce8439..6feb4e56f 100644 --- a/api/useDayplans.ts +++ b/api/useDayplans.ts @@ -94,7 +94,11 @@ const mapDayPlan: (dayplan: DayPlanData) => DayPlan = ({ doneOn: doneOn ? new Date(doneOn) : undefined, projectId: project?.id, })) - .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()), + .sort((a, b) => + a.done === b.done + ? a.createdAt.getTime() - b.createdAt.getTime() + : (a.done ? 1 : 0) - (b.done ? 1 : 0) + ), projectTasks: projectTasks .map(({ id, task, done, createdAt, projects }) => ({ id, diff --git a/components/dayplan/dayplan-form.tsx b/components/dayplan/dayplan-form.tsx index ea5a9a32e..1cc22c9c4 100644 --- a/components/dayplan/dayplan-form.tsx +++ b/components/dayplan/dayplan-form.tsx @@ -18,7 +18,6 @@ import { } from "../ui/form"; import { Input } from "../ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; -import { useToast } from "../ui/use-toast"; const FormSchema = z.object({ goal: z @@ -49,13 +48,8 @@ const DayPlanForm: FC = ({ date: getHours(new Date()) < 12 ? new Date() : addDays(new Date(), 1), }, }); - const { toast } = useToast(); const handleSubmit = ({ date, goal }: z.infer) => { - toast({ - title: "You created a new day plan.", - description: `Your goal for ${date.toLocaleDateString()} is "${goal}".`, - }); onSubmit(goal, date); }; diff --git a/components/dayplan/task-form.tsx b/components/dayplan/task-form.tsx index b9f0931ca..6c88f5240 100644 --- a/components/dayplan/task-form.tsx +++ b/components/dayplan/task-form.tsx @@ -13,7 +13,6 @@ import { FormMessage, } from "../ui/form"; import { Input } from "../ui/input"; -import { useToast } from "../ui/use-toast"; const FormSchema = z.object({ todo: z @@ -36,10 +35,8 @@ const TaskForm: FC = ({ createTodo }) => { resolver: zodResolver(FormSchema), defaultValues: { todo: "", projectId: "" }, }); - const { toast } = useToast(); const handleSubmit = ({ todo, projectId }: z.infer) => { - toast({ title: "You created a new task", description: "new task created" }); createTodo(todo, projectId !== "" ? projectId : undefined); form.reset(); }; diff --git a/docs/releases/next.md b/docs/releases/next.md index 063a4a414..e143b49c4 100644 --- a/docs/releases/next.md +++ b/docs/releases/next.md @@ -4,15 +4,5 @@ - Aufgabenliste umstellen auf Checkbox - Editieren von Einträgen in Aufgabenliste ermöglichen - Löschen von Einträgen ermöglichen (in Today's Tasks; immer zur Sicherheit abfragen) - -In Arbeit: - -- Löschen von Einträgen ermöglichen (Meetings und bei Notizen; immer zur Sicherheit abfragen) - -- Hinweise bei den Tagesplänen, wenn offene Pläne in anderen Kontexten existieren - -- Nochmal prüfen, was bei der Migration passiert, ob ich dem Alert traue; dann kann ich migrieren und entsprechend die Logik für die Legacy Tasks endlich entfernen - +- Aufgaben im Tagesplan sortieren (erledigte nach unten, dann nach Erstelldatum) - Die Bestätigung, dass eine Aufgabe im Tagesplan angelegt wurde, ist unnötig - -- Aufgaben im Tagesplan sortieren (erledigte nach unten, ansonsten nach Projekt Prio sortieren, dann nach Erstelldatum)