diff --git a/src/auth/RequireAuth.tsx b/src/auth/RequireAuth.tsx index 835bd74..6b7cb66 100644 --- a/src/auth/RequireAuth.tsx +++ b/src/auth/RequireAuth.tsx @@ -8,6 +8,8 @@ const RequireAuth = () => { const { auth, loading } = useAuthContext(); const location = useLocation(); + // console.log('RequireAuth render: loading =', loading, ' auth =', auth); + if (loading) { return ; } @@ -15,4 +17,4 @@ const RequireAuth = () => { return auth ? : ; }; -export { RequireAuth }; +export { RequireAuth }; \ No newline at end of file diff --git a/src/auth/_helpers.ts b/src/auth/_helpers.ts index 916d562..ecb6bf9 100644 --- a/src/auth/_helpers.ts +++ b/src/auth/_helpers.ts @@ -41,9 +41,9 @@ export function setupAxios(axios: any) { (config: { headers: { Authorization: string }; params?: any; url?: any; httpsAgent: any }) => { const auth = getAuth(); - if (auth?.access_token) { - config.headers.Authorization = `Bearer ${auth.access_token}`; - } + // if (auth?.access_token) { + // config.headers.Authorization = `Bearer ${auth.access_token}`; + // } return config; }, async (err: any) => await Promise.reject(err) diff --git a/src/auth/_models.ts b/src/auth/_models.ts index 8bf87ed..cf1d2a1 100644 --- a/src/auth/_models.ts +++ b/src/auth/_models.ts @@ -2,14 +2,21 @@ import { type TLanguageCode } from '@/i18n'; export interface AuthModel { id: string; - access_token: string; - refresh_token: string; + // access_token: string; + // refresh_token: string; role_name: string; - roles_list: []; - company: any; - user: any; - // api_token: string; - statusbalance: string; + // roles_list: []; + // company: any; + // user: any; + // // api_token: string; + // statusbalance: string; + username?: string; + password?: string; + user?: UserModel; + statusbalance?: string; + access_token?: string; + token_type?: string; + // language?: TLanguageCode; } export interface UserModel { @@ -18,18 +25,21 @@ export interface UserModel { name: string; password: string | undefined; email: string; - id_role: string; + customer?: { + id: string; + }; + // id_role: string; role_name: string; - roles_list: []; - token: AuthModel; + // roles_list: []; + // token: AuthModel; - first_name: string; - last_name: string; - fullname?: string; - occupation?: string; - companyName?: string; - phone?: string; - roles?: number[]; - pic?: string; - language?: TLanguageCode; + // first_name: string; + // last_name: string; + // fullname?: string; + // occupation?: string; + // companyName?: string; + // phone?: string; + // roles?: number[]; + // pic?: string; + // language?: TLanguageCode; } diff --git a/src/auth/pages/jwt/Login.tsx b/src/auth/pages/jwt/Login.tsx index ebe2bac..cb4e002 100644 --- a/src/auth/pages/jwt/Login.tsx +++ b/src/auth/pages/jwt/Login.tsx @@ -1,84 +1,81 @@ import { type MouseEvent, useState } from 'react'; -import { Link, useLocation, useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import clsx from 'clsx'; import * as Yup from 'yup'; import { useFormik } from 'formik'; import { KeenIcon } from '@/components'; -import { toAbsoluteUrl } from '@/utils'; -import { useAuthContext } from '@/auth'; -import { useLayout } from '@/providers'; import { Alert } from '@/components'; +import { useAuthContext } from '@/auth'; import moment from 'moment'; import { Helmet } from 'react-helmet'; +// --------------------------------------------------------------------------- +// Hardcoded credentials (no API call). The actual session (auth/currentUser) +// is still set through AuthContext's login(), which — while its provider is +// in DUMMY_MODE — stores a local dummy session instead of hitting the API. +// --------------------------------------------------------------------------- +const HARDCODED_USERNAME = 'admin'; +const HARDCODED_PASSWORD = 'admin'; + const loginSchema = Yup.object().shape({ username: Yup.string().required('Username is required'), password: Yup.string() .min(3, 'Minimum 3 symbols') .max(50, 'Maximum 50 symbols') - .required('Password is required'), - token: Yup.string(), - // .max(3, 'Maximum 6 code'), - // .required('Token is required'), - remember: Yup.boolean() + .required('Password is required') }); -// const initialValues = { -// username: '', -// password: '', -// token: '', -// remember: false -// }; +const initialValues = { + username: '', + password: '' +}; const Login = () => { const [loading, setLoading] = useState(false); const { login } = useAuthContext(); const navigate = useNavigate(); const location = useLocation(); - const from = location.state?.from?.pathname || '/'; + const from = (location.state as any)?.from?.pathname || '/'; const [showPassword, setShowPassword] = useState(false); - const { currentLayout } = useLayout(); - // const formik = useFormik({ - // initialValues, - // validationSchema: loginSchema, - // onSubmit: async (values, { setStatus, setSubmitting }) => { - // setLoading(true); + const formik = useFormik({ + initialValues, + validationSchema: loginSchema, + onSubmit: async (values, { setStatus, setSubmitting }) => { + setLoading(true); + setStatus(undefined); - // try { - // if (!login) throw new Error('JWTProvider is required for this form.'); - // await login(values.username, values.password, values.token); + if (values.username !== HARDCODED_USERNAME || values.password !== HARDCODED_PASSWORD) { + setStatus('Incorrect username or password. Please try again.'); + setSubmitting(false); + setLoading(false); + return; + } - // if (values.remember) { - // localStorage.setItem('username', values.username); - // } else { - // localStorage.removeItem('username'); - // } + try { + if (!login) throw new Error('AuthProvider is required for this form.'); + await login(values.username, values.password); + navigate(from, { replace: true }); + } catch (error) { + console.error('Login error:', error); + setStatus('Something went wrong while signing in. Please try again.'); + setSubmitting(false); + } - // navigate(from, { replace: true }); - // } catch (error: any) { - // if (error.response && error.response.data) { - // setStatus(error.response.data.message); - // } else { - // setStatus('The login details are incorrect'); - // } + setLoading(false); + } + }); - // setSubmitting(false); - // } - // setLoading(false); - // } - // }); + const togglePassword = (event: MouseEvent) => { + event.preventDefault(); + setShowPassword(!showPassword); + }; - // const togglePassword = (event: MouseEvent) => { - // event.preventDefault(); - // setShowPassword(!showPassword); - // }; - - // const handleKeyPress = (e: { key: string }) => { - // if (e.key === 'Enter') { - // formik.handleSubmit(); - // } - // }; + const handleKeyPress = (e: { key: string }) => { + if (e.key === 'Enter') { + formik.handleSubmit(); + } + }; return ( <> @@ -86,33 +83,29 @@ const Login = () => { REVENUE | Sign In - + Sign in - {/* {formik.status && {formik.status}} */} + {formik.status && {formik.status}} Login - {/* {formik.touched.username && formik.errors.username && ( + {formik.touched.username && formik.errors.username && ( {formik.errors.username} - )} */} + )} @@ -124,15 +117,13 @@ const Login = () => { type={showPassword ? 'text' : 'password'} placeholder="Enter Password" autoComplete="off" - // {...formik.getFieldProps('password')} - // className={clsx('form-control', { - // 'is-invalid': formik.touched.password && formik.errors.password - // })} - // onKeyPress={handleKeyPress} + {...formik.getFieldProps('password')} + className={clsx('form-control', { + 'is-invalid': formik.touched.password && formik.errors.password + })} + onKeyPress={handleKeyPress} /> - + { /> - {/* {formik.touched.password && formik.errors.password && ( + {formik.touched.password && formik.errors.password && ( {formik.errors.password} - )} */} + )} {loading ? 'Please wait...' : 'Sign In'} @@ -164,4 +155,4 @@ const Login = () => { ); }; -export { Login }; +export { Login }; \ No newline at end of file diff --git a/src/auth/providers/JWTProvider.tsx b/src/auth/providers/JWTProvider.tsx index b580279..95a76cd 100644 --- a/src/auth/providers/JWTProvider.tsx +++ b/src/auth/providers/JWTProvider.tsx @@ -21,6 +21,34 @@ export const FORGOT_PASSWORD_URL = `${API_URL}/reset_password`; export const RESET_PASSWORD_URL = `${API_URL}/update_password`; export const GET_USER_URL = `${API_URL}/user/detail`; +// --------------------------------------------------------------------------- +// DUMMY MODE — set to `false` to restore the real API-backed auth flow. +// While `true`, every method below skips its axios call and works off a +// local, hardcoded session instead. This keeps the rest of the app (route +// guards, `getAuth()`, etc.) working without a live backend. +// --------------------------------------------------------------------------- +const DUMMY_MODE = true; + +const DUMMY_USER: UserModel = { + id: 1, + name: 'Administrator', + username: 'admin', + email: 'admin@example.com', + password: undefined, + customer: { id: 'dummy-customer-id' }, + role_name: 'Admin' +}; + +const DUMMY_AUTH: AuthModel = { + id: 'dummy-user-id', + username: 'admin', + role_name: 'Admin', + user: DUMMY_USER, + statusbalance: 'Y', + access_token: 'dummy-token', + token_type: 'Bearer' +}; + interface AuthContextProps { loading: boolean; setLoading: Dispatch>; @@ -53,6 +81,13 @@ const AuthProvider = ({ children }: PropsWithChildren) => { }; const login = async (username: string, password: string, token?: string) => { + if (DUMMY_MODE) { + // No API call — just persist a local dummy session. + saveAuth(DUMMY_AUTH); + setCurrentUser(DUMMY_USER); + return; + } + try { const { data: auth } = await axios .post(LOGIN_URL, { username, password, token }) @@ -82,10 +117,18 @@ const AuthProvider = ({ children }: PropsWithChildren) => { }; const requestPasswordResetLink = async (email: string) => { + if (DUMMY_MODE) { + console.warn('[AuthProvider] DUMMY_MODE: requestPasswordResetLink skipped (no API call).'); + return; + } await axios.put(FORGOT_PASSWORD_URL + '/' + email); }; const changePassword = async (token: string, password: string, retype_password: string) => { + if (DUMMY_MODE) { + console.warn('[AuthProvider] DUMMY_MODE: changePassword skipped (no API call).'); + return; + } await axios.put(`${RESET_PASSWORD_URL}/${token}`, { password, retype_password @@ -93,6 +136,10 @@ const AuthProvider = ({ children }: PropsWithChildren) => { }; const getUser = async () => { + if (DUMMY_MODE) { + return { data: DUMMY_USER }; + } + let _axios = await axios .get(`${GET_USER_URL}/${authHelper.getAuth()?.id}`) .then((response) => response.data.data); @@ -101,6 +148,13 @@ const AuthProvider = ({ children }: PropsWithChildren) => { }; const verify = async () => { + if (DUMMY_MODE) { + // Keep whatever session is already in memory/local storage as-is; + // no API round-trip to re-validate it. + setLoading(false); + return; + } + if (auth) { try { const { data: user } = await getUser(); @@ -127,7 +181,20 @@ const AuthProvider = ({ children }: PropsWithChildren) => { } }; + // Ensures `loading` always resolves, even if no external wrapper calls + // verify() on app start. Safe to call twice if such a wrapper does exist. + useEffect(() => { + verify(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const logout = async () => { + if (DUMMY_MODE) { + saveAuth(undefined); + setCurrentUser(undefined); + return; + } + const createActivity = { module: 'Logout', description: `Logout`, @@ -161,4 +228,4 @@ const AuthProvider = ({ children }: PropsWithChildren) => { ); }; -export { AuthContext, AuthProvider }; +export { AuthContext, AuthProvider }; \ No newline at end of file diff --git a/src/routing/AppRoutingSetup.tsx b/src/routing/AppRoutingSetup.tsx index 2300a7f..93ae249 100644 --- a/src/routing/AppRoutingSetup.tsx +++ b/src/routing/AppRoutingSetup.tsx @@ -59,7 +59,7 @@ import ComingSoonPage from '@/pages/ComingSoonPage'; const AppRoutingSetup = (): ReactElement => { return ( - {/* }> */} + }> }> } /> } /> @@ -142,7 +142,7 @@ const AppRoutingSetup = (): ReactElement => { /> */} {/* DISBURSEMENT */} - {/* */} + } /> } /> } />