Skip to content

Commit

Permalink
made password dialogs naming simpler (#544)
Browse files Browse the repository at this point in the history
  • Loading branch information
ImJustChew authored Sep 28, 2024
2 parents 8049373 + 3205992 commit 70e2f25
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 219 deletions.
10 changes: 3 additions & 7 deletions src/app/[lang]/(mods-pages)/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import useUserTimetable from "@/hooks/contexts/useUserTimetable";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { GraduationCap, Hash } from "lucide-react";
import ChangePasswordDialog from "@/components/Forms/ChangePasswordDialog";
import { useAuthState } from "react-firebase-hooks/auth";
import { auth } from "@/config/firebase";
import {
Expand All @@ -45,6 +44,7 @@ import {
import { useLocalStorage } from "usehooks-ts";
import { Badge } from "@/components/ui/badge";
import { event } from "@/lib/gtag";
import ChangePasswordDialog from "@/components/Forms/ChangePasswordDialog";

const DisplaySettingsCard = () => {
const { darkMode, setDarkMode, language, setLanguage } = useSettings();
Expand Down Expand Up @@ -183,7 +183,6 @@ const CalendarSettingsCard = () => {
const AccountInfoSettingsCard = () => {
const { user, ais, signOut, getACIXSTORE } = useHeadlessAIS();
const dict = useDictionary();
const [openChangePassword, setOpenChangePassword] = useState(false);

return (
<Card id="account">
Expand All @@ -209,12 +208,9 @@ const AccountInfoSettingsCard = () => {
</div>
</div>
<div className="flex flex-row justify-end items-center w-full gap-2">
<ChangePasswordDialog
open={openChangePassword}
setOpen={setOpenChangePassword}
>
{/* <ChangePasswordDialog>
<Button variant={"ghost"}>更新密碼</Button>
</ChangePasswordDialog>
</ChangePasswordDialog> */}
<Dialog>
<DialogTrigger asChild>
<Button variant="destructive">
Expand Down
145 changes: 94 additions & 51 deletions src/components/Forms/ChangePasswordDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"use client";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
Expand All @@ -26,98 +24,143 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { PropsWithChildren } from "react";
import { useHeadlessAIS } from "@/hooks/contexts/useHeadlessAIS";
import { Loader2 } from "lucide-react";
import { toast } from "../ui/use-toast";
import { updateUserPassword } from "@/lib/headless_ais";

// * 8~16字元
// * 至少包含1大寫英文字母
// * 至少包含1小寫英文字母
// * 至少包含1個數字
// * 密碼三代不重覆
const formSchema = z.object({
newPassword: z
.string()
.min(8)
.max(16)
.refine(
(value) => {
const hasLowerCase = /[a-z]/.test(value);
const hasUpperCase = /[A-Z]/.test(value);
const hasNumber = /[0-9]/.test(value);
return hasLowerCase && hasUpperCase && hasNumber;
},
{
message:
"Password must contain at least 1 uppercase letter, 1 lowercase letter, and 1 number",
},
),
});
const formSchema = z
.object({
oldPassword: z.string().min(8),
newPassword: z
.string()
.min(8)
.max(16)
.refine(
(value) => {
const hasLowerCase = /[a-z]/.test(value);
const hasUpperCase = /[A-Z]/.test(value);
const hasNumber = /[0-9]/.test(value);
return hasLowerCase && hasUpperCase && hasNumber;
},
{
message:
"Password must contain at least 1 uppercase letter, 1 lowercase letter, and 1 number",
},
),
confirmPassword: z.string().min(8).max(16),
})
.refine((data) => data.newPassword === data.confirmPassword, {
message: "Passwords do not match",
});

const ChangePasswordDialog = ({
open,
setOpen,
children,
}: PropsWithChildren<{ open: boolean; setOpen: (s: boolean) => void }>) => {
const { signIn, signOut, user } = useHeadlessAIS();
const ChangePasswordDialog = ({ children }: PropsWithChildren) => {
const { getACIXSTORE, signIn, user } = useHeadlessAIS();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
oldPassword: "",
newPassword: "",
confirmPassword: "",
},
});

async function onSubmit(values: z.infer<typeof formSchema>) {
if (!user) return;
const ACIXSTORE = await getACIXSTORE(true);

if (!ACIXSTORE) {
throw new Error("Login Failed!");
}

const res = await updateUserPassword(
ACIXSTORE,
values.oldPassword,
values.newPassword,
);

if (!res) {
throw new Error("Login Failed!");
}

const success = await signIn(user.studentid, values.newPassword);

if (!success) {
setOpen(false);
toast({
title: "密碼更新成功",
});
} else {
toast({
title: "密碼更新失敗",
});
throw new Error("Login Failed!");
}

toast({
title: "Password Updated!",
});
}

return (
<Dialog open={open} onOpenChange={setOpen}>
<Dialog>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>更新密碼</DialogTitle>
<DialogTitle>跟新校務資訊系統密碼</DialogTitle>
<DialogDescription>
同學~你好像在校務資訊系統有跟新密碼哦,請也在這邊跟新!
在這邊更新密碼會同時更新校務資訊系統的密碼哦~
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="oldPassword"
render={({ field }) => (
<FormItem>
<FormLabel>Old Password</FormLabel>
<FormControl>
<Input
placeholder="Old Password"
type="password"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="newPassword"
render={({ field }) => (
<FormItem>
<FormLabel>New Password</FormLabel>
<FormControl>
<Input placeholder="Password" type="password" {...field} />
<Input
placeholder="New Password"
type="password"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="confirmPassword"
render={({ field }) => (
<FormItem>
<FormLabel>Confirm Password</FormLabel>
<FormControl>
<Input
placeholder="Confirm Password"
type="password"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter>
<DialogClose>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button type="submit">
{form.formState.isSubmitting ? (
<Loader2 className="animate-spin" />
) : (
"Submit"
)}
</Button>
</DialogFooter>
<Button type="submit">Submit</Button>
</form>
</Form>
</DialogContent>
Expand Down
130 changes: 130 additions & 0 deletions src/components/Forms/RenewPasswordDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"use client";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";

import { z } from "zod";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "../ui/form";
import { Button } from "../ui/button";
import { Input } from "../ui/input";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { PropsWithChildren } from "react";
import { useHeadlessAIS } from "@/hooks/contexts/useHeadlessAIS";
import { Loader2 } from "lucide-react";
import { toast } from "../ui/use-toast";

// * 8~16字元
// * 至少包含1大寫英文字母
// * 至少包含1小寫英文字母
// * 至少包含1個數字
// * 密碼三代不重覆
const formSchema = z.object({
newPassword: z
.string()
.min(8)
.max(16)
.refine(
(value) => {
const hasLowerCase = /[a-z]/.test(value);
const hasUpperCase = /[A-Z]/.test(value);
const hasNumber = /[0-9]/.test(value);
return hasLowerCase && hasUpperCase && hasNumber;
},
{
message:
"Password must contain at least 1 uppercase letter, 1 lowercase letter, and 1 number",
},
),
});

const RenewPasswordDialog = ({
open,
setOpen,
children,
}: PropsWithChildren<{ open: boolean; setOpen: (s: boolean) => void }>) => {
const { signIn, signOut, user, isACIXSTOREValid } = useHeadlessAIS();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
newPassword: "",
},
});

async function onSubmit(values: z.infer<typeof formSchema>) {
if (!user) return;
const success = await signIn(user.studentid, values.newPassword);
if (!success) {
setOpen(false);
toast({
title: "密碼更新成功",
});
} else {
toast({
title: "密碼更新失敗",
});
}
}

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild disabled={!isACIXSTOREValid}>
{children}
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>更新密碼</DialogTitle>
<DialogDescription>
同學~你好像在校務資訊系統有跟新密碼哦,請也在這邊跟新!
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="newPassword"
render={({ field }) => (
<FormItem>
<FormLabel>New Password</FormLabel>
<FormControl>
<Input placeholder="Password" type="password" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter>
<DialogClose>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button type="submit">
{form.formState.isSubmitting ? (
<Loader2 className="animate-spin" />
) : (
"Submit"
)}
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
};

export default RenewPasswordDialog;
Loading

0 comments on commit 70e2f25

Please sign in to comment.