feat: manage fruits — full CRUD with images and synonyms (story #02)

Backend: domain structs, FruitRepository interface + pg implementation,
9 Echo v4 handlers (list/get/create/update/delete, image sub-resources),
migration 000002 (fruit_type ENUM, fruits, fruit_synonyms, fruit_images),
route-scoped BodyLimit("5M") for uploads, http.DetectContentType for serving.

Frontend: typed fetch API layer, Pinia setup-style fruitStore, FruitList
(paginated), FruitCreate, and FruitDetail (edit + synonyms editor + image
gallery). 25 backend unit tests + integration test; 18 frontend tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 10:18:01 +02:00
co-authored by Claude Sonnet 4.6
parent a9100fc7d0
commit 1b889ac112
17 changed files with 2507 additions and 15 deletions
+17
View File
@@ -1,5 +1,8 @@
import { createRouter, createWebHistory } from 'vue-router'
import HelloWorld from '../views/HelloWorld.vue'
import FruitList from '../views/FruitList.vue'
import FruitCreate from '../views/FruitCreate.vue'
import FruitDetail from '../views/FruitDetail.vue'
const router = createRouter({
history: createWebHistory(),
@@ -8,6 +11,20 @@ const router = createRouter({
path: '/',
component: HelloWorld,
},
{
path: '/fruits',
component: FruitList,
},
{
// /fruits/new must come before /:id so vue-router v5 doesn't capture "new" as a param
path: '/fruits/new',
component: FruitCreate,
},
{
path: '/fruits/:id',
component: FruitDetail,
props: true,
},
],
})