added task size management features and fixed trash recover feature
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { type Component, createSignal, createEffect, onCleanup, onMount, For, Show } from "solid-js";
|
||||
import { addTask, calculateDateFromUrgency, calculateUrgencyFromDate, store, upsertTagDefinition, type TaskTemplate } from "@/store";
|
||||
import { Search, Command, Clock, ArrowUpCircle, Plus, Copy, ChevronDown } from "lucide-solid";
|
||||
import { Search, Command, Clock, ArrowUpCircle, Plus, 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";
|
||||
@@ -11,7 +11,7 @@ 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 } from "@/lib/constants";
|
||||
import { PRIORITY_OPTIONS, URGENCY_OPTIONS, SIZE_OPTIONS } from "@/lib/constants";
|
||||
|
||||
const TaskEditor = lazy(() => import("./TaskEditor").then(m => ({ default: m.TaskEditor })));
|
||||
|
||||
@@ -28,6 +28,7 @@ export const QuickEntry: Component = () => {
|
||||
const [tags, setTags] = createSignal<string[]>([]);
|
||||
const [description, setDescription] = createSignal("");
|
||||
const [editorInstance, setEditorInstance] = createSignal<any>(null);
|
||||
const [size, setSize] = createSignal<string>("5");
|
||||
|
||||
// Reactive shorthand parsing
|
||||
createEffect(() => {
|
||||
@@ -205,7 +206,8 @@ export const QuickEntry: Component = () => {
|
||||
priority: finalPriority,
|
||||
dueDate: finalDueDate || undefined,
|
||||
tags: finalTags,
|
||||
content: description()
|
||||
content: description(),
|
||||
size: parseInt(size())
|
||||
});
|
||||
|
||||
setInput("");
|
||||
@@ -213,6 +215,7 @@ export const QuickEntry: Component = () => {
|
||||
setUrgency("5");
|
||||
setTags([]);
|
||||
setDescription("");
|
||||
setSize("5");
|
||||
lastParsedTags = [];
|
||||
setDateString("");
|
||||
setIsOpen(false);
|
||||
@@ -275,6 +278,7 @@ export const QuickEntry: Component = () => {
|
||||
</TextField>
|
||||
</div>
|
||||
|
||||
{/* Row 1: Priority, Size, Urgency */}
|
||||
<div class="flex flex-col md:flex-row items-stretch md:items-center p-4 gap-4 border-b border-border bg-muted/10">
|
||||
<div class="flex-1 space-y-1.5">
|
||||
<label class="text-[10px] font-bold uppercase tracking-wider text-muted-foreground ml-1">Priority</label>
|
||||
@@ -306,6 +310,36 @@ export const QuickEntry: Component = () => {
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 space-y-1.5">
|
||||
<label class="text-[10px] font-bold uppercase tracking-wider text-muted-foreground ml-1">Size</label>
|
||||
<Select<any>
|
||||
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) => (
|
||||
<SelectItem item={props.item}>
|
||||
{props.item.rawValue.label}
|
||||
</SelectItem>
|
||||
)}
|
||||
>
|
||||
<SelectTrigger class="w-full bg-background border-none shadow-sm h-10 px-3 overflow-hidden">
|
||||
<div class="flex items-center gap-2 truncate">
|
||||
<Gauge size={14} class="text-primary shrink-0" />
|
||||
<span class="text-sm font-medium truncate">
|
||||
{SIZE_OPTIONS.find(o => o.value === size())?.label || size()}
|
||||
</span>
|
||||
</div>
|
||||
</SelectTrigger>
|
||||
<SelectContent />
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 space-y-1.5">
|
||||
<label class="text-[10px] font-bold uppercase tracking-wider text-muted-foreground ml-1">Urgency (Time)</label>
|
||||
<Select<any>
|
||||
@@ -332,8 +366,48 @@ export const QuickEntry: Component = () => {
|
||||
<SelectContent />
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 space-y-1.5">
|
||||
{/* Row 2: Tags + Due Date */}
|
||||
<div class="flex flex-col md:flex-row items-stretch md:items-center px-4 py-3 gap-4 bg-muted/10 border-b border-border">
|
||||
<div class="flex-1 flex flex-wrap gap-2 items-center">
|
||||
<TagPicker selectedTags={tags()} onTagsChange={setTags} />
|
||||
<For each={tags()}>
|
||||
{(tag) => (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
class="h-7 px-2 text-xs gap-1 bg-background border border-border/50 cursor-pointer hover:bg-destructive/10 hover:border-destructive/30 group/tag transition-all"
|
||||
style={{
|
||||
"background-color": (() => {
|
||||
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))}
|
||||
>
|
||||
<div
|
||||
class="w-1.5 h-1.5 rounded-full transition-colors group-hover/tag:bg-destructive"
|
||||
style={{
|
||||
"background-color": (store.tagDefinitions.find(d => d.name === tag)?.value ?? 5) > 5 ? "#22c55e" : (store.tagDefinitions.find(d => d.name === tag)?.value ?? 5) < 5 ? "#ef4444" : "#94a3b8"
|
||||
}}
|
||||
/>
|
||||
{tag}
|
||||
</Badge>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto md:min-w-[200px] space-y-1.5">
|
||||
<label class="text-[10px] font-bold uppercase tracking-wider text-muted-foreground ml-1">Due Date</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
@@ -353,43 +427,6 @@ export const QuickEntry: Component = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-4 pb-4 bg-muted/10 border-b border-border flex flex-wrap gap-2 items-center">
|
||||
<TagPicker selectedTags={tags()} onTagsChange={setTags} />
|
||||
<For each={tags()}>
|
||||
{(tag) => (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
class="h-7 px-2 text-xs gap-1 bg-background border border-border/50 cursor-pointer hover:bg-destructive/10 hover:border-destructive/30 group/tag transition-all"
|
||||
style={{
|
||||
"background-color": (() => {
|
||||
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))}
|
||||
>
|
||||
<div
|
||||
class="w-1.5 h-1.5 rounded-full transition-colors group-hover/tag:bg-destructive"
|
||||
style={{
|
||||
"background-color": (store.tagDefinitions.find(d => d.name === tag)?.value ?? 5) > 5 ? "#22c55e" : (store.tagDefinitions.find(d => d.name === tag)?.value ?? 5) < 5 ? "#ef4444" : "#94a3b8"
|
||||
}}
|
||||
/>
|
||||
{tag}
|
||||
</Badge>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
|
||||
<div class="px-4 py-3 bg-card border-b border-border min-h-[120px] max-h-[300px] overflow-y-auto">
|
||||
<Suspense fallback={<div class="h-20 animate-pulse bg-muted/20 rounded-lg" />}>
|
||||
<TaskEditor
|
||||
|
||||
Reference in New Issue
Block a user