update, consume for login done

This commit is contained in:
wayanrivan
2026-07-07 11:34:38 +07:00
parent 0ec6ef1841
commit 8a7dfc2c86
8 changed files with 59 additions and 139 deletions

View File

@ -4,7 +4,8 @@ GENERATE_SOURCEMAP=false
# VITE_APP_API_URL= # VITE_APP_API_URL=
# VITE_APP_API_URL=http://127.0.0.1:4003/apitest # VITE_APP_API_URL=http://127.0.0.1:4003/apitest
VITE_APP_API_URL=https://tpay.shiblysolution.id/api # VITE_APP_API_URL=https://tpay.shiblysolution.id/api
VITE_APP_API_URL=http://127.0.0.1:7001/api
# VITE_APP_API_URL=http://api-v2.tpay.tl/api # VITE_APP_API_URL=http://api-v2.tpay.tl/api
# VITE_APP_URL_TRANSACTION=https://tpay.shiblysolution.id/test/x/api # VITE_APP_URL_TRANSACTION=https://tpay.shiblysolution.id/test/x/api
# VITE_APP_API_URL=http://127.0.0.1:4003/apitesting # VITE_APP_API_URL=http://127.0.0.1:4003/apitesting

View File

@ -2,7 +2,7 @@ VITE_APP_NAME=tpay-dashboard-tl
VITE_APP_VERSION=1=9.1.1 VITE_APP_VERSION=1=9.1.1
GENERATE_SOURCEMAP=false GENERATE_SOURCEMAP=false
VITE_APP_API_URL=https://tpay.shiblysolution.id/api VITE_APP_API_URL=http://127.0.0.1:7001/api
# VITE_APP_API_URL=http://api-v2.tpay.tl/api # VITE_APP_API_URL=http://api-v2.tpay.tl/api
VITE_BASE_URL=/dashboard/ VITE_BASE_URL=/dashboard/
VITE_ENV=staging VITE_ENV=staging

View File

@ -41,9 +41,9 @@ export function setupAxios(axios: any) {
(config: { headers: { Authorization: string }; params?: any; url?: any; httpsAgent: any }) => { (config: { headers: { Authorization: string }; params?: any; url?: any; httpsAgent: any }) => {
const auth = getAuth(); const auth = getAuth();
// if (auth?.access_token) { if (auth?.access_token) {
// config.headers.Authorization = `Bearer ${auth.access_token}`; config.headers.Authorization = `Bearer ${auth.access_token}`;
// } }
return config; return config;
}, },
async (err: any) => await Promise.reject(err) async (err: any) => await Promise.reject(err)

View File

@ -1,33 +1,33 @@
import { type MouseEvent, useState } from 'react'; import { type MouseEvent, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom'; import { Link, useLocation, useNavigate } from 'react-router-dom';
import clsx from 'clsx'; import clsx from 'clsx';
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { KeenIcon } from '@/components'; import { KeenIcon } from '@/components';
import { Alert } from '@/components'; import { toAbsoluteUrl } from '@/utils';
import { useAuthContext } from '@/auth'; import { useAuthContext } from '@/auth';
import { useLayout } from '@/providers';
import { Alert } from '@/components';
import moment from 'moment'; import moment from 'moment';
import { Helmet } from 'react-helmet'; 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({ const loginSchema = Yup.object().shape({
username: Yup.string().required('Username is required'), username: Yup.string().required('Username is required'),
password: Yup.string() password: Yup.string()
.min(3, 'Minimum 3 symbols') .min(3, 'Minimum 3 symbols')
.max(50, 'Maximum 50 symbols') .max(50, 'Maximum 50 symbols')
.required('Password is required') .required('Password is required'),
token: Yup.string(),
// .max(3, 'Maximum 6 code'),
// .required('Token is required'),
remember: Yup.boolean()
}); });
const initialValues = { const initialValues = {
username: '', username: '',
password: '' password: '',
token: '',
remember: false
}; };
const Login = () => { const Login = () => {
@ -35,33 +35,36 @@ const Login = () => {
const { login } = useAuthContext(); const { login } = useAuthContext();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const from = (location.state as any)?.from?.pathname || '/'; const from = location.state?.from?.pathname || '/';
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const { currentLayout } = useLayout();
const formik = useFormik({ const formik = useFormik({
initialValues, initialValues,
validationSchema: loginSchema, validationSchema: loginSchema,
onSubmit: async (values, { setStatus, setSubmitting }) => { onSubmit: async (values, { setStatus, setSubmitting }) => {
setLoading(true); setLoading(true);
setStatus(undefined);
if (values.username !== HARDCODED_USERNAME || values.password !== HARDCODED_PASSWORD) {
setStatus('Incorrect username or password. Please try again.');
setSubmitting(false);
setLoading(false);
return;
}
try { try {
if (!login) throw new Error('AuthProvider is required for this form.'); if (!login) throw new Error('JWTProvider is required for this form.');
await login(values.username, values.password); await login(values.username, values.password, values.token);
if (values.remember) {
localStorage.setItem('username', values.username);
} else {
localStorage.removeItem('username');
}
navigate(from, { replace: true }); navigate(from, { replace: true });
} catch (error) { } catch (error: any) {
console.error('Login error:', error); if (error.response && error.response.data) {
setStatus('Something went wrong while signing in. Please try again.'); setStatus(error.response.data.message);
} else {
setStatus('The login details are incorrect');
}
setSubmitting(false); setSubmitting(false);
} }
setLoading(false); setLoading(false);
} }
}); });
@ -80,10 +83,14 @@ const Login = () => {
return ( return (
<> <>
<Helmet> <Helmet>
<title>REVENUE | Sign In</title> <title>TPAY | Sign In</title>
</Helmet> </Helmet>
<div className="card max-w-[390px] w-full"> <div className="card max-w-[390px] w-full">
<form className="card-body flex flex-col gap-5 p-10" onSubmit={formik.handleSubmit} noValidate> <form
className="card-body flex flex-col gap-5 p-10"
onSubmit={formik.handleSubmit}
noValidate
>
<div className="text-center"> <div className="text-center">
<h3 className="text-lg font-semibold text-gray-900 leading-none ">Sign in</h3> <h3 className="text-lg font-semibold text-gray-900 leading-none ">Sign in</h3>
</div> </div>
@ -137,6 +144,7 @@ const Login = () => {
</span> </span>
)} )}
</div> </div>
<button <button
type="submit" type="submit"
className="btn btn-primary flex justify-center grow" className="btn btn-primary flex justify-center grow"
@ -155,4 +163,4 @@ const Login = () => {
); );
}; };
export { Login }; export { Login };

View File

@ -17,37 +17,7 @@ import { doSaveLogActivity } from '@/actions/GlobalActions';
const API_URL = apiConfig.service_dashboard; const API_URL = apiConfig.service_dashboard;
export const LOGIN_URL = `${API_URL}/login`; export const LOGIN_URL = `${API_URL}/login`;
export const FORGOT_PASSWORD_URL = `${API_URL}/reset_password`; export const GET_USER_URL = `${API_URL}/user/profile`;
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 { interface AuthContextProps {
loading: boolean; loading: boolean;
@ -57,9 +27,6 @@ interface AuthContextProps {
currentUser: UserModel | undefined; currentUser: UserModel | undefined;
setCurrentUser: Dispatch<SetStateAction<UserModel | undefined>>; setCurrentUser: Dispatch<SetStateAction<UserModel | undefined>>;
login: (email: string, password: string, token?: string) => Promise<void>; login: (email: string, password: string, token?: string) => Promise<void>;
requestPasswordResetLink: (email: string) => Promise<void>;
changePassword: (token: string, password: string, password_confirmation: string) => Promise<void>;
getUser: () => Promise<AxiosResponse<any> | {}>;
logout: () => void; logout: () => void;
verify: () => Promise<void>; verify: () => Promise<void>;
} }
@ -81,25 +48,16 @@ const AuthProvider = ({ children }: PropsWithChildren) => {
}; };
const login = async (username: string, password: string, token?: string) => { 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 { try {
const { data: auth } = await axios const { data: auth } = await axios
.post(LOGIN_URL, { username, password, token }) .post(LOGIN_URL, { username, password })
.then((response) => response.data); .then((response) => response.data);
const enhancedAuth: AuthModel = { const enhancedAuth: AuthModel = {
...auth.token, ...auth.token,
id: auth.user.id, id: auth.userid,
role_name: auth.role_name,
user: auth.user, user: auth.user,
statusbalance: auth.role?.status_balance ?? null // SAFE ACCESS };
};
saveAuth(enhancedAuth); saveAuth(enhancedAuth);
setCurrentUser(auth.user); setCurrentUser(auth.user);
@ -116,45 +74,15 @@ 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
});
};
const getUser = async () => { const getUser = async () => {
if (DUMMY_MODE) {
return { data: DUMMY_USER };
}
let _axios = await axios let _axios = await axios
.get(`${GET_USER_URL}/${authHelper.getAuth()?.id}`) .get(`${GET_USER_URL}/${authHelper.getAuth()?.user?.id}`)
.then((response) => response.data.data); .then((response) => response.data.data);
return { data: _axios }; return { data: _axios };
}; };
const verify = async () => { 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) { if (auth) {
try { try {
const { data: user } = await getUser(); const { data: user } = await getUser();
@ -162,15 +90,12 @@ const AuthProvider = ({ children }: PropsWithChildren) => {
// Perbarui auth yang sekarang dengan statusbalance // Perbarui auth yang sekarang dengan statusbalance
saveAuth({ saveAuth({
...auth, ...auth,
statusbalance: user.role.status_balance
}); });
const createCacheUser = { const createCacheUser = {
name: user.name, name: user.name,
email: user.email, email: user.email,
username: user.username, username: user.username,
role_name: auth.role_name,
statusbalance: user.role.status_balance
}; };
localStorage.setItem('user', JSON.stringify(createCacheUser)); localStorage.setItem('user', JSON.stringify(createCacheUser));
@ -181,20 +106,7 @@ 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 () => { const logout = async () => {
if (DUMMY_MODE) {
saveAuth(undefined);
setCurrentUser(undefined);
return;
}
const createActivity = { const createActivity = {
module: 'Logout', module: 'Logout',
description: `Logout`, description: `Logout`,
@ -216,9 +128,6 @@ const AuthProvider = ({ children }: PropsWithChildren) => {
currentUser, currentUser,
setCurrentUser, setCurrentUser,
login, login,
requestPasswordResetLink,
changePassword,
getUser,
logout, logout,
verify verify
}} }}
@ -228,4 +137,4 @@ const AuthProvider = ({ children }: PropsWithChildren) => {
); );
}; };
export { AuthContext, AuthProvider }; export { AuthContext, AuthProvider };

View File

@ -19,7 +19,7 @@ const API_URL = import.meta.env.VITE_APP_API_URL;
const apiConfig: apiConfigProps = { const apiConfig: apiConfigProps = {
service_feedback: `${API_URL}/k`, service_feedback: `${API_URL}/k`,
// service_dashboard: `${API_URL}${import.meta.env.VITE_ENV != 'development' ? '/d' : ''}`, // service_dashboard: `${API_URL}${import.meta.env.VITE_ENV != 'development' ? '/d' : ''}`,
service_dashboard: `${API_URL}/d`, service_dashboard: `${API_URL}`,
service_customer: `${API_URL}/c`, service_customer: `${API_URL}/c`,
// service_master_data2: `${API_URL}/m`, // service_master_data2: `${API_URL}/m`,
service_master_data: `${API_URL}/t`, service_master_data: `${API_URL}/t`,

View File

@ -11,6 +11,8 @@ const HeaderTopbar = () => {
const itemUserRef = useRef<any>(null); const itemUserRef = useRef<any>(null);
const itemNotificationsRef = useRef<any>(null); const itemNotificationsRef = useRef<any>(null);
console.log('HeaderTopbar getAuth():', getAuth());
const { isRTL } = useLanguage(); const { isRTL } = useLanguage();
const [isSticky, setIsSticky] = useState(false); const [isSticky, setIsSticky] = useState(false);
@ -32,11 +34,11 @@ const HeaderTopbar = () => {
<span <span
className={`font-semibold text-lg leading-tight ${isSticky ? 'text-black' : 'text-white'}`} className={`font-semibold text-lg leading-tight ${isSticky ? 'text-black' : 'text-white'}`}
> >
{getAuth()?.user.name} {getAuth()?.user?.name}
</span>
<span className={`text-xs ${isSticky ? 'text-gray-700' : 'text-white'}`}>
{getAuth()?.user?.email}
</span> </span>
{/* <span className={`text-xs ${isSticky ? 'text-gray-700' : 'text-white'}`}>
as {getAuth()?.role_name}
</span> */}
</div> </div>
<Menu className="w-12 h-12"> <Menu className="w-12 h-12">

View File

@ -45,9 +45,9 @@ const DropdownUser = ({ menuItemRef }: IDropdownUserProps) => {
alt="" alt=""
/> */} /> */}
<div className="flex flex-col"> <div className="flex flex-col">
<p className="text-[14px] text-gray-800 font-semibold">{auth?.user.username}</p> <p className="text-[14px] text-gray-800 font-semibold">{auth?.user?.username}</p>
<p className="text-xs text-gray-600 hover:text-primary font-medium leading-none"> <p className="text-xs text-gray-600 hover:text-primary font-medium leading-none">
{auth?.user.email} {auth?.user?.email}
</p> </p>
</div> </div>
</div> </div>