tag prefixing migration and improvements

This commit is contained in:
2026-02-27 10:53:02 -06:00
parent 3826a2cbe2
commit 2abf4ae5a9
4 changed files with 13 additions and 44 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
import { type Component, createSignal, For, Show } from "solid-js";
import { Search, X, Hash, ArrowUpCircle, Clock, Minus, Save, Trash2, Bookmark } from "lucide-solid";
import { Search, X, Tag, ArrowUpCircle, Clock, Minus, Save, Trash2, Bookmark } from "lucide-solid";
import { store, setFilter, clearFilter, saveFilterTemplate, applyFilterTemplate, removeFilterTemplate, loadAllHistory } from "@/store";
import { Button } from "./ui/button";
import { cn } from "@/lib/utils";
@@ -235,7 +235,7 @@ export const FilterBar: Component = () => {
setFilter({ tags: newTags });
}}
>
<Show when={tag.excluded} fallback={<Hash size={12} class="opacity-70" />}>
<Show when={tag.excluded} fallback={<Tag size={12} class="opacity-70" />}>
<Minus size={12} class="opacity-100" />
</Show>
<span class={cn(tag.excluded && "line-through opacity-70")}>{tag.name}</span>
+2 -2
View File
@@ -3,7 +3,7 @@ import { store, upsertTagDefinition } from "@/store";
import { useTheme } from "./ThemeProvider";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Button } from "@/components/ui/button";
import { Hash, Plus } from "lucide-solid";
import { Tag, Plus } from "lucide-solid";
interface TagPickerProps {
selectedTags: string[];
@@ -71,7 +71,7 @@ export const TagPicker: Component<TagPickerProps> = (props) => {
return (
<Popover open={open()} onOpenChange={setOpen}>
<PopoverTrigger as={Button} variant="ghost" size="sm" class="h-8 border-dashed flex gap-2 items-center text-muted-foreground hover:text-foreground">
<Hash size={14} />
<Tag size={14} />
<span>Add Tag</span>
</PopoverTrigger>
<PopoverContent class="p-3 w-[min(calc(100vw-2rem),280px)] flex flex-col gap-3" align="start">
+1 -32
View File
@@ -2,14 +2,12 @@ import { type Component, createSignal, createMemo, For, Show } from "solid-js";
import { store, renameTagDefinition } from "@/store";
import { pb } from "@/lib/pocketbase";
import { type Note } from "@/store";
import { Trash2, Lock, Unlock, Hash, StickyNote, Search, X, Link } from "lucide-solid";
import { Trash2, Lock, Unlock, StickyNote, Search, Link, X } from "lucide-solid";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { TaskEditor } from "@/components/TaskEditor";
import { TagPicker } from "@/components/TagPicker";
import { NOTES_COLLECTION } from "@/lib/constants";
import { TaskCard } from "@/components/TaskCard";
import { Badge } from "@/components/ui/badge";
import { NotesSidebar } from "@/components/NotesSidebar";
export const NotepadView: Component<{
@@ -147,7 +145,6 @@ export const NotepadView: Component<{
)}>
<Show when={activeNote()} fallback={
<div class="text-center space-y-4 opacity-40 select-none">
<StickyNote size={64} class="mx-auto" />
<p class="text-sm font-medium tracking-wide">Select a note or create a new one</p>
</div>
}>
@@ -203,34 +200,6 @@ export const NotepadView: Component<{
</div>
</div>
{/* Tags */}
<div class="flex flex-wrap gap-2 items-center">
<For each={note().tags}>
{(tag) => (
<Badge variant="secondary" class="h-6 px-2 text-xs bg-muted/30 border border-border/50 text-foreground shadow-sm">
{tag}
<Show when={canEdit}>
<button
class="ml-2 hover:text-destructive transition-colors shrink-0 outline-none"
onClick={() => {
handleUpdateNote(note().id, { tags: note().tags.filter(t => t !== tag) });
}}
>
<X size={10} />
</button>
</Show>
</Badge>
)}
</For>
<Show when={canEdit}>
<div class="opacity-70 hover:opacity-100 transition-opacity">
<TagPicker
selectedTags={note().tags || []}
onTagsChange={(newTags) => handleUpdateNote(note().id, { tags: newTags })}
/>
</div>
</Show>
</div>
</div>
{/* Editor Instance */}
+8 -8
View File
@@ -2,7 +2,7 @@ import { type Component, For, Show, createSignal, lazy, Suspense } from "solid-j
import { ThemeToggle } from "../components/ThemeToggle";
import { store, restoreTask, deleteTaskPermanently, addTemplate, removeTemplate, updateTemplate, upsertTagDefinition, removeTagDefinition, renameTagDefinition, addShareRule, removeShareRule, updateShareRule, createBucket, updateBucket, deleteBucket, toggleBucketSubscription } from "@/store";
import { useTheme } from "@/components/ThemeProvider";
import { Trash2, Undo2, ArrowLeftRight, Hash, Copy, Plus, ChevronDown, ChevronRight, Type, Share2, Users, Tag, Upload, Box, Edit2, Archive, HelpCircle } from "lucide-solid";
import { Trash2, Undo2, ArrowLeftRight, Tag, ChevronDown, ChevronRight, Share2, Users, HelpCircle, Copy, Plus, Type, Upload, Box, Edit2, Archive } from "lucide-solid";
import { TagPicker } from "@/components/TagPicker";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
@@ -268,7 +268,7 @@ export const SettingsView: Component<{ setView?: (v: string) => void }> = (props
<div class="min-w-0">
<p class="text-xs font-bold truncate">To: {getUserName(rule.targetUserId)}</p>
<p class="text-[0.5625rem] text-muted-foreground uppercase font-black tracking-widest opacity-60 flex items-center gap-1">
{rule.type === 'all' ? 'All Tasks' : `Tag: ${rule.tagName}`}
{rule.type === 'all' ? 'All Tasks' : `Tag: ${rule.tagName} `}
<span class="mx-1">•</span>
<button
class="hover:text-primary transition-colors hover:underline"
@@ -320,7 +320,7 @@ export const SettingsView: Component<{ setView?: (v: string) => void }> = (props
<div class="min-w-0">
<p class="text-xs font-bold truncate">From: {getUserName(rule.ownerId)}</p>
<p class="text-[0.5625rem] text-muted-foreground uppercase font-black tracking-widest opacity-60 flex items-center gap-1">
{rule.type === 'all' ? 'All Tasks' : `Tag: ${rule.tagName}`}
{rule.type === 'all' ? 'All Tasks' : `Tag: ${rule.tagName} `}
<span class="mx-1">•</span>
{rule.share_mode === 'SEND_TASK' ? 'HANDOFF' : 'COLLAB'}
</p>
@@ -528,7 +528,7 @@ export const SettingsView: Component<{ setView?: (v: string) => void }> = (props
size="icon"
class="h-8 w-8 text-muted-foreground hover:text-destructive hover:bg-destructive/10 opacity-0 group-hover/bucket:opacity-100 transition-opacity"
onClick={() => {
if (confirm(`Delete the bucket "${bucket.name}"? This cannot be undone and will affect all users.`)) {
if (confirm(`Delete the bucket "${bucket.name}" ? This cannot be undone and will affect all users.`)) {
deleteBucket(bucket.id);
}
}}
@@ -889,7 +889,7 @@ export const SettingsView: Component<{ setView?: (v: string) => void }> = (props
>
<div class="space-y-0.5">
<h3 class="text-base font-semibold flex items-center gap-2">
<Hash size={16} />
<Tag size={16} />
Global Tags
</h3>
<p class="text-sm text-muted-foreground">Manage your global tag definitions and values.</p>
@@ -902,7 +902,7 @@ export const SettingsView: Component<{ setView?: (v: string) => void }> = (props
<Show when={isTagsOpen()}>
<div class="space-y-2 pt-2 animate-in fade-in slide-in-from-top-2 duration-300">
<div class="space-y-2">
<For each={store.tagDefinitions.filter(t => !t.isUser && !store.buckets.some(b => b.name === t.name)).sort((a, b) => a.name.localeCompare(b.name))} fallback={
<For each={store.tagDefinitions.filter(t => !t.isUser && !t.name.startsWith('#') && !store.buckets.some(b => b.name === t.name || `@${b.name} ` === t.name)).sort((a, b) => a.name.localeCompare(b.name))} fallback={
<div class="text-center py-8 text-muted-foreground text-sm italic border border-dashed border-border rounded-xl">
No custom tags found.
</div>
@@ -988,10 +988,10 @@ export const SettingsView: Component<{ setView?: (v: string) => void }> = (props
<div class="bg-muted/50 p-1 rounded-md group-open/details:rotate-90 transition-transform">
<ChevronRight size={12} />
</div>
System Tags (Users & Buckets)
System Tags (Users, Buckets & Notes)
</summary>
<div class="pt-2 pl-4 space-y-2 animate-in fade-in slide-in-from-top-1 duration-200">
<For each={store.tagDefinitions.filter(t => t.isUser || store.buckets.some(b => b.name === t.name)).sort((a, b) => a.name.localeCompare(b.name))}>
<For each={store.tagDefinitions.filter(t => t.isUser || t.name.startsWith('#') || store.buckets.some(b => b.name === t.name || `@${b.name}` === t.name)).sort((a, b) => a.name.localeCompare(b.name))}>
{(tag) => (
<div class="flex flex-col sm:flex-row sm:items-center justify-between p-2 rounded-lg bg-muted/10 border border-border/20 gap-3 sm:gap-4 group opacity-80">
<div class="flex items-center gap-3 min-w-0 flex-1">