refactor: redesign SettingsView layout for improved user experience

This commit is contained in:
Owen Gretzinger
2025-07-10 18:00:45 -04:00
parent 2b3f547b07
commit 6b717d5bd8
+95 -45
View File
@@ -6,49 +6,110 @@ struct SettingsView: View {
var body: some View { var body: some View {
NavigationStack { NavigationStack {
Form { ScrollView {
Section("API Keys") { VStack(spacing: 32) {
SecureField("Deepgram API Key", text: $viewModel.settings.deepgramKey) // API Keys Section
.textFieldStyle(.roundedBorder) VStack(alignment: .leading, spacing: 16) {
Text("API Keys")
SecureField("OpenAI API Key", text: $viewModel.settings.openAIKey) .font(.headline)
.textFieldStyle(.roundedBorder) .fontWeight(.semibold)
}
VStack(spacing: 12) {
Section("Personal Information") { VStack(alignment: .leading, spacing: 4) {
VStack(alignment: .leading) { Text("Deepgram API Key")
Text("About Yourself") .font(.subheadline)
.font(.caption) .foregroundColor(.secondary)
.foregroundColor(.secondary) SecureField("Enter your Deepgram API key", text: $viewModel.settings.deepgramKey)
TextEditor(text: $viewModel.settings.userBlurb) .textFieldStyle(.roundedBorder)
.frame(minHeight: 80) .frame(maxWidth: .infinity)
.scrollContentBackground(.hidden) }
.background(Color.gray.opacity(0.05))
.cornerRadius(8) VStack(alignment: .leading, spacing: 4) {
Text("OpenAI API Key")
.font(.subheadline)
.foregroundColor(.secondary)
SecureField("Enter your OpenAI API key", text: $viewModel.settings.openAIKey)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: .infinity)
}
}
} }
} .padding(.horizontal, 24)
Section("AI Generation") { Divider()
VStack(alignment: .leading) { .padding(.horizontal, 24)
HStack {
Text("System Prompt") // Personal Information Section
VStack(alignment: .leading, spacing: 16) {
Text("Personal Information")
.font(.headline)
.fontWeight(.semibold)
VStack(alignment: .leading, spacing: 8) {
Text("About Yourself")
.font(.subheadline)
.foregroundColor(.secondary)
Text("This information will be included in the AI context when generating meeting notes.")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundColor(.secondary)
TextEditor(text: $viewModel.settings.userBlurb)
.frame(minHeight: 80, maxHeight: 120)
.scrollContentBackground(.hidden)
.background(Color(.controlBackgroundColor))
.cornerRadius(8)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color(.separatorColor), lineWidth: 1)
)
}
}
.padding(.horizontal, 24)
Divider()
.padding(.horizontal, 24)
// AI Generation Section
VStack(alignment: .leading, spacing: 16) {
HStack {
Text("AI Generation")
.font(.headline)
.fontWeight(.semibold)
Spacer() Spacer()
Button("Reset to Default") { Button("Reset to Default") {
viewModel.resetToDefaults() viewModel.resetToDefaults()
} }
.font(.caption) .font(.caption)
.foregroundColor(.accentColor)
}
VStack(alignment: .leading, spacing: 8) {
Text("System Prompt")
.font(.subheadline)
.foregroundColor(.secondary)
Text("Customize how the AI generates your meeting notes.")
.font(.caption)
.foregroundColor(.secondary)
TextEditor(text: $viewModel.settings.systemPrompt)
.frame(minHeight: 100, maxHeight: 160)
.scrollContentBackground(.hidden)
.background(Color(.controlBackgroundColor))
.cornerRadius(8)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color(.separatorColor), lineWidth: 1)
)
} }
TextEditor(text: $viewModel.settings.systemPrompt)
.frame(minHeight: 120)
.scrollContentBackground(.hidden)
.background(Color.gray.opacity(0.05))
.cornerRadius(8)
} }
.padding(.horizontal, 24)
Spacer(minLength: 20)
} }
.padding(.vertical, 24)
} }
.navigationTitle("Settings") .navigationTitle("Settings")
.frame(minWidth: 600, minHeight: 500)
.toolbar { .toolbar {
ToolbarItem(placement: .cancellationAction) { ToolbarItem(placement: .cancellationAction) {
Button("Cancel") { Button("Cancel") {
@@ -61,23 +122,12 @@ struct SettingsView: View {
viewModel.saveSettings() viewModel.saveSettings()
} }
.disabled(viewModel.isSaving) .disabled(viewModel.isSaving)
.buttonStyle(.borderedProminent)
} }
} }
.overlay { .onChange(of: viewModel.saveSuccessful) { _, isSuccessful in
if viewModel.saveSuccessful { if isSuccessful {
VStack { dismiss()
Spacer()
HStack {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
Text("Settings saved successfully")
}
.padding()
.background(Color.green.opacity(0.1))
.cornerRadius(10)
.padding()
}
.transition(.move(edge: .bottom).combined(with: .opacity))
} }
} }
.alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) { .alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) {