diff --git a/src/App.tsx b/src/App.tsx index 96266e7..f03cfd9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,8 +16,8 @@ const SnowballView = lazy(() => import('./views/SnowballView').then(m => ({ defa const DigInView = lazy(() => import('./views/DigInView').then(m => ({ default: m.DigInView }))); const ProgressView = lazy(() => import('./views/ProgressView').then(m => ({ default: m.ProgressView }))); const HelpView = lazy(() => import('./views/HelpView').then(m => ({ default: m.HelpView }))); -const EmbedNotesView = lazy(() => import('./views/EmbedNotesView').then(m => ({ default: m.EmbedNotesView }))); -const EmbedQuickAddView = lazy(() => import('./views/EmbedQuickAddView').then(m => ({ default: m.EmbedQuickAddView }))); +const NotepadView = lazy(() => import('./views/NotepadView').then(m => ({ default: m.NotepadView }))); +const QuickEntryForm = lazy(() => import('./components/QuickEntryForm').then(m => ({ default: m.QuickEntryForm }))); const App: Component = () => { // Basic routing state @@ -99,11 +99,14 @@ const App: Component = () => {
}> - - + + {(() => { + const [selectedNoteId, setSelectedNoteId] = createSignal(null); + return ; + })()} - - + +
diff --git a/src/components/QuickEntry.tsx b/src/components/QuickEntry.tsx index fc63999..f70c768 100644 --- a/src/components/QuickEntry.tsx +++ b/src/components/QuickEntry.tsx @@ -1,39 +1,18 @@ -import { type Component, createSignal, createEffect, onCleanup, onMount, For, Show } from "solid-js"; -import { addTask, calculateDateFromUrgency, calculateUrgencyFromDate, store, upsertTagDefinition, currentTaskContext, type TaskTemplate } from "@/store"; -import { Search, Command, Clock, ArrowUpCircle, Plus, Copy, ChevronDown, Gauge, X } from "lucide-solid"; +import { type Component, createSignal, createEffect, onCleanup, onMount } from "solid-js"; +import { store, currentTaskContext } from "@/store"; +import { Plus, X } from "lucide-solid"; import { Button } from "@/components/ui/button"; -import { Badge } from "@/components/ui/badge"; -import { TextField, TextFieldInput } from "@/components/ui/textfield"; -import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select"; -import { TagPicker } from "@/components/TagPicker"; -import { useTheme } from "./ThemeProvider"; -import { getThemeAdjustedColor } from "@/lib/colors"; -import { lazy, Suspense } from "solid-js"; -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { toast } from "solid-sonner"; -import { PRIORITY_OPTIONS, URGENCY_OPTIONS, SIZE_OPTIONS } from "@/lib/constants"; import { cn } from "@/lib/utils"; - -const TaskEditor = lazy(() => import("./TaskEditor").then(m => ({ default: m.TaskEditor }))); +import { QuickEntryForm } from "./QuickEntryForm"; export const QuickEntry: Component = () => { - const { resolvedTheme } = useTheme(); - // Diff tracking for reactive tags - let lastParsedTags: string[] = []; - const [isOpen, setIsOpen] = createSignal(false); - const [input, setInput] = createSignal(""); - - const [priority, setPriority] = createSignal(""); - const [urgency, setUrgency] = createSignal(""); - const [tags, setTags] = createSignal([]); - const [description, setDescription] = createSignal(""); - const [editorInstance, setEditorInstance] = createSignal(null); - const [size, setSize] = createSignal("3"); + const [initialTags, setInitialTags] = createSignal([]); // Autofill tags based on context when opening createEffect(() => { if (isOpen()) { + const tags: string[] = []; const ctx = currentTaskContext(); if (store.isNotepadMode) { // Not in a specific context, but in Notes mode. @@ -43,98 +22,28 @@ export const QuickEntry: Component = () => { const note = store.notes.find(n => n.id === activeNoteId); if (note && note.title) { const noteTagName = note.title.trim(); - if (noteTagName && !tags().includes(noteTagName)) { - setTags(prev => [...prev, noteTagName]); - } + if (noteTagName) tags.push(noteTagName); } } } else if (typeof ctx === 'object') { if ('bucketId' in ctx) { // Bucket View: Autofill bucket name const bucketName = ctx.name; - if (bucketName && !tags().includes(bucketName)) { - setTags(prev => [...prev, bucketName]); - } + if (bucketName) tags.push(bucketName); } else if ('userId' in ctx) { // Shared User View: Autofill user name (assuming name IS the tag for sharing) const userName = ctx.name; - if (userName && !tags().includes(userName)) { - setTags(prev => [...prev, userName]); - } + if (userName) tags.push(userName); } } - } - }); - - // Reactive shorthand parsing - createEffect(() => { - const text = input(); - - const pMatch = text.match(/\/p(\d+)/); - if (pMatch) { - const val = Math.min(10, Math.max(1, parseInt(pMatch[1]))).toString(); - setPriority(val); - } - - const uMatch = text.match(/\/u(\d+)/); - if (uMatch) { - const val = Math.min(10, Math.max(1, parseInt(uMatch[1]))).toString(); - if (val !== urgency()) { - onUrgencySimpleChange(val); - } - } - - // Tag Shortcut Parsing: /t [tag name]:[optional value] - // This is reactive. To avoid "prefix storm", only add if terminated by : or / - // Tag Shortcut Parsing: /t [tag name]:[optional value] - // Regex: /t, name (no : or /), lookahead for terminator (: or /), optional value group - const tagRegex = /\/t\s*([^:/]+)(?=(?:[:/]))(?:[:](\d+))?/g; - - const newParsedTags: string[] = []; - let match; - - while ((match = tagRegex.exec(text)) !== null) { - const tagName = match[1].trim(); - if (tagName) { - newParsedTags.push(tagName); - } - } - - const currentTags = [...tags()]; - // Determine changes - const tagsToRemove = lastParsedTags.filter(t => !newParsedTags.includes(t)); - const tagsToAdd = newParsedTags.filter(t => !currentTags.includes(t)); - - // Apply changes - let nextTags = currentTags.filter(t => !tagsToRemove.includes(t)); - nextTags = [...nextTags, ...tagsToAdd]; - - if (JSON.stringify(nextTags) !== JSON.stringify(currentTags)) { - setTags(nextTags); - } - - lastParsedTags = newParsedTags; - }); - - // Explicit date string for the input (YYYY-MM-DDTHH:mm) - const [dateString, setDateString] = createSignal(""); - let inputRef: HTMLInputElement | undefined; - - createEffect(() => { - if (isOpen()) { - // Slight delay to ensure DOM is ready and animations don't interfere - setTimeout(() => inputRef?.focus(), 50); + setInitialTags(tags); } }); const handleKeyDown = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === "k") { e.preventDefault(); - if (isOpen()) { - inputRef?.focus(); - } else { - setIsOpen(true); - } + setIsOpen(prev => !prev); } if (e.key === "Escape") { setIsOpen(false); @@ -144,145 +53,6 @@ export const QuickEntry: Component = () => { onMount(() => window.addEventListener("keydown", handleKeyDown)); onCleanup(() => window.removeEventListener("keydown", handleKeyDown)); - // When urgency changes via dropdown, update the date - const onUrgencySimpleChange = (val: any) => { - const value = typeof val === 'object' ? val.value : val; - if (!value) return; - - setUrgency(value); - const level = parseInt(value); - if (!isNaN(level)) { - const newDateIso = calculateDateFromUrgency(level); - // Format for datetime-local input: YYYY-MM-DDTHH:mm - const dateObj = new Date(newDateIso); - const localIso = new Date(dateObj.getTime() - (dateObj.getTimezoneOffset() * 60000)).toISOString().slice(0, 16); - setDateString(localIso); - } - }; - - // When date changes via input, update urgency level - const onDateInputChange = (e: Event) => { - const val = (e.currentTarget as HTMLInputElement).value; - setDateString(val); - if (val) { - const level = calculateUrgencyFromDate(new Date(val).toISOString()); - setUrgency(level.toString()); - } - }; - - const parseAndAdd = async () => { - let text = input(); - if (!text || !priority() || !urgency()) return; - - let finalPriority = parseInt(priority()); - let finalUrgency = parseInt(urgency()); - const finalTags = [...tags()]; - - // 1. Handle Priority / Urgency and clean them from text - const pMatch = text.match(/\/p\s*(\d+)/); - if (pMatch) { - finalPriority = Math.min(10, Math.max(1, parseInt(pMatch[1]))); - text = text.replace(pMatch[0], ""); - } - - const uMatch = text.match(/\/u\s*(\d+)/); - if (uMatch) { - finalUrgency = Math.min(10, Math.max(1, parseInt(uMatch[1]))); - text = text.replace(uMatch[0], ""); - } - - // 2. Handle Tags: /t [name]:[value] - const segments = text.split(/\/t\s*/); - let title = segments[0]; - - for (let i = 1; i < segments.length; i++) { - const seg = segments[i]; - const firstColon = seg.indexOf(':'); - let tagName = ""; - let tagValue: number | undefined; - let remainingText = ""; - - if (firstColon === -1) { - tagName = seg.trim(); - } else { - tagName = seg.slice(0, firstColon).trim(); - const afterColon = seg.slice(firstColon + 1); - const valueMatch = afterColon.match(/^\s*(\d+)/); - - if (valueMatch) { - tagValue = Math.min(10, Math.max(0, parseInt(valueMatch[1]))); - remainingText = afterColon.slice(valueMatch[0].length); - } else { - remainingText = afterColon; - } - } - - if (tagName) { - if (!finalTags.includes(tagName)) { - finalTags.push(tagName); - } - if (tagValue !== undefined) { - // update global definition - await upsertTagDefinition(tagName, tagValue, undefined, resolvedTheme()); - } - } - title += " " + remainingText; - } - - let finalDueDate: Date | null = null; - if (dateString()) { - finalDueDate = new Date(dateString()); - } else { - const u = finalUrgency; - const dateStr = calculateDateFromUrgency(u); - finalDueDate = new Date(dateStr); - } - - addTask(title.replace(/\s+/g, " ").trim(), { - priority: finalPriority, - dueDate: finalDueDate || undefined, - tags: finalTags, - content: description(), - size: parseInt(size()) - }); - - setInput(""); - setPriority(""); - setUrgency(""); - setTags([]); - setDescription(""); - setSize("3"); - lastParsedTags = []; - setDateString(""); - setIsOpen(false); - // Reset editor content - const instance = editorInstance(); - if (instance) instance.commands.setContent(""); - }; - - const applyTemplate = (template: TaskTemplate) => { - setInput(template.title); - setPriority(template.priority.toString()); - setUrgency(template.urgency.toString()); - setTags([...(template.tags || [])]); - setDescription(template.content || ""); - - // Sync urgency to date - const level = template.urgency; - const newDateIso = calculateDateFromUrgency(level); - const dateObj = new Date(newDateIso); - const localIso = new Date(dateObj.getTime() - (dateObj.getTimezoneOffset() * 60000)).toISOString().slice(0, 16); - setDateString(localIso); - - // Update editor if ready - const instance = editorInstance(); - if (instance) { - instance.commands.setContent(template.content || ""); - } - - toast.info(`Applied template: ${template.name}`); - }; - return ( <> - - {/* Row 1: Priority, Size, Urgency */} -
-
- - - value={priority()} - onChange={(val) => { - const v = typeof val === 'object' ? val.value : val; - if (v) setPriority(v); - }} - options={PRIORITY_OPTIONS} - optionValue="value" - optionTextValue="label" - placeholder="Priority" - itemComponent={(props) => ( - - {props.item.rawValue.label} - - )} - > - -
- - - {PRIORITY_OPTIONS.find(o => o.value === priority())?.label || (priority() === "" ? "Pick Priority" : priority())} - -
-
- - -
- -
- - - value={size()} - onChange={(val) => { - const v = typeof val === 'object' ? val.value : val; - if (v) setSize(v); - }} - options={SIZE_OPTIONS} - optionValue="value" - optionTextValue="label" - placeholder="Size" - itemComponent={(props) => ( - - {props.item.rawValue.label} - - )} - > - -
- - - {SIZE_OPTIONS.find(o => o.value === size())?.label || size()} - -
-
- - -
- -
- - - value={urgency()} - onChange={(val) => onUrgencySimpleChange(val)} - options={URGENCY_OPTIONS} - optionValue="value" - optionTextValue="label" - placeholder="Urgency" - itemComponent={(props) => ( - - {props.item.rawValue.label} - - )} - > - -
- - - {URGENCY_OPTIONS.find(o => o.value === urgency())?.label || (urgency() === "" ? "Pick Urgency" : urgency())} - -
-
- - -
-
- - {/* Row 2: Tags + Due Date */} -
-
- - - {(tag) => ( - { - const def = store.tagDefinitions.find(d => d.name === tag); - const color = getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()); - return color ? `${color}15` : undefined; - })(), - "border-color": (() => { - const def = store.tagDefinitions.find(d => d.name === tag); - const color = getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()); - return color ? `${color}30` : undefined; - })(), - "color": (() => { - const def = store.tagDefinitions.find(d => d.name === tag); - return getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()) || "inherit"; - })() - }} - onClick={() => setTags(tags().filter(t => t !== tag))} - > -
d.name === tag)?.value ?? 5) > 5 ? "#22c55e" : (store.tagDefinitions.find(d => d.name === tag)?.value ?? 5) < 5 ? "#ef4444" : "#94a3b8" - }} - /> - {tag} - - )} - -
- -
- -
- (e.target as HTMLInputElement).showPicker?.()} - onKeyDown={(e) => { - if (e.key !== "Tab" && e.key !== "Escape") { - e.preventDefault(); - } - }} - step="900" - class="w-full h-10 px-3 bg-background border-none rounded-md text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-ring z-[102] relative cursor-pointer" - /> -
-
-
- -
- }> - - -
+
+
-
-
- - - Enter to Add -
- -
- 0}> - - - - - - - -
- - {(template) => ( - - )} - -
-
-
-
- - -
+
+ setIsOpen(false)} + autoFocus={true} + initialTags={initialTags()} + />
diff --git a/src/components/QuickEntryForm.tsx b/src/components/QuickEntryForm.tsx new file mode 100644 index 0000000..90e93db --- /dev/null +++ b/src/components/QuickEntryForm.tsx @@ -0,0 +1,452 @@ +import { type Component, createSignal, createEffect, onMount, For, Show, Suspense, lazy } from "solid-js"; +import { addTask, calculateDateFromUrgency, calculateUrgencyFromDate, store, upsertTagDefinition, type TaskTemplate } from "@/store"; +import { Search, Command, Clock, ArrowUpCircle, Copy, ChevronDown, Gauge } from "lucide-solid"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { TextField, TextFieldInput } from "@/components/ui/textfield"; +import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select"; +import { TagPicker } from "@/components/TagPicker"; +import { useTheme } from "./ThemeProvider"; +import { getThemeAdjustedColor } from "@/lib/colors"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { toast } from "solid-sonner"; +import { PRIORITY_OPTIONS, URGENCY_OPTIONS, SIZE_OPTIONS } from "@/lib/constants"; + +const TaskEditor = lazy(() => import("./TaskEditor").then(m => ({ default: m.TaskEditor }))); + +interface QuickEntryFormProps { + onSuccess?: () => void; + autoFocus?: boolean; + initialTitle?: string; + initialTags?: string[]; +} + +export const QuickEntryForm: Component = (props) => { + const { resolvedTheme } = useTheme(); + // Diff tracking for reactive tags + let lastParsedTags: string[] = []; + + const [input, setInput] = createSignal(props.initialTitle || ""); + const [priority, setPriority] = createSignal(""); + const [urgency, setUrgency] = createSignal(""); + const [tags, setTags] = createSignal(props.initialTags || []); + const [description, setDescription] = createSignal(""); + const [editorInstance, setEditorInstance] = createSignal(null); + const [size, setSize] = createSignal("3"); + const [dateString, setDateString] = createSignal(""); + + let inputRef: HTMLInputElement | undefined; + + onMount(() => { + if (props.autoFocus) { + setTimeout(() => inputRef?.focus(), 100); + } + }); + + // Reactive shorthand parsing + createEffect(() => { + const text = input(); + + const pMatch = text.match(/\/p(\d+)/); + if (pMatch) { + const val = Math.min(10, Math.max(1, parseInt(pMatch[1]))).toString(); + setPriority(val); + } + + const uMatch = text.match(/\/u(\d+)/); + if (uMatch) { + const val = Math.min(10, Math.max(1, parseInt(uMatch[1]))).toString(); + if (val !== urgency()) { + onUrgencySimpleChange(val); + } + } + + const tagRegex = /\/t\s*([^:/]+)(?=(?:[:/]))(?:[:](\d+))?/g; + const newParsedTags: string[] = []; + let match; + while ((match = tagRegex.exec(text)) !== null) { + const tagName = match[1].trim(); + if (tagName) { + newParsedTags.push(tagName); + } + } + + const currentTags = [...tags()]; + const tagsToRemove = lastParsedTags.filter(t => !newParsedTags.includes(t)); + const tagsToAdd = newParsedTags.filter(t => !currentTags.includes(t)); + + let nextTags = currentTags.filter(t => !tagsToRemove.includes(t)); + nextTags = [...nextTags, ...tagsToAdd]; + + if (JSON.stringify(nextTags) !== JSON.stringify(currentTags)) { + setTags(nextTags); + } + lastParsedTags = newParsedTags; + }); + + const onUrgencySimpleChange = (val: any) => { + const value = typeof val === 'object' ? val.value : val; + if (!value) return; + + setUrgency(value); + const level = parseInt(value); + if (!isNaN(level)) { + const newDateIso = calculateDateFromUrgency(level); + const dateObj = new Date(newDateIso); + const localIso = new Date(dateObj.getTime() - (dateObj.getTimezoneOffset() * 60000)).toISOString().slice(0, 16); + setDateString(localIso); + } + }; + + const onDateInputChange = (e: Event) => { + const val = (e.currentTarget as HTMLInputElement).value; + setDateString(val); + if (val) { + const level = calculateUrgencyFromDate(new Date(val).toISOString()); + setUrgency(level.toString()); + } + }; + + const parseAndAdd = async () => { + let text = input(); + if (!text || !priority() || !urgency()) return; + + let finalPriority = parseInt(priority()); + let finalUrgency = parseInt(urgency()); + const finalTags = [...tags()]; + + const pMatch = text.match(/\/p\s*(\d+)/); + if (pMatch) { + finalPriority = Math.min(10, Math.max(1, parseInt(pMatch[1]))); + text = text.replace(pMatch[0], ""); + } + + const uMatch = text.match(/\/u\s*(\d+)/); + if (uMatch) { + finalUrgency = Math.min(10, Math.max(1, parseInt(uMatch[1]))); + text = text.replace(uMatch[0], ""); + } + + const segments = text.split(/\/t\s*/); + let title = segments[0]; + + for (let i = 1; i < segments.length; i++) { + const seg = segments[i]; + const firstColon = seg.indexOf(':'); + let tagName = ""; + let tagValue: number | undefined; + let remainingText = ""; + + if (firstColon === -1) { + tagName = seg.trim(); + } else { + tagName = seg.slice(0, firstColon).trim(); + const afterColon = seg.slice(firstColon + 1); + const valueMatch = afterColon.match(/^\s*(\d+)/); + + if (valueMatch) { + tagValue = Math.min(10, Math.max(0, parseInt(valueMatch[1]))); + remainingText = afterColon.slice(valueMatch[0].length); + } else { + remainingText = afterColon; + } + } + + if (tagName) { + if (!finalTags.includes(tagName)) { + finalTags.push(tagName); + } + if (tagValue !== undefined) { + await upsertTagDefinition(tagName, tagValue, undefined, resolvedTheme()); + } + } + title += " " + remainingText; + } + + let finalDueDate: Date | null = null; + if (dateString()) { + finalDueDate = new Date(dateString()); + } else { + const u = finalUrgency; + const dateStr = calculateDateFromUrgency(u); + finalDueDate = new Date(dateStr); + } + + addTask(title.replace(/\s+/g, " ").trim(), { + priority: finalPriority, + dueDate: finalDueDate || undefined, + tags: finalTags, + content: description(), + size: parseInt(size()) + }); + + setInput(""); + setPriority(""); + setUrgency(""); + setTags([]); + setDescription(""); + setSize("3"); + lastParsedTags = []; + setDateString(""); + + const instance = editorInstance(); + if (instance) instance.commands.setContent(""); + + if (props.onSuccess) props.onSuccess(); + }; + + const applyTemplate = (template: TaskTemplate) => { + setInput(template.title); + setPriority(template.priority.toString()); + setUrgency(template.urgency.toString()); + setTags([...(template.tags || [])]); + setDescription(template.content || ""); + + const level = template.urgency; + const newDateIso = calculateDateFromUrgency(level); + const dateObj = new Date(newDateIso); + const localIso = new Date(dateObj.getTime() - (dateObj.getTimezoneOffset() * 60000)).toISOString().slice(0, 16); + setDateString(localIso); + + const instance = editorInstance(); + if (instance) { + instance.commands.setContent(template.content || ""); + } + toast.info(`Applied template: ${template.name}`); + }; + + 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" + /> + +
+ +
+
+
+ + + value={priority()} + onChange={(val) => { + const v = typeof val === 'object' ? val.value : val; + if (v) setPriority(v); + }} + options={PRIORITY_OPTIONS} + optionValue="value" + optionTextValue="label" + placeholder="Priority" + itemComponent={(props) => ( + + {props.item.rawValue.label} + + )} + > + +
+ + + {PRIORITY_OPTIONS.find(o => o.value === priority())?.label || (priority() === "" ? "Pick Priority" : priority())} + +
+
+ + +
+ +
+ + + value={size()} + onChange={(val) => { + const v = typeof val === 'object' ? val.value : val; + if (v) setSize(v); + }} + options={SIZE_OPTIONS} + optionValue="value" + optionTextValue="label" + placeholder="Size" + itemComponent={(props) => ( + + {props.item.rawValue.label} + + )} + > + +
+ + + {SIZE_OPTIONS.find(o => o.value === size())?.label || size()} + +
+
+ + +
+ +
+ + + value={urgency()} + onChange={(val) => onUrgencySimpleChange(val)} + options={URGENCY_OPTIONS} + optionValue="value" + optionTextValue="label" + placeholder="Urgency" + itemComponent={(props) => ( + + {props.item.rawValue.label} + + )} + > + +
+ + + {URGENCY_OPTIONS.find(o => o.value === urgency())?.label || (urgency() === "" ? "Pick Urgency" : urgency())} + +
+
+ + +
+
+ +
+
+ + + {(tag) => ( + { + const def = store.tagDefinitions.find(d => d.name === tag); + const color = getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()); + return color ? `${color}15` : undefined; + })(), + "border-color": (() => { + const def = store.tagDefinitions.find(d => d.name === tag); + const color = getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()); + return color ? `${color}30` : undefined; + })(), + "color": (() => { + const def = store.tagDefinitions.find(d => d.name === tag); + return getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()) || "inherit"; + })() + }} + onClick={() => setTags(tags().filter(t => t !== tag))} + > +
d.name === tag)?.value ?? 5) > 5 ? "#22c55e" : (store.tagDefinitions.find(d => d.name === tag)?.value ?? 5) < 5 ? "#ef4444" : "#94a3b8" + }} + /> + {tag} + + )} + +
+ +
+ +
+ (e.target as HTMLInputElement).showPicker?.()} + onKeyDown={(e) => { + if (e.key !== "Tab" && e.key !== "Escape") { + e.preventDefault(); + } + }} + step="900" + class="w-full h-10 px-3 bg-background border-none rounded-md text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-ring relative cursor-pointer" + /> +
+
+
+ +
+ }> + + +
+
+ +
+
+ + + Enter to Add +
+ +
+ 0}> + + + + + + + +
+ + {(template) => ( + + )} + +
+
+
+
+ + +
+
+
+ ); +}; diff --git a/src/views/EmbedNotesView.tsx b/src/views/EmbedNotesView.tsx deleted file mode 100644 index a5a1ecb..0000000 --- a/src/views/EmbedNotesView.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { type Component, createSignal } from "solid-js"; -import { NotepadView } from "./NotepadView"; - -export const EmbedNotesView: Component = () => { - const [selectedNoteId, setSelectedNoteId] = createSignal(null); - - return ( -
- -
- ); -}; diff --git a/src/views/EmbedQuickAddView.tsx b/src/views/EmbedQuickAddView.tsx deleted file mode 100644 index 4bbe3dc..0000000 --- a/src/views/EmbedQuickAddView.tsx +++ /dev/null @@ -1,287 +0,0 @@ -import { type Component, createSignal, onMount, For, Show } from "solid-js"; -import { addTask, calculateDateFromUrgency, calculateUrgencyFromDate, store, type TaskTemplate } from "@/store"; -import { Search, Clock, ArrowUpCircle, Copy, ChevronDown, Gauge } from "lucide-solid"; -import { Button } from "@/components/ui/button"; -import { Badge } from "@/components/ui/badge"; -import { TextField, TextFieldInput } from "@/components/ui/textfield"; -import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select"; -import { TagPicker } from "@/components/TagPicker"; -import { useTheme } from "@/components/ThemeProvider"; -import { getThemeAdjustedColor } from "@/lib/colors"; -import { lazy, Suspense } from "solid-js"; -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { toast } from "solid-sonner"; -import { PRIORITY_OPTIONS, URGENCY_OPTIONS, SIZE_OPTIONS } from "@/lib/constants"; - -const TaskEditor = lazy(() => import("@/components/TaskEditor").then(m => ({ default: m.TaskEditor }))); - -export const EmbedQuickAddView: Component = () => { - const { resolvedTheme } = useTheme(); - - const [input, setInput] = createSignal(""); - const [priority, setPriority] = createSignal(""); - const [urgency, setUrgency] = createSignal(""); - const [tags, setTags] = createSignal([]); - const [description, setDescription] = createSignal(""); - const [editorInstance, setEditorInstance] = createSignal(null); - const [size, setSize] = createSignal("3"); - const [dateString, setDateString] = createSignal(""); - - let inputRef: HTMLInputElement | undefined; - - onMount(() => { - setTimeout(() => inputRef?.focus(), 100); - }); - - const onUrgencySimpleChange = (val: any) => { - const value = typeof val === 'object' ? val.value : val; - if (!value) return; - - setUrgency(value); - const level = parseInt(value); - if (!isNaN(level)) { - const newDateIso = calculateDateFromUrgency(level); - const dateObj = new Date(newDateIso); - const localIso = new Date(dateObj.getTime() - (dateObj.getTimezoneOffset() * 60000)).toISOString().slice(0, 16); - setDateString(localIso); - } - }; - - const onDateInputChange = (e: Event) => { - const val = (e.currentTarget as HTMLInputElement).value; - setDateString(val); - if (val) { - const level = calculateUrgencyFromDate(new Date(val).toISOString()); - setUrgency(level.toString()); - } - }; - - const handleCreateTask = async () => { - let text = input(); - if (!text || !priority() || !urgency()) return; - - let finalPriority = parseInt(priority()); - let finalUrgency = parseInt(urgency()); - const finalTags = [...tags()]; - - let finalDueDate: Date | null = null; - if (dateString()) { - finalDueDate = new Date(dateString()); - } else { - const dateStr = calculateDateFromUrgency(finalUrgency); - finalDueDate = new Date(dateStr); - } - - await addTask(text.trim(), { - priority: finalPriority, - dueDate: finalDueDate || undefined, - tags: finalTags, - content: description(), - size: parseInt(size()) - }); - - toast.success("Task created successfully"); - - // Reset - setInput(""); - setPriority("5"); - setUrgency("5"); - setTags([]); - setDescription(""); - setSize("3"); - setDateString(""); - const instance = editorInstance(); - if (instance) instance.commands.setContent(""); - }; - - const applyTemplate = (template: TaskTemplate) => { - setInput(template.title); - setPriority(template.priority.toString()); - setUrgency(template.urgency.toString()); - setTags([...(template.tags || [])]); - setDescription(template.content || ""); - - const level = template.urgency; - const newDateIso = calculateDateFromUrgency(level); - const dateObj = new Date(newDateIso); - const localIso = new Date(dateObj.getTime() - (dateObj.getTimezoneOffset() * 60000)).toISOString().slice(0, 16); - setDateString(localIso); - - const instance = editorInstance(); - if (instance) instance.commands.setContent(template.content || ""); - toast.info(`Applied template: ${template.name}`); - }; - - return ( -
-
-
- - - setInput(e.currentTarget.value)} - onKeyDown={(e: any) => e.key === "Enter" && handleCreateTask()} - placeholder="Task title..." - class="border-none shadow-none focus-visible:ring-0 text-lg" - /> - -
- -
-
- - - value={priority()} - onChange={(val) => { - const v = typeof val === 'object' ? val.value : val; - if (v) setPriority(v); - }} - options={PRIORITY_OPTIONS} - optionValue="value" - optionTextValue="label" - placeholder="Priority" - itemComponent={(props) => {props.item.rawValue.label}} - > - -
- - {PRIORITY_OPTIONS.find(o => o.value === priority())?.label || priority()} -
-
- - -
- -
- - - value={size()} - onChange={(val) => { - const v = typeof val === 'object' ? val.value : val; - if (v) setSize(v); - }} - options={SIZE_OPTIONS} - optionValue="value" - optionTextValue="label" - placeholder="Size" - itemComponent={(props) => {props.item.rawValue.label}} - > - -
- - {SIZE_OPTIONS.find(o => o.value === size())?.label || size()} -
-
- - -
- -
- - - value={urgency()} - onChange={(val) => onUrgencySimpleChange(val)} - options={URGENCY_OPTIONS} - optionValue="value" - optionTextValue="label" - placeholder="Urgency" - itemComponent={(props) => {props.item.rawValue.label}} - > - -
- - {URGENCY_OPTIONS.find(o => o.value === urgency())?.label || urgency()} -
-
- - -
-
- -
-
- - - {(tag) => ( - { - const def = store.tagDefinitions.find(d => d.name === tag); - const color = getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()); - return color ? `${color}15` : undefined; - })(), - "color": (() => { - const def = store.tagDefinitions.find(d => d.name === tag); - return getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()) || "inherit"; - })() - }} - onClick={() => setTags(tags().filter(t => t !== tag))} - > - {tag} - - )} - -
-
- - -
-
- -
- }> - - -
-
- -
- 0}> - - - - Templates - - - -
- - {(template) => ( - - )} - -
-
-
-
- -
-
- ); -};