GET /shopping-list?date= (shopping-list.service.ts/.routes.ts) somme les ingrédients de chaque recette planifiée sur la semaine, mis à l'échelle par les portions de chaque créneau (PlanningItem.portions / Recipe.portions), regroupés par paire (ingredientId, unitId) — jamais null contrairement à GET /planning, une semaine vide redescend en items: []. Côté web, ShoppingListPage rend cette liste groupée par rayon (même IngredientCategory que IngredientPicker), triée alphabétiquement en français à l'intérieur d'un rayon (shopping-list.ts, logique pure extraite du composant). WeekNavigator (flèches + calendrier) est extrait de PlanningPage vers features/planning/ pour être partagé entre les deux pages ; ses libellés migrent de planning.* vers common.weekNav.*/ common.calendar.*/common.days.*, plus génériques pour une page qui n'est plus seulement le planning. ComingSoonPage retiré (plus aucun appelant, Liste de courses avait le dernier stub restant). Tests : Mocha (agrégation, mise à l'échelle par portions, unités non fusionnées) + Cucumber (shopping-list.feature : liste vide, groupement/tri, navigation de semaine) + mise à jour de layout.cy.ts/planning-page.cy.ts pour le nouveau rendu. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
143 lines
3.4 KiB
SCSS
143 lines
3.4 KiB
SCSS
// =============================================================================
|
|
// Styles for WeekNavigator.tsx (arrows + label + calendar popover) —
|
|
// colocated next to the component since nothing else uses these classes.
|
|
// Extracted from planning-page.scss once ShoppingListPage started reusing
|
|
// the component — same design tokens, no light/dark duplication needed
|
|
// (every `var(--color-*)` below already resolves per-theme globally, see
|
|
// styles/_theme.scss).
|
|
// =============================================================================
|
|
|
|
.week-nav {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
|
|
&__arrow {
|
|
width: 2rem;
|
|
height: 2rem;
|
|
display: grid;
|
|
place-items: center;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-base);
|
|
background: var(--color-surface);
|
|
color: var(--color-text);
|
|
font-size: var(--font-size-md);
|
|
cursor: pointer;
|
|
|
|
&:hover {
|
|
background: var(--color-surface-alt);
|
|
}
|
|
}
|
|
|
|
&__label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
padding: 0.45rem var(--space-md);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-base);
|
|
background: var(--color-surface);
|
|
color: var(--color-text);
|
|
font-weight: 600;
|
|
font-size: var(--font-size-sm);
|
|
cursor: pointer;
|
|
|
|
&:hover {
|
|
background: var(--color-surface-alt);
|
|
}
|
|
}
|
|
}
|
|
|
|
.today-badge {
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 600;
|
|
color: var(--color-primary);
|
|
background: color-mix(in srgb, var(--color-primary) 14%, transparent);
|
|
padding: 0.1rem 0.4rem;
|
|
border-radius: var(--radius-pill);
|
|
}
|
|
|
|
// --- Calendar popover -------------------------------------------------------
|
|
.calendar-popover {
|
|
position: absolute;
|
|
top: calc(100% + var(--space-xs));
|
|
right: 0;
|
|
z-index: 10;
|
|
width: 18rem;
|
|
padding: var(--space-md);
|
|
background: var(--color-surface);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-md);
|
|
box-shadow: var(--shadow-md);
|
|
|
|
&__header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: var(--space-sm);
|
|
font-weight: 700;
|
|
font-size: var(--font-size-sm);
|
|
text-transform: capitalize;
|
|
|
|
button {
|
|
width: 1.6rem;
|
|
height: 1.6rem;
|
|
border: none;
|
|
background: none;
|
|
cursor: pointer;
|
|
font-size: var(--font-size-base);
|
|
color: var(--color-text-muted);
|
|
border-radius: var(--radius-base);
|
|
|
|
&:hover {
|
|
background: var(--color-surface-alt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.calendar-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
gap: 2px;
|
|
|
|
&__weekday {
|
|
text-align: center;
|
|
font-size: var(--font-size-xs);
|
|
color: var(--color-text-muted);
|
|
font-weight: 600;
|
|
padding-bottom: var(--space-xs);
|
|
}
|
|
|
|
&__day {
|
|
aspect-ratio: 1;
|
|
display: grid;
|
|
place-items: center;
|
|
font-size: var(--font-size-sm);
|
|
border-radius: var(--radius-base);
|
|
cursor: pointer;
|
|
color: var(--color-text);
|
|
border: none;
|
|
background: none;
|
|
font: inherit;
|
|
|
|
&:hover {
|
|
background: var(--color-surface-alt);
|
|
}
|
|
|
|
&--muted {
|
|
color: var(--color-border);
|
|
}
|
|
|
|
&--in-selected-week {
|
|
background: color-mix(in srgb, var(--color-primary) 14%, transparent);
|
|
border-radius: 0;
|
|
}
|
|
|
|
&--today {
|
|
box-shadow: inset 0 0 0 2px var(--color-primary);
|
|
font-weight: 700;
|
|
}
|
|
}
|
|
}
|