feat: fruit search by name/synonym and type filter (story #06)
Backend: List repo query gains name (ILIKE with wildcard escaping on
name + synonym LEFT JOIN, SELECT DISTINCT) and types (ANY cast) params;
handler parses ?name= and ?type=, resolves combined-type aliases from
domain.FruitTypeAliases, validates plain types against validFruitTypes
to prevent Postgres enum cast errors. ORDER BY f.name, f.id for stable
pagination.
Frontend: listFruits gains optional {name, type} params; fruitStore adds
searchName/searchType state and setSearch action (resets offset, refetches);
FruitList.vue wires text input (debounced 300 ms + Enter) and flat type
dropdown (17 enum values + 4 aliases per spec §5 order); debounce timer
cleared on unmount.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,7 @@ var (
|
||||
// FruitRepository is the consumer-defined interface the handler depends on.
|
||||
// The pg implementation in the repository package satisfies this structurally.
|
||||
type FruitRepository interface {
|
||||
List(ctx context.Context, limit, offset int) ([]domain.Fruit, int, error)
|
||||
List(ctx context.Context, limit, offset int, name string, types []string) ([]domain.Fruit, int, error)
|
||||
Get(ctx context.Context, id int) (domain.Fruit, error)
|
||||
Create(ctx context.Context, dto domain.FruitWriteDTO) (domain.Fruit, error)
|
||||
Update(ctx context.Context, id int, dto domain.FruitWriteDTO) (domain.Fruit, error)
|
||||
@@ -107,7 +107,18 @@ func (h *FruitHandler) List(c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
fruits, total, err := h.repo.List(c.Request().Context(), limit, offset)
|
||||
name := c.QueryParam("name")
|
||||
var types []string
|
||||
if typeParam := c.QueryParam("type"); typeParam != "" {
|
||||
if expanded, ok := domain.FruitTypeAliases[typeParam]; ok {
|
||||
types = expanded
|
||||
} else if _, ok := validFruitTypes[typeParam]; ok {
|
||||
types = []string{typeParam}
|
||||
}
|
||||
// unknown typeParam → types stays nil → no filter applied
|
||||
}
|
||||
|
||||
fruits, total, err := h.repo.List(c.Request().Context(), limit, offset, name, types)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "internal server error"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user