Spec: 04 · Fruit Publications
Taiga Story: #4 · 04-fruit-publications
Status: Ready
Branch: feature/04-fruit-publications
Depends on: 02-manage-fruits (fruits table)
1. Goal
Maintainers can manage publications (books, catalogues) that document fruits. Each publication has a cover image, linked fruits, linked PDFs per fruit, and linked fruit images from the publication. Fruit detail page shows descriptions and publication images.
2. Background / Scope / Context / Constraints
osdb_pub_id is the unique external identifier (label: "OSDB Kürzel").
- Cover image stored as BYTEA in
publications.image_data.
- PDF stored as BYTEA in
publication_descriptions.pdf_data (one PDF per fruit per publication).
- Fruit images from publications stored in
publication_fruit_images (BYTEA); image_type hardcoded to 'fruit', cannot be changed.
- On fruit detail page: show description list only if at least one description exists; link text = publication title; link opens PDF.
- On fruit detail page: show publication fruit images labeled "Publication title · Author".
- Mouseclick on cover image → overlay/drawer with full-size image.
3. Necessary Refactorings
fruit_images.image_type already defined in story #2 — no changes.
thumbnail_data column on publication_fruit_images deferred to story #7 migration.
4. Related Data Model
5. Affected Places / Related Components
Backend
| File |
Purpose |
backend/internal/domain/publication.go |
Publication, PublicationDescription, PublicationFruitImage structs |
backend/internal/repository/publication_repo.go |
DB queries |
backend/internal/handler/publication_handler.go |
Echo handlers |
backend/cmd/server/router.go |
Register /api/v1/publications routes |
migrations/000003_create_publications.up.sql |
Schema above |
migrations/000003_create_publications.down.sql |
DROP TABLEs |
API Endpoints
| Method |
Path |
Description |
| GET |
/api/v1/publications |
List |
| POST |
/api/v1/publications |
Create |
| GET |
/api/v1/publications/:id |
Get (with linked fruits list) |
| PUT |
/api/v1/publications/:id |
Update |
| DELETE |
/api/v1/publications/:id |
Delete |
| POST |
/api/v1/publications/:id/image |
Upload cover image |
| GET |
/api/v1/publications/:id/image |
Serve cover image |
| DELETE |
/api/v1/publications/:id/image |
Delete cover image |
| GET |
/api/v1/publications/:id/fruits |
List linked fruits |
| POST |
/api/v1/publications/:id/fruits |
Link a fruit |
| DELETE |
/api/v1/publications/:id/fruits/:fruitId |
Unlink a fruit |
| GET |
/api/v1/publications/:id/descriptions |
List descriptions (fruit + link metadata) |
| POST |
/api/v1/publications/:id/descriptions |
Upload PDF description (body: fruitId + PDF) |
| GET |
/api/v1/publications/:id/descriptions/:descId |
Serve PDF |
| DELETE |
/api/v1/publications/:id/descriptions/:descId |
Delete |
| GET |
/api/v1/publications/:id/fruit-images |
List pub fruit images |
| POST |
/api/v1/publications/:id/fruit-images |
Upload fruit image (fruitId + image) |
| GET |
/api/v1/publications/:id/fruit-images/:imgId |
Serve image |
| DELETE |
/api/v1/publications/:id/fruit-images/:imgId |
Delete |
| GET |
/api/v1/fruits/:id/descriptions |
All descriptions for a fruit (across publications) |
| GET |
/api/v1/fruits/:id/publication-images |
All pub images for a fruit |
Frontend
| File |
Purpose |
frontend/src/api/publications.ts |
API wrapper |
frontend/src/stores/publicationStore.ts |
Pinia store |
frontend/src/views/PublicationList.vue |
List + link to detail |
frontend/src/views/PublicationDetail.vue |
Full management UI |
frontend/src/components/ImageOverlayDrawer.vue |
Full-size image overlay on click |
frontend/src/views/FruitDetail.vue |
Extended: descriptions section + pub images section |
frontend/src/router/index.ts |
Add /publications, /publications/:id |
6. Out of Scope
- Bulk import from XML (story #5)
- Thumbnail generation (story #7)
- Authentication guards (story #8)
7. Verification
| Check |
Expected |
| Create publication with duplicate osdb_pub_id |
409 Conflict |
| Upload cover image → serve back → correct bytes |
Pass |
| Link fruit to publication → appears in GET /publications/:id/fruits |
Pass |
| Upload PDF description → GET description → Content-Type: application/pdf |
Pass |
| Fruit detail page: no descriptions → section hidden |
Pass |
| Fruit detail page: 1+ description → links visible |
Pass |
| Click cover image thumbnail → overlay opens full-size |
Pass |
| Pub fruit images on fruit detail labeled correctly |
Publication title · Author shown |
go test ./... |
Green |
npm run test |
Green |
8. Open Questions
| # |
Question |
Impact |
| 1 |
Is one PDF per (publication, fruit) pair enforced (UNIQUE) or allow multiples? |
Schema + UI |
| 2 |
Can a fruit image exist in a publication without the fruit being linked via publication_fruits? |
Data integrity |
| 3 |
Listing publications: include fruit count or just metadata? |
API response shape |
| 4 |
PDF viewer: open in new tab or inline browser PDF viewer? |
UX |