feat: AI API Setting page with 4 provider sections and toggles

Add OpenAI, Claude AI, and DentalManagement AI sections to the AI API
Setting page, each with a masked API key input and an on/off toggle
(defaulting to off). Rename sidebar label from "Google AI Settings" to
"AI API Setting". Add provider-key and provider-enabled backend endpoints
and extend the AiSettings schema with 6 new fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ff
2026-06-05 16:36:01 -04:00
parent 1bbca38344
commit 2457e12b5c
50 changed files with 930 additions and 102 deletions

View File

@@ -3,6 +3,13 @@ export const AiSettingsAggregateResultSchema = z.object({ _count: z.object({
id: z.number(),
userId: z.number(),
apiKey: z.number(),
aiEnabled: z.number(),
openAiKey: z.number(),
openAiEnabled: z.number(),
claudeAiKey: z.number(),
claudeAiEnabled: z.number(),
dentalMgmtKey: z.number(),
dentalMgmtEnabled: z.number(),
afterHoursEnabled: z.number(),
openPhoneReply: z.number(),
user: z.number()
@@ -18,10 +25,16 @@ export const AiSettingsAggregateResultSchema = z.object({ _count: z.object({
_min: z.object({
id: z.number().int().nullable(),
userId: z.number().int().nullable(),
apiKey: z.string().nullable()
apiKey: z.string().nullable(),
openAiKey: z.string().nullable(),
claudeAiKey: z.string().nullable(),
dentalMgmtKey: z.string().nullable()
}).nullable().optional(),
_max: z.object({
id: z.number().int().nullable(),
userId: z.number().int().nullable(),
apiKey: z.string().nullable()
apiKey: z.string().nullable(),
openAiKey: z.string().nullable(),
claudeAiKey: z.string().nullable(),
dentalMgmtKey: z.string().nullable()
}).nullable().optional()});

View File

@@ -3,6 +3,13 @@ export const AiSettingsCreateResultSchema = z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
aiEnabled: z.boolean(),
openAiKey: z.string(),
openAiEnabled: z.boolean(),
claudeAiKey: z.string(),
claudeAiEnabled: z.boolean(),
dentalMgmtKey: z.string(),
dentalMgmtEnabled: z.boolean(),
afterHoursEnabled: z.boolean(),
openPhoneReply: z.boolean(),
user: z.unknown()

View File

@@ -3,6 +3,13 @@ export const AiSettingsDeleteResultSchema = z.nullable(z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
aiEnabled: z.boolean(),
openAiKey: z.string(),
openAiEnabled: z.boolean(),
claudeAiKey: z.string(),
claudeAiEnabled: z.boolean(),
dentalMgmtKey: z.string(),
dentalMgmtEnabled: z.boolean(),
afterHoursEnabled: z.boolean(),
openPhoneReply: z.boolean(),
user: z.unknown()

View File

@@ -3,6 +3,13 @@ export const AiSettingsFindFirstResultSchema = z.nullable(z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
aiEnabled: z.boolean(),
openAiKey: z.string(),
openAiEnabled: z.boolean(),
claudeAiKey: z.string(),
claudeAiEnabled: z.boolean(),
dentalMgmtKey: z.string(),
dentalMgmtEnabled: z.boolean(),
afterHoursEnabled: z.boolean(),
openPhoneReply: z.boolean(),
user: z.unknown()

View File

@@ -4,6 +4,13 @@ export const AiSettingsFindManyResultSchema = z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
aiEnabled: z.boolean(),
openAiKey: z.string(),
openAiEnabled: z.boolean(),
claudeAiKey: z.string(),
claudeAiEnabled: z.boolean(),
dentalMgmtKey: z.string(),
dentalMgmtEnabled: z.boolean(),
afterHoursEnabled: z.boolean(),
openPhoneReply: z.boolean(),
user: z.unknown()

View File

@@ -3,6 +3,13 @@ export const AiSettingsFindUniqueResultSchema = z.nullable(z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
aiEnabled: z.boolean(),
openAiKey: z.string(),
openAiEnabled: z.boolean(),
claudeAiKey: z.string(),
claudeAiEnabled: z.boolean(),
dentalMgmtKey: z.string(),
dentalMgmtEnabled: z.boolean(),
afterHoursEnabled: z.boolean(),
openPhoneReply: z.boolean(),
user: z.unknown()

View File

@@ -3,12 +3,26 @@ export const AiSettingsGroupByResultSchema = z.array(z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
aiEnabled: z.boolean(),
openAiKey: z.string(),
openAiEnabled: z.boolean(),
claudeAiKey: z.string(),
claudeAiEnabled: z.boolean(),
dentalMgmtKey: z.string(),
dentalMgmtEnabled: z.boolean(),
afterHoursEnabled: z.boolean(),
openPhoneReply: z.boolean(),
_count: z.object({
id: z.number(),
userId: z.number(),
apiKey: z.number(),
aiEnabled: z.number(),
openAiKey: z.number(),
openAiEnabled: z.number(),
claudeAiKey: z.number(),
claudeAiEnabled: z.number(),
dentalMgmtKey: z.number(),
dentalMgmtEnabled: z.number(),
afterHoursEnabled: z.number(),
openPhoneReply: z.number(),
user: z.number()
@@ -24,11 +38,17 @@ export const AiSettingsGroupByResultSchema = z.array(z.object({
_min: z.object({
id: z.number().int().nullable(),
userId: z.number().int().nullable(),
apiKey: z.string().nullable()
apiKey: z.string().nullable(),
openAiKey: z.string().nullable(),
claudeAiKey: z.string().nullable(),
dentalMgmtKey: z.string().nullable()
}).nullable().optional(),
_max: z.object({
id: z.number().int().nullable(),
userId: z.number().int().nullable(),
apiKey: z.string().nullable()
apiKey: z.string().nullable(),
openAiKey: z.string().nullable(),
claudeAiKey: z.string().nullable(),
dentalMgmtKey: z.string().nullable()
}).nullable().optional()
}));

View File

@@ -3,6 +3,13 @@ export const AiSettingsUpdateResultSchema = z.nullable(z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
aiEnabled: z.boolean(),
openAiKey: z.string(),
openAiEnabled: z.boolean(),
claudeAiKey: z.string(),
claudeAiEnabled: z.boolean(),
dentalMgmtKey: z.string(),
dentalMgmtEnabled: z.boolean(),
afterHoursEnabled: z.boolean(),
openPhoneReply: z.boolean(),
user: z.unknown()

View File

@@ -3,6 +3,13 @@ export const AiSettingsUpsertResultSchema = z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
aiEnabled: z.boolean(),
openAiKey: z.string(),
openAiEnabled: z.boolean(),
claudeAiKey: z.string(),
claudeAiEnabled: z.boolean(),
dentalMgmtKey: z.string(),
dentalMgmtEnabled: z.boolean(),
afterHoursEnabled: z.boolean(),
openPhoneReply: z.boolean(),
user: z.unknown()