| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- import { zodResolver } from '@hookform/resolvers/zod'
- import { groupBy } from 'lodash'
- import { SubmitHandler, useForm } from 'react-hook-form'
- import { toast } from 'sonner'
- import {
- Button,
- Card,
- CardContent,
- CardFooter,
- Form,
- FormControl,
- FormField,
- Input,
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
- } from 'ui'
- import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
- import {
- PageSection,
- PageSectionContent,
- PageSectionMeta,
- PageSectionSummary,
- PageSectionTitle,
- } from 'ui-patterns/PageSection'
- import z from 'zod'
- import { useProfileIdentitiesQuery } from '@/data/profile/profile-identities-query'
- import { useProfileUpdateMutation } from '@/data/profile/profile-update-mutation'
- import { useProfile } from '@/lib/profile'
- const FormSchema = z.object({
- first_name: z.string().optional(),
- last_name: z.string().optional(),
- username: z.string().optional(),
- primary_email: z.string().email().optional(),
- })
- const formId = 'profile-information-form'
- export const ProfileInformation = () => {
- const { profile } = useProfile()
- const {
- data: identityData,
- isPending: isIdentitiesLoading,
- isSuccess: isIdentitiesSuccess,
- } = useProfileIdentitiesQuery()
- const identities = (identityData?.identities ?? []).filter((x) => x.identity_data?.email !== null)
- const dedupedIdentityEmails = Object.keys(groupBy(identities, 'identity_data.email'))
- const defaultValues = {
- first_name: profile?.first_name ?? '',
- last_name: profile?.last_name ?? '',
- username: profile?.username ?? '',
- primary_email: profile?.primary_email ?? '',
- }
- const form = useForm({
- resolver: zodResolver(FormSchema as any),
- defaultValues,
- values: defaultValues,
- })
- const { mutate: updateProfile, isPending: isUpdatingProfile } = useProfileUpdateMutation({
- onSuccess: (data) => {
- toast.success('Successfully saved profile')
- const { first_name, last_name, username, primary_email } = data
- form.reset({
- first_name: first_name ?? undefined,
- last_name: last_name ?? undefined,
- username,
- primary_email,
- })
- },
- onError: (error) => toast.error(`Failed to update profile: ${error.message}`),
- })
- const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = async (data) => {
- updateProfile({
- firstName: data.first_name || '',
- lastName: data.last_name || '',
- username: data.username || '',
- primaryEmail: data.primary_email || '',
- })
- }
- return (
- <PageSection>
- <PageSectionMeta>
- <PageSectionSummary>
- <PageSectionTitle>Profile information</PageSectionTitle>
- </PageSectionSummary>
- </PageSectionMeta>
- <PageSectionContent>
- <Form {...form}>
- <form id={formId} className="space-y-6 w-full" onSubmit={form.handleSubmit(onSubmit)}>
- <Card>
- <CardContent>
- <FormField
- control={form.control}
- name="first_name"
- render={({ field }) => (
- <FormItemLayout label="First name" layout="flex-row-reverse">
- <FormControl className="col-span-8">
- <Input {...field} placeholder="First name" className="w-full" />
- </FormControl>
- </FormItemLayout>
- )}
- />
- </CardContent>
- <CardContent>
- <FormField
- control={form.control}
- name="last_name"
- render={({ field }) => (
- <FormItemLayout label="Last name" layout="flex-row-reverse">
- <FormControl className="col-span-8">
- <Input {...field} placeholder="Last name" className="w-full" />
- </FormControl>
- </FormItemLayout>
- )}
- />
- </CardContent>
- <CardContent>
- <FormField
- control={form.control}
- name="primary_email"
- render={({ field }) => (
- <FormItemLayout
- label="Primary email"
- description={
- profile?.is_sso_user
- ? 'Managed by your SSO provider and cannot be changed here'
- : 'Used for account notifications'
- }
- layout="flex-row-reverse"
- >
- <FormControl className="col-span-8">
- <div className="flex flex-col gap-1">
- <Select
- value={field.value}
- onValueChange={field.onChange}
- disabled={profile?.is_sso_user}
- >
- <SelectTrigger className="col-span-8 w-full">
- <SelectValue placeholder="Select primary email" />
- </SelectTrigger>
- <SelectContent className="col-span-8">
- {isIdentitiesSuccess &&
- dedupedIdentityEmails.map((email) => (
- <SelectItem key={email} value={email}>
- {email}
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
- </div>
- </FormControl>
- </FormItemLayout>
- )}
- />
- </CardContent>
- <CardContent>
- <FormField
- control={form.control}
- name="username"
- render={({ field }) => (
- <FormItemLayout
- label="Username"
- description={
- profile?.is_sso_user
- ? 'Managed by your SSO provider and cannot be changed here'
- : 'Display name used across dashboard'
- }
- layout="flex-row-reverse"
- >
- <FormControl className="col-span-8">
- <div className="flex flex-col gap-1">
- <Input
- {...field}
- className="w-full"
- placeholder="Username"
- disabled={profile?.is_sso_user}
- />
- </div>
- </FormControl>
- </FormItemLayout>
- )}
- />
- </CardContent>
- <CardFooter className="justify-end space-x-2">
- {form.formState.isDirty && (
- <Button type="default" onClick={() => form.reset()}>
- Cancel
- </Button>
- )}
- <Button
- type="primary"
- htmlType="submit"
- loading={isUpdatingProfile || isIdentitiesLoading}
- disabled={!form.formState.isDirty}
- >
- Save
- </Button>
- </CardFooter>
- </Card>
- </form>
- </Form>
- </PageSectionContent>
- </PageSection>
- )
- }
|