Merge remote-tracking branch 'origin/staging' into origin/production
This commit is contained in:
@@ -115,13 +115,12 @@ export default function List() {
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleImportButton = () => {
|
||||
const [paramImport, setParamImport] = useState('');
|
||||
const handleImportButton = (param:any) => {
|
||||
setParamImport(param);
|
||||
if (importForm?.current) {
|
||||
handleClose();
|
||||
importForm.current ? importForm.current.click() : console.log('No File selected');
|
||||
} else {
|
||||
alert('No file selected');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -144,12 +143,18 @@ export default function List() {
|
||||
formData.append('file', importForm.current?.files[0]);
|
||||
|
||||
setImportLoading(true);
|
||||
let url = 'claim-requests/import';
|
||||
if(paramImport == 'invoice')
|
||||
{
|
||||
url = 'customer-service/request/import-invoice'
|
||||
}
|
||||
axios
|
||||
.post(`claim-requests/import`, formData)
|
||||
.post(`${url}`, formData)
|
||||
.then((response) => {
|
||||
handleCancelImportButton();
|
||||
loadDataTableData();
|
||||
// loadDataTableData();
|
||||
setImportResult(response.data);
|
||||
setParamImport(response.data.metaData);
|
||||
// alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows');
|
||||
setImportLoading(false);
|
||||
})
|
||||
@@ -166,6 +171,28 @@ export default function List() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleExportReportFiled = async () => {
|
||||
|
||||
await axios
|
||||
.post('customer-service/request/exportFiledInvoice', { params: importResult?.data.result_rows })
|
||||
.then((res) => {
|
||||
enqueueSnackbar('Data berhasil di Export', {
|
||||
variant: 'success',
|
||||
anchorOrigin: { horizontal: 'right', vertical: 'top' },
|
||||
});
|
||||
setImportLoading(false);
|
||||
|
||||
document.location.href = res.data.data.file_url;
|
||||
})
|
||||
.catch((err) =>
|
||||
enqueueSnackbar('Data Gagal di Export', {
|
||||
variant: 'error',
|
||||
anchorOrigin: { horizontal: 'right', vertical: 'top' },
|
||||
})
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
const handleGetTemplate = (type :string) => {
|
||||
axios.get('corporates/import-document-example/' + type)
|
||||
.then((response) => {
|
||||
@@ -220,9 +247,11 @@ export default function List() {
|
||||
'aria-labelledby': 'basic-button',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleImportButton}>Import</MenuItem>
|
||||
<MenuItem onClick={() => {handleImportButton('claim')}}>Import</MenuItem>
|
||||
<MenuItem onClick={() => {handleGetTemplate('claim-request')}}>Download Template</MenuItem>
|
||||
<MenuItem onClick={() => {handleGetData('data-plan-benefit')}}>Download Claim Request</MenuItem>
|
||||
<MenuItem onClick={() => {handleImportButton('invoice')}}>Import Invoice</MenuItem>
|
||||
<MenuItem onClick={() => {handleGetTemplate('final-log-invoice')}}>Download Template Invoice</MenuItem>
|
||||
</Menu>
|
||||
{/* <Button
|
||||
variant="contained"
|
||||
@@ -265,16 +294,35 @@ export default function List() {
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
)}
|
||||
{importResult && (
|
||||
{importResult && importResult?.metaData == 'invoice' ? (
|
||||
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
|
||||
<Box sx={{ color: 'text.secondary' }}>
|
||||
Last Import Result :{' '}
|
||||
<Box sx={{ color: 'success.main', display: 'inline' }}>
|
||||
{importResult.data.total_success_row ?? 0}
|
||||
</Box>{' '}
|
||||
Row Processed,{' '}
|
||||
<Box sx={{ color: 'error.main', display: 'inline' }}>
|
||||
{importResult.data.total_failed_row}
|
||||
</Box>{' '}
|
||||
Failed,
|
||||
{/* {importResult.data.failed_rows.map((row, index) => (
|
||||
<Typography variant='body' key={index} color="error"> [Code={row.code ? row.code : 'Required'}]</Typography>
|
||||
))} */}
|
||||
Report:
|
||||
<u onClick={handleExportReportFiled} style={{cursor:'pointer'}}>Download Data Result Import</u>
|
||||
</Box>
|
||||
</Stack>
|
||||
) : importResult ? (
|
||||
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
|
||||
<Box sx={{ color: 'text.secondary' }}>
|
||||
Last Import Result Report :{' '}
|
||||
<a href={importResult.result_file?.url ?? '#'}>
|
||||
{importResult.result_file?.name ?? '-'}
|
||||
Last Import Result Report {paramImport} :{' '}
|
||||
<a href={importResult?.result_file?.url ?? '#'}>
|
||||
{importResult?.result_file?.name ?? '-'}
|
||||
</a>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
):''}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"txtDialogMember3" : "Detail",
|
||||
"txtDialogMember4" : "Please select services",
|
||||
"txtDialogMember5" : "Admission Date",
|
||||
"txtDialogMember6" : "Please select admission date",
|
||||
"txtDialogMember6" : "Please select admission date",
|
||||
"txtWarningDischargeDate" : "Please select discharge date",
|
||||
"txtCreateAt" : "Create at",
|
||||
"txtDateBirth" : "Date of Birth",
|
||||
@@ -59,5 +59,24 @@
|
||||
"txtApprove": "Approve",
|
||||
"txtDialogConfirmation": "Are you sure you want to proceed with this action?",
|
||||
"txtStartDate": "Start Date",
|
||||
"txtEndDate": "End Date"
|
||||
"txtEndDate": "End Date",
|
||||
"txtHelp1" : "Has problem with your account?",
|
||||
"txtLupaSandi": "Forgot password?",
|
||||
"txtIngatkanSaya": "Remember me",
|
||||
"txtLogin": "Login",
|
||||
"txtForgotYourPassword": "Forgot your password?",
|
||||
"txtPleaseEnterPassword": "Please enter the email address associated with your account and We will email you a code to reset your password.",
|
||||
"txtBack": "Back",
|
||||
"txtSuccessSend": "Request sent successfully",
|
||||
"txtCodeConfirm": "We have sent a confirmation email to",
|
||||
"txtPleasCheck": "Please check your email.",
|
||||
"txtCheckEmail": "Please check your email!",
|
||||
"txtEmail": "We have emailed a 6-digit confirmation code and check spam folder, please enter the code in below box to verify your email.",
|
||||
"txtDont": "Don’t have a code?",
|
||||
"txtResendCode": "Resend code",
|
||||
"txtSecond": "Second",
|
||||
"txtPleaseInput": "Please enter your new password.",
|
||||
"txtNewPassword": "New Password",
|
||||
"txtConfPassword": "Confirm Kata Sandi"
|
||||
|
||||
}
|
||||
|
||||
@@ -59,5 +59,23 @@
|
||||
"txtApprove": "Terima",
|
||||
"txtDialogConfirmation": "Apakah Anda yakin ingin melanjutkan tindakan ini?",
|
||||
"txtStartDate": "Tanggal Mulai",
|
||||
"txtEndDate": "Tanggal Akhir"
|
||||
"txtEndDate": "Tanggal Akhir",
|
||||
"txtHelp1" : "Punya masalah dengan akun Anda?",
|
||||
"txtLupaSandi": "Lupa sandi?",
|
||||
"txtIngatkanSaya": "Ingatkan saya",
|
||||
"txtLogin": "Masuk",
|
||||
"txtForgotYourPassword": "Lupa password anda?",
|
||||
"txtPleaseEnterPassword": "Silakan masukkan alamat email yang terkait dengan akun Anda dan Kami akan mengirimkan email berisi kode untuk mengatur ulang kata sandi Anda.",
|
||||
"txtBack": "Kembali",
|
||||
"txtSuccessSend": "Permintaan berhasil dikirim",
|
||||
"txtCodeConfirm": "Kami telah mengirimkan email konfirmasi ke",
|
||||
"txtPleasCheck": "Mohon cek email Anda.",
|
||||
"txtCheckEmail": "Mohon periksa email Anda!",
|
||||
"txtEmail": "Kami telah mengirimkan kode konfirmasi 6 digit melalui email cek juga difolder spam, silakan masukkan kode di kotak bawah ini untuk memverifikasi email Anda.",
|
||||
"txtDont": "Tidak mendapatkan kode?",
|
||||
"txtResendCode": "Kirim ulang kode",
|
||||
"txtSecond": "Detik",
|
||||
"txtPleaseInput": "Mohon masukan kata sandi baru Anda.",
|
||||
"txtNewPassword": "Kata Sandi Baru",
|
||||
"txtConfPassword": "Konfirmasi Kata Sandi"
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export type JWTContextType = {
|
||||
isInitialized: boolean;
|
||||
user: AuthUser;
|
||||
method: 'jwt';
|
||||
login: (email: string, password: string) => Promise<void>;
|
||||
login: (email: string, password: string, rememberMe: boolean) => Promise<void>;
|
||||
register: (email: string, password: string, firstName: string, lastName: string) => Promise<void>;
|
||||
logout: () => Promise<void>;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
const getLocalizedData = async (locale) => {
|
||||
const response = await fetch(`/lang/${locale}.json`); // Mengambil file lokal berdasarkan bahasa yang dipilih
|
||||
const data = await response.json();
|
||||
return data;
|
||||
// const getLocalizedData = async (locale) => {
|
||||
// const response = await fetch(`/lang/${locale}.json`); // Mengambil file lokal berdasarkan bahasa yang dipilih
|
||||
// const data = await response.json();
|
||||
// return data;
|
||||
// };
|
||||
|
||||
// export default getLocalizedData;
|
||||
|
||||
// LocalizationUtil.js
|
||||
import idID from './lang/id-ID.json';
|
||||
import enUS from './lang/en-US.json';
|
||||
|
||||
const localizedData = {
|
||||
'id-ID': idID,
|
||||
'en-US': enUS,
|
||||
// Tambahkan bahasa lain sesuai kebutuhan
|
||||
};
|
||||
|
||||
export default getLocalizedData;
|
||||
const getLocalizedData = async (locale) => {
|
||||
return localizedData[locale] || localizedData['id-ID'];
|
||||
};
|
||||
|
||||
export default getLocalizedData;
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ export default function Table<T>({
|
||||
]);
|
||||
params.setAppliedParams(parameters);
|
||||
};
|
||||
|
||||
|
||||
const { localeData }: any = useContext(LanguageContext);
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
@@ -106,7 +106,7 @@ export default function Table<T>({
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{selected.useSelected && selected.selectedRows.length > 0 ? (
|
||||
{selected.useSelected && selected.selectedRows.length > 0 ? (
|
||||
<>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1', }} align="left" colSpan={selected.totRows} sx={{ padding: 2 }}>
|
||||
<Grid container alignItems="center" justifyContent="space-between">
|
||||
@@ -169,10 +169,10 @@ export default function Table<T>({
|
||||
</TableCell>
|
||||
))}
|
||||
</>
|
||||
|
||||
|
||||
)}
|
||||
|
||||
|
||||
|
||||
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
);
|
||||
@@ -294,7 +294,7 @@ export default function Table<T>({
|
||||
</form>
|
||||
</Grid>
|
||||
}
|
||||
|
||||
|
||||
</Fragment>
|
||||
) : null }
|
||||
|
||||
@@ -380,7 +380,7 @@ export default function Table<T>({
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
) : null }
|
||||
) : null }
|
||||
|
||||
{/* Export Report */}
|
||||
|
||||
@@ -389,11 +389,11 @@ export default function Table<T>({
|
||||
<FormControl fullWidth>
|
||||
<Button variant='contained' sx={{p:2}}>
|
||||
<Download />
|
||||
<Typography variant='inherit' sx={{marginLeft: 1}}>Export</Typography>
|
||||
<Typography variant='inherit' sx={{marginLeft: 1}}>Export</Typography>
|
||||
</Button>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
) : null }
|
||||
) : null }
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -428,7 +428,7 @@ export default function Table<T>({
|
||||
</TableCell>
|
||||
):(
|
||||
<TableCell>
|
||||
|
||||
|
||||
</TableCell>
|
||||
))}
|
||||
{headCells &&
|
||||
@@ -443,7 +443,7 @@ export default function Table<T>({
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} align="center">
|
||||
<TableCell colSpan={headCells?.length} align="center">
|
||||
{localeData.txtDataNotFound}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
@@ -5,7 +5,7 @@ export const LanguageContext = createContext();
|
||||
export const LanguageProvider = ({ children }) => {
|
||||
const [currentLocale, setCurrentLocale] = useState(localStorage.getItem('currentLocale') ? localStorage.getItem('currentLocale') : 'id-ID');
|
||||
const [localeData, setLocaleData] = useState('id');
|
||||
const cancelToken = useRef(null);
|
||||
const cancelToken = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createContext, ReactNode, useEffect, useReducer } from 'react';
|
||||
// utils
|
||||
import axios from '@/utils/axios';
|
||||
// import { isValidToken, setSession } from '@/utils/jwt';
|
||||
import { setSession, getSession, setUser, getUser } from '@/utils/token';
|
||||
import { setSession, getSession, setUser, getUser, getCookie } from '@/utils/token';
|
||||
// @types
|
||||
import { ActionMap, AuthState, AuthUser, JWTContextType } from '@/@types/auth';
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -86,12 +86,16 @@ function AuthProvider({ children }: AuthProviderProps) {
|
||||
const initialize = async () => {
|
||||
try {
|
||||
const accessToken = getSession();
|
||||
if (accessToken) {
|
||||
setSession(accessToken);
|
||||
const rememberMe = getCookie('rememberMe') == 'OK' ? false : true;
|
||||
|
||||
if (accessToken) {
|
||||
const userString = getUser();
|
||||
const storedUser = userString ? JSON.parse(userString) : null;
|
||||
setUser(storedUser, rememberMe);
|
||||
setSession(accessToken, rememberMe);
|
||||
const response = await axios.get('/user');
|
||||
const user = response.data;
|
||||
|
||||
const response = await axios.get('/user');
|
||||
const user = response.data;
|
||||
|
||||
dispatch({
|
||||
type: Types.Initial,
|
||||
payload: {
|
||||
@@ -126,16 +130,16 @@ function AuthProvider({ children }: AuthProviderProps) {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type' : 'application/json',
|
||||
'Accept-Language': (localStorage.getItem('currentLocale') ? localStorage.getItem('currentLocale') : 'id-ID'),
|
||||
'Accept-Language': localStorage.getItem('currentLocale') ?? 'id-ID',
|
||||
},
|
||||
};
|
||||
|
||||
const login = async (email: string, password: string) => axios
|
||||
const login = async (email: string, password: string, rememberMe: boolean) => axios
|
||||
.post('/login', { email, password }, headers)
|
||||
.then((response) => {
|
||||
const { user, token } = response.data.data;
|
||||
setSession(token);
|
||||
setUser(user);
|
||||
setSession(token, rememberMe);
|
||||
setUser(user, rememberMe);
|
||||
|
||||
dispatch({
|
||||
type: Types.Login,
|
||||
@@ -168,8 +172,9 @@ function AuthProvider({ children }: AuthProviderProps) {
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
setSession(null);
|
||||
setUser(null);
|
||||
await axios.post('logout');
|
||||
setSession(null, false);
|
||||
setUser(null, false);
|
||||
dispatch({ type: Types.Logout });
|
||||
};
|
||||
|
||||
@@ -187,9 +192,9 @@ function AuthProvider({ children }: AuthProviderProps) {
|
||||
);
|
||||
|
||||
// if (state.isInitialized) {
|
||||
// return (!state.isAuthenticated && location.pathname !== '/auth/login') ?
|
||||
// return (!state.isAuthenticated && location.pathname !== '/auth/login') ?
|
||||
// (<Navigate to="/auth/login" replace={true} />)
|
||||
// : false && location.pathname == '/auth/login' ?
|
||||
// : false && location.pathname == '/auth/login' ?
|
||||
// (<Navigate to="/dashboard" replace={true} />)
|
||||
// : (
|
||||
// <AuthContext.Provider
|
||||
|
||||
82
frontend/hospital-portal/src/lang/en-US.json
Normal file
82
frontend/hospital-portal/src/lang/en-US.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"greeting": "Hello",
|
||||
"buttonText": "Click Me",
|
||||
"infoLogin": "Enter the registered account",
|
||||
"txtLogin1" : "Sign in to Hospital Portal",
|
||||
"txtLogin2" : "Enter your details below",
|
||||
"txtCardSearchMember1" : "Membership Query",
|
||||
"txtCardSearchMember2" : "Search Member",
|
||||
"txtCardSearchMember3" : "Date of Birth",
|
||||
"txtCardSearchMember4" : "Member ID",
|
||||
"txtCardSearchMember5" : "Member",
|
||||
"txtDialogMember1" : "Services",
|
||||
"txtDialogMember2" : "Request LOG",
|
||||
"txtDialogMember3" : "Detail",
|
||||
"txtDialogMember4" : "Please select services",
|
||||
"txtDialogMember5" : "Admission Date",
|
||||
"txtDialogMember6" : "Please select admission date",
|
||||
"txtWarningDischargeDate" : "Please select discharge date",
|
||||
"txtCreateAt" : "Create at",
|
||||
"txtDateBirth" : "Date of Birth",
|
||||
"txtGender" : "Gender",
|
||||
"txtMaritalStatus" : "Marital Status",
|
||||
"txtLanguage" : "Language",
|
||||
"txtRelationship" : "Relationship",
|
||||
"txtRequestDate" : "Request Date",
|
||||
"txtMemberID" : "Member ID",
|
||||
"txtClaimCode" : "Claim Code",
|
||||
"txtRequestCode" : "Request Code",
|
||||
"txtName" : "Name",
|
||||
"txtStatus" : "Status",
|
||||
"txtSearch" : "Search Name or Member ID...",
|
||||
"txtAll" : "All",
|
||||
"txtSubmissionDate" : "Admission Date",
|
||||
"txtDataNotFound" : "Data Not Found",
|
||||
"txtConditionDocument" : "Condition Document",
|
||||
"txtDiagnosisDokument" : "Diagnosis Dokument",
|
||||
"txtSupportingResultDocument" : "Supporting Result Document",
|
||||
"txtAddResult" : "Add Result",
|
||||
"txtServiceType" : "Service Type",
|
||||
"txtAdditionalDocuments" : "Additional Documents",
|
||||
"txtAddNew" : "Add New",
|
||||
"txtAddress" : "Address",
|
||||
"txtProvider": "Provider",
|
||||
"txtAlertProvider" : "Please enter provider",
|
||||
"txtHelp" : "Need help?",
|
||||
"txtContactUs" : "Contact Us",
|
||||
"txtNotifications" : "Notifications",
|
||||
"txtYouHave" : "You have",
|
||||
"txtUnm" : "unread messages",
|
||||
"txtNew" : "New",
|
||||
"txtBeforeThat" : "Before that",
|
||||
"txtDischargeDate" : "Discharge Date",
|
||||
"txtPatner" : "Patner",
|
||||
"txtSelected": "Selected",
|
||||
"txtConfirmation": "Confirmation",
|
||||
"txtReason": "Reason Decline",
|
||||
"txtCancel": "Cancel",
|
||||
"txtDecline": "Decline",
|
||||
"txtApprove": "Approve",
|
||||
"txtDialogConfirmation": "Are you sure you want to proceed with this action?",
|
||||
"txtStartDate": "Start Date",
|
||||
"txtEndDate": "End Date",
|
||||
"txtHelp1" : "Has problem with your account?",
|
||||
"txtLupaSandi": "Forgot password?",
|
||||
"txtIngatkanSaya": "Remember me",
|
||||
"txtLogin": "Login",
|
||||
"txtForgotYourPassword": "Forgot your password?",
|
||||
"txtPleaseEnterPassword": "Please enter the email address associated with your account and We will email you a code to reset your password.",
|
||||
"txtBack": "Back",
|
||||
"txtSuccessSend": "Request sent successfully",
|
||||
"txtCodeConfirm": "We have sent a confirmation email to",
|
||||
"txtPleasCheck": "Please check your email.",
|
||||
"txtCheckEmail": "Please check your email!",
|
||||
"txtEmail": "We have emailed a 6-digit confirmation code and check spam folder, please enter the code in below box to verify your email.",
|
||||
"txtDont": "Don’t have a code?",
|
||||
"txtResendCode": "Resend code",
|
||||
"txtSecond": "Second",
|
||||
"txtPleaseInput": "Please enter your new password.",
|
||||
"txtNewPassword": "New Password",
|
||||
"txtConfPassword": "Confirm Kata Sandi"
|
||||
|
||||
}
|
||||
81
frontend/hospital-portal/src/lang/id-ID.json
Normal file
81
frontend/hospital-portal/src/lang/id-ID.json
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"greeting": "Halo",
|
||||
"buttonText": "Klik Saya",
|
||||
"infoLogin": "Masukan akun yang telah terdaftar",
|
||||
"txtLogin1" : "Masuk ke Hospital Portal",
|
||||
"txtLogin2" : "Masukkan detail Anda di bawah ini",
|
||||
"txtCardSearchMember1" : "Pengajuan Jaminan",
|
||||
"txtCardSearchMember2" : "Cari Anggota",
|
||||
"txtCardSearchMember3" : "Tanggal Lahir",
|
||||
"txtCardSearchMember4" : "Member ID",
|
||||
"txtCardSearchMember5" : "Member",
|
||||
"txtDialogMember1" : "Layanan",
|
||||
"txtDialogMember2" : "Request LOG",
|
||||
"txtDialogMember3" : "Detail",
|
||||
"txtDialogMember4" : "Mohon pilih layanan",
|
||||
"txtDialogMember5" : "Tanggal Masuk",
|
||||
"txtDialogMember6" : "Mohon pilih tanggal masuk",
|
||||
"txtWarningDischargeDate" : "Mohon pilih tanggal keluar",
|
||||
"txtCreateAt" : "Tanggal Buat",
|
||||
"txtDateBirth" : "Tanggal Lahir",
|
||||
"txtGender" : "Jenis Kelamin",
|
||||
"txtMaritalStatus" : "Status Perkawinan",
|
||||
"txtLanguage" : "Bahasa",
|
||||
"txtRelationship" : "Hubungan",
|
||||
"txtRequestDate" : "Tanggal Permintaan",
|
||||
"txtMemberID" : "ID Anggota",
|
||||
"txtClaimCode" : "Kode Klaim",
|
||||
"txtRequestCode" : "Kode Pengajuan",
|
||||
"txtName" : "Nama",
|
||||
"txtStatus" : "Status",
|
||||
"txtSearch" : "Cari Nama atau ID Anggota...",
|
||||
"txtAll" : "Semua",
|
||||
"txtSubmissionDate" : "Tanggal Masuk",
|
||||
"txtDataNotFound" : "Data Tidak Ditemukan",
|
||||
"txtConditionDocument" : "Dokumen Kondisi",
|
||||
"txtDiagnosisDokument" : "Dokumen Diagnosis",
|
||||
"txtSupportingResultDocument" : "Dokumen Pendukung",
|
||||
"txtAddResult" : "Tambah Hasil",
|
||||
"txtServiceType" : "Tipe Layanan",
|
||||
"txtAdditionalDocuments" : "Dokumen Tambahan",
|
||||
"txtAddNew" : "Tambah Baru",
|
||||
"txtAddress" : "Alamat",
|
||||
"txtProvider": "Provider",
|
||||
"txtAlertProvider" : "Mohon masukan provider",
|
||||
"txtHelp" : "Butuh Bantuan?",
|
||||
"txtContactUs" : "Kontak Kami",
|
||||
"txtNotifications" : "Notifikasi",
|
||||
"txtYouHave" : "Anda memiliki",
|
||||
"txtUnm" : "pesan yang belum dibaca",
|
||||
"txtNew" : "Baru",
|
||||
"txtBeforeThat" : "Sebelum",
|
||||
"txtDischargeDate" : "Tanggal Keluar",
|
||||
"txtPatner" : "Rekanan",
|
||||
"txtSelected": "Terpilih",
|
||||
"txtConfirmation": "Konfirmasi",
|
||||
"txtReason": "Alasan Penolakan",
|
||||
"txtCancel": "Batal",
|
||||
"txtDecline": "Tolak",
|
||||
"txtApprove": "Terima",
|
||||
"txtDialogConfirmation": "Apakah Anda yakin ingin melanjutkan tindakan ini?",
|
||||
"txtStartDate": "Tanggal Mulai",
|
||||
"txtEndDate": "Tanggal Akhir",
|
||||
"txtHelp1" : "Punya masalah dengan akun Anda?",
|
||||
"txtLupaSandi": "Lupa sandi?",
|
||||
"txtIngatkanSaya": "Ingatkan saya",
|
||||
"txtLogin": "Masuk",
|
||||
"txtForgotYourPassword": "Lupa password anda?",
|
||||
"txtPleaseEnterPassword": "Silakan masukkan alamat email yang terkait dengan akun Anda dan Kami akan mengirimkan email berisi kode untuk mengatur ulang kata sandi Anda.",
|
||||
"txtBack": "Kembali",
|
||||
"txtSuccessSend": "Permintaan berhasil dikirim",
|
||||
"txtCodeConfirm": "Kami telah mengirimkan email konfirmasi ke",
|
||||
"txtPleasCheck": "Mohon cek email Anda.",
|
||||
"txtCheckEmail": "Mohon periksa email Anda!",
|
||||
"txtEmail": "Kami telah mengirimkan kode konfirmasi 6 digit melalui email cek juga difolder spam, silakan masukkan kode di kotak bawah ini untuk memverifikasi email Anda.",
|
||||
"txtDont": "Tidak mendapatkan kode?",
|
||||
"txtResendCode": "Kirim ulang kode",
|
||||
"txtSecond": "Detik",
|
||||
"txtPleaseInput": "Mohon masukan kata sandi baru Anda.",
|
||||
"txtNewPassword": "Kata Sandi Baru",
|
||||
"txtConfPassword": "Konfirmasi Kata Sandi"
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import { IconButtonAnimate } from '@/components/animate';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useAuth from '@/hooks/useAuth';
|
||||
|
||||
import { getUser } from '@/utils/token';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const MENU_OPTIONS = [
|
||||
@@ -45,6 +47,8 @@ export default function AccountPopover() {
|
||||
navigate('/auth/login');
|
||||
};
|
||||
|
||||
const userString = getUser();
|
||||
const storedUser = userString ? JSON.parse(userString) : null;
|
||||
return (
|
||||
<>
|
||||
<IconButtonAnimate
|
||||
@@ -89,7 +93,7 @@ export default function AccountPopover() {
|
||||
Hospital Admin
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }} noWrap>
|
||||
hospitaladmin@gmail.com
|
||||
{storedUser?.email}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import Iconify from '@/components/Iconify';
|
||||
// sections
|
||||
import { ForgetPasswordForm } from '@/sections/auth/forget-password';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useState, useContext, useEffect } from 'react';
|
||||
import { LanguageContext } from '@/contexts/LanguageContext';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -25,6 +27,8 @@ const RootStyle = styled('div')(({ theme }) => ({
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function ForgetPassword() {
|
||||
|
||||
const { localeData } = useContext(LanguageContext);
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const token = searchParams.get('token');
|
||||
|
||||
@@ -42,12 +46,12 @@ export default function ForgetPassword() {
|
||||
startIcon={<Iconify icon={'eva:arrow-ios-back-fill'} width={20} height={20} />}
|
||||
sx={{ mb: 3 }}
|
||||
>
|
||||
Back
|
||||
{localeData.txtBack}
|
||||
</Button>
|
||||
|
||||
<Typography variant="h3" paragraph></Typography>
|
||||
<Typography sx={{ color: 'text.secondary' }}>
|
||||
Please enter your new password.
|
||||
{localeData.txtPleaseInput}
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ mt: 5, mb: 3 }}>
|
||||
|
||||
@@ -78,29 +78,28 @@ export default function Login() {
|
||||
const smUp = useResponsive("up", "sm");
|
||||
|
||||
const mdUp = useResponsive("up", "md");
|
||||
const handleClick = () => {
|
||||
window.location.href = 'https://wa.me/6285890008500';
|
||||
};
|
||||
|
||||
return (
|
||||
<Page title="Login">
|
||||
<RootStyle>
|
||||
<HeaderStyle>
|
||||
{/*<Logo sx={{ width: 150, height: 150 }} />
|
||||
<Logo sx={{ width: 150, height: 150, display: 'none' }} />
|
||||
{smUp && (
|
||||
<Typography variant="body2" sx={{ mt: { md: -2 } }}>
|
||||
Has problem with your account? {""}
|
||||
{localeData.txtHelp1} {""}
|
||||
<Link
|
||||
variant="subtitle2"
|
||||
component={RouterLink}
|
||||
to="#"
|
||||
onClick={(e) => {
|
||||
window.location.href =
|
||||
"mailto:admin@linksehat.com";
|
||||
e.preventDefault();
|
||||
}}
|
||||
onClick={handleClick}
|
||||
>
|
||||
Contact Us
|
||||
{localeData.txtContactUs}
|
||||
</Link>
|
||||
</Typography>
|
||||
)}*/}
|
||||
)}
|
||||
</HeaderStyle>
|
||||
|
||||
{/* {mdUp && (
|
||||
@@ -116,7 +115,7 @@ export default function Login() {
|
||||
/>
|
||||
</SectionStyle>
|
||||
)} */}
|
||||
|
||||
|
||||
<Container maxWidth="sm">
|
||||
<ContentStyle>
|
||||
<Card sx={{padding:2}}>
|
||||
@@ -125,7 +124,7 @@ export default function Login() {
|
||||
alignItems="center"
|
||||
sx={{ mb: 5 }}
|
||||
>
|
||||
|
||||
|
||||
<Logo sx={{ width: 90, height: 90 }} />
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useContext } from 'react';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
// @mui
|
||||
import { styled } from '@mui/material/styles';
|
||||
@@ -13,6 +13,9 @@ import Page from '@/components/Page';
|
||||
import { ResetPasswordForm } from '@/sections/auth/reset-password';
|
||||
// assets
|
||||
import { SentIcon } from '@/assets';
|
||||
import { LanguageContext } from '@/contexts/LanguageContext';
|
||||
|
||||
import { useNavigate } from 'react-router-dom'; // Import useNavigate
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -27,10 +30,17 @@ const RootStyle = styled('div')(({ theme }) => ({
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function ResetPassword() {
|
||||
const { localeData } = useContext(LanguageContext);
|
||||
const [email, setEmail] = useState('');
|
||||
|
||||
const [sent, setSent] = useState(false);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSent = () => {
|
||||
setSent(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Page title="Reset Password" sx={{ height: 1 }}>
|
||||
<RootStyle>
|
||||
@@ -41,16 +51,15 @@ export default function ResetPassword() {
|
||||
{!sent ? (
|
||||
<>
|
||||
<Typography variant="h3" paragraph>
|
||||
Forgot your password?
|
||||
{localeData.txtForgotYourPassword}
|
||||
</Typography>
|
||||
|
||||
<Typography sx={{ color: 'text.secondary', mb: 5 }}>
|
||||
Please enter the email address associated with your account and We will email you
|
||||
a link to reset your password.
|
||||
{localeData.txtPleaseEnterPassword}
|
||||
</Typography>
|
||||
|
||||
<ResetPasswordForm
|
||||
onSent={() => setSent(true)}
|
||||
onSent={handleSent}
|
||||
onGetEmail={(value) => setEmail(value)}
|
||||
/>
|
||||
|
||||
@@ -61,7 +70,7 @@ export default function ResetPassword() {
|
||||
to={PATH_AUTH.login}
|
||||
sx={{ mt: 3 }}
|
||||
>
|
||||
Back
|
||||
{localeData.txtBack}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
@@ -69,24 +78,24 @@ export default function ResetPassword() {
|
||||
<SentIcon sx={{ mb: 5, mx: 'auto', height: 160 }} />
|
||||
|
||||
<Typography variant="h3" gutterBottom>
|
||||
Request sent successfully
|
||||
{localeData.txtSuccessSend}
|
||||
</Typography>
|
||||
|
||||
<Typography>
|
||||
We have sent a confirmation email to
|
||||
{localeData.txtCodeConfirm}
|
||||
<strong>{email}</strong>
|
||||
<br />
|
||||
Please check your email.
|
||||
{localeData.txtPleasCheck}
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
size="large"
|
||||
variant="contained"
|
||||
component={RouterLink}
|
||||
to={PATH_AUTH.login}
|
||||
to={PATH_AUTH.verify}
|
||||
sx={{ mt: 5 }}
|
||||
>
|
||||
Back
|
||||
Next
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { Link as RouterLink, useLocation } from 'react-router-dom';
|
||||
// @mui
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Box, Button, Link, Container, Typography } from '@mui/material';
|
||||
@@ -11,6 +11,9 @@ import Page from '@/components/Page';
|
||||
import Iconify from '@/components/Iconify';
|
||||
// sections
|
||||
import { VerifyCodeForm } from '@/sections/auth/verify-code';
|
||||
import { useState, useContext, useEffect } from 'react';
|
||||
import { LanguageContext } from '@/contexts/LanguageContext';
|
||||
import axios from '@/utils/axios';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -21,9 +24,41 @@ const RootStyle = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(12, 0),
|
||||
}));
|
||||
|
||||
function useQuery() {
|
||||
return new URLSearchParams(useLocation().search);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function VerifyCode() {
|
||||
const { localeData } = useContext(LanguageContext);
|
||||
const query = useQuery();
|
||||
const email = query.get('email');
|
||||
const [timer, setTimer] = useState(60); // Initialize timer with 60 seconds
|
||||
const [canResend, setCanResend] = useState(false); // State to control resend button visibility
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setTimer((prev) => {
|
||||
if (prev > 0) {
|
||||
return prev - 1;
|
||||
} else {
|
||||
clearInterval(interval);
|
||||
setCanResend(true); // Enable resend button when timer reaches 0
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval); // Cleanup interval on component unmount
|
||||
}, []);
|
||||
|
||||
const handleResend = () => {
|
||||
setCanResend(false);
|
||||
setTimer(60); // Reset timer to 60 seconds
|
||||
// Add logic to resend the code here
|
||||
axios.post('/verify-email', {email: email});
|
||||
};
|
||||
|
||||
return (
|
||||
<Page title="Verify" sx={{ height: 1 }}>
|
||||
<RootStyle>
|
||||
@@ -34,30 +69,42 @@ export default function VerifyCode() {
|
||||
<Button
|
||||
size="small"
|
||||
component={RouterLink}
|
||||
to={PATH_AUTH.login}
|
||||
to={PATH_AUTH.resetPassword}
|
||||
startIcon={<Iconify icon={'eva:arrow-ios-back-fill'} width={20} height={20} />}
|
||||
sx={{ mb: 3 }}
|
||||
>
|
||||
Back
|
||||
{localeData.txtBack}
|
||||
</Button>
|
||||
|
||||
<Typography variant="h3" paragraph>
|
||||
Please check your email!
|
||||
{localeData.txtCheckEmail}
|
||||
</Typography>
|
||||
<Typography variant='subtitle1'>{email}</Typography>
|
||||
<Typography sx={{ color: 'text.secondary' }}>
|
||||
We have emailed a 6-digit confirmation code to acb@domain, please enter the code in
|
||||
below box to verify your email.
|
||||
{localeData.txtEmail}
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ mt: 5, mb: 3 }}>
|
||||
<VerifyCodeForm />
|
||||
<VerifyCodeForm
|
||||
onGetEmail={email}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Typography variant="body2" align="center">
|
||||
{/* <Typography variant="body2" align="center">
|
||||
Don’t have a code?
|
||||
<Link variant="subtitle2" underline="none" onClick={() => {}}>
|
||||
Resend code
|
||||
</Link>
|
||||
</Typography> */}
|
||||
<Typography variant="body2" align="center">
|
||||
{localeData.txtDont}
|
||||
{canResend ? (
|
||||
<Link sx={{cursor: 'pointer'}} variant="subtitle2" underline="none" onClick={handleResend}>
|
||||
{localeData.txtResendCode}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${localeData.txtResendCode} ${timer} ${localeData.txtSecond}`}</span>
|
||||
)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Container>
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function Router() {
|
||||
// { path: 'register-unprotected', element: <Register /> },
|
||||
{ path: 'reset-password', element: <ResetPassword /> },
|
||||
{ path: 'forget-password', element: <ForgetPassword /> },
|
||||
// { path: 'verify', element: <VerifyCode /> },
|
||||
{ path: 'verify', element: <VerifyCode /> },
|
||||
],
|
||||
},
|
||||
// {
|
||||
|
||||
@@ -2,8 +2,8 @@ import * as Yup from 'yup';
|
||||
// form
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Link as RouterLink, useNavigate } from 'react-router-dom';
|
||||
import { useCallback, useEffect, useContext, useMemo, useRef, useState } from 'react';
|
||||
import { Link as RouterLink, useNavigate, useLocation } from 'react-router-dom';
|
||||
|
||||
// @mui
|
||||
import { Alert, IconButton, InputAdornment, Stack, Typography } from '@mui/material';
|
||||
@@ -14,6 +14,9 @@ import useIsMountedRef from '@/hooks/useIsMountedRef';
|
||||
import { FormProvider, RHFTextField } from '@/components/hook-form';
|
||||
import axios from '@/utils/axios';
|
||||
import Iconify from '@/components/Iconify';
|
||||
import { LanguageContext } from '@/contexts/LanguageContext';
|
||||
|
||||
import { useSnackbar } from 'notistack';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -26,9 +29,19 @@ type Props = {
|
||||
token: string;
|
||||
};
|
||||
|
||||
function useQuery() {
|
||||
return new URLSearchParams(useLocation().search);
|
||||
}
|
||||
|
||||
export default function ForgetPasswordForm({ token }: Props) {
|
||||
const { localeData } = useContext(LanguageContext);
|
||||
const isMountedRef = useIsMountedRef();
|
||||
const navigate = useNavigate();
|
||||
const query = useQuery();
|
||||
const email = query.get('email');
|
||||
|
||||
const token_ = query.get('token');
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const [showPasswordNew, setShowPasswordNew] = useState(false);
|
||||
const [showPasswordConfirmNew, setShowPasswordConfirmNew] = useState(false);
|
||||
@@ -47,19 +60,19 @@ export default function ForgetPasswordForm({ token }: Props) {
|
||||
|
||||
const onSubmit = async (data: FormValuesProps) => {
|
||||
try {
|
||||
await axios.post('/forget-password', { ...data, token });
|
||||
console.log(data);
|
||||
await axios.post('/forget-password', { ...data, email:email, token:token_ });
|
||||
|
||||
|
||||
// await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
if (isMountedRef.current) {
|
||||
enqueueSnackbar('Password reset was successful', { variant: 'success' });
|
||||
navigate('/auth/login', { replace: true });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
if (isMountedRef.current) {
|
||||
setError('afterSubmit', { ...error, message: error.response.data.message });
|
||||
setError('afterSubmit', { ...error, message: error.response.data.meta.message });
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -68,10 +81,10 @@ export default function ForgetPasswordForm({ token }: Props) {
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
|
||||
<Stack spacing={3}>
|
||||
{!!errors.afterSubmit && <Alert severity="error">{errors.afterSubmit.message}</Alert>}
|
||||
<Typography>Kata Sandi Baru</Typography>
|
||||
<Typography>{localeData.txtNewPassword}</Typography>
|
||||
<RHFTextField
|
||||
name="new_password"
|
||||
label="Kata Sandi Baru"
|
||||
label={localeData.txtNewPassword}
|
||||
type={showPasswordNew ? 'text' : 'password'}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
@@ -83,10 +96,10 @@ export default function ForgetPasswordForm({ token }: Props) {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Typography>Konfirmasi Kata Sandi </Typography>
|
||||
<Typography>{localeData.txtConfPassword}</Typography>
|
||||
<RHFTextField
|
||||
name="confirm_new_password"
|
||||
label="Konfirmasi Kata Sandi"
|
||||
label={localeData.txtConfPassword}
|
||||
type={showPasswordConfirmNew ? 'text' : 'password'}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function LoginForm() {
|
||||
|
||||
const onSubmit = async (data: FormValuesProps) => {
|
||||
try {
|
||||
const loginResult = await login(data.email, data.password);
|
||||
const loginResult = await login(data.email, data.password, data.remember);
|
||||
|
||||
navigate('/dashboard');
|
||||
} catch (error) {
|
||||
@@ -100,10 +100,10 @@ export default function LoginForm() {
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ my: 2 }}>
|
||||
{/*<RHFCheckbox name="remember" label="Remember me" />
|
||||
<RHFCheckbox name="remember" label={localeData.txtIngatkanSaya}/>
|
||||
<Link component={RouterLink} variant="subtitle2" to={PATH_AUTH.resetPassword}>
|
||||
Forgot password?
|
||||
</Link>*/}
|
||||
{localeData.txtLupaSandi}
|
||||
</Link>
|
||||
</Stack>
|
||||
|
||||
<LoadingButton
|
||||
@@ -114,7 +114,7 @@ export default function LoginForm() {
|
||||
loading={isSubmitting}
|
||||
sx={{ marginTop: 2 }}
|
||||
>
|
||||
Login
|
||||
{localeData.txtLogin}
|
||||
</LoadingButton>
|
||||
</FormProvider>
|
||||
);
|
||||
|
||||
@@ -10,6 +10,7 @@ import useIsMountedRef from '@/hooks/useIsMountedRef';
|
||||
// components
|
||||
import { FormProvider, RHFTextField } from '@/components/hook-form';
|
||||
import axios from '@/utils/axios';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -26,6 +27,8 @@ type Props = {
|
||||
export default function ResetPasswordForm({ onSent, onGetEmail }: Props) {
|
||||
const isMountedRef = useIsMountedRef();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const ResetPasswordSchema = Yup.object().shape({
|
||||
email: Yup.string().email('Email must be a valid email address').required('Email is required'),
|
||||
});
|
||||
@@ -43,19 +46,22 @@ export default function ResetPasswordForm({ onSent, onGetEmail }: Props) {
|
||||
|
||||
const onSubmit = async (data: FormValuesProps) => {
|
||||
try {
|
||||
await axios.post('/verify-email', data);
|
||||
console.log(data);
|
||||
const response = await axios.post('/verify-email', data);
|
||||
if(response.data.data.email)
|
||||
{
|
||||
|
||||
onGetEmail(response.data.data.email);
|
||||
navigate(`/auth/verify?email=${response.data.data.email}`);
|
||||
}
|
||||
// await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
if (isMountedRef.current) {
|
||||
onSent();
|
||||
onGetEmail(data.email);
|
||||
onGetEmail(response.data.data.email);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
if (isMountedRef.current) {
|
||||
setError('afterSubmit', { ...error, message: error.response.data.message });
|
||||
setError('afterSubmit', { ...error, message: error.response.data.meta.message });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,9 +6,12 @@ import { useEffect } from 'react';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
// @mui
|
||||
import { OutlinedInput, Stack } from '@mui/material';
|
||||
import { OutlinedInput, Alert, Stack } from '@mui/material';
|
||||
import useIsMountedRef from '@/hooks/useIsMountedRef';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { FormProvider, RHFTextField } from '@/components/hook-form';
|
||||
// routes
|
||||
import axios from '@/utils/axios';
|
||||
// import { PATH_DASHBOARD } from '@/routes/paths';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -23,8 +26,11 @@ type FormValuesProps = {
|
||||
};
|
||||
|
||||
type ValueNames = 'code1' | 'code2' | 'code3' | 'code4' | 'code5' | 'code6';
|
||||
|
||||
export default function VerifyCodeForm() {
|
||||
type Props = {
|
||||
onGetEmail: (value: string) => void;
|
||||
};
|
||||
export default function VerifyCodeForm({ onGetEmail }: Props) {
|
||||
const isMountedRef = useIsMountedRef();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
@@ -51,8 +57,9 @@ export default function VerifyCodeForm() {
|
||||
watch,
|
||||
control,
|
||||
setValue,
|
||||
setError,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, isValid },
|
||||
formState: { isSubmitting, isValid , errors},
|
||||
} = useForm({
|
||||
mode: 'onBlur',
|
||||
resolver: yupResolver(VerifyCodeSchema),
|
||||
@@ -67,16 +74,36 @@ export default function VerifyCodeForm() {
|
||||
}, []);
|
||||
|
||||
const onSubmit = async (data: FormValuesProps) => {
|
||||
// try {
|
||||
// await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
// console.log('code:', Object.values(data).join(''));
|
||||
|
||||
// enqueueSnackbar('Verify success!', { variant: 'success' });
|
||||
|
||||
// navigate('/dashboard', { replace: true });
|
||||
// } catch (error) {
|
||||
// console.error(error);
|
||||
// }
|
||||
|
||||
try {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
console.log('code:', Object.values(data).join(''));
|
||||
|
||||
enqueueSnackbar('Verify success!', { variant: 'success' });
|
||||
|
||||
navigate('/dashboard', { replace: true });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
const response = await axios.post('/verify-code', {email: onGetEmail, token: Object.values(data).join('')});
|
||||
// await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
if(response.data.meta.code === 200)
|
||||
{
|
||||
navigate(`/auth/forget-password?email=${response.data.data.email}&token=${response.data.data.token}`);
|
||||
}
|
||||
if (isMountedRef.current) {
|
||||
if(response.data.meta.code === 404)
|
||||
{
|
||||
setError('afterSubmit', { ...response, message: response.data.meta.message });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (isMountedRef.current) {
|
||||
setError('afterSubmit', { ...error, message: error.response.data.meta.message });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasteClipboard = (event: ClipboardEvent) => {
|
||||
@@ -115,35 +142,38 @@ export default function VerifyCodeForm() {
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Stack direction="row" spacing={2} justifyContent="center">
|
||||
{Object.keys(values).map((name, index) => (
|
||||
<Controller
|
||||
key={name}
|
||||
name={`code${index + 1}` as ValueNames}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<OutlinedInput
|
||||
{...field}
|
||||
id="field-code"
|
||||
autoFocus={index === 0}
|
||||
placeholder="-"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
handleChangeWithNextField(event, field.onChange)
|
||||
}
|
||||
inputProps={{
|
||||
maxLength: 1,
|
||||
sx: {
|
||||
p: 0,
|
||||
textAlign: 'center',
|
||||
width: { xs: 36, sm: 56 },
|
||||
height: { xs: 36, sm: 56 },
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
<Stack spacing={3}>
|
||||
{!!errors.afterSubmit && <Alert severity="error">{errors.afterSubmit.message}</Alert>}
|
||||
<Stack direction="row" spacing={2} justifyContent="center">
|
||||
{Object.keys(values).map((name, index) => (
|
||||
<Controller
|
||||
key={name}
|
||||
name={`code${index + 1}` as ValueNames}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<OutlinedInput
|
||||
{...field}
|
||||
id="field-code"
|
||||
autoFocus={index === 0}
|
||||
placeholder="-"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
handleChangeWithNextField(event, field.onChange)
|
||||
}
|
||||
inputProps={{
|
||||
maxLength: 1,
|
||||
sx: {
|
||||
p: 0,
|
||||
textAlign: 'center',
|
||||
width: { xs: 36, sm: 56 },
|
||||
height: { xs: 36, sm: 56 },
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<LoadingButton
|
||||
fullWidth
|
||||
|
||||
@@ -25,34 +25,98 @@ import axios from './axios';
|
||||
// }, timeLeft);
|
||||
// };
|
||||
|
||||
const setSession = (accessToken: string | null) => {
|
||||
// let expiredCookie = 1/24/60; 1 menit
|
||||
let expiredCookie = 12/24; //12 jam
|
||||
|
||||
const setCookie = (name:any, value:any, days:any) => {
|
||||
let expires = "";
|
||||
if (days) {
|
||||
const date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toUTCString();
|
||||
}
|
||||
document.cookie = name + "=" + decodeURIComponent(value || "") + expires + "; path=/; SameSite=Strict";
|
||||
};
|
||||
|
||||
const setSession = (accessToken: string | null, rememberMe: boolean) => {
|
||||
if (accessToken) {
|
||||
localStorage.setItem('accessToken', accessToken);
|
||||
const userString = getUser();
|
||||
const storedUser = userString ? JSON.parse(userString) : null;
|
||||
if(rememberMe)
|
||||
{
|
||||
localStorage.setItem('accessToken', accessToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
setCookie('accessToken', accessToken, expiredCookie);
|
||||
setCookie('rememberMe', 'OK', expiredCookie);
|
||||
}
|
||||
|
||||
axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
|
||||
axios.defaults.headers.common['Accept-Language'] = (localStorage.getItem('currentLocale') ? localStorage.getItem('currentLocale') : 'id-ID');
|
||||
axios.defaults.headers.common['Accept-Language'] = localStorage.getItem('currentLocale') ?? 'id-ID';
|
||||
axios.defaults.headers.common['Accept'] = 'application/json';
|
||||
axios.defaults.headers.common['Content-Type'] = 'application/json';
|
||||
axios.defaults.headers.common['Organization-id'] = storedUser?.organization_id;
|
||||
// This function below will handle when token is expired
|
||||
// const { exp } = jwtDecode(accessToken);
|
||||
// handleTokenExpired(exp);
|
||||
} else {
|
||||
localStorage.removeItem('accessToken');
|
||||
removeCookie('accessToken');
|
||||
removeCookie('rememberMe');
|
||||
delete axios.defaults.headers.common.Authorization;
|
||||
delete axios.defaults.headers.common['Accept-Language'];
|
||||
delete axios.defaults.headers.common['Accept'];
|
||||
delete axios.defaults.headers.common['Content-Type'];
|
||||
delete axios.defaults.headers.common['Content-Type'];
|
||||
}
|
||||
};
|
||||
|
||||
const setUser = (user: any) => {
|
||||
const setUser = (user: any, rememberMe: boolean) => {
|
||||
if (user) {
|
||||
localStorage.setItem('user', user);
|
||||
if(rememberMe)
|
||||
{
|
||||
localStorage.setItem('user', JSON.stringify(user));
|
||||
}
|
||||
else
|
||||
{
|
||||
setCookie('user', JSON.stringify(user), expiredCookie);
|
||||
setCookie('rememberMe', 'OK', expiredCookie);
|
||||
}
|
||||
|
||||
} else {
|
||||
localStorage.removeItem('user');
|
||||
removeCookie('user');
|
||||
removeCookie('rememberMe');
|
||||
}
|
||||
};
|
||||
|
||||
const getSession = () => window.localStorage.getItem('accessToken')
|
||||
const getUser = () => window.localStorage.getItem('user')
|
||||
const getCookie = (name:any) => {
|
||||
const cookies = document.cookie.split('; ');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookiePair = cookies[i].split('=');
|
||||
if (cookiePair[0] === name) {
|
||||
return decodeURIComponent(cookiePair[1]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export { setSession, getSession, setUser, getUser };
|
||||
const getSession = () => {
|
||||
const localToken = window.localStorage.getItem('accessToken');
|
||||
const cookieToken = getCookie('accessToken');
|
||||
// Prioritaskan token dari localStorage
|
||||
return localToken || cookieToken;
|
||||
};
|
||||
// const getUser = () => window.localStorage.getItem('user') || window.sessionStorage.getItem('user')
|
||||
const getUser = () => {
|
||||
const localUser = window.localStorage.getItem('user');
|
||||
const cookieUser = getCookie('user');
|
||||
|
||||
// Prioritaskan token dari localStorage
|
||||
return localUser || cookieUser;
|
||||
};
|
||||
const removeCookie = (name:any) => {
|
||||
document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
||||
};
|
||||
|
||||
export { setSession, getSession, setUser, getUser, getCookie };
|
||||
|
||||
Reference in New Issue
Block a user