Compare commits

..

63 Commits

Author SHA1 Message Date
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
victorAnumudu ec47aa5f9c added min and max length for url name 2025-09-22 15:53:22 +01:00
CHIEFSOFT\ameye 9267ded0f1 send URL name 2025-09-22 08:58:41 -04:00
ameye 0b24ca650d Merge branch 'url-name-field' of MERMS/MermsPanelReactJS into master 2025-09-19 23:16:46 +00:00
victorAnumudu b535a656a0 added url name field 2025-09-19 17:28:01 +01:00
CHIEFSOFT\ameye e69cc9130e fix page 2025-09-16 06:07:40 -04:00
CHIEFSOFT\ameye daafb66cbb url configure 2025-09-14 23:37:57 -04:00
CHIEFSOFT\ameye 3852468afe text fix 2025-09-14 23:19:49 -04:00
CHIEFSOFT\ameye d32204e08f test data 2025-09-14 23:14:24 -04:00
CHIEFSOFT\ameye 8372209923 cleanop plot 2025-09-14 22:45:23 -04:00
CHIEFSOFT\ameye 6c3f96d9a3 config url 2025-09-14 22:25:13 -04:00
CHIEFSOFT\ameye c25acecb1a Color config added 2025-09-14 07:24:48 -04:00
CHIEFSOFT\ameye 3f5ae4685e URL Configuration 2025-09-14 07:21:33 -04:00
ameye a80298c824 Merge branch 'login-input' of MERMS/MermsPanelReactJS into master 2025-09-13 20:24:28 +00:00
27 changed files with 959 additions and 407 deletions
+3
View File
@@ -16,3 +16,6 @@ REACT_APP_TERMS_LINK='https://qa-www.mermsemr.com/terms'
# Inactivity timeout/logout AT 10MINS
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
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'
# Inactivity timeout/logout AT 10MINS
REACT_APP_TIMEOUT=600000
REACT_APP_TIMEOUT=600000
# show download button
REACT_APP_SHOW_DOWNLOAD=0
+4
View File
@@ -14,6 +14,10 @@
border-radius: 10px;
}
.border-radius-10 {
border-radius: 10px;
}
.login-links{
margin-top: 50px;
display: flex;
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 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>
)
}
+3 -1
View File
@@ -8,6 +8,7 @@ import siteLinks from '../../links/siteLinks'
import { useMutation } from '@tanstack/react-query'
import { recoverPWD } from '../../services/services';
import getImage from '../../utils/getImage';
import AuthFooter from './AuthFooter';
const validationSchema = Yup.object().shape({
username: Yup.string()
@@ -49,7 +50,7 @@ export default function Forgetpwd2() {
<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="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>
{!mutation.isSuccess && <p>Please enter your username.</p>}
<Formik
@@ -97,6 +98,7 @@ export default function Forgetpwd2() {
);
}}
</Formik>
<AuthFooter />
</div>
</div>
</div>
+4 -33
View File
@@ -9,8 +9,7 @@ import siteLinks from '../../links/siteLinks'
import { loginUser } from '../../services/services'
import { updateUserDetails } from '../../store/UserDetails'
import GoogleDownload from '../../assets/img/download/andriod.jpg'
import IOSDownload from '../../assets/img/download/apple.jpg'
import AuthFooter from './AuthFooter'
export default function Login() {
@@ -47,12 +46,12 @@ export default function Login() {
console.log(error)
},
onSuccess: (res) => {
if(res?.data?.error_message){
if(res?.data && res?.data?.error_message){
throw({message: res?.data?.error_message})
}
const {token, room, uid} = res?.data
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('room', room)
@@ -133,35 +132,7 @@ export default function Login() {
</div>
</div>
</form>
<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>
<AuthFooter />
</div>
</div>
</div>
+5 -1
View File
@@ -82,9 +82,13 @@ export default function Signup() {
</button>
</div>
<div className="mt-3">
<hr />
<p className='font-medium'>Already have an account ?
<Link to={siteLinks.login}
className='hover:text-primary font-bold'> Sign In
className='bg-secondary; hover:text-primary font-bold' style={{paddingRight: '10px'}}>
<button className="btn btn-warning text-uppercase">
Sign In
</button>
</Link>
</p>
</div>
+155 -111
View File
@@ -1,101 +1,120 @@
import React, { useState } from 'react'
import { Form, Formik } from "formik";
import React, {useState} from 'react'
import {Form, Formik} from "formik";
import * as Yup from "yup";
// import LoginImg from '../../assets/bg/login.svg'
import { Link } from 'react-router-dom'
import {Link} from 'react-router-dom'
import siteLinks from '../../links/siteLinks'
import { useMutation } from '@tanstack/react-query';
import { signUpUser } from '../../services/services';
import {useMutation} from '@tanstack/react-query';
import {signUpUser} from '../../services/services';
import getImage from '../../utils/getImage';
import AuthFooter from './AuthFooter';
const validationSchema = Yup.object().shape({
email: Yup.string()
.email("Wrong email format")
// .matches(
// /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/,
// "Invalid email format"
// )
.min(3, "Minimum 3 characters")
.max(50, "Maximum 50 characters")
.required("Email is required"),
.email("Wrong email format")
// .matches(
// /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/,
// "Invalid email format"
// )
.min(3, "Minimum 3 characters")
.max(50, "Maximum 50 characters")
.required("Email is required"),
firstname: Yup.string().required("Firstname is required"),
lastname: Yup.string().required("Lastname is required"),
isChecked: Yup.bool().oneOf([true], "Please accept the terms & policy"), // use bool instead of boolean
// username: Yup.string().min(3, "Minimum 3 characters").max(50, "Maximum 50 characters").required("Email is required"),
// password: Yup.string().min(3, "Minimum 3 characters").max(50, "Maximum 50 characters").required("Email is required"),
})
})
const initialValues = {
const initialValues = {
email: '',
firstname: '',
lastname: '',
isChecked: false,
// username: '',
// password: ''
};
};
export default function Signup2() {
const mutation = useMutation({
mutationFn: (fields) => {
return signUpUser(fields)
},
onSuccess: (res) => {
console.log('res', res)
const mutation = useMutation({
mutationFn: (fields) => {
return signUpUser(fields)
},
onSuccess: (res) => {
console.log('res', res)
}
})
const signUp = (values) => {
// helpers.resetForm()
// console.log('values', values, helpers)
delete values.isChecked
mutation.mutate(values)
}
})
const signUp = (values) => {
// helpers.resetForm()
// console.log('values', values, helpers)
delete values.isChecked
mutation.mutate(values)
}
return (
<div className="app">
<div className="app-wrap">
<div className="app-contant">
<div className="vh-100 bg-white custom-bg">
<div className="container-fluid p-0">
<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-1" style={{maxWidth: '520px'}}>
<div className="mt-5 d-flex">
<div className="bg-white register p-5">
<h1 className="mb-2">{process.env.REACT_APP_PANEL_NAME}</h1>
<p>Welcome, Please create your account.</p>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={signUp}
>
{(props) => {
return (
<Form className='mt-2 mt-sm-5'>
<div className="row">
{!mutation.isSuccess ?
<>
<div className="col-12 col-md-6">
<div className="form-group">
<label className={`text-black fw-bold control-label ${(props.errors.firstname && props.touched.firstname) && 'text-danger'}`}>First Name*</label>
<input type="text" name='firstname' className="form-control" placeholder="First Name" value={props.values.firstname} onChange={props.handleChange} />
</div>
</div>
<div className="col-12 col-md-6">
<div className="form-group">
<label className={`text-black fw-bold control-label ${(props.errors.lastname && props.touched.lastname) && 'text-danger'}`}>Last Name*</label>
<input type="text" name='lastname' className="form-control" placeholder="Last Name" value={props.values.lastname} onChange={props.handleChange} />
</div>
</div>
<div className="col-12">
<div className="form-group">
<label className={`text-black fw-bold control-label ${(props.errors.email && props.touched.email) && 'text-danger'}`}>Email*</label>
<input type="email" name='email' className="form-control" placeholder="Email" value={props.values.email} onChange={props.handleChange} />
</div>
</div>
{/* <div className="col-12">
return (
<div className="app">
<div className="app-wrap">
<div className="app-contant">
<div className="vh-100 bg-white custom-bg">
<div className="container-fluid p-0">
<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-1"
style={{maxWidth: '520px'}}>
<div className="mt-5 d-flex">
<div className="bg-white register px-5 pt-5 pb-3">
<h1 className="mb-2">{process.env.REACT_APP_PANEL_NAME}</h1>
<p>Welcome, please create your account.</p>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={signUp}
>
{(props) => {
return (
<Form className='mt-2 mt-sm-5'>
<div className="row">
{!mutation.isSuccess ?
<>
<div className="col-12 col-md-6">
<div className="form-group">
<label
className={`text-black fw-bold control-label ${(props.errors.firstname && props.touched.firstname) && 'text-danger'}`}>First
Name*</label>
<input type="text" name='firstname'
className="form-control"
placeholder="First Name"
value={props.values.firstname}
onChange={props.handleChange}/>
</div>
</div>
<div className="col-12 col-md-6">
<div className="form-group">
<label
className={`text-black fw-bold control-label ${(props.errors.lastname && props.touched.lastname) && 'text-danger'}`}>Last
Name*</label>
<input type="text" name='lastname'
className="form-control"
placeholder="Last Name"
value={props.values.lastname}
onChange={props.handleChange}/>
</div>
</div>
<div className="col-12">
<div className="form-group">
<label
className={`text-black fw-bold control-label ${(props.errors.email && props.touched.email) && 'text-danger'}`}>Email*</label>
<input type="email" name='email'
className="form-control"
placeholder="Email"
value={props.values.email}
onChange={props.handleChange}/>
</div>
</div>
{/* <div className="col-12">
<div className="form-group">
<label className={`text-black fw-bold control-label ${(props.errors.username && props.touched.username) && 'text-danger'}`}>Username*</label>
<input type="text" name='username' className="form-control" placeholder="Username" value={props.values.username} onChange={props.handleChange} />
@@ -107,62 +126,87 @@ export default function Signup2() {
<input type="password" name='password' className="form-control" placeholder="Password" value={props.values.password} onChange={props.handleChange} />
</div>
</div> */}
<div className="col-12">
<div className="form-check">
<input name='isChecked' className="form-check-input" type="checkbox" id="gridCheck" value={props.values.isChecked} onChange={props.handleChange} />
<label className="form-check-label" htmlFor="gridCheck">
I accept terms & policy
</label>
</div>
<span className={`${(props.errors.isChecked && props.touched.isChecked) && 'text-danger'}`}>{props.errors.isChecked}</span>
</div>
<div className="col-12">
<div className="form-check">
<input name='isChecked'
className="form-check-input"
type="checkbox" id="gridCheck"
value={props.values.isChecked}
onChange={props.handleChange}/>
<label className="form-check-label"
htmlFor="gridCheck">
I accept terms & policy
</label>
</div>
<span
className={`${(props.errors.isChecked && props.touched.isChecked) && 'text-danger'}`}>{props.errors.isChecked}</span>
</div>
{mutation.error &&
<>
<div className="col-12">
<p className='text-danger'>{mutation.error.message}</p>
</div>
{mutation.error &&
<>
<div className="col-12">
<p className='text-danger'>{mutation.error.message}</p>
</div>
</>
}
<div className="col-12 mt-3 text-end">
<button type='submit'
className="btn btn-primary text-uppercase">{mutation.isPending ? 'loading...' : 'Sign up'}</button>
</div>
</>
:
<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: '-30px'}}>Check
your email to continue.</h4>
<img className='' style={{width: '200px'}}
src={getImage('check-mail.png')}
alt='mail-alert'/>
<Link to={siteLinks.login}
className='p-2 text-primary'
style={{color: '#6FCAEF'}}>Home</Link>
</div>
</div>
}
<div className="col-12 mt-3 text-end">
<button type='submit' className="btn btn-primary text-uppercase">{mutation.isPending ? 'loading...' : 'Sign up'}</button>
</div>
</>
:
<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: '-30px'}}>Check your email to continue.</h4>
<img className='' style={{width: '200px'}} src={getImage('check-mail.png')} alt='mail-alert' />
<Link to={siteLinks.login} className='p-2 text-primary' style={{color: '#6FCAEF'}}>Home</Link>
<div className="col-12 mt-3">
<p>
<span style={{paddingLeft: '5px' , fontWeight: 'bolder'}}>Already have an account? </span>
<Link
to={siteLinks.login}>
<button
className="btn btn-warning text-uppercase">
Sign In
</button>
</Link>
</p>
</div>
</div>
}
<div className="col-12 mt-3">
<p>Already have an account ?<Link to={siteLinks.login}> Sign In</Link></p>
</div>
</div>
</Form>
);
}}
</Formik>
</Form>
);
}}
</Formik>
<AuthFooter />
</div>
</div>
</div>
</div>
{/* <div className="signup-bg col-sm-6 col-xxl-9 col-lg-7 b-gradient o-hidden order-1 order-sm-2">
{/* <div className="signup-bg col-sm-6 col-xxl-9 col-lg-7 b-gradient o-hidden order-1 order-sm-2">
<div className="row align-items-center h-100">
<div className="col-7 mx-auto ">
<img className="img-fluid" src={LoginImg} alt="" />
</div>
</div>
</div> */}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
)
}
+1 -1
View File
@@ -41,7 +41,7 @@ export default function SocketIOContextProvider({children}) {
socket.on(socketOnEvents.receive_message, (data) => {
// setSocketMsgReceived(data.message);
// 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({
queryKey: [...queryKeys.recentAction],
// type: 'active',
+1 -1
View File
@@ -59,7 +59,7 @@ export default function DashPayments() {
</tr>
</thead>
<tbody>
{payments.length > 0 ?
{payments && payments?.length > 0 ?
payments.map((item, index) => {
return (
<tr key={index}>
+1 -1
View File
@@ -50,7 +50,7 @@ export default function Products() {
</div>
<div className="report-details">
<p><span style={{fontWeight: 'bolder', color: '#00557A'}}>{product?.status_text}</span></p>
<h4><span style={{paddingLeft: '10px'}}>{product?.name}</span></h4>
<h4><span className='text-truncate' style={{paddingLeft: '10px'}}>{product?.name}</span></h4>
</div>
</div>
</Link>
+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>*/}
</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">
{isFetching ?
<>
+2 -2
View File
@@ -77,7 +77,7 @@ export default function RecentActions() {
<h4>{dataAction?.data?.completed}</h4>
</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">
<thead>
<tr>
@@ -94,7 +94,7 @@ export default function RecentActions() {
let text = action?.status == '5' ? 'completed' : action?.status == '3' ? 'verifying' : action?.status == '0' ? 'processing' : 'processing'
return (
<tr key={index}>
<td>{action?.id}</td>
<td>{(action?.id).toString().slice(-4)}</td>
<td>{action?.action_label}</td>
<td>{new Date(action?.added).toDateString()}</td>
<td>
@@ -78,6 +78,7 @@ export default function UserHeader(){
<span className="bg-success user-status"></span>
</a>
<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="d-flex align-items-center justify-content-between">
<div className="mr-1">
+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 getImage from "../../utils/getImage";
import { useMutation, useQuery } from "@tanstack/react-query";
import { productRefreshSite, getSettingsData } from "../../services/services";
import Settings from "./settingsTab/Settings";
import queryKeys from "../../services/queryKeys";
import {SocketContextValues} from "../context/SocketIOContext";
export default function ProductActive({productData}){
const {joinRoom} = SocketContextValues() // Destructures values from socket context
const iframe = useRef()
const [refreshMsg, setRefreshMsg] = useState('')
const refresh = useMutation({
mutationFn: (fields) => {
return productRefreshSite(fields)
},
onSuccess: (res) => {
setRefreshMsg(res?.data?.message)
setTimeout(()=>{setRefreshMsg('')},3000)
iframe.current.src += ''
}
})
@@ -31,6 +36,13 @@ export default function ProductActive({productData}){
}
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(
<>
{/*<BreadcrumbComBS title='Active Product Name' paths={['Dashboard', 'Product']} />*/}
@@ -54,12 +66,17 @@ export default function ProductActive({productData}){
</button>
</div>
</div>
<div className="card-body">
<div className="card-body" style={{minHeight: '680px'}}>
<iframe ref={iframe} style={{borderWidth: '0px'}} src={externalUrl} width="100%" height="600" title={externalUrl}></iframe>
</div>
<div className="p-4 ml-auto">
<button type="button" onClick={handleRefresh} className="btn btn-primary">{refresh.isPending ? 'Loading...' : 'Refresh Site'}
</button>
<div className="d-flex justify-end gap-3">
{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>
@@ -70,7 +87,7 @@ export default function ProductActive({productData}){
<h4 className="card-title"> Site Settings </h4>
</div>
</div>
<div className="card-body">
<div className="card-body" style={{minHeight: '680px'}}>
<Settings productData={productData} />
</div>
</div>
+70 -63
View File
@@ -1,39 +1,41 @@
import { useEffect } from "react";
import { useQuery } from "@tanstack/react-query";
import {useEffect} from "react";
import {useQuery} from "@tanstack/react-query";
import queryKeys from "../../services/queryKeys";
import { productProvision } from "../../services/services";
import {productProvision} from "../../services/services";
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 productTitle = props?.productData?.title;
const productDescription = props?.productData?.description;
const productTitle = props?.productData?.title;
const productDescription = props?.productData?.description;
const productID = props?.productData?.product_id
const productUID = props?.productData?.product_uid
const productSubUID = props?.productData?.product_subscription_uid
const reqData = {
product_id : productID,
product_id: productID,
product_subscription_uid: productSubUID
}
const {data:provision, isFetching, isError, error} = useQuery({
const {data: provision, isFetching, isError, error} = useQuery({
queryKey: queryKeys.myproduct_provision,
queryFn: () => productProvision(reqData)
})
const provisionData = provision?.data
useEffect(()=>{
joinRoom(productSubUID); // provision subscription room
},[])
useEffect(() => {
const provision_room = "PROVISION_" + productSubUID;
console.log("JOINING ROOM ON START *** ", provision_room);
joinRoom(provision_room); // provision subscription room
}, [])
return (
<>
{isFetching ?
{isFetching ?
<>
<div className="row">
<div className="col-12">
@@ -42,82 +44,87 @@ export default function ProductProvision(props){
</div>
</>
: isError ?
<div className="row">
<div className="col-12">
<p className='text-danger'>{error.message}</p>
</div>
</div>
:
<>
<div className="row">
<div className="col-md-12">
<div className="card card-statistics">
<div className="card-header">
<div className="col-12">
<p className='text-danger'>{error.message}</p>
</div>
</div>
:
<>
<div className="row">
<div className="col-md-12">
<div className="card card-statistics">
<div className="card-header">
<div className="card-heading">
<h4 className="card-title">Creating - {productTitle} </h4>
<h4 className="card-title">Creating - {productTitle} </h4>
</div>
</div>
<div className="card-body">
<div className="progress">
<div className="progress-bar progress-bar-striped progress-bar-animated" role="progressbar"
aria-valuenow={`${provisionData?.percent_completed}%`} aria-valuemin="0" aria-valuemax="100" style={{width:`${provisionData?.percent_completed}%`}} ></div>
<div className="progress-bar progress-bar-striped progress-bar-animated"
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 className="row">
<div className="col-md-12">
<div className="row">
<div className="col-md-12">
</div>
</div>
</div>
<div className="row">
<div className="col-lg-6">
<div className="row">
<div className="col-lg-6">
<div className="card card-statistics">
<div className="card-header">
<div className="card-heading">
<h4 className="card-title">Progress Information</h4>
<div className="card card-statistics">
<div className="card-header">
<div className="card-heading">
<h4 className="card-title">Progress Information</h4>
</div>
</div>
</div>
<div className="card-body">
<div className="table-responsive">
<table className="table table-info mb-0">
<thead>
<tr>
<th scope="col" style={{width: '10px'}}>#</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>
<div className="card-body">
<div className="table-responsive">
<table className="table table-info mb-0">
<thead>
<tr>
<th scope="col" style={{width: '10px'}}>#</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>
))}
</tbody>
</table>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div className="col-lg-6">
<div className="card card-statistics ">
<h4 className="card-title" style={{padding:'10px'}}>Started creating your selection</h4>
<img className="card-img-top" src={getImage('widget/working.jpg')} alt="Card image cap" />
{/* <div className="card-body">
<div className="col-lg-6">
<div className="card card-statistics ">
<h4 className="card-title" style={{padding: '10px'}}>Started creating your
selection</h4>
<img className="card-img-top" src={getImage('widget/working.jpg')}
alt="Card image cap"/>
{/* <div className="card-body">
<div className="" dangerouslySetInnerHTML={{__html: productDescription}}/>
</div> */}
</div>
</div>
</div>
</div>
</div>
</>
</>
}
</>
)
@@ -0,0 +1,179 @@
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";
const ColorStyleConfigure = memo(({name = 'Full Name', data, productData}) => {
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
@@ -6,8 +6,18 @@ import NoYesBooleanDropdown from './NoYesBooleanDropdown';
import {IoMdArrowDropdown} from 'react-icons/io';
import queryKeys from '../../../services/queryKeys';
import sortObjectByListOrder from '../../../helpers/sortObjectByListOrder';
import URLConfiguration from "./URLConfiguration";
import ColorStyleConfigure from "./ColorStyleConfigure";
const GeneralTab = memo(({name = 'Full Name', data, isCustom, productData, backendValues, setFieldsChanged}) => {
const GeneralTab = memo(({
name = 'Full Name',
data,
tabKey,
isCustom,
productData,
backendValues,
setFieldsChanged
}) => {
const queryClient = useQueryClient()
@@ -81,7 +91,7 @@ const GeneralTab = memo(({name = 'Full Name', data, isCustom, productData, backe
}
submitSettings.mutate(reqData)
}
console.log(tabKey);
return (
<>
{backendValues?.isFetching || !backendValues?.data ?
@@ -101,8 +111,17 @@ const GeneralTab = memo(({name = 'Full Name', data, isCustom, productData, backe
:
<>
{isCustom === true ?
<SiteTemplateSelector name={name} data={sortedData} isCustom={isCustom}
productData={productData}/>
<>
{(tabKey === 'template_tab') &&
<SiteTemplateSelector name={name} data={sortedData} isCustom={isCustom}
productData={productData}/>}
{(tabKey === 'url_config_tab') &&
<URLConfiguration name={name} data={sortedData} isCustom={isCustom}
productData={productData}/>}
{(tabKey === 'color_scheme_tab') &&
<ColorStyleConfigure name={name} data={sortedData} isCustom={isCustom}
productData={productData}/>}
</>
:
<div className="page-account-form">
<div className="p-0" style={{minHeight: '500px'}}>
@@ -109,7 +109,7 @@ const Settings = memo(({productData}) => {
// id={value.controls} role="tabpanel"
// aria-labelledby={key}
>
<GeneralTab 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>
))}
</>
@@ -1,14 +1,16 @@
import React, {memo} from 'react'
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 { 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 {data:templateData, isFetching, isError, error} = useQuery({
const {data: templateData, isFetching, isError, error} = useQuery({
queryKey: queryKeys.productTemplateData,
queryFn: () => {
let reqData = {
@@ -16,7 +18,7 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id
}
return getProductTemplateData(reqData)
return getProductTemplateData(reqData)
},
staleTime: 0
})
@@ -24,16 +26,17 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
const templateResponse = templateData?.data
const currentTemUID = templateResponse?.current_template_uid
const templates = templateResponse?.templates
const custom_template_name = templateResponse?.custom_template_name
// console.log('data Template', templateResponse)
// console.log("Page data == ", data)
console.log("Page data == ", data)
const handleActivateTemplate = useMutation({
mutationFn: (fields) => {
return activateTemplate(fields)
},
onSuccess: (res) => {
if(res?.data?.resultCode != '0'){
if (res?.data?.resultCode != '0') {
throw new Error(res.data.resultDescription)
}
queryClient.refetchQueries({ // refetches productProvision API call
@@ -41,7 +44,7 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
})
},
onSettled: () => {
setTimeout(()=>{
setTimeout(() => {
handleActivateTemplate.reset()
}, 3000)
}
@@ -57,51 +60,73 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
// 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">
{isFetching ?
<>
{!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 className="row">
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
))
}
{/* {Object.entries(data)?.map(([key, value]) => (
</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'}}>
<>
{!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 className="card card-statistics">
<div className="card-body">
@@ -116,24 +141,25 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
</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 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>
@@ -0,0 +1,141 @@
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 <>
<div className="card card-statistics">
<div className="card-header">
<div className="card-heading">
<h4 className="card-title" style={{textTransform: 'none'}}>{defaultUrl}</h4>
</div>
</div>
{/*<div className="card-body">*/}
{/* <div className="form-group">*/}
{/* /!*<label htmlFor="exampleInputEmail1">Email address</label>*!/*/}
{/* <input type="email" className="form-control"*/}
{/* aria-describedby="defaultUrlHelp" value={defaultUrl} readOnly={true} />*/}
{/* </div>*/}
{/*</div>*/}
</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>
</Form>
);
}}
</Formik>
</>
}
export default URLConfiguration
@@ -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 { useLocation } from "react-router-dom";
// import { Form, Formik } from "formik";
import { Form, Formik } from "formik";
import * as Yup from "yup";
import {useMutation, useQuery} from "@tanstack/react-query";
import getImage from "../../utils/getImage";
@@ -13,17 +13,19 @@ import {updateUserDetails} from "../../store/UserDetails";
import {useDispatch} from "react-redux";
// const validationSchema = Yup.object().shape({
// practice: Yup.string().required("Required"),
// specialization: Yup.string().required("Required"),
// introduction: Yup.string().min(1, "Minimum 10 characters").max(50, "Maximum 50 characters").required("Required"),
// })
// const initialValues = {
// practice: '',
// specialization: '',
// introduction: '',
// };
const validationSchema = Yup.object().shape({
practice: Yup.string().required("Required"),
specialization: Yup.string().when('practice', {
is: (value) => typeof value === 'string' && value.trim().length > 0,
then: (schema) => schema.required('Required'),
otherwise: (schema) => schema,
}),
introduction: Yup.string().min(1, "Minimum 1 character").max(50, "Maximum 50 characters"),
url_name: Yup.string().min(6, "Minimum 6 characters").max(16, "Maximum 16 characters").required("Required").matches(
/^[a-zA-Z0-9]+$/, // Regex for alphanumeric characters
'Must contain only alphanumeric characters' // Custom error message
),
})
export default function ProfileCompleteCom() {
@@ -35,28 +37,27 @@ export default function ProfileCompleteCom() {
const {state: {redirectLink}} = useLocation()
const [practices, setPractices] = useState([])
const [specialties, setSpecialties] = useState([])
const [initialValues, setInitialValues] = useState({
practice: '',
specialization: '',
introduction: '',
url_name: ''
})
const specialties = useMemo(() => { // FUNCTION TO UPDATE SPECIALITY ARRAY EACH TIME PRACTICE CHANGES
const handleUpdateSpecialties = (e) => {
setInitialValues(prev => ({...prev, specialization: ''}))
if (!initialValues.practice) {
return []
}
const specialtiesArr = practices.filter(item => item.practice == initialValues.practice)[0]?.specialties
return specialtiesArr
}, [initialValues.practice])
const specialtiesArr = practices.filter(item => item.practice == e.target.value)[0]?.specialties
setSpecialties(specialtiesArr)
}
const mutation = useMutation({
mutationFn: (fields) => {
const {practice, specialization} = fields
if (!practice || !specialization) {
throw new Error('Please select both practice and specialization fields')
const {practice, specialization, url_name} = fields
if (!practice || !specialization || !url_name) {
throw new Error('Please Select both Practice, Specialization and Enter URL_Name')
}
return completeProfile(fields)
},
@@ -93,15 +94,11 @@ export default function ProfileCompleteCom() {
}
})
const handlePracticeChange = ({target: {name, value}}) => {
setInitialValues(prev => ({...prev, [name]: value}))
}
const handleCompleteProfile = () => { // FUNCTION TO COMPLETE PROFILE
const handleCompleteProfile = (values) => { // FUNCTION TO COMPLETE PROFILE
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
...initialValues
...values
}
mutation.mutate(reqData)
}
@@ -145,89 +142,146 @@ export default function ProfileCompleteCom() {
<div className="card-body">
<div className='h-100 row flex-column'>
{/* <div className="row"> */}
<>
<div className="">
<div className="form-group position-relative">
<label className={`text-black fw-bold control-label`}>Practice :</label>
<div className="position-relative">
{/* <select onChange={props.handleChange} name='practice' value={props.values.practice} className="form-control">
<option value=''>Select</option>
{practices.map((practice, index)=>(
<option key={index} value={practice.practice}>{practice.practice}</option>
))}
</select> */}
<select onChange={handlePracticeChange} name='practice'
value={initialValues.practice} className="form-control">
<option value=''>Select</option>
{practices.map((practice, index) => (
<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>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleCompleteProfile}
enableReinitialize={true}
>
{(props) => {
return (
<Form className='mt-2'>
<>
<div className="">
<div className="form-group position-relative">
<label className={`text-black fw-bold control-label`}>Practice : <span className="text-danger">{(props.errors.practice && props.touched.practice) && props.errors.practice}</span></label>
<div className="position-relative">
{/* <select onChange={props.handleChange} name='practice' value={props.values.practice} className="form-control">
<option value=''>Select</option>
{practices.map((practice, index)=>(
<option key={index} value={practice.practice}>{practice.practice}</option>
))}
</select> */}
<select
onChange={(e) => {props.handleChange(e); props.setFieldValue('specialization', ''); handleUpdateSpecialties(e)}}
name='practice'
value={props.values.practice} className="form-control">
<option value=''>Select</option>
{practices.map((practice, index) => (
<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="form-group">
<label className={`text-black fw-bold control-label`}>Your
Specialization :</label>
<div className="position-relative">
<select onChange={handlePracticeChange} name='specialization'
value={initialValues.specialization}
className="form-control">
<option value=''>Select</option>
{specialties.map((specialty, index) => (
<option key={index} value={specialty}>{specialty}</option>
))}
</select>
<IoMdArrowDropdown className='position-absolute w-auto' style={{
top: '50%',
right: '2px',
transform: 'translateY(-50%)'
}}/>
</div>
</div>
</div>
<div className="">
<div className="form-group">
<label className={`text-black fw-bold control-label`}>Your
Specialization : <span className="text-danger">{(props.errors.specialization && props.touched.specialization) && props.errors.specialization}</span></label>
<div className="position-relative">
<select onChange={props.handleChange} name='specialization'
value={props.values.specialization}
className="form-control">
<option value=''>Select</option>
{specialties.map((specialty, index) => (
<option key={index} value={specialty}>{specialty}</option>
))}
</select>
<IoMdArrowDropdown className='position-absolute w-auto' style={{
top: '50%',
right: '2px',
transform: 'translateY(-50%)'
}}/>
</div>
</div>
</div>
<div className="">
<div className="form-group position-relative">
<label className={`text-black fw-bold control-label`}>Other General
Information :</label>
<textarea name='introduction' rows={10} style={{resize: 'none'}}
className="form-control" value={initialValues.introduction}
onChange={handlePracticeChange}/>
</div>
</div>
<div className="">
<div className="form-group position-relative">
<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>
<textarea name='introduction' rows={5} style={{resize: 'none'}}
className="form-control" value={props.values.introduction}
onChange={props.handleChange}/>
</div>
</div>
<div className="">
<div className="form-group position-relativ'e">
{/*<label className={`text-black fw-bold control-label`}>What we use this*/}
{/* information for :</label>*/}
<div style={{fontSize: '14px', borderRadius: '10px', backgroundColor: 'aliceblue', fontWeight:'bolder', padding: '15px' }}>
MERMS A.I. agents use the information supplied to help generate useful entries for your product settings.
</div>
</div>
</div>
<div className="">
<div className="form-group position-relativ'e">
{/*<label className={`text-black fw-bold control-label`}>What we use this*/}
{/* information for :</label>*/}
<div style={{
fontSize: '14px',
borderRadius: '10px',
backgroundColor: 'aliceblue',
fontWeight: 'bolder',
padding: '15px'
}}>
MERMS A.I. agents use the information supplied to help generate
useful entries for your product settings.
</div>
</div>
</div>
{(mutation.isError || mutation.isSuccess) &&
<>
<div className="">
<p className={`${mutation.isSuccess ? 'text-success' : 'text-danger'}`}>{mutation.isSuccess ? 'Completed successfully, redirecting...' : mutation.error.message}</p>
</div>
</>
}
<div className="">
<div className="form-group position-relative">
<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'}}>
{/* <select onChange={handlePracticeChange} name='url_name'
value={initialValues.url_name} className="form-control">
<option value=''>Select</option>
{practices.map((practice, index) => (
<option key={index}
value={practice.practice}>{practice.practice}</option>
))}
</select>
<IoMdArrowDropdown className='position-absolute w-auto' style={{
top: '50%',
right: '2px',
transform: 'translateY(-50%)'
}}/> */}
<input
className="form-control"
onChange={props.handleChange} name='url_name'
value={props.values.url_name}
minLength={6}
maxLength={16}
/>
<p className="border-radius-10 p-2 border border-warning"
style={{fontSize: "1.0rem"}}>We use the URL Name to form part of
your default URL when we configure
a new URL for your products. You can always change your product
URL. <br/>
<b>Example : <span style={{color: 'red'}}>url_name</span>.product.mermsemr.com
</b>
</p>
</div>
</div>
</div>
<div className="mt-auto text-end">
<button type='button' onClick={handleCompleteProfile}
className="btn btn-primary text-uppercase">{mutation.isPending ? 'loading...' : 'Continue'}</button>
</div>
</>
{(mutation.isError || mutation.isSuccess) &&
<>
<div className="">
<p className={`${mutation.isSuccess ? 'text-success' : 'text-danger'}`}>{mutation.isSuccess ? 'Completed successfully, redirecting...' : mutation.error.message}</p>
</div>
</>
}
<div className="mt-auto text-end">
<button type='submit'
className="btn btn-primary text-uppercase">{mutation.isPending ? 'loading...' : 'Continue'}</button>
</div>
</>
</Form>
);
}}
</Formik>
{/* </div> */}
</div>
</div>
+16 -6
View File
@@ -7,13 +7,23 @@ import ReactApexChart from "react-apexcharts";
series: [
{
// name: "High - 2013",
name: 'High',
name: 'Professional Website',
data: [28, 29, 33, 36, 32, 32, 33, 33, 36, 32, 32, 33]
},
{
// name: "Low - 2013",
name: 'Low',
name: 'Personal Website',
data: [12, 11, 14, 18, 17, 13, 13, 14, 18, 17, 13, 13]
},
{
// name: "Low - 2013",
name: 'Personal Forum',
data: [10, 11, 14, 19, 18, 23, 17, 14, 10, 17, 23, 10]
},
{
// name: "High - 2013",
name: 'Professional Forum',
data: [20, 19, 30, 36, 30, 35, 33, 33, 36, 32, 32, 30]
}
],
options: {
@@ -35,7 +45,7 @@ import ReactApexChart from "react-apexcharts";
show: false
}
},
colors: ['#77B6EA', '#545454'],
colors: ['#77B6EA', '#545454', '#F50898','#213ece'],
dataLabels: {
enabled: true,
},
@@ -43,7 +53,7 @@ import ReactApexChart from "react-apexcharts";
curve: 'smooth'
},
title: {
text: 'Average High & Low Temperature',
text: 'Recent Sites Traffic',
align: 'left'
},
grid: {
@@ -64,7 +74,7 @@ import ReactApexChart from "react-apexcharts";
},
yaxis: {
title: {
text: 'Temperature'
text: 'Visits'
},
min: 5,
max: 40
@@ -86,7 +96,7 @@ import ReactApexChart from "react-apexcharts";
return (
<div>
<div id="chart">
<ReactApexChart options={state.options} series={state.series} type="line" height={350} />
<ReactApexChart options={state.options} series={state.series} type="line" height={450} />
</div>
<div id="html-dist"></div>
</div>
+24 -2
View File
@@ -26,8 +26,8 @@ const postAuxEnd = (path, postData, media=false) => {
return axios.post(`${basePath}${path}`, postData).then(res => {
return res
}).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);
})
}
@@ -214,6 +214,12 @@ export const getProductTemplateData = (reqData) => {
return postAuxEnd(`/panel/account/products/templates`, postData, false)
}
export const getProductColorStyles = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/products/color-styles`, postData, false)
}
// FUNCTION TO ACTIVATE TEMPLATE
export const activateTemplate = (reqData) => {
let postData = {
@@ -222,6 +228,14 @@ export const activateTemplate = (reqData) => {
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
export const completeProfile = (reqData) => {
let postData = {
@@ -246,6 +260,14 @@ export const getCommonPractice = (reqData) => {
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)
}