Signed-off-by: Max <ai-agent@topdoglabs.com>
This commit is contained in:
parent
8aaca14e3a
commit
5c6bd134bd
15
README.md
15
README.md
@ -167,6 +167,21 @@ Task and sprint board built with Next.js + Zustand and Supabase-backed API persi
|
||||
- Delete any comment or reply from the thread
|
||||
- Thread data is persisted in Supabase through the existing `/api/tasks` sync flow.
|
||||
|
||||
Current persisted comment shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "string",
|
||||
"text": "string",
|
||||
"createdAt": "2026-02-23T19:00:00.000Z",
|
||||
"commentAuthorId": "user-uuid-or-assistant",
|
||||
"replies": []
|
||||
}
|
||||
```
|
||||
|
||||
- `commentAuthorId` is required.
|
||||
- Legacy `author` objects are not supported by the UI parser.
|
||||
|
||||
### Backlog drag and drop
|
||||
|
||||
Backlog view supports moving tasks between:
|
||||
|
||||
@ -85,6 +85,20 @@ A unified CLI that covers all API operations.
|
||||
./scripts/gantt.sh task attach <task-uuid> ./research.pdf
|
||||
```
|
||||
|
||||
Comment payload persisted by the API/CLI:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "string",
|
||||
"text": "string",
|
||||
"createdAt": "2026-02-23T19:00:00.000Z",
|
||||
"commentAuthorId": "user-uuid-or-assistant",
|
||||
"replies": []
|
||||
}
|
||||
```
|
||||
|
||||
- `commentAuthorId` is the only supported author field.
|
||||
|
||||
### Project Commands
|
||||
|
||||
```bash
|
||||
|
||||
@ -468,6 +468,25 @@ export default function Home() {
|
||||
return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name))
|
||||
}, [users, currentUser])
|
||||
|
||||
const knownUsersById = useMemo(() => {
|
||||
const byId = new Map<string, AssignableUser>()
|
||||
|
||||
const addUser = (id: string | undefined, name: string | undefined, avatarUrl?: string, email?: string) => {
|
||||
if (!id || !name) return
|
||||
if (id.trim().length === 0 || name.trim().length === 0) return
|
||||
byId.set(id, { id, name, avatarUrl, email })
|
||||
}
|
||||
|
||||
assignableUsers.forEach((user) => byId.set(user.id, user))
|
||||
tasks.forEach((task) => {
|
||||
addUser(task.createdById, task.createdByName, task.createdByAvatarUrl)
|
||||
addUser(task.updatedById, task.updatedByName, task.updatedByAvatarUrl)
|
||||
addUser(task.assigneeId, task.assigneeName, task.assigneeAvatarUrl, task.assigneeEmail)
|
||||
})
|
||||
|
||||
return byId
|
||||
}, [assignableUsers, tasks])
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true
|
||||
const loadSession = async () => {
|
||||
@ -756,7 +775,7 @@ export default function Home() {
|
||||
|
||||
const resolveAssignee = (assigneeId: string | undefined) => {
|
||||
if (!assigneeId) return null
|
||||
return assignableUsers.find((user) => user.id === assigneeId) || null
|
||||
return knownUsersById.get(assigneeId) || null
|
||||
}
|
||||
|
||||
const setNewTaskAssignee = (assigneeId: string) => {
|
||||
|
||||
@ -330,6 +330,25 @@ export default function TaskDetailPage() {
|
||||
return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name))
|
||||
}, [users, currentUser])
|
||||
|
||||
const knownUsersById = useMemo(() => {
|
||||
const byId = new Map<string, AssignableUser>()
|
||||
|
||||
const addUser = (id: string | undefined, name: string | undefined, avatarUrl?: string, email?: string) => {
|
||||
if (!id || !name) return
|
||||
if (id.trim().length === 0 || name.trim().length === 0) return
|
||||
byId.set(id, { id, name, avatarUrl, email })
|
||||
}
|
||||
|
||||
assignableUsers.forEach((user) => byId.set(user.id, user))
|
||||
tasks.forEach((task) => {
|
||||
addUser(task.createdById, task.createdByName, task.createdByAvatarUrl)
|
||||
addUser(task.updatedById, task.updatedByName, task.updatedByAvatarUrl)
|
||||
addUser(task.assigneeId, task.assigneeName, task.assigneeAvatarUrl, task.assigneeEmail)
|
||||
})
|
||||
|
||||
return byId
|
||||
}, [assignableUsers, tasks])
|
||||
|
||||
const sortedSprints = useMemo(
|
||||
() =>
|
||||
sprints
|
||||
@ -419,7 +438,7 @@ export default function TaskDetailPage() {
|
||||
|
||||
const resolveAssignee = (assigneeId: string | undefined) => {
|
||||
if (!assigneeId) return null
|
||||
return assignableUsers.find((user) => user.id === assigneeId) || null
|
||||
return knownUsersById.get(assigneeId) || null
|
||||
}
|
||||
|
||||
const setEditedTaskAssignee = (assigneeId: string) => {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user