Compare commits

...

109 Commits

Author SHA1 Message Date
victorAnumudu 5ad75e412b https validator added 2026-02-25 19:49:31 +01:00
ameye e9d6689418 Merge branch 'link-update-validation-removed' of MERMS/MermsPanelReactJS into master 2026-02-25 07:46:25 +00:00
victorAnumudu ed38e0db98 made update link params optional 2026-02-24 09:18:06 +01:00
ameye 351f027a6f Merge branch 'payment-table-update' of MERMS/MermsPanelReactJS into master 2026-02-23 22:50:07 +00:00
victorAnumudu 20d56ee307 payment table updated 2026-02-23 19:14:36 +01:00
ameye f5c798c6c1 Merge branch 'header-profile-image' of MERMS/MermsPanelReactJS into master 2026-02-16 11:27:02 +00:00
victorAnumudu a11f5100ba header profile image bug fixed 2026-02-16 11:32:16 +01:00
CHIEFSOFT\ameye 7628a561f8 Merge branch 'master' of https://gitlab.chiefsoft.net/MERMS/MermsPanelReactJS 2026-02-15 17:33:56 -05:00
CHIEFSOFT\ameye 615acff0b7 fixed reports 2026-02-15 17:33:34 -05:00
ameye aea161ccaf Merge branch 'profile-picture-upload' of MERMS/MermsPanelReactJS into master 2026-02-14 11:00:42 +00:00
victorAnumudu 66a2bcacd5 profile image upload added 2026-02-10 18:17:44 +01:00
ameye ec9d84b779 Merge branch 'truncating-text' of MERMS/MermsPanelReactJS into master 2026-02-10 17:00:38 +00:00
victorAnumudu 8ee793d11e text truncated 2026-02-09 19:11:17 +01:00
ameye 46f17cbdfe Merge branch 'template-payload-fix' of MERMS/MermsPanelReactJS into master 2026-01-21 09:45:48 +00:00
victorAnumudu 283994e43a pauload fixed 2026-01-21 10:25:56 +01:00
ameye b7d242ff0a Merge branch 'template-media-set' of MERMS/MermsPanelReactJS into master 2026-01-21 08:44:57 +00:00
victorAnumudu f6bae30bdd btn name changed 2026-01-21 04:19:05 +01:00
victorAnumudu a7e2e865de added template media set endpoint 2026-01-21 03:27:48 +01:00
ameye 51887cf0d6 Merge branch 'upload-btn-bug' of MERMS/MermsPanelReactJS into master 2026-01-20 17:11:43 +00:00
victorAnumudu 5ea1047356 image pop up added 2026-01-20 17:15:58 +01:00
victorAnumudu 9b9a7cc5da fixed upload btn display 2026-01-20 14:31:55 +01:00
ameye 290e1fbd7e Merge branch 'image-list-resized' of MERMS/MermsPanelReactJS into master 2026-01-19 20:44:45 +00:00
victorAnumudu 6884aa19b2 fixed image size 2026-01-19 19:36:21 +01:00
CHIEFSOFT\ameye 597a45dcba site image 2026-01-18 18:13:58 -05:00
CHIEFSOFT\ameye 4f7274c30c load template settings 2026-01-17 19:05:12 -05:00
ameye 290356780c Merge branch 'config-temp' of MERMS/MermsPanelReactJS into master 2026-01-13 08:14:09 +00:00
victorAnumudu bd1450887b added config template page 2026-01-13 05:01:23 +01:00
CHIEFSOFT\ameye 923a2483ed Added report starter 2026-01-12 12:08:13 -05:00
ameye 7f0ccf35b2 Merge branch 'report-endpoints' of MERMS/MermsPanelReactJS into master 2026-01-09 19:09:59 +00:00
victorAnumudu af2fdcede6 added endpoint for payment, product and system reports 2026-01-09 18:10:34 +01:00
ameye a9f2136125 Merge branch 'system-table' of MERMS/MermsPanelReactJS into master 2026-01-09 12:24:14 +00:00
victorAnumudu 5d04abc0db added system table report 2026-01-09 09:01:22 +01:00
CHIEFSOFT\ameye 26d095afda System table added 2026-01-08 11:52:24 -05:00
CHIEFSOFT\ameye cd030d8d12 Merge branch 'master' of https://gitlab.chiefsoft.net/MERMS/MermsPanelReactJS 2026-01-07 04:02:14 -05:00
ameye 4a0b7925d0 Merge branch 'report-tab' of MERMS/MermsPanelReactJS into master 2026-01-07 09:01:59 +00:00
victorAnumudu 9d947f19b9 fixed report tab bug 2026-01-06 20:27:07 +01:00
CHIEFSOFT\ameye 7a99beab57 Merge branch 'master' of https://gitlab.chiefsoft.net/MERMS/MermsPanelReactJS 2026-01-05 15:08:17 -05:00
ameye 02731c021a Merge branch 'file-list-refresh' of MERMS/MermsPanelReactJS into master 2026-01-05 20:07:37 +00:00
victorAnumudu fecae9d626 file list refresh and scroll bar added 2026-01-05 14:37:19 +01:00
CHIEFSOFT\ameye a4867a1b73 Added report starter 2026-01-03 12:09:35 -05:00
CHIEFSOFT\ameye 51c2e4b568 Added report starter 2026-01-03 12:05:42 -05:00
CHIEFSOFT\ameye 756c084059 Added report starter 2026-01-03 11:04:15 -05:00
CHIEFSOFT\ameye 064973e190 Added report starter 2026-01-03 10:24:34 -05:00
CHIEFSOFT\ameye d165e9dc0f upload page 2026-01-01 12:19:36 -05:00
CHIEFSOFT\ameye 77109e8369 Added Media starter icons 2026-01-01 11:23:16 -05:00
CHIEFSOFT\ameye 2924401c9b media files 2026-01-01 01:05:48 -05:00
CHIEFSOFT\ameye f6c2b1129d server path 2025-12-28 14:34:21 -05:00
CHIEFSOFT\ameye 8ba167a17c panel values 2025-12-28 14:13:16 -05:00
CHIEFSOFT\ameye 7f65b286b1 Media upload 2025-12-28 10:58:56 -05:00
CHIEFSOFT\ameye afc9e5be0f Added Media starter 2025-12-14 14:26:11 -05:00
ameye 7aefb555ed Merge branch 'user-picture' of MERMS/MermsPanelReactJS into master 2025-12-02 22:58:40 +00:00
victorAnumudu 325d28c1a8 user profile picture link added 2025-12-02 21:42:41 +01:00
ameye 25269a44c3 Merge branch 'calendar-refresh' of MERMS/MermsPanelReactJS into master 2025-11-28 23:15:35 +00:00
victorAnumudu 47df35d076 calendar cache time removed 2025-11-28 20:50:41 +01:00
CHIEFSOFT\ameye 1deb15029d added h 2025-11-28 10:20:01 -05:00
ameye 30b284064d Merge branch 'username-length' of MERMS/MermsPanelReactJS into master 2025-11-27 20:37:49 +00:00
CHIEFSOFT\ameye 4cbe78efc3 report pssge 2025-11-27 15:36:41 -05:00
victorAnumudu 92ac7d74f4 added update profile endpoint 2025-11-27 21:27:03 +01:00
ameye f0382cea9e Merge branch 'scroll-bar' of MERMS/MermsPanelReactJS into master 2025-11-25 18:46:09 +00:00
victorAnumudu 2123af0abe template max height added 2025-11-25 18:57:46 +01:00
ameye a3c306bf89 Merge branch 'footer-content' of MERMS/MermsPanelReactJS into master 2025-11-25 05:09:37 +00:00
victorAnumudu a61abe718a added footer content to forget pwd page 2025-11-24 19:48:40 +01:00
ameye 2b91506c61 Merge branch 'login-error-fix' of MERMS/MermsPanelReactJS into master 2025-11-23 19:28:20 +00:00
victorAnumudu 253cace3fe fixed login error issue 2025-11-20 20:08:01 +01:00
CHIEFSOFT\ameye aa55a1a4e0 Logi mesage 2025-11-16 07:34:34 -05:00
ameye 8b763882fa Merge branch 'app-download-switch' of MERMS/MermsPanelReactJS into master 2025-10-30 17:02:20 +00:00
victorAnumudu 30540e46ba added app download show/hide env value 2025-10-30 17:53:16 +01:00
victorAnumudu b195b1f787 added app download show/hide env value 2025-10-30 17:52:13 +01:00
ameye 2ddd04a1a1 Merge branch 'auth-footer-component' of MERMS/MermsPanelReactJS into master 2025-10-20 19:27:38 +00:00
victorAnumudu 6ea26740a4 added auth footer component 2025-10-20 17:44:47 +01:00
CHIEFSOFT\ameye f334ca49f0 opacity adk=just 2025-10-19 13:06:19 -04:00
CHIEFSOFT\ameye fa9d7f69e4 Color configre cleanup 2025-10-19 13:04:10 -04:00
CHIEFSOFT\ameye a4db58ba97 react panel fix 2025-10-19 08:46:45 -04:00
CHIEFSOFT\ameye ddc747d9ca provision data 2025-10-19 08:37:58 -04:00
ameye ac337eb693 Merge branch 'text-truncate' of MERMS/MermsPanelReactJS into master 2025-10-17 18:17:28 +00:00
victorAnumudu f2c3415b1d text nowrap 2025-10-17 17:46:08 +01:00
ameye 37450925e1 Merge branch 'id-number-reduction' of MERMS/MermsPanelReactJS into master 2025-10-16 15:18:04 +00:00
victorAnumudu 3b20fcec68 last four characters displayed 2025-10-15 17:53:37 +01:00
ameye d87a083c3e Merge branch 'product-url-scrollbar' of MERMS/MermsPanelReactJS into master 2025-10-14 20:03:06 +00:00
victorAnumudu 3f8a7a6b3b added scrollbar to producturl table 2025-10-14 17:55:10 +01:00
ameye 91d82db40c Merge branch 'menu-close' of MERMS/MermsPanelReactJS into master 2025-10-08 21:24:40 +00:00
victorAnumudu 2dc12d7d0a menu close fixed 2025-10-08 20:27:14 +01:00
ameye 164195d4cc Merge branch 'refresh-msg' of MERMS/MermsPanelReactJS into master 2025-10-06 17:43:31 +00:00
victorAnumudu 7ad1a585ea added refresh message display 2025-10-06 17:13:28 +01:00
CHIEFSOFT\ameye a8c2dd84f1 provison room 2025-10-06 11:52:38 -04:00
CHIEFSOFT\ameye ddcc6f0cd2 Rebuild site text change 2025-10-05 14:00:30 -04:00
CHIEFSOFT\ameye 683f81e8a6 customn temaplte 2025-10-05 13:53:11 -04:00
CHIEFSOFT\ameye 8e09c30c5c fix error on display 2025-10-05 13:18:16 -04:00
ameye af0d4db5de Merge branch 'external-url-populate' of MERMS/MermsPanelReactJS into master 2025-10-04 10:02:39 +00:00
victorAnumudu 6f3dae4116 populated externl URL 2025-10-04 10:23:29 +01:00
CHIEFSOFT\ameye 13900793af added external URL 2025-10-03 20:34:31 -04:00
CHIEFSOFT\ameye 698c89edfc fix URL page 2025-10-03 15:46:03 -04:00
CHIEFSOFT\ameye 4f5a383c99 Url configuration 2025-10-03 15:16:16 -04:00
ameye a5b6a11880 Merge branch 'set-url-endpoint' of MERMS/MermsPanelReactJS into master 2025-10-03 18:30:58 +00:00
victorAnumudu 0f58da3dce added set external url endpoint 2025-10-03 17:36:10 +01:00
ameye 1101e80d91 Merge branch 'set-url' of MERMS/MermsPanelReactJS into master 2025-09-30 20:17:32 +00:00
victorAnumudu b39a7ab58c url display fixed 2025-09-30 20:58:27 +01:00
ameye ad90def3c9 Merge branch 'url-alphanumeric-validator' of MERMS/MermsPanelReactJS into master 2025-09-30 09:54:33 +00:00
CHIEFSOFT\ameye 737430bf04 text transform 2025-09-30 05:38:08 -04:00
CHIEFSOFT\ameye d371ada805 configure url page 2025-09-30 05:21:01 -04:00
CHIEFSOFT\ameye b77f1d6213 display fix 2025-09-28 22:43:39 -04:00
victorAnumudu 64085c6be5 added alphanumeric validation for url name 2025-09-28 06:58:21 +01:00
ameye 3f8b45b6d6 Merge branch 'validation-fix' of MERMS/MermsPanelReactJS into master 2025-09-23 12:06:58 +00:00
victorAnumudu 4b6c927efc fixed validation bug 2025-09-23 05:56:33 +01:00
CHIEFSOFT\ameye 8a8adcbbc7 url name 2025-09-22 19:26:28 -04:00
CHIEFSOFT\ameye 706baadb33 url_name 2025-09-22 19:18:07 -04:00
ameye 58128fdd96 Merge branch 'url-name-payload' of MERMS/MermsPanelReactJS into master 2025-09-22 16:33:02 +00:00
victorAnumudu 14e8b1b01d made url name a required payload 2025-09-22 17:30:49 +01:00
ameye 504bfbcae4 Merge branch 'url-length' of MERMS/MermsPanelReactJS into master 2025-09-22 15:51:46 +00:00
52 changed files with 2321 additions and 612 deletions
+3
View File
@@ -16,3 +16,6 @@ REACT_APP_TERMS_LINK='https://qa-www.mermsemr.com/terms'
# Inactivity timeout/logout AT 10MINS # Inactivity timeout/logout AT 10MINS
REACT_APP_TIMEOUT=600000 REACT_APP_TIMEOUT=600000
# show download button
REACT_APP_SHOW_DOWNLOAD=0
+3
View File
@@ -17,3 +17,6 @@ REACT_APP_TERMS_LINK='https://qa-www.mermsemr.com/terms'
# Inactivity timeout/logout AT 10MINS # Inactivity timeout/logout AT 10MINS
REACT_APP_TIMEOUT=600000 REACT_APP_TIMEOUT=600000
# show download button
REACT_APP_SHOW_DOWNLOAD=0
+4 -1
View File
@@ -14,4 +14,7 @@ REACT_APP_CONTACTS_LINK='https://www.mermsemr.com/contacts'
REACT_APP_TERMS_LINK='https://www.mermsemr.com/terms' REACT_APP_TERMS_LINK='https://www.mermsemr.com/terms'
# Inactivity timeout/logout AT 10MINS # Inactivity timeout/logout AT 10MINS
REACT_APP_TIMEOUT=600000 REACT_APP_TIMEOUT=600000
# show download button
REACT_APP_SHOW_DOWNLOAD=0
+7
View File
@@ -51,4 +51,11 @@ button{
.accordion-button, .accordion-button:not(.collapsed) { .accordion-button, .accordion-button:not(.collapsed) {
background-color: transparent!important; background-color: transparent!important;
}
@media (min-width: 992px) {
.modal-50 {
min-width: 50%;
max-width: 50%;
}
} }
+2
View File
@@ -26,6 +26,7 @@ import ProfileCompletePage from './views/ProfileCompletePage';
import SubscribePage from './views/Subscribe' import SubscribePage from './views/Subscribe'
import StartPage from "./views/StartPage"; import StartPage from "./views/StartPage";
import TrafficPage from "./views/TrafficPage"; import TrafficPage from "./views/TrafficPage";
import MyMediaPage from './views/MyMediaPage.jsx';
function AppRouters() { function AppRouters() {
return ( return (
@@ -52,6 +53,7 @@ function AppRouters() {
<Route path={siteLinks.profile_complete} element={<ProfileCompletePage/>}/> <Route path={siteLinks.profile_complete} element={<ProfileCompletePage/>}/>
<Route path={siteLinks.product} element={<ProductPage/>}/> <Route path={siteLinks.product} element={<ProductPage/>}/>
<Route path={siteLinks.reports} element={<ReportsPage/>}/> <Route path={siteLinks.reports} element={<ReportsPage/>}/>
<Route path={siteLinks.my_media} element={<MyMediaPage />}/>
<Route path={siteLinks.comments} element={<CommentsPage/>}/> <Route path={siteLinks.comments} element={<CommentsPage/>}/>
<Route path={siteLinks.contacts} element={<ContactsPage/>}/> <Route path={siteLinks.contacts} element={<ContactsPage/>}/>
<Route path={siteLinks.user} element={<UserPage/>}/> <Route path={siteLinks.user} element={<UserPage/>}/>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

+42
View File
@@ -0,0 +1,42 @@
import React from 'react'
import GoogleDownload from '../../assets/img/download/andriod.jpg'
import IOSDownload from '../../assets/img/download/apple.jpg'
export default function AuthFooter() {
return (
<div className='w-100'>
{Number(process.env.REACT_APP_SHOW_DOWNLOAD) == 100 &&
<>
<div className="row" style={{margin: '5px'}}>
<hr />
</div>
<div className="row" style={{marginTop: '20px'}}>
<div className="col-6">
<div className="app-store-icons-wrap text-center">
<a className="icon google"
href='#' >
<img src={IOSDownload} className='w-80 h-auto' alt='IOS Download' />
</a>
</div>
</div>
<div className="col-6">
<div className="app-store-icons-wrap text-center">
<a className="icon apple" href='#'>
<img src={GoogleDownload} className='w-80 h-auto' alt='IOS Download' />
</a>
</div>
</div>
</div>
</>
}
<div className="login-links">
<a href={process.env.REACT_APP_HOME_LINK}>Home</a>
<a href={process.env.REACT_APP_ABOUT_LINK}>About</a>
<a href={process.env.REACT_APP_CONTACTS_LINK}>Contact</a>
<a href={process.env.REACT_APP_TERMS_LINK}>Terms</a>
</div>
</div>
)
}
+5 -3
View File
@@ -8,6 +8,7 @@ import siteLinks from '../../links/siteLinks'
import { useMutation } from '@tanstack/react-query' import { useMutation } from '@tanstack/react-query'
import { recoverPWD } from '../../services/services'; import { recoverPWD } from '../../services/services';
import getImage from '../../utils/getImage'; import getImage from '../../utils/getImage';
import AuthFooter from './AuthFooter';
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
username: Yup.string() username: Yup.string()
@@ -17,7 +18,7 @@ const validationSchema = Yup.object().shape({
// "Invalid email format" // "Invalid email format"
// ) // )
.min(3, "Minimum 3 characters") .min(3, "Minimum 3 characters")
.max(50, "Maximum 50 characters") .max(25, "Maximum 25 characters")
.required("Email is required"), .required("Email is required"),
}) })
@@ -49,7 +50,7 @@ export default function Forgetpwd2() {
<div className="row no-gutters justify-content-center"> <div className="row no-gutters justify-content-center">
<div className="col-11 col-sm-6 col-lg-5 col-xxl-4 align-self-center order-2 order-sm-1h" style={{maxWidth: '520px'}}> <div className="col-11 col-sm-6 col-lg-5 col-xxl-4 align-self-center order-2 order-sm-1h" style={{maxWidth: '520px'}}>
<div className="mt-5 d-flex"> <div className="mt-5 d-flex">
<div className="bg-white register p-5"> <div className="bg-white register px-5 pt-5 pb-3">
<h1 className="mb-2">{process.env.REACT_APP_PANEL_NAME}</h1> <h1 className="mb-2">{process.env.REACT_APP_PANEL_NAME}</h1>
{!mutation.isSuccess && <p>Please enter your username.</p>} {!mutation.isSuccess && <p>Please enter your username.</p>}
<Formik <Formik
@@ -65,7 +66,7 @@ export default function Forgetpwd2() {
<> <>
<div className="col-12"> <div className="col-12">
<div className="form-group"> <div className="form-group">
<label className={`text-black fw-bold control-label ${(props.errors.username && props.touched.username) && 'text-danger'}`}>Username*</label> <label className={`text-black fw-bold control-label`}>Username* <span className='text-danger' style={{fontSize: '12px'}}>{(props.errors.username && props.touched.username) && props.errors.username}</span></label>
<input type="text" name='username' className="form-control" placeholder="Username" value={props.values.username} onChange={props.handleChange} /> <input type="text" name='username' className="form-control" placeholder="Username" value={props.values.username} onChange={props.handleChange} />
</div> </div>
</div> </div>
@@ -97,6 +98,7 @@ export default function Forgetpwd2() {
); );
}} }}
</Formik> </Formik>
<AuthFooter />
</div> </div>
</div> </div>
</div> </div>
+4 -33
View File
@@ -9,8 +9,7 @@ import siteLinks from '../../links/siteLinks'
import { loginUser } from '../../services/services' import { loginUser } from '../../services/services'
import { updateUserDetails } from '../../store/UserDetails' import { updateUserDetails } from '../../store/UserDetails'
import GoogleDownload from '../../assets/img/download/andriod.jpg' import AuthFooter from './AuthFooter'
import IOSDownload from '../../assets/img/download/apple.jpg'
export default function Login() { export default function Login() {
@@ -47,12 +46,12 @@ export default function Login() {
console.log(error) console.log(error)
}, },
onSuccess: (res) => { onSuccess: (res) => {
if(res?.data?.error_message){ if(res?.data && res?.data?.error_message){
throw({message: res?.data?.error_message}) throw({message: res?.data?.error_message})
} }
const {token, room, uid} = res?.data const {token, room, uid} = res?.data
if(!token || !room){ if(!token || !room){
throw({message: 'something went wrong, try again!'}) throw({message: 'Unable to complete your login, Please try again!'})
} }
localStorage.setItem('token', token) localStorage.setItem('token', token)
localStorage.setItem('room', room) localStorage.setItem('room', room)
@@ -133,35 +132,7 @@ export default function Login() {
</div> </div>
</div> </div>
</form> </form>
<div className="row" style={{margin: '5px'}}> <AuthFooter />
<hr />
</div>
<div className="row" style={{marginTop: '20px'}}>
<div className="col-6">
<div className="app-store-icons-wrap text-center">
<a className="icon google"
href='#' >
<img src={IOSDownload} className='w-80 h-auto' alt='IOS Download' />
</a>
</div>
</div>
<div className="col-6">
<div className="app-store-icons-wrap text-center">
<a className="icon apple" href='#'>
<img src={GoogleDownload} className='w-80 h-auto' alt='IOS Download' />
</a>
</div>
</div>
</div>
<div className="login-links">
<a href={process.env.REACT_APP_HOME_LINK}>Home</a>
<a href={process.env.REACT_APP_ABOUT_LINK}>About</a>
<a href={process.env.REACT_APP_CONTACTS_LINK}>Contact</a>
<a href={process.env.REACT_APP_TERMS_LINK}>Terms</a>
</div>
</div> </div>
</div> </div>
</div> </div>
+14 -9
View File
@@ -9,6 +9,7 @@ import siteLinks from '../../links/siteLinks'
import {useMutation} from '@tanstack/react-query'; import {useMutation} from '@tanstack/react-query';
import {signUpUser} from '../../services/services'; import {signUpUser} from '../../services/services';
import getImage from '../../utils/getImage'; import getImage from '../../utils/getImage';
import AuthFooter from './AuthFooter';
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
email: Yup.string() email: Yup.string()
@@ -64,9 +65,9 @@ export default function Signup2() {
<div className="col-11 col-sm-6 col-lg-5 col-xxl-4 align-self-center order-2 order-sm-1" <div className="col-11 col-sm-6 col-lg-5 col-xxl-4 align-self-center order-2 order-sm-1"
style={{maxWidth: '520px'}}> style={{maxWidth: '520px'}}>
<div className="mt-5 d-flex"> <div className="mt-5 d-flex">
<div className="bg-white register p-5"> <div className="bg-white register px-5 pt-5 pb-3">
<h1 className="mb-2">{process.env.REACT_APP_PANEL_NAME}</h1> <h1 className="mb-2">{process.env.REACT_APP_PANEL_NAME}</h1>
<p>Welcome, Please create your account.</p> <p>Welcome, please create your account.</p>
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
validationSchema={validationSchema} validationSchema={validationSchema}
@@ -173,19 +174,23 @@ export default function Signup2() {
} }
<div className="col-12 mt-3"> <div className="col-12 mt-3">
<p>Already have an account ?<Link <p>
to={siteLinks.login}> <span style={{paddingLeft: '5px' , fontWeight: 'bolder'}}>Already have an account? </span>
<button <Link
className="btn btn-warning text-uppercase"> to={siteLinks.login}>
Sign In <button
</button> className="btn btn-warning text-uppercase">
</Link></p> Sign In
</button>
</Link>
</p>
</div> </div>
</div> </div>
</Form> </Form>
); );
}} }}
</Formik> </Formik>
<AuthFooter />
</div> </div>
</div> </div>
</div> </div>
+2 -1
View File
@@ -48,7 +48,8 @@ export default function Calendar(){
} }
const {data, isFetching, isError, error} = useQuery({ const {data, isFetching, isError, error} = useQuery({
queryKey: queryKeys.calendar_events, queryKey: queryKeys.calendar_events,
queryFn: () => getCalendarEvents(reqData) queryFn: () => getCalendarEvents(reqData),
staleTime: 0
}) })
const receievedEvents = data?.data const receievedEvents = data?.data
+1 -1
View File
@@ -41,7 +41,7 @@ export default function SocketIOContextProvider({children}) {
socket.on(socketOnEvents.receive_message, (data) => { socket.on(socketOnEvents.receive_message, (data) => {
// setSocketMsgReceived(data.message); // setSocketMsgReceived(data.message);
// dispatch(tableReload({type:'CHATMESSAGELIST'})) // dispatches to update chat message sending from owner to worker and vice versa // dispatch(tableReload({type:'CHATMESSAGELIST'})) // dispatches to update chat message sending from owner to worker and vice versa
console.log('DATA', data) console.log('SOCKET RECEIVED DATA *** ', data)
queryClient.refetchQueries({ queryClient.refetchQueries({
queryKey: [...queryKeys.recentAction], queryKey: [...queryKeys.recentAction],
// type: 'active', // type: 'active',
+1 -1
View File
@@ -59,7 +59,7 @@ export default function DashPayments() {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{payments.length > 0 ? {payments && payments?.length > 0 ?
payments.map((item, index) => { payments.map((item, index) => {
return ( return (
<tr key={index}> <tr key={index}>
+20 -15
View File
@@ -41,22 +41,27 @@ export default function Products() {
</div> </div>
: :
<div className="row m-b-20"> <div className="row m-b-20">
{products && products.map((product, index) => ( {products && products.map((product, index) =>
<div key={product.uid+index} className={`col-xxs-6 col-xl-4 col-xxl-6 mb-2 mb-xxl-0`}> {
<Link to={productPath(product?.product_id)} > // let productName = product?.name?.length > 14 ? product?.name?.substring(0, 14) + '...' : product?.name
<div className={`d-flex align-items-center extraProductCard ${product?.icon_style}`} style={{borderColor:'black', borderWidth: '2px'}} > return (
<div className="icon-container img-icon m-r-20 bg-light-gray rounded"> <div key={product.uid+index} className={`col-12 col-lg-6 mb-2 mb-xxl-0`}>
<i className={`fa ${product?.product_icon} text-primary`}></i> <Link to={productPath(product?.product_id)} >
<div className={`d-flex align-items-center extraProductCard ${product?.icon_style}`} style={{borderColor:'black', borderWidth: '2px'}} >
<div className="icon-container img-icon m-r-20 bg-light-gray rounded">
<i className={`fa ${product?.product_icon} text-primary`}></i>
</div>
<div className="report-details overflow-hidden">
<p><span style={{fontWeight: 'bolder', color: '#00557A'}}>{product?.status_text}</span></p>
<h4><span className='w-100 d-inline-block text-truncate' style={{paddingLeft: '10px'}}>{product?.name}</span></h4>
</div>
</div> </div>
<div className="report-details"> </Link>
<p><span style={{fontWeight: 'bolder', color: '#00557A'}}>{product?.status_text}</span></p>
<h4><span style={{paddingLeft: '10px'}}>{product?.name}</span></h4> </div>
</div> )
</div> }
</Link> )}
</div>
))}
</div> </div>
} }
<div className="apexchart-wrapper"> <div className="apexchart-wrapper">
+1 -1
View File
@@ -31,7 +31,7 @@ export default function ProductsURL() {
{/*<a className="btn btn-xs" href="#!">Export <i className="zmdi zmdi-download pl-1"></i> </a>*/} {/*<a className="btn btn-xs" href="#!">Export <i className="zmdi zmdi-download pl-1"></i> </a>*/}
</div> </div>
</div> </div>
<div className="card-body scrollbar scroll_dark pt-0" style={{maxHeight: '350px'}}> <div className="overflow-y-auto card-body scrollbar scroll_dark pt-0" style={{maxHeight: '350px'}}>
<div className="datatable-wrapper table-responsive"> <div className="datatable-wrapper table-responsive">
{isFetching ? {isFetching ?
<> <>
+2 -2
View File
@@ -77,7 +77,7 @@ export default function RecentActions() {
<h4>{dataAction?.data?.completed}</h4> <h4>{dataAction?.data?.completed}</h4>
</div> </div>
</div> </div>
<div className="table-responsive m-t-20"> <div className="overflow-y-auto table-responsive m-t-20">
<table id="datatable-buttons" className="table"> <table id="datatable-buttons" className="table">
<thead> <thead>
<tr> <tr>
@@ -94,7 +94,7 @@ export default function RecentActions() {
let text = action?.status == '5' ? 'completed' : action?.status == '3' ? 'verifying' : action?.status == '0' ? 'processing' : 'processing' let text = action?.status == '5' ? 'completed' : action?.status == '3' ? 'verifying' : action?.status == '0' ? 'processing' : 'processing'
return ( return (
<tr key={index}> <tr key={index}>
<td>{action?.id}</td> <td>{(action?.id).toString().slice(-4)}</td>
<td>{action?.action_label}</td> <td>{action?.action_label}</td>
<td>{new Date(action?.added).toDateString()}</td> <td>{new Date(action?.added).toDateString()}</td>
<td> <td>
@@ -11,7 +11,7 @@ export default function UserFooter(){
<p>&copy; Copyright {year}. All rights reserved.</p> <p>&copy; Copyright {year}. All rights reserved.</p>
</div> </div>
<div className="col col-sm-6 ml-sm-auto text-center text-sm-right"> <div className="col col-sm-6 ml-sm-auto text-center text-sm-right">
<p>A division of <i className="fa fa-key text-danger mx-1"></i> autoMedSys A.I.</p> <p>A division of autoMedSys A.I.</p>
</div> </div>
</div> </div>
</footer> </footer>
@@ -7,7 +7,7 @@ import siteLinks from "../../../links/siteLinks";
export default function UserHeader(){ export default function UserHeader(){
const { userDetails } = useSelector((state) => state?.userDetails); // CHECKS IF USER Details are avaliable, to determine if user is active const { userDetails } = useSelector((state) => state?.userDetails); // USER Details
const nav_menu = useRef(null) const nav_menu = useRef(null)
@@ -74,10 +74,15 @@ export default function UserHeader(){
<ul className="navbar-nav nav-right ml-auto"> <ul className="navbar-nav nav-right ml-auto">
<li className="nav-item user-profile"> <li className="nav-item user-profile">
<a onClick={toggleMenu} className="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdow"> <a onClick={toggleMenu} className="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdow">
<img src={getImage('profile-pic-circle.png')} alt="avtar-img" /> <img
src={userDetails?.picture ? userDetails?.picture : getImage('profile-pic-circle.png')}
// src={getImage('profile-pic-circle.png')}
alt="avtar-img"
/>
<span className="bg-success user-status"></span> <span className="bg-success user-status"></span>
</a> </a>
<div ref={nav_menu} onClick={toggleMenu} className="dropdown-menu animated fadeIn"> <div ref={nav_menu} onClick={toggleMenu} className="dropdown-menu animated fadeIn">
<div className="position-fixed" style={{top: '0px', left: '0px', right: '0px', bottom: '0px'}}></div>
<div className="bg-gradient px-4 py-3"> <div className="bg-gradient px-4 py-3">
<div className="d-flex align-items-center justify-content-between"> <div className="d-flex align-items-center justify-content-between">
<div className="mr-1"> <div className="mr-1">
@@ -48,6 +48,7 @@ export default function UserMenu() {
</div> </div>
</Link> </Link>
<ul id="collapseTwo" className="collapse" aria-labelledby="headingTwo" data-bs-parent="#sidebarNav"> <ul id="collapseTwo" className="collapse" aria-labelledby="headingTwo" data-bs-parent="#sidebarNav">
<li className={`${pathname == siteLinks.my_media ? 'active' : ''}`}><Link to={siteLinks.my_media}>Files and Media</Link></li>
<li className={`${pathname == siteLinks.subscription ? 'active' : ''}`}><Link to={siteLinks.subscription}>Subscription</Link></li> <li className={`${pathname == siteLinks.subscription ? 'active' : ''}`}><Link to={siteLinks.subscription}>Subscription</Link></li>
<li className={`${pathname == siteLinks.settings ? 'active' : ''}`}><Link to={siteLinks.settings}>Settings</Link></li> <li className={`${pathname == siteLinks.settings ? 'active' : ''}`}><Link to={siteLinks.settings}>Settings</Link></li>
</ul> </ul>
+248
View File
@@ -0,0 +1,248 @@
import React from "react";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
import {useState, useRef} from "react";
import {useQuery, useMutation, useQueryClient} from "@tanstack/react-query";
import queryKeys from "../../services/queryKeys";
import {getMediaFileList, uploadFile} from "../../services/services";
import getImage from "../../utils/getImage";
export default function MyMedia() {
const queryClient = useQueryClient()
const basePath = process.env.REACT_APP_MAIN_API
const [selectedFile, setSelectedFile] = useState(null);
const [message, setMessage] = useState('');
const [imageLink, setImageLink] = useState('')
console.log('imageLink', imageLink)
// Function to handle file selection
const handleFileChange = (event) => {
setSelectedFile(event.target.files[0]);
setMessage('');
};
// Function to handle the upload to the API
const _handleUpload = async () => {
if (!selectedFile) {
setMessage('Please select a file first!');
return;
}
const formData = new FormData();
// 'file' is the field name that your API endpoint expects
formData.append('file', selectedFile);
formData.append("member_uid", localStorage.getItem('uid'));
formData.append("token", localStorage.getItem('token'));
try {
// Replace with your actual API endpoint URL
const apiEndpoint = basePath + '/upload/webfiles';
const response = await fetch(apiEndpoint, {
method: 'POST',
// The browser automatically sets the 'Content-Type' header to
// 'multipart/form-data' when you provide a FormData object as the body,
// which is required for file uploads.
body: formData,
});
if (response.ok) {
const result = await response.json();
setMessage('File uploaded successfully!');
console.log('Success:', result);
} else {
setMessage('Upload failed.');
console.error('Upload failed:', response.statusText);
}
} catch (error) {
setMessage('An error occurred during the upload.');
console.error('Error:', error);
}
}
const uploadFileMutation = useMutation({
mutationFn: (fields) => {
if (!fields.file) {
throw({message: 'Please select a file first!'})
}
return uploadFile(fields)
},
onSuccess: (res) => {
// console.log('res', res.data)
// if(res.data.resultCode != '0' || !res?.data?.pending_uid){
// throw({message: res?.data?.resultDescription})
// }
setSelectedFile(null)
queryClient.refetchQueries({
queryKey: [...queryKeys.my_files],
// type: 'active',
// exact: true,
})
},
onSettled: () => {
setTimeout(() => {
uploadFileMutation.reset()
}, 3000)
}
})
const handleUpload = () => {
let reqData = {
token: localStorage.getItem("token"), // USER TOKEN
member_uid: localStorage.getItem("uid"), // USER UID
file: selectedFile
};
// console.log(reqData)
uploadFileMutation.mutate(reqData)
}
const {data, isFetching, isError, error} = useQuery({
queryKey: queryKeys.my_files,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid') // USER UID
}
return getMediaFileList(reqData)
}
})
const mediaFileList = data?.data
//debugger;
return (
<>
<BreadcrumbComBS title='Files' paths={['Dashboard', 'Files']}/>
<div className="row">
<div className="col-xl-6 col-xxl-4 m-b-30">
<div className="card card-statistics mb-0 widget-support-list">
<div className="card-header">
<div className="card-heading">
<h4 className="card-title">Upload File</h4>
</div>
</div>
<div className="card-body pl-0 pr-0 scrollbar scroll_dark">
<div className="widget-text">
<div>
<input className="form-control form-control-sm" type="file"
onChange={handleFileChange}/>
{selectedFile && (
<div>
<h4>Selected File Details:</h4>
<ul>
<li>Name: {selectedFile.name}</li>
<li>Type: {selectedFile.type}</li>
<li>Size: {selectedFile.size} bytes</li>
</ul>
<div style={{width: '100%', textAlign: 'right'}}>
<button
className="btn btn-square btn-inverse-light btn-xs d-inline-block mt-2 mb-0"
onClick={handleUpload}
disabled={!selectedFile || uploadFileMutation.isPending || uploadFileMutation.isSuccess}>
Upload
</button>
</div>
<>
{/* {message && <p>{message}</p>} */}
<p>{uploadFileMutation.isPending ? 'uploading...' : uploadFileMutation.isError ? uploadFileMutation?.error?.message : uploadFileMutation.isSuccess ? 'File Uploaded' : ''}</p>
</>
</div>
)}
</div>
</div>
</div>
</div>
</div>
<div className="col-xl-6 col-xxl-4 m-b-30">
<div className="card card-statistics h-100 mb-0 widget-downloads-list" style={{}}>
<div className="card-header d-flex justify-content-between">
<div className="card-heading">
<div className="card-heading">
<h4 className="card-title">Files List</h4>
</div>
</div>
</div>
{isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div className="card-body scrollbar scroll_dark"
style={{minHeight: '400px', maxHeight: '500px', overflowY: 'auto'}}>
{mediaFileList && mediaFileList?.file_list && mediaFileList?.file_list.map((item, index) => {
const file_url = (mediaFileList?.media_server + "/" + item?.file_group + "/" + item?.file_uid + "/" + item.filename).toLowerCase();
const avtarImage =
item?.file_type === undefined
? "icons/01.png"
: "icons/" + item.file_type + ".png";
return (<div key={index}
className={`widget-text ${imageLink == file_url && 'bg-light'}`}>
<div className={`media align-items-center`}
onClick={() => setImageLink(file_url)} style={{cursor: 'pointer'}}>
<img src={getImage(avtarImage)}
// src={`assets/img/file-icon/${item.file_type}.png`}
className="img-fluid"
alt={`${item.file_type}`}/>
<div className="media-body">
<h4 className="mb-0 ml-3">{item.filename}</h4>
</div>
<div>
<a href={`${file_url}`} target='_blank'
className="btn btn-icon btn-round btn-outline-success">
<i className="ti ti-download"></i>
</a>
<a href=""
className="btn btn-icon btn-round btn-outline-danger ml-2">
<i className="ti ti-close"></i>
</a>
</div>
</div>
</div>)
})
}
</div>
}
</div>
</div>
<div className="col-xl-6 col-xxl-4 m-b-30">
<div className="card card-statistics h-100 mb-0 widget-branches-list">
<div className="card-header">
<div className="card-heading">
<h4 className="card-title">Preview</h4>
</div>
</div>
<div
className="card-body d-flex justify-content-center align-items-center pl-0 pr-0 scrollbar scroll_dark">
{imageLink &&
<img className="w-100 h-auto" src={imageLink} alt='file-image'/>
}
</div>
<div className="card-header d-flex justify-content-between align-items-center">
{imageLink &&
<>
<p>0 x 0 px</p>
<p>size: 0 bytes</p>
</>
}
</div>
</div>
</div>
</div>
</>
)
}
+23 -6
View File
@@ -1,21 +1,26 @@
import React, { useMemo, useRef, useState } from "react"; import React, {useEffect, useMemo, useRef, useState} from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import getImage from "../../utils/getImage"; import getImage from "../../utils/getImage";
import { useMutation, useQuery } from "@tanstack/react-query"; import { useMutation, useQuery } from "@tanstack/react-query";
import { productRefreshSite, getSettingsData } from "../../services/services"; import { productRefreshSite, getSettingsData } from "../../services/services";
import Settings from "./settingsTab/Settings"; import Settings from "./settingsTab/Settings";
import queryKeys from "../../services/queryKeys"; import queryKeys from "../../services/queryKeys";
import {SocketContextValues} from "../context/SocketIOContext";
export default function ProductActive({productData}){ export default function ProductActive({productData}){
const {joinRoom} = SocketContextValues() // Destructures values from socket context
const iframe = useRef() const iframe = useRef()
const [refreshMsg, setRefreshMsg] = useState('')
const refresh = useMutation({ const refresh = useMutation({
mutationFn: (fields) => { mutationFn: (fields) => {
return productRefreshSite(fields) return productRefreshSite(fields)
}, },
onSuccess: (res) => { onSuccess: (res) => {
setRefreshMsg(res?.data?.message)
setTimeout(()=>{setRefreshMsg('')},3000)
iframe.current.src += '' iframe.current.src += ''
} }
}) })
@@ -31,6 +36,13 @@ export default function ProductActive({productData}){
} }
let externalUrl= 'https://'+productData?.internal_url let externalUrl= 'https://'+productData?.internal_url
const productSubUID = productData.subscription_uid;
useEffect(() => {
const provision_room = "PROVISION_"+productSubUID;
console.log("JOINING ROOM ON ACTIVE *** ", provision_room);
joinRoom(provision_room); // provision subscription room
}, [])
return( return(
<> <>
{/*<BreadcrumbComBS title='Active Product Name' paths={['Dashboard', 'Product']} />*/} {/*<BreadcrumbComBS title='Active Product Name' paths={['Dashboard', 'Product']} />*/}
@@ -54,12 +66,17 @@ export default function ProductActive({productData}){
</button> </button>
</div> </div>
</div> </div>
<div className="card-body"> <div className="card-body" style={{minHeight: '600px', maxHeight: '600px'}}>
<iframe ref={iframe} style={{borderWidth: '0px'}} src={externalUrl} width="100%" height="600" title={externalUrl}></iframe> <iframe ref={iframe} style={{borderWidth: '0px'}} src={externalUrl} width="100%" height="600" title={externalUrl}></iframe>
</div> </div>
<div className="p-4 ml-auto"> <div className="p-4 ml-auto">
<button type="button" onClick={handleRefresh} className="btn btn-primary">{refresh.isPending ? 'Loading...' : 'Refresh Site'} <div className="d-flex justify-end gap-3">
</button> {refreshMsg &&
<p className="text-success text-center">{refreshMsg}</p>
}
<button type="button" onClick={handleRefresh} className="btn btn-primary">{refresh.isPending ? 'Initiating...' : 'Rebuild Site'}
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -70,7 +87,7 @@ export default function ProductActive({productData}){
<h4 className="card-title"> Site Settings </h4> <h4 className="card-title"> Site Settings </h4>
</div> </div>
</div> </div>
<div className="card-body"> <div className="card-body" style={{minHeight: '680px'}}>
<Settings productData={productData} /> <Settings productData={productData} />
</div> </div>
</div> </div>
+70 -63
View File
@@ -1,39 +1,41 @@
import { useEffect } from "react"; import {useEffect} from "react";
import { useQuery } from "@tanstack/react-query"; import {useQuery} from "@tanstack/react-query";
import queryKeys from "../../services/queryKeys"; import queryKeys from "../../services/queryKeys";
import { productProvision } from "../../services/services"; import {productProvision} from "../../services/services";
import getImage from "../../utils/getImage"; import getImage from "../../utils/getImage";
import { SocketContextValues } from "../context/SocketIOContext"; import {SocketContextValues} from "../context/SocketIOContext";
export default function ProductProvision(props){ export default function ProductProvision(props) {
const {joinRoom} = SocketContextValues() // Destructures values from socket context const {joinRoom} = SocketContextValues() // Destructures values from socket context
const productTitle = props?.productData?.title; const productTitle = props?.productData?.title;
const productDescription = props?.productData?.description; const productDescription = props?.productData?.description;
const productID = props?.productData?.product_id const productID = props?.productData?.product_id
const productUID = props?.productData?.product_uid const productUID = props?.productData?.product_uid
const productSubUID = props?.productData?.product_subscription_uid const productSubUID = props?.productData?.product_subscription_uid
const reqData = { const reqData = {
product_id : productID, product_id: productID,
product_subscription_uid: productSubUID product_subscription_uid: productSubUID
} }
const {data:provision, isFetching, isError, error} = useQuery({ const {data: provision, isFetching, isError, error} = useQuery({
queryKey: queryKeys.myproduct_provision, queryKey: queryKeys.myproduct_provision,
queryFn: () => productProvision(reqData) queryFn: () => productProvision(reqData)
}) })
const provisionData = provision?.data const provisionData = provision?.data
useEffect(()=>{ useEffect(() => {
joinRoom(productSubUID); // provision subscription room const provision_room = "PROVISION_" + productSubUID;
},[]) console.log("JOINING ROOM ON START *** ", provision_room);
joinRoom(provision_room); // provision subscription room
}, [])
return ( return (
<> <>
{isFetching ? {isFetching ?
<> <>
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12">
@@ -42,82 +44,87 @@ export default function ProductProvision(props){
</div> </div>
</> </>
: isError ? : isError ?
<div className="row">
<div className="col-12">
<p className='text-danger'>{error.message}</p>
</div>
</div>
:
<>
<div className="row"> <div className="row">
<div className="col-md-12"> <div className="col-12">
<div className="card card-statistics"> <p className='text-danger'>{error.message}</p>
<div className="card-header"> </div>
</div>
:
<>
<div className="row">
<div className="col-md-12">
<div className="card card-statistics">
<div className="card-header">
<div className="card-heading"> <div className="card-heading">
<h4 className="card-title">Creating - {productTitle} </h4> <h4 className="card-title">Creating - {productTitle} </h4>
</div> </div>
</div> </div>
<div className="card-body"> <div className="card-body">
<div className="progress"> <div className="progress">
<div className="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" <div className="progress-bar progress-bar-striped progress-bar-animated"
aria-valuenow={`${provisionData?.percent_completed}%`} aria-valuemin="0" aria-valuemax="100" style={{width:`${provisionData?.percent_completed}%`}} ></div> role="progressbar"
aria-valuenow={`${provisionData?.percent_completed}%`}
aria-valuemin="0" aria-valuemax="100"
style={{width: `${provisionData?.percent_completed}%`}}></div>
</div> </div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> <div className="row">
<div className="row"> <div className="col-md-12">
<div className="col-md-12">
</div>
</div> </div>
</div> <div className="row">
<div className="row"> <div className="col-lg-6">
<div className="col-lg-6">
<div className="card card-statistics"> <div className="card card-statistics">
<div className="card-header"> <div className="card-header">
<div className="card-heading"> <div className="card-heading">
<h4 className="card-title">Progress Information</h4> <h4 className="card-title">Progress Information</h4>
</div>
</div> </div>
</div> <div className="card-body">
<div className="card-body"> <div className="table-responsive">
<div className="table-responsive"> <table className="table table-info mb-0">
<table className="table table-info mb-0"> <thead>
<thead> <tr>
<tr> <th scope="col" style={{width: '10px'}}>#</th>
<th scope="col" style={{width: '10px'}}>#</th> <th scope="col">Action</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
{provisionData?.activities?.map(item => (
<tr key={item.id}>
<th scope="row">{item.id}</th>
<td>{item.action}</td>
</tr> </tr>
</thead>
<tbody>
{provisionData?.activities?.map(item => (
<tr key={item.id}>
<th scope="row">{item.id}</th>
<td>{item.action}</td>
</tr>
))} ))}
</tbody> </tbody>
</table> </table>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div className="col-lg-6"> <div className="col-lg-6">
<div className="card card-statistics "> <div className="card card-statistics ">
<h4 className="card-title" style={{padding:'10px'}}>Started creating your selection</h4> <h4 className="card-title" style={{padding: '10px'}}>Started creating your
<img className="card-img-top" src={getImage('widget/working.jpg')} alt="Card image cap" /> selection</h4>
{/* <div className="card-body"> <img className="card-img-top" src={getImage('widget/working.jpg')}
alt="Card image cap"/>
{/* <div className="card-body">
<div className="" dangerouslySetInnerHTML={{__html: productDescription}}/> <div className="" dangerouslySetInnerHTML={{__html: productDescription}}/>
</div> */} </div> */}
</div> </div>
</div>
</div> </div>
</div> </>
</>
} }
</> </>
) )
+1 -1
View File
@@ -126,7 +126,7 @@ export default function ProductStart(props){
</div> </div>
{/* Vertical Center Modal */} {/* Vertical Center Modal */}
<div ref={modalRef} className="modal fade" id="verticalCenter" tabIndex="-1" role="dialog" aria-hidden="true"> <div ref={modalRef} className="modal fade" id="verticalCenter" tabIndex="-1" role="dialog" aria-hidden="false">
<div className="modal-dialog modal-dialog-centered" role="document"> <div className="modal-dialog modal-dialog-centered" role="document">
<div className="modal-content"> <div className="modal-content">
<div className="modal-header"> <div className="modal-header">
@@ -1,6 +1,179 @@
const ColorStyleConfigure =()=>{ import React, {memo} from 'react'
import getImage from "../../../utils/getImage";
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
import queryKeys from '../../../services/queryKeys';
import {
getProductcolorStyleCss,
activateTemplate,
getProductColorStyles,
activateColorStyle
} from '../../../services/services';
import {Link} from "react-router-dom";
import siteLinks from "../../../links/siteLinks";
return <>COLOR CONFIG</> const ColorStyleConfigure = memo(({name = 'Full Name', data, productData}) => {
}
export default ColorStyleConfigure const queryClient = useQueryClient()
const {data: colorStyleCss, isFetching, isError, error} = useQuery({
queryKey: queryKeys.productcolorStyleCss,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id
}
return getProductColorStyles(reqData)
},
staleTime: 0
})
const templateResponse = colorStyleCss?.data
const currentColorUID = templateResponse?.current_colorstyle_uid
const color_styles = templateResponse?.color_styles
const custom_template_name = templateResponse?.custom_template_name
// console.log('data Template', templateResponse)
console.log("Page data == ", data)
const handleActivateTemplate = useMutation({
mutationFn: (fields) => {
return activateColorStyle(fields)
},
onSuccess: (res) => {
if (res?.data?.resultCode != '0') {
throw new Error(res.data.resultDescription)
}
queryClient.refetchQueries({ // refetches productProvision API call
queryKey: [...queryKeys.settingsData],
})
},
onSettled: () => {
setTimeout(() => {
handleActivateTemplate.reset()
}, 3000)
}
})
const handleSubmit = (style_uid) => {
const reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id,
color_style_uid: style_uid
}
// console.log(reqData)
handleActivateTemplate.mutate(reqData)
}
if (custom_template_name && custom_template_name != '') {
// This implies we have a custom template , just return here
return <>
<div className='col-12'>
<div
className="rounded-2 d-flex flex-column justify-content-between align-items-center"
style={{backgroundColor: '#F2FAF7'}}>
<h4 className='p-4 text-black'
style={{marginBottom: '20px'}}>Custom Product Template.</h4>
<img className='' style={{width: '200px'}}
src={getImage('custom-template.png')}
alt='mail-alert'/>
<h4 className='p-4 text-black'
style={{marginTop: '20px'}}>This product is using a custom template named <span
style={{color: 'darkred'}}>&ldquo;{custom_template_name}&rdquo;</span> .</h4>
</div>
</div>
</>
}
return (
<div className="page-account-form">
<div className="p-0">
{isFetching ?
<>
<div className="row">
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</div>
</>
: isError ?
<div className="row">
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
</div>
:
<div className="row overflow-y-auto" style={{maxHeight: '550px'}}>
<>
{!color_styles?.length ?
<p>No data Found</p>
:
color_styles.map(color_style => (
<div key={color_style.color_style_uid} className="col-xl-6 col-sm-6">
<div className="card card-statistics">
<div className="card-body" style={{
backgroundColor: `#${color_style.color_code}`,
opacity: '0.85',
borderRadius: '10px'
}}>
<div className="text-center p-2">
{/*<div className="mb-2">*/}
{/* <img src={getImage('file-icon/svg.png')}*/}
{/* alt={template.title}/>*/}
{/*</div>*/}
<h4 className="mb-0">{color_style.title}</h4>
{currentColorUID === color_style.color_style_uid ?
<button className="btn btn-light"
disabled={true}>Active</button>
:
<button
onClick={() => handleSubmit(color_style.color_style_uid)}
className="btn btn-primary">Select</button>
}
</div>
</div>
</div>
</div>
))
}
{/* {Object.entries(data)?.map(([key, value]) => (
<div key={key} className="col-xl-6 col-sm-6">
<div className="card card-statistics">
<div className="card-body">
<div className="text-center p-2">
<div className="mb-2">
<img src={getImage(value.banner)} alt={value.title} />
</div>
<h4 className="mb-0">{value.title}</h4>
<a href="javascript:void(0)" className="btn btn-light">Activate</a>
</div>
</div>
</div>
</div>
))} */}
</>
<div className="col-12">
<>
{handleActivateTemplate.isPending ?
<p className={'text-center '}>loading...</p>
:
handleActivateTemplate.isError ?
<p className={'text-center text-danger'}>{handleActivateTemplate.error.message}</p>
:
handleActivateTemplate.isSuccess ?
<p className={'text-center text-success'}>Templated activated
successfully</p>
:
null
}
</>
</div>
</div>
}
</div>
</div>
)
}
)
export default ColorStyleConfigure
@@ -112,15 +112,15 @@ const GeneralTab = memo(({
<> <>
{isCustom === true ? {isCustom === true ?
<> <>
{(tabKey === 'template_tab') && <SiteTemplateSelector name={name} data={sortedData} {(tabKey === 'template_tab') &&
isCustom={isCustom} <SiteTemplateSelector name={name} data={sortedData} isCustom={isCustom}
productData={productData}/>} productData={productData}/>}
{(tabKey === 'url_config_tab') && <URLConfiguration name={name} data={sortedData} {(tabKey === 'url_config_tab') &&
isCustom={isCustom} <URLConfiguration name={name} data={sortedData} isCustom={isCustom}
productData={productData}/>} productData={productData}/>}
{(tabKey === 'color_scheme_tab') && <ColorStyleConfigure name={name} data={sortedData} {(tabKey === 'color_scheme_tab') &&
isCustom={isCustom} <ColorStyleConfigure name={name} data={sortedData} isCustom={isCustom}
productData={productData}/>} productData={productData}/>}
</> </>
: :
<div className="page-account-form"> <div className="page-account-form">
+26 -6
View File
@@ -5,6 +5,7 @@ import queryKeys from '../../../services/queryKeys';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import sortObjectByListOrder from '../../../helpers/sortObjectByListOrder' import sortObjectByListOrder from '../../../helpers/sortObjectByListOrder'
import TemplateConfigure from './TemplateConfigure';
const Settings = memo(({productData}) => { const Settings = memo(({productData}) => {
@@ -24,7 +25,7 @@ const Settings = memo(({productData}) => {
}) })
const settingsConfig = configData?.data?.settings_items const settingsConfig = configData?.data?.settings_items
// console.log('CONFIG DATA...', settingsConfig) // console.log('CONFIG DATA...', settingsConfig)
// console.log('configData', configData?.data?.subscription_template)
const [fieldsChanged, setFieldsChanged] = useState(false) const [fieldsChanged, setFieldsChanged] = useState(false)
@@ -81,12 +82,12 @@ const Settings = memo(({productData}) => {
</div> </div>
</div> </div>
: :
<div className="tab tab-vertical"> <div className="d-flex">
<ul className="nav nav-tabs" role="tablist"> <ul className="bg-body-secondary flex-column nav" role="tablist" style={{width: '25%', minHeight: '670px', maxHeight: '670px'}}>
<> <>
{Object.entries(sortedSettingsConfig).map(([key, value], index) => ( {Object.entries(sortedSettingsConfig).map(([key, value], index) => (
<li key={key} className="nav-item"> <li key={key} className="nav-item">
<a className={`nav-link ${(activeTab == value.controls || (index == 0 & !activeTab)) && 'active show'}`} <p className={`text-black nav-link ${(activeTab == value.controls || (index == 0 & !activeTab)) && 'active show bg-primary text-white'}`}
id={key} id={key}
// data-bs-toggle="pill" // data-bs-toggle="pill"
// data-bs-target={`#${value.controls}`} // data-bs-target={`#${value.controls}`}
@@ -97,12 +98,27 @@ const Settings = memo(({productData}) => {
onClick={()=>handleChangeTab(value.controls)} onClick={()=>handleChangeTab(value.controls)}
> >
{value.title} {value.title}
</a> </p>
</li> </li>
))} ))}
{configData?.data?.subscription_template &&
<li className="mt-auto nav-item">
<p className={`text-black nav-link ${(activeTab == 'config_temp') && 'active show bg-primary text-white'}`}
// data-bs-toggle="pill"
// data-bs-target={`#${value.controls}`}
type="button"
// role="tab"
// aria-controls={value.controls}
// aria-selected="true"
onClick={()=>handleChangeTab('config_temp')}
>
Configure Template
</p>
</li>
}
</> </>
</ul> </ul>
<div className="tab-content"> <div className="p-3 tab-content" style={{width: '75%'}}>
<> <>
{Object.entries(sortedSettingsConfig).map(([key, value], index) => ( {Object.entries(sortedSettingsConfig).map(([key, value], index) => (
<div key={key} className={`tab-pane fade ${(activeTab == value.controls || (index == 0 & !activeTab)) && 'active show'}`} <div key={key} className={`tab-pane fade ${(activeTab == value.controls || (index == 0 & !activeTab)) && 'active show'}`}
@@ -112,6 +128,10 @@ const Settings = memo(({productData}) => {
<GeneralTab tabKey={key} name={value.title} data={value.data} isCustom={value.custom} productData={productData} backendValues={settingsData} setFieldsChanged={setFieldsChanged} /> <GeneralTab tabKey={key} name={value.title} data={value.data} isCustom={value.custom} productData={productData} backendValues={settingsData} setFieldsChanged={setFieldsChanged} />
</div> </div>
))} ))}
<div className={`tab-pane fade ${(activeTab == 'config_temp') && 'active show'}`}
>
<TemplateConfigure productData={productData} />
</div>
</> </>
</div> </div>
</div> </div>
@@ -1,14 +1,16 @@
import React, {memo} from 'react' import React, {memo} from 'react'
import getImage from "../../../utils/getImage"; import getImage from "../../../utils/getImage";
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
import queryKeys from '../../../services/queryKeys'; import queryKeys from '../../../services/queryKeys';
import { getProductTemplateData, activateTemplate } from '../../../services/services'; import {getProductTemplateData, activateTemplate} from '../../../services/services';
import {Link} from "react-router-dom";
import siteLinks from "../../../links/siteLinks";
const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{ const SiteTemplateSelector = memo(({name = 'Full Name', data, productData}) => {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const {data:templateData, isFetching, isError, error} = useQuery({ const {data: templateData, isFetching, isError, error} = useQuery({
queryKey: queryKeys.productTemplateData, queryKey: queryKeys.productTemplateData,
queryFn: () => { queryFn: () => {
let reqData = { let reqData = {
@@ -16,7 +18,7 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
uid: localStorage.getItem('uid'), // USER UID uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id product_id: productData?.product_id
} }
return getProductTemplateData(reqData) return getProductTemplateData(reqData)
}, },
staleTime: 0 staleTime: 0
}) })
@@ -24,16 +26,17 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
const templateResponse = templateData?.data const templateResponse = templateData?.data
const currentTemUID = templateResponse?.current_template_uid const currentTemUID = templateResponse?.current_template_uid
const templates = templateResponse?.templates const templates = templateResponse?.templates
const custom_template_name = templateResponse?.custom_template_name
// console.log('data Template', templateResponse) // console.log('data Template', templateResponse)
// console.log("Page data == ", data) console.log("Page data == ", data)
const handleActivateTemplate = useMutation({ const handleActivateTemplate = useMutation({
mutationFn: (fields) => { mutationFn: (fields) => {
return activateTemplate(fields) return activateTemplate(fields)
}, },
onSuccess: (res) => { onSuccess: (res) => {
if(res?.data?.resultCode != '0'){ if (res?.data?.resultCode != '0') {
throw new Error(res.data.resultDescription) throw new Error(res.data.resultDescription)
} }
queryClient.refetchQueries({ // refetches productProvision API call queryClient.refetchQueries({ // refetches productProvision API call
@@ -41,7 +44,7 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
}) })
}, },
onSettled: () => { onSettled: () => {
setTimeout(()=>{ setTimeout(() => {
handleActivateTemplate.reset() handleActivateTemplate.reset()
}, 3000) }, 3000)
} }
@@ -57,51 +60,73 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
// console.log(reqData) // console.log(reqData)
handleActivateTemplate.mutate(reqData) handleActivateTemplate.mutate(reqData)
} }
if (custom_template_name && custom_template_name != '') {
// This implies we have a custom template , just return here
return <>
<div className='col-12'>
<div
className="rounded-2 d-flex flex-column justify-content-between align-items-center"
style={{backgroundColor: '#F2FAF7'}}>
<h4 className='p-4 text-black'
style={{marginBottom: '20px'}}>Custom Product Template.</h4>
<img className='' style={{width: '200px'}}
src={getImage('custom-template.png')}
alt='mail-alert'/>
<h4 className='p-4 text-black'
style={{marginTop: '20px'}}>This product is using a custom template named <span
style={{color: 'darkred'}}>&ldquo;{custom_template_name}&rdquo;</span> .</h4>
</div>
</div>
</>
}
return ( return (
<div className="page-account-form"> <div className="page-account-form">
<div className="p-0"> <div className="p-0">
{isFetching ? {isFetching ?
<>
<div className="row">
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</div>
</>
: isError ?
<div className="row">
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
</div>
:
<div className="row">
<> <>
{!templates?.length ? <div className="row">
<p>No data Found</p> <div className="col-12">
: <p className='text-mute'>Loading...</p>
templates.map(template => (
<div key={template.template_uid} className="col-xl-6 col-sm-6">
<div className="card card-statistics">
<div className="card-body">
<div className="text-center p-2">
<div className="mb-2">
<img src={getImage('file-icon/svg.png')} alt={template.title} />
</div>
<h4 className="mb-0">{template.title}</h4>
{currentTemUID == template.template_uid ?
<button className="btn btn-light" disabled={true}>Active</button>
:
<button onClick={()=>handleSubmit(template.template_uid)} className="btn btn-primary">Activate</button>
}
</div>
</div>
</div>
</div> </div>
)) </div>
} </>
{/* {Object.entries(data)?.map(([key, value]) => ( : isError ?
<div className="row">
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
</div>
:
<div className="row overflow-y-auto" style={{maxHeight: '550px'}}>
<>
{!templates?.length ?
<p>No data Found</p>
:
templates.map(template => (
<div key={template.template_uid} className="col-xl-6 col-sm-6">
<div className="card card-statistics">
<div className="card-body">
<div className="text-center p-2">
<div className="mb-2">
<img src={getImage('file-icon/svg.png')}
alt={template.title}/>
</div>
<h4 className="mb-0">{template.title}</h4>
{currentTemUID == template.template_uid ?
<button className="btn btn-light"
disabled={true}>Active</button>
:
<button onClick={() => handleSubmit(template.template_uid)}
className="btn btn-primary">Activate</button>
}
</div>
</div>
</div>
</div>
))
}
{/* {Object.entries(data)?.map(([key, value]) => (
<div key={key} className="col-xl-6 col-sm-6"> <div key={key} className="col-xl-6 col-sm-6">
<div className="card card-statistics"> <div className="card card-statistics">
<div className="card-body"> <div className="card-body">
@@ -116,24 +141,25 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
</div> </div>
</div> </div>
))} */} ))} */}
</> </>
<div className="col-12"> <div className="col-12">
<> <>
{handleActivateTemplate.isPending ? {handleActivateTemplate.isPending ?
<p className={'text-center '}>loading...</p> <p className={'text-center '}>loading...</p>
: :
handleActivateTemplate.isError ? handleActivateTemplate.isError ?
<p className={'text-center text-danger'}>{handleActivateTemplate.error.message}</p> <p className={'text-center text-danger'}>{handleActivateTemplate.error.message}</p>
: :
handleActivateTemplate.isSuccess? handleActivateTemplate.isSuccess ?
<p className={'text-center text-success'}>Templated activated successfully</p> <p className={'text-center text-success'}>Templated activated
: successfully</p>
null :
} null
</> }
</div> </>
</div> </div>
</div>
} }
</div> </div>
</div> </div>
@@ -0,0 +1,130 @@
import React, {memo, useState} from 'react'
import getImage from "../../../utils/getImage";
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
import queryKeys from '../../../services/queryKeys';
import {getTemplateConfig} from '../../../services/services';
import {Link} from "react-router-dom";
import siteLinks from "../../../links/siteLinks";
import UploadModal from './UploadModal';
const TemplateConfigure = ({productData}) => {
const [selectedSectionDetails, setSelectedSectionDetails] = useState({})
// /panel/myproduct/template-config
const queryClient = useQueryClient()
const {data: templateData, isFetching, isError, error} = useQuery({
queryKey: queryKeys.myTemplateConfig,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id
}
return getTemplateConfig(reqData)
},
staleTime: 0
})
const templateResponse = templateData?.data
const templateImages = templateResponse?.template_images?.data;
// debugger;
console.log("templateResponse", templateResponse);
// const currentColorUID = templateResponse?.current_colorstyle_uid
// const color_styles = templateResponse?.color_styles
// const custom_template_name = templateResponse?.custom_template_name
return <>
<div className="card card-statistics">
{isFetching ?
<>
<div className="row">
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</div>
</>
: isError ?
<div className="row">
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
</div>
:
<>
<div className="card-header">
<div className="card-heading">
<h4 className="card-title"
style={{textTransform: 'none'}}>{templateResponse?.template_name}</h4>
</div>
</div>
<div>
<div className="col-12">
<div>
<div>
<div>
<h4>Image List</h4>
</div>
</div>
<div>
<ul className="list-unstyled">
{templateImages && templateImages.map(
(item) => {
const currImage = item?.default_val;
return (
<li className="media">
<div style={{
display: 'flex',
flexDirection: 'row',
padding: '5px',
backgroundColor: 'aliceblue',
margin: '2px',
maxHeight: '150px'
}}>
<div className='d-flex justify-content-center align-items-center' style={{padding: '6px', width: '120px', height: '100px'}}>
<img className="mb-xxs-0 img-fluid"
style={{height: 'auto', maxHeight: '100px'}}
src={currImage} alt="image"/>
</div>
<div className="media-body" style={{padding: '2px'}}>
<div style={{
display: 'flex',
flexDirection: 'column'
}}>
<div style={{textAlign: 'right',width: '100%'}}></div>
{/* [Change Image] */}
<label onClick={()=>setSelectedSectionDetails(item)} className='w-100 text-end' data-bs-toggle="modal" data-bs-target="#verticalCenter" style={{cursor: 'pointer'}}>[Change Image]</label>
{/* <input id={item?.id} name={item?.id} className="d-none form-control form-control-sm" type="file" onChange={handleFileChange}/> */}
<div>
<h5 className="mt-0 mb-1">{item?.name}</h5>
{item?.description}
</div>
</div>
</div>
</div>
</li>
)
}
)
}
</ul>
</div>
</div>
</div>
</div>
</>
} </div>
<UploadModal productId={productData?.product_id} selectedSectionDetails={selectedSectionDetails} />
</>
}
export default TemplateConfigure
@@ -1,40 +1,140 @@
const URLConfiguration = () => { import {Form, Formik} from "formik";
import * as Yup from "yup";
import {useMutation} from '@tanstack/react-query';
import {setExternalURL} from '../../../services/services';
import { useState } from "react";
const validationSchema = Yup.object().shape({
url: Yup.string().required("URL is required").matches(/^https?:\/\/[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-]+\.[a-zA-Z]+/, 'Must be like: https://example.mysite.com'),
})
// const initialValues = {
// url: '',
// };
const URLConfiguration = ({productData}) => {
const [externalURLChanged, setExternalURLChanged] = useState(true)
const initialValues = {
url: productData?.external_url || '',
};
let defaultUrl = 'https://' + productData?.internal_url
let externalUrl = productData?.external_url
const handleExternalURLChanged = (e) => {
if(e.target.value == externalUrl){
setExternalURLChanged(true)
}else{
setExternalURLChanged(false)
}
}
// API to set url
const setURL = useMutation({
mutationFn: (fields) => {
return setExternalURL(fields)
},
onSuccess: (res) => {
if (res.data.resultCode != '0') {
// throw({message: res?.data?.resultDescription})
throw({message: 'Something went wrong!'})
}
},
onSettled: () => {
setTimeout(() => {
setURL.reset()
}, 3000)
}
// onError: (err) => {
// console.log('err', err)
// }
})
const handleSubmit = (values) => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
subscription_uid: productData?.subscription_uid,
external_url: values.url
}
setURL.mutate(reqData)
}
return <> return <>
<div className="card card-statistics"> <div className="card card-statistics">
<div className="card-header"> <div className="card-header">
<div className="card-heading"> <div className="card-heading">
<h4 className="card-title">URL Configuration</h4> <h4 className="card-title" style={{textTransform: 'none'}}>{defaultUrl}</h4>
</div> </div>
</div> </div>
<div className="card-body button-list"> {/*<div className="card-body">*/}
<div className="row"> {/* <div className="form-group">*/}
<div className="col-12 mb-2"> {/* /!*<label htmlFor="exampleInputEmail1">Email address</label>*!/*/}
<div className="alert alert-primary" role="alert" style={{borderRadius: '10px'}}> {/* <input type="email" className="form-control"*/}
<h3 className="text-white">Default URL</h3> {/* aria-describedby="defaultUrlHelp" value={defaultUrl} readOnly={true} />*/}
<p className="text-white"> {/* </div>*/}
https://127476.devprov.mermsemr.com {/*</div>*/}
</p> </div>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{(props) => {
return (
<Form className='w-full'>
<div className="card card-statistics" style={{backgroundColor: '#b6e5ef'}}>
<div className="card-header">
<div className="card-heading">
<h4 className="card-title" style={{textTransform: 'none'}}>Set your own URL</h4>
</div>
</div>
<div className="card-body">
<div className="form-group">
<label htmlFor="exampleInputEmail1">Enter your full URL <span
className={`${(props.errors.url && props.touched.url) && 'text-danger'}`}>{props.errors.url}</span></label>
<input value={props.values.url} onChange={(e)=>{props.handleChange(e); handleExternalURLChanged(e)}} type="text"
className="form-control" id="url" aria-describedby="url"
placeholder="https://example.mysite.com"/>
</div>
<div style={{width: '100%', textAlign: 'right'}}>
<button
type="submit"
disabled={setURL.isPending || externalURLChanged}
className="btn btn-primary"
>
{setURL.isPending ? 'Loading...' : 'Submit'}
</button>
</div>
</div>
{setURL.error &&
<div className="col-12">
<p className='text-danger'>{setURL.error.message}</p>
</div>
}
{setURL.isSuccess &&
<div className="col-12">
<p className='text-success'>{'Completed successfully'}</p>
</div>
}
<div style={{backgroundColor: '#94b8c0', borderRadius: '10px', padding: '10px'}}>
Final steps to configure your URL:<br/>
DNS:<br/>
DNS:<br/>
DNS:<br/>
</div>
</div> </div>
</div> </Form>
</div> );
</div> }}
</div> </Formik>
<div className="card card-statistics">
<div className="card-header">
<div className="card-heading">
<h4 className="card-title">Default</h4>
</div>
</div>
<div className="card-body">
<div className="form-group">
<label htmlFor="exampleInputEmail1">Email address</label>
<input type="email" className="form-control" id="exampleInputEmail1"
aria-describedby="emailHelp" placeholder="Enter your url"/>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</div>
</div>
</> </>
} }
@@ -0,0 +1,168 @@
import React, { useRef, useState } from 'react'
import { Modal } from "bootstrap";
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
import queryKeys from '../../../services/queryKeys';
import {getMediaFileList, templateMediaSet} from '../../../services/services';
export default function UploadModal({productId, selectedSectionDetails}) {
const queryClient = useQueryClient()
const [selectedFile, setSelectedFile] = useState('')
const handleSelectedFile = (file_uid) => {
// if(selectedFile.length && selectedFile.includes(file_uid)){
// let indexOfItem = selectedFile.indexOf(file_uid)
// let oldFiles = [...selectedFile]
// oldFiles.splice(indexOfItem, 1)
// setSelectedFile(oldFiles)
// }else{
// setSelectedFile(prev => [...prev, file_uid])
// }
setSelectedFile(file_uid)
}
const modalRef = useRef(null)
const dismissModal = () => {
const body = document.querySelector('body')
body.removeAttribute('style')
// body.classList.toggle('modal-open')
const modalBackdrop = document.querySelectorAll('.modal-backdrop')
modalBackdrop.forEach(item => {
if (item) {
item.remove();
}
})
const modal = Modal.getInstance(modalRef.current);
modal && modal.hide();
};
const changeTemplateMediaSet = useMutation({
mutationFn: (fields) => {
if(!fields.file_uid){
throw({message: 'Please select a file first!'})
}
return templateMediaSet(fields)
},
onSuccess: (res) => {
// console.log('res', res.data)
if(res.data.resultCode != '0'){
throw({message: res?.data?.resultDescription})
}
setSelectedFile('')
dismissModal() // to close modal
queryClient.refetchQueries({
queryKey: [...queryKeys.myTemplateConfig],
// type: 'active',
// exact: true,
})
},
onSettled: () => {
setTimeout(()=>{
changeTemplateMediaSet.reset()
}, 3000)
}
})
const handleUpload = () => {
let reqData = {
token: localStorage.getItem("token"), // USER TOKEN
uid: localStorage.getItem("uid"), // USER UID
product_id: productId,
file_uid: selectedFile,
image_id: selectedSectionDetails?.id
};
// console.log('reqData', reqData)
changeTemplateMediaSet.mutate(reqData)
}
const {data, isFetching, isError, error} = useQuery({
queryKey: queryKeys.my_files,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid') // USER UID
}
return getMediaFileList(reqData)
}
})
const mediaFileList = data?.data
// console.log('mediaFileList', mediaFileList) //file_list
return (
<>
{/* Vertical Center Modal */}
<div ref={modalRef} className="modal fade" id="verticalCenter" tabIndex="-1" role="dialog" aria-hidden="false">
<div className="modal-50 modal-dialog modal-dialog-centered modal-50" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title" style={{fontSize: '18px'}} id="verticalCenterTitle">Select New Picture</h5>
<button type="button" className="close" data-bs-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div className="modal-body">
{isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div className='row g-5 justify-content-center'>
{mediaFileList?.file_list?.length ?
<>
{mediaFileList?.file_list?.map(item => {
const file_url = (mediaFileList?.media_server + "/" + item?.file_group + "/" + item?.file_uid + "/" + item.filename).toLowerCase();
return(
<div onClick={()=>handleSelectedFile(item?.file_uid)} className={`p-3 text-center col-4`} style={{cursor: 'pointer'}}>
<div className={`p-3 ${(selectedFile == item?.file_uid) && 'bg-light'}`} style={{Height: '250px'}}>
<img key={item?.file_uid} className="img-fluid" style={{}} src={file_url} alt='file-image' />
</div>
</div>
)
})
}
</>
:
<p>No File(s) found!</p>
}
</div>
}
{changeTemplateMediaSet.error &&
<>
<div className="col-12">
<p className='text-danger'>{changeTemplateMediaSet.error.message}</p>
</div>
</>
}
{changeTemplateMediaSet.isSuccess &&
<>
<div className="col-12">
<p className='text-success'>{'subscription is successful'}</p>
</div>
</>
}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
{selectedFile &&
<button type="button" className="btn btn-primary" disabled={changeTemplateMediaSet?.isPending || changeTemplateMediaSet?.isSuccess} onClick={handleUpload}>{changeTemplateMediaSet?.isPending ? 'Applying...' : changeTemplateMediaSet?.isSuccess ? 'Applied' : 'Apply'}</button>
}
</div>
</div>
</div>
</div>
{/* END of Vertical Center Modal */}
</>
)
}
@@ -1,7 +1,7 @@
import React, {useEffect, useMemo, useState} from "react"; import React, {useCallback, useEffect, useMemo, useState} from "react";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
// import { useLocation } from "react-router-dom"; // import { useLocation } from "react-router-dom";
// import { Form, Formik } from "formik"; import { Form, Formik } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import {useMutation, useQuery} from "@tanstack/react-query"; import {useMutation, useQuery} from "@tanstack/react-query";
import getImage from "../../utils/getImage"; import getImage from "../../utils/getImage";
@@ -13,17 +13,19 @@ import {updateUserDetails} from "../../store/UserDetails";
import {useDispatch} from "react-redux"; import {useDispatch} from "react-redux";
// const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
// practice: Yup.string().required("Required"), practice: Yup.string().required("Required"),
// specialization: Yup.string().required("Required"), specialization: Yup.string().when('practice', {
// introduction: Yup.string().min(1, "Minimum 10 characters").max(50, "Maximum 50 characters").required("Required"), is: (value) => typeof value === 'string' && value.trim().length > 0,
// }) then: (schema) => schema.required('Required'),
otherwise: (schema) => schema,
// const initialValues = { }),
// practice: '', introduction: Yup.string().min(1, "Minimum 1 character").max(50, "Maximum 50 characters"),
// specialization: '', url_name: Yup.string().min(6, "Minimum 6 characters").max(16, "Maximum 16 characters").required("Required").matches(
// introduction: '', /^[a-zA-Z0-9]+$/, // Regex for alphanumeric characters
// }; 'Must contain only alphanumeric characters' // Custom error message
),
})
export default function ProfileCompleteCom() { export default function ProfileCompleteCom() {
@@ -35,6 +37,7 @@ export default function ProfileCompleteCom() {
const {state: {redirectLink}} = useLocation() const {state: {redirectLink}} = useLocation()
const [practices, setPractices] = useState([]) const [practices, setPractices] = useState([])
const [specialties, setSpecialties] = useState([])
const [initialValues, setInitialValues] = useState({ const [initialValues, setInitialValues] = useState({
practice: '', practice: '',
@@ -43,21 +46,18 @@ export default function ProfileCompleteCom() {
url_name: '' url_name: ''
}) })
const specialties = useMemo(() => { // FUNCTION TO UPDATE SPECIALITY ARRAY EACH TIME PRACTICE CHANGES const handleUpdateSpecialties = (e) => {
setInitialValues(prev => ({...prev, specialization: ''})) setInitialValues(prev => ({...prev, specialization: ''}))
if (!initialValues.practice) { const specialtiesArr = practices.filter(item => item.practice == e.target.value)[0]?.specialties
return [] setSpecialties(specialtiesArr)
} }
const specialtiesArr = practices.filter(item => item.practice == initialValues.practice)[0]?.specialties
return specialtiesArr
}, [initialValues.practice])
const mutation = useMutation({ const mutation = useMutation({
mutationFn: (fields) => { mutationFn: (fields) => {
const {practice, specialization} = fields const {practice, specialization, url_name} = fields
if (!practice || !specialization) { if (!practice || !specialization || !url_name) {
throw new Error('Please select both practice and specialization fields') throw new Error('Please Select both Practice, Specialization and Enter URL_Name')
} }
return completeProfile(fields) return completeProfile(fields)
}, },
@@ -94,15 +94,11 @@ export default function ProfileCompleteCom() {
} }
}) })
const handlePracticeChange = ({target: {name, value}}) => { const handleCompleteProfile = (values) => { // FUNCTION TO COMPLETE PROFILE
setInitialValues(prev => ({...prev, [name]: value}))
}
const handleCompleteProfile = () => { // FUNCTION TO COMPLETE PROFILE
let reqData = { let reqData = {
token: localStorage.getItem('token'), // USER TOKEN token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID uid: localStorage.getItem('uid'), // USER UID
...initialValues ...values
} }
mutation.mutate(reqData) mutation.mutate(reqData)
} }
@@ -146,132 +142,146 @@ export default function ProfileCompleteCom() {
<div className="card-body"> <div className="card-body">
<div className='h-100 row flex-column'> <div className='h-100 row flex-column'>
{/* <div className="row"> */} {/* <div className="row"> */}
<> <Formik
<div className=""> initialValues={initialValues}
<div className="form-group position-relative"> validationSchema={validationSchema}
<label className={`text-black fw-bold control-label`}>Practice :</label> onSubmit={handleCompleteProfile}
<div className="position-relative"> enableReinitialize={true}
{/* <select onChange={props.handleChange} name='practice' value={props.values.practice} className="form-control"> >
<option value=''>Select</option> {(props) => {
{practices.map((practice, index)=>( return (
<option key={index} value={practice.practice}>{practice.practice}</option> <Form className='mt-2'>
))} <>
</select> */} <div className="">
<select onChange={handlePracticeChange} name='practice' <div className="form-group position-relative">
value={initialValues.practice} className="form-control"> <label className={`text-black fw-bold control-label`}>Practice : <span className="text-danger">{(props.errors.practice && props.touched.practice) && props.errors.practice}</span></label>
<option value=''>Select</option> <div className="position-relative">
{practices.map((practice, index) => ( {/* <select onChange={props.handleChange} name='practice' value={props.values.practice} className="form-control">
<option key={index} <option value=''>Select</option>
value={practice.practice}>{practice.practice}</option> {practices.map((practice, index)=>(
))} <option key={index} value={practice.practice}>{practice.practice}</option>
</select> ))}
<IoMdArrowDropdown className='position-absolute w-auto' style={{ </select> */}
top: '50%', <select
right: '2px', onChange={(e) => {props.handleChange(e); props.setFieldValue('specialization', ''); handleUpdateSpecialties(e)}}
transform: 'translateY(-50%)' name='practice'
}}/> value={props.values.practice} className="form-control">
</div> <option value=''>Select</option>
</div> {practices.map((practice, index) => (
</div> <option key={index}
value={practice.practice}>{practice.practice}</option>
))}
</select>
<IoMdArrowDropdown className='position-absolute w-auto' style={{
top: '50%',
right: '2px',
transform: 'translateY(-50%)'
}}/>
</div>
</div>
</div>
<div className=""> <div className="">
<div className="form-group"> <div className="form-group">
<label className={`text-black fw-bold control-label`}>Your <label className={`text-black fw-bold control-label`}>Your
Specialization :</label> Specialization : <span className="text-danger">{(props.errors.specialization && props.touched.specialization) && props.errors.specialization}</span></label>
<div className="position-relative"> <div className="position-relative">
<select onChange={handlePracticeChange} name='specialization' <select onChange={props.handleChange} name='specialization'
value={initialValues.specialization} value={props.values.specialization}
className="form-control"> className="form-control">
<option value=''>Select</option> <option value=''>Select</option>
{specialties.map((specialty, index) => ( {specialties.map((specialty, index) => (
<option key={index} value={specialty}>{specialty}</option> <option key={index} value={specialty}>{specialty}</option>
))} ))}
</select> </select>
<IoMdArrowDropdown className='position-absolute w-auto' style={{ <IoMdArrowDropdown className='position-absolute w-auto' style={{
top: '50%', top: '50%',
right: '2px', right: '2px',
transform: 'translateY(-50%)' transform: 'translateY(-50%)'
}}/> }}/>
</div> </div>
</div> </div>
</div> </div>
<div className=""> <div className="">
<div className="form-group position-relative"> <div className="form-group position-relative">
<label className={`text-black fw-bold control-label`}>Other General <label className={`text-black fw-bold control-label`}>Other General Information : <span className="text-danger">{(props.errors.introduction && props.touched.introduction) && props.errors.introduction}</span></label>
Information :</label> <textarea name='introduction' rows={5} style={{resize: 'none'}}
<textarea name='introduction' rows={5} style={{resize: 'none'}} className="form-control" value={props.values.introduction}
className="form-control" value={initialValues.introduction} onChange={props.handleChange}/>
onChange={handlePracticeChange}/> </div>
</div> </div>
</div>
<div className=""> <div className="">
<div className="form-group position-relativ'e"> <div className="form-group position-relativ'e">
{/*<label className={`text-black fw-bold control-label`}>What we use this*/} {/*<label className={`text-black fw-bold control-label`}>What we use this*/}
{/* information for :</label>*/} {/* information for :</label>*/}
<div style={{ <div style={{
fontSize: '14px', fontSize: '14px',
borderRadius: '10px', borderRadius: '10px',
backgroundColor: 'aliceblue', backgroundColor: 'aliceblue',
fontWeight: 'bolder', fontWeight: 'bolder',
padding: '15px' padding: '15px'
}}> }}>
MERMS A.I. agents use the information supplied to help generate MERMS A.I. agents use the information supplied to help generate
useful entries for your product settings. useful entries for your product settings.
</div> </div>
</div> </div>
</div> </div>
<div className=""> <div className="">
<div className="form-group position-relative"> <div className="form-group position-relative">
<label className={`text-black fw-bold control-label`}>URL Name :</label> <label className={`text-black fw-bold control-label`}>URL Name : <span className="text-danger">{(props.errors.url_name && props.touched.url_name) && props.errors.url_name}</span></label>
<div className="position-relative d-flex flex-column flex-xxl-row" style={{gap: '10px'}}> <div className="position-relative d-flex flex-column flex-xxl-row" style={{gap: '10px'}}>
{/* <select onChange={handlePracticeChange} name='url_name' {/* <select onChange={handlePracticeChange} name='url_name'
value={initialValues.url_name} className="form-control"> value={initialValues.url_name} className="form-control">
<option value=''>Select</option> <option value=''>Select</option>
{practices.map((practice, index) => ( {practices.map((practice, index) => (
<option key={index} <option key={index}
value={practice.practice}>{practice.practice}</option> value={practice.practice}>{practice.practice}</option>
))} ))}
</select> </select>
<IoMdArrowDropdown className='position-absolute w-auto' style={{ <IoMdArrowDropdown className='position-absolute w-auto' style={{
top: '50%', top: '50%',
right: '2px', right: '2px',
transform: 'translateY(-50%)' transform: 'translateY(-50%)'
}}/> */} }}/> */}
<input <input
className="form-control" className="form-control"
onChange={handlePracticeChange} name='url_name' onChange={props.handleChange} name='url_name'
value={initialValues.url_name} value={props.values.url_name}
minLength={6} minLength={6}
maxLength={16} maxLength={16}
/> />
<p className="border-radius-10 p-2 border border-warning" <p className="border-radius-10 p-2 border border-warning"
style={{fontSize: "1.0rem"}}>We use the URL Name to form part of style={{fontSize: "1.0rem"}}>We use the URL Name to form part of
your default URL when we configure your default URL when we configure
a new URL for your products. You can always change your product a new URL for your products. You can always change your product
URL. <br/> URL. <br/>
<b>Example : <span style={{color: 'red'}}>url_name</span>.product.mermsemr.com <b>Example : <span style={{color: 'red'}}>url_name</span>.product.mermsemr.com
</b> </b>
</p> </p>
</div> </div>
</div> </div>
</div> </div>
{(mutation.isError || mutation.isSuccess) && {(mutation.isError || mutation.isSuccess) &&
<> <>
<div className=""> <div className="">
<p className={`${mutation.isSuccess ? 'text-success' : 'text-danger'}`}>{mutation.isSuccess ? 'Completed successfully, redirecting...' : mutation.error.message}</p> <p className={`${mutation.isSuccess ? 'text-success' : 'text-danger'}`}>{mutation.isSuccess ? 'Completed successfully, redirecting...' : mutation.error.message}</p>
</div> </div>
</> </>
} }
<div className="mt-auto text-end"> <div className="mt-auto text-end">
<button type='button' onClick={handleCompleteProfile} <button type='submit'
className="btn btn-primary text-uppercase">{mutation.isPending ? 'loading...' : 'Continue'}</button> className="btn btn-primary text-uppercase">{mutation.isPending ? 'loading...' : 'Continue'}</button>
</div> </div>
</> </>
</Form>
);
}}
</Formik>
{/* </div> */} {/* </div> */}
</div> </div>
</div> </div>
@@ -0,0 +1,89 @@
import React, {useState} from "react";
import {useQuery} from "@tanstack/react-query";
import {getPaymentReports} from "../../services/services";
import queryKeys from "../../services/queryKeys";
export default function PaymentReportTable() {
const [page, setPage] = useState(0)
const {data, isFetching, isError, error} = useQuery({
queryKey: [...queryKeys.payment_report, page],
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
page
}
return getPaymentReports(reqData)
},
staleTime: 0
})
// console.log('DATA', data?.data)
const paymentReportData = data?.data?.payment || []
return (
<>
<div className="row">
<div className="col-md-12 m-b-30">
<div className="d-block d-sm-flex flex-nowrap align-items-center">
<div className="page-title mb-2 mb-sm-0">
<h4>Payments Report</h4>
</div>
</div>
</div>
</div>
<div className="row">
{isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div className="col-lg-12">
<div className="card card-statistics">
<div className="card-body">
<div className="export-table-wrapper table-responsive">
<table id="export-table" className="table table-bordered">
<thead className="thead-light">
<tr>
<th>Added</th>
<th>Name</th>
<th>Amount</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{paymentReportData.length > 0 ? paymentReportData.map((item, index) => {
return (
<tr key={index}>
<td>{item?.added}</td>
<td>{item?.option_name}</td>
<td>{item?.currency}{item?.amount}</td>
<td>{item?.status}</td>
</tr>
)
})
:
<tr>
<td colSpan={4} className='text-center'>No data found</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
}
</div>
</>
)
}
@@ -0,0 +1,90 @@
import React, {useState} from "react";
import {useQuery} from "@tanstack/react-query";
import {getProductReports} from "../../services/services";
import queryKeys from "../../services/queryKeys";
export default function ProductReportTable() {
const [page, setPage] = useState(0)
const {data, isFetching, isError, error} = useQuery({
queryKey: [...queryKeys.product_report, page],
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
page
}
return getProductReports(reqData)
},
staleTime: 0
})
// console.log('DATA', data?.data)
const productReportData = data?.data?.product || []
return (
<>
<div className="row">
<div className="col-md-12 m-b-30">
<div className="d-block d-sm-flex flex-nowrap align-items-center">
<div className="page-title mb-2 mb-sm-0">
<h4>Products Report</h4>
</div>
</div>
</div>
</div>
<div className="row">
{isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div className="col-lg-12">
<div className="card card-statistics">
<div className="card-body">
<div className="export-table-wrapper table-responsive">
<table id="export-table" className="table table-bordered">
<thead className="thead-light">
<tr>
<th>ID</th>
<th>Added</th>
<th>Name</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{productReportData.length > 0 ? productReportData.map((item, index) => {
return (
<tr key={index}>
<td>{item?.product_id}</td>
<td>{item?.added}</td>
<td>{item?.product_name}</td>
<td>{item?.status}</td>
</tr>
)
})
:
<tr>
<td colSpan={4} className='text-center'>No data found</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
}
</div>
</>
)
}
+90 -5
View File
@@ -1,14 +1,99 @@
import React from "react"; import React, { useState } from "react";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
import {useQuery} from "@tanstack/react-query";
import queryKeys from "../../services/queryKeys";
import {getReportsTopicsList} from "../../services/services";
import PaymentReportTable from "./PaymentReportTable";
import ProductReportTable from "./ProductReportTable";
import sortArrayByListOrder from "../../helpers/sortArrayByLIstOrder";
import SystemReportTable from "./SystemReportTable";
export default function Reports(){ export default function Reports() {
return( const [activeTab, setActiveTab] = useState('payment')
const {data, isFetching, isError, error} = useQuery({
queryKey: queryKeys.topics,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid') // USER UID
}
return getReportsTopicsList(reqData)
}
})
const reportTopicList = data?.data?.topics?.topics;
const renderedTable = {
payment: <PaymentReportTable />,
product: <ProductReportTable />,
system: <SystemReportTable />
}
const sortedReportTopicList= sortArrayByListOrder(reportTopicList ? reportTopicList : []) // SORTED SETTINGSCONFIG
return (
<> <>
<BreadcrumbComBS title='Reports' paths={['Dashboard', 'Reports']} /> <BreadcrumbComBS title='Reports' paths={['Dashboard', 'Reports']}/>
<div className="row"> <div className="row">
<div className="vh-100 col-12 flex align-items-center">Coming Soon</div> {isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div>
<div>
<div className="card card-statistics" style={{minHeight: '500px'}}>
{/*<div className="card-header">*/}
{/* <div className="card-heading">*/}
{/* <h4 className="card-title"> Tab vertical </h4>*/}
{/* </div>*/}
{/*</div>*/}
<div className="card-body">
<div className="tab tab-vertical">
{/* <!-- Tabs --> */}
<ul className="nav nav-tabs" id="myTab" role="tablist">
{sortedReportTopicList && sortedReportTopicList.map((item, index) => {
return (
<li key={index} className="nav-item" role="presentation" onClick={() => setActiveTab(item?.url)}>
<a className={`nav-link ${index == 0 && 'active'}`} id={`tab-${item?.url}`} href="#" data-bs-toggle="tab" data-bs-target={`#content-${item?.url}`} type="button" role="tab">
{item?.name}
</a>
</li>
)
})}
</ul>
{/* <!-- Tab Content --> */}
{/* <div className="tab-content">
{sortedReportTopicList && sortedReportTopicList.map((item, index) => {
return (
<div key={index} className={`tab-pane fade show ${index == 0 && 'active'}`} id={`content-${item?.url}`} role="tabpanel" style={{minHeight: '400px'}}>
{renderedTable[item?.url]}
</div>
)
})}
</div> */}
<div className="tab-content">
<div className={`tab-pane fade show active`} style={{minHeight: '400px'}}>
{renderedTable[activeTab]}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
</div> </div>
</> </>
) )
@@ -0,0 +1,90 @@
import React, {useState} from "react";
import {useQuery} from "@tanstack/react-query";
import {getSystemReports} from "../../services/services";
import queryKeys from "../../services/queryKeys";
export default function SystemReportTable() {
const [page, setPage] = useState(0)
const {data, isFetching, isError, error} = useQuery({
queryKey: [...queryKeys.system_report, page],
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
page
}
return getSystemReports(reqData)
},
staleTime: 0
})
// console.log('DATA', data?.data)
const systemReportData = data?.data?.system || []
return (
<>
<div className="row">
<div className="col-md-12 m-b-30">
<div className="d-block d-sm-flex flex-nowrap align-items-center">
<div className="page-title mb-2 mb-sm-0">
<h4>Systems Report</h4>
</div>
</div>
</div>
</div>
<div className="row">
{isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div className="col-lg-12">
<div className="card card-statistics">
<div className="card-body">
<div className="export-table-wrapper table-responsive">
<table id="export-table" className="table table-bordered">
<thead className="thead-light">
<tr>
<th>Added</th>
<th>Action Name</th>
<th>Description</th>
<th>Updated</th>
</tr>
</thead>
<tbody>
{systemReportData.length > 0 ? systemReportData.map((item, index) => {
return (
<tr key={index}>
<td>{item?.added}</td>
<td>{item?.action_name}</td>
<td>{item?.status_description}</td>
<td>{item?.updated}</td>
</tr>
)
})
:
<tr>
<td colSpan={4} className='text-center'>No data found</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
}
</div>
</>
)
}
+159
View File
@@ -0,0 +1,159 @@
import React, { useRef, useState } from 'react'
import { Form, Formik } from "formik";
import * as Yup from "yup";
import { Modal } from "bootstrap";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { updateLinks } from "../../services/services";
import queryKeys from '../../services/queryKeys';
const linksValidationSchema = Yup.object().shape({
facebook_url: Yup.string().matches(/^https:\/\/.+/,"URL must start with https://"),
twitter_url: Yup.string().matches(/^https:\/\/.+/,"URL must start with https://"),
blogger_url: Yup.string().matches(/^https:\/\/.+/,"URL must start with https://"),
google_url: Yup.string().matches(/^https:\/\/.+/,"URL must start with https://"),
linked_url: Yup.string().matches(/^https:\/\/.+/,"URL must start with https://"),
website_url: Yup.string().matches(/^https:\/\/.+/,"URL must start with https://"),
})
export default function LinksForm({data}) {
const modalRef = useRef(null)
const queryClient = useQueryClient()
const [intialData] = useState(data)
const [infoToUpdate, setInfoToUpdate] = useState({})
const dismissModal = () => {
const body = document.querySelector('body')
body.removeAttribute('style')
// body.classList.toggle('modal-open')
const modalBackdrop = document.querySelectorAll('.modal-backdrop')
modalBackdrop.forEach(item => {
if (item) {
item.remove();
}
})
const modal = Modal.getInstance(modalRef.current);
modal && modal.hide();
};
// UPDATE LINKS MUTATION
const updateLinksMutation = useMutation({
mutationFn: (fields) => {
return updateLinks(fields)
},
onSuccess: (res) => {
if(res.data.resultCode != '0'){
throw({message: res?.data?.resultDescription ? res?.data?.resultDescription : 'En error occured'})
}
},
onSettled: ()=>{
setTimeout(() => {
dismissModal() //CLOSE MODAL HERE
queryClient.refetchQueries({
queryKey: [...queryKeys.profile_data], // type: 'active', // exact: true,
})
updateLinksMutation.reset()
}, 3000);
}
})
const handleSetInfoToUpdate = (values, helpers) => {
setInfoToUpdate(values)
var modal = new Modal(document.getElementById('modal_links'));
modal.show();
}
const proceed = () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
url_list: {...infoToUpdate}
}
// console.log(reqData)
updateLinksMutation.mutate(reqData)
}
return (
<>
<Formik
initialValues={intialData}
validationSchema={linksValidationSchema}
onSubmit={handleSetInfoToUpdate}
>
{(props) => {
return (
<Form className=''>
<div className="form-group">
<label htmlFor="fb">Facebook URL: {(props.errors.facebook_url && props.touched.facebook_url) && <span className="text-danger">{props.errors.facebook_url}</span>}</label>
<input type="text" className="form-control" name="facebook_url" value={props.values?.facebook_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="tr">Twitter URL: {(props.errors.twitter_url && props.touched.twitter_url) && <span className="text-danger">{props.errors.twitter_url}</span>}</label>
<input type="text" className="form-control" name="twitter_url" value={props.values?.twitter_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="br">Blogger URL: {(props.errors.blogger_url && props.touched.blogger_url) && <span className="text-danger">{props.errors.blogger_url}</span>}</label>
<input type="text" className="form-control" name="blogger_url" value={props.values?.blogger_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="go">Google+ URL: {(props.errors.google_url && props.touched.google_url) && <span className="text-danger">{props.errors.google_url}</span>}</label>
<input type="text" className="form-control" name="google_url" value={props.values?.google_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="li">LinkedIn URL: {(props.errors.linked_url && props.touched.linked_url) && <span className="text-danger">{props.errors.linked_url}</span>}</label>
<input type="text" className="form-control" name="linked_url" value={props.values?.linked_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="we">Website URL: {(props.errors.website_url && props.touched.website_url) && <span className="text-danger">{props.errors.website_url}</span>}</label>
<input type="text" className="form-control" name="website_url" value={props.values?.website_url} onChange={props.handleChange} />
</div>
<div style={{textAlign: "right"}}>
<button type="submit" className="btn btn-primary">Update Links
</button>
</div>
</Form>
);
}}
</Formik>
{/* Vertical Center Modal */}
<div ref={modalRef} className="modal fade" id="modal_links" tabIndex="-1" role="dialog" aria-hidden="true">
<div className="modal-dialog modal-dialog-centered" role="document">
<div className="modal-content">
<div className="modal-header">
{/* <h5 className="modal-title" style={{fontSize: '18px'}} id="verticalCenterTitle">{'productTitle'}</h5> */}
<button type="button" className="close" data-bs-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div className="modal-body">
<h5 className="text-center" style={{fontSize: '18px'}}>Are you sure, you want to update?</h5>
{(updateLinksMutation.error || updateLinksMutation.isSuccess) && (
<div className="col-12">
<p className={`p-2 text-center ${updateLinksMutation.isSuccess ? 'text-success' : 'text-danger'}`}>
{updateLinksMutation.isSuccess ? 'Updated Successfully' : updateLinksMutation.error.message}
</p>
</div>
)}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
<button type="button" className="btn btn-primary" disabled={updateLinksMutation.isSuccess} onClick={proceed}>{updateLinksMutation.isPending ? 'Updating...' : 'Update'}</button>
</div>
</div>
</div>
</div>
{/* END of Vertical Center Modal */}
</>
)
}
+164
View File
@@ -0,0 +1,164 @@
import React, { useRef, useState } from 'react'
import { Form, Formik } from "formik";
import * as Yup from "yup";
import { Modal } from "bootstrap";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { updateProfile } from "../../services/services";
import queryKeys from '../../services/queryKeys';
import { useDispatch } from 'react-redux';
import { updateUserDetails } from '../../store/UserDetails'
const profileValidationSchema = Yup.object().shape({
firstname: Yup.string().required("firstname is required"),
lastname: Yup.string().required("lastname is required"),
email: Yup.string().required("email is required"),
account_name: Yup.string().required("account name is required"),
phone: Yup.string().required("phone is required"),
full_address: Yup.string().required("full address is required"),
})
export default function ProfileForm({data}) {
const dispatch = useDispatch()
const modalRef = useRef(null)
const queryClient = useQueryClient()
const [intialData] = useState(data)
const [infoToUpdate, setInfoToUpdate] = useState({})
const dismissModal = () => {
const body = document.querySelector('body')
body.removeAttribute('style')
// body.classList.toggle('modal-open')
const modalBackdrop = document.querySelectorAll('.modal-backdrop')
modalBackdrop.forEach(item => {
if (item) {
item.remove();
}
})
const modal = Modal.getInstance(modalRef.current);
modal && modal.hide();
};
// UPDATE PROFILE MUTATION
const updateProfileMutation = useMutation({
mutationFn: (fields) => {
return updateProfile(fields)
},
onSuccess: (res) => {
if(res.data.resultCode != '0'){
throw({message: res?.data?.resultDescription ? res?.data?.resultDescription : 'An error occured'})
}
const account_name = res?.data?.personal_data?.account_name
dispatch(updateUserDetails({ account_name }));
},
onSettled: ()=>{
setTimeout(() => {
dismissModal() //CLOSE MODAL HERE
queryClient.refetchQueries({
queryKey: [...queryKeys.profile_data], // type: 'active', // exact: true,
})
updateProfileMutation.reset()
}, 3000);
}
})
const handleSetInfoToUpdate = (values, helpers) => {
delete values.email
delete values.country
setInfoToUpdate(values)
var modal = new Modal(document.getElementById('modal'));
modal.show();
}
const proceed = () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
...infoToUpdate
}
updateProfileMutation.mutate(reqData)
}
return (
<>
<Formik
initialValues={intialData}
validationSchema={profileValidationSchema}
onSubmit={handleSetInfoToUpdate}
>
{(props) => {
return (
<Form className=''>
<div className="form-row">
<div className="form-group col-md-12">
<label htmlFor="name1">First Name {(props.errors.firstname && props.touched.firstname) && <span className="text-danger">{props.errors.firstname}*</span>}</label>
<input type="text" className="form-control" name="firstname" value={props.values?.firstname} onChange={props.handleChange} />
</div>
<div className="form-group col-md-12">
<label htmlFor="name1">Last Name {(props.errors.lastname && props.touched.lastname) && <span className="text-danger">{props.errors.lastname}*</span>}</label>
<input type="text" className="form-control" name="lastname" value={props.values?.lastname} onChange={props.handleChange} />
</div>
<div className="form-group col-md-12">
<label htmlFor="name1">Account Name {(props.errors.account_name && props.touched.account_name) && <span className="text-danger">{props.errors.account_name}*</span>}</label>
<input type="text" className="form-control" name="account_name" value={props.values?.account_name} onChange={props.handleChange} />
</div>
<div className="form-group col-md-12">
<label htmlFor="phone1">Phone Number {(props.errors.phone && props.touched.phone) && <span className="text-danger">{props.errors.phone}*</span>}</label>
<input type="text" className="form-control" name="phone" value={props.values?.phone} onChange={props.handleChange} />
</div>
<div className="form-group col-md-12">
<label htmlFor="email1">Email {(props.errors.email && props.touched.email) && <span className="text-danger">{props.errors.email}*</span>}</label>
<input type="text" className="form-control" name="email" readOnly value={props.values?.email} onChange={props.handleChange} />
</div>
</div>
<div className="form-group">
<label htmlFor="add1">Address {(props.errors.full_address && props.touched.full_address) && <span className="text-danger">{props.errors.full_address}*</span>}</label>
<input type="text" className="form-control" name="full_address" value={props.values?.full_address} onChange={props.handleChange} />
</div>
<div style={{textAlign: "right"}}>
<button type="submit" className="btn btn-primary">Update Profile
</button>
</div>
</Form>
);
}}
</Formik>
{/* Vertical Center Modal */}
<div ref={modalRef} className="modal fade" id="modal" tabIndex="-1" role="dialog" aria-hidden="true">
<div className="modal-dialog modal-dialog-centered" role="document">
<div className="modal-content">
<div className="modal-header">
{/* <h5 className="modal-title" style={{fontSize: '18px'}} id="verticalCenterTitle">{'productTitle'}</h5> */}
<button type="button" className="close" data-bs-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div className="modal-body">
<h5 className="text-center" style={{fontSize: '18px'}}>Are you sure, you want to update?</h5>
{(updateProfileMutation.error || updateProfileMutation.isSuccess) && (
<div className="col-12">
<p className={`p-2 text-center ${updateProfileMutation.isSuccess ? 'text-success' : 'text-danger'}`}>
{updateProfileMutation.isSuccess ? 'Updated Successfully' : updateProfileMutation.error.message}
</p>
</div>
)}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
<button type="button" className="btn btn-primary" disabled={updateProfileMutation.isSuccess} onClick={proceed}>{updateProfileMutation.isPending ? 'Updating...' : 'Update'}</button>
</div>
</div>
</div>
</div>
{/* END of Vertical Center Modal */}
</>
)
}
+124
View File
@@ -0,0 +1,124 @@
import React, { memo, useRef, useState } from 'react'
import { useSelector, useDispatch } from 'react-redux';
import getImage from '../../utils/getImage';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { uploadProfileImg } from '../../services/services';
import queryKeys from '../../services/queryKeys';
import { updateUserDetails } from '../../store/UserDetails'
const ProfileImage = memo(({intialData}) => {
const dispatch = useDispatch()
const queryClient = useQueryClient()
const [selectedImg, setSelectedImg] = useState(null)
// const {userDetails} = useSelector((state) => state?.userDetails); // CHECKS FOR ACTIVE USER DETAILS
const avtarImage = "avtar/merms-user.png";
// browser profile img
const browserImg = useRef(null);
const browseProfileImg = () => {
browserImg.current.click();
};
const profileImgChangeHandler = (event) => {
setSelectedImg(event.target.files[0])
}
const uploadProfileMutation = useMutation({
mutationFn: (fields) => {
if(!fields.file){
throw new Error('Please, select an image')
}
return uploadProfileImg(fields)
},
onSuccess: (res) => {
console.log('res', res)
if(res?.data?.picture){
dispatch(updateUserDetails({ picture: res?.data?.picture }));
}
// if(res.data.resultCode != '0'){
// throw({message: res?.data?.resultDescription ? res?.data?.resultDescription : 'An error occured'})
// }
// const account_name = res?.data?.personal_data?.account_name
},
onSettled: ()=>{
setTimeout(() => {
// queryClient.refetchQueries({
// queryKey: [...queryKeys.profile_data], // type: 'active', // exact: true,
// })
uploadProfileMutation.reset()
}, 3000);
}
})
const proceedToUpload = () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
member_uid: localStorage.getItem('uid'), // USER UID
file: selectedImg
}
// console.log('reqData', reqData)
uploadProfileMutation.mutate(reqData)
}
return (
<div className="col-xl-3 pb-xl-0 pb-5 border-right">
<div className="page-account-profil pt-5">
<div className="profile-img text-center rounded-circle">
<div className="pt-5">
<div className="bg-img m-auto">
<img
src={selectedImg ? URL.createObjectURL(selectedImg) : intialData?.personal_data?.picture ? intialData?.personal_data?.picture : getImage(avtarImage)}
// src={getImage(avtarImage)}
className="img-fluid" alt="user"
/>
<input
ref={browserImg}
className='d-none'
type='file'
accept="image/*"
onChange={(e) => profileImgChangeHandler(e)}
id='profile-image'
/>
</div>
<div className="profile pt-4">
<h4 className="mb-1">{intialData?.personal_data?.lastname} {intialData?.personal_data?.firstname}</h4>
<div style={{padding: '10px'}}>
<hr/>
</div>
</div>
</div>
</div>
<div className="profile-btn text-center">
<div>
<button onClick={browseProfileImg} className="btn btn-light text-primary mb-2">Change
</button>
</div>
{selectedImg &&
<div>
<button onClick={proceedToUpload} disabled={uploadProfileMutation.isSuccess || uploadProfileMutation.isPending} className="btn btn-light text-primary mb-2">
{uploadProfileMutation.isPending ? 'Upload...' : 'Upload New Avatar'}
</button>
</div>
}
{/* success or error message */}
{(uploadProfileMutation.isSuccess || uploadProfileMutation.isError) &&
<div>
<p className={`${uploadProfileMutation.isSuccess ? 'text-success' : 'text-danger'}`}>
{uploadProfileMutation.isSuccess ? 'Uploaded successfully' : uploadProfileMutation.error.message}
</p>
</div>
}
</div>
</div>
</div>
)
}
)
export default ProfileImage
+7 -198
View File
@@ -1,36 +1,14 @@
import React, { useEffect, useMemo, useState } from "react"; import React, { useMemo, useState } from "react";
import { Form, Formik } from "formik";
import * as Yup from "yup";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
import getImage from "../../utils/getImage"; import getImage from "../../utils/getImage";
import queryKeys from "../../services/queryKeys"; import queryKeys from "../../services/queryKeys";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { profileDetails } from "../../services/services"; import { profileDetails } from "../../services/services";
import ProfileForm from "./ProfileForm";
import LinksForm from "./LinksForm";
import ProfileImage from "./ProfileImage";
const profileValidationSchema = Yup.object().shape({
// firstname: Yup.string().required("firstname is required"),
// lastname: Yup.string().required("lastname is required"),
// email: Yup.string().required("email is required"),
// account_name: Yup.string().required("account name is required"),
// phone: Yup.string().required("phone is required"),
// full_address: Yup.string().required("full address is required"),
})
const linksValidationSchema = Yup.object().shape({
// facebook_url: Yup.string().required("facebook is required"),
// twitter_url: Yup.string().required("twitter is required"),
// blogger_url: Yup.string().required("blog is required"),
// google_url: Yup.string().required("google is required"),
// linked_url: Yup.string().required("linkedin is required"),
// website_url: Yup.string().required("website is required"),
})
export default function Settings() { export default function Settings() {
const avtarImage = "avtar/merms-user.png";
const [intialData, setInitialData] = useState({ const [intialData, setInitialData] = useState({
external_links: {}, external_links: {},
@@ -53,15 +31,6 @@ export default function Settings() {
setInitialData({external_links: data?.external_links, personal_data: data?.personal_data}) setInitialData({external_links: data?.external_links, personal_data: data?.personal_data})
},[profileInfo]) },[profileInfo])
// console.log('INI', intialData) // console.log('INI', intialData)
const updateProfile = (values, helpers) => {
console.log('Values', values)
}
const updateLinks = (values, helpers) => {
console.log('Values', values)
}
return ( return (
<> <>
@@ -90,131 +59,14 @@ export default function Settings() {
<div className="card card-statistics"> <div className="card card-statistics">
<div className="card-body p-0" style={{backgroundColor: "#f9f9fb"}}> <div className="card-body p-0" style={{backgroundColor: "#f9f9fb"}}>
<div className="row no-gutters"> <div className="row no-gutters">
<div className="col-xl-3 pb-xl-0 pb-5 border-right"> <ProfileImage intialData={intialData} />
<div className="page-account-profil pt-5">
<div className="profile-img text-center rounded-circle">
<div className="pt-5">
<div className="bg-img m-auto">
{/*<img src="assets/img/avtar/01.jpg" className="img-fluid"*/}
{/* alt="users-avatar" />*/}
<img src={getImage(avtarImage)}
className="img-fluid" alt="user"/>
</div>
<div className="profile pt-4">
<h4 className="mb-1">{intialData?.personal_data?.lastname} {intialData?.personal_data?.firstname}</h4>
<div style={{padding: '10px'}}>
<hr/>
</div>
</div>
</div>
</div>
<div className="profile-btn text-center">
<div>
<button className="btn btn-light text-primary mb-2">Upload New Avatar
</button>
</div>
{/*<div>*/}
{/* <button className="btn btn-danger">Delete</button>*/}
{/*</div>*/}
</div>
</div>
</div>
<div className="col-xl-5 col-md-6 col-12 border-t border-right"> <div className="col-xl-5 col-md-6 col-12 border-t border-right">
<div className="page-account-form"> <div className="page-account-form">
<div className="form-titel border-bottom p-3"> <div className="form-titel border-bottom p-3">
<h5 className="mb-0 py-2">Edit Your Personal Settings</h5> <h5 className="mb-0 py-2">Edit Your Personal Settings</h5>
</div> </div>
<div className="p-4"> <div className="p-4">
<Formik <ProfileForm data={intialData.personal_data} />
initialValues={intialData?.personal_data}
validationSchema={profileValidationSchema}
onSubmit={updateProfile}
>
{(props) => {
return (
<Form className=''>
<div className="form-row">
<div className="form-group col-md-12">
<label htmlFor="name1">First Name {(props.errors.firstname && props.touched.firstname) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="firstname" value={props.values?.firstname} onChange={props.handleChange} />
</div>
<div className="form-group col-md-12">
<label htmlFor="name1">Last Name {(props.errors.lastname && props.touched.lastname) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="lastname" value={props.values?.lastname} onChange={props.handleChange} />
</div>
<div className="form-group col-md-12">
<label htmlFor="name1">Account Name {(props.errors.account_name && props.touched.account_name) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="account_name" value={props.values?.account_name} onChange={props.handleChange} />
</div>
{/*<div className="form-group col-md-12">*/}
{/* <label htmlFor="title1">Email</label>*/}
{/* <input type="text" className="form-control" name="title1"*/}
{/* value="email@email.com" />*/}
{/*</div>*/}
<div className="form-group col-md-12">
<label htmlFor="phone1">Phone Number {(props.errors.phone && props.touched.phone) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="phone" value={props.values?.phone} onChange={props.handleChange} />
</div>
<div className="form-group col-md-12">
<label htmlFor="email1">Email {(props.errors.email && props.touched.email) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="email" value={props.values?.email} onChange={props.handleChange} />
</div>
</div>
<div className="form-group">
<label htmlFor="add1">Address {(props.errors.full_address && props.touched.full_address) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="full_address" value={props.values?.full_address} onChange={props.handleChange} />
</div>
{/*<div className="form-group">*/}
{/* <label htmlFor="add2">Address 2</label>*/}
{/* <input type="text" className="form-control" id="add2"*/}
{/* value="1234 North Avenue Luke Lane, South Bend, IN 360001"/>*/}
{/*</div>*/}
{/*<div className="form-row">*/}
{/* <div className="form-group col-md-4">*/}
{/* <label htmlFor="inputState3">City</label>*/}
{/* <select id="inputState3" className="form-control">*/}
{/* <option>Choose...</option>*/}
{/* <option selected="">London</option>*/}
{/* <option>Montreal</option>*/}
{/* <option>Delhi</option>*/}
{/* <option>Tokyo</option>*/}
{/* </select>*/}
{/* </div>*/}
{/* <div className="form-group col-md-4">*/}
{/* <label htmlFor="inputState4">State</label>*/}
{/* <select id="inputState4" className="form-control">*/}
{/* <option>Choose...</option>*/}
{/* <option selected="">England</option>*/}
{/* <option>California</option>*/}
{/* <option>Texas</option>*/}
{/* <option>Scotland</option>*/}
{/* </select>*/}
{/* </div>*/}
{/* <div className="form-group col-md-4">*/}
{/* <label htmlFor="inputZip">Zip</label>*/}
{/* <input type="text" className="form-control" id="inputZip"*/}
{/* value="EC1A 1BB" />*/}
{/* </div>*/}
{/*</div>*/}
{/*<div className="form-group">*/}
{/* <div className="form-check">*/}
{/* <input className="form-check-input" type="checkbox"*/}
{/* id="gridCheck" />*/}
{/* <label className="form-check-label" htmlFor="gridCheck">*/}
{/* I agree to receive email notification.*/}
{/* </label>*/}
{/* </div>*/}
{/*</div>*/}
<div style={{textAlign: "right"}}>
<button type="submit" className="btn btn-primary">Update Profile
</button>
</div>
</Form>
);
}}
</Formik>
</div> </div>
</div> </div>
</div> </div>
@@ -224,50 +76,7 @@ export default function Settings() {
<h5 className="mb-0 py-2">Your External Link</h5> <h5 className="mb-0 py-2">Your External Link</h5>
</div> </div>
<div className="p-4"> <div className="p-4">
<Formik <LinksForm data={intialData.external_links} />
initialValues={intialData?.external_links}
validationSchema={linksValidationSchema}
onSubmit={updateLinks}
>
{(props) => {
return (
<Form className=''>
<div className="form-group">
<label htmlFor="fb">Facebook URL: {(props.errors.facebook_url && props.touched.facebook_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="facebook_url" value={props.values?.facebook_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="tr">Twitter URL: {(props.errors.twitter_url && props.touched.twitter_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="twitter_url" value={props.values?.twitter_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="br">Blogger URL: {(props.errors.blogger_url && props.touched.blogger_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="blogger_url" value={props.values?.blogger_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="go">Google+ URL: {(props.errors.google_url && props.touched.google_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="google_url" value={props.values?.google_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="li">LinkedIn URL: {(props.errors.linked_url && props.touched.linked_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="linked_url" value={props.values?.linked_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="we">Website URL: {(props.errors.website_url && props.touched.website_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="website_url" value={props.values?.website_url} onChange={props.handleChange} />
</div>
<div style={{textAlign: "right"}}>
<button type="submit" className="btn btn-primary">Update Links
</button>
</div>
</Form>
);
}}
</Formik>
</div> </div>
</div> </div>
</div> </div>
+1 -1
View File
@@ -57,7 +57,7 @@ export default function Subscription() {
<h2 className="text-primary pt-3">{currentSubscription?.display_name}</h2> <h2 className="text-primary pt-3">{currentSubscription?.display_name}</h2>
</div> </div>
<div className="pt-2" style={{textAlign: 'left'}}> <div className="pt-2" style={{textAlign: 'left'}}>
<div style={{fontSize: '12px', fontWeight: 'bolder' , color: "#3E3699" }}> <div style={{fontSize: '12px', fontWeight: 'bolder', color: "#3E3699"}}>
Next Payment: {getDateTimeFromDateString(currentSubscription?.next_payment)} Next Payment: {getDateTimeFromDateString(currentSubscription?.next_payment)}
</div> </div>
</div> </div>
+2
View File
@@ -2063,6 +2063,8 @@ ul.activity {
.img-icon{ .img-icon{
width:60px; width:60px;
height:60px; height:60px;
min-width:60px;
min-height:60px;
border-radius:100px; border-radius:100px;
text-align: center; text-align: center;
line-height: 60px; line-height: 60px;
+17
View File
@@ -0,0 +1,17 @@
function sortArrayByListOrder(data) {
// Convert object to array if needed
const array = Array.isArray(data)
? [...data]
: Object.values(data);
// Sort ascending by list_order
array.sort((a, b) => {
const orderA = a.list_order ?? 0;
const orderB = b.list_order ?? 0;
return orderA - orderB;
});
return array;
}
export default sortArrayByListOrder
+1
View File
@@ -11,6 +11,7 @@ const siteLinks = {
comments: '/comments', comments: '/comments',
reports: '/reports', reports: '/reports',
subscription: '/subscription', subscription: '/subscription',
my_media: '/my-files',
subscription_success:'/subscription-success', subscription_success:'/subscription-success',
subscribe: '/subscribe', subscribe: '/subscribe',
onboard: '/subscription', onboard: '/subscription',
+6
View File
@@ -8,9 +8,15 @@ const queryKeys = {
recentAction: ['recent-action'], recentAction: ['recent-action'],
settingsData: ['settings_data'], settingsData: ['settings_data'],
myProductConfig: ['myproduct_config'], myProductConfig: ['myproduct_config'],
myTemplateConfig: ['mytemplate_config'],
productTemplateData: ['product_template_data'], productTemplateData: ['product_template_data'],
subscriptions: ['subscriptions'], subscriptions: ['subscriptions'],
profile_data: ['profile_data'], profile_data: ['profile_data'],
my_files: ['my_files'],
topics: ['topics'],
payment_report: ['payment_report'],
product_report: ['product_report'],
system_report: ['system_report'],
dashboard: ['dashboard'], dashboard: ['dashboard'],
topBar: ['top-bar'], topBar: ['top-bar'],
+121 -5
View File
@@ -1,10 +1,9 @@
import axios from "axios" import axios from "axios"
axios.interceptors.request.use( axios.interceptors.request.use(
config => { config => {
config.headers = { config.headers = {
Accept: "application/json", // Accept: "application/json",
"Access-Control-Allow-Origin": "*", "Access-Control-Allow-Origin": "*",
// "Access-Control-Expose-Headers": "Access-Control-Allow-Origin", // "Access-Control-Expose-Headers": "Access-Control-Allow-Origin",
// "Access-Control-Allow-Headers": "Origin, X-API-KEY, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method, Access-Control-Allow-Headers, Authorization, observe, enctype, Content-Length, X-Csrf-Token", // "Access-Control-Allow-Headers": "Origin, X-API-KEY, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method, Access-Control-Allow-Headers, Authorization, observe, enctype, Content-Length, X-Csrf-Token",
@@ -23,11 +22,20 @@ axios.interceptors.request.use(
const postAuxEnd = (path, postData, media=false) => { const postAuxEnd = (path, postData, media=false) => {
const basePath = media ? process.env.REACT_APP_MAIN_API : process.env.REACT_APP_MAIN_API const basePath = media ? process.env.REACT_APP_MAIN_API : process.env.REACT_APP_MAIN_API
return axios.post(`${basePath}${path}`, postData).then(res => { let newPostData = {}
if(!media){
newPostData = {...postData}
}else{
newPostData = new FormData();
for (let data in postData) {
newPostData.append(data, postData[data]);
}
}
return axios.post(`${basePath}${path}`, newPostData).then(res => {
return res return res
}).catch(err => { }).catch(err => {
// console.log('res', err.response.data) // throw new Error(err.response.data.error_message);
throw new Error(err.response.data.error_message); throw new Error(err);
}) })
} }
@@ -84,6 +92,22 @@ export const profileDetails = (reqData) => {
return postAuxEnd(`/panel/account/profile`, postData, false) return postAuxEnd(`/panel/account/profile`, postData, false)
} }
// FUNCTION TO UPDATE PROFILE
export const updateProfile = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/profile-update`, postData, false)
}
// FUNCTION TO UPDATE LINKS
export const updateLinks = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/profilelinks-update`, postData, false)
}
// FUNCTION TO GET PRODUCT BY ID // FUNCTION TO GET PRODUCT BY ID
export const MyProductData = (reqData) => { export const MyProductData = (reqData) => {
let postData = { let postData = {
@@ -214,6 +238,20 @@ export const getProductTemplateData = (reqData) => {
return postAuxEnd(`/panel/account/products/templates`, postData, false) return postAuxEnd(`/panel/account/products/templates`, postData, false)
} }
export const getProductColorStyles = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/products/color-styles`, postData, false)
}
export const getTemplateConfig = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/myproduct/template-config`, postData, false)
}
// FUNCTION TO ACTIVATE TEMPLATE // FUNCTION TO ACTIVATE TEMPLATE
export const activateTemplate = (reqData) => { export const activateTemplate = (reqData) => {
let postData = { let postData = {
@@ -222,6 +260,14 @@ export const activateTemplate = (reqData) => {
return postAuxEnd(`/panel/account/template/activate`, postData, false) return postAuxEnd(`/panel/account/template/activate`, postData, false)
} }
export const activateColorStyle = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/colorstyle/activate`, postData, false)
}
// FUNCTION TO GET PRODUCT SUBSCRIPTIONS // FUNCTION TO GET PRODUCT SUBSCRIPTIONS
export const completeProfile = (reqData) => { export const completeProfile = (reqData) => {
let postData = { let postData = {
@@ -238,6 +284,59 @@ export const getSubscriptions = (reqData) => {
return postAuxEnd(`/panel/subscription/products`, postData, false) return postAuxEnd(`/panel/subscription/products`, postData, false)
} }
export const getMediaFileList = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/media-files`, postData, false)
}
export const uploadFile = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/upload/webfiles`, postData, true)
}
// FUNCTION TO CHANGE TEMPLATE MEDIA SET
export const templateMediaSet = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/myproduct/template-media-set`, postData, false)
}
export const getReportsTopicsList = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/report/topics`, postData, false)
}
// FUNCTION TO GET PAYMENT REPORTS
export const getPaymentReports = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/report/item/payment`, postData, false)
}
// FUNCTION TO GET PRODUCT REPORTS
export const getProductReports = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/report/item/product`, postData, false)
}
// FUNCTION TO GET SYSTEM REPORTS
export const getSystemReports = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/report/item/system`, postData, false)
}
// FUNCTION TO GET COMMON PRACTICE // FUNCTION TO GET COMMON PRACTICE
export const getCommonPractice = (reqData) => { export const getCommonPractice = (reqData) => {
let postData = { let postData = {
@@ -246,6 +345,23 @@ export const getCommonPractice = (reqData) => {
return postAuxEnd(`/panel/common/practice`, postData, false) return postAuxEnd(`/panel/common/practice`, postData, false)
} }
// FUNCTION TO SET EXTERNAL URL
export const setExternalURL = (reqData) => {
let postData = {
...reqData
}
return postAuxEnd('/panel/myproduct/external-url', postData, false)
}
// FUNCTION TO UPLOAD PROFILE IMAGE
export const uploadProfileImg = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/upload/profile-picture`, postData, true)
// throw new Error('Opps')
}
+1 -1
View File
@@ -9,7 +9,7 @@ export const userSlice = createSlice({
initialState, initialState,
reducers: { reducers: {
updateUserDetails: (state, action) => { updateUserDetails: (state, action) => {
state.userDetails = { ...action.payload }; state.userDetails = { ...state.userDetails, ...action.payload };
}, },
}, },
}); });
+8
View File
@@ -0,0 +1,8 @@
import React from 'react'
import MyMedia from "../component/mymedia/MyMedia";
export default function MyMediaPage() {
return (
<MyMedia />
)
}