fix: disable update button when nothing changed (#5592)

* fix: disable update button when nothing changed

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: invalidate after dsubmission

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* Fix isDirty state for appearance settings

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: alannnc <alannnc@gmail.com>
pull/5654/head
Udit Takkar 2022-11-24 04:53:40 +05:30 committed by GitHub
parent e3a7fd006b
commit d9a8ab3864
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 136 additions and 111 deletions

View File

@ -41,9 +41,25 @@ const SkeletonLoader = () => {
const AppearanceView = () => { const AppearanceView = () => {
const { t } = useLocale(); const { t } = useLocale();
const utils = trpc.useContext();
const { data: user, isLoading } = trpc.viewer.me.useQuery(); const { data: user, isLoading } = trpc.viewer.me.useQuery();
const formMethods = useForm({
defaultValues: {
theme: user?.theme,
brandColor: user?.brandColor || "#292929",
darkBrandColor: user?.darkBrandColor || "#fafafa",
hideBranding: user?.hideBranding,
},
});
const {
formState: { isSubmitting, isDirty },
} = formMethods;
const mutation = trpc.viewer.updateProfile.useMutation({ const mutation = trpc.viewer.updateProfile.useMutation({
onSuccess: () => { onSuccess: async () => {
await utils.viewer.me.invalidate();
showToast(t("settings_updated_successfully"), "success"); showToast(t("settings_updated_successfully"), "success");
}, },
onError: () => { onError: () => {
@ -51,129 +67,138 @@ const AppearanceView = () => {
}, },
}); });
const formMethods = useForm();
if (isLoading) return <SkeletonLoader />; if (isLoading) return <SkeletonLoader />;
if (user) if (!user) return null;
return (
<Form
form={formMethods}
handleSubmit={(values) => {
mutation.mutate({
...values,
// Radio values don't support null as values, therefore we convert an empty string
// back to null here.
theme: values.theme || null,
});
}}>
<Meta title="Appearance" description="Manage settings for your booking appearance" />
<div className="mb-6 flex items-center text-sm">
<div>
<p className="font-semibold">{t("theme")}</p>
<p className="text-gray-600">{t("theme_applies_note")}</p>
</div>
</div>
<div className="flex flex-col justify-between sm:flex-row">
<ThemeLabel
variant="system"
value={null}
label={t("theme_system")}
defaultChecked={user.theme === null}
register={formMethods.register}
/>
<ThemeLabel
variant="light"
value="light"
label={t("theme_light")}
defaultChecked={user.theme === "light"}
register={formMethods.register}
/>
<ThemeLabel
variant="dark"
value="dark"
label={t("theme_dark")}
defaultChecked={user.theme === "dark"}
register={formMethods.register}
/>
</div>
<hr className="border-1 my-8 border-neutral-200" /> const isDisabled = isSubmitting || !isDirty;
<div className="mb-6 flex items-center text-sm">
<div>
<p className="font-semibold">{t("custom_brand_colors")}</p>
<p className="mt-0.5 leading-5 text-gray-600">{t("customize_your_brand_colors")}</p>
</div>
</div>
<div className="block justify-between sm:flex"> return (
<Controller <Form
name="brandColor" form={formMethods}
control={formMethods.control} handleSubmit={(values) => {
defaultValue={user.brandColor} mutation.mutate({
render={() => ( ...values,
<div> // Radio values don't support null as values, therefore we convert an empty string
<p className="mb-2 block text-sm font-medium text-gray-900">{t("light_brand_color")}</p> // back to null here.
<ColorPicker theme: values.theme || null,
defaultValue={user.brandColor} });
onChange={(value) => formMethods.setValue("brandColor", value)} }}>
/> <Meta title="Appearance" description="Manage settings for your booking appearance" />
</div> <div className="mb-6 flex items-center text-sm">
)} <div>
/> <p className="font-semibold">{t("theme")}</p>
<Controller <p className="text-gray-600">{t("theme_applies_note")}</p>
name="darkBrandColor"
control={formMethods.control}
defaultValue={user.darkBrandColor}
render={() => (
<div className="mt-6 sm:mt-0">
<p className="mb-2 block text-sm font-medium text-gray-900">{t("dark_brand_color")}</p>
<ColorPicker
defaultValue={user.darkBrandColor}
onChange={(value) => formMethods.setValue("darkBrandColor", value)}
/>
</div>
)}
/>
</div> </div>
{/* TODO future PR to preview brandColors */} </div>
{/* <Button <div className="flex flex-col justify-between sm:flex-row">
<ThemeLabel
variant="system"
value={null}
label={t("theme_system")}
defaultChecked={user.theme === null}
register={formMethods.register}
/>
<ThemeLabel
variant="light"
value="light"
label={t("theme_light")}
defaultChecked={user.theme === "light"}
register={formMethods.register}
/>
<ThemeLabel
variant="dark"
value="dark"
label={t("theme_dark")}
defaultChecked={user.theme === "dark"}
register={formMethods.register}
/>
</div>
<hr className="border-1 my-8 border-neutral-200" />
<div className="mb-6 flex items-center text-sm">
<div>
<p className="font-semibold">{t("custom_brand_colors")}</p>
<p className="mt-0.5 leading-5 text-gray-600">{t("customize_your_brand_colors")}</p>
</div>
</div>
<div className="block justify-between sm:flex">
<Controller
name="brandColor"
control={formMethods.control}
defaultValue={user.brandColor}
render={() => (
<div>
<p className="mb-2 block text-sm font-medium text-gray-900">{t("light_brand_color")}</p>
<ColorPicker
defaultValue={user.brandColor}
onChange={(value) => formMethods.setValue("brandColor", value, { shouldDirty: true })}
/>
</div>
)}
/>
<Controller
name="darkBrandColor"
control={formMethods.control}
defaultValue={user.darkBrandColor}
render={() => (
<div className="mt-6 sm:mt-0">
<p className="mb-2 block text-sm font-medium text-gray-900">{t("dark_brand_color")}</p>
<ColorPicker
defaultValue={user.darkBrandColor}
onChange={(value) => formMethods.setValue("darkBrandColor", value, { shouldDirty: true })}
/>
</div>
)}
/>
</div>
{/* TODO future PR to preview brandColors */}
{/* <Button
color="secondary" color="secondary"
EndIcon={Icon.FiExternalLink} EndIcon={Icon.FiExternalLink}
className="mt-6" className="mt-6"
onClick={() => window.open(`${WEBAPP_URL}/${user.username}/${user.eventTypes[0].title}`, "_blank")}> onClick={() => window.open(`${WEBAPP_URL}/${user.username}/${user.eventTypes[0].title}`, "_blank")}>
Preview Preview
</Button> */} </Button> */}
<hr className="border-1 my-8 border-neutral-200" /> <hr className="border-1 my-8 border-neutral-200" />
<Controller <Controller
name="hideBranding" name="hideBranding"
control={formMethods.control} control={formMethods.control}
defaultValue={user.hideBranding} defaultValue={user.hideBranding}
render={({ field: { value } }) => ( render={({ field: { value } }) => (
<> <>
<div className="flex w-full text-sm"> <div className="flex w-full text-sm">
<div className="mr-1 flex-grow"> <div className="mr-1 flex-grow">
<div className="flex items-center"> <div className="flex items-center">
<p className="mr-2 font-semibold">{t("disable_cal_branding")}</p>{" "} <p className="mr-2 font-semibold">{t("disable_cal_branding")}</p>
<Badge variant="gray">{t("pro")}</Badge> <Badge variant="gray">{t("pro")}</Badge>
</div>
<p className="mt-0.5 text-gray-600">{t("removes_cal_branding")}</p>
</div>
<div className="flex-none">
<Switch
onCheckedChange={(checked) => formMethods.setValue("hideBranding", checked)}
checked={value}
/>
</div> </div>
<p className="mt-0.5 text-gray-600">{t("removes_cal_branding")}</p>
</div> </div>
</> <div className="flex-none">
)} <Switch
/> id="hideBranding"
<Button type="submit" color="primary" className="mt-8"> onCheckedChange={(checked) =>
{t("update")} formMethods.setValue("hideBranding", checked, { shouldDirty: true })
</Button> }
</Form> checked={value}
); />
</div>
</div>
</>
)}
/>
<Button
disabled={isDisabled}
type="submit"
loading={mutation.isLoading}
color="primary"
className="mt-8">
{t("update")}
</Button>
</Form>
);
}; };
AppearanceView.getLayout = getLayout; AppearanceView.getLayout = getLayout;