Compare commits

..

127 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
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
victorAnumudu e9bc1c1318 added login input fields max length 2025-09-13 16:47:18 +01:00
ameye 2543a91b19 Merge branch 'profile-name-fix' of MERMS/MermsPanelReactJS into master 2025-09-08 21:34:30 +00:00
victorAnumudu 4c38b0a31f profile name fixed 2025-09-08 22:30:18 +01:00
ameye 6ed396a34c Merge branch 'profile-settings' of MERMS/MermsPanelReactJS into master 2025-09-08 20:45:21 +00:00
victorAnumudu 465a480f02 profile details endpoint added 2025-09-08 21:24:22 +01:00
CHIEFSOFT\ameye 4409ed45f3 comments page cleanup 2025-09-08 11:32:08 -04:00
CHIEFSOFT\ameye 470c94ae5e remove address 2 2025-09-08 11:08:13 -04:00
CHIEFSOFT\ameye 52fd4275e9 plot page 2025-09-06 12:53:43 -04:00
CHIEFSOFT\ameye 0dadaf00b0 page style 2025-09-06 12:52:19 -04:00
CHIEFSOFT\ameye 0fbe0f4c3f start plot page 2025-09-06 12:38:17 -04:00
CHIEFSOFT\ameye 464182e583 Traffic page 2025-09-06 12:17:14 -04:00
ameye 517886df7e Merge branch 'config-reload' of MERMS/MermsPanelReactJS into master 2025-09-05 15:32:06 +00:00
victorAnumudu 3db5d56410 reloads config settings endpoint on change of product 2025-09-05 15:43:35 +01:00
CHIEFSOFT\ameye 6c107c8000 page c;leanup 2025-09-04 13:57:49 -04:00
ameye 225a3d36e4 Merge branch 'settings-page-sort' of MERMS/MermsPanelReactJS into master 2025-09-04 17:47:39 +00:00
victorAnumudu f66f92c58d fixed settings page sorting by list order 2025-09-04 17:46:47 +01:00
CHIEFSOFT\ameye 599e8a7715 Fix page 2025-09-04 11:52:35 -04:00
ameye 97b75e0d9b Merge branch 'signup-redirect' of MERMS/MermsPanelReactJS into master 2025-09-04 11:02:23 +00:00
victorAnumudu 4bc985892e redirects to /start on complete signup 2025-09-04 11:43:48 +01:00
CHIEFSOFT\ameye c951f925d8 panel data key 2025-09-03 21:00:28 -04:00
CHIEFSOFT\ameye 1681ca1437 Start page 2025-09-02 14:58:59 -04:00
ameye d2cb38f141 Merge branch 'template-endpoint' of MERMS/MermsPanelReactJS into master 2025-09-01 20:10:56 +00:00
victorAnumudu 67d26f6ab0 template endpoint added 2025-09-01 21:01:47 +01:00
ameye 72dc343d01 Merge branch 'dashboard-payments-fix' of MERMS/MermsPanelReactJS into master 2025-09-01 15:11:16 +00:00
victorAnumudu 4969ad1ae4 fixed dashboard payments endpoint 2025-09-01 16:08:26 +01:00
ameye 9e33578ef2 Merge branch 'payments-endpoint' of MERMS/MermsPanelReactJS into master 2025-09-01 14:34:42 +00:00
victorAnumudu be6dab1ec9 added dashboard payments endpoint 2025-09-01 15:28:47 +01:00
CHIEFSOFT\ameye aae69ffd3b footer data 2025-09-01 09:24:30 -04:00
CHIEFSOFT\ameye 6294d7cafd Modify link 2025-09-01 08:39:10 -04:00
CHIEFSOFT\ameye be483c9451 Fix syntax 2025-08-31 14:26:10 -04:00
CHIEFSOFT\ameye 6559d00052 start app 2025-08-31 14:25:00 -04:00
CHIEFSOFT\ameye 6f3ed362b7 Environmant label 2025-08-31 14:21:31 -04:00
CHIEFSOFT\ameye fe88a6d7f2 added Site name to env 2025-08-31 14:07:50 -04:00
CHIEFSOFT\ameye 6adb6aed1f fix text 2025-08-31 13:57:02 -04:00
CHIEFSOFT\ameye 373c5427c2 reformat code 2025-08-31 07:36:10 -04:00
ameye a9473debdb Merge branch 'date-formatting' of MERMS/MermsPanelReactJS into master 2025-08-31 11:26:32 +00:00
CHIEFSOFT\ameye e4eb445059 Fix code layout 2025-08-30 23:02:49 -04:00
CHIEFSOFT\ameye 44b2e08006 Fix header text 2025-08-30 22:27:35 -04:00
victorAnumudu 17239f9ea8 formatted date string and image bug 2025-08-30 22:30:45 +01:00
CHIEFSOFT\ameye 519b6e5585 Removed spaces 2025-08-30 15:42:22 -04:00
CHIEFSOFT\ameye 56fb97eda1 settings age 2025-08-30 14:29:05 -04:00
CHIEFSOFT\ameye 1abc998120 Icons 2025-08-30 13:58:58 -04:00
CHIEFSOFT\ameye 858dd39936 Improve contacts 2025-08-30 12:46:32 -04:00
CHIEFSOFT\ameye 292c8409d6 fix UID use 2025-08-30 12:20:24 -04:00
CHIEFSOFT\ameye 45c457e210 entries help 2025-08-30 11:51:11 -04:00
CHIEFSOFT\ameye 1e7014172d new image data 2025-08-30 11:17:47 -04:00
CHIEFSOFT\ameye 98bf50fcde fix text 2025-08-30 10:32:16 -04:00
CHIEFSOFT\ameye 4385d8bec0 fix page rad 2025-08-30 10:23:58 -04:00
CHIEFSOFT\ameye d1c86ec9b2 fix text 2025-08-30 09:41:34 -04:00
CHIEFSOFT\ameye dae4862db2 styling tob bar 2025-08-30 09:32:59 -04:00
CHIEFSOFT\ameye d80bb55205 fonst sizes 2025-08-30 09:11:35 -04:00
CHIEFSOFT\ameye 3205d5627e more styles 2025-08-30 08:41:25 -04:00
CHIEFSOFT\ameye bfea6b956b option_name 2025-08-30 07:53:05 -04:00
CHIEFSOFT\ameye 4ec74ff895 sub tabs 2025-08-30 07:48:26 -04:00
ameye 84b4523154 Merge branch 'template-bug-fix' of MERMS/MermsPanelReactJS into master 2025-08-26 21:38:17 +00:00
victorAnumudu c19cfdd524 fixed template data bug 2025-08-26 18:15:55 +01:00
ameye 2476c491be Merge branch 'template-api-fix' of MERMS/MermsPanelReactJS into master 2025-08-26 15:57:36 +00:00
victorAnumudu ece281ef8a API data added 2025-08-26 16:04:02 +01:00
ameye 8ae0de1004 Merge branch 'template-api' of MERMS/MermsPanelReactJS into master 2025-08-25 19:43:40 +00:00
victorAnumudu 13ac1d1f85 added template API 2025-08-25 19:55:01 +01:00
CHIEFSOFT\ameye 5a408311ae Activation button 2025-08-25 09:09:42 -04:00
CHIEFSOFT\ameye 9c64d8178e Fix text 2025-08-24 08:30:42 -04:00
CHIEFSOFT\ameye be8f54bb42 Fix text 2025-08-24 08:06:06 -04:00
CHIEFSOFT\ameye 2c6102dfa5 options 2025-08-23 23:02:53 -04:00
CHIEFSOFT\ameye bbd6ecac58 Added link 2025-08-23 18:31:01 -04:00
CHIEFSOFT\ameye 56ce8f0556 pakage json 2025-08-23 17:06:21 -04:00
CHIEFSOFT\ameye 9e4de10251 stripe button 2025-08-23 16:53:50 -04:00
CHIEFSOFT\ameye 777f3cbec4 merms panel 2025-08-23 08:30:32 -04:00
CHIEFSOFT\ameye a6e9d507ae fix sub page 2025-08-23 08:10:06 -04:00
CHIEFSOFT\ameye c1af89919e fix description 2025-08-23 07:22:03 -04:00
CHIEFSOFT\ameye a8686ac2e7 fix descriptions 2025-08-23 07:15:19 -04:00
ameye 591b1fa3b9 Merge branch 'payment-switch' of MERMS/MermsPanelReactJS into master 2025-08-19 18:28:58 +00:00
victorAnumudu 3d5bdc2afd fixed payment switch btn 2025-08-19 19:11:49 +01:00
CHIEFSOFT\ameye cdb676a047 payment forms 2025-08-19 11:54:55 -04:00
ameye 34febdedfa Merge branch 'selected-subscribe' of MERMS/MermsPanelReactJS into master 2025-08-18 19:09:17 +00:00
victorAnumudu cb4f9fbb42 fixed selected subscription page 2025-08-18 19:40:49 +01:00
Olusesan Ameye ad4a92ceed Support test/qa envrionment 2025-08-17 10:42:08 +00:00
CHIEFSOFT\ameye d71fe9cc01 env to qa 2025-08-16 06:54:41 -04:00
CHIEFSOFT\ameye aeb9f0512c NODE_ENV 2025-08-16 06:47:27 -04:00
CHIEFSOFT\ameye 62c3a8be82 correct api path 2025-08-16 06:24:44 -04:00
CHIEFSOFT\ameye c7c1da919d subscribe cards 2025-08-16 06:15:09 -04:00
CHIEFSOFT\ameye 669a249499 removed wrong entry 2025-08-16 06:09:16 -04:00
ameye 2cc57f0b0a Merge branch 'page-settings-fix' of MERMS/MermsPanelReactJS into master 2025-08-15 10:50:29 +00:00
victorAnumudu 2be1f2ebd1 fixed page settings return value bug 2025-08-15 08:59:08 +01:00
CHIEFSOFT\ameye 54be47e1c8 Subscribing starter 2025-08-14 12:16:59 -04:00
CHIEFSOFT\ameye 8eefbbede8 subscribe add 2025-08-14 09:12:53 -04:00
ameye fa6b53ede0 Merge branch 'complete-profile-bug' of MERMS/MermsPanelReactJS into master 2025-08-14 11:47:26 +00:00
CHIEFSOFT\ameye a5ac576176 Subscribe and payment starter 2025-08-14 07:47:06 -04:00
66 changed files with 3228 additions and 1610 deletions
+10 -8
View File
@@ -1,15 +1,17 @@
REACT_APP_PANEL_NAME="MERMS Panel DEV"
SKIP_PREFLIGHT_CHECK=true SKIP_PREFLIGHT_CHECK=true
REACT_APP_NODE_ENV="development" REACT_APP_NODE_ENV="development"
REACT_APP_SOCKET_URL="https://socket.mermsemr.com" NODE_ENV="development"
REACT_APP_MAIN_API="https://api.mermsemr.com" REACT_APP_SOCKET_URL="https://devsocket.mermsemr.com"
REACT_APP_MEDIA_SERVER="https://media.mermsemr.com" REACT_APP_MAIN_API="https://devapi.mermsemr.com"
REACT_APP_MAIN_SOCKET="https://dsocket.mermsemr.com" REACT_APP_MEDIA_SERVER="https://qa-media.mermsemr.com"
REACT_APP_MAIN_SOCKET="https://devsocket.mermsemr.com"
# login footer links # login footer links
REACT_APP_HOME_LINK='https://www.mermsemr.com/' REACT_APP_HOME_LINK='https://qa-www.mermsemr.com/'
REACT_APP_ABOUT_LINK='https://www.mermsemr.com/about' REACT_APP_ABOUT_LINK='https://qa-www.mermsemr.com/about'
REACT_APP_CONTACTS_LINK='https://www.mermsemr.com/contacts' REACT_APP_CONTACTS_LINK='https://qa-www.mermsemr.com/contacts'
REACT_APP_TERMS_LINK='https://www.mermsemr.com/terms' REACT_APP_TERMS_LINK='https://qa-www.mermsemr.com/terms'
# Inactivity timeout/logout AT 10MINS # Inactivity timeout/logout AT 10MINS
REACT_APP_TIMEOUT=600000 REACT_APP_TIMEOUT=600000
+3
View File
@@ -1,7 +1,10 @@
REACT_APP_PANEL_NAME="MERMS Panel DEV"
SKIP_PREFLIGHT_CHECK=true SKIP_PREFLIGHT_CHECK=true
REACT_APP_NODE_ENV="development" REACT_APP_NODE_ENV="development"
NODE_ENV="development"
REACT_APP_SOCKET_URL="https://devsocket.mermsemr.com" REACT_APP_SOCKET_URL="https://devsocket.mermsemr.com"
REACT_APP_MAIN_API="https://devapi.mermsemr.com" REACT_APP_MAIN_API="https://devapi.mermsemr.com"
REACT_APP_MAIN_API_LL="http://localhost:14700"
REACT_APP_MEDIA_SERVER="https://qa-media.mermsemr.com" REACT_APP_MEDIA_SERVER="https://qa-media.mermsemr.com"
REACT_APP_MAIN_SOCKET="https://devsocket.mermsemr.com" REACT_APP_MAIN_SOCKET="https://devsocket.mermsemr.com"
+3 -1
View File
@@ -1,7 +1,9 @@
REACT_APP_PANEL_NAME="MERMS Panel"
SKIP_PREFLIGHT_CHECK=true SKIP_PREFLIGHT_CHECK=true
REACT_APP_NODE_ENV="production" REACT_APP_NODE_ENV="production"
NODE_ENV="production"
REACT_APP_SOCKET_URL="https://socket.mermsemr.com" REACT_APP_SOCKET_URL="https://socket.mermsemr.com"
REACT_APP_MAIN_API="https://devapi.mermsemr.com" REACT_APP_MAIN_API="https://api.mermsemr.com"
REACT_APP_MEDIA_SERVER="https://media.mermsemr.com" REACT_APP_MEDIA_SERVER="https://media.mermsemr.com"
REACT_APP_MAIN_SOCKET="https://socket.mermsemr.com" REACT_APP_MAIN_SOCKET="https://socket.mermsemr.com"
+2
View File
@@ -1,5 +1,7 @@
REACT_APP_PANEL_NAME="MERMS Panel QA"
SKIP_PREFLIGHT_CHECK=true SKIP_PREFLIGHT_CHECK=true
REACT_APP_NODE_ENV="development" REACT_APP_NODE_ENV="development"
NODE_ENV="development"
REACT_APP_SOCKET_URL="https://devsocket.mermsemr.com" REACT_APP_SOCKET_URL="https://devsocket.mermsemr.com"
REACT_APP_MAIN_API="https://devapi.mermsemr.com" REACT_APP_MAIN_API="https://devapi.mermsemr.com"
REACT_APP_MEDIA_SERVER="https://qa-media.mermsemr.com" REACT_APP_MEDIA_SERVER="https://qa-media.mermsemr.com"
+1 -1
View File
@@ -126,7 +126,7 @@ RUN apk add --no-cache --virtual .build-deps-yarn curl gnupg tar \
# add app # add app
COPY . ./ COPY . ./
# start app # start appdpvle
#CMD ["npm","run", "start"] #CMD ["npm","run", "start"]
# CMD ["yarn", "start"] # CMD ["yarn", "start"]
+3 -2
View File
@@ -10,6 +10,8 @@
"@fullcalendar/timegrid": "^6.1.15", "@fullcalendar/timegrid": "^6.1.15",
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"@reduxjs/toolkit": "^2.4.0", "@reduxjs/toolkit": "^2.4.0",
"@stripe/react-stripe-js": "^3.9.1",
"@stripe/stripe-js": "^7.8.0",
"@tanstack/react-query": "^5.62.3", "@tanstack/react-query": "^5.62.3",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
@@ -33,8 +35,7 @@
}, },
"scripts": { "scripts": {
"start": "react-scripts start -e .env.development", "start": "react-scripts start -e .env.development",
"build": "react-scripts start -e .env.development", "build": "GENERATE_SOURCEMAP=false react-scripts build -e .env.production",
"build_real": "GENERATE_SOURCEMAP=false react-scripts build -e .env.production",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
+6 -2
View File
@@ -7,7 +7,7 @@
<meta name="theme-color" content="#000000"/> <meta name="theme-color" content="#000000"/>
<meta <meta
name="description" name="description"
content="Web site created using create-react-app" content="Empowering Healthcare Decision-Making with Artificial Intelligence"
/> />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png"/> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png"/>
<link rel="manifest" href="%PUBLIC_URL%/manifest.json"/> <link rel="manifest" href="%PUBLIC_URL%/manifest.json"/>
@@ -17,7 +17,11 @@
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Y9QSQFV003"></script> <script async src="https://www.googletagmanager.com/gtag/js?id=G-Y9QSQFV003"></script>
<script> <script>
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date()); gtag('js', new Date());
gtag('config', 'G-Y9QSQFV003'); gtag('config', 'G-Y9QSQFV003');
+6 -1
View File
@@ -4,12 +4,17 @@ set -x
export NODE_ENV="${NODE_ENV:-development}" export NODE_ENV="${NODE_ENV:-development}"
if [ $NODE_ENV == "development" ]; then if [ $NODE_ENV = "development" ]; then
# this runs webpack-dev-server with hot reloading # this runs webpack-dev-server with hot reloading
echo "Development build" echo "Development build"
npm install --legacy-peer-deps npm install --legacy-peer-deps
npm start npm start
# npm run build # npm run build
elif [ $NODE_ENV = "qa" -o $NODE_ENV = "test" ]; then
echo "QA build"
export NODE_ENV="test"
npm install --legacy-peer-deps
npm start
else else
# build the app and serve it via nginx # build the app and serve it via nginx
echo "Production build" echo "Production build"
+4
View File
@@ -14,6 +14,10 @@
border-radius: 10px; border-radius: 10px;
} }
.border-radius-10 {
border-radius: 10px;
}
.login-links{ .login-links{
margin-top: 50px; margin-top: 50px;
display: flex; display: flex;
+7 -1
View File
@@ -23,7 +23,9 @@ import SubscriptionPage from './views/SubscriptionPage';
import OnboardPage from "./views/OnboardPage"; import OnboardPage from "./views/OnboardPage";
import AccPWDResetPage from './views/AccPWDResetPage'; import AccPWDResetPage from './views/AccPWDResetPage';
import ProfileCompletePage from './views/ProfileCompletePage'; import ProfileCompletePage from './views/ProfileCompletePage';
import SubscribePage from './views/Subscribe'
import StartPage from "./views/StartPage";
import TrafficPage from "./views/TrafficPage";
function AppRouters() { function AppRouters() {
return ( return (
@@ -44,7 +46,9 @@ function AppRouters() {
{/* protected routes */} {/* protected routes */}
<Route element={<SocketIOContextProvider/>}> <Route element={<SocketIOContextProvider/>}>
<Route element={<UserExist/>}> <Route element={<UserExist/>}>
<Route path={siteLinks.start} element={<StartPage/>}/>
<Route path={siteLinks.dash} element={<HomePage/>}/> <Route path={siteLinks.dash} element={<HomePage/>}/>
<Route path={siteLinks.traffic} element={<TrafficPage/>}/>
<Route path={siteLinks.profile_complete} element={<ProfileCompletePage/>}/> <Route path={siteLinks.profile_complete} element={<ProfileCompletePage/>}/>
<Route path={siteLinks.product} element={<ProductPage/>}/> <Route path={siteLinks.product} element={<ProductPage/>}/>
<Route path={siteLinks.reports} element={<ReportsPage/>}/> <Route path={siteLinks.reports} element={<ReportsPage/>}/>
@@ -52,9 +56,11 @@ function AppRouters() {
<Route path={siteLinks.contacts} element={<ContactsPage/>}/> <Route path={siteLinks.contacts} element={<ContactsPage/>}/>
<Route path={siteLinks.user} element={<UserPage/>}/> <Route path={siteLinks.user} element={<UserPage/>}/>
<Route path={siteLinks.subscription} element={<SubscriptionPage/>}/> <Route path={siteLinks.subscription} element={<SubscriptionPage/>}/>
<Route path={siteLinks.subscription_success} element={<SubscriptionPage/>}/>
<Route path={siteLinks.onboard} element={<OnboardPage/>}/> <Route path={siteLinks.onboard} element={<OnboardPage/>}/>
<Route path={siteLinks.calendar} element={<CalendarPage/>}/> <Route path={siteLinks.calendar} element={<CalendarPage/>}/>
<Route path={siteLinks.settings} element={<SettingsPage/>}/> <Route path={siteLinks.settings} element={<SettingsPage/>}/>
<Route path={siteLinks.subscribe} element={<SubscribePage/>}/>
<Route path={siteLinks.help} element={<HelpPage/>}/> <Route path={siteLinks.help} element={<HelpPage/>}/>
</Route> </Route>
</Route> </Route>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 KiB

+2 -2
View File
@@ -66,7 +66,7 @@ export default function CSignup() {
localStorage.setItem('room', room) localStorage.setItem('room', room)
localStorage.setItem('uid', uid) localStorage.setItem('uid', uid)
dispatch(updateUserDetails({ ...res?.data })); dispatch(updateUserDetails({ ...res?.data }));
navigate('/dash') // later add redux to dispatch state navigate(siteLinks.start, {replace: true}) // later add redux to dispatch state
}, },
// onError: (err) => { // onError: (err) => {
// console.log('err', err) // console.log('err', err)
@@ -100,7 +100,7 @@ export default function CSignup() {
<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="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="mt-5 d-flex">
<div className="bg-white register p-5"> <div className="bg-white register p-5">
<h1 className="mb-2">MERMS Panel</h1> <h1 className="mb-2">{process.env.REACT_APP_PANEL_NAME}</h1>
{/* <p>Welcome, Enter your password.</p> */} {/* <p>Welcome, Enter your password.</p> */}
<div <div
> >
+1 -1
View File
@@ -50,7 +50,7 @@ export default function Forgetpwd2() {
<div className="col-11 col-sm-6 col-lg-5 col-xxl-4 align-self-center order-2 order-sm-1h" style={{maxWidth: '520px'}}> <div className="col-11 col-sm-6 col-lg-5 col-xxl-4 align-self-center order-2 order-sm-1h" style={{maxWidth: '520px'}}>
<div className="mt-5 d-flex"> <div className="mt-5 d-flex">
<div className="bg-white register p-5"> <div className="bg-white register p-5">
<h1 className="mb-2">MERMS Panel</h1> <h1 className="mb-2">{process.env.REACT_APP_PANEL_NAME}</h1>
{!mutation.isSuccess && <p>Please enter your username.</p>} {!mutation.isSuccess && <p>Please enter your username.</p>}
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
+1 -1
View File
@@ -27,7 +27,7 @@ export default function Login() {
<div className="col-span-1 lg:col-span-2 xl:col-span-2 place-content-center order-2 sm:order-1"> <div className="col-span-1 lg:col-span-2 xl:col-span-2 place-content-center order-2 sm:order-1">
<div className="w-full p-4 px-8 md:p-10 flex flex-col gap-6 items-start justify-start"> <div className="w-full p-4 px-8 md:p-10 flex flex-col gap-6 items-start justify-start">
<div className='w-full text-left'> <div className='w-full text-left'>
<h1 className="mb-2 text-black text-4xl font-semibold">MERMS Panel</h1> <h1 className="mb-2 text-black text-4xl font-semibold">{process.env.REACT_APP_PANEL_NAME}</h1>
<p className='text-black-gray text-base'>Welcome back, please login to your account.</p> <p className='text-black-gray text-base'>Welcome back, please login to your account.</p>
</div> </div>
<form className="w-full text-14 text-left text-black-gray"> <form className="w-full text-14 text-left text-black-gray">
+12 -4
View File
@@ -86,20 +86,20 @@ export default function Login() {
<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="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="mt-5 d-flex">
<div className="bg-white register px-5 pt-5 pb-3"> <div className="bg-white register px-5 pt-5 pb-3">
<h1 className="mb-2">MERMS Panel</h1> <h1 className="mb-2">{process.env.REACT_APP_PANEL_NAME}</h1>
<p>Welcome back, please login to your account.</p> <p>Welcome back, please login to your account.</p>
<form className="mt-3 mt-sm-5"> <form className="mt-3 mt-sm-5">
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12">
<div className="form-group"> <div className="form-group">
<label className="control-label text-black fw-bold">User Name*</label> <label className="control-label text-black fw-bold">User Name*</label>
<input maxLength={55} name='username' value={fields.username} onChange={handleChange} type="text" className="form-control" placeholder="Username" /> <input maxLength={25} name='username' value={fields.username} onChange={handleChange} type="text" className="form-control" placeholder="Username" />
</div> </div>
</div> </div>
<div className="col-12"> <div className="col-12">
<div className="form-group"> <div className="form-group">
<label className="control-label text-black fw-bold">Password*</label> <label className="control-label text-black fw-bold">Password*</label>
<input maxLength={55} name='password' value={fields.password} onChange={handleChange} type="password" className="form-control" placeholder="Password" /> <input maxLength={25} name='password' value={fields.password} onChange={handleChange} type="password" className="form-control" placeholder="Password" />
</div> </div>
</div> </div>
<div className="col-12"> <div className="col-12">
@@ -124,10 +124,18 @@ export default function Login() {
<button type='button' onClick={()=>{login.mutate(fields)}} className="btn btn-primary text-uppercase">{login.isPending ? 'loading...' : 'Sign In'}</button> <button type='button' onClick={()=>{login.mutate(fields)}} className="btn btn-primary text-uppercase">{login.isPending ? 'loading...' : 'Sign In'}</button>
</div> </div>
<div className="col-12 mt-3"> <div className="col-12 mt-3">
<p>Don't have an account ?<Link to={siteLinks.signup}><span style={{fontWeight: 'bolder'}}>Sign Up</span></Link></p> <p> <Link to={siteLinks.signup}>
{/*<span style={{fontWeight: 'bolder'}}>Sign Up</span>*/}
<button className="btn btn-warning text-uppercase">
Sign Up
</button>
</Link><span style={{paddingLeft: '5px' , fontWeight: 'bolder'}}> if you don't have an account yet.</span></p>
</div> </div>
</div> </div>
</form> </form>
<div className="row" style={{margin: '5px'}}>
<hr />
</div>
<div className="row" style={{marginTop: '20px'}}> <div className="row" style={{marginTop: '20px'}}>
<div className="col-6"> <div className="col-6">
<div className="app-store-icons-wrap text-center"> <div className="app-store-icons-wrap text-center">
+18 -5
View File
@@ -27,7 +27,7 @@ export default function Signup() {
<div className="col-span-1 lg:col-span-2 xl:col-span-2 place-content-center order-2 sm:order-1"> <div className="col-span-1 lg:col-span-2 xl:col-span-2 place-content-center order-2 sm:order-1">
<div className="w-full p-4 px-8 md:p-10 flex flex-col gap-6 items-start justify-start"> <div className="w-full p-4 px-8 md:p-10 flex flex-col gap-6 items-start justify-start">
<div className='w-full text-left'> <div className='w-full text-left'>
<h1 className="mb-2 text-black text-4xl font-semibold">MERMS Panel</h1> <h1 className="mb-2 text-black text-4xl font-semibold">{process.env.REACT_APP_PANEL_NAME}</h1>
<p className='text-black-gray text-base'>Welcome, Please create your account.</p> <p className='text-black-gray text-base'>Welcome, Please create your account.</p>
</div> </div>
<form className="w-full text-14 text-left text-black-gray"> <form className="w-full text-14 text-left text-black-gray">
@@ -69,22 +69,35 @@ export default function Signup() {
<div className="flex gap-2"> <div className="flex gap-2">
<input className="form-check-input" type="checkbox" id="gridCheck"/> <input className="form-check-input" type="checkbox" id="gridCheck"/>
<label className="font-semibold form-check-label" htmlFor="gridCheck"> <label className="font-semibold form-check-label" htmlFor="gridCheck">
I accept terms & policy I accept terms & policy of use.
</label> </label>
</div> </div>
</div> </div>
</div> </div>
<div className="w-full mt-3"> <div className="w-full mt-3">
<button onClick={()=>{navigate(siteLinks.home)}} className="bg-primary rounded-sm px-4 py-2 text-white font-medium uppercase">Sign Up</button> <button onClick={() => {
navigate(siteLinks.home)
}} className="bg-primary rounded-sm px-4 py-2 text-white font-medium uppercase">Sign
Up
</button>
</div> </div>
<div className="mt-3"> <div className="mt-3">
<p className='font-medium'>Already have an account ?<Link to={siteLinks.login} className=' hover:text-primary'> Sign In</Link></p> <hr />
<p className='font-medium'>Already have an account ?
<Link to={siteLinks.login}
className='bg-secondary; hover:text-primary font-bold' style={{paddingRight: '10px'}}>
<button className="btn btn-warning text-uppercase">
Sign In
</button>
</Link>
</p>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<div className="bg-login_gradient h-full col-span-1 lg:col-span-3 xl:col-span-6 place-content-center order-1 sm:order-2"> <div
className="bg-login_gradient h-full col-span-1 lg:col-span-3 xl:col-span-6 place-content-center order-1 sm:order-2">
<div className="w-full"> <div className="w-full">
<div className="w-2/3 mx-auto"> <div className="w-2/3 mx-auto">
<img className="w-[80%]" src={LoginImg} alt=""/> <img className="w-[80%]" src={LoginImg} alt=""/>
+56 -17
View File
@@ -61,10 +61,11 @@ export default function Signup2() {
<div className="vh-100 bg-white custom-bg"> <div className="vh-100 bg-white custom-bg">
<div className="container-fluid p-0"> <div className="container-fluid p-0">
<div className="row no-gutters justify-content-center"> <div className="row no-gutters justify-content-center">
<div className="col-11 col-sm-6 col-lg-5 col-xxl-4 align-self-center order-2 order-sm-1" style={{maxWidth: '520px'}}> <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="mt-5 d-flex">
<div className="bg-white register p-5"> <div className="bg-white register p-5">
<h1 className="mb-2">MERMS Panel</h1> <h1 className="mb-2">{process.env.REACT_APP_PANEL_NAME}</h1>
<p>Welcome, Please create your account.</p> <p>Welcome, Please create your account.</p>
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
@@ -79,20 +80,37 @@ export default function Signup2() {
<> <>
<div className="col-12 col-md-6"> <div className="col-12 col-md-6">
<div className="form-group"> <div className="form-group">
<label className={`text-black fw-bold control-label ${(props.errors.firstname && props.touched.firstname) && 'text-danger'}`}>First Name*</label> <label
<input type="text" name='firstname' className="form-control" placeholder="First Name" value={props.values.firstname} onChange={props.handleChange} /> 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> </div>
<div className="col-12 col-md-6"> <div className="col-12 col-md-6">
<div className="form-group"> <div className="form-group">
<label className={`text-black fw-bold control-label ${(props.errors.lastname && props.touched.lastname) && 'text-danger'}`}>Last Name*</label> <label
<input type="text" name='lastname' className="form-control" placeholder="Last Name" value={props.values.lastname} onChange={props.handleChange} /> 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> </div>
<div className="col-12"> <div className="col-12">
<div className="form-group"> <div className="form-group">
<label className={`text-black fw-bold control-label ${(props.errors.email && props.touched.email) && 'text-danger'}`}>Email*</label> <label
<input type="email" name='email' className="form-control" placeholder="Email" value={props.values.email} onChange={props.handleChange} /> 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> </div>
{/* <div className="col-12"> {/* <div className="col-12">
@@ -109,12 +127,18 @@ export default function Signup2() {
</div> */} </div> */}
<div className="col-12"> <div className="col-12">
<div className="form-check"> <div className="form-check">
<input name='isChecked' className="form-check-input" type="checkbox" id="gridCheck" value={props.values.isChecked} onChange={props.handleChange} /> <input name='isChecked'
<label className="form-check-label" htmlFor="gridCheck"> 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 I accept terms & policy
</label> </label>
</div> </div>
<span className={`${(props.errors.isChecked && props.touched.isChecked) && 'text-danger'}`}>{props.errors.isChecked}</span> <span
className={`${(props.errors.isChecked && props.touched.isChecked) && 'text-danger'}`}>{props.errors.isChecked}</span>
</div> </div>
{mutation.error && {mutation.error &&
@@ -126,21 +150,36 @@ export default function Signup2() {
} }
<div className="col-12 mt-3 text-end"> <div className="col-12 mt-3 text-end">
<button type='submit' className="btn btn-primary text-uppercase">{mutation.isPending ? 'loading...' : 'Sign up'}</button> <button type='submit'
className="btn btn-primary text-uppercase">{mutation.isPending ? 'loading...' : 'Sign up'}</button>
</div> </div>
</> </>
: :
<div className='col-12'> <div className='col-12'>
<div className="rounded-2 d-flex flex-column justify-content-between align-items-center" style={{backgroundColor: '#F2FAF7'}}> <div
<h4 className='p-4 text-black' style={{marginBottom: '-30px'}}>Check your email to continue.</h4> className="rounded-2 d-flex flex-column justify-content-between align-items-center"
<img className='' style={{width: '200px'}} src={getImage('check-mail.png')} alt='mail-alert' /> style={{backgroundColor: '#F2FAF7'}}>
<Link to={siteLinks.login} className='p-2 text-primary' style={{color: '#6FCAEF'}}>Home</Link> <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> </div>
} }
<div className="col-12 mt-3"> <div className="col-12 mt-3">
<p>Already have an account ?<Link to={siteLinks.login}> Sign In</Link></p> <p>Already have an account ?<Link
to={siteLinks.login}>
<button
className="btn btn-warning text-uppercase">
Sign In
</button>
</Link></p>
</div> </div>
</div> </div>
</Form> </Form>
+248 -271
View File
@@ -1,31 +1,114 @@
import React from "react"; "use client";
import React, { useEffect, useState } from "react";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
import getImage from "../../utils/getImage"; import getImage from "../../utils/getImage";
import { useMutation, useQuery } from "@tanstack/react-query";
import { commentsData } from "../../services/services";
import queryKeys from "../../services/queryKeys";
import getCustomTime from "../../utils/getCustomTime";
export default function Comments() { export default function Comments() {
// const {data:contacts, isFetching, isError, error} = useQuery({
// queryKey: queryKeys.contacts,
// queryFn: () => contactData()
// })
const [activeCategoryUID, setActiveCategoryUID] = useState("0"); // HOLDS VALUE OF THE ACTIVE CATEGORY
const [activeContactUID, setActiveContactUID] = useState("");
const [activeDetail, setActiveDetail] = useState([]);
const [filteredContactData, setFiltererdContactData] = useState([]);
const getContactData = useMutation({
mutationFn: (reqData) => {
return commentsData(reqData);
},
onError: (error) => {
console.log(error);
},
onSuccess: (res) => {
if (res?.data?.resultCode != "0") {
throw { message: "Something went wrong" };
}
setFiltererdContactData(res?.data?.contacts);
},
});
const changeActiveUID = (uid) => {
setActiveContactUID(uid);
let detail = contactsData.filter((item) => item.uid == uid);
setActiveDetail(detail);
};
const changeActiveCategoryUID = (id) => {
let filteredConData = [];
setActiveCategoryUID(id);
if (id == "0") {
filteredConData = contactsData;
} else {
filteredConData = contactsData.filter((item) => item.category == id);
}
setFiltererdContactData(filteredConData);
changeActiveUID(filteredConData[0]?.uid);
};
useEffect(() => {
let reqData = {
token: localStorage.getItem("token"), // USER TOKEN
uid: localStorage.getItem("uid"), // USER UID
};
getContactData.mutate(reqData);
}, []);
const contactsData = getContactData?.data?.data?.contacts; // LIST OF CONTACTS
const contactsCategory = getContactData?.data?.data?.category; // LIST OF CATEGORY
return ( return (
<> <>
<BreadcrumbComBS title='Comments' paths={['Dashboard', 'Comments']} /> <BreadcrumbComBS title="Comments" paths={["Dashboard", "Comments"]} />
{getContactData?.isPending ? (
<>
<div className="row">
<div className="col-12">
<p className="text-mute">Loading...</p>
</div>
</div>
</>
) : getContactData?.error ? (
<div className="row">
<div className="col-12">
<p className="text-danger">{getContactData?.error?.message}</p>
</div>
</div>
) : (
<div className="row"> <div className="row">
{/*<div className="vh-100 col-12 flex align-items-center">Coming Soon</div>*/} {/*<div className="vh-100 col-12 flex align-items-center">Coming Soon</div>*/}
<div className="col-12"> <div className="col-12">
<div className="card card-statistics mail-contant"> <div
className="card card-statistics mail-contant"
style={{ minHeight: "200px", borderRadius: "10px" }}
>
<div className="card-body p-0"> <div className="card-body p-0">
<div className="row no-gutters"> <div className="row no-gutters">
<div className="col-md-4 col-xxl-2 col-md-4"> <div className="col-md-4 col-xxl-2 col-md-4">
<div className="mail-sidebar"> <div className="mail-sidebar">
<div className="row justify-content-center"> <div className="row justify-content-center">
<div className="col-12"> <div className="d-none col-12">
<div className="text-center mail-sidebar-title px-4"> <div className="text-center mail-sidebar-title px-4">
<a href="javascript:void(0)" className="btn btn-primary btn-block py-3 font-weight-bold font-18"><i className="fa fa-plus pl-2"></i></a> <a
href="#"
className="btn btn-primary btn-block py-3 font-weight-bold font-18"
>
<i className="fa fa-plus pl-2"></i>
</a>
</div> </div>
</div> </div>
<div className="col-12"> <div className="col-12">
<div className="px-4 py-4"> <div className="px-4 py-4">
<ul className="pl-0"> <ul className="pl-0">
<li className="py-2"> <li className="py-2">
<a href="javascript:void(0)"> <a href="#">
<span className="nav align-items-center"> <span className="nav align-items-center">
<span> <span>
<i className="fa fa-envelope-o text-primary pr-4"></i> <i className="fa fa-envelope-o text-primary pr-4"></i>
@@ -34,71 +117,89 @@ export default function Comments(){
<span>Inbox</span> <span>Inbox</span>
</span> </span>
<span className="nav-item ml-auto text-right"> <span className="nav-item ml-auto text-right">
<span className="badge badge-pill badge-primary float-right">0+</span> <span className="badge badge-pill badge-primary float-right">
</span> {contactsData?.length}
</span> </span>
</a>
</li>
<li className="py-2">
<a href="javascript:void(0)">
<span className="nav align-items-center">
<span>
<i className="fa fa-paper-plane-o pr-4"></i>
</span>
<span>
<span>Sent Mail</span>
</span> </span>
</span> </span>
</a> </a>
</li> </li>
{/*<li className="py-2">*/}
{/* <a href="#">*/}
{/* <span*/}
{/* className="nav align-items-center">*/}
{/* <span>*/}
{/* <i className="fa fa-paper-plane-o pr-4"></i>*/}
{/* </span>*/}
{/* <span>*/}
{/* <span>Replied Mail</span>*/}
{/* </span>*/}
{/* </span>*/}
{/* </a>*/}
{/*</li>*/}
</ul>
</ul>
<ul className="pl-0 mt-5"> <ul className="pl-0 mt-5">
<li className="py-2"> <li
<a href="javascript:void(0)"> className="py-2"
onClick={() => changeActiveCategoryUID("0")}
style={{ cursor: "pointer" }}
>
<div>
<span className="nav align-items-center"> <span className="nav align-items-center">
<span> <span>
<i className="fa fa-circle-o text-danger pr-4"></i> <i
className={`fa fa-circle-o pr-4 ${
activeCategoryUID == "0"
? "text-primary"
: "text-warning"
}`}
></i>
</span> </span>
<span> <span>
<span>Personal</span> <span>All</span>
</span> </span>
</span> </span>
</a> </div>
</li> </li>
<li className="py-2"> {contactsCategory &&
<a href="javascript:void(0)"> contactsCategory.map((item) => (
<li
key={item?.cid}
className="py-2"
onClick={() =>
changeActiveCategoryUID(`${item?.cid}`)
}
style={{ cursor: "pointer" }}
>
<div>
<span className="nav align-items-center"> <span className="nav align-items-center">
<span> <span>
<i className="fa fa-circle-o pr-4 text-warning"></i> <i
className={`fa fa-circle-o pr-4 ${
activeCategoryUID ==
`${item?.cid}`
? "text-primary"
: "text-warning"
}`}
></i>
</span> </span>
<span> <span>
<span>Work</span> <span>{item?.description}</span>
</span> </span>
</span> </span>
</a> </div>
</li>
<li className="py-2">
<a href="javascript:void(0)">
<span className="nav align-items-center">
<span>
<i className="fa fa-plus pr-4"></i>
</span>
<span>
<span>Add Category</span>
</span>
</span>
</a>
</li> </li>
))}
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="col-md-8 col-xxl-4 border-md-t"> <div className={`${filteredContactData.length > 0 ? 'col-md-8 col-xxl-4' : 'col-md-8 col-xxl-10'} border-md-t`}>
<div className="mail-content border-right border-n h-100"> <div className="mail-content border-right border-n h-100" style={{placeContent: 'center'}}>
<div className="mail-search border-bottom"> {/* <div className="mail-search border-bottom">
<div className="row align-items-center mx-0"> <div className="row align-items-center mx-0">
<div className="col-12"> <div className="col-12">
<div className="form-group pt-3"> <div className="form-group pt-3">
@@ -107,205 +208,120 @@ export default function Comments(){
</div> </div>
</div> </div>
</div> </div>
</div> </div> */}
<div className="mail-msg scrollbar scroll_dark"> <div className="mail-msg scrollbar scroll_dark">
<div className="mail-msg-item"> { filteredContactData.length ?
<a href="javascript:void(0)"> filteredContactData?.map((contact, index) => {
const isActive =
contact?.uid == activeContactUID ||
(!activeContactUID && index == 0);
const avtarImage =
contact?.category === undefined
? "avtar/01.jpg"
: "avtar/" + contact.category + ".png";
return (
<div
key={contact?.uid}
onClick={() => changeActiveUID(contact?.uid)}
className={`mail-msg-item ${
isActive && "bg-light"
}`}
>
<a href="#">
<div className="media align-items-center"> <div className="media align-items-center">
<div className="mr-3"> <div className="mr-3">
<div className="bg-img"> <div className="bg-img">
<img src={getImage("avtar/01.jpg")} className="img-fluid" alt="user" /> <img
src={getImage(avtarImage)}
className="img-fluid"
alt="user"
/>
</div> </div>
</div> </div>
<div className="w-100"> <div className="w-100">
<div className="mail-msg-item-titel justify-content-between"> <div className="mail-msg-item-titel justify-content-between">
<p>Martin smith</p> <p>
<p className="d-none d-xl-block">06:59 <span> PM </span></p> <span
style={{
fontSize: "14px",
color: "#148399",
fontWeight: "bolder",
}}
>
{contact?.sender}
</span>
</p>
{/* <p className="d-none d-xl-block">06:59 <span> PM </span></p> */}
<p className="d-none d-xl-block">
<span style={{ fontSize: "14px" }}>
{new Date(
contact?.added
).toDateString()}
</span>
</p>
</div> </div>
<h5 className="mb-0 my-2">Saas Designer</h5> <h5 className="mb-0 my-2">
<p>Since there is not an "all the above" category, I'll take the opportunity to enthusiastically congratulate you on the very high quality.</p> {contact?.title}
<p className="d-xl-none">06:59 <span> PM </span></p> </h5>
</div> <p>
</div> {contact?.message?.length < 100
</a> ? contact?.message
</div> : contact?.message.substring(0, 101) +
<div className="mail-msg-item"> " ..."}
<a href="javascript:void(0)"> </p>
<div className="media align-items-center"> <p className="d-xl-none">
<div className="mr-3"> <span>
<div className="bg-img"> {new Date(
<img src={getImage("avtar/02.jpg")} className="img-fluid" alt="user" /> contact?.added
</div> ).toDateString()}
</div> {/* {getCustomTime(contact.added)} */}
<div className="w-100"> </span>
<div className="mail-msg-item-titel justify-content-between"> </p>
<p>DutcaPatrick</p>
<p className="d-none d-xl-block">06:59 <span> PM </span></p>
</div>
<h5 className="mb-0 my-2">Mobile app Designer </h5>
<p>Very nice template, lots of pages and good documentation.</p>
<p className="d-xl-none">06:59 <span> PM </span></p>
</div>
</div>
</a>
</div>
<div className="mail-msg-item">
<a href="javascript:void(0)">
<div className="media align-items-center">
<div className="mr-3">
<div className="bg-img">
<img src={getImage("avtar/03.jpg")} className="img-fluid" alt="user" />
</div>
</div>
<div className="w-100">
<div className="mail-msg-item-titel justify-content-between">
<p>m_morsch</p>
<p className="d-none d-xl-block">06:59 <span> PM </span></p>
</div>
<h5 className="mb-0 my-2">Landing page Designer</h5>
<p>Excellent and at a great price... thank you very much!</p>
<p className="d-xl-none">06:59 <span> PM </span></p>
</div>
</div>
</a>
</div>
<div className="mail-msg-item">
<a href="javascript:void(0)">
<div className="media align-items-center">
<div className="mr-3">
<div className="bg-img">
<img src={getImage("avtar/04.jpg")} className="img-fluid" alt="user" />
</div>
</div>
<div className="w-100">
<div className="mail-msg-item-titel justify-content-between">
<p>AnnaHorno</p>
<p className="d-none d-xl-block">06:59 <span> PM </span></p>
</div>
<h5 className="mb-0 my-2">Re-Design ios app</h5>
<p>Solved my theme problem in 10 minutes. We thank you.</p>
<p className="d-xl-none">06:59 <span> PM </span></p>
</div>
</div>
</a>
</div>
<div className="mail-msg-item">
<a href="javascript:void(0)">
<div className="media align-items-center">
<div className="mr-3">
<div className="bg-img">
<img src={getImage("avtar/05.jpg")} className="img-fluid" alt="user" />
</div>
</div>
<div className="w-100">
<div className="mail-msg-item-titel justify-content-between">
<p>Wdcorbitt</p>
<p className="d-none d-xl-block">06:59 <span> PM </span></p>
</div>
<h5 className="mb-0 my-2">Mobil UX/UI Designer</h5>
<p>Asked for information and received it EXTREMELY quickly. Great layout - good code - great price! </p>
<p className="d-xl-none">06:59 <span> PM </span></p>
</div>
</div>
</a>
</div>
<div className="mail-msg-item">
<a href="javascript:void(0)">
<div className="media align-items-center">
<div className="mr-3">
<div className="bg-img">
<img src={getImage("avtar/06.jpg")} className="img-fluid" alt="user" />
</div>
</div>
<div className="w-100">
<div className="mail-msg-item-titel justify-content-between">
<p>Anne Smith</p>
<p className="d-none d-xl-block">06:59 <span> PM </span></p>
</div>
<h5 className="mb-0 my-2">Jobly Opportunity</h5>
<p>Mentor has so many features and layouts. Its a great choice.</p>
<p className="d-xl-none">06:59 <span> PM </span></p>
</div>
</div>
</a>
</div>
<div className="mail-msg-item">
<a href="javascript:void(0)">
<div className="media align-items-center">
<div className="mr-3">
<div className="bg-img">
<img src={getImage("avtar/07.jpg")} className="img-fluid" alt="user" />
</div>
</div>
<div className="w-100">
<div className="mail-msg-item-titel justify-content-between">
<p>Paul Flavius</p>
<p className="d-none d-xl-block">06:59 <span> PM </span></p>
</div>
<h5 className="mb-0 my-2">Saas Designer</h5>
<p>There are many people in the world with amazing talents who realize only a small percentage of their potential. </p>
<p className="d-xl-none">06:59 <span> PM </span></p>
</div>
</div>
</a>
</div>
<div className="mail-msg-item">
<a href="javascript:void(0)">
<div className="media align-items-center">
<div className="mr-3">
<div className="bg-img">
<img src={getImage("avtar/08.jpg")} className="img-fluid" alt="user" />
</div>
</div>
<div className="w-100">
<div className="mail-msg-item-titel justify-content-between">
<p>Sara Lisbon</p>
<p className="d-none d-xl-block">06:59 <span> PM </span></p>
</div>
<h5 className="mb-0 my-2">UI Designer</h5>
<p>We can look a bit further back in time to Albert Einstein or even further back to Abraham Lincoln.</p>
<p className="d-xl-none">06:59 <span> PM </span></p>
</div>
</div>
</a>
</div>
<div className="mail-msg-item">
<a href="javascript:void(0)">
<div className="media align-items-center">
<div className="mr-3">
<div className="bg-img">
<img src={getImage("avtar/09.jpg")} className="img-fluid" alt="user" />
</div>
</div>
<div className="w-100">
<div className="mail-msg-item-titel justify-content-between">
<p>Annahorno</p>
<p className="d-none d-xl-block">06:59 <span> PM </span></p>
</div>
<h5 className="mb-0 my-2">Saas Designer</h5>
<p>One of the most difficult aspects of achieving success is staying motivated over the long haul.</p>
<p className="d-xl-none">06:59 <span> PM </span></p>
</div> </div>
</div> </div>
</a> </a>
</div> </div>
);
})
:
<p className="text-center">Messages will appear here as soon as they are available for selection</p>
}
</div> </div>
</div> </div>
</div> </div>
{filteredContactData.length > 0 &&
<div className="col-xxl-6 border-t border-xxl-t"> <div className="col-xxl-6 border-t border-xxl-t">
<div className="mail-chat py-5 px-5"> <div className="mail-chat py-5 px-5">
<div className="media align-items-center"> <div className="media align-items-center">
<div className="bg-img mr-3"> <div className="bg-img mr-3">
<img src={getImage("avtar/03.jpg")} className="img-fluid" alt="user" /> <img
src={activeContactUID ? getImage("avtar/" + activeDetail[0].category + ".png") : getImage(filteredContactData[0] == undefined ? "avtar/01.jpg": "avtar/" + filteredContactData[0].category + ".png")}
className="img-fluid"
alt="user"
/>
</div> </div>
<div> <div>
<h4 className="mb-0">Dutca Patrick</h4> <h4 className="mb-0" style={{ color: "#148399" }}>
<p>30 Min ago</p> {activeContactUID
? activeDetail[0]?.sender
: filteredContactData[0]?.sender}
</h4>
<p>
{activeContactUID
? new Date(activeDetail[0]?.added).toDateString()
: new Date(
filteredContactData[0]?.added
).toDateString()}
</p>
</div> </div>
</div> </div>
<div className="mt-4 d-flex justify-content-between"> <div className="mt-4 d-flex justify-content-between">
<div> <div>
<h3>Landing page Designer...</h3> <h3>
{activeContactUID
? activeDetail[0]?.title
: filteredContactData[0]?.title}
</h3>
</div> </div>
<div className="d-flex"> <div className="d-flex">
{/*<a href="javascript:void(0)"><i className="fa fa-reply font-22 pr-3"></i></a>*/} {/*<a href="javascript:void(0)"><i className="fa fa-reply font-22 pr-3"></i></a>*/}
@@ -313,60 +329,21 @@ export default function Comments(){
</div> </div>
</div> </div>
<div> <div>
<p className="my-4">hey adminjon...</p> <p>
<p className="mb-2">I truly believe Augustines words are true and if you look at history you know it is true. There are many people in the world with amazing talents who realize only a small percentage of their potential. We all know people who live this truth.</p> {activeContactUID
<p>We also know those epic stories, those modern-day legends surrounding the early failures of such supremely successful folks as Michael Jordan and Bill Gates. We can look a bit further back in time to Albert Einstein or even further back to Abraham Lincoln. What made each of these people so successful? Motivation.</p> ? activeDetail[0]?.message
<p>We know this in our gut, but what can we do about it? How can we motivate ourselves? One of the most difficult aspects of achieving success is staying motivated over the long haul.</p> : filteredContactData[0]?.message}
<div className="my-5"> </p>
<p>Have lovely Day,</p>
<p>adminjon</p>
</div> </div>
</div> </div>
</div> </div>
{/*<div className="d-md-flex px-5 py-4">*/} }
{/* <div className="flex-fill align-items-center">*/} </div>
{/* <div className="d-flex">*/} </div>
{/* <i className="ti ti-clip pr-3 font-22"></i>*/} </div>
{/* <p className="pr-3 font-weight-bold">Wireframe</p>*/} </div>
{/* <p>(220.MB)</p>*/} </div>
{/* </div>*/} )}
{/* </div>*/} </>
{/* <div className="flex-fill text-left text-md-right"><a href="javascript:void(0)" className="text-primary"><i className="ti ti-download pr-2"></i><span>Download</span></a></div>*/} );
{/*</div>*/}
<div className="bg-light mail-f px-4 py-3">
<div className="py-2 bg-white px-4 py-3 d-flex justify-content-between">
<p>Click here to <a href="#editer" data-toggle="collapse" className="text-primary px-1">Reply</a>or<a href="#forward" data-toggle="collapse" className="text-primary px-1">Forward</a></p>
<a href="javascript:void(0)" className="text-primary"><i className="fa fa-microphone"></i></a>
</div>
<div className="collapse" id="editer">
<div className="form-group">
<textarea className="form-control mt-3" id="exampleFormControlTextarea1" rows="3" placeholder="Type here..."></textarea>
</div>
</div>
<div className="collapse" id="forward">
<div className="form-group">
<input className="form-control mt-3" id="exampleFormControl" placeholder="Email Address" />
</div>
</div>
<div className="d-flex align-items-center justify-content-between py-2">
<div>
<ul className="nav">
<li className="nav-item pr-3"><a href="javascript:void(0)"><i className="ti ti-clip font-20"></i></a></li>
<li className="nav-item pr-3"><a href="javascript:void(0)"><i className="ti ti-face-smile font-20"></i></a></li>
<li className="nav-item"><a href="javascript:void(0)"><i className="ti ti-gallery font-20"></i></a></li>
</ul>
</div>
<div>
<a href="javascript:void(0)" className="btn btn-primary"><span>Send</span> <i className="fa fa-paper-plane"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
} }
+191 -151
View File
@@ -1,4 +1,4 @@
"use client" "use client";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
import getImage from "../../utils/getImage"; import getImage from "../../utils/getImage";
@@ -8,86 +8,87 @@ import queryKeys from "../../services/queryKeys";
import getCustomTime from "../../utils/getCustomTime"; import getCustomTime from "../../utils/getCustomTime";
export default function Contacts() { export default function Contacts() {
// const {data:contacts, isFetching, isError, error} = useQuery({ // const {data:contacts, isFetching, isError, error} = useQuery({
// queryKey: queryKeys.contacts, // queryKey: queryKeys.contacts,
// queryFn: () => contactData() // queryFn: () => contactData()
// }) // })
const [activeCategoryUID, setActiveCategoryUID] = useState('0') // HOLDS VALUE OF THE ACTIVE CATEGORY const [activeCategoryUID, setActiveCategoryUID] = useState("0"); // HOLDS VALUE OF THE ACTIVE CATEGORY
const [activeContactUID, setActiveContactUID] = useState('') const [activeContactUID, setActiveContactUID] = useState("");
const [activeDetail, setActiveDetail] = useState([]) const [activeDetail, setActiveDetail] = useState([]);
const [filteredContactData, setFiltererdContactData] = useState([])
const [filteredContactData, setFiltererdContactData] = useState([]);
const getContactData = useMutation({ const getContactData = useMutation({
mutationFn: (reqData) => { mutationFn: (reqData) => {
return contactData(reqData) return contactData(reqData);
}, },
onError: (error) => { onError: (error) => {
console.log(error) console.log(error);
}, },
onSuccess: (res) => { onSuccess: (res) => {
if(res?.data?.resultCode != '0'){ if (res?.data?.resultCode != "0") {
throw({message: 'Something went wrong'}) throw { message: "Something went wrong" };
} }
setFiltererdContactData(res?.data?.contacts) setFiltererdContactData(res?.data?.contacts);
} },
}) });
const changeActiveUID = (uid) => { const changeActiveUID = (uid) => {
setActiveContactUID(uid) setActiveContactUID(uid);
let detail = contactsData.filter(item => item.uid == uid) let detail = contactsData.filter((item) => item.uid == uid);
setActiveDetail(detail) setActiveDetail(detail);
} };
const changeActiveCategoryUID = (id) => { const changeActiveCategoryUID = (id) => {
let filteredConData = [] let filteredConData = [];
setActiveCategoryUID(id) setActiveCategoryUID(id);
if(id == '0'){ if (id == "0") {
filteredConData = contactsData filteredConData = contactsData;
} else { } else {
filteredConData = contactsData.filter(item => item.category == id) filteredConData = contactsData.filter((item) => item.category == id);
}
setFiltererdContactData(filteredConData)
changeActiveUID(filteredConData[0]?.uid)
} }
setFiltererdContactData(filteredConData);
changeActiveUID(filteredConData[0]?.uid);
};
useEffect(() => { useEffect(() => {
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
} };
getContactData.mutate(reqData) getContactData.mutate(reqData);
},[]) }, []);
const contactsData = getContactData?.data?.data?.contacts // LIST OF CONTACTS const contactsData = getContactData?.data?.data?.contacts; // LIST OF CONTACTS
const contactsCategory = getContactData?.data?.data?.category // LIST OF CATEGORY const contactsCategory = getContactData?.data?.data?.category; // LIST OF CATEGORY
return ( return (
<> <>
<BreadcrumbComBS title='Contacts' paths={['Dashboard', 'Contacts']} /> <BreadcrumbComBS title="Contacts" paths={["Dashboard", "Contacts"]} />
{getContactData?.isPending ? {getContactData?.isPending ? (
<> <>
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12">
<p className='text-mute'>Loading...</p> <p className="text-mute">Loading...</p>
</div> </div>
</div> </div>
</> </>
: getContactData?.error ? ) : getContactData?.error ? (
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12">
<p className='text-danger'>{getContactData?.error?.message}</p> <p className="text-danger">{getContactData?.error?.message}</p>
</div> </div>
</div> </div>
: ) : (
<div className="row"> <div className="row">
{/*<div className="vh-100 col-12 flex align-items-center">Coming Soon</div>*/} {/*<div className="vh-100 col-12 flex align-items-center">Coming Soon</div>*/}
<div className="col-12"> <div className="col-12">
<div className="card card-statistics mail-contant"> <div
className="card card-statistics mail-contant"
style={{ minHeight: "200px", borderRadius: "10px" }}
>
<div className="card-body p-0"> <div className="card-body p-0">
<div className="row no-gutters"> <div className="row no-gutters">
<div className="col-md-4 col-xxl-2 col-md-4"> <div className="col-md-4 col-xxl-2 col-md-4">
@@ -95,7 +96,12 @@ export default function Contacts(){
<div className="row justify-content-center"> <div className="row justify-content-center">
<div className="d-none col-12"> <div className="d-none col-12">
<div className="text-center mail-sidebar-title px-4"> <div className="text-center mail-sidebar-title px-4">
<a href="#" className="btn btn-primary btn-block py-3 font-weight-bold font-18"><i className="fa fa-plus pl-2"></i></a> <a
href="#"
className="btn btn-primary btn-block py-3 font-weight-bold font-18"
>
<i className="fa fa-plus pl-2"></i>
</a>
</div> </div>
</div> </div>
<div className="col-12"> <div className="col-12">
@@ -111,32 +117,44 @@ export default function Contacts(){
<span>Inbox</span> <span>Inbox</span>
</span> </span>
<span className="nav-item ml-auto text-right"> <span className="nav-item ml-auto text-right">
<span className="badge badge-pill badge-primary float-right">{contactsData?.length}</span> <span className="badge badge-pill badge-primary float-right">
{contactsData?.length}
</span>
</span> </span>
</span> </span>
</a> </a>
</li> </li>
<li className="py-2"> {/*<li className="py-2">*/}
<a href="#"> {/* <a href="#">*/}
<span className="nav align-items-center"> {/* <span*/}
<span> {/* className="nav align-items-center">*/}
<i className="fa fa-paper-plane-o pr-4"></i> {/* <span>*/}
</span> {/* <i className="fa fa-paper-plane-o pr-4"></i>*/}
<span> {/* </span>*/}
<span>Replied Mail</span> {/* <span>*/}
</span> {/* <span>Replied Mail</span>*/}
</span> {/* </span>*/}
</a> {/* </span>*/}
</li> {/* </a>*/}
{/*</li>*/}
</ul> </ul>
<ul className="pl-0 mt-5"> <ul className="pl-0 mt-5">
<li className="py-2" onClick={()=>changeActiveCategoryUID('0')} style={{cursor: 'pointer'}}> <li
className="py-2"
onClick={() => changeActiveCategoryUID("0")}
style={{ cursor: "pointer" }}
>
<div> <div>
<span className="nav align-items-center"> <span className="nav align-items-center">
<span> <span>
<i className={`fa fa-circle-o pr-4 ${activeCategoryUID == '0' ? 'text-primary' : 'text-warning'}`}></i> <i
className={`fa fa-circle-o pr-4 ${
activeCategoryUID == "0"
? "text-primary"
: "text-warning"
}`}
></i>
</span> </span>
<span> <span>
<span>All</span> <span>All</span>
@@ -144,12 +162,27 @@ export default function Contacts(){
</span> </span>
</div> </div>
</li> </li>
{contactsCategory && contactsCategory.map(item => ( {contactsCategory &&
<li key={item?.cid} className="py-2" onClick={()=>changeActiveCategoryUID(`A00000${item?.cid}`)} style={{cursor: 'pointer'}}> contactsCategory.map((item) => (
<li
key={item?.cid}
className="py-2"
onClick={() =>
changeActiveCategoryUID(`${item?.cid}`)
}
style={{ cursor: "pointer" }}
>
<div> <div>
<span className="nav align-items-center"> <span className="nav align-items-center">
<span> <span>
<i className={`fa fa-circle-o pr-4 ${activeCategoryUID == `A00000${item?.cid}` ? 'text-primary' : 'text-warning'}`}></i> <i
className={`fa fa-circle-o pr-4 ${
activeCategoryUID ==
`${item?.cid}`
? "text-primary"
: "text-warning"
}`}
></i>
</span> </span>
<span> <span>
<span>{item?.description}</span> <span>{item?.description}</span>
@@ -158,71 +191,89 @@ export default function Contacts(){
</div> </div>
</li> </li>
))} ))}
{/* <li className="py-2">
<a href="#">
<span className="nav align-items-center">
<span>
<i className="fa fa-circle-o pr-4 text-warning"></i>
</span>
<span>
<span>Work</span>
</span>
</span>
</a>
</li>
<li className="py-2">
<a href="#">
<span className="nav align-items-center">
<span>
<i className="fa fa-plus pr-4"></i>
</span>
<span>
<span>Add Category</span>
</span>
</span>
</a>
</li> */}
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="col-md-8 col-xxl-4 border-md-t"> <div className={`${filteredContactData.length > 0 ? 'col-md-8 col-xxl-4' : 'col-md-8 col-xxl-10'} border-md-t`}>
<div className="mail-content border-right border-n h-100"> <div className="mail-content border-right border-n h-100" style={{placeContent: 'center'}}>
<div className="mail-search border-bottom"> {/* <div className="mail-search border-bottom">
<div className="row align-items-center mx-0"> <div className="row align-items-center mx-0">
<div className="col-12"> <div className="col-12">
{/*<div className="form-group pt-3">*/} <div className="form-group pt-3">
{/* <input type="text" className="form-control" id="search" placeholder="Search.." />*/} <input type="text" className="form-control" id="search" placeholder="Search.." />
{/* <i className="fa fa-search"></i>*/} <i className="fa fa-search"></i>
{/*</div>*/}
</div> </div>
</div> </div>
</div> </div>
</div> */}
<div className="mail-msg scrollbar scroll_dark"> <div className="mail-msg scrollbar scroll_dark">
{contactsData && filteredContactData?.map((contact, index)=> { { filteredContactData.length ?
const isActive = (contact?.uid == activeContactUID) || (!activeContactUID && index == 0) filteredContactData?.map((contact, index) => {
const isActive =
contact?.uid == activeContactUID ||
(!activeContactUID && index == 0);
const avtarImage =
contact?.category === undefined
? "avtar/01.jpg"
: "avtar/" + contact.category + ".png";
return ( return (
<div key={contact?.uid} onClick={()=>changeActiveUID(contact?.uid)} className={`mail-msg-item ${isActive && 'bg-light'}`}> <div
key={contact?.uid}
onClick={() => changeActiveUID(contact?.uid)}
className={`mail-msg-item ${
isActive && "bg-light"
}`}
>
<a href="#"> <a href="#">
<div className="media align-items-center"> <div className="media align-items-center">
<div className="mr-3"> <div className="mr-3">
<div className="bg-img"> <div className="bg-img">
<img src={getImage("avtar/01.jpg")} className="img-fluid" alt="user" /> <img
src={getImage(avtarImage)}
className="img-fluid"
alt="user"
/>
</div> </div>
</div> </div>
<div className="w-100"> <div className="w-100">
<div className="mail-msg-item-titel justify-content-between"> <div className="mail-msg-item-titel justify-content-between">
<p>{contact?.sender}</p> <p>
<span
style={{
fontSize: "14px",
color: "#148399",
fontWeight: "bolder",
}}
>
{contact?.sender}
</span>
</p>
{/* <p className="d-none d-xl-block">06:59 <span> PM </span></p> */} {/* <p className="d-none d-xl-block">06:59 <span> PM </span></p> */}
<p className="d-none d-xl-block"><span>{new Date(contact?.added).toDateString()}</span></p> <p className="d-none d-xl-block">
<span style={{ fontSize: "14px" }}>
{new Date(
contact?.added
).toDateString()}
</span>
</p>
</div> </div>
<h5 className="mb-0 my-2">{contact?.title}</h5> <h5 className="mb-0 my-2">
<p>{contact?.message?.length < 100 ? contact?.message : contact?.message.substring(0,101) + ' ...' }</p> {contact?.title}
</h5>
<p>
{contact?.message?.length < 100
? contact?.message
: contact?.message.substring(0, 101) +
" ..."}
</p>
<p className="d-xl-none"> <p className="d-xl-none">
<span> <span>
{new Date(contact?.added).toDateString()} {new Date(
contact?.added
).toDateString()}
{/* {getCustomTime(contact.added)} */} {/* {getCustomTime(contact.added)} */}
</span> </span>
</p> </p>
@@ -230,26 +281,47 @@ export default function Contacts(){
</div> </div>
</a> </a>
</div> </div>
) );
})} })
:
<p className="text-center">Messages will appear here as soon as they are available for selection</p>
}
</div> </div>
</div> </div>
</div> </div>
{filteredContactData.length > 0 &&
<div className="col-xxl-6 border-t border-xxl-t"> <div className="col-xxl-6 border-t border-xxl-t">
<div className="mail-chat py-5 px-5"> <div className="mail-chat py-5 px-5">
<div className="media align-items-center"> <div className="media align-items-center">
<div className="bg-img mr-3"> <div className="bg-img mr-3">
<img src={getImage("avtar/03.jpg")} className="img-fluid" alt="user" /> <img
src={activeContactUID ? getImage("avtar/" + activeDetail[0].category + ".png") : getImage(filteredContactData[0] == undefined ? "avtar/01.jpg": "avtar/" + filteredContactData[0].category + ".png")}
className="img-fluid"
alt="user"
/>
</div> </div>
<div> <div>
<h4 className="mb-0">{activeContactUID ? activeDetail[0]?.sender : filteredContactData[0]?.sender}</h4> <h4 className="mb-0" style={{ color: "#148399" }}>
<p>{activeContactUID ? new Date(activeDetail[0]?.added).toDateString() : new Date(filteredContactData[0]?.added).toDateString()}</p> {activeContactUID
? activeDetail[0]?.sender
: filteredContactData[0]?.sender}
</h4>
<p>
{activeContactUID
? new Date(activeDetail[0]?.added).toDateString()
: new Date(
filteredContactData[0]?.added
).toDateString()}
</p>
</div> </div>
</div> </div>
<div className="mt-4 d-flex justify-content-between"> <div className="mt-4 d-flex justify-content-between">
<div> <div>
<h3>{activeContactUID ? activeDetail[0]?.title : filteredContactData[0]?.title}</h3> <h3>
{activeContactUID
? activeDetail[0]?.title
: filteredContactData[0]?.title}
</h3>
</div> </div>
<div className="d-flex"> <div className="d-flex">
{/*<a href="javascript:void(0)"><i className="fa fa-reply font-22 pr-3"></i></a>*/} {/*<a href="javascript:void(0)"><i className="fa fa-reply font-22 pr-3"></i></a>*/}
@@ -257,53 +329,21 @@ export default function Contacts(){
</div> </div>
</div> </div>
<div> <div>
<p>{activeContactUID ? activeDetail[0]?.message : filteredContactData[0]?.message}</p> <p>
{/* <p className="my-4">hey adminjon...</p> {activeContactUID
<p className="mb-2">I truly believe Augustines words are true and if you look at history you know it is true. There are many people in the world with amazing talents who realize only a small percentage of their potential. We all know people who live this truth.</p> ? activeDetail[0]?.message
<p>We also know those epic stories, those modern-day legends surrounding the early failures of such supremely successful folks as Michael Jordan and Bill Gates. We can look a bit further back in time to Albert Einstein or even further back to Abraham Lincoln. What made each of these people so successful? Motivation.</p> : filteredContactData[0]?.message}
<p>We know this in our gut, but what can we do about it? How can we motivate ourselves? One of the most difficult aspects of achieving success is staying motivated over the long haul.</p> */} </p>
{/*<div className="my-5">*/}
{/* <p>Have lovely Day,</p>*/}
{/* <p>adminjon</p>*/}
{/*</div>*/}
</div>
</div>
<div className="bg-light mail-f px-4 py-3">
<div className="py-2 bg-white px-4 py-3 d-flex justify-content-between">
<p>Click here to <a href="#editer" data-toggle="collapse" className="text-primary px-1">Reply</a>or<a href="#forward" data-toggle="collapse" className="text-primary px-1">Forward</a></p>
<a href="#" className="text-primary"><i className="fa fa-microphone"></i></a>
</div>
<div className="collapse" id="editer">
<div className="form-group">
<textarea className="form-control mt-3" id="exampleFormControlTextarea1" rows="3" placeholder="Type here..."></textarea>
</div>
</div>
<div className="collapse" id="forward">
<div className="form-group">
<input className="form-control mt-3" id="exampleFormControl" placeholder="Email Address" />
</div>
</div>
<div className="d-flex align-items-center justify-content-between py-2">
<div>
{/*<ul className="nav">*/}
{/* <li className="nav-item pr-3"><a href="javascript:void(0)"><i className="ti ti-clip font-20"></i></a></li>*/}
{/* <li className="nav-item pr-3"><a href="javascript:void(0)"><i className="ti ti-face-smile font-20"></i></a></li>*/}
{/* <li className="nav-item"><a href="javascript:void(0)"><i className="ti ti-gallery font-20"></i></a></li>*/}
{/*</ul>*/}
</div>
<div>
{/*<a href="javascript:void(0)" className="btn btn-primary"><span>Send</span> <i className="fa fa-paper-plane"></i></a>*/}
</div>
</div>
</div>
</div>
</div>
</div>
</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) => { 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',
+88
View File
@@ -0,0 +1,88 @@
import React from 'react'
import { getDashPayments } from '../../services/services'
import { useQuery } from '@tanstack/react-query'
import queryKeys from '../../services/queryKeys'
import getDateTimeFromDateString from '../../helpers/getDateTimeFromDateString'
import getImage from '../../utils/getImage'
export default function DashPayments() {
const {data, isFetching, isError, error} = useQuery({
queryKey: queryKeys.dash_payments,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid') // USER UID
}
return getDashPayments(reqData)
}
})
const payments = data?.data?.member_payments
// console.log('data', payments)
return (
<>
<div className="col-xxl-4 m-b-30" style={{minHeight: '300px'}}>
<div className="card card-statistics h-100 mb-0 panel_round_c3">
<div className="card-header d-flex justify-content-between">
<div className="card-heading">
<h4 className="card-title">Payments</h4>
</div>
</div>
<div className="overflow-y-auto card-body scrollbar scroll_dark pt-0" style={{maxHeight: '350px'}}>
<div className="datatable-wrapper table-responsive">
{isFetching ?
<>
<div className="col-12">
<div className="p-4">
<p className='text-mute'>Loading...</p>
</div>
</div>
</>
: isError ?
<div className="col-12">
<div className="p-4">
<p className='text-danger'>{error.message}</p>
</div>
</div>
:
<table id="datatable" className="table table-borderless table-striped">
<thead>
<tr>
{/* <th style={{width: '30px'}}>#</th> */}
<th>Date</th>
<th style={{width: '130px'}}>Description</th>
<th style={{width: '80px'}}>Amount</th>
</tr>
</thead>
<tbody>
{payments && payments?.length > 0 ?
payments.map((item, index) => {
return (
<tr key={index}>
{/* <td>{Number(item?.id).toString().padStart(6,'0')}</td> */}
<td>
{getDateTimeFromDateString(item?.added)}
</td>
<td>{item?.option_name}</td>
<td className='text-right'>${item?.amount}</td>
</tr>
)
})
:
<td colSpan={3} className='text-center'>No record found</td>
}
</tbody>
</table>
}
</div>
</div>
</div>
</div>
</>
)
}
+2 -35
View File
@@ -5,6 +5,7 @@ import Products from "./Products";
import TopBar from "./TopBar"; import TopBar from "./TopBar";
import ProductsURL from "./ProductsURL"; import ProductsURL from "./ProductsURL";
import { SocketContextValues } from "../context/SocketIOContext"; import { SocketContextValues } from "../context/SocketIOContext";
import DashPayments from "./DashPayments";
export default function HomeSections(){ export default function HomeSections(){
@@ -52,41 +53,7 @@ export default function HomeSections(){
<div className="row"> <div className="row">
<ProductsURL /> <ProductsURL />
<div className="col-xxl-4 m-b-30" style={{minHeight: '300px'}}> <DashPayments />
<div className="card card-statistics h-100 mb-0 panel_round_c3">
<div className="card-header d-flex justify-content-between">
<div className="card-heading">
<h4 className="card-title">Payments</h4>
</div>
{/*<div className="dropdown">*/}
{/* <a className="p-2" href="#!" data-toggle="dropdown" aria-haspopup="true"*/}
{/* aria-expanded="false">*/}
{/* <i className="fe fe-circle"></i>*/}
{/* </a>*/}
{/* <div className="dropdown-menu custom-dropdown dropdown-menu-right p-4">*/}
{/* <h6 className="mb-1">Action</h6>*/}
{/* <a className="dropdown-item" href="#!"><i className="fa-fw fa fa-file-o pr-2"></i>View*/}
{/* reports</a>*/}
{/* <a className="dropdown-item" href="#!"><i className="fa-fw fa fa-edit pr-2"></i>Edit reports</a>*/}
{/* <a className="dropdown-item" href="#!"><i className="fa-fw fa fa-bar-chart-o pr-2"></i>Statistics</a>*/}
{/* <h6 className="mb-1 mt-3">Export</h6>*/}
{/* <a className="dropdown-item" href="#!"><i className="fa-fw fa fa-file-pdf-o pr-2"></i>Export*/}
{/* to PDF</a>*/}
{/* <a className="dropdown-item" href="#!"><i className="fa-fw fa fa-file-excel-o pr-2"></i>Export*/}
{/* to CSV</a>*/}
{/* </div>*/}
{/*</div>*/}
</div>
<div className="card-body">
{/*<h5>We only started collecting data from February 2019 </h5>*/}
{/*<p>Questions about the Net Earnings number? <a*/}
{/* className="btn btn-square btn-inverse-success btn-xs ml-1" href="#">Click here</a></p>*/}
<div className="row mt-4">
.
</div>
</div>
</div>
</div>
</div> </div>
</>; </>;
+3 -3
View File
@@ -44,13 +44,13 @@ export default function Products() {
{products && products.map((product, index) => ( {products && products.map((product, index) => (
<div key={product.uid+index} className={`col-xxs-6 col-xl-4 col-xxl-6 mb-2 mb-xxl-0`}> <div key={product.uid+index} className={`col-xxs-6 col-xl-4 col-xxl-6 mb-2 mb-xxl-0`}>
<Link to={productPath(product?.product_id)} > <Link to={productPath(product?.product_id)} >
<div className={`d-flex align-items-center extraProductCard ${product?.icon_style}`} > <div className={`d-flex align-items-center extraProductCard ${product?.icon_style}`} style={{borderColor:'black', borderWidth: '2px'}} >
<div className="icon-container img-icon m-r-20 bg-light-gray rounded"> <div className="icon-container img-icon m-r-20 bg-light-gray rounded">
<i className={`fa ${product?.product_icon} text-primary`}></i> <i className={`fa ${product?.product_icon} text-primary`}></i>
</div> </div>
<div className="report-details"> <div className="report-details">
<p>{product?.status_text}</p> <p><span style={{fontWeight: 'bolder', color: '#00557A'}}>{product?.status_text}</span></p>
<h4>{product?.name}</h4> <h4><span style={{paddingLeft: '10px'}}>{product?.name}</span></h4>
</div> </div>
</div> </div>
</Link> </Link>
+12 -3
View File
@@ -1,6 +1,7 @@
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import { topBar } from '../../services/services' import { topBar } from '../../services/services'
import queryKeys from '../../services/queryKeys' import queryKeys from '../../services/queryKeys'
import { Link } from 'react-router-dom'
export default function TopBar() { export default function TopBar() {
@@ -56,19 +57,26 @@ export default function TopBar() {
<> <>
{data && data?.map((item, index)=>{ {data && data?.map((item, index)=>{
let textColor = item?.description == 'Contacts' ? 'text-danger' : item?.description == 'Site Traffic' ? 'text-primary' : item?.description == 'Appointments' ? 'text-orange' : 'text-success' let textColor = item?.description == 'Contacts' ? 'text-danger' : item?.description == 'Site Traffic' ? 'text-primary' : item?.description == 'Appointments' ? 'text-orange' : 'text-success'
let dataSpan = ''
if(item?.extra_style){
const data = item.data_span.split(' ')
dataSpan = `${data[0]} ${data[1]}`
}
return ( return (
<div key={item.id + index} className="col-sm-6 col-xxl-3"> <div key={item.id + index} className="col-sm-6 col-xxl-3">
<div className={`card card-statistics ecommerce-contant overflow-h ${item?.extra_style} `}> <Link to={item?.link}>
<div className={`card card-statistics ecommerce-contant overflow-h ${item?.extra_style} `} style={{borderRadius: '10px'}}>
<div className="card-body p-0"> <div className="card-body p-0">
<div className="d-flex m-b-0 ecommerce-contant-text h-100"> <div className="d-flex m-b-0 ecommerce-contant-text h-100">
<div className="w-100"> <div className="w-100">
<div className="row p-3"> <div className="row p-3">
<div className="col"> <div className="col">
<h3 className="mb-0">{item?.value || 0}</h3> <h3 className="mb-0">{item?.value || 0}</h3>
<small className="d-block">{item?.data_span}</small> <small className="d-block">{item?.extra_style ? dataSpan : item?.data_span}</small>
</div> </div>
<div className="col text-right"> <div className="col text-right">
<h5 className="text-muted mb-0"><a href={item?.link}>{item?.description}</a></h5> <h5 className="text-muted mb-0">{item?.description}</h5>
</div> </div>
</div> </div>
<div className="apexchart-wrapper"> <div className="apexchart-wrapper">
@@ -78,6 +86,7 @@ export default function TopBar() {
</div> </div>
</div> </div>
</div> </div>
</Link>
</div> </div>
) )
})} })}
+3 -3
View File
@@ -16,7 +16,7 @@ export default function UserMenu() {
return ( return (
<> <>
<div className="sidebar-nav scrollbar scroll_dark"> <div className="sidebar-nav scrollbar scroll_dark">
<ul className="metismenu " id="sidebarNav"> <ul className="metismenu h-100" id="sidebarNav">
<li className="nav-static-title">Panel</li> <li className="nav-static-title">Panel</li>
<li className={`${pathname == siteLinks.dash ? 'active' : ''}`}> <li className={`${pathname == siteLinks.dash ? 'active' : ''}`}>
<Link className="has-arrow" to='#' data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> <Link className="has-arrow" to='#' data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
@@ -30,7 +30,7 @@ export default function UserMenu() {
<ul id="collapseOne" className="collapse show" aria-labelledby="headingOne" data-bs-parent="#sidebarNav"> <ul id="collapseOne" className="collapse show" aria-labelledby="headingOne" data-bs-parent="#sidebarNav">
<li className={`${pathname == siteLinks.dash ? 'active' : ''}`}><Link to={siteLinks.dash}>Home</Link></li> <li className={`${pathname == siteLinks.dash ? 'active' : ''}`}><Link to={siteLinks.dash}>Home</Link></li>
<li className={`${pathname == siteLinks.calendar ? 'active' : ''}`}><Link to={siteLinks.calendar}>Calendar</Link></li> <li className={`${pathname == siteLinks.calendar ? 'active' : ''}`}><Link to={siteLinks.calendar}>Calendar</Link></li>
<li className={`${pathname == siteLinks.contacts ? 'active' : ''}`}><Link to={siteLinks.contacts}>Contacts</Link></li> <li className={`${pathname == siteLinks.contacts ? 'active' : ''}`}><Link to={siteLinks.contacts}>Sites Contacts</Link></li>
<li className={`${pathname == siteLinks.comments ? 'active' : ''}`}><Link to={siteLinks.comments}>Comments</Link></li> <li className={`${pathname == siteLinks.comments ? 'active' : ''}`}><Link to={siteLinks.comments}>Comments</Link></li>
</ul> </ul>
</li> </li>
@@ -54,7 +54,7 @@ export default function UserMenu() {
</li> </li>
<li className="sidebar-banner p-4 bg-gradient text-center m-3 d-block rounded"> <li className="sidebar-banner p-4 bg-gradient text-center m-3 mt-auto d-block rounded">
<h5 className="text-white mb-1">MERMS Panel</h5> <h5 className="text-white mb-1">MERMS Panel</h5>
<Link className="btn btn-square btn-inverse-light btn-xs d-inline-block mt-2 mb-0" to='' onClick={logout}> Log Out</Link> <Link className="btn btn-square btn-inverse-light btn-xs d-inline-block mt-2 mb-0" to='' onClick={logout}> Log Out</Link>
</li> </li>
+23 -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']} />*/}
@@ -46,7 +58,9 @@ export default function ProductActive({productData}){
<div className="card card-statistics"> <div className="card card-statistics">
<div className="card-header"> <div className="card-header">
<div className="card-heading d-flex justify-content-between"> <div className="card-heading d-flex justify-content-between">
<h4 className="card-title">{externalUrl}</h4> {/*<h4 className="card-title">{externalUrl}</h4>*/}
<h4 style={{color: '#148399', fontWeight: 'bolder'}}>
<a href={externalUrl} target='_blank'>{externalUrl}</a></h4>
<button type="button" onClick={()=>iframe.current.src += ''} className="btn"> <button type="button" onClick={()=>iframe.current.src += ''} className="btn">
<img src={getImage('refresh.png')} style={{width: '30px', height: 'auto'}} alt='refresh page' /> <img src={getImage('refresh.png')} style={{width: '30px', height: 'auto'}} alt='refresh page' />
</button> </button>
@@ -56,11 +70,16 @@ 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">
{refreshMsg &&
<p className="text-success text-center">{refreshMsg}</p>
}
<button type="button" onClick={handleRefresh} className="btn btn-primary">{refresh.isPending ? 'Initiating...' : 'Rebuild Site'}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div>
<div className="col-xxl-6"> <div className="col-xxl-6">
<div className="card card-statistics"> <div className="card card-statistics">
<div className="card-header"> <div className="card-header">
+5 -2
View File
@@ -2,14 +2,16 @@ import { useQuery } from '@tanstack/react-query'
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
// import getImage from "../../utils/getImage"; // import getImage from "../../utils/getImage";
import ProductStart from "./ProductStart"; import ProductStart from "./ProductStart";
import { useLocation } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import {MyProductData} from "../../services/services"; import {MyProductData} from "../../services/services";
import ProductActive from "./ProductActive"; import ProductActive from "./ProductActive";
import ProductProvision from "./ProductProvision"; import ProductProvision from "./ProductProvision";
import {productConst} from "../../constants/products"; import {productConst} from "../../constants/products";
import queryKeys from "../../services/queryKeys"; import queryKeys from "../../services/queryKeys";
import siteLinks from '../../links/siteLinks';
export default function ProductFactory(){ export default function ProductFactory(){
const navigate = useNavigate()
const location = useLocation(); const location = useLocation();
const pathname = location.pathname; const pathname = location.pathname;
@@ -45,8 +47,9 @@ export default function ProductFactory(){
</> </>
: isError ? : isError ?
<div className="row"> <div className="row">
<div className="col-12"> <div className="text-center col-12" style={{minHeight: '500px', placeContent: 'center'}}>
<p className='text-danger'>{error?.message}</p> <p className='text-danger'>{error?.message}</p>
<button onClick={() => navigate(siteLinks.home)} className='mt-3 btn btn-primary'>Return Home</button>
</div> </div>
</div> </div>
: :
+12 -5
View File
@@ -28,7 +28,9 @@ export default function ProductProvision(props){
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 (
@@ -59,8 +61,11 @@ export default function ProductProvision(props){
</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>
@@ -108,8 +113,10 @@ export default function ProductProvision(props){
<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>
<img className="card-img-top" src={getImage('widget/working.jpg')}
alt="Card image cap"/>
{/* <div className="card-body"> {/* <div className="card-body">
<div className="" dangerouslySetInnerHTML={{__html: productDescription}}/> <div className="" dangerouslySetInnerHTML={{__html: productDescription}}/>
</div> */} </div> */}
+9 -13
View File
@@ -83,7 +83,7 @@ export default function ProductStart(props){
<img className="card-img-top" src={getImage(productBanner)} alt="Card image cap" /> <img className="card-img-top" src={getImage(productBanner)} alt="Card image cap" />
<div className="card-body"> <div className="card-body">
<h4 className="card-title">{productTitle}</h4> <h4 className="card-title">{productTitle}</h4>
<div className="card-text" dangerouslySetInnerHTML={{__html: productDescription}}/> <div className="card-text" style={{fontSize: '14px'}} dangerouslySetInnerHTML={{__html: productDescription}}/>
{/* <p className="card-text">{productDescription}</p> */} {/* <p className="card-text">{productDescription}</p> */}
</div> </div>
</div> </div>
@@ -97,7 +97,7 @@ export default function ProductStart(props){
</> </>
<div className="card-body"> <div className="card-body">
<p className="card-text">Start with your goals in mind and then work possible. </p> <p className="card-text">We are actively working to launch this feature soon and will keep you informed every step of the way. </p>
</div> </div>
<ul className="list-group list-group-flush"> <ul className="list-group list-group-flush">
<li className="list-group-item"><h4>Coming soon!!!</h4></li> <li className="list-group-item"><h4>Coming soon!!!</h4></li>
@@ -109,19 +109,15 @@ export default function ProductStart(props){
{/*<img className="card-img-top" src={getImage('widget/01.jpg')} alt="Card image cap" />*/} {/*<img className="card-img-top" src={getImage('widget/01.jpg')} alt="Card image cap" />*/}
<div className="card-body"> <div className="card-body">
<h4 className="card-title">Subscription</h4> <h4 className="card-title">Subscription</h4>
<p className="card-text">Start with your goals in mind and then work possible.ith yand Goals. If the plan doesnt support the vision then change it! </p> <p className="card-text">Launch your <span style={{color: '#148399' , fontWeight: 'bolder'}}>{productTitle}</span> with us for free! Benefit from our dedicated support and customize features that align perfectly with your goals. Experience everything at no cost! </p>
</div> </div>
<ul className="list-group list-group-flush"> <ul className="list-group list-group-flush">
<li className="list-group-item"><h4>{promotion_text}</h4></li> <li className="list-group-item"><h4>{promotion_text}</h4></li>
<li className="list-group-item">90 days free and 3.95/Month</li> <li className="list-group-item">90 days free and $5.99/Month</li>
<li className="list-group-item"></li> <li className="list-group-item"></li>
</ul> </ul>
{/*<div className="card-body">*/}
{/* <a href="javascript:void(0)" className="card-link">Card link</a>*/}
{/* <a href="javascript:void(0)" className="card-link">Another link</a>*/}
{/*</div>*/}
<div className="subscribe-box"> <div className="subscribe-box">
<button className="btn btn-primary mt-2" data-bs-toggle="modal" data-bs-target="#verticalCenter">Start Subscription</button> <button className="btn btn-primary mt-2" data-bs-toggle="modal" data-bs-target="#verticalCenter">Activate Now!</button>
</div> </div>
</div> </div>
</> </>
@@ -134,13 +130,13 @@ export default function ProductStart(props){
<div className="modal-dialog modal-dialog-centered" role="document"> <div className="modal-dialog modal-dialog-centered" role="document">
<div className="modal-content"> <div className="modal-content">
<div className="modal-header"> <div className="modal-header">
<h5 className="modal-title" id="verticalCenterTitle">{productTitle}</h5> <h5 className="modal-title" style={{fontSize: '18px'}} id="verticalCenterTitle">{productTitle}</h5>
<button type="button" className="close" data-bs-dismiss="modal" aria-label="Close"> <button type="button" className="close" data-bs-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div className="modal-body"> <div className="modal-body">
<div className="" dangerouslySetInnerHTML={{__html: saleText}}/> <div style={{fontSize: '18px'}} className="" dangerouslySetInnerHTML={{__html: saleText}}/>
{/* {mutation.error && {/* {mutation.error &&
<> <>
<div className="col-12"> <div className="col-12">
@@ -162,8 +158,8 @@ export default function ProductStart(props){
)} )}
</div> </div>
<div className="modal-footer"> <div className="modal-footer">
<button type="button" className="btn btn-danger" data-bs-dismiss="modal">Close</button> <button type="button" className="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
<button type="button" className="btn btn-success" disabled={mutation.isSuccess} onClick={handleSubscribe}>{mutation.isPending ? 'loading...' : 'Start'}</button> <button type="button" className="btn btn-primary" disabled={mutation.isSuccess} onClick={handleSubscribe}>{mutation.isPending ? 'activating...' : 'Start Activation'}</button>
</div> </div>
</div> </div>
</div> </div>
@@ -0,0 +1,6 @@
const ColorStyleConfigure =()=>{
return <>COLOR CONFIG</>
}
export default ColorStyleConfigure
@@ -1,25 +1,55 @@
import React, {memo, useState} from 'react' import React, {memo, useEffect, useMemo, useState} from 'react'
import { useMutation } from "@tanstack/react-query"; import {useMutation, useQueryClient} from "@tanstack/react-query";
import {pageSettings} from "../../../services/services"; import {pageSettings} from "../../../services/services";
import SiteTemplateSelector from './SiteTemplateSelector'; import SiteTemplateSelector from './SiteTemplateSelector';
import NoYesBooleanDropdown from './NoYesBooleanDropdown'; import NoYesBooleanDropdown from './NoYesBooleanDropdown';
import {IoMdArrowDropdown} from 'react-icons/io'; 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()
const [reqStatus, setReqStatus] = useState({error: null, message: ''}) const [reqStatus, setReqStatus] = useState({error: null, message: ''})
// const computeFieldData = useMemo(()=>{
// const fieldData = {}
// Object.entries(data)?.forEach(([key, value]) => { // LOOP TO POPULATE FIELDDATA PROPERTIES WITH DATA OF EACH TAB
// fieldData[value?.name?.toLowerCase().replaceAll(" ", "_")] = ''
// })
// backendValues?.data?.forEach(item => { //LOOPING THROUGH USER ALREADY ADDED DATA FROM BACKEND IF ANY AND UPDATING THE FIELDDATA OBJECT
// fieldData[item?.setting_key?.toLowerCase().replaceAll(" ", "_")] = item?.setting_value
// })
// return fieldData
// },[backendValues.data])
const [fields, setFields] = useState({})
const sortedData = sortObjectByListOrder(data ? data : {}) // SORTED SETTINGSCONFIG
useEffect(() => {
const fieldData = {} const fieldData = {}
Object.entries(data)?.forEach(([key, value]) => { // LOOP TO POPULATE FIELDDATA PROPERTIES WITH DATA OF EACH TAB Object.entries(sortedData)?.forEach(([key, value]) => { // LOOP TO POPULATE FIELDDATA PROPERTIES WITH DATA OF EACH TAB
fieldData[value?.name?.toLowerCase().replaceAll(" ", "_")] = '' fieldData[value?.name?.toLowerCase().replaceAll(" ", "_")] = ''
}) })
backendValues.forEach(item => { //LOOPING THROUGH USER ALREADY ADDED DATA FROM BACKEND IF ANY AND UPDATING THE FIELDDATA OBJECT backendValues?.data?.forEach(item => { //LOOPING THROUGH USER ALREADY ADDED DATA FROM BACKEND IF ANY AND UPDATING THE FIELDDATA OBJECT
fieldData[item?.setting_key?.toLowerCase().replaceAll(" ", "_")] = item?.setting_value fieldData[item?.setting_key?.toLowerCase().replaceAll(" ", "_")] = item?.setting_value
}) })
setFields(fieldData)
console.log('fieldData', fieldData) }, [backendValues.data])
const [fields, setFields] = useState(fieldData)
const handleChange = ({target: {name, value}}) => { const handleChange = ({target: {name, value}}) => {
setFields(prev => ({...prev, [name]: value})) setFields(prev => ({...prev, [name]: value}))
@@ -41,6 +71,9 @@ const GeneralTab = memo(({name='Full Name', data, isCustom, productData, backend
setReqStatus({error: true, message: 'Unable to complete, try again later'}) setReqStatus({error: true, message: 'Unable to complete, try again later'})
}, },
onSettled: () => { onSettled: () => {
queryClient.refetchQueries({ // refetches productProvision API call
queryKey: [...queryKeys.settingsData],
})
setTimeout(() => { setTimeout(() => {
setReqStatus({error: null, message: ''}) setReqStatus({error: null, message: ''})
}, 3000) }, 3000)
@@ -56,41 +89,73 @@ const GeneralTab = memo(({name='Full Name', data, isCustom, productData, backend
...fields ...fields
} }
} }
// console.log('formInfo', reqData)
submitSettings.mutate(reqData) submitSettings.mutate(reqData)
} }
console.log(tabKey);
if (isCustom === true){
return <SiteTemplateSelector name={name} data={data} isCustom={isCustom} productData={productData} />
}
return ( return (
<>
{backendValues?.isFetching || !backendValues?.data ?
<>
<div className="row">
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</div>
</>
: backendValues?.isError ?
<div className="row">
<div className="col-12">
<p className='text-danger'>{backendValues?.error.message}</p>
</div>
</div>
:
<>
{isCustom === true ?
<>
{(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="page-account-form">
<div className="p-0" style={{minHeight: '500px'}}> <div className="p-0" style={{minHeight: '500px'}}>
<form id='tab_form'> <form id='tab_form'>
<div className="form-row"> <div className="form-row">
<> <>
{Object.entries(data)?.map(([key, value]) => { {Object.entries(sortedData)?.map(([key, value]) => {
let fieldName = value.name.toLowerCase().replaceAll(" ", "_") let fieldName = key; // value.key.toLowerCase().replaceAll(" ", "_")
let fieldValue = fields[value.name.toLowerCase().replaceAll(" ", "_")] let fieldValue = fields[key]; //fields[value.name.toLowerCase().replaceAll(" ", "_")]
return ( return (
<div key={key} className="form-group col-md-12"> <div key={key} className="form-group col-md-12">
<label htmlFor="name1">{value.name}</label> <label htmlFor="name1">{value.name}</label>
{value.controls == 'TEXT' ? {value.controls === 'TEXT' ?
<input name={fieldName} type="text" className="form-control" id={key} value={fieldValue} onChange={handleChange} /> <input name={fieldName} type="text"
:value.controls == 'TEXTAREA' ? className="form-control" id={key}
<textarea name={fieldName} rows={5} style={{resize: 'none'}} type="text" className="form-control" id={key} value={fieldValue} onChange={handleChange} /> value={fieldValue} onChange={handleChange}/>
: value.controls == 'SELECT_NO_YES' ? : value.controls === 'TEXTAREA' ?
<textarea name={fieldName} rows={5}
style={{resize: 'none'}} type="text"
className="form-control" id={key}
value={fieldValue}
onChange={handleChange}/>
: value.controls === 'SELECT_NO_YES' ?
// <NoYesBooleanDropdown name={fieldName} value={fieldValue} onChange={handleChange} /> // <NoYesBooleanDropdown name={fieldName} value={fieldValue} onChange={handleChange} />
<div className='position-relative'> <div className='position-relative'>
<select onChange={handleChange} name={fieldName} value={fieldValue} className="form-control"> <select onChange={handleChange}
name={fieldName} value={fieldValue}
className="form-control">
<option value=''>Select</option> <option value=''>Select</option>
<option value='0'>No</option> <option value='0'>No</option>
<option value='1'>Yes</option> <option value='1'>Yes</option>
</select> </select>
<IoMdArrowDropdown className='position-absolute w-auto' style={{top: '50%', right: '2px', transform: 'translateY(-50%)'}} /> <IoMdArrowDropdown
className='position-absolute w-auto'
style={{
top: '50%',
right: '2px',
transform: 'translateY(-50%)'
}}/>
</div> </div>
: :
null null
@@ -108,12 +173,17 @@ const GeneralTab = memo(({name='Full Name', data, isCustom, productData, backend
</> </>
} }
<div className="form-group col-md-12" style={{textAlign: 'right'}}> <div className="form-group col-md-12" style={{textAlign: 'right'}}>
<button onClick={handleSubmit} type="button" className="btn btn-primary" disabled={submitSettings.isPending}>{submitSettings.isPending ? 'Loading...' : 'Update'}</button> <button onClick={handleSubmit} type="button" className="btn btn-primary"
disabled={submitSettings.isPending}>{submitSettings.isPending ? 'Loading...' : 'Update'}</button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
}
</>
}
</>
) )
} }
) )
+31 -70
View File
@@ -1,78 +1,35 @@
import React, { memo, useMemo, useState } from 'react' import React, { memo, useMemo, useState } from 'react'
import GeneralTab from './GeneralTab' import GeneralTab from './GeneralTab'
import { getSettingsData } from '../../../services/services'; import { getSettingsData, getMyProductConfig } from '../../../services/services';
import queryKeys from '../../../services/queryKeys'; import queryKeys from '../../../services/queryKeys';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import sortObjectByListOrder from '../../../helpers/sortObjectByListOrder'
const Settings = memo(({productData}) => { const Settings = memo(({productData}) => {
const { userDetails: { uid }} = useSelector((state) => state?.userDetails); // GETS USER UID const { userDetails: { uid }} = useSelector((state) => state?.userDetails); // GETS USER UID
const dataFields ={ const {data:configData, isFetching:configIsFetching, configIsError, error:configError} = useQuery({
site_title: { name: 'Title', controls: 'TEXT', active: true }, queryKey: queryKeys.myProductConfig,
site_description: { name: 'Description', controls: 'TEXTAREA', active: true }, queryFn: () => {
site_logo_text: { name: 'Logo Text', controls: 'TEXT', active: true }, let reqData = {
site_contact_email: { name: 'Email', controls: 'TEXT', active: true }, token: localStorage.getItem('token'), // USER TOKEN
site_contact_phone: { name: 'Phone', controls: 'TEXT', active: true }, uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id
} }
return getMyProductConfig(reqData)
const socialFields ={ },
facebook: { name: 'Facebook', controls: 'TEXT', active: true }, staleTime: 0,
twitter: { name: 'Twitter', controls: 'TEXT', active: true }, })
youtube: { name: 'Youtube', controls: 'TEXT', active: true }, const settingsConfig = configData?.data?.settings_items
} // console.log('CONFIG DATA...', settingsConfig)
const homeFields ={
banner_text: { name: 'Banner Text', controls: 'TEXT', active: true },
banner_description: { name: 'Banner Description', controls: 'TEXTAREA', active: true },
}
const footerFields ={
footer_description: { name: 'Footer Description', controls: 'TEXTAREA', active: true },
boolean_footer_show_email: { name: 'Show email in footer', controls: 'SELECT_NO_YES', active: true },
}
const aboutFields ={
about_title: { name: 'About Title', controls: 'TEXT', active: true },
about_description: { name: 'About Details', controls: 'TEXTAREA', active: true },
about_extra_1: { name: 'Extra About us', controls: 'TEXTAREA', active: true },
about_extra_2: { name: 'More About us', controls: 'TEXTAREA', active: true },
}
const templateData = {
template_16 : { title: 'Template Name-16', template_id: '02af24fd-2b1a-46ed-af21-87018e726408', banner: 'file-icon/svg.png', active: '' },
template_22 : { title: 'Template Name-22', template_id: '8b296894-42e4-4f2e-abd1-7c2a38d6e07b', banner: 'file-icon/svg.png', active: '' },
template_47 : { title: 'Template Name-47', template_id: 'ef2ffa1c-9272-42cd-9d33-0e614047b4f8', banner: 'file-icon/svg.png', active: '' },
template_25 : { title: 'Template Name-25', template_id: 'b3a7ba31-dc47-4a40-a5cc-fd1ff27d6b78', banner: 'file-icon/svg.png', active: '' },
template_49 : { title: 'Template Name-49', template_id: '60959c69-6672-4f69-a006-eeb7d210e605', banner: 'file-icon/svg.png', active: '' },
template_27 : { title: 'Template Name-27', template_id: 'e4acb98a-c584-45f2-bece-af677dcf0a1f', banner: 'file-icon/svg.png', active: '' },
template_51 : { title: 'Template Name-51', template_id: '975ee42e-3169-4978-92d7-d28e7e2ac014', banner: 'file-icon/svg.png', active: '' },
template_9 : { title: 'Template Name-9', template_id: 'fc8f0738-6500-4775-9895-2047cd275302', banner: 'file-icon/svg.png', active: '' },
}
const contactFields ={
contact_title : { name: 'Contact Title', controls: 'TEXT', active: true },
contact_introduction: { name: 'Extra Introduction', controls: 'TEXTAREA', active: true },
}
const settingsObject = useMemo(()=>{
return {
settings: { title: 'Settings', controls: 'settings', active: 'active show' , custom: false, data: dataFields},
home_tab: { title: 'Home Page', controls: 'home', active: '', custom: false, data: homeFields},
footer_tab: { title: 'Footer', controls: 'footer', active: '', custom: false, data: footerFields },
about_tab: { title: 'About Page', controls: 'about', active: '', custom: false, data: aboutFields },
contact_tab: { title: 'Contact Page', controls: 'contact', active: '', custom: false, data: contactFields },
social_tab: { title: 'Socials', controls: 'social', active: '', custom: false, data: socialFields },
template_tab: { title: 'Template', controls: 'template', active: '', custom: true, data: templateData },
color_scheme_tab: { title: 'Color Scheme', controls: 'color-scheme', active: '', custom: true, data: {} },
};
},[])
const [fieldsChanged, setFieldsChanged] = useState(false) const [fieldsChanged, setFieldsChanged] = useState(false)
const [activeTab, setActiveTab] = useState(Object.entries(settingsObject)[0][1]?.controls) // const [activeTab, setActiveTab] = useState(Object.entries(settingsConfig)[0][1]?.controls)
const [activeTab, setActiveTab] = useState(null)
const handleChangeTab = (value) => { const handleChangeTab = (value) => {
// if(fieldsChanged){ // if(fieldsChanged){
@@ -97,15 +54,19 @@ const Settings = memo(({productData}) => {
product_id: productData?.product_id product_id: productData?.product_id
} }
return getSettingsData(reqData) return getSettingsData(reqData)
} },
staleTime: 0,
enabled: settingsConfig ? true : false
}) })
const settingsData = data?.data?.settings const settingsData = {data: data?.data?.settings, isFetching, isError, error}
// console.log('data', settingsData) // console.log('data', settingsData)
const sortedSettingsConfig = sortObjectByListOrder(settingsConfig ? settingsConfig : {}) // SORTED SETTINGSCONFIG
return ( return (
<> <>
{isFetching ? {configIsFetching ?
<> <>
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12">
@@ -113,19 +74,19 @@ const Settings = memo(({productData}) => {
</div> </div>
</div> </div>
</> </>
: isError ? : configIsError ?
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12">
<p className='text-danger'>{error.message}</p> <p className='text-danger'>{configError.message}</p>
</div> </div>
</div> </div>
: :
<div className="tab tab-vertical"> <div className="tab tab-vertical">
<ul className="nav nav-tabs" role="tablist"> <ul className="nav nav-tabs" role="tablist">
<> <>
{Object.entries(settingsObject).map(([key, value]) => ( {Object.entries(sortedSettingsConfig).map(([key, value], index) => (
<li key={key} className="nav-item"> <li key={key} className="nav-item">
<a className={`nav-link ${activeTab == value.controls && 'active show'}`} <a className={`nav-link ${(activeTab == value.controls || (index == 0 & !activeTab)) && 'active show'}`}
id={key} id={key}
// data-bs-toggle="pill" // data-bs-toggle="pill"
// data-bs-target={`#${value.controls}`} // data-bs-target={`#${value.controls}`}
@@ -143,12 +104,12 @@ const Settings = memo(({productData}) => {
</ul> </ul>
<div className="tab-content"> <div className="tab-content">
<> <>
{Object.entries(settingsObject).map(([key, value]) => ( {Object.entries(sortedSettingsConfig).map(([key, value], index) => (
<div key={key} className={`tab-pane fade ${activeTab == value.controls && 'active show'}`} <div key={key} className={`tab-pane fade ${(activeTab == value.controls || (index == 0 & !activeTab)) && 'active show'}`}
// id={value.controls} role="tabpanel" // id={value.controls} role="tabpanel"
// aria-labelledby={key} // 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> </div>
))} ))}
</> </>
@@ -1,14 +1,132 @@
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 queryKeys from '../../../services/queryKeys';
import {getProductTemplateData, activateTemplate} from '../../../services/services';
import {Link} from "react-router-dom";
import siteLinks from "../../../links/siteLinks";
const SiteTemplateSelector = memo(({name='Full Name', data}) =>{ const SiteTemplateSelector = memo(({name = 'Full Name', data, productData}) => {
const queryClient = useQueryClient()
const {data: templateData, isFetching, isError, error} = useQuery({
queryKey: queryKeys.productTemplateData,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id
}
return getProductTemplateData(reqData)
},
staleTime: 0
})
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') {
throw new Error(res.data.resultDescription)
}
queryClient.refetchQueries({ // refetches productProvision API call
queryKey: [...queryKeys.settingsData],
})
},
onSettled: () => {
setTimeout(() => {
handleActivateTemplate.reset()
}, 3000)
}
})
const handleSubmit = (tem_uid) => {
const reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id,
template_uid: tem_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 ( return (
<div className="page-account-form"> <div className="page-account-form">
<div className="p-0"> <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"> <div className="row">
<> <>
{Object.entries(data)?.map(([key, value]) => ( {!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">
@@ -22,10 +140,28 @@ const SiteTemplateSelector = memo(({name='Full Name', data}) =>{
</div> </div>
</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>
}
</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
@@ -0,0 +1,128 @@
import React, {memo, useState} from 'react'
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { pageSettings } from "../../../services/services";
import SiteTemplateSelector from './SiteTemplateSelector';
import NoYesBooleanDropdown from './NoYesBooleanDropdown';
import { IoMdArrowDropdown } from 'react-icons/io';
import queryKeys from '../../../services/queryKeys';
const GeneralTab = memo(({name='Full Name', data, isCustom, productData, backendValues, setFieldsChanged}) =>{
const queryClient = useQueryClient()
const [reqStatus, setReqStatus] = useState({error: null, message: ''})
const fieldData = {}
Object.entries(data)?.forEach(([key, value]) => { // LOOP TO POPULATE FIELDDATA PROPERTIES WITH DATA OF EACH TAB
fieldData[value?.name?.toLowerCase().replaceAll(" ", "_")] = ''
})
backendValues.forEach(item => { //LOOPING THROUGH USER ALREADY ADDED DATA FROM BACKEND IF ANY AND UPDATING THE FIELDDATA OBJECT
fieldData[item?.setting_key?.toLowerCase().replaceAll(" ", "_")] = item?.setting_value
})
// console.log('fieldData', fieldData)
const [fields, setFields] = useState(fieldData)
const handleChange = ({target:{name, value}}) => {
setFields(prev => ({...prev, [name]:value}))
setFieldsChanged(true)
}
const submitSettings = useMutation({
mutationFn: (fields) => {
return pageSettings(fields)
},
onSuccess: (res) => {
if(res?.data?.resultCode != '0'){
return setReqStatus({error: true, message: 'Unable to complete, try again later'})
}
setFieldsChanged(false)
setReqStatus({error: false, message: 'Completed successfully'})
},
onError: (err) => {
setReqStatus({error: true, message: 'Unable to complete, try again later'})
},
onSettled: () => {
queryClient.refetchQueries({ // refetches productProvision API call
queryKey: [...queryKeys.settingsData],
})
setTimeout(()=>{
setReqStatus({error: null, message: ''})
},3000)
},
})
const handleSubmit = () => {
const reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id,
settings : {
...fields
}
}
// console.log('formInfo', reqData)
submitSettings.mutate(reqData)
}
if (isCustom === true){
return <SiteTemplateSelector name={name} data={data} isCustom={isCustom} productData={productData} />
}
return (
<div className="page-account-form">
<div className="p-0" style={{ minHeight: '500px'}}>
<form id='tab_form'>
<div className="form-row">
<>
{Object.entries(data)?.map(([key, value]) => {
let fieldName = value.name.toLowerCase().replaceAll(" ", "_")
let fieldValue = fields[value.name.toLowerCase().replaceAll(" ", "_")]
return (
<div key={key} className="form-group col-md-12">
<label htmlFor="name1">{value.name}</label>
{value.controls == 'TEXT' ?
<input name={fieldName} type="text" className="form-control" id={key} value={fieldValue} onChange={handleChange} />
:value.controls == 'TEXTAREA' ?
<textarea name={fieldName} rows={5} style={{resize: 'none'}} type="text" className="form-control" id={key} value={fieldValue} onChange={handleChange} />
: value.controls == 'SELECT_NO_YES' ?
// <NoYesBooleanDropdown name={fieldName} value={fieldValue} onChange={handleChange} />
<div className='position-relative'>
<select onChange={handleChange} name={fieldName} value={fieldValue} className="form-control">
<option value=''>Select</option>
<option value='0'>No</option>
<option value='1'>Yes</option>
</select>
<IoMdArrowDropdown className='position-absolute w-auto' style={{top: '50%', right: '2px', transform: 'translateY(-50%)'}} />
</div>
:
null
}
</div>
)
}
)}
</>
{reqStatus.message &&
<>
<div className="col-12">
<p className={reqStatus.error ? 'text-danger' : 'text-success'}>{reqStatus.message}</p>
</div>
</>
}
<div className="form-group col-md-12" style={{textAlign:'right'}}>
<button onClick={handleSubmit} type="button" className="btn btn-primary" disabled={submitSettings.isPending}>{submitSettings.isPending ? 'Loading...' : 'Update'}</button>
</div>
</div>
</form>
</div>
</div>
)
}
)
export default GeneralTab
@@ -0,0 +1,179 @@
import React, { memo, useMemo, useState } from 'react'
import GeneralTab from './GeneralTab'
import { getSettingsData, getMyProductConfig } from '../../../services/services';
import queryKeys from '../../../services/queryKeys';
import { useSelector } from 'react-redux';
import { useQuery } from '@tanstack/react-query';
const Settings = memo(({productData}) => {
const { userDetails: { uid }} = useSelector((state) => state?.userDetails); // GETS USER UID
const {data:configData, isFetching:configIsFetching, configIsError, error:configError} = useQuery({
queryKey: queryKeys.myProductConfig,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id
}
return getMyProductConfig(reqData)
}
})
const settingsConfig = configData?.data?.settings_items
// console.log('CONFIG DATA...', settingsConfig)
const dataFields ={
site_title: { name: 'Title', controls: 'TEXT', active: true },
site_description: { name: 'Description', controls: 'TEXTAREA', active: true },
site_logo_text: { name: 'Logo Text', controls: 'TEXT', active: true },
site_contact_email: { name: 'Email', controls: 'TEXT', active: true },
site_contact_phone: { name: 'Phone', controls: 'TEXT', active: true },
}
const socialFields ={
facebook: { name: 'Facebook', controls: 'TEXT', active: true },
twitter: { name: 'Twitter', controls: 'TEXT', active: true },
youtube: { name: 'Youtube', controls: 'TEXT', active: true },
}
const homeFields ={
banner_text: { name: 'Banner Text', controls: 'TEXT', active: true },
banner_description: { name: 'Banner Description', controls: 'TEXTAREA', active: true },
}
const footerFields ={
footer_description: { name: 'Footer Description', controls: 'TEXTAREA', active: true },
boolean_footer_show_email: { name: 'Show email in footer', controls: 'SELECT_NO_YES', active: true },
boolean_footer_show_made_by: { name: 'Show made by in footer', controls: 'SELECT_NO_YES', active: true },
boolean_footer_show_socials: { name: 'Show social in footer', controls: 'SELECT_NO_YES', active: true },
}
const aboutFields ={
about_title: { name: 'About Title', controls: 'TEXT', active: true },
about_description: { name: 'About Details', controls: 'TEXTAREA', active: true },
about_extra_1: { name: 'Extra About us', controls: 'TEXTAREA', active: true },
about_extra_2: { name: 'More About us', controls: 'TEXTAREA', active: true },
}
const templateData = {
template_16 : { title: 'Template Name-16', template_id: '02af24fd-2b1a-46ed-af21-87018e726408', banner: 'file-icon/svg.png', active: '' },
template_22 : { title: 'Template Name-22', template_id: '8b296894-42e4-4f2e-abd1-7c2a38d6e07b', banner: 'file-icon/svg.png', active: '' },
template_47 : { title: 'Template Name-47', template_id: 'ef2ffa1c-9272-42cd-9d33-0e614047b4f8', banner: 'file-icon/svg.png', active: '' },
template_25 : { title: 'Template Name-25', template_id: 'b3a7ba31-dc47-4a40-a5cc-fd1ff27d6b78', banner: 'file-icon/svg.png', active: '' },
template_49 : { title: 'Template Name-49', template_id: '60959c69-6672-4f69-a006-eeb7d210e605', banner: 'file-icon/svg.png', active: '' },
template_27 : { title: 'Template Name-27', template_id: 'e4acb98a-c584-45f2-bece-af677dcf0a1f', banner: 'file-icon/svg.png', active: '' },
template_51 : { title: 'Template Name-51', template_id: '975ee42e-3169-4978-92d7-d28e7e2ac014', banner: 'file-icon/svg.png', active: '' },
template_9 : { title: 'Template Name-9', template_id: 'fc8f0738-6500-4775-9895-2047cd275302', banner: 'file-icon/svg.png', active: '' },
}
const contactFields ={
contact_title : { name: 'Contact Title', controls: 'TEXT', active: true },
contact_introduction: { name: 'Extra Introduction', controls: 'TEXTAREA', active: true },
}
const settingsObject = useMemo(()=>{
return {
settings: { title: 'Settings', controls: 'settings', active: 'active show' , custom: false, data: dataFields},
home_tab: { title: 'Home Page', controls: 'home', active: '', custom: false, data: homeFields},
footer_tab: { title: 'Footer', controls: 'footer', active: '', custom: false, data: footerFields },
about_tab: { title: 'About Page', controls: 'about', active: '', custom: false, data: aboutFields },
contact_tab: { title: 'Contact Page', controls: 'contact', active: '', custom: false, data: contactFields },
social_tab: { title: 'Socials', controls: 'social', active: '', custom: false, data: socialFields },
template_tab: { title: 'Template', controls: 'template', active: '', custom: true, data: templateData },
color_scheme_tab: { title: 'Color Scheme', controls: 'color-scheme', active: '', custom: true, data: {} },
};
},[])
const [fieldsChanged, setFieldsChanged] = useState(false)
const [activeTab, setActiveTab] = useState(Object.entries(settingsObject)[0][1]?.controls)
const handleChangeTab = (value) => {
// if(fieldsChanged){
// const proceed = confirm('Continue without saving changes')
// if(proceed){
// setActiveTab(value)
// setFieldsChanged(false)
// }
// }else{
// setActiveTab(value)
// }
setActiveTab(value)
}
const {data, isFetching, isError, error} = useQuery({
queryKey: queryKeys.settingsData,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id
}
return getSettingsData(reqData)
}
})
const settingsData = data?.data?.settings
// console.log('data', settingsData)
return (
<>
{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="tab tab-vertical">
<ul className="nav nav-tabs" role="tablist">
<>
{Object.entries(settingsObject).map(([key, value]) => (
<li key={key} className="nav-item">
<a className={`nav-link ${activeTab == value.controls && 'active show'}`}
id={key}
// data-bs-toggle="pill"
// data-bs-target={`#${value.controls}`}
type="button"
// role="tab"
// aria-controls={value.controls}
// aria-selected="true"
onClick={()=>handleChangeTab(value.controls)}
>
{value.title}
</a>
</li>
))}
</>
</ul>
<div className="tab-content">
<>
{Object.entries(settingsObject).map(([key, value]) => (
<div key={key} className={`tab-pane fade ${activeTab == value.controls && 'active show'}`}
// id={value.controls} role="tabpanel"
// aria-labelledby={key}
>
<GeneralTab name={value.title} data={value.data} isCustom={value.custom} productData={productData} backendValues={settingsData} setFieldsChanged={setFieldsChanged} />
</div>
))}
</>
</div>
</div>
}
</>
)
}
)
export default Settings
@@ -1,65 +1,78 @@
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";
import {IoMdArrowDropdown} from "react-icons/io"; import {IoMdArrowDropdown} from "react-icons/io";
import {completeProfile, getCommonPractice} from '../../services/services'; import {completeProfile, getCommonPractice} from '../../services/services';
import siteLinks from "../../links/siteLinks"; import siteLinks from "../../links/siteLinks";
import { useNavigate } from "react-router-dom"; import {useLocation, useNavigate} from "react-router-dom";
import {updateUserDetails} from "../../store/UserDetails";
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() {
const dispatch = useDispatch()
const navigate = useNavigate() const navigate = useNavigate()
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: '',
specialization: '', specialization: '',
introduction: '', introduction: '',
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)
}, },
onError: () => { onError: () => {
setTimeout(()=>{mutation.reset()}, 4000) setTimeout(() => {
mutation.reset()
}, 4000)
}, },
onSuccess: (res) => { onSuccess: (res) => {
if (res.data.resultCode != '0') {
throw({message: res?.data?.resultDescription})
}
dispatch(updateUserDetails({profile_completed: res?.data?.profile_completed}));
setTimeout(() => { setTimeout(() => {
navigate(siteLinks.home) navigate(redirectLink)
}, 2000) }, 2000)
// console.log('res', res) // console.log('res', res)
} }
@@ -81,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)
} }
@@ -104,7 +113,7 @@ export default function ProfileCompleteCom(){
return <> return <>
<BreadcrumbComBS title='Update Profile' paths={['Dashboard', 'Profile']} /> <BreadcrumbComBS title='Tell us more about your practice.' paths={['Dashboard', 'Profile']}/>
{commonPractices?.isFetching ? {commonPractices?.isFetching ?
<> <>
@@ -123,7 +132,7 @@ export default function ProfileCompleteCom(){
: :
<div className="row pt-1"> <div className="row pt-1">
<div className="col-md-6 m-b-30"> <div className="col-md-6 m-b-30">
<div className="card card-statistics h-100 mb-0"> <div className="card card-statistics h-100 mb-0" style={{borderRadius: '10px'}}>
{/* <div className="card-header d-flex align-items-center justify-content-between"> {/* <div className="card-header d-flex align-items-center justify-content-between">
<div className="card-heading"> <div className="card-heading">
<h4 className="card-title">My Product URLs</h4> <h4 className="card-title">My Product URLs</h4>
@@ -133,10 +142,19 @@ 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
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleCompleteProfile}
enableReinitialize={true}
>
{(props) => {
return (
<Form className='mt-2'>
<> <>
<div className=""> <div className="">
<div className="form-group position-relative"> <div className="form-group position-relative">
<label className={`text-black fw-bold control-label`}>Practice :</label> <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"> <div className="position-relative">
{/* <select onChange={props.handleChange} name='practice' value={props.values.practice} className="form-control"> {/* <select onChange={props.handleChange} name='practice' value={props.values.practice} className="form-control">
<option value=''>Select</option> <option value=''>Select</option>
@@ -144,39 +162,108 @@ export default function ProfileCompleteCom(){
<option key={index} value={practice.practice}>{practice.practice}</option> <option key={index} value={practice.practice}>{practice.practice}</option>
))} ))}
</select> */} </select> */}
<select onChange={handlePracticeChange} name='practice' value={initialValues.practice} className="form-control"> <select
onChange={(e) => {props.handleChange(e); props.setFieldValue('specialization', ''); handleUpdateSpecialties(e)}}
name='practice'
value={props.values.practice} className="form-control">
<option value=''>Select</option> <option value=''>Select</option>
{practices.map((practice, index) => ( {practices.map((practice, index) => (
<option key={index} value={practice.practice}>{practice.practice}</option> <option key={index}
value={practice.practice}>{practice.practice}</option>
))} ))}
</select> </select>
<IoMdArrowDropdown className='position-absolute w-auto' style={{top: '50%', right: '2px', transform: 'translateY(-50%)'}} /> <IoMdArrowDropdown className='position-absolute w-auto' style={{
top: '50%',
right: '2px',
transform: 'translateY(-50%)'
}}/>
</div> </div>
</div> </div>
</div> </div>
<div className=""> <div className="">
<div className="form-group"> <div className="form-group">
<label className={`text-black fw-bold control-label`}>Specialization :</label> <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"> <div className="position-relative">
<select onChange={handlePracticeChange} name='specialization' value={initialValues.specialization} className="form-control"> <select onChange={props.handleChange} name='specialization'
value={props.values.specialization}
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={{top: '50%', right: '2px', transform: 'translateY(-50%)'}} /> <IoMdArrowDropdown className='position-absolute w-auto' style={{
top: '50%',
right: '2px',
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`}>General Information :</label> <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={10} style={{resize: 'none'}} className="form-control" value={initialValues.introduction} onChange={handlePracticeChange} /> <textarea name='introduction' rows={5} style={{resize: 'none'}}
className="form-control" value={props.values.introduction}
onChange={props.handleChange}/>
</div> </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>
<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>
{(mutation.isError || mutation.isSuccess) && {(mutation.isError || mutation.isSuccess) &&
<> <>
@@ -187,9 +274,14 @@ export default function ProfileCompleteCom(){
} }
<div className="mt-auto text-end"> <div className="mt-auto text-end">
<button type='button' onClick={handleCompleteProfile} className="btn btn-primary text-uppercase">{mutation.isPending ? 'loading...' : 'Continue'}</button> <button type='submit'
className="btn btn-primary text-uppercase">{mutation.isPending ? 'loading...' : 'Continue'}</button>
</div> </div>
</> </>
</Form>
);
}}
</Formik>
{/* </div> */} {/* </div> */}
</div> </div>
</div> </div>
@@ -197,7 +289,7 @@ export default function ProfileCompleteCom(){
</div> </div>
<div className="col-md-6 m-b-30"> <div className="col-md-6 m-b-30">
<div className="text-center img-block left-column wow fadeInRight"> <div className="text-center img-block left-column wow fadeInRight">
<img className="img-fluid" src={getImage('img-07.png')} alt="content-image" /> <img className="img-fluid" src={getImage('tell-us-more.png')} alt="content-image"/>
</div> </div>
</div> </div>
</div> </div>
+139 -80
View File
@@ -1,8 +1,67 @@
import React from "react"; import React, { useEffect, useMemo, useState } from "react";
import { Form, Formik } from "formik";
import * as Yup from "yup";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
import getImage from "../../utils/getImage";
import queryKeys from "../../services/queryKeys";
import { useQuery } from "@tanstack/react-query";
import { profileDetails } from "../../services/services";
const profileValidationSchema = Yup.object().shape({
// firstname: Yup.string().required("firstname is required"),
// lastname: Yup.string().required("lastname is required"),
// email: Yup.string().required("email is required"),
// account_name: Yup.string().required("account name is required"),
// phone: Yup.string().required("phone is required"),
// full_address: Yup.string().required("full address is required"),
})
const linksValidationSchema = Yup.object().shape({
// facebook_url: Yup.string().required("facebook is required"),
// twitter_url: Yup.string().required("twitter is required"),
// blogger_url: Yup.string().required("blog is required"),
// google_url: Yup.string().required("google is required"),
// linked_url: Yup.string().required("linkedin is required"),
// website_url: Yup.string().required("website is required"),
})
export default function Settings() { export default function Settings() {
const avtarImage = "avtar/merms-user.png";
const [intialData, setInitialData] = useState({
external_links: {},
personal_data: {},
})
const {data:profileInfo, isFetching, isError, error} = useQuery({
queryKey: queryKeys.profile_data,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid') // USER UID
}
return profileDetails(reqData)
}
})
// const profileData = profileInfo?.data // profile data
useMemo(()=>{
const data = profileInfo?.data
setInitialData({external_links: data?.external_links, personal_data: data?.personal_data})
},[profileInfo])
// console.log('INI', intialData)
const updateProfile = (values, helpers) => {
console.log('Values', values)
}
const updateLinks = (values, helpers) => {
console.log('Values', values)
}
return ( return (
<> <>
@@ -11,7 +70,21 @@ export default function Settings(){
{/* <div className="vh-100 col-12 flex align-items-center">Coming Soon</div>*/} {/* <div className="vh-100 col-12 flex align-items-center">Coming Soon</div>*/}
{/*</div>*/} {/*</div>*/}
{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 account-contant"> <div className="row account-contant">
<div className="col-12"> <div className="col-12">
<div className="card card-statistics"> <div className="card card-statistics">
@@ -22,39 +95,18 @@ export default function Settings(){
<div className="profile-img text-center rounded-circle"> <div className="profile-img text-center rounded-circle">
<div className="pt-5"> <div className="pt-5">
<div className="bg-img m-auto"> <div className="bg-img m-auto">
<img src="assets/img/avtar/01.jpg" className="img-fluid" {/*<img src="assets/img/avtar/01.jpg" className="img-fluid"*/}
alt="users-avatar" /> {/* alt="users-avatar" />*/}
<img src={getImage(avtarImage)}
className="img-fluid" alt="user"/>
</div> </div>
<div className="profile pt-4"> <div className="profile pt-4">
<h4 className="mb-1">Alice Williams</h4> <h4 className="mb-1">{intialData?.personal_data?.lastname} {intialData?.personal_data?.firstname}</h4>
<p>Enthusiast</p> <div style={{padding: '10px'}}>
<hr/>
</div> </div>
</div> </div>
</div> </div>
<div className="py-5 profile-counter">
<ul className="nav justify-content-center text-center">
<li className="nav-item border-right px-3">
<div>
<h4 className="mb-0">90</h4>
<p>Post</p>
</div>
</li>
<li className="nav-item border-right px-3">
<div>
<h4 className="mb-0">1.5K</h4>
<p>Messages</p>
</div>
</li>
<li className="nav-item px-3">
<div>
<h4 className="mb-0">4.4K</h4>
<p>Members</p>
</div>
</li>
</ul>
</div> </div>
<div className="profile-btn text-center"> <div className="profile-btn text-center">
@@ -74,49 +126,50 @@ export default function Settings(){
<h5 className="mb-0 py-2">Edit Your Personal Settings</h5> <h5 className="mb-0 py-2">Edit Your Personal Settings</h5>
</div> </div>
<div className="p-4"> <div className="p-4">
<form> <Formik
initialValues={intialData?.personal_data}
validationSchema={profileValidationSchema}
onSubmit={updateProfile}
>
{(props) => {
return (
<Form className=''>
<div className="form-row"> <div className="form-row">
<div className="form-group col-md-12"> <div className="form-group col-md-12">
<label htmlFor="name1">First Name</label> <label htmlFor="name1">First Name {(props.errors.firstname && props.touched.firstname) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" id="name1" <input type="text" className="form-control" name="firstname" value={props.values?.firstname} onChange={props.handleChange} />
value="Alice" />
</div> </div>
<div className="form-group col-md-12"> <div className="form-group col-md-12">
<label htmlFor="name1">Last Name</label> <label htmlFor="name1">Last Name {(props.errors.lastname && props.touched.lastname) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" id="name1" <input type="text" className="form-control" name="lastname" value={props.values?.lastname} onChange={props.handleChange} />
value="Williams" />
</div> </div>
<div className="form-group col-md-12"> <div className="form-group col-md-12">
<label htmlFor="name1">Account Name</label> <label htmlFor="name1">Account Name {(props.errors.account_name && props.touched.account_name) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" id="name1" <input type="text" className="form-control" name="account_name" value={props.values?.account_name} onChange={props.handleChange} />
value="This is the best hospital name" />
</div> </div>
{/*<div className="form-group col-md-12">*/} {/*<div className="form-group col-md-12">*/}
{/* <label htmlFor="title1">Email</label>*/} {/* <label htmlFor="title1">Email</label>*/}
{/* <input type="text" className="form-control" id="title1"*/} {/* <input type="text" className="form-control" name="title1"*/}
{/* value="email@email.com" />*/} {/* value="email@email.com" />*/}
{/*</div>*/} {/*</div>*/}
<div className="form-group col-md-12"> <div className="form-group col-md-12">
<label htmlFor="phone1">Phone Number</label> <label htmlFor="phone1">Phone Number {(props.errors.phone && props.touched.phone) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" id="phone1" <input type="text" className="form-control" name="phone" value={props.values?.phone} onChange={props.handleChange} />
value="(01) 97 563 15613" />
</div> </div>
<div className="form-group col-md-12"> <div className="form-group col-md-12">
<label htmlFor="email1">Email</label> <label htmlFor="email1">Email {(props.errors.email && props.touched.email) && <span className="text-danger">*</span>}</label>
<input type="email" className="form-control" id="email1" <input type="text" className="form-control" name="email" value={props.values?.email} onChange={props.handleChange} />
value="alicewilliams@gmail.com" />
</div> </div>
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="add1">Address</label> <label htmlFor="add1">Address {(props.errors.full_address && props.touched.full_address) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" id="add1" <input type="text" className="form-control" name="full_address" value={props.values?.full_address} onChange={props.handleChange} />
value="17504 Carlton Cuevas Rd, Gulfport, MS, 39503" />
</div>
<div className="form-group">
<label htmlFor="add2">Address 2</label>
<input type="text" className="form-control" id="add2"
value="1234 North Avenue Luke Lane, South Bend, IN 360001" />
</div> </div>
{/*<div className="form-group">*/}
{/* <label htmlFor="add2">Address 2</label>*/}
{/* <input type="text" className="form-control" id="add2"*/}
{/* value="1234 North Avenue Luke Lane, South Bend, IN 360001"/>*/}
{/*</div>*/}
{/*<div className="form-row">*/} {/*<div className="form-row">*/}
{/* <div className="form-group col-md-4">*/} {/* <div className="form-group col-md-4">*/}
@@ -158,8 +211,10 @@ export default function Settings(){
<button type="submit" className="btn btn-primary">Update Profile <button type="submit" className="btn btn-primary">Update Profile
</button> </button>
</div> </div>
</Form>
</form> );
}}
</Formik>
</div> </div>
</div> </div>
</div> </div>
@@ -169,46 +224,50 @@ export default function Settings(){
<h5 className="mb-0 py-2">Your External Link</h5> <h5 className="mb-0 py-2">Your External Link</h5>
</div> </div>
<div className="p-4"> <div className="p-4">
<form> <Formik
initialValues={intialData?.external_links}
validationSchema={linksValidationSchema}
onSubmit={updateLinks}
>
{(props) => {
return (
<Form className=''>
<div className="form-group"> <div className="form-group">
<label htmlFor="fb">Facebook URL:</label> <label htmlFor="fb">Facebook URL: {(props.errors.facebook_url && props.touched.facebook_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" id="fb" <input type="text" className="form-control" name="facebook_url" value={props.values?.facebook_url} onChange={props.handleChange} />
value="https://www.facebook.com/" />
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="tr">Twitter URL:</label> <label htmlFor="tr">Twitter URL: {(props.errors.twitter_url && props.touched.twitter_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" id="tr" <input type="text" className="form-control" name="twitter_url" value={props.values?.twitter_url} onChange={props.handleChange} />
value="https://twitter.com/" />
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="br">Blogger URL:</label> <label htmlFor="br">Blogger URL: {(props.errors.blogger_url && props.touched.blogger_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" id="br" <input type="text" className="form-control" name="blogger_url" value={props.values?.blogger_url} onChange={props.handleChange} />
value="https://www.blogger.com" />
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="go">Google+ URL:</label> <label htmlFor="go">Google+ URL: {(props.errors.google_url && props.touched.google_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" id="go" <input type="text" className="form-control" name="google_url" value={props.values?.google_url} onChange={props.handleChange} />
value="https://plus.google.com/discover" />
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="li">LinkedIn URL:</label> <label htmlFor="li">LinkedIn URL: {(props.errors.linked_url && props.touched.linked_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" id="li" <input type="text" className="form-control" name="linked_url" value={props.values?.linked_url} onChange={props.handleChange} />
value="https://in.linkedin.com/" />
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="we">Website URL:</label> <label htmlFor="we">Website URL: {(props.errors.website_url && props.touched.website_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" id="we" <input type="text" className="form-control" name="website_url" value={props.values?.website_url} onChange={props.handleChange} />
value="https://yourwebsite.com" />
</div> </div>
<div style={{textAlign: "right"}}> <div style={{textAlign: "right"}}>
<button type="submit" className="btn btn-primary">Update Links</button> <button type="submit" className="btn btn-primary">Update Links
</button>
</div> </div>
</Form>
</form> );
}}
</Formik>
</div> </div>
</div> </div>
</div> </div>
@@ -217,7 +276,7 @@ export default function Settings(){
</div> </div>
</div> </div>
</div> </div>
}
</> </>
) )
} }
+82
View File
@@ -0,0 +1,82 @@
import React from "react";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
import getImage from "../../utils/getImage";
export default function Start() {
const bgImg = getImage("side-banner.jpg");
return (
<>
<BreadcrumbComBS title='Get Started...' paths={['Dashboard', 'Start']}/>
<div className="row">
<div className="col-xl-3 col-md-6">
<div className="card card-statistics text-center py-3"
style={{minHeight: '550px', borderRadius: '10px', backgroundImage: `url(${bgImg})`}}>
</div>
</div>
<div className="col-xl-3 col-md-6">
<div className="card card-statistics text-center py-3">
<div className="card-body pricing-content">
<div className="pricing-content-card">
<h5>Start with</h5>
<h2 className="text-primary pt-3"><a href="/product/A000001">Personal Website</a></h2>
<ul className="py-2">
<li>post jobs</li>
<li>advanced instructors search</li>
<li>invite candidates</li>
<li>post events</li>
<li>cancel any time</li>
</ul>
<div className="pt-2"><a href="/product/A000001"
className="btn btn-primary btn-round btn-sm">Start</a></div>
</div>
</div>
</div>
</div>
<div className="col-xl-3 col-md-6">
<div className="card card-statistics text-center py-3">
<div className="card-body pricing-content">
<div className="pricing-content-card">
<h5>Start with</h5>
<h2 className="text-primary pt-3"><a href="/product/A000002">Business Website</a></h2>
<ul className="py-2">
<li>post jobs</li>
<li>advanced instructors search</li>
<li>invite candidates</li>
<li>post events</li>
<li>cancel any time</li>
</ul>
<div className="pt-2"><a href="/product/A000001"
className="btn btn-primary btn-round btn-sm">Start</a></div>
</div>
</div>
</div>
</div>
<div className="col-xl-3 col-md-6">
{/*<div className="card card-statistics text-center py-3">*/}
{/*<div className="card-body pricing-content">*/}
{/*<div className="pricing-content-card">*/}
{/* <h5>small</h5>*/}
{/* <h2 className="text-primary pt-3">$80</h2>*/}
{/* <p className="text-primary pb-3">/ Monthly</p>*/}
{/* <ul className="py-2">*/}
{/* <li>post jobs</li>*/}
{/* <li>advanced instructors search</li>*/}
{/* <li>invite candidates</li>*/}
{/* <li>post events</li>*/}
{/* <li>cancel any time</li>*/}
{/* </ul>*/}
{/* <div className="pt-2"><a href="javascript:void(0)" className="btn btn-inverse-secondary btn-round btn-sm">go premium</a></div>*/}
{/*</div>*/}
{/*</div>*/}
{/*</div>*/}
</div>
</div>
</>
)
}
@@ -0,0 +1,57 @@
import React from 'react';
import { loadStripe } from '@stripe/stripe-js';
import { Elements, useStripe, useElements, CardElement } from '@stripe/react-stripe-js';
import {MyProductData, StripeSubscriptionCreate} from '../../services/services';
import {useQuery} from "@tanstack/react-query";
import queryKeys from "../../services/queryKeys";
import { useNavigate } from 'react-router-dom';
//const stripePromise = loadStripe('your_stripe_publishable_key');
const stripePromise = loadStripe('pk_test_51RqL5WLjZLojw6IZmEpwFidNZSl9lLlVUHNvuFZNEz1eTR9XXepnyyVhfvXe9cp4eMnqkDPpoe9wxLLRSV0dxRee00UfhayUOT');
const CheckoutForm = ({ priceId, customerId ,option_name }) => {
const stripe = useStripe();
const elements = useElements();
const navigate = useNavigate();
const handleSubmit = async (event) => {
event.preventDefault();
if (!stripe || !elements) {
return;
}
let reqData = {
priceId : priceId,
customerId: customerId,
option_name: option_name,
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid') // USER UID
}
StripeSubscriptionCreate(reqData).then( (res)=>{
console.log(res);
console.log(res.data.stripe_session);
//navigate(res.data.stripe_session)
window.location.replace(res.data.stripe_session);
});
};
return (
<form onSubmit={handleSubmit}>
<CardElement />
<button type="submit" disabled={!stripe}>
Subscribe
</button>
</form>
);
};
const StripeSubscriptionButton = ({ priceId, customerId ,option_name}) => {
return (
<Elements stripe={stripePromise}>
<CheckoutForm priceId={priceId} customerId={customerId} option_name={option_name} />
</Elements>
);
};
export default StripeSubscriptionButton;
@@ -0,0 +1,35 @@
import React from 'react'
export default function SubcribePaymentOptions({activePaymentType, setActivePaymentType}){
const handleSwitch = ({target:{name, value}}) => {
console.log('SWITCH', name, value)
setActivePaymentType(value)
}
return <>
<div className="card-header">
<div className="card-heading">
<h4 className="card-title">Select Payment Option</h4>
</div>
</div>
<div className="card-body">
<div className="form-check form-check-inline">
<input onChange={handleSwitch} className="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1"
value="previous" checked={activePaymentType == 'previous'} />
<label className="form-check-label" htmlFor="inlineRadio1">Previous Cards</label>
</div>
<div className="form-check form-check-inline">
<input onChange={handleSwitch} className="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2"
value="new" checked={activePaymentType == 'new'} />
<label className="form-check-label" htmlFor="inlineRadio2">Add New card</label>
</div>
</div>
{/*<div className="card card-statistics"> </div>*/}
</>
}
+72
View File
@@ -0,0 +1,72 @@
import React, {useState} from 'react'
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
import {Link, useLocation} from 'react-router-dom'
import SubscribeNewCard from "./SubscribeNewCard";
import SubscribePreviousCard from "./SubscribePreviousCard";
import SubcribePaymentOptions from "./SubcribePaymentOptions";
import StripeSubscriptionButton from "./StripeSubscriptionButton";
export default function Subscribe() {
const {state: {selectedSubscription,customerId}} = useLocation()
console.log('selectedSubscription', selectedSubscription)
console.log('selectedSubscription.option_name',selectedSubscription.option_name)
console.log('customerId', customerId)
let [activePaymentType, setActivePaymentType] = useState('previous')
return (
<>
<BreadcrumbComBS title='Activate or Update your Subscription' paths={['Dashboard', 'subscribe']}/>
<div className="row">
<div className="col-12 col-lg-6 col-xl-6">
<div className="card card-statistics text-center py-3">
<div className="card-body pricing-content">
<div className="pricing-content-card">
<h5>Current Subscription(s)</h5>
{/*<h2 className="text-primary pt-3">{currentSubscription?.display_name}</h2>*/}
<SubcribePaymentOptions activePaymentType={activePaymentType}
setActivePaymentType={setActivePaymentType}/>
{activePaymentType == 'new' ?
<SubscribeNewCard/>
:
<SubscribePreviousCard/>
}
<>
<StripeSubscriptionButton priceId={selectedSubscription.stripe_price_id} customerId={customerId} option_name={selectedSubscription.option_name} />
</>
</div>
</div>
</div>
</div>
<>
<div key="basic" className="col-12 col-lg-6 col-xl-6">
<div className="card card-statistics text-center py-3">
<div className="card-body pricing-content">
<div className="pricing-content-card">
<h5>{selectedSubscription.display_name}</h5>
<h2 className="text-primary pt-3">${selectedSubscription.monthly}</h2>
<p className="text-primary pb-3">/ Monthly</p>
<ol className="py-2"
style={{fontSize: '16px', fontWeight: 'bold', textAlign: 'left'}}>
{selectedSubscription?.items?.map(item => (
<li key={item.description}>{item.description}</li>
))}
</ol>
{/*<div className="pt-2">*/}
{/* <button className="btn btn-inverse-secondary btn-round btn-sm">Go {subscriptionSelection.display_name}</button>*/}
{/*</div>*/}
</div>
</div>
</div>
</div>
</>
</div>
</>
)
}
@@ -0,0 +1,28 @@
import React from 'react'
export default function SubscribeNewCard(){
return <>
<div className="row">
<div className="col-md-12 col-12 selects-contant">
<div className="card card-statistics">
<div className="card-header">
<div className="card-heading" style={{textAlign: 'left'}}>
<h4 className="card-title">Pay with New Card</h4>
</div>
</div>
<div className="card-body">
<div className="form-group mb-0">
</div>
</div>
</div>
</div>
</div>
</>
}
@@ -0,0 +1,36 @@
import React from 'react'
import { IoMdArrowDropdown } from 'react-icons/io'
export default function SubscribePreviousCard() {
return <>
{/*<div className="row select-wrapper">*/}
{/* <div className="col-md-12 col-12 selects-contant">*/}
<div className="row">
<div className="col-md-12 col-12 selects-contant">
<div className="card card-statistics">
<div className="card-header">
<div className="card-heading" style={{textAlign: 'left'}}>
<h4 className="card-title">Pay with Previous Card</h4>
</div>
</div>
<div className="card-body">
<div className="position-relative form-group mb-0">
<select className="js-basic-single form-control" name="state">
<option value="AK">Visa xxxx xxxx xxxx 1234</option>
<option value="HI">Mastercard xxxx xxxx xxxx 1234</option>
</select>
<IoMdArrowDropdown className='position-absolute w-auto' style={{top: '50%', right: '2px', transform: 'translateY(-50%)'}} />
</div>
</div>
</div>
</div>
{/* </div>*/}
{/*</div>*/}
</div>
</>
}
+34 -11
View File
@@ -4,9 +4,12 @@ import getImage from "../../utils/getImage";
import {getSubscriptions} from '../../services/services'; import {getSubscriptions} from '../../services/services';
import {useQuery} from '@tanstack/react-query'; import {useQuery} from '@tanstack/react-query';
import queryKeys from '../../services/queryKeys'; import queryKeys from '../../services/queryKeys';
import siteLinks from "../../links/siteLinks";
import {Link, useNavigate} from 'react-router-dom'
import getDateTimeFromDateString from '../../helpers/getDateTimeFromDateString';
export default function Subscription() { export default function Subscription() {
const navigate = useNavigate()
const pricingFields = { const pricingFields = {
starter: {name: 'Starter', price: 5.99, active: true}, starter: {name: 'Starter', price: 5.99, active: true},
basic: {name: 'Basic', price: 12.99, active: true}, basic: {name: 'Basic', price: 12.99, active: true},
@@ -26,7 +29,9 @@ export default function Subscription() {
const currentSubscription = data?.data?.current_product const currentSubscription = data?.data?.current_product
const otherSubscriptions = data?.data?.options const otherSubscriptions = data?.data?.options
console.log('urlData', data?.data) const stripe_customer_id = data?.data.stripe_customer_id
// console.log('urlData', data?.data)
return ( return (
<> <>
@@ -45,12 +50,17 @@ export default function Subscription() {
: :
<div className="row"> <div className="row">
<div className="col-12 col-lg-6 col-xl-3"> <div className="col-12 col-lg-6 col-xl-3">
<div className="card card-statistics text-center py-3"> <div className="card card-statistics text-center py-3" style={{backgroundColor: '#e6f5f4'}}>
<div className="card-body pricing-content"> <div className="card-body pricing-content">
<div className="pricing-content-card"> <div className="pricing-content-card" style={{minHeight: '350px'}}>
<h5>Current Subscription(s)</h5> <h5>Your Subscription(s)</h5>
<h2 className="text-primary pt-3">{currentSubscription?.display_name}</h2> <h2 className="text-primary pt-3">{currentSubscription?.display_name}</h2>
</div> </div>
<div className="pt-2" style={{textAlign: 'left'}}>
<div style={{fontSize: '12px', fontWeight: 'bolder' , color: "#3E3699" }}>
Next Payment: {getDateTimeFromDateString(currentSubscription?.next_payment)}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -61,19 +71,32 @@ export default function Subscription() {
<div key={key} className="col-12 col-lg-6 col-xl-3"> <div key={key} className="col-12 col-lg-6 col-xl-3">
<div className="card card-statistics text-center py-3"> <div className="card card-statistics text-center py-3">
<div className="card-body pricing-content"> <div className="card-body pricing-content">
<div className="pricing-content-card"> <div className="pricing-content-card" style={{minHeight: '350px'}}>
<h5>{value.display_name}</h5> <h5>{value.display_name}</h5>
<h2 className="text-primary pt-3">${value.monthly}</h2> <h2 className="text-primary pt-3">${value.monthly}</h2>
<p className="text-primary pb-3">/ Monthly</p> <p className="text-primary pb-3">/ Monthly</p>
<ul className="py-2"> <ol className="py-2"
style={{fontSize: '16px', fontWeight: 'bold', textAlign: 'left'}}>
{value?.items?.map(item => ( {value?.items?.map(item => (
<li key={item}>{item}</li> <li key={item.description}>{item.description}</li>
))} ))}
</ul> </ol>
<div className="pt-2">
<button className="btn btn-inverse-secondary btn-round btn-sm">Go {value.display_name}</button>
</div> </div>
</div> </div>
<div className="pt-2" style={{fontWeight: 'bolder'}}>
{
(currentSubscription?.display_name == value.option_name) ? 'Current Subscription' :
<button onClick={() => {
navigate(siteLinks.subscribe, {
state: {
selectedSubscription: value,
customerId: stripe_customer_id
}
})
}}
className="btn btn-inverse-secondary btn-round btn-sm">Go {value.display_name}</button>
}
</div> </div>
</div> </div>
</div> </div>
+44
View File
@@ -0,0 +1,44 @@
import React from "react";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
import TrafficChart from "./TrafficChart";
export default function Traffic() {
return (
<>
<BreadcrumbComBS title='Traffic' paths={['Dashboard', 'Traffic']}/>
<div className="row">
<div className="col-lg-4">
<div className="card card-statistics" style={{minHeight: '350px', borderRadius: '10px', backgroundColor: 'aliceblue'}}>
<div className="card-header">
<div className="card-heading">
<h4 className="card-title">Site Traffic Monitoring</h4>
</div>
</div>
<div className="card-body">
</div>
</div>
</div>
<div className="col-lg-8">
<div className="card card-statistics">
<div className="card-header">
<div className="card-heading">
<h4 className="card-title">Plot</h4>
</div>
</div>
<div className="card-body">
<div className="apexchart-wrapper">
<TrafficChart />
</div>
</div>
</div>
</div>
</div>
</>
)
}
+108
View File
@@ -0,0 +1,108 @@
import { useState } from "react";
import ReactApexChart from "react-apexcharts";
const TrafficChart = () => {
const [state, setState] = useState({
series: [
{
// name: "High - 2013",
name: 'Professional Website',
data: [28, 29, 33, 36, 32, 32, 33, 33, 36, 32, 32, 33]
},
{
// name: "Low - 2013",
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: {
chart: {
height: 350,
type: 'line',
dropShadow: {
enabled: true,
color: '#000',
top: 18,
left: 7,
blur: 10,
opacity: 0.5
},
zoom: {
enabled: false
},
toolbar: {
show: false
}
},
colors: ['#77B6EA', '#545454', '#F50898','#213ece'],
dataLabels: {
enabled: true,
},
stroke: {
curve: 'smooth'
},
title: {
text: 'Recent Sites Traffic',
align: 'left'
},
grid: {
borderColor: '#e7e7e7',
row: {
colors: ['#f3f3f3', 'transparent'], // takes an array which will be repeated on columns
opacity: 0.5
},
},
markers: {
size: 1
},
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'],
title: {
text: 'Month'
}
},
yaxis: {
title: {
text: 'Visits'
},
min: 5,
max: 40
},
legend: {
position: 'top',
horizontalAlign: 'right',
floating: true,
offsetY: -25,
offsetX: -5
}
},
});
return (
<div>
<div id="chart">
<ReactApexChart options={state.options} series={state.series} type="line" height={450} />
</div>
<div id="html-dist"></div>
</div>
);
}
export default TrafficChart
+7 -2
View File
@@ -99,8 +99,13 @@ $event-padding: 10px;
border-radius: 5px !important; border-radius: 5px !important;
} }
.billing{ .billing{
background-color: darkgoldenrod; background-color: #b9c2c5;
color: white; color: #77117a;
}
.next_billing{
background-color: #cff1f0;
color: #19548e;
} }
.extraProductCard{ .extraProductCard{
background-color: aliceblue; background-color: aliceblue;
+17
View File
@@ -0,0 +1,17 @@
function getDateFromDateString(dateString) {
const date = new Date(dateString);
// Ensure the date is valid
if (isNaN(date)) {
return "Invalid date string";
}
// Get the year, month, and day
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed, so we add 1
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
export default getDateFromDateString
+33
View File
@@ -0,0 +1,33 @@
function getDateTimeFromDateString(dateString) {
const date = new Date(dateString);
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const dayName = days[date.getDay()];
const monthName = months[date.getMonth()];
const day = date.getDate();
const year = date.getFullYear();
// Add ordinal suffix
function getOrdinal(n) {
if (n > 3 && n < 21) return "th"; // 4-20 are all "th"
switch (n % 10) {
case 1: return "st";
case 2: return "nd";
case 3: return "rd";
default: return "th";
}
}
// Format time in 12hr with AM/PM
let hours = date.getHours();
const minutes = date.getMinutes().toString().padStart(2, "0");
const ampm = hours >= 12 ? "PM" : "AM";
hours = hours % 12 || 12;
return `${dayName}, ${monthName} ${day}${getOrdinal(day)} ${year} ${hours}:${minutes}${ampm}`;
}
export default getDateTimeFromDateString
+14
View File
@@ -0,0 +1,14 @@
const sortObjectByListOrder = (data) => {
const sortedEntriesByValue = Object.entries(data).sort((a, b) => {
if(a[1].list_order > b[1].list_order){
return 1
}else{
return -1
}
}); // Sorts numerically by value
const sortedObjectByValue = Object.fromEntries(sortedEntriesByValue);
return sortedObjectByValue
}
export default sortObjectByListOrder
+4
View File
@@ -3,12 +3,16 @@ const siteLinks = {
help: '/help', help: '/help',
home: '/', home: '/',
dash: '/dash', dash: '/dash',
traffic: '/traffic',
start: '/start',
profile_complete: '/profile-complete', profile_complete: '/profile-complete',
product: '/product/*', product: '/product/*',
contacts: '/contacts', contacts: '/contacts',
comments: '/comments', comments: '/comments',
reports: '/reports', reports: '/reports',
subscription: '/subscription', subscription: '/subscription',
subscription_success:'/subscription-success',
subscribe: '/subscribe',
onboard: '/subscription', onboard: '/subscription',
user: '/user', user: '/user',
calendar: '/calendar', calendar: '/calendar',
+4
View File
@@ -1,12 +1,16 @@
const queryKeys = { const queryKeys = {
user_details: ['user_details'], user_details: ['user_details'],
product_url: ['product_url'], product_url: ['product_url'],
dash_payments: ['dash_payments'],
products: ['product-data'], products: ['product-data'],
myproduct_provision: ['myproduct_provision'], myproduct_provision: ['myproduct_provision'],
product_page: ['product_page'], product_page: ['product_page'],
recentAction: ['recent-action'], recentAction: ['recent-action'],
settingsData: ['settings_data'], settingsData: ['settings_data'],
myProductConfig: ['myproduct_config'],
productTemplateData: ['product_template_data'],
subscriptions: ['subscriptions'], subscriptions: ['subscriptions'],
profile_data: ['profile_data'],
dashboard: ['dashboard'], dashboard: ['dashboard'],
topBar: ['top-bar'], topBar: ['top-bar'],
+66 -2
View File
@@ -26,7 +26,8 @@ const postAuxEnd = (path, postData, media=false) => {
return axios.post(`${basePath}${path}`, postData).then(res => { return axios.post(`${basePath}${path}`, postData).then(res => {
return res return res
}).catch(err => { }).catch(err => {
throw new Error(err.response.data.msg); // console.log('res', err.response.data)
throw new Error(err.response.data.error_message);
}) })
} }
@@ -75,6 +76,14 @@ export const topBar = (reqData) => {
return postAuxEnd(`/panel/account/bar`, postData, false) return postAuxEnd(`/panel/account/bar`, postData, false)
} }
// FUNCTION TO GET PROFILE DATA
export const profileDetails = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/profile`, postData, false)
}
// FUNCTION TO GET PRODUCT BY ID // FUNCTION TO GET PRODUCT BY ID
export const MyProductData = (reqData) => { export const MyProductData = (reqData) => {
let postData = { let postData = {
@@ -83,6 +92,13 @@ export const MyProductData = (reqData) => {
return postAuxEnd(`/panel/myproduct/dash`, postData, false) return postAuxEnd(`/panel/myproduct/dash`, postData, false)
} }
export const StripeSubscriptionCreate = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/subscription/start`, postData, false)
}
// FUNCTION TO GET CALENDAR EVENTS // FUNCTION TO GET CALENDAR EVENTS
export const getCalendarEvents = (reqData) => { export const getCalendarEvents = (reqData) => {
let postData = { let postData = {
@@ -91,7 +107,7 @@ export const getCalendarEvents = (reqData) => {
return postAuxEnd(`/panel/account/calendar`, postData, false) return postAuxEnd(`/panel/account/calendar`, postData, false)
} }
// FUNCTION TO GET DASHBOARD PRODUCT DATA SECTION // FUNCTION TO GET CONTACT DATA
export const contactData = (reqData) => { export const contactData = (reqData) => {
let postData = { let postData = {
...reqData, ...reqData,
@@ -99,6 +115,14 @@ export const contactData = (reqData) => {
return postAuxEnd(`/panel/contacts`, postData, false) return postAuxEnd(`/panel/contacts`, postData, false)
} }
// FUNCTION TO GET COMMENTS DATA
export const commentsData = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/comments`, postData, false)
}
// FUNCTION TO GET DASHBOARD PRODUCT URL DATA SECTION // FUNCTION TO GET DASHBOARD PRODUCT URL DATA SECTION
export const productsURL = (reqData) => { export const productsURL = (reqData) => {
let postData = { let postData = {
@@ -107,6 +131,14 @@ export const productsURL = (reqData) => {
return postAuxEnd(`/panel/account/products/url`, postData, false) return postAuxEnd(`/panel/account/products/url`, postData, false)
} }
// FUNCTION TO GET DASHBOARD PAYMENTS
export const getDashPayments = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/payments`, postData, false)
}
// FUNCTION TO GET DASHBOARD PRODUCT DATA SECTION // FUNCTION TO GET DASHBOARD PRODUCT DATA SECTION
export const productsData = (reqData) => { export const productsData = (reqData) => {
let postData = { let postData = {
@@ -166,6 +198,30 @@ export const getSettingsData = (reqData) => {
return postAuxEnd(`/panel/myproduct/settings/values`, postData, false) return postAuxEnd(`/panel/myproduct/settings/values`, postData, false)
} }
// FUNCTION TO GET MY PRODUCT CONFIGURATION
export const getMyProductConfig = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/myproduct/configuration`, postData, false)
}
// FUNCTION TO GET SETTINGS DATA
export const getProductTemplateData = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/products/templates`, postData, false)
}
// FUNCTION TO ACTIVATE TEMPLATE
export const activateTemplate = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/template/activate`, postData, false)
}
// FUNCTION TO GET PRODUCT SUBSCRIPTIONS // FUNCTION TO GET PRODUCT SUBSCRIPTIONS
export const completeProfile = (reqData) => { export const completeProfile = (reqData) => {
let postData = { let postData = {
@@ -190,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)
}
+3 -2
View File
@@ -2,18 +2,19 @@ import React, { useEffect } from 'react'
import ProductFactory from '../component/product/ProductFactory' import ProductFactory from '../component/product/ProductFactory'
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import siteLinks from '../links/siteLinks'; import siteLinks from '../links/siteLinks';
import { useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
export default function ProductPage() { export default function ProductPage() {
const navigate = useNavigate() const navigate = useNavigate()
let {pathname} = useLocation()
const { userDetails: { profile_completed }} = useSelector((state) => state?.userDetails); // CHECKS IF USER Details are avaliable, to determine if user is active const { userDetails: { profile_completed }} = useSelector((state) => state?.userDetails); // CHECKS IF USER Details are avaliable, to determine if user is active
useEffect(()=>{ useEffect(()=>{
if(!profile_completed){ if(!profile_completed){
navigate(siteLinks.profile_complete, {replace: true, state:{profile_completed: false}}) navigate(siteLinks.profile_complete, {replace: true, state:{redirectLink: pathname}})
} }
},[]) },[])
+7
View File
@@ -0,0 +1,7 @@
import Start from "../component/start/Start";
export default function StartPage(){
return <Start />
}
+9
View File
@@ -0,0 +1,9 @@
import React from 'react'
//import Signup2 from '../component/auth/Signup2'
import Subscribe from '../component/subscribe/Subscribe'
export default function SubscribePage() {
return (
<Subscribe />
)
}
+8
View File
@@ -0,0 +1,8 @@
import React from 'react'
import Traffic from "../component/traffic/Traffic";
export default function TrafficPage() {
return (
<Traffic />
)
}