feat: enhance MemoCompleteList with task clearing functionality and integrate task extraction utility

This commit is contained in:
Ahmed-Elgendy25 2026-03-24 00:44:55 +02:00 committed by =AhmedAshraf
parent eabe09a69e
commit 41405bc3fa
2 changed files with 122 additions and 12 deletions

View File

@ -1,19 +1,123 @@
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { useMemoViewContext } from "../MemoViewContext";
import { useCreateMemo, useDeleteMemo, useUpdateMemo } from "@/hooks/useMemoQueries";
import { extractTasks } from "@/utils/markdown-manipulation";
import { toast } from "react-hot-toast";
function MemoCompleteList() {
const { memo } = useMemoViewContext();
const { mutate: updateMemo } = useUpdateMemo();
const { mutate: deleteMemo } = useDeleteMemo();
const { mutate: createMemo } = useCreateMemo();
// Parse tasks once per render for both counting and clearing
const tasks = extractTasks(memo.content);
const completedTasks = tasks.filter((task) => task.checked);
const completedCount = completedTasks.length;
const hasCompletedTasks = completedCount > 0;
const handleClearCompleted = () => {
if (!hasCompletedTasks) {
return;
}
const previousContent = memo.content;
// Remove completed task lines based on the precomputed task list
const lines = previousContent.split("\n");
const completedLines = completedTasks
.map((task) => task.lineNumber)
.sort((a, b) => b - a);
for (const lineNumber of completedLines) {
if (lineNumber >= 0 && lineNumber < lines.length) {
lines.splice(lineNumber, 1);
}
}
const newContent = lines.join("\n");
if (newContent === previousContent) {
return;
}
const isNowEmpty = newContent.trim().length === 0;
if (isNowEmpty) {
// Delete memo when clearing completed tasks leaves it empty
deleteMemo(memo.name);
toast.custom(
(t) => (
<div className="flex items-center gap-3 rounded-md border border-border bg-card px-4 py-2 text-sm text-foreground shadow-lg">
<span>{completedCount} completed items cleared and memo deleted. Undo?</span>
<Button
variant="outline"
size="sm"
onClick={() => {
// Recreate memo with original content
createMemo({
...memo,
name: "",
content: previousContent,
});
toast.dismiss(t.id);
}}
>
Undo
</Button>
</div>
),
{ position: "bottom-center" },
);
} else {
// Just update content when there is still other content
updateMemo({
update: {
name: memo.name,
content: newContent,
},
updateMask: ["content"],
});
toast.custom(
(t) => (
<div className="flex items-center gap-3 rounded-md border border-border bg-card px-4 py-2 text-sm text-foreground shadow-lg">
<span>{completedCount} completed items cleared. Undo?</span>
<Button
variant="outline"
size="sm"
onClick={() => {
updateMemo({
update: {
name: memo.name,
content: previousContent,
},
updateMask: ["content"],
});
toast.dismiss(t.id);
}}
>
Undo
</Button>
</div>
),
{ position: "bottom-center" },
);
}
};
return (
<section className=" flex items-center gap-3">
<div className=" flex items-center gap-2 ">
<span>Completed Task</span>
<Switch
defaultChecked
className=" [&>[data-slot=switch-thumb][data-state=unchecked]]:bg-mauve-400 [&>[data-slot=switch-thumb][data-state=checked]]:bg-white"
/>
</div>
<Button className=" not-hover:border-foreground not-hover:text-foreground bg-transparent border-2 transition-all ease-linear">Clear Completed</Button>
<section className="flex items-center gap-3">
<Button
type="button"
disabled={!hasCompletedTasks}
onClick={handleClearCompleted}
className="not-hover:border-foreground not-hover:text-foreground bg-transparent border-2 transition-all ease-linear"
>
Clear Completed
</Button>
</section>
)
);
}
export default MemoCompleteList
export default MemoCompleteList;

View File

@ -1,6 +1,12 @@
import { useMemoViewContext } from "../MemoViewContext";
import MemoCompleteList from "./MemoCompleteList";
function MemoFooter() {
const { memo } = useMemoViewContext();
const hasTaskList = memo.property?.hasTaskList;
if (!hasTaskList) {
return null;
}
return (
<footer className=" w-full mt-5 flex justify-end items-center">
<MemoCompleteList/>