From 26259c252dd83a900f348e25f2a2e62eb477e2d2 Mon Sep 17 00:00:00 2001 From: Chris Chen Date: Thu, 25 Jun 2026 13:29:52 -0700 Subject: [PATCH] =?UTF-8?q?feat(church-profile):=20AI=20=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=20tab=20(provider/model/key)=20with=20masked=20keys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../disbursement/models/disbursement.model.ts | 7 ++- .../church-profile-page.component.html | 47 +++++++++++++++++++ .../church-profile-page.component.ts | 30 ++++++++++-- 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/APP/src/app/features/disbursement/models/disbursement.model.ts b/APP/src/app/features/disbursement/models/disbursement.model.ts index 060fe10..0ba7aa9 100644 --- a/APP/src/app/features/disbursement/models/disbursement.model.ts +++ b/APP/src/app/features/disbursement/models/disbursement.model.ts @@ -49,6 +49,11 @@ export interface ChurchProfileDto { email: string | null; website: string | null; address: string | null; city: string | null; state: string | null; zipCode: string | null; bankName: string | null; bankAccountNumber: string | null; bankRoutingNumber: string | null; nextCheckNumber: number; + aiProvider: string; + claudeModel: string | null; claudeApiKeyMasked: string | null; + geminiModel: string | null; geminiApiKeyMasked: string | null; } -export type UpdateChurchProfileRequest = Omit; +export type UpdateChurchProfileRequest = + Omit + & { claudeApiKey: string | null; geminiApiKey: string | null }; diff --git a/APP/src/app/features/disbursement/pages/church-profile-page/church-profile-page.component.html b/APP/src/app/features/disbursement/pages/church-profile-page/church-profile-page.component.html index e7ce993..8a52131 100644 --- a/APP/src/app/features/disbursement/pages/church-profile-page/church-profile-page.component.html +++ b/APP/src/app/features/disbursement/pages/church-profile-page/church-profile-page.component.html @@ -82,5 +82,52 @@ + + + + +
+
+ + + + + + + + +

+ Leave a key blank to keep the saved one. / 金鑰留空表示沿用已儲存的設定。 +

+
+ +
+ + {{ savedMsg }} +
+
+
+
diff --git a/APP/src/app/features/disbursement/pages/church-profile-page/church-profile-page.component.ts b/APP/src/app/features/disbursement/pages/church-profile-page/church-profile-page.component.ts index d156251..f83ba00 100644 --- a/APP/src/app/features/disbursement/pages/church-profile-page/church-profile-page.component.ts +++ b/APP/src/app/features/disbursement/pages/church-profile-page/church-profile-page.component.ts @@ -4,8 +4,9 @@ import { FormsModule } from '@angular/forms'; import { ButtonsModule } from '@progress/kendo-angular-buttons'; import { InputsModule } from '@progress/kendo-angular-inputs'; import { LayoutModule } from '@progress/kendo-angular-layout'; +import { DropDownsModule } from '@progress/kendo-angular-dropdowns'; import { DisbursementApiService } from '../../services/disbursement-api.service'; -import { ChurchProfileDto } from '../../models/disbursement.model'; +import { ChurchProfileDto, UpdateChurchProfileRequest } from '../../models/disbursement.model'; import { HasPermissionDirective } from '../../../../core/directives/has-permission.directive'; import { PermissionModules } from '../../../../core/models/permission.model'; import { SiteSettingsTabComponent } from '../../../settings/components/site-settings-tab/site-settings-tab.component'; @@ -15,7 +16,7 @@ import { NotificationSettingsTabComponent } from '../../../settings/components/n selector: 'app-church-profile-page', standalone: true, imports: [ - CommonModule, FormsModule, ButtonsModule, InputsModule, LayoutModule, + CommonModule, FormsModule, ButtonsModule, InputsModule, LayoutModule, DropDownsModule, HasPermissionDirective, SiteSettingsTabComponent, NotificationSettingsTabComponent, ], templateUrl: './church-profile-page.component.html', @@ -25,6 +26,15 @@ export class ChurchProfilePageComponent implements OnInit { saving = false; savedMsg = ''; + /** Bound to the password inputs; blank means "keep the saved key". Reset after each save. */ + claudeApiKeyInput = ''; + geminiApiKeyInput = ''; + + readonly aiProviders = [ + { text: 'Claude', value: 'Claude' }, + { text: 'Gemini', value: 'Gemini' }, + ]; + /** Settings module gates the Site / Notification tabs. */ readonly settingsPermission = { module: PermissionModules.Settings, action: 'read' as const }; @@ -38,9 +48,21 @@ export class ChurchProfilePageComponent implements OnInit { if (!this.model || this.saving) return; this.saving = true; this.savedMsg = ''; - const { id, ...req } = this.model; + const { id, claudeApiKeyMasked, geminiApiKeyMasked, ...rest } = this.model; + const req: UpdateChurchProfileRequest = { + ...rest, + claudeApiKey: this.claudeApiKeyInput.trim() || null, + geminiApiKey: this.geminiApiKeyInput.trim() || null, + }; this.api.updateChurchProfile(req).subscribe({ - next: () => { this.saving = false; this.savedMsg = 'Saved / 已儲存'; }, + next: () => { + this.saving = false; + this.savedMsg = 'Saved / 已儲存'; + // Clear the key inputs and reload so the masked placeholders reflect the new keys. + this.claudeApiKeyInput = ''; + this.geminiApiKeyInput = ''; + this.api.getChurchProfile().subscribe(p => (this.model = p)); + }, error: () => { // Error message is shown globally by httpErrorInterceptor. this.saving = false;