Selaa lähdekoodia

change password fn

tags/Baseline_30082024_FRONTEND_UAT
MSI\derek 1 vuosi sitten
vanhempi
commit
07c7623e12
7 muutettua tiedostoa jossa 387 lisäystä ja 0 poistoa
  1. +53
    -0
      src/app/(main)/settings/changepassword/page.tsx
  2. +22
    -0
      src/app/(main)/settings/staff/user/page.tsx
  3. +107
    -0
      src/components/ChangePassword/ChangePassword.tsx
  4. +144
    -0
      src/components/ChangePassword/ChangePasswordForm.tsx
  5. +40
    -0
      src/components/ChangePassword/ChangePasswordLoading.tsx
  6. +20
    -0
      src/components/ChangePassword/ChangePasswordWrapper.tsx
  7. +1
    -0
      src/components/ChangePassword/index.ts

+ 53
- 0
src/app/(main)/settings/changepassword/page.tsx Näytä tiedosto

@@ -0,0 +1,53 @@
import { preloadClaims } from "@/app/api/claims";
import { preloadStaff, preloadTeamLeads } from "@/app/api/staff";
import ChangePassword from "@/components/ChangePassword";
import StaffSearch from "@/components/StaffSearch";
import TeamSearch from "@/components/TeamSearch";
import UserGroupSearch from "@/components/UserGroupSearch";
import UserSearch from "@/components/UserSearch";
import { I18nProvider, getServerI18n } from "@/i18n";
import Add from "@mui/icons-material/Add";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import { Metadata } from "next";
import Link from "next/link";
import { Suspense } from "react";


export const metadata: Metadata = {
title: "Change Password",
};


const ChangePasswordPage: React.FC = async () => {
const { t } = await getServerI18n("User Group");
// preloadTeamLeads();
// preloadStaff();
return (
<>
<Stack
direction="row"
justifyContent="space-between"
flexWrap="wrap"
rowGap={2}
>
<Typography variant="h4" marginInlineEnd={2}>
{t("Change Password")}
</Typography>
</Stack>
{/* <I18nProvider namespaces={["User Group", "common"]}>
<Suspense fallback={<UserGroupSearch.Loading />}>
<UserGroupSearch />
</Suspense>
</I18nProvider> */}
<I18nProvider namespaces={["User Group", "common"]}>
<Suspense fallback={<ChangePassword.Loading />}>
<ChangePassword />
</Suspense>
</I18nProvider>
</>
);
};
export default ChangePasswordPage;

+ 22
- 0
src/app/(main)/settings/staff/user/page.tsx Näytä tiedosto

@@ -0,0 +1,22 @@
import { Edit } from "@mui/icons-material";
import { Metadata } from "next";
import { I18nProvider, getServerI18n } from "@/i18n";
import EditUser from "@/components/EditUser";
import { Typography } from "@mui/material";
import { Suspense } from "react";

const User: React.FC = async () => {
const { t } = await getServerI18n("user");

return (
<>
<Typography variant="h4">{t("Edit User")}</Typography>
<I18nProvider namespaces={["user", "common"]}>
<Suspense fallback={<EditUser.Loading />}>
<EditUser />
</Suspense>
</I18nProvider>
</>
);
};
export default User;

+ 107
- 0
src/components/ChangePassword/ChangePassword.tsx Näytä tiedosto

@@ -0,0 +1,107 @@
"use client";
import { PasswordInputs, changePassword } from "@/app/api/user/actions";
import { Grid } from "@mui/material";
import { useRouter } from "next/navigation";
import { useCallback, useState } from "react";
import { FormProvider, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Button, Stack, Tab, Tabs, TabsProps, Typography } from "@mui/material";
import { Check, Close, Error } from "@mui/icons-material";
import ChagnePasswordForm from "./ChangePasswordForm";
import { ServerFetchError } from "@/app/utils/fetchUtil";

// interface Props {
// // auth?: auth[]
// // users?: UserResult[]
// }

const ChangePassword: React.FC = () => {
const formProps = useForm<PasswordInputs>();
const [serverError, setServerError] = useState("");
const router = useRouter();
// const [tabIndex, setTabIndex] = useState(0);
const { t } = useTranslation();

const onSubmit = useCallback<SubmitHandler<PasswordInputs>>(
async (data) => {
try {
let haveError = false;
// Minimum eight characters, at least one uppercase letter, one lowercase letter, one number and one special character:
let regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/

if (data.newPassword.length < 8 || data.newPassword.length > 20) {
haveError = true
formProps.setError("newPassword", { message: "The password requires 8-20 characters", type: "required" })
}
if (!regex.test(data.newPassword)) {
haveError = true
formProps.setError("newPassword", { message: "A combination of uppercase letters, lowercase letters, numbers, and symbols is required.", type: "required" })
}
if (data.password == data.newPassword) {
haveError = true
formProps.setError("newPassword", { message: "The new password cannot be the same as the old password", type: "required" })
}
if (data.newPassword != data.newPasswordCheck) {
haveError = true
formProps.setError("newPassword", { message: "The new password has to be the same as the new password", type: "required" })
formProps.setError("newPasswordCheck", { message: "The new password has to be the same as the new password", type: "required" })
}
if (haveError) {
return
}
const postData = {
password: data.password,
newPassword: data.newPassword
}
// await changePassword(postData)
// router.replace("/home")
} catch (e) {
console.log(e)
setServerError(t("An error has occurred. Please try again later."));
}
},
[router]
);

const handleCancel = () => {
router.push(`/home`);
};

const onSubmitError = useCallback<SubmitErrorHandler<PasswordInputs>>(
(errors) => {
console.log(errors);
},
[]
);

return (
<FormProvider {...formProps}>
<Stack
spacing={2}
component="form"
onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
>
<ChagnePasswordForm />
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
variant="outlined"
startIcon={<Close />}
onClick={handleCancel}
>
{t("Cancel")}
</Button>
<Button
variant="contained"
startIcon={<Check />}
type="submit"
// disabled={Boolean(formProps.watch("isGridEditing"))}
>
{t("Confirm")}
</Button>
</Stack>
</Stack>
</FormProvider>
);
};

export default ChangePassword;

+ 144
- 0
src/components/ChangePassword/ChangePasswordForm.tsx Näytä tiedosto

@@ -0,0 +1,144 @@
"use client";
import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Grid from "@mui/material/Grid";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { useCallback, useState } from "react";
import { PasswordInputs } from "@/app/api/user/actions";
import { Visibility, VisibilityOff } from "@mui/icons-material";
import { IconButton, InputAdornment } from "@mui/material";

const ChagnePasswordForm: React.FC = () => {
const { t } = useTranslation();
const [showNewPassword, setShowNewPassword] = useState(false);
const handleClickShowNewPassword = () => setShowNewPassword(!showNewPassword);
const handleMouseDownNewPassword = () => setShowNewPassword(!showNewPassword);

const [showPassword, setShowPassword] = useState(false);
const handleClickShowPassword = () => setShowPassword(!showPassword);
const handleMouseDownPassword = () => setShowPassword(!showPassword);

const {
register,
formState: { errors, defaultValues },
control,
reset,
resetField,
setValue,
} = useFormContext<PasswordInputs>();

// const resetGroup = useCallback(() => {
// console.log(defaultValues);
// if (defaultValues !== undefined) {
// resetField("description");
// }
// }, [defaultValues]);

return (
<Card sx={{ display: "block" }}>
<CardContent component={Stack} spacing={4}>
<Box>
<Typography variant="overline" display="block" marginBlockEnd={1}>
{t("Group Info")}
</Typography>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={6}>
<TextField
label={t("Input Old Password")}
fullWidth
type={showPassword ? "text" : "password"}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
>
{showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
)
}}
{...register("password", {
required: true,
})}
error={Boolean(errors.password)}
helperText={
Boolean(errors.password) &&
(errors.password?.message
? t(errors.password.message)
: t("Please input correct password"))
}
/>
</Grid>
<Grid item xs={6} />
<Grid item xs={6}>
<TextField
label={t("Input New Password")}
fullWidth
type={showNewPassword ? "text" : "password"}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowNewPassword}
onMouseDown={handleMouseDownNewPassword}
>
{showNewPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
)
}}
{...register("newPassword")}
error={Boolean(errors.newPassword)}
helperText={
Boolean(errors.newPassword) &&
(errors.newPassword?.message
? t(errors.newPassword.message)
: t("Please input correct newPassword"))
}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Input New Password Again")}
fullWidth
type={showNewPassword ? "text" : "password"}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowNewPassword}
onMouseDown={handleMouseDownNewPassword}
>
{showNewPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
)
}}
{...register("newPasswordCheck")}
error={Boolean(errors.newPassword)}
helperText={
Boolean(errors.newPassword) &&
(errors.newPassword?.message
? t(errors.newPassword.message)
: t("Please input correct newPassword"))
}
/>
</Grid>
</Grid>
</Box>
</CardContent>
</Card>
);
};
export default ChagnePasswordForm;

+ 40
- 0
src/components/ChangePassword/ChangePasswordLoading.tsx Näytä tiedosto

@@ -0,0 +1,40 @@
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Skeleton from "@mui/material/Skeleton";
import Stack from "@mui/material/Stack";
import React from "react";

// Can make this nicer
export const ChangePasswordLoading: React.FC = () => {
return (
<>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton
variant="rounded"
height={50}
width={100}
sx={{ alignSelf: "flex-end" }}
/>
</Stack>
</CardContent>
</Card>
<Card>Change Password
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
</Stack>
</CardContent>
</Card>
</>
);
};

export default ChangePasswordLoading;

+ 20
- 0
src/components/ChangePassword/ChangePasswordWrapper.tsx Näytä tiedosto

@@ -0,0 +1,20 @@
import React from "react";
import ChangePasswordLoading from "./ChangePasswordLoading";
import ChangePassword from "./ChangePassword";

interface SubComponents {
Loading: typeof ChangePasswordLoading;
}

const ChangePasswordWrapper: React.FC & SubComponents = async () => {
// const records = await fetchAuth()
// const users = await fetchUser()
// console.log(users)
// const auth = records.records as auth[]

return <ChangePassword />;
};

ChangePasswordWrapper.Loading = ChangePasswordLoading;

export default ChangePasswordWrapper;

+ 1
- 0
src/components/ChangePassword/index.ts Näytä tiedosto

@@ -0,0 +1 @@
export { default } from "./ChangePasswordWrapper";

Ladataan…
Peruuta
Tallenna