Add task template suggestions for quick task creation
- Add TaskTemplate model with category grouping support - Add TaskTemplateApi for fetching templates from backend - Add TaskSuggestionDropdown component for Android task form - Add TaskTemplatesBrowserSheet for browsing all templates - Add TaskSuggestionsView and TaskTemplatesBrowserView for iOS - Update DataManager to cache task templates - Update APILayer with template fetch and search methods - Update TaskFormView (iOS) with template suggestions - Update AddTaskDialog (Android) with template suggestions - Update onboarding task view to use templates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -91,6 +91,11 @@ struct TaskFormView: View {
|
||||
// Error alert state
|
||||
@State private var errorAlert: ErrorAlertInfo? = nil
|
||||
|
||||
// Template suggestions
|
||||
@State private var showingTemplatesBrowser = false
|
||||
@State private var filteredSuggestions: [TaskTemplate] = []
|
||||
@State private var showSuggestions = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ZStack {
|
||||
@@ -120,9 +125,60 @@ struct TaskFormView: View {
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
}
|
||||
|
||||
// Browse Templates Button (only for new tasks)
|
||||
if !isEditMode {
|
||||
Section {
|
||||
Button {
|
||||
showingTemplatesBrowser = true
|
||||
} label: {
|
||||
HStack {
|
||||
Image(systemName: "list.bullet.rectangle")
|
||||
.font(.system(size: 18))
|
||||
.foregroundColor(Color.appPrimary)
|
||||
.frame(width: 28)
|
||||
|
||||
Text("Browse Task Templates")
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("\(dataManager.taskTemplateCount) tasks")
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Quick Start")
|
||||
} footer: {
|
||||
Text("Choose from common home maintenance tasks or create your own below")
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
}
|
||||
|
||||
Section {
|
||||
TextField(L10n.Tasks.titleLabel, text: $title)
|
||||
.focused($focusedField, equals: .title)
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
TextField(L10n.Tasks.titleLabel, text: $title)
|
||||
.focused($focusedField, equals: .title)
|
||||
.onChange(of: title) { newValue in
|
||||
updateSuggestions(query: newValue)
|
||||
}
|
||||
|
||||
// Inline suggestions dropdown
|
||||
if showSuggestions && !filteredSuggestions.isEmpty && focusedField == .title {
|
||||
TaskSuggestionsView(
|
||||
suggestions: filteredSuggestions,
|
||||
onSelect: { template in
|
||||
selectTaskTemplate(template)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if !titleError.isEmpty {
|
||||
Text(titleError)
|
||||
@@ -286,9 +342,53 @@ struct TaskFormView: View {
|
||||
errorAlert = nil
|
||||
}
|
||||
)
|
||||
.sheet(isPresented: $showingTemplatesBrowser) {
|
||||
TaskTemplatesBrowserView { template in
|
||||
selectTaskTemplate(template)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Suggestions Helpers
|
||||
|
||||
private func updateSuggestions(query: String) {
|
||||
if query.count >= 2 {
|
||||
filteredSuggestions = dataManager.searchTaskTemplates(query: query)
|
||||
showSuggestions = !filteredSuggestions.isEmpty
|
||||
} else {
|
||||
filteredSuggestions = []
|
||||
showSuggestions = false
|
||||
}
|
||||
}
|
||||
|
||||
private func selectTaskTemplate(_ template: TaskTemplate) {
|
||||
// Fill in the title
|
||||
title = template.title
|
||||
|
||||
// Fill in description if available
|
||||
description = template.description_
|
||||
|
||||
// Auto-select matching category by ID or name
|
||||
if let categoryId = template.categoryId {
|
||||
selectedCategory = taskCategories.first(where: { $0.id == Int(categoryId.int32Value) })
|
||||
} else if let category = template.category {
|
||||
selectedCategory = taskCategories.first(where: { $0.name.lowercased() == category.name.lowercased() })
|
||||
}
|
||||
|
||||
// Auto-select matching frequency by ID or name
|
||||
if let frequencyId = template.frequencyId {
|
||||
selectedFrequency = taskFrequencies.first(where: { $0.id == Int(frequencyId.int32Value) })
|
||||
} else if let frequency = template.frequency {
|
||||
selectedFrequency = taskFrequencies.first(where: { $0.name.lowercased() == frequency.name.lowercased() })
|
||||
}
|
||||
|
||||
// Clear suggestions and dismiss keyboard
|
||||
showSuggestions = false
|
||||
filteredSuggestions = []
|
||||
focusedField = nil
|
||||
}
|
||||
|
||||
private func setDefaults() {
|
||||
// Set default values if not already set
|
||||
if selectedCategory == nil && !taskCategories.isEmpty {
|
||||
|
||||
Reference in New Issue
Block a user