|
| 1 | +import { FormLabel } from '@app-builder/components/Form/Tanstack/FormLabel'; |
| 2 | +import { FormTextArea } from '@app-builder/components/Form/Tanstack/FormTextArea'; |
| 3 | + |
| 4 | +import { Page } from '@app-builder/components/Page'; |
| 5 | +import { CollapsiblePaper } from '@app-builder/components/Paper'; |
| 6 | +import { initServerServices } from '@app-builder/services/init.server'; |
| 7 | +import { getRoute } from '@app-builder/utils/routes'; |
| 8 | +import { type LoaderFunctionArgs } from '@remix-run/node'; |
| 9 | +import { useLoaderData } from '@remix-run/react'; |
| 10 | +import { useForm } from '@tanstack/react-form'; |
| 11 | +import { useTranslation } from 'react-i18next'; |
| 12 | +import { Button, MenuCommand } from 'ui-design-system'; |
| 13 | + |
| 14 | +import { z } from 'zod/v4'; |
| 15 | + |
| 16 | +export async function loader({ request }: LoaderFunctionArgs) { |
| 17 | + const { authService } = initServerServices(request); |
| 18 | + const { user } = await authService.isAuthenticated(request, { |
| 19 | + failureRedirect: getRoute('/sign-in'), |
| 20 | + }); |
| 21 | + |
| 22 | + return { user }; |
| 23 | +} |
| 24 | + |
| 25 | +/* Mock data */ |
| 26 | +// Supported languages list. use BCP47 format. |
| 27 | +const supportedLanguages = ['en', 'fr', 'ar']; |
| 28 | + |
| 29 | +// Structure : The desired output structure of the IA report. Free text defined by the user. (e.g. "The report should be in the following format: <name>, <age>, <gender>, <address>, <phone number>, <email>, <nationality>, <occupation>, <income>, <assets>, <liabilities>, <net worth>, <source of income>, <source of assets>, <source of liabilities>, <source of net worth>") |
| 30 | +// Org description : The description of the organization. |
| 31 | + |
| 32 | +// Supported LLM models list. |
| 33 | +const supportedLLMModels = ['gpt-4o', 'gpt-4o-mini', 'claude-3-5-sonnet-20240620']; |
| 34 | + |
| 35 | +// Domain filter for KYC enrichment : Free array of string domains. |
| 36 | + |
| 37 | +// Search context size (PerplexitySearchContextSize) |
| 38 | +const supportedSearchContextSizes = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000]; |
| 39 | + |
| 40 | +const editIACaseReviewSchema = z.object({ |
| 41 | + language: z.enum(supportedLanguages), |
| 42 | + structure: z.string(), |
| 43 | + orgDescription: z.string(), |
| 44 | + llmModel: z.enum(supportedLLMModels), |
| 45 | + domainFilter: z.array(z.string()), |
| 46 | + searchContextSize: z.enum(supportedSearchContextSizes.map(String)), |
| 47 | +}); |
| 48 | + |
| 49 | +export default function IACaseReviewSettings() { |
| 50 | + const { t } = useTranslation(['settings']); |
| 51 | + const { user } = useLoaderData<typeof loader>(); |
| 52 | + const form = useForm({ |
| 53 | + onSubmit: ({ value, formApi }) => { |
| 54 | + if (formApi.state.isValid) { |
| 55 | + //TODO: Create a query mutation to update the IA case review settings. |
| 56 | + // Mock for now |
| 57 | + console.log('value', value); |
| 58 | + // setToastMessage(session, { |
| 59 | + // type: 'success', |
| 60 | + // messageKey: 'common:success.save', |
| 61 | + // }); |
| 62 | + } |
| 63 | + }, |
| 64 | + validators: { |
| 65 | + onSubmit: editIACaseReviewSchema as unknown as any, |
| 66 | + }, |
| 67 | + defaultValues: { |
| 68 | + language: 'en', |
| 69 | + structure: '', |
| 70 | + orgDescription: '', |
| 71 | + llmModel: 'gpt-4o', |
| 72 | + domainFilter: [], |
| 73 | + searchContextSize: 100, |
| 74 | + }, |
| 75 | + }); |
| 76 | + console.log('user', user); |
| 77 | + return ( |
| 78 | + <Page.Container> |
| 79 | + <Page.Content className="max-w-(--breakpoint-xl)"> |
| 80 | + <form |
| 81 | + className="flex flex-col gap-4" |
| 82 | + onSubmit={(e) => { |
| 83 | + e.preventDefault(); |
| 84 | + e.stopPropagation(); |
| 85 | + form.handleSubmit(); |
| 86 | + }} |
| 87 | + > |
| 88 | + <div className="flex flex-col gap-4"> |
| 89 | + <CollapsiblePaper.Container> |
| 90 | + <CollapsiblePaper.Title> |
| 91 | + <span className="flex-1">{t('settings:ia_case_review')}</span> |
| 92 | + </CollapsiblePaper.Title> |
| 93 | + <CollapsiblePaper.Content> |
| 94 | + <div className="flex flex-col gap-8"> |
| 95 | + {/* TODO: Add Org description */} |
| 96 | + <form.Field name="orgDescription"> |
| 97 | + {(field) => ( |
| 98 | + <div className="group flex w-full flex-col gap-2"> |
| 99 | + <FormLabel name={field.name}> |
| 100 | + {t('settings:ia_case_review.org_description')} |
| 101 | + </FormLabel> |
| 102 | + <FormTextArea |
| 103 | + name={field.name} |
| 104 | + onChange={(e) => field.handleChange(e.currentTarget.value)} |
| 105 | + onBlur={field.handleBlur} |
| 106 | + defaultValue={field.state.value} |
| 107 | + valid={field.state.meta.errors.length === 0} |
| 108 | + /> |
| 109 | + </div> |
| 110 | + )} |
| 111 | + </form.Field> |
| 112 | + |
| 113 | + {/* TODO: Add Language */} |
| 114 | + <form.Field name="language"> |
| 115 | + {(field) => ( |
| 116 | + <div className="group flex w-full flex-col gap-2"> |
| 117 | + <FormLabel name={field.name}> |
| 118 | + {t('settings:ia_case_review.language')} |
| 119 | + </FormLabel> |
| 120 | + <MenuCommand.Menu> |
| 121 | + <MenuCommand.Trigger> |
| 122 | + <MenuCommand.SelectButton>{field.state.value}</MenuCommand.SelectButton> |
| 123 | + </MenuCommand.Trigger> |
| 124 | + |
| 125 | + <MenuCommand.Content |
| 126 | + sameWidth |
| 127 | + sideOffset={4} |
| 128 | + align="start" |
| 129 | + className="min-w-24" |
| 130 | + > |
| 131 | + <MenuCommand.List> |
| 132 | + {supportedLanguages.map((language) => ( |
| 133 | + <MenuCommand.Item |
| 134 | + key={language} |
| 135 | + value={language} |
| 136 | + onSelect={() => field.handleChange(language)} |
| 137 | + > |
| 138 | + {language} |
| 139 | + </MenuCommand.Item> |
| 140 | + ))} |
| 141 | + </MenuCommand.List> |
| 142 | + </MenuCommand.Content> |
| 143 | + </MenuCommand.Menu> |
| 144 | + </div> |
| 145 | + )} |
| 146 | + </form.Field> |
| 147 | + {/* TODO: Add Structure */} |
| 148 | + <form.Field name="structure"> |
| 149 | + {(field) => ( |
| 150 | + <div className="group flex w-full flex-col gap-2"> |
| 151 | + <FormLabel name={field.name}> |
| 152 | + {t('settings:ia_case_review.structure')} |
| 153 | + </FormLabel> |
| 154 | + <FormTextArea |
| 155 | + name={field.name} |
| 156 | + onChange={(e) => field.handleChange(e.currentTarget.value)} |
| 157 | + onBlur={field.handleBlur} |
| 158 | + defaultValue={field.state.value} |
| 159 | + valid={field.state.meta.errors.length === 0} |
| 160 | + /> |
| 161 | + </div> |
| 162 | + )} |
| 163 | + </form.Field> |
| 164 | + </div> |
| 165 | + </CollapsiblePaper.Content> |
| 166 | + </CollapsiblePaper.Container> |
| 167 | + {/* TODO: Add LLM Model */} |
| 168 | + <CollapsiblePaper.Container> |
| 169 | + <CollapsiblePaper.Title> |
| 170 | + <span className="flex-1">{t('settings:ia_llm_config')}</span> |
| 171 | + </CollapsiblePaper.Title> |
| 172 | + <CollapsiblePaper.Content> |
| 173 | + <div className="flex flex-col gap-8"> |
| 174 | + {/* TODO: Add LLM Model */} |
| 175 | + <form.Field name="llmModel"> |
| 176 | + {(field) => ( |
| 177 | + <div className="group flex w-full flex-col gap-2"> |
| 178 | + <FormLabel name={field.name}> |
| 179 | + {t('settings:ia_llm_config.llm_model')} |
| 180 | + </FormLabel> |
| 181 | + <MenuCommand.Menu> |
| 182 | + <MenuCommand.Trigger> |
| 183 | + <MenuCommand.SelectButton>{field.state.value}</MenuCommand.SelectButton> |
| 184 | + </MenuCommand.Trigger> |
| 185 | + <MenuCommand.Content |
| 186 | + sameWidth |
| 187 | + sideOffset={4} |
| 188 | + align="start" |
| 189 | + className="min-w-24" |
| 190 | + > |
| 191 | + <MenuCommand.List> |
| 192 | + {supportedLLMModels.map((model) => ( |
| 193 | + <MenuCommand.Item |
| 194 | + key={model} |
| 195 | + value={model} |
| 196 | + onSelect={() => field.handleChange(model)} |
| 197 | + > |
| 198 | + {model} |
| 199 | + </MenuCommand.Item> |
| 200 | + ))} |
| 201 | + </MenuCommand.List> |
| 202 | + </MenuCommand.Content> |
| 203 | + </MenuCommand.Menu> |
| 204 | + </div> |
| 205 | + )} |
| 206 | + </form.Field> |
| 207 | + </div> |
| 208 | + |
| 209 | + {/* TODO: Add Search Context Size */} |
| 210 | + |
| 211 | + <form.Field name="searchContextSize"> |
| 212 | + {(field) => ( |
| 213 | + <div className="group flex w-full flex-col gap-2"> |
| 214 | + <FormLabel name={field.name}> |
| 215 | + {t('settings:ia_llm_config.search_context_size')} |
| 216 | + </FormLabel> |
| 217 | + <MenuCommand.Menu> |
| 218 | + <MenuCommand.Trigger> |
| 219 | + <MenuCommand.SelectButton>{field.state.value}</MenuCommand.SelectButton> |
| 220 | + </MenuCommand.Trigger> |
| 221 | + |
| 222 | + <MenuCommand.Content |
| 223 | + sameWidth |
| 224 | + sideOffset={4} |
| 225 | + align="start" |
| 226 | + className="min-w-24" |
| 227 | + > |
| 228 | + <MenuCommand.List> |
| 229 | + {supportedSearchContextSizes.map((size) => ( |
| 230 | + <MenuCommand.Item |
| 231 | + key={size} |
| 232 | + value={size} |
| 233 | + onSelect={() => field.handleChange(size)} |
| 234 | + > |
| 235 | + {size} |
| 236 | + </MenuCommand.Item> |
| 237 | + ))} |
| 238 | + </MenuCommand.List> |
| 239 | + </MenuCommand.Content> |
| 240 | + </MenuCommand.Menu> |
| 241 | + </div> |
| 242 | + )} |
| 243 | + </form.Field> |
| 244 | + </CollapsiblePaper.Content> |
| 245 | + </CollapsiblePaper.Container> |
| 246 | + {/* TODO: Add Domain Filter */} |
| 247 | + {/* TODO: Add Domain Filter */} |
| 248 | + </div> |
| 249 | + <Button type="submit" variant="primary"> |
| 250 | + {t('common:save')} |
| 251 | + </Button> |
| 252 | + </form> |
| 253 | + </Page.Content> |
| 254 | + </Page.Container> |
| 255 | + ); |
| 256 | +} |
0 commit comments