Files
calcount/frontend/src/lib/stores.svelte.js
T
Craig b69661c997 TICKET-005: Frontend daily view — milestone M1 complete
- Dashboard: progress bar vs target, entries grouped by meal slot,
  edit/delete inline, date navigation (UTC-safe shiftDate helper)
- Add Food manual creation form, search-and-log flow with live preview
- Minimal target form; loading/error/empty states throughout
- Stores (current date, log, summary) with summary refresh on mutation
- All HTTP via lib/api.js; formatting via lib/format.js; runes only
- Full suites green (backend 104 passed, frontend vitest + build)
2026-07-26 13:55:39 +01:00

91 lines
3.0 KiB
JavaScript

/**
* Shared state lives in stores, not prop-drilling (spec §8.2 rule 3).
* Mutation flow: component → api.js → update store from the response.
* Svelte 5 runes style only (spec §8.2 rule 8).
*/
import { api } from './api.js'
import { shiftDate } from './format.js'
/** Return today as a YYYY-MM-DD string — the server never decides "today" (spec §8.1 rule 8). */
function todayString() {
return new Date().toISOString().slice(0, 10)
}
// ── Current date ────────────────────────────────────────────────────────────
export const currentDate = $state({ value: todayString() })
export function setDate(dateStr) {
currentDate.value = dateStr
}
export function goPrevDay() {
currentDate.value = shiftDate(currentDate.value, -1)
}
export function goNextDay() {
currentDate.value = shiftDate(currentDate.value, 1)
}
// ── App view ─────────────────────────────────────────────────────────────────
export const appView = $state({ current: 'dashboard' })
/** Simple navigation requests from child components (e.g., "Set a target"). */
export const navigateTo = (view) => { appView.current = view }
// ── Day data (log + summary) ─────────────────────────────────────────────────
export const dayData = $state({
log: [],
summary: null, // { date, totals: { calories, protein_g, ... }, target: {...} | null }
loading: false,
error: null,
})
export async function refreshDayData() {
const date = currentDate.value
dayData.loading = true
dayData.error = null
try {
const [log, summary] = await Promise.all([
api.getLog(date),
api.getSummary(date),
])
dayData.log = log
dayData.summary = summary
dayData.loading = false
} catch (e) {
dayData.error = e.message
dayData.loading = false
}
}
/** Re-fetch the summary for the current date without touching the log array. */
async function refreshSummary() {
try {
dayData.summary = await api.getSummary(currentDate.value)
} catch {
// Keep the previous summary on failure; the user can retry.
}
}
/** Add a newly-created log entry to the store then refresh the summary. */
export async function addLogEntryToStore(entry) {
dayData.log = [...dayData.log, entry]
await refreshSummary()
}
/** Update a log entry in the store after a successful API PUT, then refresh the summary. */
export async function updateLogEntryInStore(updated) {
dayData.log = dayData.log.map(e => (e.id === updated.id ? updated : e))
await refreshSummary()
}
/** Remove a log entry from the store after a successful API DELETE, then refresh the summary. */
export async function removeLogEntryFromStore(id) {
dayData.log = dayData.log.filter(e => e.id !== id)
await refreshSummary()
}