203 lines
7.5 KiB
Swift
203 lines
7.5 KiB
Swift
import SwiftUI
|
|
|
|
struct DishDrawerView: View {
|
|
let dishes: [Dish]
|
|
let tags: [Tag]
|
|
let language: AppLanguage
|
|
let usedDishIds: Set<UUID>
|
|
var onAddDish: () -> Void
|
|
var onQuickAssignDish: (Dish) -> Void
|
|
var onEditDish: (Dish) -> Void
|
|
var onDeleteDish: (Dish) -> Void
|
|
@Binding var draggedDish: Dish?
|
|
@State private var searchText: String = ""
|
|
@State private var hideUsedThisWeek: Bool = false
|
|
|
|
private var filteredDishes: [Dish] {
|
|
let term = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
return Dish.stableSortedForDisplay(dishes).filter { dish in
|
|
let matchesSearch = term.isEmpty || dish.name.localizedCaseInsensitiveContains(term)
|
|
let matchesUsedFilter = !hideUsedThisWeek || !usedDishIds.contains(dish.id)
|
|
return matchesSearch && matchesUsedFilter
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(spacing: 12) {
|
|
HStack {
|
|
HStack(spacing: 6) {
|
|
Image(systemName: "list.clipboard")
|
|
Text("home_my_dishes")
|
|
.font(.mealMoodH3)
|
|
}
|
|
.foregroundColor(.mealMoodTextPrimary)
|
|
|
|
Spacer()
|
|
|
|
Button(action: onAddDish) {
|
|
Image(systemName: "plus.circle.fill")
|
|
.font(.system(size: 28))
|
|
.foregroundColor(.mealMoodCoral)
|
|
}
|
|
}
|
|
.padding(.horizontal, 16)
|
|
|
|
if dishes.isEmpty {
|
|
VStack(spacing: 16) {
|
|
Image(systemName: "fork.knife")
|
|
.font(.system(size: 40))
|
|
.foregroundColor(Color(hex: "#C4C4C4"))
|
|
|
|
Text("home_add_first_dish")
|
|
.font(.mealMoodBody)
|
|
.foregroundColor(.mealMoodTextSecondary)
|
|
|
|
PrimaryButton(title: String(localized: "home_add_dish"), icon: nil, action: onAddDish)
|
|
.padding(.horizontal, 40)
|
|
}
|
|
.padding(.vertical, 32)
|
|
} else {
|
|
HStack(spacing: 8) {
|
|
Image(systemName: "magnifyingglass")
|
|
.foregroundColor(.mealMoodTextSecondary)
|
|
TextField("home_my_dishes_search", text: $searchText)
|
|
.font(.mealMoodBody)
|
|
if !searchText.isEmpty {
|
|
Button {
|
|
searchText = ""
|
|
} label: {
|
|
Image(systemName: "xmark.circle.fill")
|
|
.foregroundColor(.mealMoodTextSecondary)
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|
|
.padding(.horizontal, 12)
|
|
.padding(.vertical, 10)
|
|
.background(Color.mealMoodSurface)
|
|
.cornerRadius(12)
|
|
.padding(.horizontal, 16)
|
|
|
|
Button {
|
|
hideUsedThisWeek.toggle()
|
|
} label: {
|
|
HStack(spacing: 8) {
|
|
Image(systemName: hideUsedThisWeek ? "checkmark.square.fill" : "square")
|
|
.foregroundColor(hideUsedThisWeek ? .mealMoodCoral : .mealMoodTextSecondary)
|
|
Text("home_my_dishes_hide_used")
|
|
.font(.mealMoodSmall)
|
|
.foregroundColor(.mealMoodTextPrimary)
|
|
Spacer()
|
|
}
|
|
.padding(.horizontal, 16)
|
|
.padding(.vertical, 6)
|
|
}
|
|
.buttonStyle(.plain)
|
|
|
|
LazyVStack(spacing: 8) {
|
|
ForEach(filteredDishes, id: \.id) { dish in
|
|
DishCardView(
|
|
dish: dish,
|
|
tags: tags,
|
|
language: language,
|
|
onEdit: { onEditDish(dish) },
|
|
onDelete: { onDeleteDish(dish) }
|
|
)
|
|
.contentShape(Rectangle())
|
|
.onTapGesture {
|
|
onQuickAssignDish(dish)
|
|
}
|
|
.swipeActions(edge: .leading, allowsFullSwipe: false) {
|
|
Button {
|
|
onEditDish(dish)
|
|
} label: {
|
|
Label("dish_edit_title", systemImage: "pencil")
|
|
}
|
|
.tint(.mealMoodCoral)
|
|
}
|
|
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
|
|
Button(role: .destructive) {
|
|
onDeleteDish(dish)
|
|
} label: {
|
|
Label("dish_delete", systemImage: "trash")
|
|
}
|
|
}
|
|
.draggable("dish:\(dish.id.uuidString)") {
|
|
DishCardView(dish: dish, tags: tags, language: language)
|
|
.frame(width: 200)
|
|
.opacity(0.8)
|
|
}
|
|
}
|
|
|
|
if filteredDishes.isEmpty {
|
|
Text("home_my_dishes_search_empty")
|
|
.font(.mealMoodBody)
|
|
.foregroundColor(.mealMoodTextSecondary)
|
|
.padding(.top, 8)
|
|
}
|
|
}
|
|
.padding(.horizontal, 16)
|
|
}
|
|
}
|
|
.padding(.top, 16)
|
|
}
|
|
}
|
|
|
|
struct DishCardView: View {
|
|
let dish: Dish
|
|
let tags: [Tag]
|
|
let language: AppLanguage
|
|
var onEdit: (() -> Void)? = nil
|
|
var onDelete: (() -> Void)? = nil
|
|
|
|
private var dishTags: [Tag] {
|
|
tags.filter { dish.tagIds.contains($0.id) }
|
|
}
|
|
|
|
var body: some View {
|
|
HStack {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(dish.name)
|
|
.font(.mealMoodBodyBold)
|
|
.foregroundColor(.mealMoodTextPrimary)
|
|
|
|
if let desc = dish.descriptionText, !desc.isEmpty {
|
|
Text(desc)
|
|
.font(.mealMoodCaption)
|
|
.foregroundColor(.mealMoodTextSecondary)
|
|
.lineLimit(1)
|
|
}
|
|
}
|
|
|
|
Spacer()
|
|
|
|
HStack(spacing: 4) {
|
|
ForEach(dishTags.prefix(3)) { tag in
|
|
TagDot(color: tag.color, size: 10)
|
|
}
|
|
}
|
|
|
|
if onEdit != nil || onDelete != nil {
|
|
HStack(spacing: 8) {
|
|
if let onEdit {
|
|
Button(action: onEdit) {
|
|
Image(systemName: "pencil.circle")
|
|
.foregroundColor(.mealMoodCoral)
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
if let onDelete {
|
|
Button(action: onDelete) {
|
|
Image(systemName: "trash.circle")
|
|
.foregroundColor(.mealMoodError)
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding(14)
|
|
.mealCardStyle()
|
|
}
|
|
}
|