Compare commits

..

26 Commits

Author SHA1 Message Date
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
10 changed files with 483 additions and 321 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

+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}>
+21 -4
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']} />*/}
@@ -58,8 +70,13 @@ export default function ProductActive({productData}){
<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 -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> </>
</>
} }
</> </>
) )
@@ -112,15 +112,9 @@ const GeneralTab = memo(({
<> <>
{isCustom === true ? {isCustom === true ?
<> <>
{(tabKey === 'template_tab') && <SiteTemplateSelector name={name} data={sortedData} {(tabKey === 'template_tab') && <SiteTemplateSelector name={name} data={sortedData} isCustom={isCustom} productData={productData}/>}
isCustom={isCustom} {(tabKey === 'url_config_tab') && <URLConfiguration name={name} data={sortedData} isCustom={isCustom} productData={productData}/>}
productData={productData}/>} {(tabKey === 'color_scheme_tab') && <ColorStyleConfigure 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="page-account-form">
@@ -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">
<>
{!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>
@@ -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>
</> </>
} }
@@ -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>
+8
View File
@@ -246,6 +246,14 @@ 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)
}