feat: add product selection step before meal planning
Inserts a new PlanProductsSheet as step 1 of the planning flow. Users see their current products as a multi-select checklist (all selected by default) before choosing the planning mode and dates. - Empty state explains the benefit and offers "Add products" CTA while always allowing "Plan without products" to skip - Selected product IDs flow through PlanMenuSheet → PlanDatePickerSheet → MenuService.generateForDates → backend - Backend: added ProductIDs field to generate-menu request body; uses ListForPromptByIDs when set, ListForPrompt otherwise - Backend: added Repository.ListForPromptByIDs (filtered SQL query) - All 12 ARB locale files updated with planProducts* keys Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -161,6 +161,46 @@ func (r *Repository) ListForPrompt(requestContext context.Context, userID string
|
||||
return lines, rows.Err()
|
||||
}
|
||||
|
||||
func (r *Repository) ListForPromptByIDs(requestContext context.Context, userID string, ids []string) ([]string, error) {
|
||||
rows, queryError := r.pool.Query(requestContext, `
|
||||
WITH up AS (
|
||||
SELECT name, quantity, unit,
|
||||
(added_at + storage_days * INTERVAL '1 day') AS expires_at
|
||||
FROM user_products
|
||||
WHERE user_id = $1 AND id = ANY($2)
|
||||
)
|
||||
SELECT name, quantity, unit, expires_at
|
||||
FROM up
|
||||
ORDER BY expires_at ASC`, userID, ids)
|
||||
if queryError != nil {
|
||||
return nil, fmt.Errorf("list user products by ids for prompt: %w", queryError)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var lines []string
|
||||
now := time.Now()
|
||||
for rows.Next() {
|
||||
var name, unit string
|
||||
var qty float64
|
||||
var expiresAt time.Time
|
||||
if scanError := rows.Scan(&name, &qty, &unit, &expiresAt); scanError != nil {
|
||||
return nil, fmt.Errorf("scan user product for prompt: %w", scanError)
|
||||
}
|
||||
daysLeft := int(expiresAt.Sub(now).Hours() / 24)
|
||||
line := fmt.Sprintf("- %s %.0f %s", name, qty, unit)
|
||||
switch {
|
||||
case daysLeft <= 0:
|
||||
line += " (expires today ⚠)"
|
||||
case daysLeft == 1:
|
||||
line += " (expires tomorrow ⚠)"
|
||||
case daysLeft <= 3:
|
||||
line += fmt.Sprintf(" (expires in %d days ⚠)", daysLeft)
|
||||
}
|
||||
lines = append(lines, line)
|
||||
}
|
||||
return lines, rows.Err()
|
||||
}
|
||||
|
||||
// --- helpers ---
|
||||
|
||||
func scanUserProduct(row pgx.Row) (*UserProduct, error) {
|
||||
|
||||
Reference in New Issue
Block a user