204 lines
7.8 KiB
TypeScript
204 lines
7.8 KiB
TypeScript
import apiClient from "@/services/api";
|
||
import { cn } from "@/utils/class-merge";
|
||
import { EyeIcon, EyeSlashIcon, XCircleIcon } from "@heroicons/react/24/outline";
|
||
import { FunctionComponent } from "preact";
|
||
import { useEffect, useState } from "preact/hooks";
|
||
import {
|
||
InputSwitch,
|
||
InputSwitchPassThroughMethodOptions,
|
||
InputSwitchPassThroughOptions,
|
||
} from "primereact/inputswitch";
|
||
import { SubmitHandler, useForm } from "react-hook-form";
|
||
import Button from "./ui/Button";
|
||
import ModalWindow, { ModalWindowProps } from "./ui/Modal";
|
||
|
||
interface ISettings {
|
||
login: string;
|
||
oldPassword: string;
|
||
newPassword: string;
|
||
tgId: string;
|
||
}
|
||
|
||
export interface IAPIResponse {
|
||
profile: IAPIProfile;
|
||
}
|
||
|
||
export interface IAPIProfile {
|
||
username: string;
|
||
email: string;
|
||
status: string;
|
||
avatar_url: string;
|
||
telegram_notifications: boolean;
|
||
telegram_chat_id: string;
|
||
}
|
||
|
||
export interface IAPIUpdateSettingsResponse {
|
||
success: boolean;
|
||
username: string;
|
||
email: string;
|
||
avatar_url: string;
|
||
password_changed: boolean;
|
||
}
|
||
|
||
const SwitchStyles: InputSwitchPassThroughOptions = {
|
||
root: ({ props }: InputSwitchPassThroughMethodOptions) => ({
|
||
className: cn("inline-block relative", "w-12 h-7", {
|
||
"opacity-60 select-none pointer-events-none cursor-default": props.disabled,
|
||
}),
|
||
}),
|
||
input: {
|
||
className: cn("absolute appearance-none top-0 left-0 size-full p-0 m-0 opacity-0 z-10 outline-none cursor-pointer"),
|
||
},
|
||
slider: ({ props }: InputSwitchPassThroughMethodOptions) => ({
|
||
className: cn(
|
||
"absolute cursor-pointer top-0 left-0 right-0 bottom-0 border border-transparent",
|
||
"transition-colors duration-200 rounded-2xl",
|
||
"focus:outline-none focus:outline-offset-0 focus:shadow-[0_0_0_0.2rem_rgba(191,219,254,1)] ",
|
||
"before:absolute before:content-'' before:top-1/2 before:bg-white before:w-5 before:h-5 before:left-1 before:-mt-2.5 before:rounded-full before:transition-duration-200",
|
||
{
|
||
"bg-gray-200 hover:bg-gray-300": !props.checked,
|
||
"bg-[rgba(251,194,199,0.53)] before:transform before:translate-x-5": props.checked,
|
||
}
|
||
),
|
||
}),
|
||
};
|
||
|
||
const ModalSettings: FunctionComponent<ModalWindowProps> = ({ isOpen, setIsOpen, onClose, ...rest }) => {
|
||
const { handleSubmit, register, setValue, reset } = useForm<ISettings>({
|
||
defaultValues: {
|
||
login: "",
|
||
oldPassword: "",
|
||
newPassword: "",
|
||
},
|
||
mode: "onChange",
|
||
});
|
||
|
||
const changeSettings: SubmitHandler<ISettings> = async (data: ISettings) => {
|
||
interface IReq {
|
||
username?: string;
|
||
current_password?: string;
|
||
password?: string;
|
||
}
|
||
const req: IReq = {
|
||
username: data.login || undefined,
|
||
current_password: data.oldPassword || undefined,
|
||
password: data.newPassword || undefined,
|
||
};
|
||
|
||
const response = await apiClient<IAPIUpdateSettingsResponse>("/api/settings/edit_profile/", {
|
||
method: "PATCH",
|
||
body: JSON.stringify(req),
|
||
});
|
||
if (!response.success) console.error("Error changing settings");
|
||
|
||
if (enableNotification && data.tgId) {
|
||
await apiClient("/api/settings/save_chat_id/", {
|
||
method: "PATCH",
|
||
body: JSON.stringify({ telegram_chat_id: data.tgId }),
|
||
});
|
||
if (oldEnabledNotif !== enableNotification) {
|
||
await apiClient("/api/settings/toggle_telegram_notifications/", { method: "PATCH" });
|
||
}
|
||
}
|
||
|
||
handleClose();
|
||
};
|
||
|
||
const [enableNotification, setEnableNotification] = useState(false);
|
||
const [login, setLogin] = useState("");
|
||
const [showOldPassword, setShowOldPassword] = useState(false);
|
||
const [showNewPassword, setShowNewPassword] = useState(false);
|
||
const [oldEnabledNotif, setOldEnabledNotif] = useState(false);
|
||
const [tgId, setTgId] = useState("");
|
||
|
||
useEffect(() => {
|
||
const getSettings = async () => {
|
||
const response = await apiClient<IAPIResponse>("/api/settings/view_settings/", { method: "GET" });
|
||
setEnableNotification(response.profile.telegram_notifications);
|
||
setOldEnabledNotif(response.profile.telegram_notifications);
|
||
setLogin(response.profile.username);
|
||
setTgId(response.profile.telegram_chat_id);
|
||
};
|
||
if (isOpen) getSettings();
|
||
}, [isOpen]);
|
||
|
||
const handleClose = () => {
|
||
setIsOpen!(false);
|
||
setEnableNotification(false);
|
||
reset();
|
||
if (onClose) onClose();
|
||
};
|
||
|
||
return (
|
||
<ModalWindow setIsOpen={setIsOpen} isOpen={isOpen} onClose={handleClose} {...rest} className="h-fit! md:w-[40%]!">
|
||
<form onSubmit={handleSubmit(changeSettings)} class="flex w-full flex-col gap-4">
|
||
<div class="flex flex-col items-start gap-2">
|
||
<span class="ms-5 text-sm">Имя</span>
|
||
<div class="flex w-full flex-row items-center rounded-[4rem] bg-[rgba(251,194,199,0.53)] px-5 py-3 leading-8">
|
||
<input class="flex-1 outline-0" {...register("login")} placeholder={login} />
|
||
<XCircleIcon class="size-6 cursor-pointer" onClick={() => setValue("login", "")} />
|
||
</div>
|
||
</div>
|
||
<div class="flex flex-col items-start gap-2">
|
||
<span class="ms-5 text-sm">Старый пароль</span>
|
||
<div class="flex w-full flex-row items-center gap-1 rounded-[4rem] bg-[rgba(251,194,199,0.53)] px-5 py-3 leading-8">
|
||
<input
|
||
class="flex-1 outline-0"
|
||
{...register("oldPassword")}
|
||
placeholder="Новый пароль"
|
||
type={showOldPassword ? "text" : "password"}
|
||
/>
|
||
{showOldPassword ? (
|
||
<EyeIcon class="size-6 cursor-pointer" onClick={() => setShowOldPassword(false)} />
|
||
) : (
|
||
<EyeSlashIcon class="size-6 cursor-pointer" onClick={() => setShowOldPassword(true)} />
|
||
)}
|
||
<XCircleIcon class="size-6 cursor-pointer" onClick={() => setValue("oldPassword", "")} />
|
||
</div>
|
||
</div>
|
||
<div class="flex flex-col items-start gap-2">
|
||
<span class="ms-5 text-sm">Новый пароль</span>
|
||
<div class="flex w-full flex-row items-center gap-1 rounded-[4rem] bg-[rgba(251,194,199,0.53)] px-5 py-3 leading-8">
|
||
<input
|
||
class="flex-1 outline-0"
|
||
{...register("newPassword", {
|
||
minLength: { value: 8, message: "Пароль должен быть не менее 8 символов" },
|
||
})}
|
||
placeholder="Новый пароль"
|
||
type={showNewPassword ? "text" : "password"}
|
||
/>
|
||
{showNewPassword ? (
|
||
<EyeIcon class="size-6 cursor-pointer" onClick={() => setShowNewPassword(false)} />
|
||
) : (
|
||
<EyeSlashIcon class="size-6 cursor-pointer" onClick={() => setShowNewPassword(true)} />
|
||
)}
|
||
<XCircleIcon class="size-6 cursor-pointer" onClick={() => setValue("newPassword", "")} />
|
||
</div>
|
||
</div>
|
||
<div class="flex flex-row items-center justify-between rounded-[4rem] px-5 py-4 ring-3 ring-[rgba(251,194,199,0.53)]">
|
||
<span>Уведомления</span>
|
||
<InputSwitch
|
||
pt={SwitchStyles}
|
||
checked={enableNotification}
|
||
onChange={(e) => setEnableNotification(e.value)}
|
||
/>
|
||
</div>
|
||
<div class={cn({ hidden: !enableNotification })}>
|
||
<div class="flex flex-col items-start gap-2">
|
||
<span class="ms-5 text-sm">Telegram ID</span>
|
||
<div class="flex w-full flex-row items-center rounded-[4rem] px-5 py-3 leading-8 ring-2 ring-[rgba(206,232,251,0.7)]">
|
||
<input class="flex-1 outline-0" {...register("tgId")} placeholder={tgId} />
|
||
<XCircleIcon class="size-6 cursor-pointer" onClick={() => setValue("tgId", "")} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<Button type="submit">Сохранить</Button>
|
||
</form>
|
||
</ModalWindow>
|
||
);
|
||
};
|
||
|
||
ModalSettings.displayName = "ModalSettings";
|
||
|
||
export default ModalSettings;
|