tag hiding in buckets to prevent orphaning
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { type Component, createMemo, For } from "solid-js";
|
import { type Component, createMemo, For } from "solid-js";
|
||||||
import { type Task, calculateUrgencyFromDate, setActiveTaskId, store, copyTask, updateTask } from "@/store";
|
import { type Task, calculateUrgencyFromDate, setActiveTaskId, store, copyTask, updateTask, currentTaskContext } from "@/store";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Clock, ArrowUpCircle, Copy, Users2 } from "lucide-solid";
|
import { Clock, ArrowUpCircle, Copy, Users2 } from "lucide-solid";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
@@ -26,6 +26,20 @@ export const TaskCard: Component<{ task: Task }> = (props) => {
|
|||||||
return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
|
return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Filter out bucket tags if we are currently INSIDE that bucket view
|
||||||
|
const visibleTags = createMemo(() => {
|
||||||
|
const tags = props.task.tags || [];
|
||||||
|
const ctx = currentTaskContext();
|
||||||
|
|
||||||
|
if (typeof ctx === 'object' && 'bucketId' in ctx) {
|
||||||
|
// We are in a bucket view. Hide the tag that matches this bucket's name.
|
||||||
|
const bucketName = (ctx as { name: string }).name.toLowerCase();
|
||||||
|
return tags.filter(t => t.toLowerCase() !== bucketName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags;
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class={cn(
|
class={cn(
|
||||||
@@ -126,9 +140,9 @@ export const TaskCard: Component<{ task: Task }> = (props) => {
|
|||||||
})()}
|
})()}
|
||||||
|
|
||||||
{/* Tags */}
|
{/* Tags */}
|
||||||
{props.task.tags && props.task.tags.length > 0 && (
|
{visibleTags().length > 0 && (
|
||||||
<div class="flex flex-wrap items-center gap-1 min-w-0">
|
<div class="flex flex-wrap items-center gap-1 min-w-0">
|
||||||
<For each={props.task.tags}>
|
<For each={visibleTags()}>
|
||||||
{(tag) => (
|
{(tag) => (
|
||||||
<Badge
|
<Badge
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type Component, createEffect, createSignal, For } from "solid-js";
|
import { type Component, createEffect, createSignal, For, createMemo } from "solid-js";
|
||||||
import { Sheet, SheetContent } from "@/components/ui/sheet";
|
import { Sheet, SheetContent } from "@/components/ui/sheet";
|
||||||
import { type Task, removeTask, restoreTask, updateTask, saveTaskAsTemplate, shareTask, revokeShare, splitTask, loadTaskContent } from "@/store";
|
import { type Task, removeTask, restoreTask, updateTask, saveTaskAsTemplate, shareTask, revokeShare, splitTask, loadTaskContent, currentTaskContext } from "@/store";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
import { ArrowUpCircle, Clock, Calendar, Type, Trash2, X, Copy, MoreHorizontal, Gauge, Share2, UserMinus, GitBranch, Tag, Settings } from "lucide-solid";
|
import { ArrowUpCircle, Clock, Calendar, Type, Trash2, X, Copy, MoreHorizontal, Gauge, Share2, UserMinus, GitBranch, Tag, Settings } from "lucide-solid";
|
||||||
@@ -96,7 +96,19 @@ export const TaskDetail: Component<TaskDetailProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateTags = (tags: string[]) => {
|
const updateTags = (tags: string[]) => {
|
||||||
updateTask(props.task.id, { tags });
|
const ctx = currentTaskContext();
|
||||||
|
let newTags = [...tags];
|
||||||
|
|
||||||
|
// If in a bucket view, ensure the bucket tag is preserved (it was hidden from the UI so it's missing from 'tags')
|
||||||
|
if (typeof ctx === 'object' && 'bucketId' in ctx) {
|
||||||
|
const bucketName = (ctx as { name: string }).name;
|
||||||
|
// Check case-insensitive existence
|
||||||
|
if (!newTags.some(t => t.toLowerCase() === bucketName.toLowerCase())) {
|
||||||
|
newTags.push(bucketName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTask(props.task.id, { tags: newTags });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDateInputChange = (e: Event) => {
|
const onDateInputChange = (e: Event) => {
|
||||||
@@ -110,6 +122,20 @@ export const TaskDetail: Component<TaskDetailProps> = (props) => {
|
|||||||
// Calculate current urgency level for display
|
// Calculate current urgency level for display
|
||||||
const currentUrgency = () => calculateUrgencyFromDate(props.task.dueDate).toString();
|
const currentUrgency = () => calculateUrgencyFromDate(props.task.dueDate).toString();
|
||||||
|
|
||||||
|
// Filter out bucket tags if we are currently INSIDE that bucket view
|
||||||
|
const visibleTags = createMemo(() => {
|
||||||
|
const tags = props.task.tags || [];
|
||||||
|
const ctx = currentTaskContext();
|
||||||
|
|
||||||
|
if (typeof ctx === 'object' && 'bucketId' in ctx) {
|
||||||
|
// We are in a bucket view. Hide the tag that matches this bucket's name.
|
||||||
|
const bucketName = (ctx as { name: string }).name.toLowerCase();
|
||||||
|
return tags.filter(t => t.toLowerCase() !== bucketName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags;
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sheet open={props.isOpen} onOpenChange={(open) => {
|
<Sheet open={props.isOpen} onOpenChange={(open) => {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
@@ -333,11 +359,11 @@ export const TaskDetail: Component<TaskDetailProps> = (props) => {
|
|||||||
<span class="text-[0.625rem] uppercase font-bold tracking-wider text-muted-foreground/60 shrink-0">Tags</span>
|
<span class="text-[0.625rem] uppercase font-bold tracking-wider text-muted-foreground/60 shrink-0">Tags</span>
|
||||||
<div class="flex flex-wrap items-center gap-1.5 min-w-0 flex-1">
|
<div class="flex flex-wrap items-center gap-1.5 min-w-0 flex-1">
|
||||||
<TagPicker
|
<TagPicker
|
||||||
selectedTags={props.task.tags || []}
|
selectedTags={visibleTags()}
|
||||||
onTagsChange={updateTags}
|
onTagsChange={updateTags}
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-wrap items-center gap-1.5">
|
<div class="flex flex-wrap items-center gap-1.5">
|
||||||
<For each={props.task.tags || []}>
|
<For each={visibleTags()}>
|
||||||
{(tag) => (
|
{(tag) => (
|
||||||
<Badge
|
<Badge
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
@@ -358,7 +384,7 @@ export const TaskDetail: Component<TaskDetailProps> = (props) => {
|
|||||||
return getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()) || "inherit";
|
return getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()) || "inherit";
|
||||||
})()
|
})()
|
||||||
}}
|
}}
|
||||||
onClick={() => updateTags((props.task.tags || []).filter(t => t !== tag))}
|
onClick={() => updateTags(visibleTags().filter(t => t !== tag))}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="w-1.5 h-1.5 rounded-full"
|
class="w-1.5 h-1.5 rounded-full"
|
||||||
|
|||||||
Reference in New Issue
Block a user