feat: improve recipe import UX and add comprehensive test coverage
## Changes ### Recipe Import Improvements - Move tag input to top of import preview for better UX - Allow users to add tags immediately after importing, before viewing full details - Keep focus in tag input field after pressing Enter for rapid tag addition ### Recipe Scraper Enhancements - Remove deprecated supported_only parameter from Python scraper - Update Dockerfile to explicitly install latest recipe-scrapers package - Ensure compatibility with latest recipe-scrapers library (14.55.0+) ### Testing Infrastructure - Add comprehensive tests for recipe tagging features (87% coverage) - Add real integration tests for auth routes (37% coverage on auth.routes.ts) - Add real integration tests for backup routes (74% coverage on backup.routes.ts) - Add real integration tests for scraper service (67% coverage) - Overall project coverage improved from 72.7% to 77.6% ### Test Coverage Details - 377 tests passing (up from 341) - 7 new tests for quick tagging feature - 17 new tests for authentication flows - 16 new tests for backup functionality - 6 new tests for recipe scraper integration All tests verify: - Tag CRUD operations work correctly - Tags properly connected using connectOrCreate pattern - Recipe import with live URL scraping - Security (path traversal prevention, rate limiting) - Error handling and validation Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -118,46 +118,9 @@ function RecipeImport() {
|
||||
<div className="recipe-detail" style={{ marginTop: '2rem' }}>
|
||||
<h3>Imported Recipe Preview</h3>
|
||||
|
||||
{importedRecipe.imageUrl && (
|
||||
<img src={importedRecipe.imageUrl} alt={importedRecipe.title} />
|
||||
)}
|
||||
|
||||
<h2>{importedRecipe.title}</h2>
|
||||
|
||||
{importedRecipe.description && <p>{importedRecipe.description}</p>}
|
||||
|
||||
<div className="recipe-meta">
|
||||
{importedRecipe.prepTime && <span>Prep: {importedRecipe.prepTime} min</span>}
|
||||
{importedRecipe.cookTime && <span>Cook: {importedRecipe.cookTime} min</span>}
|
||||
{importedRecipe.totalTime && <span>Total: {importedRecipe.totalTime} min</span>}
|
||||
{importedRecipe.servings && <span>Servings: {importedRecipe.servings}</span>}
|
||||
</div>
|
||||
|
||||
{importedRecipe.ingredients && importedRecipe.ingredients.length > 0 && (
|
||||
<div className="ingredients">
|
||||
<h3>Ingredients</h3>
|
||||
<ul>
|
||||
{importedRecipe.ingredients.map((ingredient, index) => (
|
||||
<li key={index}>{ingredient.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{importedRecipe.instructions && importedRecipe.instructions.length > 0 && (
|
||||
<div className="instructions">
|
||||
<h3>Instructions</h3>
|
||||
<ol>
|
||||
{importedRecipe.instructions.map((instruction) => (
|
||||
<li key={instruction.step}>{instruction.text}</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tag Management Section */}
|
||||
<div className="import-tags-section" style={{ marginTop: '2rem', marginBottom: '2rem' }}>
|
||||
<h3>Add Tags</h3>
|
||||
{/* Tag Management Section - Moved to top */}
|
||||
<div className="import-tags-section" style={{ marginTop: '1rem', marginBottom: '2rem' }}>
|
||||
<h4>Add Tags</h4>
|
||||
<div className="import-tags-inline">
|
||||
<div className="import-tags-display">
|
||||
{selectedTags.length > 0 ? (
|
||||
@@ -212,6 +175,43 @@ function RecipeImport() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{importedRecipe.imageUrl && (
|
||||
<img src={importedRecipe.imageUrl} alt={importedRecipe.title} />
|
||||
)}
|
||||
|
||||
<h2>{importedRecipe.title}</h2>
|
||||
|
||||
{importedRecipe.description && <p>{importedRecipe.description}</p>}
|
||||
|
||||
<div className="recipe-meta">
|
||||
{importedRecipe.prepTime && <span>Prep: {importedRecipe.prepTime} min</span>}
|
||||
{importedRecipe.cookTime && <span>Cook: {importedRecipe.cookTime} min</span>}
|
||||
{importedRecipe.totalTime && <span>Total: {importedRecipe.totalTime} min</span>}
|
||||
{importedRecipe.servings && <span>Servings: {importedRecipe.servings}</span>}
|
||||
</div>
|
||||
|
||||
{importedRecipe.ingredients && importedRecipe.ingredients.length > 0 && (
|
||||
<div className="ingredients">
|
||||
<h3>Ingredients</h3>
|
||||
<ul>
|
||||
{importedRecipe.ingredients.map((ingredient, index) => (
|
||||
<li key={index}>{ingredient.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{importedRecipe.instructions && importedRecipe.instructions.length > 0 && (
|
||||
<div className="instructions">
|
||||
<h3>Instructions</h3>
|
||||
<ol>
|
||||
{importedRecipe.instructions.map((instruction) => (
|
||||
<li key={instruction.step}>{instruction.text}</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button onClick={handleSave} disabled={loading}>
|
||||
Save Recipe
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user