mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-04-02 22:15:23 -04:00
Refactor profile and form submission logic to support partial updates and improve type handling
This commit is contained in:
@@ -46,7 +46,7 @@ import {RadioToggleGroup} from 'web/components/widgets/radio-toggle-group'
|
||||
import {Select} from 'web/components/widgets/select'
|
||||
import {Slider} from 'web/components/widgets/slider'
|
||||
import {Title} from 'web/components/widgets/title'
|
||||
import {useChoicesContext} from 'web/hooks/use-choices'
|
||||
import {ChoiceMap, ChoiceSetter, useChoicesContext} from 'web/hooks/use-choices'
|
||||
import {api} from 'web/lib/api'
|
||||
import {useLocale, useT} from 'web/lib/locale'
|
||||
import {track} from 'web/lib/service/analytics'
|
||||
@@ -60,7 +60,7 @@ export const OptionalProfileUserForm = (props: {
|
||||
user: BaseUser
|
||||
buttonLabel?: string
|
||||
bottomNavBarVisible?: boolean
|
||||
onSubmit: () => Promise<void>
|
||||
onSubmit: (profile?: ProfileWithoutUser) => Promise<void>
|
||||
}) => {
|
||||
const {profile, user, buttonLabel, setProfile, onSubmit, bottomNavBarVisible = true} = props
|
||||
|
||||
@@ -90,11 +90,11 @@ export const OptionalProfileUserForm = (props: {
|
||||
const [isExtracting, setIsExtracting] = useState(false)
|
||||
const [parsingEditor, setParsingEditor] = useState<any>(null)
|
||||
|
||||
const handleLLMExtract = async () => {
|
||||
const handleLLMExtract = async (): Promise<Partial<ProfileWithoutUser>> => {
|
||||
const llmContent = parsingEditor?.getText?.() ?? ''
|
||||
if (!llmContent) {
|
||||
toast.error(t('profile.llm.extract.error_empty', 'Please enter content to extract from'))
|
||||
return
|
||||
return {}
|
||||
}
|
||||
setIsExtracting(true)
|
||||
const isInputUrl = isUrl(llmContent)
|
||||
@@ -103,11 +103,13 @@ export const OptionalProfileUserForm = (props: {
|
||||
...(isInputUrl ? {url: urlize(llmContent).trim()} : {content: llmContent.trim()}),
|
||||
}
|
||||
try {
|
||||
const extracted = await api('llm-extract-profile', payload)
|
||||
for (const data of Object.entries(removeNullOrUndefinedProps(extracted))) {
|
||||
const key = data[0]
|
||||
let extractedProfile = await api('llm-extract-profile', payload)
|
||||
extractedProfile = removeNullOrUndefinedProps(extractedProfile)
|
||||
for (const data of Object.entries(extractedProfile)) {
|
||||
const key = data[0] as keyof ProfileWithoutUser
|
||||
let value = data[1]
|
||||
let choices, setChoices: any
|
||||
let choices: ChoiceMap | undefined
|
||||
let setChoices: ChoiceSetter | undefined
|
||||
if (key === 'interests') {
|
||||
choices = interestChoices
|
||||
setChoices = setInterestChoices
|
||||
@@ -133,19 +135,26 @@ export const OptionalProfileUserForm = (props: {
|
||||
}
|
||||
debug({value, converter})
|
||||
} else if (key === 'keywords') setKeywordsString((value as string[]).join(', '))
|
||||
setProfile(
|
||||
key as keyof ProfileWithoutUser,
|
||||
value as ProfileWithoutUser[keyof ProfileWithoutUser],
|
||||
)
|
||||
;(extractedProfile as Record<string, unknown>)[key] = value
|
||||
}
|
||||
if (!isInputUrl) extractedProfile.bio = parsingEditor?.getJSON?.()
|
||||
debug({
|
||||
text: parsingEditor?.getText?.(),
|
||||
json: parsingEditor?.getJSON?.(),
|
||||
extracted: extractedProfile,
|
||||
})
|
||||
|
||||
for (const key of Object.keys(extractedProfile) as (keyof ProfileWithoutUser)[]) {
|
||||
setProfile(key, extractedProfile[key] as ProfileWithoutUser[typeof key])
|
||||
}
|
||||
if (!isInputUrl) setProfile('bio', parsingEditor?.getJSON?.())
|
||||
debug({text: parsingEditor?.getText?.(), json: parsingEditor?.getJSON?.(), extracted})
|
||||
|
||||
parsingEditor?.commands?.clearContent?.()
|
||||
|
||||
toast.success(
|
||||
t('profile.llm.extract.success', 'Profile data extracted! Please review below.'),
|
||||
)
|
||||
|
||||
return extractedProfile
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
toast.error(
|
||||
@@ -162,6 +171,7 @@ export const OptionalProfileUserForm = (props: {
|
||||
} finally {
|
||||
setIsExtracting(false)
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
const errorToast = () => {
|
||||
@@ -169,19 +179,22 @@ export const OptionalProfileUserForm = (props: {
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
let finalProfile = profile
|
||||
|
||||
if (parsingEditor?.getText?.()?.trim()) {
|
||||
await handleLLMExtract()
|
||||
const extractedProfile = await handleLLMExtract()
|
||||
finalProfile = {...profile, ...extractedProfile}
|
||||
}
|
||||
|
||||
// Validate age before submitting
|
||||
if (typeof profile['age'] === 'number') {
|
||||
if (profile['age'] < 18) {
|
||||
if (typeof finalProfile['age'] === 'number') {
|
||||
if (finalProfile['age'] < 18) {
|
||||
setAgeError(t('profile.optional.age.error_min', 'You must be at least 18 years old'))
|
||||
setIsSubmitting(false)
|
||||
errorToast()
|
||||
return
|
||||
}
|
||||
if (profile['age'] > 100) {
|
||||
if (finalProfile['age'] > 100) {
|
||||
setAgeError(t('profile.optional.age.error_max', 'Please enter a valid age'))
|
||||
setIsSubmitting(false)
|
||||
errorToast()
|
||||
@@ -193,7 +206,7 @@ export const OptionalProfileUserForm = (props: {
|
||||
|
||||
track('submit optional profile')
|
||||
|
||||
await onSubmit()
|
||||
await onSubmit(finalProfile)
|
||||
|
||||
choices.refreshInterests()
|
||||
choices.refreshCauses()
|
||||
|
||||
@@ -70,10 +70,13 @@ const useChoices = (label: OptionTableKey) => {
|
||||
return {choices, refreshChoices}
|
||||
}
|
||||
|
||||
export type ChoiceMap = Record<string, string>
|
||||
export type ChoiceSetter = React.Dispatch<React.SetStateAction<ChoiceMap>>
|
||||
|
||||
export type UseAllChoices = {
|
||||
interests: Record<string, string>
|
||||
causes: Record<string, string>
|
||||
work: Record<string, string>
|
||||
interests: ChoiceMap
|
||||
causes: ChoiceMap
|
||||
work: ChoiceMap
|
||||
refreshInterests: () => void
|
||||
refreshCauses: () => void
|
||||
refreshWork: () => void
|
||||
|
||||
@@ -73,8 +73,8 @@ function ProfilePageInner(props: {user: User; profile: Profile}) {
|
||||
setBaseUser((prevState) => ({...prevState, [key]: value}))
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
const {interests, causes, work, ...otherProfileProps} = profile
|
||||
async function submitForm(finalProfile?: ProfileWithoutUser) {
|
||||
const {interests, causes, work, ...otherProfileProps} = finalProfile ?? profile
|
||||
const parsedProfile = removeUndefinedProps(otherProfileProps) as any
|
||||
debug('parsedProfile', parsedProfile)
|
||||
const promises: Promise<any>[] = filterDefined([
|
||||
@@ -124,7 +124,7 @@ function ProfilePageInner(props: {user: User; profile: Profile}) {
|
||||
setProfile={setProfileState}
|
||||
user={baseUser}
|
||||
buttonLabel={t('profile.save', 'Save')}
|
||||
onSubmit={async () => await submitForm()}
|
||||
onSubmit={submitForm}
|
||||
/>
|
||||
</Col>
|
||||
</Col>
|
||||
|
||||
@@ -16,8 +16,7 @@ import {CompassLoadingIndicator} from 'web/components/widgets/loading-indicator'
|
||||
import {useTracking} from 'web/hooks/use-tracking'
|
||||
import {api} from 'web/lib/api'
|
||||
import {auth, CACHED_REFERRAL_USERNAME_KEY} from 'web/lib/firebase/users'
|
||||
import {useLocale} from 'web/lib/locale'
|
||||
import {useT} from 'web/lib/locale'
|
||||
import {useLocale, useT} from 'web/lib/locale'
|
||||
import {getLocale} from 'web/lib/locale-cookie'
|
||||
import {track} from 'web/lib/service/analytics'
|
||||
import {safeLocalStorage} from 'web/lib/util/local'
|
||||
@@ -75,7 +74,7 @@ export default function SignupPage() {
|
||||
scrollTo(0, 0)
|
||||
}
|
||||
|
||||
const handleFinalSubmit = async () => {
|
||||
const handleFinalSubmit = async (finalProfile?: ProfileWithoutUser) => {
|
||||
setIsSubmitting(true)
|
||||
const referredByUsername = safeLocalStorage
|
||||
? (safeLocalStorage.getItem(CACHED_REFERRAL_USERNAME_KEY) ?? undefined)
|
||||
@@ -85,7 +84,7 @@ export default function SignupPage() {
|
||||
const deviceToken = ensureDeviceToken()
|
||||
try {
|
||||
const profile = removeNullOrUndefinedProps({
|
||||
...profileForm,
|
||||
...(finalProfile ?? profileForm),
|
||||
referred_by_username: referredByUsername,
|
||||
}) as any
|
||||
const {interests, causes, work, ...otherProfileProps} = profile
|
||||
|
||||
Reference in New Issue
Block a user