added tags and tag manager
This commit is contained in:
+132
-2
@@ -1,4 +1,4 @@
|
||||
import { createStore, reconcile } from "solid-js/store";
|
||||
import { createStore, reconcile, produce } from "solid-js/store";
|
||||
import { createSignal, createEffect, createRoot } from "solid-js";
|
||||
import { pb, TASGRID_COLLECTION } from "@/lib/pocketbase";
|
||||
import { toast } from "solid-sonner";
|
||||
@@ -31,6 +31,7 @@ interface TaskStore {
|
||||
uWeight: number;
|
||||
matrixScaleDays: number;
|
||||
prefId?: string; // ID of the user_preferences record
|
||||
tagDefinitions?: Record<string, number>;
|
||||
}
|
||||
|
||||
// Initial empty state
|
||||
@@ -39,6 +40,7 @@ export const [store, setStore] = createStore<TaskStore>({
|
||||
pWeight: 1.0,
|
||||
uWeight: 1.0,
|
||||
matrixScaleDays: 30,
|
||||
tagDefinitions: {}, // Map<Name, Value 0-10>
|
||||
});
|
||||
|
||||
// Auto-persist changes to localStorage (wrapped in root to avoid console warning)
|
||||
@@ -84,7 +86,21 @@ export const calculateUrgencyFromDate = (dateStr: string): number => {
|
||||
|
||||
export const getCombinedScore = (task: Task): number => {
|
||||
const urgencyScore = calculateUrgencyScore(task.dueDate);
|
||||
return (task.priority * store.pWeight) + (urgencyScore * store.uWeight);
|
||||
let baseScore = (task.priority * store.pWeight) + (urgencyScore * store.uWeight);
|
||||
|
||||
// Tag adjustments
|
||||
if (task.tags && task.tags.length > 0) {
|
||||
task.tags.forEach(tagName => {
|
||||
const defVal = store.tagDefinitions?.[tagName];
|
||||
if (defVal !== undefined) {
|
||||
// Formula: (Value - 5) * 0.1
|
||||
// 5 -> 0, 10 -> 0.5, 0 -> -0.5
|
||||
baseScore += (defVal - 5) * 0.1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return baseScore;
|
||||
};
|
||||
|
||||
// -- Persistence & Sync --
|
||||
@@ -126,6 +142,7 @@ export const initStore = async () => {
|
||||
pWeight: prefs.pWeight || 1.0,
|
||||
uWeight: prefs.uWeight || 1.0,
|
||||
matrixScaleDays: prefs.matrixScaleDays || 30,
|
||||
tagDefinitions: prefs.tagDefinitions || {},
|
||||
prefId: userId // The ID to update is the User ID
|
||||
});
|
||||
}
|
||||
@@ -354,6 +371,119 @@ export const deleteTaskPermanently = async (id: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const upsertTagDefinition = async (name: string, value: number) => {
|
||||
// Optimistic update
|
||||
setStore("tagDefinitions", name, value);
|
||||
|
||||
// Persist
|
||||
const map = { ...store.tagDefinitions };
|
||||
const userId = pb.authStore.model?.id;
|
||||
if (userId) {
|
||||
try {
|
||||
// Need to fetch user record first? Or iterate local?
|
||||
// Pocketbase update can take partial JSON?
|
||||
// "Taskgrid_pref" is a JSON field. We usually need to send the whole object or merge it manually if the backend doesn't merge.
|
||||
// But here we rely on store state having everything.
|
||||
|
||||
// Re-construct full prefs object
|
||||
const prefs = {
|
||||
pWeight: store.pWeight,
|
||||
uWeight: store.uWeight,
|
||||
matrixScaleDays: store.matrixScaleDays,
|
||||
tagDefinitions: map
|
||||
};
|
||||
|
||||
await pb.collection('users').update(userId, {
|
||||
Taskgrid_pref: prefs
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Failed to persist tag definition", e);
|
||||
toast.error("Failed to save tag definition");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const removeTagDefinition = async (name: string) => {
|
||||
// 1. Remove from all tasks
|
||||
const tasksWithTag = store.tasks.filter(t => t.tags?.includes(name));
|
||||
for (const task of tasksWithTag) {
|
||||
const newTags = task.tags.filter(t => t !== name);
|
||||
updateTask(task.id, { tags: newTags });
|
||||
}
|
||||
|
||||
// 2. Remove definition locally
|
||||
setStore("tagDefinitions", produce((defs) => {
|
||||
if (defs) delete defs[name];
|
||||
}));
|
||||
|
||||
// 3. Persist
|
||||
const map = { ...store.tagDefinitions };
|
||||
const userId = pb.authStore.model?.id;
|
||||
if (userId) {
|
||||
try {
|
||||
const prefs = {
|
||||
pWeight: store.pWeight,
|
||||
uWeight: store.uWeight,
|
||||
matrixScaleDays: store.matrixScaleDays,
|
||||
tagDefinitions: map
|
||||
};
|
||||
await pb.collection('users').update(userId, {
|
||||
Taskgrid_pref: prefs
|
||||
});
|
||||
toast.success(`Tag "${name}" deleted`);
|
||||
} catch (e) {
|
||||
console.error("Failed to delete tag definition", e);
|
||||
toast.error("Failed to delete tag");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const renameTagDefinition = async (oldName: string, newName: string) => {
|
||||
if (!newName || !newName.trim() || newName === oldName) return;
|
||||
const finalName = newName.trim();
|
||||
|
||||
// 1. Get current value
|
||||
const val = store.tagDefinitions?.[oldName] ?? 5;
|
||||
|
||||
// 2. Update all tasks
|
||||
const tasksWithTag = store.tasks.filter(t => t.tags?.includes(oldName));
|
||||
for (const task of tasksWithTag) {
|
||||
const newTags = task.tags.map(t => t === oldName ? finalName : t);
|
||||
// Dedupe
|
||||
const uniqueTags = [...new Set(newTags)];
|
||||
updateTask(task.id, { tags: uniqueTags });
|
||||
}
|
||||
|
||||
// 3. Update definition locally
|
||||
setStore("tagDefinitions", produce((defs) => {
|
||||
if (defs) {
|
||||
defs[finalName] = val;
|
||||
delete defs[oldName];
|
||||
}
|
||||
}));
|
||||
|
||||
// 4. Persist
|
||||
const map = { ...store.tagDefinitions };
|
||||
const userId = pb.authStore.model?.id;
|
||||
if (userId) {
|
||||
try {
|
||||
const prefs = {
|
||||
pWeight: store.pWeight,
|
||||
uWeight: store.uWeight,
|
||||
matrixScaleDays: store.matrixScaleDays,
|
||||
tagDefinitions: map
|
||||
};
|
||||
await pb.collection('users').update(userId, {
|
||||
Taskgrid_pref: prefs
|
||||
});
|
||||
toast.success(`Tag renamed to "${finalName}"`);
|
||||
} catch (e) {
|
||||
console.error("Failed to rename tag", e);
|
||||
toast.error("Failed to rename tag");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Legacy cleanup - We don't need local storage persistence setup anymore
|
||||
export const setupPersistence = () => {
|
||||
|
||||
Reference in New Issue
Block a user