From 4259649fec0f0035cdacf516de9ac0e8371db68b Mon Sep 17 00:00:00 2001 From: Timothy Cardoza Date: Thu, 19 Mar 2026 10:14:23 -0500 Subject: [PATCH] task bucket fixes and task creation clarity --- src/components/QuickEntryForm.tsx | 77 ++++++++++++++++++++++--------- src/store/index.ts | 24 +++++++++- 2 files changed, 77 insertions(+), 24 deletions(-) diff --git a/src/components/QuickEntryForm.tsx b/src/components/QuickEntryForm.tsx index 9c9eed3..868bd76 100644 --- a/src/components/QuickEntryForm.tsx +++ b/src/components/QuickEntryForm.tsx @@ -3,6 +3,7 @@ import { addTask, calculateDateFromUrgency, calculateUrgencyFromDate, store, ups import { Search, Command, Clock, ArrowUpCircle, Copy, ChevronDown, Gauge, RotateCcw } from "lucide-solid"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; +import { cn } from "@/lib/utils"; import { TextField, TextFieldInput } from "@/components/ui/textfield"; import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select"; import { TagPicker } from "@/components/TagPicker"; @@ -29,6 +30,7 @@ const [tags, setTags] = createSignal([]); const [description, setDescription] = createSignal(""); const [size, setSize] = createSignal("3"); const [dateString, setDateString] = createSignal(""); +const [showValidation, setShowValidation] = createSignal(false); let lastParsedTags: string[] = []; @@ -40,6 +42,7 @@ const resetQuickEntryState = () => { setDescription(""); setSize("3"); setDateString(""); + setShowValidation(false); lastParsedTags = []; }; @@ -140,7 +143,12 @@ export const QuickEntryForm: Component = (props) => { const parseAndAdd = async () => { let text = input(); - if (!text || !priority() || !urgency()) return; + if (!text || !priority() || !urgency()) { + setShowValidation(true); + toast.error("Please fill in all required fields (Title, Priority, Urgency)"); + if (!text) inputRef?.focus(); + return; + } let finalPriority = parseInt(priority()); let finalUrgency = parseInt(urgency()); @@ -268,20 +276,33 @@ export const QuickEntryForm: Component = (props) => { return (
-
- - - setInput(e.currentTarget.value)} - onKeyDown={(e) => e.key === "Enter" && parseAndAdd()} - placeholder="Task title... type /p1-10 for priority and /u1-10 for urgency" - class="border-none shadow-none focus-visible:ring-0 text-lg" - /> - +
+
+ + + setInput(e.currentTarget.value)} + onKeyDown={(e) => e.key === "Enter" && parseAndAdd()} + placeholder="Task title... type /p1-10 for priority and /u1-10 for urgency" + class="border-none shadow-none focus-visible:ring-0 text-lg p-0" + /> + +
+ +
+
+ + Title is required to create a task + +
+
@@ -305,11 +326,17 @@ export const QuickEntryForm: Component = (props) => { {props.item.rawValue.label} )} + validationState={showValidation() && !priority() ? "invalid" : "valid"} > - +
- - + + {PRIORITY_OPTIONS.find(o => o.value === priority())?.label || (priority() === "" ? "Pick Priority" : priority())}
@@ -364,11 +391,17 @@ export const QuickEntryForm: Component = (props) => { {props.item.rawValue.label} )} + validationState={showValidation() && !urgency() ? "invalid" : "valid"} > - +
- - + + {URGENCY_OPTIONS.find(o => o.value === urgency())?.label || (urgency() === "" ? "Pick Urgency" : urgency())}
@@ -505,7 +538,7 @@ export const QuickEntryForm: Component = (props) => { -
diff --git a/src/store/index.ts b/src/store/index.ts index af8af78..f41aacb 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -346,8 +346,7 @@ export const matchesFilter = (task: Task) => { // EXCLUDE Buckets from the main "Mine" view // The user wants to see "Direct Shares" (User Tags), not "Bucket Subscriptions" here. - const isBucket = store.buckets.some(b => b.name.toLowerCase() === tagName.toLowerCase()); - if (isBucket) return false; + if (isBucketTag(tagName)) return false; return task.tags?.some(t => t.toLowerCase() === tagName.toLowerCase()); } @@ -755,6 +754,7 @@ const shouldTaskBeVisible = (task: any, currentUserId: string): boolean => { // We ignore the rule.ownerId check (which would match the task.user) // and instead match purely on the tag. if (rule.ownerId === rule.targetUserId && rule.type === 'tag') { + if (isBucketTag(rule.tagName || "")) return false; const tags: string[] = task.tags || []; return tags.some(t => t.toLowerCase() === (rule.tagName || "").toLowerCase()); } @@ -787,6 +787,26 @@ const isTaskInSubscribedBucket = (task: any): boolean => { }); }; +// Helper: detect bucket tags robustly (case-insensitive, with or without '@') +const isBucketTag = (tagName: string): boolean => { + const raw = (tagName || "").trim().toLowerCase(); + if (!raw) return false; + const normalized = raw.startsWith("@") ? raw.slice(1) : raw; + + const bucketNameMatch = store.buckets.some(b => { + const bName = (b.name || "").trim().toLowerCase(); + return normalized === bName || raw === `@${bName}`; + }); + if (bucketNameMatch) return true; + + const bucketTagDefMatch = store.tagDefinitions.some(d => { + if (!d.isBucket) return false; + const dName = (d.name || "").trim().toLowerCase(); + return raw === dName || normalized === dName || raw === `@${dName}`; + }); + return bucketTagDefMatch; +}; + export const subscribeToRealtime = async () => { // Unsubscribe first to avoid duplicates await pb.collection(TASGRID_COLLECTION).unsubscribe('*');