Implement graceful recipe error handling with filename display (#4363)
Signed-off-by: Jon Andersen <jon.andersen.se@gmail.com>
This commit is contained in:
@@ -372,67 +372,90 @@ Parameters you can use:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render a recipe item
|
// Render a recipe item with error handling
|
||||||
const RecipeItem = ({ savedRecipe }: { savedRecipe: SavedRecipe }) => (
|
const RecipeItem = ({ savedRecipe }: { savedRecipe: SavedRecipe }) => {
|
||||||
<Card className="py-2 px-4 mb-2 bg-background-default border-none hover:bg-background-muted cursor-pointer transition-all duration-150">
|
try {
|
||||||
<div className="flex justify-between items-start gap-4">
|
return (
|
||||||
<div className="min-w-0 flex-1">
|
<Card className="py-2 px-4 mb-2 bg-background-default border-none hover:bg-background-muted cursor-pointer transition-all duration-150">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex justify-between items-start gap-4">
|
||||||
<h3 className="text-base truncate max-w-[50vw]">{savedRecipe.recipe.title}</h3>
|
<div className="min-w-0 flex-1">
|
||||||
{savedRecipe.isGlobal ? (
|
<div className="flex items-center gap-2 mb-1">
|
||||||
<Globe className="w-4 h-4 text-text-muted flex-shrink-0" />
|
<h3 className="text-base truncate max-w-[50vw]">{savedRecipe.recipe.title}</h3>
|
||||||
) : (
|
{savedRecipe.isGlobal ? (
|
||||||
<Folder className="w-4 h-4 text-text-muted flex-shrink-0" />
|
<Globe className="w-4 h-4 text-text-muted flex-shrink-0" />
|
||||||
)}
|
) : (
|
||||||
</div>
|
<Folder className="w-4 h-4 text-text-muted flex-shrink-0" />
|
||||||
<p className="text-text-muted text-sm mb-2 line-clamp-2">
|
)}
|
||||||
{savedRecipe.recipe.description}
|
</div>
|
||||||
</p>
|
<p className="text-text-muted text-sm mb-2 line-clamp-2">
|
||||||
<div className="flex items-center text-xs text-text-muted">
|
{savedRecipe.recipe.description}
|
||||||
<Calendar className="w-3 h-3 mr-1" />
|
</p>
|
||||||
{savedRecipe.lastModified.toLocaleDateString()}
|
<div className="flex items-center text-xs text-text-muted">
|
||||||
</div>
|
<Calendar className="w-3 h-3 mr-1" />
|
||||||
</div>
|
{savedRecipe.lastModified.toLocaleDateString()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 shrink-0">
|
<div className="flex items-center gap-2 shrink-0">
|
||||||
<Button
|
<Button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleLoadRecipe(savedRecipe);
|
handleLoadRecipe(savedRecipe);
|
||||||
}}
|
}}
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-8"
|
className="h-8"
|
||||||
>
|
>
|
||||||
<Bot className="w-4 h-4 mr-1" />
|
<Bot className="w-4 h-4 mr-1" />
|
||||||
Use
|
Use
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handlePreviewRecipe(savedRecipe);
|
handlePreviewRecipe(savedRecipe);
|
||||||
}}
|
}}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-8"
|
className="h-8"
|
||||||
>
|
>
|
||||||
<FileText className="w-4 h-4 mr-1" />
|
<FileText className="w-4 h-4 mr-1" />
|
||||||
Preview
|
Preview
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleDeleteRecipe(savedRecipe);
|
handleDeleteRecipe(savedRecipe);
|
||||||
}}
|
}}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-8 text-red-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20"
|
className="h-8 text-red-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20"
|
||||||
>
|
>
|
||||||
<Trash2 className="w-4 h-4" />
|
<Trash2 className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
} catch (error) {
|
||||||
|
// Error row showing failed to read file with filename and error details
|
||||||
|
return (
|
||||||
|
<Card className="py-2 px-4 mb-2 bg-red-50 border border-red-200 dark:bg-red-900/20 dark:border-red-800">
|
||||||
|
<div className="flex justify-between items-start gap-4">
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<div className="flex items-center gap-2 mb-1">
|
||||||
|
<AlertCircle className="w-4 h-4 text-red-500 flex-shrink-0" />
|
||||||
|
<h3 className="text-base text-red-700 dark:text-red-300">
|
||||||
|
Failed to read file: {savedRecipe.filename}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-red-600 dark:text-red-400 text-sm">
|
||||||
|
{error instanceof Error ? error.message : 'Unknown error'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Render skeleton loader for recipe items
|
// Render skeleton loader for recipe items
|
||||||
const RecipeSkeleton = () => (
|
const RecipeSkeleton = () => (
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export interface SavedRecipe {
|
|||||||
isGlobal: boolean;
|
isGlobal: boolean;
|
||||||
lastModified: Date;
|
lastModified: Date;
|
||||||
isArchived?: boolean;
|
isArchived?: boolean;
|
||||||
|
filename: string; // The actual filename used
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,6 +67,7 @@ async function loadRecipeFromFile(
|
|||||||
return {
|
return {
|
||||||
...recipeData,
|
...recipeData,
|
||||||
isGlobal: isGlobal,
|
isGlobal: isGlobal,
|
||||||
|
filename: recipeName,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Failed to load recipe from ${filePath}:`, error);
|
console.warn(`Failed to load recipe from ${filePath}:`, error);
|
||||||
@@ -112,6 +114,7 @@ export async function saveRecipe(recipe: Recipe, options: SaveRecipeOptions): Pr
|
|||||||
// Create saved recipe object
|
// Create saved recipe object
|
||||||
const savedRecipe: SavedRecipe = {
|
const savedRecipe: SavedRecipe = {
|
||||||
name: sanitizedName,
|
name: sanitizedName,
|
||||||
|
filename: sanitizedName,
|
||||||
recipe: recipe,
|
recipe: recipe,
|
||||||
isGlobal: global,
|
isGlobal: global,
|
||||||
lastModified: new Date(),
|
lastModified: new Date(),
|
||||||
|
|||||||
Reference in New Issue
Block a user