Some checks failed
Security Scanning / Dependency License Check (pull_request) Has been cancelled
CI Pipeline / Lint Code (pull_request) Has been cancelled
CI Pipeline / Test API Package (pull_request) Has been cancelled
CI Pipeline / Test Web Package (pull_request) Has been cancelled
CI Pipeline / Test Shared Package (pull_request) Has been cancelled
Docker Build & Deploy / Build Docker Images (pull_request) Has been cancelled
E2E Tests / End-to-End Tests (pull_request) Has been cancelled
E2E Tests / E2E Tests (Mobile) (pull_request) Has been cancelled
Security Scanning / NPM Audit (pull_request) Has been cancelled
Security Scanning / Code Quality Scan (pull_request) Has been cancelled
Security Scanning / Docker Image Security (pull_request) Has been cancelled
CI Pipeline / Build All Packages (pull_request) Has been cancelled
CI Pipeline / Generate Coverage Report (pull_request) Has been cancelled
Docker Build & Deploy / Push Docker Images (pull_request) Has been cancelled
Docker Build & Deploy / Deploy to Staging (pull_request) Has been cancelled
Docker Build & Deploy / Deploy to Production (pull_request) Has been cancelled
Security Scanning / Security Summary (pull_request) Has been cancelled
Implement comprehensive solution for managing ingredient-to-instruction mappings in cooking mode. This moves from client-side state management to persistent database storage, significantly improving reliability and user experience. ## Key Changes ### Database & Backend - Add IngredientInstructionMapping table with many-to-many relationship - Implement automatic ingredient matching algorithm with smart name extraction - Add API endpoints for mapping management (update, regenerate) - Create migration script for existing recipes ### Frontend - Simplify CookingMode to read-only display of stored mappings - Add ManageIngredientMappings page with drag-and-drop editing - Remove complex client-side state management (~200 lines) - Add navigation between cooking mode and management interface ### Testing - Add 11 comprehensive unit tests for ingredient matcher service - Update integration tests with proper mocking - All new features fully tested (24/25 API tests passing) ## Benefits - Persistent mappings across all clients/devices - Automatic generation on recipe import/creation - User control via dedicated management interface - Cleaner, more maintainable codebase 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
403 lines
7.0 KiB
CSS
403 lines
7.0 KiB
CSS
/* Manage Ingredient Mappings Styles */
|
|
|
|
.manage-mappings {
|
|
background-color: #fafafa;
|
|
min-height: 100vh;
|
|
padding: 1.5rem;
|
|
font-size: 1.05rem;
|
|
}
|
|
|
|
/* Header */
|
|
.manage-mappings-header {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
padding-bottom: 1.5rem;
|
|
border-bottom: 3px solid #1976d2;
|
|
}
|
|
|
|
.manage-mappings-title h1 {
|
|
font-size: 2rem;
|
|
margin: 0;
|
|
color: #1976d2;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.manage-mappings-title h2 {
|
|
font-size: 1.5rem;
|
|
margin: 0.5rem 0 0 0;
|
|
color: #555;
|
|
font-weight: normal;
|
|
}
|
|
|
|
.manage-mappings-controls {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.manage-mappings-controls button {
|
|
padding: 0.75rem 1.5rem;
|
|
font-size: 1rem;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
background-color: #1976d2;
|
|
color: white;
|
|
font-weight: 500;
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.manage-mappings-controls button:hover:not(:disabled) {
|
|
background-color: #0d47a1;
|
|
}
|
|
|
|
.manage-mappings-controls button:disabled {
|
|
background-color: #ccc;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.manage-mappings-controls .exit-btn {
|
|
background-color: #757575;
|
|
}
|
|
|
|
.manage-mappings-controls .exit-btn:hover {
|
|
background-color: #424242;
|
|
}
|
|
|
|
.manage-mappings-controls .save-btn {
|
|
background-color: #2e7d32;
|
|
}
|
|
|
|
.manage-mappings-controls .save-btn:hover:not(:disabled) {
|
|
background-color: #1b5e20;
|
|
}
|
|
|
|
.manage-mappings-controls .regenerate-btn {
|
|
background-color: #ff9800;
|
|
}
|
|
|
|
.manage-mappings-controls .regenerate-btn:hover:not(:disabled) {
|
|
background-color: #f57c00;
|
|
}
|
|
|
|
.manage-mappings-controls .cooking-mode-btn {
|
|
background-color: #2e7d32;
|
|
}
|
|
|
|
.manage-mappings-controls .cooking-mode-btn:hover {
|
|
background-color: #1b5e20;
|
|
}
|
|
|
|
/* Unsaved changes banner */
|
|
.unsaved-changes-banner {
|
|
background-color: #fff3e0;
|
|
border: 2px solid #ff9800;
|
|
border-radius: 8px;
|
|
padding: 1rem 1.5rem;
|
|
margin-bottom: 1.5rem;
|
|
font-weight: 500;
|
|
color: #e65100;
|
|
}
|
|
|
|
/* Instructions help */
|
|
.instructions-help {
|
|
background-color: #e3f2fd;
|
|
border: 2px solid #1976d2;
|
|
border-radius: 8px;
|
|
padding: 1rem 1.5rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.instructions-help p {
|
|
margin: 0 0 0.5rem 0;
|
|
color: #0d47a1;
|
|
}
|
|
|
|
.instructions-help ul {
|
|
margin: 0;
|
|
padding-left: 1.5rem;
|
|
color: #1565c0;
|
|
}
|
|
|
|
.instructions-help li {
|
|
margin: 0.25rem 0;
|
|
}
|
|
|
|
/* Ingredients section */
|
|
.manage-mappings-ingredients-section {
|
|
background-color: white;
|
|
padding: 2rem;
|
|
border-radius: 12px;
|
|
margin-bottom: 2rem;
|
|
border: 2px solid #2e7d32;
|
|
}
|
|
|
|
.manage-mappings-ingredients-section .hint-text {
|
|
font-size: 0.9rem;
|
|
color: #666;
|
|
font-weight: normal;
|
|
font-style: italic;
|
|
}
|
|
|
|
.manage-mappings-ingredients-section h2 {
|
|
margin-top: 0;
|
|
font-size: 1.75rem;
|
|
color: #2e7d32;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.manage-mappings-ingredients-section h3 {
|
|
font-size: 1.3rem;
|
|
color: #1b5e20;
|
|
margin-top: 1.5rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.manage-mappings-ingredients-section ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.manage-mappings-ingredients-section li.ingredient-item.draggable-source {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.75rem;
|
|
border-radius: 4px;
|
|
cursor: grab;
|
|
transition: background-color 0.2s;
|
|
border-bottom: 1px solid #e0e0e0;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.manage-mappings-ingredients-section li.ingredient-item.draggable-source:hover {
|
|
background-color: #e8f5e9;
|
|
}
|
|
|
|
.manage-mappings-ingredients-section li.ingredient-item.draggable-source:active {
|
|
cursor: grabbing;
|
|
}
|
|
|
|
.ingredient-section {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
/* Instructions section */
|
|
.manage-mappings-instructions {
|
|
margin-bottom: 3rem;
|
|
}
|
|
|
|
.manage-mappings-instructions h2 {
|
|
font-size: 1.75rem;
|
|
color: #1976d2;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
/* Section headers (for multi-section recipes) */
|
|
.instruction-section {
|
|
margin-bottom: 3rem;
|
|
}
|
|
|
|
.section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 1.5rem;
|
|
padding-bottom: 0.75rem;
|
|
border-bottom: 2px solid #1976d2;
|
|
}
|
|
|
|
.section-header h3 {
|
|
font-size: 1.5rem;
|
|
margin: 0;
|
|
color: #0d47a1;
|
|
}
|
|
|
|
.section-timing {
|
|
font-size: 1rem;
|
|
color: #666;
|
|
background-color: #fff3e0;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 6px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Individual instruction steps */
|
|
.instruction-step {
|
|
background-color: white;
|
|
padding: 1.5rem;
|
|
border-radius: 12px;
|
|
margin-bottom: 1.5rem;
|
|
border: 2px solid #e0e0e0;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.instruction-step:hover {
|
|
border-color: #1976d2;
|
|
}
|
|
|
|
.step-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.step-number {
|
|
font-size: 1.3rem;
|
|
font-weight: 700;
|
|
color: #1976d2;
|
|
}
|
|
|
|
.instruction-timing {
|
|
font-size: 1rem;
|
|
color: #d84315;
|
|
background-color: #fff3e0;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 6px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.step-text {
|
|
font-size: 1.2rem;
|
|
line-height: 1.8;
|
|
color: #212121;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
/* Inline ingredients */
|
|
.inline-ingredients {
|
|
background-color: #f1f8e9;
|
|
padding: 1rem 1.25rem;
|
|
border-radius: 8px;
|
|
margin-top: 1rem;
|
|
border-left: 4px solid #2e7d32;
|
|
}
|
|
|
|
.inline-ingredients strong {
|
|
color: #1b5e20;
|
|
font-size: 1.05rem;
|
|
display: block;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.inline-ingredients ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.inline-ingredients li.ingredient-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.5rem;
|
|
padding: 0.5rem;
|
|
border-radius: 4px;
|
|
transition: background-color 0.2s;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.inline-ingredients li.ingredient-item:hover {
|
|
background-color: #dcedc8;
|
|
}
|
|
|
|
.inline-ingredients .no-ingredients {
|
|
color: #757575;
|
|
font-style: italic;
|
|
margin: 0.5rem 0;
|
|
}
|
|
|
|
.drag-handle {
|
|
color: #9e9e9e;
|
|
font-size: 1rem;
|
|
cursor: grab;
|
|
user-select: none;
|
|
}
|
|
|
|
.ingredient-text {
|
|
flex: 1;
|
|
font-size: 1.05rem;
|
|
}
|
|
|
|
.remove-ingredient-btn {
|
|
background: none;
|
|
border: none;
|
|
color: #d32f2f;
|
|
font-size: 1.25rem;
|
|
cursor: pointer;
|
|
padding: 0.25rem 0.5rem;
|
|
border-radius: 4px;
|
|
transition: background-color 0.2s;
|
|
line-height: 1;
|
|
}
|
|
|
|
.remove-ingredient-btn:hover {
|
|
background-color: #ffebee;
|
|
}
|
|
|
|
/* Step images */
|
|
.step-image {
|
|
width: 100%;
|
|
max-width: 600px;
|
|
border-radius: 8px;
|
|
margin-top: 1rem;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
/* Footer */
|
|
.manage-mappings-footer {
|
|
text-align: center;
|
|
padding: 2rem 0;
|
|
border-top: 2px solid #e0e0e0;
|
|
}
|
|
|
|
.save-btn-large {
|
|
padding: 1rem 3rem;
|
|
font-size: 1.25rem;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
background-color: #2e7d32;
|
|
color: white;
|
|
font-weight: 600;
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.save-btn-large:hover:not(:disabled) {
|
|
background-color: #1b5e20;
|
|
}
|
|
|
|
.save-btn-large:disabled {
|
|
background-color: #ccc;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* Responsive adjustments */
|
|
@media (max-width: 768px) {
|
|
.manage-mappings-title h1 {
|
|
font-size: 1.75rem;
|
|
}
|
|
|
|
.manage-mappings-controls {
|
|
flex-direction: column;
|
|
width: 100%;
|
|
}
|
|
|
|
.manage-mappings-controls button {
|
|
width: 100%;
|
|
}
|
|
|
|
.step-text {
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.inline-ingredients li {
|
|
font-size: 1rem;
|
|
}
|
|
}
|