Redesign recipe creation and edit view with Form-based layout and URL import
Replace the single "+" button with a 2-option menu (Create New Recipe / Import from URL) across RecipeTabView, RecipeListView, and AllRecipesListView. Add ImportURLSheet for server-side recipe import with loading and error states. Completely redesign edit mode to use a native Form layout with inline editing for all sections (metadata, duration, ingredients, instructions, tools, nutrition) instead of the previous sheet-based EditableListView approach. Move delete action from edit toolbar to view mode context menu. Add recipe image display to the edit form. Refactor RecipeListView and AllRecipesListView to use closure-based callbacks instead of Binding<Bool> for the create/import actions. Add preloadedRecipeDetail support to RecipeView.ViewModel for imported recipes. Add DE/ES/FR translations for all new UI strings. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -87,6 +87,52 @@ struct RecipeMetadataSection: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Recipe Edit Metadata Section (Form-based)
|
||||
|
||||
struct RecipeEditMetadataSection: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@ObservedObject var viewModel: RecipeView.ViewModel
|
||||
|
||||
var categories: [String] {
|
||||
appState.categories.map { $0.name }
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Section("Details") {
|
||||
Picker("Category", selection: $viewModel.observableRecipeDetail.recipeCategory) {
|
||||
Text("None").tag("")
|
||||
ForEach(categories, id: \.self) { item in
|
||||
Text(item).tag(item)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
|
||||
Stepper("Servings: \(viewModel.observableRecipeDetail.recipeYield)", value: $viewModel.observableRecipeDetail.recipeYield, in: 1...99)
|
||||
|
||||
Button {
|
||||
viewModel.presentKeywordSheet = true
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Keywords")
|
||||
.foregroundStyle(.primary)
|
||||
Spacer()
|
||||
if viewModel.observableRecipeDetail.keywords.isEmpty {
|
||||
Text("None")
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
Text(viewModel.observableRecipeDetail.keywords.joined(separator: ", "))
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct PickerPopoverView<Item: Hashable & CustomStringConvertible, Collection: Sequence>: View where Collection.Element == Item {
|
||||
@Binding var isPresented: Bool
|
||||
@Binding var value: Item
|
||||
|
||||
Reference in New Issue
Block a user