- Low priority research task for iOS app ideas - Focus on MRR opportunities (K-K+) - Multi-screen apps with viral potential - Leverages iOS-specific features
461 lines
23 KiB
TypeScript
461 lines
23 KiB
TypeScript
import { create } from 'zustand'
|
|
import { persist } from 'zustand/middleware'
|
|
|
|
export type TaskType = 'idea' | 'task' | 'bug' | 'research' | 'plan'
|
|
export type TaskStatus = 'backlog' | 'in-progress' | 'review' | 'done' | 'archived'
|
|
export type Priority = 'low' | 'medium' | 'high' | 'urgent'
|
|
|
|
export interface Comment {
|
|
id: string
|
|
text: string
|
|
createdAt: string
|
|
author: 'user' | 'assistant'
|
|
}
|
|
|
|
export interface Task {
|
|
id: string
|
|
title: string
|
|
description?: string
|
|
type: TaskType
|
|
status: TaskStatus
|
|
priority: Priority
|
|
projectId: string
|
|
createdAt: string
|
|
updatedAt: string
|
|
dueDate?: string
|
|
comments: Comment[]
|
|
tags: string[]
|
|
}
|
|
|
|
export interface Project {
|
|
id: string
|
|
name: string
|
|
description?: string
|
|
color: string
|
|
createdAt: string
|
|
}
|
|
|
|
interface TaskStore {
|
|
projects: Project[]
|
|
tasks: Task[]
|
|
selectedProjectId: string | null
|
|
selectedTaskId: string | null
|
|
isLoading: boolean
|
|
lastSynced: number | null
|
|
|
|
// Sync actions
|
|
syncFromServer: () => Promise<void>
|
|
syncToServer: () => Promise<void>
|
|
|
|
// Project actions
|
|
addProject: (name: string, description?: string) => void
|
|
updateProject: (id: string, updates: Partial<Project>) => void
|
|
deleteProject: (id: string) => void
|
|
selectProject: (id: string | null) => void
|
|
|
|
// Task actions
|
|
addTask: (task: Omit<Task, 'id' | 'createdAt' | 'updatedAt' | 'comments'>) => void
|
|
updateTask: (id: string, updates: Partial<Task>) => void
|
|
deleteTask: (id: string) => void
|
|
selectTask: (id: string | null) => void
|
|
|
|
// Comment actions
|
|
addComment: (taskId: string, text: string, author: 'user' | 'assistant') => void
|
|
deleteComment: (taskId: string, commentId: string) => void
|
|
|
|
// Filters
|
|
getTasksByProject: (projectId: string) => Task[]
|
|
getTasksByStatus: (status: TaskStatus) => Task[]
|
|
getTaskById: (id: string) => Task | undefined
|
|
}
|
|
|
|
const defaultProjects: Project[] = [
|
|
{ id: '1', name: 'OpenClaw iOS', description: 'Main iOS app development', color: '#8b5cf6', createdAt: new Date().toISOString() },
|
|
{ id: '2', name: 'Web Projects', description: 'Web tools and dashboards', color: '#3b82f6', createdAt: new Date().toISOString() },
|
|
{ id: '3', name: 'Research', description: 'Experiments and learning', color: '#10b981', createdAt: new Date().toISOString() },
|
|
]
|
|
|
|
const defaultTasks: Task[] = [
|
|
{
|
|
id: '1',
|
|
title: 'Redesign Gantt Board',
|
|
description: 'Make it actually work with proper notes system',
|
|
type: 'task',
|
|
status: 'in-progress',
|
|
priority: 'high',
|
|
projectId: '2',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [
|
|
{ id: 'c1', text: 'Need 1-to-many notes, not one big text field', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c2', text: 'Agreed - will rebuild with proper comment threads', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
],
|
|
tags: ['ui', 'rewrite']
|
|
},
|
|
{
|
|
id: '2',
|
|
title: 'MoodWeave App Idea',
|
|
description: 'Social mood tracking with woven visualizations',
|
|
type: 'idea',
|
|
status: 'backlog',
|
|
priority: 'medium',
|
|
projectId: '1',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [],
|
|
tags: ['ios', 'social']
|
|
},
|
|
{
|
|
id: '3',
|
|
title: 'Set up Gitea integration for code pushes',
|
|
description: 'Create bot account on Gitea (192.168.1.128:3000) and configure git remotes for all OpenClaw projects. Decide on account name, permissions, and auth method (SSH vs token). User prefers dedicated bot account over using their personal account for audit trail.',
|
|
type: 'task',
|
|
status: 'done',
|
|
priority: 'medium',
|
|
projectId: '2',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [
|
|
{ id: 'c3', text: 'User has local Gitea at http://192.168.1.128:3000', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c4', text: 'Options: 1) Create dedicated bot account (recommended), 2) Use existing account', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c5', text: 'Account created: mbruce@topdoglabs.com / !7883Gitea (username: ai-agent)', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c6', text: 'Git configured for all 3 projects. Gitea remotes added.', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c7', text: '✅ All 3 repos created and pushed to Gitea: gantt-board, blog-backup, heartbeat-monitor', createdAt: new Date().toISOString(), author: 'assistant' }
|
|
],
|
|
tags: ['gitea', 'git', 'automation', 'infrastructure']
|
|
},
|
|
{
|
|
id: '4',
|
|
title: 'Redesign Heartbeat Monitor to match UptimeRobot',
|
|
description: 'Completely redesign the Heartbeat Monitor website to be a competitor to https://uptimerobot.com. Study their design, layout, color scheme, typography, and functionality. Match their look, feel, and style as closely as possible. Include: modern dashboard, status pages, uptime charts, incident history, public status pages.',
|
|
type: 'task',
|
|
status: 'done',
|
|
priority: 'high',
|
|
projectId: '2',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [
|
|
{ id: 'c8', text: 'Reference: https://uptimerobot.com - study their homepage, dashboard, and status page designs', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c9', text: 'Focus on: clean modern UI, blue/green color scheme, card-based layouts, uptime percentage displays, incident timelines', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c29', text: 'COMPLETED: Full rebuild with Next.js + shadcn/ui + Framer Motion. Dark OLED theme, glass-morphism cards, animated status indicators, sparkline visualizations, grid/list views, tooltips, progress bars. Production-grade at http://localhost:3005', createdAt: new Date().toISOString(), author: 'assistant' }
|
|
],
|
|
tags: ['ui', 'ux', 'redesign', 'dashboard', 'monitoring']
|
|
},
|
|
{
|
|
id: '8',
|
|
title: 'Fix Kanban board - dynamic sync without hard refresh',
|
|
description: 'Current board uses localStorage persistence which requires hard refresh (Cmd+Shift+R) to see task updates from code changes. Need to add: server-side storage (API + database/file), or sync mechanism that checks for updates on regular refresh, or real-time updates via WebSocket/polling. User should see updates on normal page refresh without clearing cache.',
|
|
type: 'task',
|
|
status: 'in-progress',
|
|
priority: 'medium',
|
|
projectId: '2',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [
|
|
{ id: 'c21', text: 'Issue: Task status changes require hard refresh to see', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c22', text: 'Current: localStorage persistence via Zustand', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c23', text: 'Need: Server-side storage or sync on regular refresh', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c44', text: 'COMPLETED: Added /api/tasks endpoint with file-based storage', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c45', text: 'COMPLETED: Store now syncs from server on load and auto-syncs changes', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c46', text: 'COMPLETED: Falls back to localStorage if server unavailable', createdAt: new Date().toISOString(), author: 'assistant' }
|
|
],
|
|
tags: ['ui', 'sync', 'localstorage', 'real-time']
|
|
},
|
|
{
|
|
id: '5',
|
|
title: 'Fix Blog Backup links to be clickable',
|
|
description: 'Make links in the Daily Digest clickable in the blog backup UI. Currently links are just text that require copy-paste. Need to render markdown links properly so users can click directly. Consider different formatting for Telegram vs Blog - Telegram gets plain text summary with "Read more at [link]", Blog gets full formatted content with clickable links.',
|
|
type: 'task',
|
|
status: 'done',
|
|
priority: 'medium',
|
|
projectId: '2',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [
|
|
{ id: 'c10', text: 'Blog should show: [headline](url) as clickable markdown links', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c11', text: 'Telegram gets summary + "Full digest at: http://localhost:3003"', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c41', text: 'COMPLETED: Fixed parseDigest to extract URLs from markdown links [Title](url) in title lines', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c42', text: 'COMPLETED: Title is now the clickable link with external link icon on hover', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c43', text: 'COMPLETED: Better hover states - title turns blue, external link icon appears', createdAt: new Date().toISOString(), author: 'assistant' }
|
|
],
|
|
tags: ['blog', 'ui', 'markdown', 'links']
|
|
},
|
|
{
|
|
id: '6',
|
|
title: 'Fix monitoring schedule - 2 of 3 sites are down',
|
|
description: 'The cron job running every 10 minutes to check heartbeat website is failing. Currently 2 of 3 websites are down and not being auto-restarted. Debug and fix the monitoring schedule to ensure all 3 sites (gantt-board, blog-backup, heartbeat-monitor) are checked and auto-restarted properly.',
|
|
type: 'bug',
|
|
status: 'done',
|
|
priority: 'urgent',
|
|
projectId: '2',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [
|
|
{ id: 'c12', text: 'Issue: Cron job exists but sites are still going down without restart', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c13', text: 'Need to verify: cron is running, checks all 3 ports, restart logic works, permissions correct', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c14', text: 'ALL SITES BACK UP - manually restarted at 14:19. Now investigating why auto-restart failed.', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c15', text: 'Problem: Port 3005 was still in use (EADDRINUSE), need better process cleanup in restart logic', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c16', text: 'FIXED: Updated cron job with pkill cleanup before restart + 2s delay. Created backup script: monitor-restart.sh', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c17', text: 'All 3 sites stable. Cron job now properly kills old processes before restarting to avoid port conflicts.', createdAt: new Date().toISOString(), author: 'assistant' }
|
|
],
|
|
tags: ['monitoring', 'cron', 'bug', 'infrastructure', 'urgent']
|
|
},
|
|
{
|
|
id: '7',
|
|
title: 'Investigate root cause - why are websites dying?',
|
|
description: 'Currently monitoring only treats the symptom (restart when down). Need to investigate what is actually killing the Next.js dev servers. Check: system logs, memory usage, file watcher limits, zombie processes, macOS power management, SSH timeout, OOM killer. Set up logging to capture what happens right before crashes.',
|
|
type: 'research',
|
|
status: 'done',
|
|
priority: 'high',
|
|
projectId: '2',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [
|
|
{ id: 'c18', text: 'Problem: Sites go down randomly - what is killing them?', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c19', text: 'Suspects: Memory leaks, file watcher hitting limits, SSH session timeout, macOS power nap, OOM killer', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c20', text: 'Need to add logging/capture to see what kills processes', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c27', text: 'COMPLETED: Root cause analysis done. Primary suspect: Next.js dev server memory leaks. Secondary: SSH timeout, OOM killer, power mgmt. Full report: root-cause-analysis.md. Monitoring script deployed.', createdAt: new Date().toISOString(), author: 'assistant' }
|
|
],
|
|
tags: ['debugging', 'research', 'infrastructure', 'root-cause']
|
|
},
|
|
{
|
|
id: '9',
|
|
title: 'Add ability to edit task priority in Kanban board',
|
|
description: 'Currently users cannot change task priority (Low/Medium/High/Urgent) from the UI. Need to add priority editing capability to the task detail view or task card. Should be a dropdown selector allowing users to re-prioritize tasks on the fly without editing code.',
|
|
type: 'task',
|
|
status: 'done',
|
|
priority: 'high',
|
|
projectId: '2',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [
|
|
{ id: 'c24', text: 'User cannot currently change priority from Medium to High in UI', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c25', text: 'Need priority dropdown/editor in task detail view', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c28', text: 'COMPLETED: Added priority buttons to task detail dialog. Click any task to see Low/Medium/High/Urgent buttons with color coding. Changes apply immediately.', createdAt: new Date().toISOString(), author: 'assistant' }
|
|
],
|
|
tags: ['ui', 'kanban', 'feature', 'priority']
|
|
},
|
|
{
|
|
id: '10',
|
|
title: 'RESEARCH: Find viable screenshot solution for OpenClaw on macOS',
|
|
description: 'INVESTIGATION NEEDED: Find a reliable, persistent way for OpenClaw AI to capture screenshots of local websites running on macOS. Current browser tool requires Chrome extension which is not connected. Puppeteer workaround is temporary. Need to research and document ALL possible options including: macOS native screenshot tools (screencapture, automator), alternative browser automation tools, canvas/headless options, or any other method that works on macOS without requiring Chrome extension.',
|
|
type: 'research',
|
|
status: 'done',
|
|
priority: 'high',
|
|
projectId: '2',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [
|
|
{ id: 'c29', text: 'PROBLEM: User needs to share screenshots of local websites with friends who cannot access home network. Browser tool unavailable (Chrome extension not connected).', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c30', text: 'INVESTIGATE: macOS native screenshot capabilities - screencapture CLI, Automator workflows, AppleScript', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c31', text: 'INVESTIGATE: Alternative browser automation - Playwright, Selenium, WebDriver without Chrome extension requirement', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c32', text: 'INVESTIGATE: OpenClaw Gateway configuration - browser profiles, node setup, gateway settings', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c33', text: 'INVESTIGATE: Third-party screenshot APIs or services that could work locally', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c34', text: 'DELIVERABLE: Document ALL options found with pros/cons, setup requirements, and recommendation for best solution', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c35', text: 'FINDING: /usr/sbin/screencapture exists but requires interactive mode or captures full screen - cannot target specific URL', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c36', text: 'FINDING: Google Chrome is installed at /Applications/Google Chrome.app - Playwright can use this for headless screenshots', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c37', text: 'FINDING: Playwright v1.58.2 is already available via npx - can automate Chrome without extension', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c38', text: 'FINDING: OpenClaw Gateway is running on port 18789 with browser profile "chrome" but extension not attached to any tab', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c39', text: 'SUCCESS: Playwright + Google Chrome works! Successfully captured screenshot of http://localhost:3005 using headless Chrome', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c40', text: 'RECOMMENDATION: Install Playwright globally or in workspace so screenshots work without temporary installs. Command: npm install -g playwright', createdAt: new Date().toISOString(), author: 'assistant' },
|
|
{ id: 'c47', text: 'COMPLETED: Playwright installed globally. Screenshots now work anytime without setup.', createdAt: new Date().toISOString(), author: 'assistant' }
|
|
],
|
|
tags: ['research', 'screenshot', 'macos', 'openclaw', 'investigation']
|
|
},
|
|
{
|
|
id: '11',
|
|
title: 'RESEARCH: Find iOS side projects with MRR potential',
|
|
description: 'Research and identify iOS app ideas that have strong Monthly Recurring Revenue (MRR) opportunities. Focus on apps that are well-designed, multi-screen experiences (not single-screen utilities), and have viral potential. Look at current App Store trends, successful indie apps, and underserved niches. Consider: subscription models, freemium tiers, in-app purchases. Target ideas that leverage iOS-specific features (widgets, Live Activities, Siri, CoreML, etc.) and could generate $1K-$10K+ MRR.',
|
|
type: 'research',
|
|
status: 'backlog',
|
|
priority: 'low',
|
|
projectId: '3',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [
|
|
{ id: 'c48', text: 'Focus: iOS apps with MRR potential (subscriptions, recurring revenue)', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c49', text: 'Requirements: Well-thought-out, multi-screen, viral potential', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c50', text: 'Target: $1K-$10K+ MRR opportunities', createdAt: new Date().toISOString(), author: 'user' },
|
|
{ id: 'c51', text: 'Research areas: App Store trends, indie success stories, underserved niches', createdAt: new Date().toISOString(), author: 'assistant' }
|
|
],
|
|
tags: ['ios', 'mrr', 'research', 'side-project', 'entrepreneurship', 'app-ideas']
|
|
}
|
|
]
|
|
|
|
// Helper to sync to server
|
|
async function syncToServer(projects: Project[], tasks: Task[]) {
|
|
try {
|
|
await fetch('/api/tasks', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ projects, tasks }),
|
|
})
|
|
} catch (error) {
|
|
console.error('Failed to sync to server:', error)
|
|
}
|
|
}
|
|
|
|
export const useTaskStore = create<TaskStore>()(
|
|
persist(
|
|
(set, get) => ({
|
|
projects: defaultProjects,
|
|
tasks: defaultTasks,
|
|
selectedProjectId: '1',
|
|
selectedTaskId: null,
|
|
isLoading: false,
|
|
lastSynced: null,
|
|
|
|
syncFromServer: async () => {
|
|
set({ isLoading: true })
|
|
try {
|
|
const res = await fetch('/api/tasks')
|
|
if (res.ok) {
|
|
const data = await res.json()
|
|
if (data.tasks?.length > 0 || data.projects?.length > 0) {
|
|
set({
|
|
projects: data.projects || get().projects,
|
|
tasks: data.tasks || get().tasks,
|
|
lastSynced: Date.now(),
|
|
})
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to sync from server:', error)
|
|
// Keep local data if server fails
|
|
} finally {
|
|
set({ isLoading: false })
|
|
}
|
|
},
|
|
|
|
syncToServer: async () => {
|
|
const { projects, tasks } = get()
|
|
await syncToServer(projects, tasks)
|
|
set({ lastSynced: Date.now() })
|
|
},
|
|
|
|
addProject: (name, description) => {
|
|
const colors = ['#8b5cf6', '#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#ec4899', '#06b6d4']
|
|
const newProject: Project = {
|
|
id: Date.now().toString(),
|
|
name,
|
|
description,
|
|
color: colors[Math.floor(Math.random() * colors.length)],
|
|
createdAt: new Date().toISOString(),
|
|
}
|
|
set((state) => {
|
|
const newState = { projects: [...state.projects, newProject] }
|
|
// Sync to server
|
|
syncToServer(newState.projects, state.tasks)
|
|
return newState
|
|
})
|
|
},
|
|
|
|
updateProject: (id, updates) => {
|
|
set((state) => {
|
|
const newProjects = state.projects.map((p) => (p.id === id ? { ...p, ...updates } : p))
|
|
syncToServer(newProjects, state.tasks)
|
|
return { projects: newProjects }
|
|
})
|
|
},
|
|
|
|
deleteProject: (id) => {
|
|
set((state) => {
|
|
const newProjects = state.projects.filter((p) => p.id !== id)
|
|
const newTasks = state.tasks.filter((t) => t.projectId !== id)
|
|
syncToServer(newProjects, newTasks)
|
|
return {
|
|
projects: newProjects,
|
|
tasks: newTasks,
|
|
selectedProjectId: state.selectedProjectId === id ? null : state.selectedProjectId,
|
|
}
|
|
})
|
|
},
|
|
|
|
selectProject: (id) => set({ selectedProjectId: id, selectedTaskId: null }),
|
|
|
|
addTask: (task) => {
|
|
const newTask: Task = {
|
|
...task,
|
|
id: Date.now().toString(),
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
comments: [],
|
|
}
|
|
set((state) => {
|
|
const newTasks = [...state.tasks, newTask]
|
|
syncToServer(state.projects, newTasks)
|
|
return { tasks: newTasks }
|
|
})
|
|
},
|
|
|
|
updateTask: (id, updates) => {
|
|
set((state) => {
|
|
const newTasks = state.tasks.map((t) =>
|
|
t.id === id ? { ...t, ...updates, updatedAt: new Date().toISOString() } : t
|
|
)
|
|
syncToServer(state.projects, newTasks)
|
|
return { tasks: newTasks }
|
|
})
|
|
},
|
|
|
|
deleteTask: (id) => {
|
|
set((state) => {
|
|
const newTasks = state.tasks.filter((t) => t.id !== id)
|
|
syncToServer(state.projects, newTasks)
|
|
return {
|
|
tasks: newTasks,
|
|
selectedTaskId: state.selectedTaskId === id ? null : state.selectedTaskId,
|
|
}
|
|
})
|
|
},
|
|
|
|
selectTask: (id) => set({ selectedTaskId: id }),
|
|
|
|
addComment: (taskId, text, author) => {
|
|
const newComment: Comment = {
|
|
id: Date.now().toString(),
|
|
text,
|
|
createdAt: new Date().toISOString(),
|
|
author,
|
|
}
|
|
set((state) => {
|
|
const newTasks = state.tasks.map((t) =>
|
|
t.id === taskId
|
|
? { ...t, comments: [...t.comments, newComment], updatedAt: new Date().toISOString() }
|
|
: t
|
|
)
|
|
syncToServer(state.projects, newTasks)
|
|
return { tasks: newTasks }
|
|
})
|
|
},
|
|
|
|
deleteComment: (taskId, commentId) => {
|
|
set((state) => {
|
|
const newTasks = state.tasks.map((t) =>
|
|
t.id === taskId
|
|
? { ...t, comments: t.comments.filter((c) => c.id !== commentId) }
|
|
: t
|
|
)
|
|
syncToServer(state.projects, newTasks)
|
|
return { tasks: newTasks }
|
|
})
|
|
},
|
|
|
|
getTasksByProject: (projectId) => {
|
|
return get().tasks.filter((t) => t.projectId === projectId)
|
|
},
|
|
|
|
getTasksByStatus: (status) => {
|
|
return get().tasks.filter((t) => t.status === status)
|
|
},
|
|
|
|
getTaskById: (id) => {
|
|
return get().tasks.find((t) => t.id === id)
|
|
},
|
|
}),
|
|
{
|
|
name: 'task-store',
|
|
}
|
|
)
|
|
)
|