Commit bd1b5dd7 by Arjun Jhukal

updated the chagne password functionality at logged in user profile

parent bca8509d
...@@ -2,6 +2,7 @@ import GlassWrapper from '@/components/molecules/GlassWrapper'; ...@@ -2,6 +2,7 @@ import GlassWrapper from '@/components/molecules/GlassWrapper';
import TabController from '@/components/molecules/TabController'; import TabController from '@/components/molecules/TabController';
import UserProfileCard from "@/components/organism/Cards/UserProfileCard"; import UserProfileCard from "@/components/organism/Cards/UserProfileCard";
import ResetPasswordForm from '@/components/pages/auth/resetPassword/ResetPasswordForm';
import EditUserProfile from '@/components/pages/dashboard/userDashboard/account/profile/editProfile'; import EditUserProfile from '@/components/pages/dashboard/userDashboard/account/profile/editProfile';
import EditUserWallet from '@/components/pages/dashboard/userDashboard/account/profile/editUserWallet'; import EditUserWallet from '@/components/pages/dashboard/userDashboard/account/profile/editUserWallet';
import { useAppSelector } from '@/hooks/hook'; import { useAppSelector } from '@/hooks/hook';
...@@ -14,11 +15,14 @@ export default function AccountTab() { ...@@ -14,11 +15,14 @@ export default function AccountTab() {
const { data, isLoading } = useGetUserGameBalanceQuery(); const { data, isLoading } = useGetUserGameBalanceQuery();
const [currentActiveTab, setCurrentActiveTab] = React.useState<profileTabProps>("account_detail"); const [currentActiveTab, setCurrentActiveTab] = React.useState<profileTabProps>("account_detail");
const handleTabChange = (tab: string) => { const handleTabChange = (tab: string) => {
setCurrentActiveTab(tab as profileTabProps); setCurrentActiveTab(tab as profileTabProps);
}; };
const user = useAppSelector((state) => state.auth.user); const user = useAppSelector((state) => state.auth.user);
console.log(user);
const renderTabContent = () => { const renderTabContent = () => {
switch (currentActiveTab) { switch (currentActiveTab) {
case "account_detail": case "account_detail":
...@@ -27,7 +31,7 @@ export default function AccountTab() { ...@@ -27,7 +31,7 @@ export default function AccountTab() {
return <EditUserWallet />; return <EditUserWallet />;
case "change_password": case "change_password":
return (<div className="px-8 lg:pt-8 pb-8"> return (<div className="px-8 lg:pt-8 pb-8">
<h2> Change Password</h2> <ResetPasswordForm email={user?.email} />
</div>); </div>);
default: default:
return null; return null;
......
import PasswordField from '@/components/molecules/PasswordField'
import { useAppDispatch } from '@/hooks/hook'
import { PATH } from '@/routes/PATH'
import { useResetPasswordMutation } from '@/services/authApi'
import { clearTokens } from '@/slice/authSlice'
import { showToast, ToastVariant } from '@/slice/toastSlice'
import { Box, InputLabel, OutlinedInput } from '@mui/material'
import { ArrowLeft } from '@wandersonalwes/iconsax-react'
import { useFormik } from 'formik'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import React from 'react'
import * as Yup from 'yup';
const validationSchema = Yup.object().shape({
emailAddress: Yup.string()
.email('Must be a valid email')
.max(255)
.required('Email is required'),
password: Yup.string()
.required('Password is required')
.test(
'no-leading-trailing-whitespace',
'Password cannot start or end with spaces',
(value) => value === value?.trim()
)
.max(16, 'Password must be less than 10 characters'),
confirmPassword: Yup.string()
.oneOf([Yup.ref('password')], 'Passwords must match')
.required('Confirm Password is required'),
})
export default function ResetPasswordForm({ email }: { email?: string }) {
const router = useRouter();
const dispatch = useAppDispatch();
const initialValues = {
emailAddress: email || "",
password: "",
confirmPassword: "",
}
const [resetPassword, { isLoading }] = useResetPasswordMutation();
const { handleSubmit, handleBlur, handleChange, errors, dirty, values, touched } = useFormik(
{
initialValues,
validationSchema,
onSubmit: async (values) => {
try {
const response = await resetPassword({
email: values.emailAddress,
password: values.password,
password_confirmation: values.confirmPassword,
}).unwrap();
dispatch(
showToast({
message: response?.message || "New password is updated",
variant: ToastVariant.SUCCESS,
autoTime: true,
}),
);
dispatch(clearTokens());
router.replace(PATH.AUTH.LOGIN.ROOT);
}
catch (e: any) {
console.log("Error", e);
dispatch(
showToast({
message: e?.data?.message || "Unable to reset password. Try again later",
variant: ToastVariant.ERROR,
autoTime: true,
}),
);
}
}
}
)
return (
<form action="" onSubmit={handleSubmit}>
<div className="grid md:grid-cols-2 gap-x-3 gap-y-5">
{!email ? <div className="col-span-2">
<div className="input_field">
<InputLabel htmlFor="emailAddress">Email Address*</InputLabel>
<OutlinedInput
name='emailAddress'
id='emailAddress'
placeholder='example@example.com'
value={values.emailAddress}
onChange={handleChange}
onBlur={handleBlur}
error={Boolean(touched.emailAddress && errors.emailAddress)}
/>
{
touched.emailAddress && errors.emailAddress ?
<span className="error ">{errors.emailAddress}</span> : null
}
</div>
</div> : ""}
<div className="col-span-2">
<div className="input_field">
<PasswordField
name="password"
label="Password*"
placeholder="XXXXXXX"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
error={touched.password ? errors.password : undefined}
/>
</div>
</div>
<div className="col-span-2">
<div className="input_field">
<PasswordField
name="confirmPassword"
label="Confirm Password*"
placeholder="XXXXXXX"
value={values.confirmPassword}
onChange={handleChange}
onBlur={handleBlur}
error={touched.confirmPassword ? errors.confirmPassword : undefined}
/>
</div>
</div>
</div>
<div className="action__group text-center flex flex-col gap-4 mt-8">
<button className='ss-btn bg-primary-grad' disabled={!dirty}>{isLoading ? "Changing Password" : "Reset Password"}</button>
<Link href={PATH.DASHBOARD.ROOT} className='ss-btn bg-secondary-grad'>Login</Link>
</div>
</form>
)
}
"use client"; "use client";
import React, { useEffect, useState } from 'react' import React from 'react'
import AuthMessageBlock from '../authMessageBlock' import AuthMessageBlock from '../authMessageBlock'
import { Box, InputLabel, OutlinedInput } from '@mui/material' import ResetPasswordForm from './ResetPasswordForm';
import PasswordField from '@/components/molecules/PasswordField' import { Box } from '@mui/material';
import { ArrowLeft } from '@wandersonalwes/iconsax-react' import Link from 'next/link';
import Link from 'next/link' import { ArrowLeft } from '@wandersonalwes/iconsax-react';
import { PATH } from '@/routes/PATH' import { PATH } from '@/routes/PATH';
import * as Yup from 'yup';
import { useFormik } from 'formik';
import { useAppDispatch } from '@/hooks/hook'
import { showToast, ToastVariant } from '@/slice/toastSlice';
import { useResetPasswordMutation } from '@/services/authApi';
import { useRouter, useSearchParams } from 'next/navigation';
const validationSchema = Yup.object().shape({
emailAddress: Yup.string()
.email('Must be a valid email')
.max(255)
.required('Email is required'),
password: Yup.string()
.required('Password is required')
.test(
'no-leading-trailing-whitespace',
'Password cannot start or end with spaces',
(value) => value === value?.trim()
)
.max(16, 'Password must be less than 10 characters'),
confirmPassword: Yup.string()
.oneOf([Yup.ref('password')], 'Passwords must match')
.required('Confirm Password is required'),
})
export default function ResetPasswordPage() { export default function ResetPasswordPage() {
const router = useRouter();
const dispatch = useAppDispatch();
const initialValues = {
emailAddress: "",
password: "",
confirmPassword: "",
}
const [resetPassword, { isLoading }] = useResetPasswordMutation();
const { handleSubmit, handleBlur, handleChange, errors, dirty, values, touched } = useFormik(
{
initialValues,
validationSchema,
onSubmit: async (values) => {
try {
const response = await resetPassword({
email: values.emailAddress,
password: values.password,
password_confirmation: values.confirmPassword,
}).unwrap();
dispatch(
showToast({
message: response?.message || "New password is updated",
variant: ToastVariant.SUCCESS,
autoTime: true,
}),
);
router.replace(PATH.AUTH.LOGIN.ROOT);
}
catch (e: any) {
console.log("Error", e);
dispatch(
showToast({
message: e?.data?.message || "Unable to reset password. Try again later",
variant: ToastVariant.ERROR,
autoTime: true,
}),
);
}
}
}
)
return ( return (
<> <>
<AuthMessageBlock <AuthMessageBlock
...@@ -95,61 +27,7 @@ export default function ResetPasswordPage() { ...@@ -95,61 +27,7 @@ export default function ResetPasswordPage() {
<Link href={PATH.DASHBOARD.ROOT} className='text-[12px] leading-[120%] font-bold lg:text-[16px] hover:text-primary flex gap-2 items-center'><ArrowLeft />Back to Dashboard</Link> <Link href={PATH.DASHBOARD.ROOT} className='text-[12px] leading-[120%] font-bold lg:text-[16px] hover:text-primary flex gap-2 items-center'><ArrowLeft />Back to Dashboard</Link>
<h1 className="text-[24px] leading-[120%] font-bold lg:text-[48px]">Forgot Password</h1> <h1 className="text-[24px] leading-[120%] font-bold lg:text-[48px]">Forgot Password</h1>
</div> </div>
<ResetPasswordForm />
<form action="" onSubmit={handleSubmit}>
<div className="grid md:grid-cols-2 gap-x-3 gap-y-5">
<div className="col-span-2">
<div className="input_field">
<InputLabel htmlFor="emailAddress">Email Address*</InputLabel>
<OutlinedInput
name='emailAddress'
id='emailAddress'
placeholder='example@example.com'
value={values.emailAddress}
onChange={handleChange}
onBlur={handleBlur}
error={Boolean(touched.emailAddress && errors.emailAddress)}
/>
{
touched.emailAddress && errors.emailAddress ?
<span className="error ">{errors.emailAddress}</span> : null
}
</div>
</div>
<div className="col-span-2">
<div className="input_field">
<PasswordField
name="password"
label="Password*"
placeholder="XXXXXXX"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
error={touched.password ? errors.password : undefined}
/>
</div>
</div>
<div className="col-span-2">
<div className="input_field">
<PasswordField
name="confirmPassword"
label="Confirm Password*"
placeholder="XXXXXXX"
value={values.confirmPassword}
onChange={handleChange}
onBlur={handleBlur}
error={touched.confirmPassword ? errors.confirmPassword : undefined}
/>
</div>
</div>
</div>
<div className="action__group text-center flex flex-col gap-4 mt-8">
<button className='ss-btn bg-primary-grad' disabled={!dirty}>{isLoading ? "Changing Password" : "Reset Password"}</button>
<Link href={PATH.DASHBOARD.ROOT} className='ss-btn bg-secondary-grad'>Login</Link>
</div>
</form>
</Box> </Box>
</> </>
) )
......
...@@ -7,11 +7,11 @@ export default function ActivityLogPage() { ...@@ -7,11 +7,11 @@ export default function ActivityLogPage() {
return ( return (
<section className="activity__log__root"> <section className="activity__log__root">
<PageHeader title='Activity Log' /> <PageHeader title='Activity Log' />
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-4 mb-8"> {/* <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-4 mb-8">
{Array.from({ length: 4 }).map((_, index) => ( {Array.from({ length: 4 }).map((_, index) => (
<ActivityAnalyticCard key={index.toString()} /> <ActivityAnalyticCard key={index.toString()} />
))} ))}
</div> </div> */}
<div className="section-title mb-4"> <div className="section-title mb-4">
<h2 className="text-[20px] leading-[140%] font-[600]"> <h2 className="text-[20px] leading-[140%] font-[600]">
All Activities All Activities
......
...@@ -4,12 +4,12 @@ import { useEffect } from "react"; ...@@ -4,12 +4,12 @@ import { useEffect } from "react";
import { useAppDispatch } from "@/hooks/hook"; import { useAppDispatch } from "@/hooks/hook";
import { setTokens } from "@/slice/authSlice"; import { setTokens } from "@/slice/authSlice";
export default function ReduxHydrator({ token, user }: { token: string; user: any }) { export default function ReduxHydrator({ token }: { token: string; }) {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
useEffect(() => { // useEffect(() => {
dispatch(setTokens({ access_token: token, user })); // dispatch(setTokens({ access_token: token, user }));
}, [dispatch, token, user]); // }, [dispatch, token, user]);
return null; return null;
} }
...@@ -24,13 +24,13 @@ export default async function ServerPrivate({ children }: { children: React.Reac ...@@ -24,13 +24,13 @@ export default async function ServerPrivate({ children }: { children: React.Reac
redirect("/"); redirect("/");
} }
// Optionally, you could fetch user data from your API here if you don't store it in JWT
const user = payload; // Or fetch user profile based on token // const user = payload;
return ( return (
<> <>
{/* ✅ Hydrate Redux store on client */} {/* ✅ Hydrate Redux store on client */}
<ReduxHydrator token={access_token} user={user} /> <ReduxHydrator token={access_token} />
{children} {children}
</> </>
); );
......
import { createApi } from "@reduxjs/toolkit/query/react"; import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./baseQuery"; import { baseQuery } from "./baseQuery";
import { NotificationResponse } from "@/types/notification"; import { ActivityResponse, NotificationResponse } from "@/types/notification";
import { GlobalResponse, QueryParams } from "@/types/config"; import { GlobalResponse, QueryParams } from "@/types/config";
export const notificationApi = createApi({ export const notificationApi = createApi({
...@@ -36,13 +36,14 @@ export const notificationApi = createApi({ ...@@ -36,13 +36,14 @@ export const notificationApi = createApi({
}), }),
invalidatesTags: ["Notification"] invalidatesTags: ["Notification"]
}), }),
getAllActivity: builder.query<NotificationResponse, { activity_type: string } & QueryParams>({ getAllActivity: builder.query<ActivityResponse, { activity_type: string, status?: string } & QueryParams>({
query: ({ search, page, per_page, activity_type }) => { query: ({ search, page, per_page, activity_type, status }) => {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (search) params.append('search', search); if (search) params.append('search', search);
if (page) params.append('page', page.toString()); if (page) params.append('page', page.toString());
if (per_page) params.append('page_size', per_page.toString()); if (per_page) params.append('page_size', per_page.toString());
if (per_page) params.append('activity_type', activity_type.toString()); if (activity_type) params.append('activity_type', activity_type.toString());
if (status) params.append('status', status.toString());
const queryString = params.toString(); const queryString = params.toString();
return { return {
url: `/api/admin/activity${queryString ? `?${queryString}` : ''}`, url: `/api/admin/activity${queryString ? `?${queryString}` : ''}`,
......
...@@ -20,7 +20,18 @@ export interface ActivityResponse { ...@@ -20,7 +20,18 @@ export interface ActivityResponse {
success: boolean; success: boolean;
message: string; message: string;
data: { data: {
data: {}[]; data: ActivityProps[];
pagination: Pagination; pagination: Pagination;
} }
} }
export interface ActivityProps {
id: number,
username: string,
email: string,
status: string,
log: string
type: string,
timestamp: string;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment