Skip to content

Add todolist workflow with freeze/archive and DB migrations#30

Merged
macintoshwan merged 1 commit intomainfrom
feature/todolist-merge
Apr 18, 2026
Merged

Add todolist workflow with freeze/archive and DB migrations#30
macintoshwan merged 1 commit intomainfrom
feature/todolist-merge

Conversation

@macintoshwan
Copy link
Copy Markdown
Owner

Summary

Port the todolist feature set onto a branch based on main so it can be merged cleanly.

Included changes

  • Added todolist workflow integrated with project task flow
  • Added project freeze/unfreeze and archive/unarchive controls
  • Unified frozen/archived visual badge style and moved badges to the front of project cards
  • Hid remaining/overdue time display for frozen and archived projects
  • Excluded frozen/archived projects from todo candidate aggregation
  • Added DB migration scripts under supabase/migrations/:
    • 001_create_todos_table.sql
    • 002_add_system_project_flag.sql
    • 003_add_project_freeze_column.sql
    • 004_add_project_archive_column.sql

Validation

  • Local build passed: npm run build

Notes

  • Original feature/todolist could not open PR due unrelated history with main.
  • This branch (feature/todolist-merge) is created from main to provide a clean merge path.

- Add todo workflow integrated with project tasks
- Add project freeze and archive lifecycle controls
- Unify frozen/archived badges and place at card front
- Hide time display for frozen/archived projects
- Add supabase migrations for todos/system/freeze/archive
Copilot AI review requested due to automatic review settings April 18, 2026 14:59
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying artemis with  Cloudflare Pages  Cloudflare Pages

Latest commit: 882a124
Status: ✅  Deploy successful!
Preview URL: https://ea68bb20.artemis-b7j.pages.dev
Branch Preview URL: https://feature-todolist-merge.artemis-b7j.pages.dev

View logs

@macintoshwan macintoshwan merged commit bd45bbd into main Apr 18, 2026
4 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Ports the todolist feature branch onto main by introducing DB migrations and wiring UI/state changes for a todo workflow plus project freeze/archive controls.

Changes:

  • Added Supabase migrations for todos plus projects flags (is_system, is_frozen, is_archived).
  • Extended frontend types/store/API to support todo data and system/temp project initialization.
  • Updated UI to add todo section, project freeze/archive controls, state badges, and read-only behavior for frozen/archived projects.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
supabase/migrations/001_create_todos_table.sql Creates todos table with RLS policies and realtime publication.
supabase/migrations/002_add_system_project_flag.sql Adds projects.is_system and unique constraint for per-user system project.
supabase/migrations/003_add_project_freeze_column.sql Adds projects.is_frozen plus column comment.
supabase/migrations/004_add_project_archive_column.sql Adds projects.is_archived plus an index.
src/types/index.ts Extends Project and store types; introduces Todo/TodoItem types.
src/styles/base.css Adds styles for project state tags, disabled status buttons, todo section, and section footer layout.
src/store/useProjectsStore.ts Adds todo state/actions and selectors.
src/lib/api.ts Adds ensureSystemTodoProject plus todo CRUD functions.
src/components/TodoListContainer.tsx New container component for deriving todo items from tasks.
src/components/TodoList.tsx New todo list UI (renders status controls and create action).
src/components/TaskList.tsx Adds disabled/read-only behavior for tasks and updates status labels.
src/components/TaskEditModal.tsx Adds support for default in-progress creation and deferred project resolution.
src/components/ProjectList.tsx Adds freeze/archive controls, state badges, hides temp/system project, and suppresses remaining time for locked projects.
src/components/ProjectEditModal.tsx Ensures new projects initialize new boolean flags.
src/components/ProjectDetail.tsx Enforces read-only mode messaging and disables edits when frozen/archived.
src/components/App.tsx Integrates todo section, temp project initialization, and checkin section footer actions.
Comments suppressed due to low confidence (1)

src/components/ProjectList.tsx:50

  • planEndDate’s useMemo logic depends on project.is_frozen / project.is_archived, but those fields aren’t included in the dependency array. Toggling freeze/archive can leave a stale planEndDate and keep showing the remaining-time badge. Include project?.is_frozen and project?.is_archived (or just depend on project) in the deps so the UI updates immediately.
  const planEndDate = useMemo(() => {
    if (project?.is_frozen || project?.is_archived) return null;
    if (project?.plan_end_date) return project.plan_end_date;
    if (!tasks || tasks.length === 0) return null;

    // 所有任务已完成则不显示
    const allDone = tasks.every((t) => t.completed);
    if (allDone) return null;

    let latest: Date | null = null;
    for (const t of tasks) {
      if (t.plan_end_date) {
        const d = new Date(t.plan_end_date);
        if (!latest || d > latest) latest = d;
      }
    }
    return latest ? latest.toISOString() : null;
  }, [project?.plan_end_date, tasks]);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/App.tsx
Comment on lines +29 to +32
function isTempProject(project: Project | null | undefined): boolean {
if (!project) return false;
return project.is_system || project.category === 'system' || project.name === '临时';
}
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isTempProject helper is duplicated here and in ProjectList.tsx with slightly different typing. Consider moving it to a shared util (or types) to ensure consistent “temp/system project” detection logic across the app and avoid future drift.

Copilot uses AI. Check for mistakes.
Comment thread src/types/index.ts
actual_start_date: string | null;
actual_end_date: string | null;
actual_duration: number | null;
actual_duration: number | null;
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line has trailing whitespace after the semicolon, which can cause noisy diffs and may violate formatting rules. Remove the extra space at the end of the line.

Suggested change
actual_duration: number | null;
actual_duration: number | null;

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +10
completed boolean DEFAULT false,
created_at timestamp with time zone DEFAULT now(),
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

completed and created_at are nullable in the table definition, but the TypeScript Todo type treats them as always present (boolean / string). Consider making them NOT NULL with defaults to keep DB constraints aligned with the app’s types and avoid unexpected null values in queries.

Suggested change
completed boolean DEFAULT false,
created_at timestamp with time zone DEFAULT now(),
completed boolean NOT NULL DEFAULT false,
created_at timestamp with time zone NOT NULL DEFAULT now(),

Copilot uses AI. Check for mistakes.
-- Enable Realtime
-- ============================================================

ALTER PUBLICATION supabase_realtime ADD TABLE public.todos;
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALTER PUBLICATION supabase_realtime ADD TABLE public.todos; will fail if the table is already part of the publication (e.g., if migrations are re-applied or the publication was modified manually). If you want these migrations to be safely re-runnable like the IF NOT EXISTS column/index changes, wrap this in a conditional DO $$ ... $$ block that checks pg_publication_rel before adding the table.

Suggested change
ALTER PUBLICATION supabase_realtime ADD TABLE public.todos;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_publication_rel pr
JOIN pg_publication p ON p.oid = pr.prpubid
JOIN pg_class c ON c.oid = pr.prrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE p.pubname = 'supabase_realtime'
AND n.nspname = 'public'
AND c.relname = 'todos'
) THEN
ALTER PUBLICATION supabase_realtime ADD TABLE public.todos;
END IF;
END
$$;

Copilot uses AI. Check for mistakes.
Comment on lines 61 to 68
interface TaskItemProps {
taskId: number;
onEdit?: (taskId: number) => void;
isProjectFrozen?: boolean;
}

const TaskItem = memo(function TaskItem({ taskId, onEdit }: TaskItemProps) {
const TaskItem = memo(function TaskItem({ taskId, onEdit, isProjectFrozen = false }: TaskItemProps) {
const task = useProjectsStore(selectTask(taskId));
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isProjectFrozen is now being used to disable edits for both frozen and archived projects (it’s passed isProjectLocked from ProjectDetail). Consider renaming this prop to something like isProjectLocked/readOnly to reflect the actual semantics and avoid confusion as the feature set grows.

Copilot uses AI. Check for mistakes.
Comment on lines +461 to +470
// 添加独立 todo
for (const id of state.todoIds) {
const todo = state.todos[id];
if (todo) {
items.push({
id: todo.id,
name: todo.name,
type: 'standalone',
completed: todo.completed,
});
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This selector also pushes items with type: 'standalone', but TodoItem.type is currently defined as only 'project-task'. Either extend the TodoItem type to include a standalone variant (and add required fields like status), or keep this selector strictly to project-task items.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +10
import { useMemo } from 'react';
import { useProjectsStore, selectProjectIds } from '../store/useProjectsStore';
import { deriveTaskStatus } from '../types';
import { TodoList } from './TodoList';
import type { TodoItem } from '../types';
import type { TaskStatus } from '../types';

interface TodoListContainerProps {
onCreateTodoTask: () => void;
onChangeTodoStatus: (taskId: number, status: TaskStatus) => Promise<void>;
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TodoListContainer is added but not referenced anywhere in the codebase (no imports/usages found). If it’s not part of the intended UI, consider removing it to avoid dead code; otherwise, wire it up (or replace the duplicated todo-item derivation currently in App.tsx).

Copilot uses AI. Check for mistakes.

export const ProjectDetail = memo(function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
const project = useProjectsStore(selectProject(projectId));
const isProjectLocked = Boolean(project?.is_frozen || project?.is_archived);
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s an extra indentation level before const isProjectLocked = ... which makes this block misaligned with the surrounding code and can trip format/lint rules. Align it with the other const declarations in the component.

Suggested change
const isProjectLocked = Boolean(project?.is_frozen || project?.is_archived);
const isProjectLocked = Boolean(project?.is_frozen || project?.is_archived);

Copilot uses AI. Check for mistakes.
CREATE POLICY "Users can update their own todos"
ON public.todos
FOR UPDATE
USING (auth.uid() = user_id);
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FOR UPDATE RLS policy only has a USING clause. Without a WITH CHECK (auth.uid() = user_id), a user who owns a row can update it and change user_id to another value, effectively bypassing ownership on the updated row. Add a matching WITH CHECK clause (or split into separate policies) so post-update rows are still constrained to the current user.

Suggested change
USING (auth.uid() = user_id);
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);

Copilot uses AI. Check for mistakes.
Comment thread src/components/App.tsx
useRealtimeSync(user?.id);

// 从 store 获取最基础的数据
const { optimisticInsertProject } = useProjectsStore();
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useProjectsStore() is called without a selector to grab optimisticInsertProject, which subscribes this component to the entire store and can trigger re-renders on any store change. Prefer useProjectsStore((s) => s.optimisticInsertProject) (and similarly select only the needed actions) to keep renders scoped.

Suggested change
const { optimisticInsertProject } = useProjectsStore();
const optimisticInsertProject = useProjectsStore((s) => s.optimisticInsertProject);

Copilot uses AI. Check for mistakes.
@macintoshwan macintoshwan deleted the feature/todolist-merge branch April 18, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants