Rename lists -> projects and items -> cards throughout

Project scope shifts from a todo list to a project-management app. This is a
straight terminology rename across code, comments, migrations, tests, and
docs — no behaviour change.

- DB: table todo_lists -> projects, todo_items -> cards, column
  todo_items.list_id -> cards.project_id, indexes renamed. Migrations 003/004
  rewritten in place (destructive; recreate the volume with `down -v`).
- API: /api/lists -> /api/projects, nested /items -> /cards, reorder body
  item_ids -> card_ids, JSON keys list/lists/item/items -> project/projects/
  card/cards, item_count -> card_count, list_id -> project_id, and the
  matching error messages.
- PHP: TodoList/TodoItem Repository + Controller -> Project/Card; shared SQL
  aliases l/i -> p/c.
- Frontend: stores lists.ts/items.ts -> projects.ts/cards.ts (useProjectsStore
  / useCardsStore, MAX_PROJECTS), ListView -> ProjectView, TodoItemRow ->
  CardRow, route /lists/:id -> /projects/:id (name "project"), types TodoList/
  TodoItem -> Project/Card, and all UI copy. CSS .lists*/.list-head* ->
  .projects*/.project-head*, .item* -> .card-row* (kept the generic .card
  panel class), .items -> .cards.
- Product name in the header, PWA manifest, index.html title and package
  descriptions -> "Project Manager" / "Projects".

Backend suite: 37 passing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-04 11:28:59 +01:00
co-authored by Claude Sonnet 5
parent be592f38fc
commit c66e5ceb9b
30 changed files with 1068 additions and 1065 deletions
+56
View File
@@ -0,0 +1,56 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import type { Card } from '../types'
const props = defineProps<{ card: Card }>()
const emit = defineEmits<{
toggle: [complete: boolean]
'save-text': [text: string]
delete: []
}>()
const text = ref(props.card.text)
watch(
() => props.card.text,
(value) => {
text.value = value
},
)
function commit() {
const next = text.value.trim()
if (next === '') {
text.value = props.card.text // the API requires a non-empty text
return
}
if (next !== props.card.text) emit('save-text', next)
}
</script>
<template>
<li class="card-row" :class="{ 'card-row--done': card.complete }">
<span class="card-row__handle" aria-hidden="true" title="Drag to reorder"></span>
<input
class="card-row__check"
type="checkbox"
:checked="card.complete"
:aria-label="card.complete ? 'Mark as not done' : 'Mark as done'"
@change="emit('toggle', ($event.target as HTMLInputElement).checked)"
/>
<input
v-model="text"
class="card-row__text"
type="text"
maxlength="1000"
aria-label="Card text"
@blur="commit"
@keyup.enter="($event.target as HTMLInputElement).blur()"
/>
<button type="button" class="card-row__delete" aria-label="Delete card" @click="emit('delete')">
</button>
</li>
</template>