/
durovcode
/
Frontend
Обзор
Документация
Войти
/
durovcode
/
Frontend
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
Аналитика
master
src/components/profileSettingsDrawer.tsx
250 строк
10 KB
Alch3m1st
Add Docker
22 сен 2024, 01:25
22 сен 2024, 01:25
f92ba9d
Код
Авторство
О чём код?
"use client" import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, } from "@/components/ui/drawer" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Button } from "./ui/button" import { Bolt, LogOut, ShieldCheck } from "lucide-react" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import QRCode from "react-qr-code" import React from "react" import { toast } from "sonner" import { InputOTP, InputOTPGroup, InputOTPSlot } from "./ui/input-otp" export const ProfileSettingsDrawer = () => { return ( <> <Drawer> <DrawerTrigger> <div className="w-[40px] h-[40px] rounded-full bg-gray-100 flex items-center justify-center cursor-pointer"> <Bolt /> </div> </DrawerTrigger> <DrawerContent> <DrawerHeader> <div className="flex flex-col items-center justify-center gap-3"> <Bolt className="w-10 h-10" /> <DrawerTitle>Настройки профиля</DrawerTitle> <DrawerDescription>Управляйте своим пространством</DrawerDescription> </div> </DrawerHeader> <div className="flex flex-col items-center justify-center pr-10 pl-10 pt-5 pb-5 gap-4"> <div className="grid w-full max-w-sm items-center gap-1.5"> <Label htmlFor="email">Эл. почта</Label> <Input type="email" id="email" placeholder="mail@example.ru" /> </div> <div className="grid w-full max-w-sm items-center gap-1.5"> <Label htmlFor="name">Ваше имя</Label> <Input type="text" id="name" placeholder="Кто-то-тамчик" /> </div> <div className="grid w-full max-w-sm items-center gap-1.5"> <Label htmlFor="picture">Картинка профиля</Label> <Input id="picture" type="file" /> </div> {!localStorage.getItem('otpJwt') && ( <div className="grid w-full max-w-sm items-center gap-1.5"> <OtpModal></OtpModal> </div> )} <div className="grid w-full max-w-sm items-center gap-1.5"> <Button onClick={() => { localStorage.removeItem("jwt"); window.location.href = "/"; }} className="w-full"> <LogOut className="mr-2 h-4 w-4" /> Выход из профиля </Button> </div> </div> <DrawerFooter> <DrawerClose> <Button variant="outline">Закрыть</Button> </DrawerClose> </DrawerFooter> </DrawerContent> </Drawer> </> ) } export const OtpModal = () => { const [otpKey, setOtpKey] = React.useState(""); const [otpData, setOtpData] = React.useState(""); const [otpValue, setOtpValue] = React.useState(""); function generateOtpQr() { fetch(`${import.meta.env.VITE_API_BASE_URL}${import.meta.env.VITE_API_PATH}/account/assign-otp`, { method: "GET", credentials: 'include', mode: "cors", headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem("jwt")}` }, }) .then(response => response.json()) .then(result => { if (result["ok"]) { console.log(result); setOtpKey( result["key"] ); setOtpData(result["uri"]); localStorage.setItem("jwt", result["token"]); } else { toast.error("Ошибочка!", { description: result["detail"], action: { label: "Скрыть" }, }); } }) .catch(error => { console.error('Error:', error); }); } function confirmOtp() { fetch(`${import.meta.env.VITE_API_BASE_URL}${import.meta.env.VITE_API_PATH}/account/assign-otp`, { method: "PUT", credentials: 'include', mode: "cors", headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem("jwt")}` }, body: JSON.stringify({ 'otp': otpValue }) }) .then(response => response.json()) .then(result => { if (result["ok"]) { toast.success("Двухфакторная аутентификация подключена! Вы можете закрыть это окно.", { description: result["detail"], action: { label: "Скрыть" }, }); } else { toast.error("Ошибочка!", { description: result["detail"], action: { label: "Скрыть" }, }); } }) .catch(error => { console.error('Error:', error); }); } return ( <> <AlertDialog> <AlertDialogTrigger> <Button onClick={() => { generateOtpQr(); }} className="w-full"> <ShieldCheck className="mr-2 h-4 w-4" /> Сгенерировать QR (OTP) </Button> </AlertDialogTrigger> <AlertDialogContent> <AlertDialogHeader> <div className="flex flex-col items-center"> <AlertDialogTitle>Ключи двухфакторной аутентификации</AlertDialogTitle> </div> </AlertDialogHeader> <div className="flex flex-col items-center justify-center gap-5"> <code className="w-fit relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold"> {otpKey ? ( <> {otpKey} </> ) : ( <> Ключ отсутствует </> )} </code> <div style={{ height: "auto", margin: "0 auto", maxWidth: 256, width: "100%" }}> <QRCode size={256} style={{ height: "auto", maxWidth: "100%", width: "100%" }} value={otpData} viewBox={`0 0 506 506`} /> </div> <p className="text-sm text-center max-w-[350px] text-gray-400">Сканируйте изображение в приложении OTP-аутентификации на Вашем смартфоне (Например, google authenticator). Если не можете использовать QR — введите ключ выше.</p> </div> <div> <div className='flex w-full flex-col gap-6 flex flex-col items-center justify-center'> <div className="space-y-2"> <InputOTP maxLength={6} value={otpValue} onChange={(value) => { setOtpValue(value) // if (otpValue.length >= 5) { // checkOtp(); // } }} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> <div className="text-center text-sm"> <>Введите одноразовый <br /> код из приложения</> </div> </div> <Button onClick={() => { confirmOtp(); }}>Подтвердить</Button> </div> </div> <div className="flex items-center justify-center gap-2"> <AlertDialogCancel>Отмена</AlertDialogCancel> </div> </AlertDialogContent> </AlertDialog> </> ) }