Compare commits

...

160 Commits

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

After

Width:  |  Height:  |  Size: 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: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 KiB

+42
View File
@@ -0,0 +1,42 @@
import React from 'react'
import GoogleDownload from '../../assets/img/download/andriod.jpg'
import IOSDownload from '../../assets/img/download/apple.jpg'
export default function AuthFooter() {
return (
<div className='w-100'>
{Number(process.env.REACT_APP_SHOW_DOWNLOAD) == 100 &&
<>
<div className="row" style={{margin: '5px'}}>
<hr />
</div>
<div className="row" style={{marginTop: '20px'}}>
<div className="col-6">
<div className="app-store-icons-wrap text-center">
<a className="icon google"
href='#' >
<img src={IOSDownload} className='w-80 h-auto' alt='IOS Download' />
</a>
</div>
</div>
<div className="col-6">
<div className="app-store-icons-wrap text-center">
<a className="icon apple" href='#'>
<img src={GoogleDownload} className='w-80 h-auto' alt='IOS Download' />
</a>
</div>
</div>
</div>
</>
}
<div className="login-links">
<a href={process.env.REACT_APP_HOME_LINK}>Home</a>
<a href={process.env.REACT_APP_ABOUT_LINK}>About</a>
<a href={process.env.REACT_APP_CONTACTS_LINK}>Contact</a>
<a href={process.env.REACT_APP_TERMS_LINK}>Terms</a>
</div>
</div>
)
}
+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
> >
+6 -4
View File
@@ -8,6 +8,7 @@ import siteLinks from '../../links/siteLinks'
import { useMutation } from '@tanstack/react-query' import { useMutation } from '@tanstack/react-query'
import { recoverPWD } from '../../services/services'; import { recoverPWD } from '../../services/services';
import getImage from '../../utils/getImage'; import getImage from '../../utils/getImage';
import AuthFooter from './AuthFooter';
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
username: Yup.string() username: Yup.string()
@@ -17,7 +18,7 @@ const validationSchema = Yup.object().shape({
// "Invalid email format" // "Invalid email format"
// ) // )
.min(3, "Minimum 3 characters") .min(3, "Minimum 3 characters")
.max(50, "Maximum 50 characters") .max(25, "Maximum 25 characters")
.required("Email is required"), .required("Email is required"),
}) })
@@ -49,8 +50,8 @@ export default function Forgetpwd2() {
<div className="row no-gutters justify-content-center"> <div className="row no-gutters justify-content-center">
<div className="col-11 col-sm-6 col-lg-5 col-xxl-4 align-self-center order-2 order-sm-1h" style={{maxWidth: '520px'}}> <div className="col-11 col-sm-6 col-lg-5 col-xxl-4 align-self-center order-2 order-sm-1h" style={{maxWidth: '520px'}}>
<div className="mt-5 d-flex"> <div className="mt-5 d-flex">
<div className="bg-white register p-5"> <div className="bg-white register px-5 pt-5 pb-3">
<h1 className="mb-2">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}
@@ -65,7 +66,7 @@ export default function Forgetpwd2() {
<> <>
<div className="col-12"> <div className="col-12">
<div className="form-group"> <div className="form-group">
<label className={`text-black fw-bold control-label ${(props.errors.username && props.touched.username) && 'text-danger'}`}>Username*</label> <label className={`text-black fw-bold control-label`}>Username* <span className='text-danger' style={{fontSize: '12px'}}>{(props.errors.username && props.touched.username) && props.errors.username}</span></label>
<input type="text" name='username' className="form-control" placeholder="Username" value={props.values.username} onChange={props.handleChange} /> <input type="text" name='username' className="form-control" placeholder="Username" value={props.values.username} onChange={props.handleChange} />
</div> </div>
</div> </div>
@@ -97,6 +98,7 @@ export default function Forgetpwd2() {
); );
}} }}
</Formik> </Formik>
<AuthFooter />
</div> </div>
</div> </div>
</div> </div>
+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">
+13 -34
View File
@@ -9,8 +9,7 @@ import siteLinks from '../../links/siteLinks'
import { loginUser } from '../../services/services' import { loginUser } from '../../services/services'
import { updateUserDetails } from '../../store/UserDetails' import { updateUserDetails } from '../../store/UserDetails'
import GoogleDownload from '../../assets/img/download/andriod.jpg' import AuthFooter from './AuthFooter'
import IOSDownload from '../../assets/img/download/apple.jpg'
export default function Login() { export default function Login() {
@@ -47,12 +46,12 @@ export default function Login() {
console.log(error) console.log(error)
}, },
onSuccess: (res) => { onSuccess: (res) => {
if(res?.data?.error_message){ if(res?.data && res?.data?.error_message){
throw({message: res?.data?.error_message}) throw({message: res?.data?.error_message})
} }
const {token, room, uid} = res?.data const {token, room, uid} = res?.data
if(!token || !room){ if(!token || !room){
throw({message: 'something went wrong, try again!'}) throw({message: 'Unable to complete your login, Please try again!'})
} }
localStorage.setItem('token', token) localStorage.setItem('token', token)
localStorage.setItem('room', room) localStorage.setItem('room', room)
@@ -86,20 +85,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,36 +123,16 @@ 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={{marginTop: '20px'}}> <AuthFooter />
<div className="col-6">
<div className="app-store-icons-wrap text-center">
<a className="icon google"
href='#' >
<img src={IOSDownload} className='w-80 h-auto' alt='IOS Download' />
</a>
</div>
</div>
<div className="col-6">
<div className="app-store-icons-wrap text-center">
<a className="icon apple" href='#'>
<img src={GoogleDownload} className='w-80 h-auto' alt='IOS Download' />
</a>
</div>
</div>
</div>
<div className="login-links">
<a href={process.env.REACT_APP_HOME_LINK}>Home</a>
<a href={process.env.REACT_APP_ABOUT_LINK}>About</a>
<a href={process.env.REACT_APP_CONTACTS_LINK}>Contact</a>
<a href={process.env.REACT_APP_TERMS_LINK}>Terms</a>
</div>
</div> </div>
</div> </div>
</div> </div>
+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=""/>
+63 -19
View File
@@ -9,6 +9,7 @@ import siteLinks from '../../links/siteLinks'
import {useMutation} from '@tanstack/react-query'; import {useMutation} from '@tanstack/react-query';
import {signUpUser} from '../../services/services'; import {signUpUser} from '../../services/services';
import getImage from '../../utils/getImage'; import getImage from '../../utils/getImage';
import AuthFooter from './AuthFooter';
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
email: Yup.string() email: Yup.string()
@@ -61,11 +62,12 @@ 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 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, Please create your account.</p> <p>Welcome, please create your account.</p>
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
validationSchema={validationSchema} validationSchema={validationSchema}
@@ -79,20 +81,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 +128,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,27 +151,46 @@ 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>
<span style={{paddingLeft: '5px' , fontWeight: 'bolder'}}>Already have an account? </span>
<Link
to={siteLinks.login}>
<button
className="btn btn-warning text-uppercase">
Sign In
</button>
</Link>
</p>
</div> </div>
</div> </div>
</Form> </Form>
); );
}} }}
</Formik> </Formik>
<AuthFooter />
</div> </div>
</div> </div>
</div> </div>
+2 -1
View File
@@ -48,7 +48,8 @@ export default function Calendar(){
} }
const {data, isFetching, isError, error} = useQuery({ const {data, isFetching, isError, error} = useQuery({
queryKey: queryKeys.calendar_events, queryKey: queryKeys.calendar_events,
queryFn: () => getCalendarEvents(reqData) queryFn: () => getCalendarEvents(reqData),
staleTime: 0
}) })
const receievedEvents = data?.data const receievedEvents = data?.data
+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 className='text-truncate' style={{paddingLeft: '10px'}}>{product?.name}</span></h4>
</div> </div>
</div> </div>
</Link> </Link>
+1 -1
View File
@@ -31,7 +31,7 @@ export default function ProductsURL() {
{/*<a className="btn btn-xs" href="#!">Export <i className="zmdi zmdi-download pl-1"></i> </a>*/} {/*<a className="btn btn-xs" href="#!">Export <i className="zmdi zmdi-download pl-1"></i> </a>*/}
</div> </div>
</div> </div>
<div className="card-body scrollbar scroll_dark pt-0" style={{maxHeight: '350px'}}> <div className="overflow-y-auto card-body scrollbar scroll_dark pt-0" style={{maxHeight: '350px'}}>
<div className="datatable-wrapper table-responsive"> <div className="datatable-wrapper table-responsive">
{isFetching ? {isFetching ?
<> <>
+2 -2
View File
@@ -77,7 +77,7 @@ export default function RecentActions() {
<h4>{dataAction?.data?.completed}</h4> <h4>{dataAction?.data?.completed}</h4>
</div> </div>
</div> </div>
<div className="table-responsive m-t-20"> <div className="overflow-y-auto table-responsive m-t-20">
<table id="datatable-buttons" className="table"> <table id="datatable-buttons" className="table">
<thead> <thead>
<tr> <tr>
@@ -94,7 +94,7 @@ export default function RecentActions() {
let text = action?.status == '5' ? 'completed' : action?.status == '3' ? 'verifying' : action?.status == '0' ? 'processing' : 'processing' let text = action?.status == '5' ? 'completed' : action?.status == '3' ? 'verifying' : action?.status == '0' ? 'processing' : 'processing'
return ( return (
<tr key={index}> <tr key={index}>
<td>{action?.id}</td> <td>{(action?.id).toString().slice(-4)}</td>
<td>{action?.action_label}</td> <td>{action?.action_label}</td>
<td>{new Date(action?.added).toDateString()}</td> <td>{new Date(action?.added).toDateString()}</td>
<td> <td>
+11 -3
View File
@@ -57,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"><Link to={item?.link}>{item?.description}</Link></h5> <h5 className="text-muted mb-0">{item?.description}</h5>
</div> </div>
</div> </div>
<div className="apexchart-wrapper"> <div className="apexchart-wrapper">
@@ -79,6 +86,7 @@ export default function TopBar() {
</div> </div>
</div> </div>
</div> </div>
</Link>
</div> </div>
) )
})} })}
@@ -11,7 +11,7 @@ export default function UserFooter(){
<p>&copy; Copyright {year}. All rights reserved.</p> <p>&copy; Copyright {year}. All rights reserved.</p>
</div> </div>
<div className="col col-sm-6 ml-sm-auto text-center text-sm-right"> <div className="col col-sm-6 ml-sm-auto text-center text-sm-right">
<p>A division of <i className="fa fa-key text-danger mx-1"></i> autoMedSys A.I.</p> <p>A division of autoMedSys A.I.</p>
</div> </div>
</div> </div>
</footer> </footer>
@@ -7,7 +7,7 @@ import siteLinks from "../../../links/siteLinks";
export default function UserHeader(){ export default function UserHeader(){
const { userDetails } = useSelector((state) => state?.userDetails); // CHECKS IF USER Details are avaliable, to determine if user is active const { userDetails } = useSelector((state) => state?.userDetails); // USER Details
const nav_menu = useRef(null) const nav_menu = useRef(null)
@@ -74,10 +74,15 @@ export default function UserHeader(){
<ul className="navbar-nav nav-right ml-auto"> <ul className="navbar-nav nav-right ml-auto">
<li className="nav-item user-profile"> <li className="nav-item user-profile">
<a onClick={toggleMenu} className="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdow"> <a onClick={toggleMenu} className="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdow">
<img src={getImage('profile-pic-circle.png')} alt="avtar-img" /> <img
src={userDetails?.picture ? userDetails?.picture : getImage('profile-pic-circle.png')}
// src={getImage('profile-pic-circle.png')}
alt="avtar-img"
/>
<span className="bg-success user-status"></span> <span className="bg-success user-status"></span>
</a> </a>
<div ref={nav_menu} onClick={toggleMenu} className="dropdown-menu animated fadeIn"> <div ref={nav_menu} onClick={toggleMenu} className="dropdown-menu animated fadeIn">
<div className="position-fixed" style={{top: '0px', left: '0px', right: '0px', bottom: '0px'}}></div>
<div className="bg-gradient px-4 py-3"> <div className="bg-gradient px-4 py-3">
<div className="d-flex align-items-center justify-content-between"> <div className="d-flex align-items-center justify-content-between">
<div className="mr-1"> <div className="mr-1">
+4 -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>
@@ -48,13 +48,14 @@ export default function UserMenu() {
</div> </div>
</Link> </Link>
<ul id="collapseTwo" className="collapse" aria-labelledby="headingTwo" data-bs-parent="#sidebarNav"> <ul id="collapseTwo" className="collapse" aria-labelledby="headingTwo" data-bs-parent="#sidebarNav">
<li className={`${pathname == siteLinks.my_media ? 'active' : ''}`}><Link to={siteLinks.my_media}>Files and Media</Link></li>
<li className={`${pathname == siteLinks.subscription ? 'active' : ''}`}><Link to={siteLinks.subscription}>Subscription</Link></li> <li className={`${pathname == siteLinks.subscription ? 'active' : ''}`}><Link to={siteLinks.subscription}>Subscription</Link></li>
<li className={`${pathname == siteLinks.settings ? 'active' : ''}`}><Link to={siteLinks.settings}>Settings</Link></li> <li className={`${pathname == siteLinks.settings ? 'active' : ''}`}><Link to={siteLinks.settings}>Settings</Link></li>
</ul> </ul>
</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>
+242
View File
@@ -0,0 +1,242 @@
import React from "react";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
import {useState, useRef} from "react";
import {useQuery, useMutation, useQueryClient} from "@tanstack/react-query";
import queryKeys from "../../services/queryKeys";
import {getMediaFileList, uploadFile} from "../../services/services";
import getImage from "../../utils/getImage";
export default function MyMedia() {
const queryClient = useQueryClient()
const basePath = process.env.REACT_APP_MAIN_API
const [selectedFile, setSelectedFile] = useState(null);
const [message, setMessage] = useState('');
const [imageLink, setImageLink] = useState('')
console.log('imageLink', imageLink)
// Function to handle file selection
const handleFileChange = (event) => {
setSelectedFile(event.target.files[0]);
setMessage('');
};
// Function to handle the upload to the API
const _handleUpload = async () => {
if (!selectedFile) {
setMessage('Please select a file first!');
return;
}
const formData = new FormData();
// 'file' is the field name that your API endpoint expects
formData.append('file', selectedFile);
formData.append("member_uid", localStorage.getItem('uid'));
formData.append("token", localStorage.getItem('token'));
try {
// Replace with your actual API endpoint URL
const apiEndpoint = basePath + '/upload/webfiles';
const response = await fetch(apiEndpoint, {
method: 'POST',
// The browser automatically sets the 'Content-Type' header to
// 'multipart/form-data' when you provide a FormData object as the body,
// which is required for file uploads.
body: formData,
});
if (response.ok) {
const result = await response.json();
setMessage('File uploaded successfully!');
console.log('Success:', result);
} else {
setMessage('Upload failed.');
console.error('Upload failed:', response.statusText);
}
} catch (error) {
setMessage('An error occurred during the upload.');
console.error('Error:', error);
}
}
const uploadFileMutation = useMutation({
mutationFn: (fields) => {
if(!fields.file){
throw({message: 'Please select a file first!'})
}
return uploadFile(fields)
},
onSuccess: (res) => {
// console.log('res', res.data)
// if(res.data.resultCode != '0' || !res?.data?.pending_uid){
// throw({message: res?.data?.resultDescription})
// }
setSelectedFile(null)
queryClient.refetchQueries({
queryKey: [...queryKeys.my_files],
// type: 'active',
// exact: true,
})
},
onSettled: () => {
setTimeout(()=>{
uploadFileMutation.reset()
}, 3000)
}
})
const handleUpload = () => {
let reqData = {
token: localStorage.getItem("token"), // USER TOKEN
member_uid: localStorage.getItem("uid"), // USER UID
file: selectedFile
};
// console.log(reqData)
uploadFileMutation.mutate(reqData)
}
const {data, isFetching, isError, error} = useQuery({
queryKey: queryKeys.my_files,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid') // USER UID
}
return getMediaFileList(reqData)
}
})
const mediaFileList = data?.data
//debugger;
return (
<>
<BreadcrumbComBS title='Files' paths={['Dashboard', 'Files']}/>
<div className="row">
<div className="col-xl-6 col-xxl-4 m-b-30">
<div className="card card-statistics mb-0 widget-support-list">
<div className="card-header">
<div className="card-heading">
<h4 className="card-title">Upload File</h4>
</div>
</div>
<div className="card-body pl-0 pr-0 scrollbar scroll_dark">
<div className="widget-text">
<div>
<input className="form-control form-control-sm" type="file"
onChange={handleFileChange}/>
{selectedFile && (
<div>
<h4>Selected File Details:</h4>
<ul>
<li>Name: {selectedFile.name}</li>
<li>Type: {selectedFile.type}</li>
<li>Size: {selectedFile.size} bytes</li>
</ul>
<div style={{width: '100%', textAlign: 'right'}}>
<button
className="btn btn-square btn-inverse-light btn-xs d-inline-block mt-2 mb-0"
onClick={handleUpload} disabled={!selectedFile || uploadFileMutation.isPending || uploadFileMutation.isSuccess}>
Upload
</button>
</div>
<>
{/* {message && <p>{message}</p>} */}
<p>{uploadFileMutation.isPending ? 'uploading...' : uploadFileMutation.isError ? uploadFileMutation?.error?.message : uploadFileMutation.isSuccess ? 'File Uploaded' : ''}</p>
</>
</div>
)}
</div>
</div>
</div>
</div>
</div>
<div className="col-xl-6 col-xxl-4 m-b-30">
<div className="card card-statistics h-100 mb-0 widget-downloads-list" style={{}}>
<div className="card-header d-flex justify-content-between">
<div className="card-heading">
<div className="card-heading">
<h4 className="card-title">Files List</h4>
</div>
</div>
</div>
{isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div className="card-body scrollbar scroll_dark" style={{minHeight: '400px', maxHeight: '500px', overflowY: 'auto'}}>
{mediaFileList && mediaFileList?.file_list && mediaFileList?.file_list.map((item, index) => {
const file_url = (mediaFileList?.media_server + "/" + item?.file_group + "/" + item?.file_uid + "/" + item.filename).toLowerCase();
const avtarImage =
item?.file_type === undefined
? "icons/01.png"
: "icons/" + item.file_type + ".png";
return (<div key={index} className={`widget-text ${imageLink == file_url && 'bg-light'}`}>
<div className={`media align-items-center`} onClick={()=>setImageLink(file_url)} style={{cursor: 'pointer'}}>
<img src={getImage(avtarImage)}
// src={`assets/img/file-icon/${item.file_type}.png`}
className="img-fluid"
alt={`${item.file_type}`}/>
<div className="media-body">
<h4 className="mb-0 ml-3">{item.filename}</h4>
</div>
<div>
<a href={`${file_url}`} target='_blank'
className="btn btn-icon btn-round btn-outline-success">
<i className="ti ti-download"></i>
</a>
<a href="" className="btn btn-icon btn-round btn-outline-danger ml-2">
<i className="ti ti-close"></i>
</a>
</div>
</div>
</div>)
})
}
</div>
}
</div>
</div>
<div className="col-xl-6 col-xxl-4 m-b-30">
<div className="card card-statistics h-100 mb-0 widget-branches-list">
<div className="card-header">
<div className="card-heading">
<h4 className="card-title">Preview</h4>
</div>
</div>
<div className="card-body d-flex justify-content-center align-items-center pl-0 pr-0 scrollbar scroll_dark">
{imageLink &&
<img className="w-100 h-auto" src={imageLink} alt='file-image' />
}
</div>
<div className="card-header d-flex justify-content-between align-items-center">
{imageLink &&
<>
<p>0 x 0 px</p>
<p>size: 0 bytes</p>
</>
}
</div>
</div>
</div>
</div>
</>
)
}
+25 -6
View File
@@ -1,21 +1,26 @@
import React, { useMemo, useRef, useState } from "react"; import React, {useEffect, useMemo, useRef, useState} from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import getImage from "../../utils/getImage"; import getImage from "../../utils/getImage";
import { useMutation, useQuery } from "@tanstack/react-query"; import { useMutation, useQuery } from "@tanstack/react-query";
import { productRefreshSite, getSettingsData } from "../../services/services"; import { productRefreshSite, getSettingsData } from "../../services/services";
import Settings from "./settingsTab/Settings"; import Settings from "./settingsTab/Settings";
import queryKeys from "../../services/queryKeys"; import queryKeys from "../../services/queryKeys";
import {SocketContextValues} from "../context/SocketIOContext";
export default function ProductActive({productData}){ export default function ProductActive({productData}){
const {joinRoom} = SocketContextValues() // Destructures values from socket context
const iframe = useRef() const iframe = useRef()
const [refreshMsg, setRefreshMsg] = useState('')
const refresh = useMutation({ const refresh = useMutation({
mutationFn: (fields) => { mutationFn: (fields) => {
return productRefreshSite(fields) return productRefreshSite(fields)
}, },
onSuccess: (res) => { onSuccess: (res) => {
setRefreshMsg(res?.data?.message)
setTimeout(()=>{setRefreshMsg('')},3000)
iframe.current.src += '' iframe.current.src += ''
} }
}) })
@@ -31,6 +36,13 @@ export default function ProductActive({productData}){
} }
let externalUrl= 'https://'+productData?.internal_url let externalUrl= 'https://'+productData?.internal_url
const productSubUID = productData.subscription_uid;
useEffect(() => {
const provision_room = "PROVISION_"+productSubUID;
console.log("JOINING ROOM ON ACTIVE *** ", provision_room);
joinRoom(provision_room); // provision subscription room
}, [])
return( return(
<> <>
{/*<BreadcrumbComBS title='Active Product Name' paths={['Dashboard', 'Product']} />*/} {/*<BreadcrumbComBS title='Active Product Name' paths={['Dashboard', 'Product']} />*/}
@@ -46,21 +58,28 @@ 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>
</div> </div>
</div> </div>
<div className="card-body"> <div className="card-body" style={{minHeight: '600px', maxHeight: '600px'}}>
<iframe ref={iframe} style={{borderWidth: '0px'}} src={externalUrl} width="100%" height="600" title={externalUrl}></iframe> <iframe ref={iframe} style={{borderWidth: '0px'}} src={externalUrl} width="100%" height="600" title={externalUrl}></iframe>
</div> </div>
<div className="p-4 ml-auto"> <div className="p-4 ml-auto">
<button type="button" onClick={handleRefresh} className="btn btn-primary">{refresh.isPending ? 'Loading...' : 'Refresh Site'} <div className="d-flex justify-end gap-3">
{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">
@@ -68,7 +87,7 @@ export default function ProductActive({productData}){
<h4 className="card-title"> Site Settings </h4> <h4 className="card-title"> Site Settings </h4>
</div> </div>
</div> </div>
<div className="card-body"> <div className="card-body" style={{minHeight: '680px'}}>
<Settings productData={productData} /> <Settings productData={productData} />
</div> </div>
</div> </div>
+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> */}
+1 -1
View File
@@ -126,7 +126,7 @@ export default function ProductStart(props){
</div> </div>
{/* Vertical Center Modal */} {/* Vertical Center Modal */}
<div ref={modalRef} className="modal fade" id="verticalCenter" tabIndex="-1" role="dialog" aria-hidden="true"> <div ref={modalRef} className="modal fade" id="verticalCenter" tabIndex="-1" role="dialog" aria-hidden="false">
<div className="modal-dialog modal-dialog-centered" role="document"> <div className="modal-dialog modal-dialog-centered" role="document">
<div className="modal-content"> <div className="modal-content">
<div className="modal-header"> <div className="modal-header">
@@ -0,0 +1,179 @@
import React, {memo} from 'react'
import getImage from "../../../utils/getImage";
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
import queryKeys from '../../../services/queryKeys';
import {
getProductcolorStyleCss,
activateTemplate,
getProductColorStyles,
activateColorStyle
} from '../../../services/services';
import {Link} from "react-router-dom";
import siteLinks from "../../../links/siteLinks";
const ColorStyleConfigure = memo(({name = 'Full Name', data, productData}) => {
const queryClient = useQueryClient()
const {data: colorStyleCss, isFetching, isError, error} = useQuery({
queryKey: queryKeys.productcolorStyleCss,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id
}
return getProductColorStyles(reqData)
},
staleTime: 0
})
const templateResponse = colorStyleCss?.data
const currentColorUID = templateResponse?.current_colorstyle_uid
const color_styles = templateResponse?.color_styles
const custom_template_name = templateResponse?.custom_template_name
// console.log('data Template', templateResponse)
console.log("Page data == ", data)
const handleActivateTemplate = useMutation({
mutationFn: (fields) => {
return activateColorStyle(fields)
},
onSuccess: (res) => {
if (res?.data?.resultCode != '0') {
throw new Error(res.data.resultDescription)
}
queryClient.refetchQueries({ // refetches productProvision API call
queryKey: [...queryKeys.settingsData],
})
},
onSettled: () => {
setTimeout(() => {
handleActivateTemplate.reset()
}, 3000)
}
})
const handleSubmit = (style_uid) => {
const reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id,
color_style_uid: style_uid
}
// console.log(reqData)
handleActivateTemplate.mutate(reqData)
}
if (custom_template_name && custom_template_name != '') {
// This implies we have a custom template , just return here
return <>
<div className='col-12'>
<div
className="rounded-2 d-flex flex-column justify-content-between align-items-center"
style={{backgroundColor: '#F2FAF7'}}>
<h4 className='p-4 text-black'
style={{marginBottom: '20px'}}>Custom Product Template.</h4>
<img className='' style={{width: '200px'}}
src={getImage('custom-template.png')}
alt='mail-alert'/>
<h4 className='p-4 text-black'
style={{marginTop: '20px'}}>This product is using a custom template named <span
style={{color: 'darkred'}}>&ldquo;{custom_template_name}&rdquo;</span> .</h4>
</div>
</div>
</>
}
return (
<div className="page-account-form">
<div className="p-0">
{isFetching ?
<>
<div className="row">
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</div>
</>
: isError ?
<div className="row">
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
</div>
:
<div className="row overflow-y-auto" style={{maxHeight: '550px'}}>
<>
{!color_styles?.length ?
<p>No data Found</p>
:
color_styles.map(color_style => (
<div key={color_style.color_style_uid} className="col-xl-6 col-sm-6">
<div className="card card-statistics">
<div className="card-body" style={{
backgroundColor: `#${color_style.color_code}`,
opacity: '0.85',
borderRadius: '10px'
}}>
<div className="text-center p-2">
{/*<div className="mb-2">*/}
{/* <img src={getImage('file-icon/svg.png')}*/}
{/* alt={template.title}/>*/}
{/*</div>*/}
<h4 className="mb-0">{color_style.title}</h4>
{currentColorUID === color_style.color_style_uid ?
<button className="btn btn-light"
disabled={true}>Active</button>
:
<button
onClick={() => handleSubmit(color_style.color_style_uid)}
className="btn btn-primary">Select</button>
}
</div>
</div>
</div>
</div>
))
}
{/* {Object.entries(data)?.map(([key, value]) => (
<div key={key} className="col-xl-6 col-sm-6">
<div className="card card-statistics">
<div className="card-body">
<div className="text-center p-2">
<div className="mb-2">
<img src={getImage(value.banner)} alt={value.title} />
</div>
<h4 className="mb-0">{value.title}</h4>
<a href="javascript:void(0)" className="btn btn-light">Activate</a>
</div>
</div>
</div>
</div>
))} */}
</>
<div className="col-12">
<>
{handleActivateTemplate.isPending ?
<p className={'text-center '}>loading...</p>
:
handleActivateTemplate.isError ?
<p className={'text-center text-danger'}>{handleActivateTemplate.error.message}</p>
:
handleActivateTemplate.isSuccess ?
<p className={'text-center text-success'}>Templated activated
successfully</p>
:
null
}
</>
</div>
</div>
}
</div>
</div>
)
}
)
export default ColorStyleConfigure
@@ -1,28 +1,55 @@
import React, {memo, useState} from 'react' import React, {memo, useEffect, useMemo, useState} from 'react'
import {useMutation, useQueryClient} 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 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 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}))
@@ -62,41 +89,79 @@ 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
@@ -114,12 +179,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>
}
</>
}
</>
) )
} }
) )
+56 -75
View File
@@ -1,78 +1,36 @@
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'
import TemplateConfigure from './TemplateConfigure';
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)
// console.log('configData', configData?.data?.subscription_template)
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 +55,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 +75,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="d-flex">
<ul className="nav nav-tabs" role="tablist"> <ul className="bg-body-secondary flex-column nav" role="tablist" style={{width: '25%', minHeight: '670px', maxHeight: '670px'}}>
<> <>
{Object.entries(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'}`} <p className={`text-black nav-link ${(activeTab == value.controls || (index == 0 & !activeTab)) && 'active show bg-primary text-white'}`}
id={key} id={key}
// data-bs-toggle="pill" // data-bs-toggle="pill"
// data-bs-target={`#${value.controls}`} // data-bs-target={`#${value.controls}`}
@@ -136,21 +98,40 @@ const Settings = memo(({productData}) => {
onClick={()=>handleChangeTab(value.controls)} onClick={()=>handleChangeTab(value.controls)}
> >
{value.title} {value.title}
</a> </p>
</li> </li>
))} ))}
{configData?.data?.subscription_template &&
<li className="mt-auto nav-item">
<p className={`text-black nav-link ${(activeTab == 'config_temp') && 'active show bg-primary text-white'}`}
// data-bs-toggle="pill"
// data-bs-target={`#${value.controls}`}
type="button"
// role="tab"
// aria-controls={value.controls}
// aria-selected="true"
onClick={()=>handleChangeTab('config_temp')}
>
Configure Template
</p>
</li>
}
</> </>
</ul> </ul>
<div className="tab-content"> <div className="p-3 tab-content" style={{width: '75%'}}>
<> <>
{Object.entries(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>
))} ))}
<div className={`tab-pane fade ${(activeTab == 'config_temp') && 'active show'}`}
>
<TemplateConfigure productData={productData} />
</div>
</> </>
</div> </div>
</div> </div>
@@ -1,11 +1,15 @@
import React, {memo} from 'react' import React, {memo} from 'react'
import getImage from "../../../utils/getImage"; import getImage from "../../../utils/getImage";
import { useQuery } from '@tanstack/react-query'; import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
import queryKeys from '../../../services/queryKeys'; import queryKeys from '../../../services/queryKeys';
import { getProductTemplateData } from '../../../services/services'; import {getProductTemplateData, activateTemplate} from '../../../services/services';
import {Link} from "react-router-dom";
import siteLinks from "../../../links/siteLinks";
const SiteTemplateSelector = memo(({name = 'Full Name', data, productData}) => { const SiteTemplateSelector = memo(({name = 'Full Name', data, productData}) => {
const queryClient = useQueryClient()
const {data: templateData, isFetching, isError, error} = useQuery({ const {data: templateData, isFetching, isError, error} = useQuery({
queryKey: queryKeys.productTemplateData, queryKey: queryKeys.productTemplateData,
queryFn: () => { queryFn: () => {
@@ -15,14 +19,67 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
product_id: productData?.product_id product_id: productData?.product_id
} }
return getProductTemplateData(reqData) return getProductTemplateData(reqData)
} },
staleTime: 0
}) })
const templateResponse = templateData?.data const templateResponse = templateData?.data
const currentTemUID = templateResponse?.current_template_uid
const templates = templateResponse?.templates const templates = templateResponse?.templates
const custom_template_name = templateResponse?.custom_template_name
// console.log('data Template', templates) // 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">
@@ -41,9 +98,9 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
</div> </div>
</div> </div>
: :
<div className="row"> <div className="row overflow-y-auto" style={{maxHeight: '550px'}}>
<> <>
{!templates.length ? {!templates?.length ?
<p>No data Found</p> <p>No data Found</p>
: :
templates.map(template => ( templates.map(template => (
@@ -52,10 +109,17 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
<div className="card-body"> <div className="card-body">
<div className="text-center p-2"> <div className="text-center p-2">
<div className="mb-2"> <div className="mb-2">
<img src={getImage('file-icon/svg.png')} alt={template.title} /> <img src={getImage('file-icon/svg.png')}
alt={template.title}/>
</div> </div>
<h4 className="mb-0">{template.title}</h4> <h4 className="mb-0">{template.title}</h4>
<a href="#" className="btn btn-light">Activate</a> {currentTemUID == template.template_uid ?
<button className="btn btn-light"
disabled={true}>Active</button>
:
<button onClick={() => handleSubmit(template.template_uid)}
className="btn btn-primary">Activate</button>
}
</div> </div>
</div> </div>
</div> </div>
@@ -78,6 +142,23 @@ const SiteTemplateSelector = memo(({name='Full Name', data, productData}) =>{
</div> </div>
))} */} ))} */}
</> </>
<div className="col-12">
<>
{handleActivateTemplate.isPending ?
<p className={'text-center '}>loading...</p>
:
handleActivateTemplate.isError ?
<p className={'text-center text-danger'}>{handleActivateTemplate.error.message}</p>
:
handleActivateTemplate.isSuccess ?
<p className={'text-center text-success'}>Templated activated
successfully</p>
:
null
}
</>
</div>
</div> </div>
} }
</div> </div>
@@ -0,0 +1,128 @@
import React, {memo, useState} from 'react'
import getImage from "../../../utils/getImage";
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
import queryKeys from '../../../services/queryKeys';
import {getTemplateConfig} from '../../../services/services';
import {Link} from "react-router-dom";
import siteLinks from "../../../links/siteLinks";
import UploadModal from './UploadModal';
const TemplateConfigure = ({productData}) => {
// /panel/myproduct/template-config
const queryClient = useQueryClient()
const {data: templateData, isFetching, isError, error} = useQuery({
queryKey: queryKeys.myTemplateConfig,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
product_id: productData?.product_id
}
return getTemplateConfig(reqData)
},
staleTime: 0
})
const templateResponse = templateData?.data
const templateImages = templateResponse?.template_images?.data;
// debugger;
console.log("templateResponse", templateResponse);
// const currentColorUID = templateResponse?.current_colorstyle_uid
// const color_styles = templateResponse?.color_styles
// const custom_template_name = templateResponse?.custom_template_name
return <>
<div className="card card-statistics">
{isFetching ?
<>
<div className="row">
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</div>
</>
: isError ?
<div className="row">
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
</div>
:
<>
<div className="card-header">
<div className="card-heading">
<h4 className="card-title"
style={{textTransform: 'none'}}>{templateResponse?.template_name}</h4>
</div>
</div>
<div>
<div className="col-12">
<div>
<div>
<div>
<h4>Image List</h4>
</div>
</div>
<div>
<ul className="list-unstyled">
{templateImages && templateImages.map(
(item) => {
const currImage = item?.default_val;
return (
<li className="media">
<div style={{
display: 'flex',
flexDirection: 'row',
padding: '5px',
backgroundColor: 'aliceblue',
margin: '2px',
maxHeight: '150px'
}}>
<div className='d-flex justify-content-center align-items-center' style={{padding: '6px', width: '120px', height: '100px'}}>
<img className="mb-xxs-0 img-fluid"
style={{height: 'auto', maxHeight: '100px'}}
src={currImage} alt="image"/>
</div>
<div className="media-body" style={{padding: '2px'}}>
<div style={{
display: 'flex',
flexDirection: 'column'
}}>
<div style={{textAlign: 'right',width: '100%'}}></div>
{/* [Change Image] */}
<label className='w-100 text-end' data-bs-toggle="modal" data-bs-target="#verticalCenter" style={{cursor: 'pointer'}}>[Change Image]</label>
{/* <input id={item?.id} name={item?.id} className="d-none form-control form-control-sm" type="file" onChange={handleFileChange}/> */}
<div>
<h5 className="mt-0 mb-1">{item?.name}</h5>
{item?.description}
</div>
</div>
</div>
</div>
</li>
)
}
)
}
</ul>
</div>
</div>
</div>
</div>
</>
} </div>
<UploadModal />
</>
}
export default TemplateConfigure
@@ -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,173 @@
import React, { useRef, useState } from 'react'
import { Modal } from "bootstrap";
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
import queryKeys from '../../../services/queryKeys';
import {getMediaFileList, getTemplateConfig, uploadFile} from '../../../services/services';
export default function UploadModal() {
const [selectedFiles, setSelectedFiles] = useState([])
const handleSelectedFiles = (file_uid) => {
if(selectedFiles.length && selectedFiles.includes(file_uid)){
let indexOfItem = selectedFiles.indexOf(file_uid)
let oldFiles = [...selectedFiles]
oldFiles.splice(indexOfItem, 1)
setSelectedFiles(oldFiles)
}else{
setSelectedFiles(prev => [...prev, file_uid])
}
}
const modalRef = useRef(null)
const dismissModal = () => {
const body = document.querySelector('body')
body.removeAttribute('style')
// body.classList.toggle('modal-open')
const modalBackdrop = document.querySelectorAll('.modal-backdrop')
modalBackdrop.forEach(item => {
if (item) {
item.remove();
}
})
const modal = Modal.getInstance(modalRef.current);
modal && modal.hide();
};
const uploadFileMutation = useMutation({
mutationFn: (fields) => {
if(!fields.file){
throw({message: 'Please select a file first!'})
}
// return uploadFile(fields)
// remember to close the modal
dismissModal()
setTimeout(()=>{
uploadFileMutation.reset()
}, 3000)
},
onSuccess: (res) => {
// console.log('res', res.data)
// if(res.data.resultCode != '0' || !res?.data?.pending_uid){
// throw({message: res?.data?.resultDescription})
// }
// setSelectedFile({id: '', img: ''})
// queryClient.refetchQueries({
// queryKey: [...queryKeys.my_files],
// type: 'active',
// exact: true,
// })
},
onSettled: () => {
setTimeout(()=>{
uploadFileMutation.reset()
}, 3000)
}
})
const handleUpload = () => {
let reqData = {
token: localStorage.getItem("token"), // USER TOKEN
member_uid: localStorage.getItem("uid"), // USER UID
// file: selectedFile?.img
};
uploadFileMutation.mutate(reqData)
}
const {data, isFetching, isError, error} = useQuery({
queryKey: queryKeys.my_files,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid') // USER UID
}
return getMediaFileList(reqData)
}
})
const mediaFileList = data?.data
// console.log('mediaFileList', mediaFileList) //file_list
return (
<>
{/* Vertical Center Modal */}
<div ref={modalRef} className="modal fade" id="verticalCenter" tabIndex="-1" role="dialog" aria-hidden="false">
<div className="modal-50 modal-dialog modal-dialog-centered modal-50" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title" style={{fontSize: '18px'}} id="verticalCenterTitle">Select New Picture</h5>
<button type="button" className="close" data-bs-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div className="modal-body">
{isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div className='row g-5 justify-content-center'>
{mediaFileList?.file_list?.length ?
<>
{mediaFileList?.file_list?.map(item => {
const file_url = (mediaFileList?.media_server + "/" + item?.file_group + "/" + item?.file_uid + "/" + item.filename).toLowerCase();
return(
<div onClick={()=>handleSelectedFiles(item?.file_uid)} className={`p-3 text-center col-4`} style={{cursor: 'pointer'}}>
<div className={`p-3 ${selectedFiles.includes(item?.file_uid) && 'bg-light'}`}>
<img key={item?.file_uid} className="img-fluid" src={file_url} alt='file-image' />
</div>
</div>
)
})
}
</>
:
<p>No File(s) found!</p>
}
</div>
}
{/* <div style={{fontSize: '18px'}} className="" dangerouslySetInnerHTML={{__html: saleText}}/> */}
{/* {mutation.error &&
<>
<div className="col-12">
<p className='text-danger'>{mutation.error.message}</p>
</div>
</>
}
{mutation.isSuccess &&
<>
<div className="col-12">
<p className='text-success'>{'subscription is successful'}</p>
</div>
</>
} */}
{/* {requestStatus.message && (
<div className="col-12">
<p className={`p-2 text-center ${requestStatus.status ? 'text-success' : 'text-danger'}`}>{requestStatus.message}</p>
</div>
)} */}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
{selectedFiles.length > 0 &&
<button type="button" className="btn btn-primary" disabled={uploadFileMutation?.isPending || uploadFileMutation?.isSuccess} onClick={handleUpload}>{uploadFileMutation?.isPending ? 'Upload...' : uploadFileMutation?.isSuccess ? 'Uploaded' : 'Upload'}</button>
}
</div>
</div>
</div>
</div>
{/* END of Vertical Center Modal */}
</>
)
}
@@ -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,7 +1,7 @@
import React, { useEffect, useMemo, useState } from "react"; import React, {useCallback, useEffect, useMemo, useState} from "react";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
// import { useLocation } from "react-router-dom"; // import { useLocation } from "react-router-dom";
// import { Form, Formik } from "formik"; import { Form, Formik } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import {useMutation, useQuery} from "@tanstack/react-query"; import {useMutation, useQuery} from "@tanstack/react-query";
import getImage from "../../utils/getImage"; import getImage from "../../utils/getImage";
@@ -13,17 +13,19 @@ import { updateUserDetails } from "../../store/UserDetails";
import {useDispatch} from "react-redux"; import {useDispatch} from "react-redux";
// const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
// practice: Yup.string().required("Required"), practice: Yup.string().required("Required"),
// specialization: Yup.string().required("Required"), specialization: Yup.string().when('practice', {
// introduction: Yup.string().min(1, "Minimum 10 characters").max(50, "Maximum 50 characters").required("Required"), is: (value) => typeof value === 'string' && value.trim().length > 0,
// }) then: (schema) => schema.required('Required'),
otherwise: (schema) => schema,
// const initialValues = { }),
// practice: '', introduction: Yup.string().min(1, "Minimum 1 character").max(50, "Maximum 50 characters"),
// specialization: '', url_name: Yup.string().min(6, "Minimum 6 characters").max(16, "Maximum 16 characters").required("Required").matches(
// introduction: '', /^[a-zA-Z0-9]+$/, // Regex for alphanumeric characters
// }; 'Must contain only alphanumeric characters' // Custom error message
),
})
export default function ProfileCompleteCom() { export default function ProfileCompleteCom() {
@@ -35,33 +37,34 @@ export default function ProfileCompleteCom(){
const {state: {redirectLink}} = useLocation() const {state: {redirectLink}} = useLocation()
const [practices, setPractices] = useState([]) const [practices, setPractices] = useState([])
const [specialties, setSpecialties] = useState([])
const [initialValues, setInitialValues] = useState({ const [initialValues, setInitialValues] = useState({
practice: '', practice: '',
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') { if (res.data.resultCode != '0') {
@@ -91,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)
} }
@@ -133,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>
@@ -143,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>
@@ -154,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) &&
<> <>
@@ -197,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>
@@ -207,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>
@@ -0,0 +1,202 @@
import React, { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { getPaymentReports } from "../../services/services";
import queryKeys from "../../services/queryKeys";
export default function PaymentReportTable() {
const [page, setPage] = useState(0)
const {data, isFetching, isError, error} = useQuery({
queryKey: [...queryKeys.payment_report, page],
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
page
}
return getPaymentReports(reqData)
}
})
console.log('DATA', data?.data)
return (
<>
<div className="row">
<div className="col-md-12 m-b-30">
<div className="d-block d-sm-flex flex-nowrap align-items-center">
<div className="page-title mb-2 mb-sm-0">
<h4>Payments Report</h4>
</div>
</div>
</div>
</div>
<div className="row">
{isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div className="col-lg-12">
<div className="card card-statistics">
<div className="card-body">
<div className="export-table-wrapper table-responsive">
<table id="export-table" className="table table-bordered">
<thead className="thead-light">
<tr>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tiger Nixon</td>
<td>System Architect</td>
<td>Edinburgh</td>
<td>61</td>
<td>2011/04/25</td>
<td>$320,800</td>
</tr>
<tr>
<td>Garrett Winters</td>
<td>Accountant</td>
<td>Tokyo</td>
<td>63</td>
<td>2011/07/25</td>
<td>$170,750</td>
</tr>
<tr>
<td>Ashton Cox</td>
<td>Junior Technical Author</td>
<td>San Francisco</td>
<td>66</td>
<td>2009/01/12</td>
<td>$86,000</td>
</tr>
<tr>
<td>Cedric Kelly</td>
<td>Senior Javascript Developer</td>
<td>Edinburgh</td>
<td>22</td>
<td>2012/03/29</td>
<td>$433,060</td>
</tr>
<tr>
<td>Airi Satou</td>
<td>Accountant</td>
<td>Tokyo</td>
<td>33</td>
<td>2008/11/28</td>
<td>$162,700</td>
</tr>
<tr>
<td>Brielle Williamson</td>
<td>Integration Specialist</td>
<td>New York</td>
<td>61</td>
<td>2012/12/02</td>
<td>$372,000</td>
</tr>
<tr>
<td>Herrod Chandler</td>
<td>Sales Assistant</td>
<td>San Francisco</td>
<td>59</td>
<td>2012/08/06</td>
<td>$137,500</td>
</tr>
<tr>
<td>Rhona Davidson</td>
<td>Integration Specialist</td>
<td>Tokyo</td>
<td>55</td>
<td>2010/10/14</td>
<td>$327,900</td>
</tr>
<tr>
<td>Colleen Hurst</td>
<td>Javascript Developer</td>
<td>San Francisco</td>
<td>39</td>
<td>2009/09/15</td>
<td>$205,500</td>
</tr>
<tr>
<td>Sonya Frost</td>
<td>Software Engineer</td>
<td>Edinburgh</td>
<td>23</td>
<td>2008/12/13</td>
<td>$103,600</td>
</tr>
<tr>
<td>Jena Gaines</td>
<td>Office Manager</td>
<td>London</td>
<td>30</td>
<td>2008/12/19</td>
<td>$90,560</td>
</tr>
<tr>
<td>Quinn Flynn</td>
<td>Support Lead</td>
<td>Edinburgh</td>
<td>22</td>
<td>2013/03/03</td>
<td>$342,000</td>
</tr>
<tr>
<td>Charde Marshall</td>
<td>Regional Director</td>
<td>San Francisco</td>
<td>36</td>
<td>2008/10/16</td>
<td>$470,600</td>
</tr>
<tr>
<td>Haley Kennedy</td>
<td>Senior Marketing Designer</td>
<td>London</td>
<td>43</td>
<td>2012/12/18</td>
<td>$313,500</td>
</tr>
<tr>
<td>Tatyana Fitzpatrick</td>
<td>Regional Director</td>
<td>London</td>
<td>19</td>
<td>2010/03/17</td>
<td>$385,750</td>
</tr>
<tr>
<td>Michael Silva</td>
<td>Marketing Designer</td>
<td>London</td>
<td>66</td>
<td>2012/11/27</td>
<td>$198,500</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
}
</div>
</>
)
}
@@ -0,0 +1,205 @@
import React, { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { getProductReports } from "../../services/services";
import queryKeys from "../../services/queryKeys";
export default function ProductReportTable() {
const [page, setPage] = useState(0)
const {data, isFetching, isError, error} = useQuery({
queryKey: [...queryKeys.product_report, page],
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
page
}
return getProductReports(reqData)
}
})
console.log('DATA', data?.data)
return (
<>
<div className="row">
<div className="col-md-12 m-b-30">
<div className="d-block d-sm-flex flex-nowrap align-items-center">
<div className="page-title mb-2 mb-sm-0">
<h4>Products Report</h4>
</div>
</div>
</div>
</div>
<div className="row">
{isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div className="col-lg-12">
<div className="card card-statistics">
<div className="card-body">
<div className="export-table-wrapper table-responsive">
<table id="export-table" className="table table-bordered">
<thead className="thead-light">
<tr>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tiger Nixon</td>
<td>System Architect</td>
<td>Edinburgh</td>
<td>61</td>
<td>2011/04/25</td>
<td>$320,800</td>
</tr>
<tr>
<td>Garrett Winters</td>
<td>Accountant</td>
<td>Tokyo</td>
<td>63</td>
<td>2011/07/25</td>
<td>$170,750</td>
</tr>
<tr>
<td>Ashton Cox</td>
<td>Junior Technical Author</td>
<td>San Francisco</td>
<td>66</td>
<td>2009/01/12</td>
<td>$86,000</td>
</tr>
<tr>
<td>Cedric Kelly</td>
<td>Senior Javascript Developer</td>
<td>Edinburgh</td>
<td>22</td>
<td>2012/03/29</td>
<td>$433,060</td>
</tr>
<tr>
<td>Airi Satou</td>
<td>Accountant</td>
<td>Tokyo</td>
<td>33</td>
<td>2008/11/28</td>
<td>$162,700</td>
</tr>
<tr>
<td>Brielle Williamson</td>
<td>Integration Specialist</td>
<td>New York</td>
<td>61</td>
<td>2012/12/02</td>
<td>$372,000</td>
</tr>
<tr>
<td>Herrod Chandler</td>
<td>Sales Assistant</td>
<td>San Francisco</td>
<td>59</td>
<td>2012/08/06</td>
<td>$137,500</td>
</tr>
<tr>
<td>Rhona Davidson</td>
<td>Integration Specialist</td>
<td>Tokyo</td>
<td>55</td>
<td>2010/10/14</td>
<td>$327,900</td>
</tr>
<tr>
<td>Colleen Hurst</td>
<td>Javascript Developer</td>
<td>San Francisco</td>
<td>39</td>
<td>2009/09/15</td>
<td>$205,500</td>
</tr>
<tr>
<td>Sonya Frost</td>
<td>Software Engineer</td>
<td>Edinburgh</td>
<td>23</td>
<td>2008/12/13</td>
<td>$103,600</td>
</tr>
<tr>
<td>Jena Gaines</td>
<td>Office Manager</td>
<td>London</td>
<td>30</td>
<td>2008/12/19</td>
<td>$90,560</td>
</tr>
<tr>
<td>Quinn Flynn</td>
<td>Support Lead</td>
<td>Edinburgh</td>
<td>22</td>
<td>2013/03/03</td>
<td>$342,000</td>
</tr>
<tr>
<td>Charde Marshall</td>
<td>Regional Director</td>
<td>San Francisco</td>
<td>36</td>
<td>2008/10/16</td>
<td>$470,600</td>
</tr>
<tr>
<td>Haley Kennedy</td>
<td>Senior Marketing Designer</td>
<td>London</td>
<td>43</td>
<td>2012/12/18</td>
<td>$313,500</td>
</tr>
<tr>
<td>Tatyana Fitzpatrick</td>
<td>Regional Director</td>
<td>London</td>
<td>19</td>
<td>2010/03/17</td>
<td>$385,750</td>
</tr>
<tr>
<td>Michael Silva</td>
<td>Marketing Designer</td>
<td>London</td>
<td>66</td>
<td>2012/11/27</td>
<td>$198,500</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
}
</div>
</>
)
}
+79 -1
View File
@@ -1,14 +1,92 @@
import React from "react"; import React from "react";
import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS";
import {useQuery} from "@tanstack/react-query";
import queryKeys from "../../services/queryKeys";
import {getReportsTopicsList} from "../../services/services";
import PaymentReportTable from "./PaymentReportTable";
import ProductReportTable from "./ProductReportTable";
import sortArrayByListOrder from "../../helpers/sortArrayByLIstOrder";
import SystemReportTable from "./SystemReportTable";
export default function Reports() { export default function Reports() {
const {data, isFetching, isError, error} = useQuery({
queryKey: queryKeys.topics,
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid') // USER UID
}
return getReportsTopicsList(reqData)
}
})
const reportTopicList = data?.data?.topics?.topics;
const renderedTable = {
payment: <PaymentReportTable />,
product: <ProductReportTable />,
system: <SystemReportTable />
}
const sortedReportTopicList= sortArrayByListOrder(reportTopicList ? reportTopicList : []) // SORTED SETTINGSCONFIG
return ( return (
<> <>
<BreadcrumbComBS title='Reports' paths={['Dashboard', 'Reports']}/> <BreadcrumbComBS title='Reports' paths={['Dashboard', 'Reports']}/>
<div className="row"> <div className="row">
<div className="vh-100 col-12 flex align-items-center">Coming Soon</div> {isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div>
<div>
<div className="card card-statistics" style={{minHeight: '550px'}}>
{/*<div className="card-header">*/}
{/* <div className="card-heading">*/}
{/* <h4 className="card-title"> Tab vertical </h4>*/}
{/* </div>*/}
{/*</div>*/}
<div className="card-body">
<div className="tab tab-vertical">
{/* <!-- Tabs --> */}
<ul className="nav nav-tabs" id="myTab" role="tablist">
{sortedReportTopicList && sortedReportTopicList.map((item, index) => {
return (
<li key={index} className="nav-item" role="presentation">
<a className={`nav-link ${index == 0 && 'active'}`} id={`tab-${item?.url}`} href="#" data-bs-toggle="tab" data-bs-target={`#content-${item?.url}`} type="button" role="tab">
{item?.name}
</a>
</li>
)
})}
</ul>
{/* <!-- Tab Content --> */}
<div className="tab-content">
{sortedReportTopicList && sortedReportTopicList.map((item, index) => {
return (
<div key={index} className={`tab-pane fade show ${index == 0 && 'active'}`} id={`content-${item?.url}`} role="tabpanel">
{renderedTable[item?.url]}
</div>
)
})}
</div>
</div>
</div>
</div>
</div>
</div>
}
</div> </div>
</> </>
) )
+205
View File
@@ -0,0 +1,205 @@
import React, { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { getSystemReports } from "../../services/services";
import queryKeys from "../../services/queryKeys";
export default function SystemReportTable() {
const [page, setPage] = useState(0)
const {data, isFetching, isError, error} = useQuery({
queryKey: [...queryKeys.system_report, page],
queryFn: () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
page
}
return getSystemReports(reqData)
},
})
console.log('DATA', data?.data)
return (
<>
<div className="row">
<div className="col-md-12 m-b-30">
<div className="d-block d-sm-flex flex-nowrap align-items-center">
<div className="page-title mb-2 mb-sm-0">
<h4>Systems Report</h4>
</div>
</div>
</div>
</div>
<div className="row">
{isFetching ?
<>
<div className="col-12">
<p className='text-mute'>Loading...</p>
</div>
</>
: isError ?
<div className="col-12">
<p className='text-danger'>{error?.message}</p>
</div>
:
<div className="col-lg-12">
<div className="card card-statistics">
<div className="card-body">
<div className="export-table-wrapper table-responsive">
<table id="export-table" className="table table-bordered">
<thead className="thead-light">
<tr>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tiger Nixon</td>
<td>System Architect</td>
<td>Edinburgh</td>
<td>61</td>
<td>2011/04/25</td>
<td>$320,800</td>
</tr>
<tr>
<td>Garrett Winters</td>
<td>Accountant</td>
<td>Tokyo</td>
<td>63</td>
<td>2011/07/25</td>
<td>$170,750</td>
</tr>
<tr>
<td>Ashton Cox</td>
<td>Junior Technical Author</td>
<td>San Francisco</td>
<td>66</td>
<td>2009/01/12</td>
<td>$86,000</td>
</tr>
<tr>
<td>Cedric Kelly</td>
<td>Senior Javascript Developer</td>
<td>Edinburgh</td>
<td>22</td>
<td>2012/03/29</td>
<td>$433,060</td>
</tr>
<tr>
<td>Airi Satou</td>
<td>Accountant</td>
<td>Tokyo</td>
<td>33</td>
<td>2008/11/28</td>
<td>$162,700</td>
</tr>
<tr>
<td>Brielle Williamson</td>
<td>Integration Specialist</td>
<td>New York</td>
<td>61</td>
<td>2012/12/02</td>
<td>$372,000</td>
</tr>
<tr>
<td>Herrod Chandler</td>
<td>Sales Assistant</td>
<td>San Francisco</td>
<td>59</td>
<td>2012/08/06</td>
<td>$137,500</td>
</tr>
<tr>
<td>Rhona Davidson</td>
<td>Integration Specialist</td>
<td>Tokyo</td>
<td>55</td>
<td>2010/10/14</td>
<td>$327,900</td>
</tr>
<tr>
<td>Colleen Hurst</td>
<td>Javascript Developer</td>
<td>San Francisco</td>
<td>39</td>
<td>2009/09/15</td>
<td>$205,500</td>
</tr>
<tr>
<td>Sonya Frost</td>
<td>Software Engineer</td>
<td>Edinburgh</td>
<td>23</td>
<td>2008/12/13</td>
<td>$103,600</td>
</tr>
<tr>
<td>Jena Gaines</td>
<td>Office Manager</td>
<td>London</td>
<td>30</td>
<td>2008/12/19</td>
<td>$90,560</td>
</tr>
<tr>
<td>Quinn Flynn</td>
<td>Support Lead</td>
<td>Edinburgh</td>
<td>22</td>
<td>2013/03/03</td>
<td>$342,000</td>
</tr>
<tr>
<td>Charde Marshall</td>
<td>Regional Director</td>
<td>San Francisco</td>
<td>36</td>
<td>2008/10/16</td>
<td>$470,600</td>
</tr>
<tr>
<td>Haley Kennedy</td>
<td>Senior Marketing Designer</td>
<td>London</td>
<td>43</td>
<td>2012/12/18</td>
<td>$313,500</td>
</tr>
<tr>
<td>Tatyana Fitzpatrick</td>
<td>Regional Director</td>
<td>London</td>
<td>19</td>
<td>2010/03/17</td>
<td>$385,750</td>
</tr>
<tr>
<td>Michael Silva</td>
<td>Marketing Designer</td>
<td>London</td>
<td>66</td>
<td>2012/11/27</td>
<td>$198,500</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
}
</div>
</>
)
}
+159
View File
@@ -0,0 +1,159 @@
import React, { useRef, useState } from 'react'
import { Form, Formik } from "formik";
import * as Yup from "yup";
import { Modal } from "bootstrap";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { updateLinks } from "../../services/services";
import queryKeys from '../../services/queryKeys';
const linksValidationSchema = Yup.object().shape({
// facebook_url: Yup.string().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 LinksForm({data}) {
const modalRef = useRef(null)
const queryClient = useQueryClient()
const [intialData] = useState(data)
const [infoToUpdate, setInfoToUpdate] = useState({})
const dismissModal = () => {
const body = document.querySelector('body')
body.removeAttribute('style')
// body.classList.toggle('modal-open')
const modalBackdrop = document.querySelectorAll('.modal-backdrop')
modalBackdrop.forEach(item => {
if (item) {
item.remove();
}
})
const modal = Modal.getInstance(modalRef.current);
modal && modal.hide();
};
// UPDATE LINKS MUTATION
const updateLinksMutation = useMutation({
mutationFn: (fields) => {
return updateLinks(fields)
},
onSuccess: (res) => {
if(res.data.resultCode != '0'){
throw({message: res?.data?.resultDescription ? res?.data?.resultDescription : 'En error occured'})
}
},
onSettled: ()=>{
setTimeout(() => {
dismissModal() //CLOSE MODAL HERE
queryClient.refetchQueries({
queryKey: [...queryKeys.profile_data], // type: 'active', // exact: true,
})
updateLinksMutation.reset()
}, 3000);
}
})
const handleSetInfoToUpdate = (values, helpers) => {
setInfoToUpdate(values)
var modal = new Modal(document.getElementById('modal_links'));
modal.show();
}
const proceed = () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
...infoToUpdate
}
console.log(reqData)
// updateLinksMutation.mutate(reqData)
}
return (
<>
<Formik
initialValues={intialData}
validationSchema={linksValidationSchema}
onSubmit={handleSetInfoToUpdate}
>
{(props) => {
return (
<Form className=''>
<div className="form-group">
<label htmlFor="fb">Facebook URL: {(props.errors.facebook_url && props.touched.facebook_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="facebook_url" value={props.values?.facebook_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="tr">Twitter URL: {(props.errors.twitter_url && props.touched.twitter_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="twitter_url" value={props.values?.twitter_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="br">Blogger URL: {(props.errors.blogger_url && props.touched.blogger_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="blogger_url" value={props.values?.blogger_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="go">Google+ URL: {(props.errors.google_url && props.touched.google_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="google_url" value={props.values?.google_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="li">LinkedIn URL: {(props.errors.linked_url && props.touched.linked_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="linked_url" value={props.values?.linked_url} onChange={props.handleChange} />
</div>
<div className="form-group">
<label htmlFor="we">Website URL: {(props.errors.website_url && props.touched.website_url) && <span className="text-danger">*</span>}</label>
<input type="text" className="form-control" name="website_url" value={props.values?.website_url} onChange={props.handleChange} />
</div>
<div style={{textAlign: "right"}}>
<button type="submit" className="btn btn-primary">Update Links
</button>
</div>
</Form>
);
}}
</Formik>
{/* Vertical Center Modal */}
<div ref={modalRef} className="modal fade" id="modal_links" tabIndex="-1" role="dialog" aria-hidden="true">
<div className="modal-dialog modal-dialog-centered" role="document">
<div className="modal-content">
<div className="modal-header">
{/* <h5 className="modal-title" style={{fontSize: '18px'}} id="verticalCenterTitle">{'productTitle'}</h5> */}
<button type="button" className="close" data-bs-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div className="modal-body">
<h5 className="text-center" style={{fontSize: '18px'}}>Are you sure, you want to update? gg</h5>
{(updateLinksMutation.error || updateLinksMutation.isSuccess) && (
<div className="col-12">
<p className={`p-2 text-center ${updateLinksMutation.isSuccess ? 'text-success' : 'text-danger'}`}>
{updateLinksMutation.isSuccess ? 'Updated Successfully' : updateLinksMutation.error.message}
</p>
</div>
)}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
<button type="button" className="btn btn-primary" disabled={updateLinksMutation.isSuccess} onClick={proceed}>{updateLinksMutation.isPending ? 'Updating...' : 'Update'}</button>
</div>
</div>
</div>
</div>
{/* END of Vertical Center Modal */}
</>
)
}
+164
View File
@@ -0,0 +1,164 @@
import React, { useRef, useState } from 'react'
import { Form, Formik } from "formik";
import * as Yup from "yup";
import { Modal } from "bootstrap";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { updateProfile } from "../../services/services";
import queryKeys from '../../services/queryKeys';
import { useDispatch } from 'react-redux';
import { updateUserDetails } from '../../store/UserDetails'
const profileValidationSchema = Yup.object().shape({
firstname: Yup.string().required("firstname is required"),
lastname: Yup.string().required("lastname is required"),
email: Yup.string().required("email is required"),
account_name: Yup.string().required("account name is required"),
phone: Yup.string().required("phone is required"),
full_address: Yup.string().required("full address is required"),
})
export default function ProfileForm({data}) {
const dispatch = useDispatch()
const modalRef = useRef(null)
const queryClient = useQueryClient()
const [intialData] = useState(data)
const [infoToUpdate, setInfoToUpdate] = useState({})
const dismissModal = () => {
const body = document.querySelector('body')
body.removeAttribute('style')
// body.classList.toggle('modal-open')
const modalBackdrop = document.querySelectorAll('.modal-backdrop')
modalBackdrop.forEach(item => {
if (item) {
item.remove();
}
})
const modal = Modal.getInstance(modalRef.current);
modal && modal.hide();
};
// UPDATE PROFILE MUTATION
const updateProfileMutation = useMutation({
mutationFn: (fields) => {
return updateProfile(fields)
},
onSuccess: (res) => {
if(res.data.resultCode != '0'){
throw({message: res?.data?.resultDescription ? res?.data?.resultDescription : 'An error occured'})
}
const account_name = res?.data?.personal_data?.account_name
dispatch(updateUserDetails({ account_name }));
},
onSettled: ()=>{
setTimeout(() => {
dismissModal() //CLOSE MODAL HERE
queryClient.refetchQueries({
queryKey: [...queryKeys.profile_data], // type: 'active', // exact: true,
})
updateProfileMutation.reset()
}, 3000);
}
})
const handleSetInfoToUpdate = (values, helpers) => {
delete values.email
delete values.country
setInfoToUpdate(values)
var modal = new Modal(document.getElementById('modal'));
modal.show();
}
const proceed = () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
...infoToUpdate
}
updateProfileMutation.mutate(reqData)
}
return (
<>
<Formik
initialValues={intialData}
validationSchema={profileValidationSchema}
onSubmit={handleSetInfoToUpdate}
>
{(props) => {
return (
<Form className=''>
<div className="form-row">
<div className="form-group col-md-12">
<label htmlFor="name1">First Name {(props.errors.firstname && props.touched.firstname) && <span className="text-danger">{props.errors.firstname}*</span>}</label>
<input type="text" className="form-control" name="firstname" value={props.values?.firstname} onChange={props.handleChange} />
</div>
<div className="form-group col-md-12">
<label htmlFor="name1">Last Name {(props.errors.lastname && props.touched.lastname) && <span className="text-danger">{props.errors.lastname}*</span>}</label>
<input type="text" className="form-control" name="lastname" value={props.values?.lastname} onChange={props.handleChange} />
</div>
<div className="form-group col-md-12">
<label htmlFor="name1">Account Name {(props.errors.account_name && props.touched.account_name) && <span className="text-danger">{props.errors.account_name}*</span>}</label>
<input type="text" className="form-control" name="account_name" value={props.values?.account_name} onChange={props.handleChange} />
</div>
<div className="form-group col-md-12">
<label htmlFor="phone1">Phone Number {(props.errors.phone && props.touched.phone) && <span className="text-danger">{props.errors.phone}*</span>}</label>
<input type="text" className="form-control" name="phone" value={props.values?.phone} onChange={props.handleChange} />
</div>
<div className="form-group col-md-12">
<label htmlFor="email1">Email {(props.errors.email && props.touched.email) && <span className="text-danger">{props.errors.email}*</span>}</label>
<input type="text" className="form-control" name="email" readOnly value={props.values?.email} onChange={props.handleChange} />
</div>
</div>
<div className="form-group">
<label htmlFor="add1">Address {(props.errors.full_address && props.touched.full_address) && <span className="text-danger">{props.errors.full_address}*</span>}</label>
<input type="text" className="form-control" name="full_address" value={props.values?.full_address} onChange={props.handleChange} />
</div>
<div style={{textAlign: "right"}}>
<button type="submit" className="btn btn-primary">Update Profile
</button>
</div>
</Form>
);
}}
</Formik>
{/* Vertical Center Modal */}
<div ref={modalRef} className="modal fade" id="modal" tabIndex="-1" role="dialog" aria-hidden="true">
<div className="modal-dialog modal-dialog-centered" role="document">
<div className="modal-content">
<div className="modal-header">
{/* <h5 className="modal-title" style={{fontSize: '18px'}} id="verticalCenterTitle">{'productTitle'}</h5> */}
<button type="button" className="close" data-bs-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div className="modal-body">
<h5 className="text-center" style={{fontSize: '18px'}}>Are you sure, you want to update?</h5>
{(updateProfileMutation.error || updateProfileMutation.isSuccess) && (
<div className="col-12">
<p className={`p-2 text-center ${updateProfileMutation.isSuccess ? 'text-success' : 'text-danger'}`}>
{updateProfileMutation.isSuccess ? 'Updated Successfully' : updateProfileMutation.error.message}
</p>
</div>
)}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
<button type="button" className="btn btn-primary" disabled={updateProfileMutation.isSuccess} onClick={proceed}>{updateProfileMutation.isPending ? 'Updating...' : 'Update'}</button>
</div>
</div>
</div>
</div>
{/* END of Vertical Center Modal */}
</>
)
}
+117
View File
@@ -0,0 +1,117 @@
import React, { memo, useRef, useState } from 'react'
// import { useSelector } from 'react-redux';
import getImage from '../../utils/getImage';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { uploadProfileImg } from '../../services/services';
import queryKeys from '../../services/queryKeys';
const ProfileImage = memo(({intialData}) => {
const queryClient = useQueryClient()
const [selectedImg, setSelectedImg] = useState(null)
// const {userDetails} = useSelector((state) => state?.userDetails); // CHECKS FOR ACTIVE USER DETAILS
const avtarImage = "avtar/merms-user.png";
// browser profile img
const browserImg = useRef(null);
const browseProfileImg = () => {
browserImg.current.click();
};
const profileImgChangeHandler = (event) => {
setSelectedImg(event.target.files[0])
}
const uploadProfileMutation = useMutation({
mutationFn: (fields) => {
if(!fields.img){
throw new Error('Please, select an image')
}
return uploadProfileImg(fields)
},
onSuccess: (res) => {
if(res.data.resultCode != '0'){
throw({message: res?.data?.resultDescription ? res?.data?.resultDescription : 'An error occured'})
}
// const account_name = res?.data?.personal_data?.account_name
// dispatch(updateUserDetails({ account_name }));
},
onSettled: ()=>{
setTimeout(() => {
queryClient.refetchQueries({
queryKey: [...queryKeys.profile_data], // type: 'active', // exact: true,
})
uploadProfileMutation.reset()
}, 3000);
}
})
const proceedToUpload = () => {
let reqData = {
token: localStorage.getItem('token'), // USER TOKEN
uid: localStorage.getItem('uid'), // USER UID
img: selectedImg
}
// console.log('reqData', reqData)
uploadProfileMutation.mutate(reqData)
}
return (
<div className="col-xl-3 pb-xl-0 pb-5 border-right">
<div className="page-account-profil pt-5">
<div className="profile-img text-center rounded-circle">
<div className="pt-5">
<div className="bg-img m-auto">
<img
src={selectedImg ? URL.createObjectURL(selectedImg) : intialData?.personal_data?.picture ? intialData?.personal_data?.picture : getImage(avtarImage)}
// src={getImage(avtarImage)}
className="img-fluid" alt="user"
/>
<input
ref={browserImg}
className='d-none'
type='file'
accept="image/*"
onChange={(e) => profileImgChangeHandler(e)}
id='profile-image'
/>
</div>
<div className="profile pt-4">
<h4 className="mb-1">{intialData?.personal_data?.lastname} {intialData?.personal_data?.firstname}</h4>
<div style={{padding: '10px'}}>
<hr/>
</div>
</div>
</div>
</div>
<div className="profile-btn text-center">
<div>
<button onClick={browseProfileImg} className="btn btn-light text-primary mb-2">Change
</button>
</div>
{selectedImg &&
<div>
<button onClick={proceedToUpload} disabled={uploadProfileMutation.isSuccess || uploadProfileMutation.isPending} className="btn btn-light text-primary mb-2">
{uploadProfileMutation.isPaused ? 'Upload...' : 'Upload New Avatar'}
</button>
</div>
}
{/* success or error message */}
{(uploadProfileMutation.isSuccess || uploadProfileMutation.isError) &&
<div>
<p className={`${uploadProfileMutation.isSuccess ? 'text-success' : 'text-danger'}`}>
{uploadProfileMutation.isSuccess ? 'Uploaded successfully' : uploadProfileMutation.error.message}
</p>
</div>
}
</div>
</div>
</div>
)
}
)
export default ProfileImage
+49 -181
View File
@@ -1,9 +1,37 @@
import React from "react"; import React, { useMemo, useState } from "react";
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";
import ProfileForm from "./ProfileForm";
import LinksForm from "./LinksForm";
import ProfileImage from "./ProfileImage";
export default function Settings() { export default function Settings() {
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)
return ( return (
<> <>
<BreadcrumbComBS title='Settings' paths={['Dashboard', 'Settings']}/> <BreadcrumbComBS title='Settings' paths={['Dashboard', 'Settings']}/>
@@ -11,155 +39,34 @@ 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">
<div className="card-body p-0" style={{backgroundColor: "#f9f9fb"}}> <div className="card-body p-0" style={{backgroundColor: "#f9f9fb"}}>
<div className="row no-gutters"> <div className="row no-gutters">
<div className="col-xl-3 pb-xl-0 pb-5 border-right"> <ProfileImage intialData={intialData} />
<div className="page-account-profil pt-5">
<div className="profile-img text-center rounded-circle">
<div className="pt-5">
<div className="bg-img m-auto">
<img src="assets/img/avtar/01.jpg" className="img-fluid"
alt="users-avatar" />
</div>
<div className="profile pt-4">
<h4 className="mb-1">Alice Williams</h4>
<p>Enthusiast</p>
</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 className="profile-btn text-center">
<div>
<button className="btn btn-light text-primary mb-2">Upload New Avatar
</button>
</div>
{/*<div>*/}
{/* <button className="btn btn-danger">Delete</button>*/}
{/*</div>*/}
</div>
</div>
</div>
<div className="col-xl-5 col-md-6 col-12 border-t border-right"> <div className="col-xl-5 col-md-6 col-12 border-t border-right">
<div className="page-account-form"> <div className="page-account-form">
<div className="form-titel border-bottom p-3"> <div className="form-titel border-bottom p-3">
<h5 className="mb-0 py-2">Edit Your Personal Settings</h5> <h5 className="mb-0 py-2">Edit Your Personal Settings</h5>
</div> </div>
<div className="p-4"> <div className="p-4">
<form> <ProfileForm data={intialData.personal_data} />
<div className="form-row">
<div className="form-group col-md-12">
<label htmlFor="name1">First Name</label>
<input type="text" className="form-control" id="name1"
value="Alice" />
</div>
<div className="form-group col-md-12">
<label htmlFor="name1">Last Name</label>
<input type="text" className="form-control" id="name1"
value="Williams" />
</div>
<div className="form-group col-md-12">
<label htmlFor="name1">Account Name</label>
<input type="text" className="form-control" id="name1"
value="This is the best hospital name" />
</div>
{/*<div className="form-group col-md-12">*/}
{/* <label htmlFor="title1">Email</label>*/}
{/* <input type="text" className="form-control" id="title1"*/}
{/* value="email@email.com" />*/}
{/*</div>*/}
<div className="form-group col-md-12">
<label htmlFor="phone1">Phone Number</label>
<input type="text" className="form-control" id="phone1"
value="(01) 97 563 15613" />
</div>
<div className="form-group col-md-12">
<label htmlFor="email1">Email</label>
<input type="email" className="form-control" id="email1"
value="alicewilliams@gmail.com" />
</div>
</div>
<div className="form-group">
<label htmlFor="add1">Address</label>
<input type="text" className="form-control" id="add1"
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 className="form-row">*/}
{/* <div className="form-group col-md-4">*/}
{/* <label htmlFor="inputState3">City</label>*/}
{/* <select id="inputState3" className="form-control">*/}
{/* <option>Choose...</option>*/}
{/* <option selected="">London</option>*/}
{/* <option>Montreal</option>*/}
{/* <option>Delhi</option>*/}
{/* <option>Tokyo</option>*/}
{/* </select>*/}
{/* </div>*/}
{/* <div className="form-group col-md-4">*/}
{/* <label htmlFor="inputState4">State</label>*/}
{/* <select id="inputState4" className="form-control">*/}
{/* <option>Choose...</option>*/}
{/* <option selected="">England</option>*/}
{/* <option>California</option>*/}
{/* <option>Texas</option>*/}
{/* <option>Scotland</option>*/}
{/* </select>*/}
{/* </div>*/}
{/* <div className="form-group col-md-4">*/}
{/* <label htmlFor="inputZip">Zip</label>*/}
{/* <input type="text" className="form-control" id="inputZip"*/}
{/* value="EC1A 1BB" />*/}
{/* </div>*/}
{/*</div>*/}
{/*<div className="form-group">*/}
{/* <div className="form-check">*/}
{/* <input className="form-check-input" type="checkbox"*/}
{/* id="gridCheck" />*/}
{/* <label className="form-check-label" htmlFor="gridCheck">*/}
{/* I agree to receive email notification.*/}
{/* </label>*/}
{/* </div>*/}
{/*</div>*/}
<div style={{textAlign:"right"}}>
<button type="submit" className="btn btn-primary">Update Profile
</button>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>
@@ -169,46 +76,7 @@ export default function Settings(){
<h5 className="mb-0 py-2">Your External Link</h5> <h5 className="mb-0 py-2">Your External Link</h5>
</div> </div>
<div className="p-4"> <div className="p-4">
<form> <LinksForm data={intialData.external_links} />
<div className="form-group">
<label htmlFor="fb">Facebook URL:</label>
<input type="text" className="form-control" id="fb"
value="https://www.facebook.com/" />
</div>
<div className="form-group">
<label htmlFor="tr">Twitter URL:</label>
<input type="text" className="form-control" id="tr"
value="https://twitter.com/" />
</div>
<div className="form-group">
<label htmlFor="br">Blogger URL:</label>
<input type="text" className="form-control" id="br"
value="https://www.blogger.com" />
</div>
<div className="form-group">
<label htmlFor="go">Google+ URL:</label>
<input type="text" className="form-control" id="go"
value="https://plus.google.com/discover" />
</div>
<div className="form-group">
<label htmlFor="li">LinkedIn URL:</label>
<input type="text" className="form-control" id="li"
value="https://in.linkedin.com/" />
</div>
<div className="form-group">
<label htmlFor="we">Website URL:</label>
<input type="text" className="form-control" id="we"
value="https://yourwebsite.com" />
</div>
<div style={{textAlign:"right"}}>
<button type="submit" className="btn btn-primary">Update Links</button>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>
@@ -217,7 +85,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>
</>
)
}
+13 -5
View File
@@ -6,7 +6,7 @@ import {useQuery} from '@tanstack/react-query';
import queryKeys from '../../services/queryKeys'; import queryKeys from '../../services/queryKeys';
import siteLinks from "../../links/siteLinks"; import siteLinks from "../../links/siteLinks";
import {Link, useNavigate} from 'react-router-dom' import {Link, useNavigate} from 'react-router-dom'
import getDateFromDateString from '../../helpers/GetDateFromDateString'; import getDateTimeFromDateString from '../../helpers/getDateTimeFromDateString';
export default function Subscription() { export default function Subscription() {
const navigate = useNavigate() const navigate = useNavigate()
@@ -57,8 +57,8 @@ export default function Subscription() {
<h2 className="text-primary pt-3">{currentSubscription?.display_name}</h2> <h2 className="text-primary pt-3">{currentSubscription?.display_name}</h2>
</div> </div>
<div className="pt-2" style={{textAlign: 'left'}}> <div className="pt-2" style={{textAlign: 'left'}}>
<div style={{fontSize: '10px'}}> <div style={{fontSize: '12px', fontWeight: 'bolder', color: "#3E3699"}}>
Next Payment: {getDateFromDateString(currentSubscription?.next_payment)} Next Payment: {getDateTimeFromDateString(currentSubscription?.next_payment)}
</div> </div>
</div> </div>
</div> </div>
@@ -84,11 +84,19 @@ export default function Subscription() {
</div> </div>
</div> </div>
<div className="pt-2"> <div className="pt-2" style={{fontWeight: 'bolder'}}>
{
(currentSubscription?.display_name == value.option_name) ? 'Current Subscription' :
<button onClick={() => { <button onClick={() => {
navigate(siteLinks.subscribe, {state: {selectedSubscription: value, customerId: stripe_customer_id }}) navigate(siteLinks.subscribe, {
state: {
selectedSubscription: value,
customerId: stripe_customer_id
}
})
}} }}
className="btn btn-inverse-secondary btn-round btn-sm">Go {value.display_name}</button> 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
+2
View File
@@ -2063,6 +2063,8 @@ ul.activity {
.img-icon{ .img-icon{
width:60px; width:60px;
height:60px; height:60px;
min-width:60px;
min-height:60px;
border-radius:100px; border-radius:100px;
text-align: center; text-align: center;
line-height: 60px; line-height: 60px;
+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
@@ -1,4 +1,4 @@
function getDateFromDateString(dateString) { function getDateTimeFromDateString(dateString) {
const date = new Date(dateString); const date = new Date(dateString);
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
@@ -30,4 +30,4 @@ function getDateFromDateString(dateString) {
return `${dayName}, ${monthName} ${day}${getOrdinal(day)} ${year} ${hours}:${minutes}${ampm}`; return `${dayName}, ${monthName} ${day}${getOrdinal(day)} ${year} ${hours}:${minutes}${ampm}`;
} }
export default getDateFromDateString export default getDateTimeFromDateString
+17
View File
@@ -0,0 +1,17 @@
function sortArrayByListOrder(data) {
// Convert object to array if needed
const array = Array.isArray(data)
? [...data]
: Object.values(data);
// Sort ascending by list_order
array.sort((a, b) => {
const orderA = a.list_order ?? 0;
const orderB = b.list_order ?? 0;
return orderA - orderB;
});
return array;
}
export default sortArrayByListOrder
+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
+3
View File
@@ -3,12 +3,15 @@ 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',
my_media: '/my-files',
subscription_success:'/subscription-success', subscription_success:'/subscription-success',
subscribe: '/subscribe', subscribe: '/subscribe',
onboard: '/subscription', onboard: '/subscription',
+9
View File
@@ -1,13 +1,22 @@
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'],
myTemplateConfig: ['mytemplate_config'],
productTemplateData: ['product_template_data'], productTemplateData: ['product_template_data'],
subscriptions: ['subscriptions'], subscriptions: ['subscriptions'],
profile_data: ['profile_data'],
my_files: ['my_files'],
topics: ['topics'],
payment_report: ['payment_report'],
product_report: ['product_report'],
system_report: ['system_report'],
dashboard: ['dashboard'], dashboard: ['dashboard'],
topBar: ['top-bar'], topBar: ['top-bar'],
+154 -5
View File
@@ -1,10 +1,9 @@
import axios from "axios" import axios from "axios"
axios.interceptors.request.use( axios.interceptors.request.use(
config => { config => {
config.headers = { config.headers = {
Accept: "application/json", // Accept: "application/json",
"Access-Control-Allow-Origin": "*", "Access-Control-Allow-Origin": "*",
// "Access-Control-Expose-Headers": "Access-Control-Allow-Origin", // "Access-Control-Expose-Headers": "Access-Control-Allow-Origin",
// "Access-Control-Allow-Headers": "Origin, X-API-KEY, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method, Access-Control-Allow-Headers, Authorization, observe, enctype, Content-Length, X-Csrf-Token", // "Access-Control-Allow-Headers": "Origin, X-API-KEY, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method, Access-Control-Allow-Headers, Authorization, observe, enctype, Content-Length, X-Csrf-Token",
@@ -23,10 +22,20 @@ axios.interceptors.request.use(
const postAuxEnd = (path, postData, media=false) => { const postAuxEnd = (path, postData, media=false) => {
const basePath = media ? process.env.REACT_APP_MAIN_API : process.env.REACT_APP_MAIN_API const basePath = media ? process.env.REACT_APP_MAIN_API : process.env.REACT_APP_MAIN_API
return axios.post(`${basePath}${path}`, postData).then(res => { let newPostData = {}
if(!media){
newPostData = {...postData}
}else{
newPostData = new FormData();
for (let data in postData) {
newPostData.append(data, postData[data]);
}
}
return axios.post(`${basePath}${path}`, newPostData).then(res => {
return res return res
}).catch(err => { }).catch(err => {
throw new Error(err.response.data.msg); // throw new Error(err.response.data.error_message);
throw new Error(err);
}) })
} }
@@ -75,6 +84,30 @@ 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 UPDATE PROFILE
export const updateProfile = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/profile-update`, postData, false)
}
// FUNCTION TO UPDATE LINKS
export const updateLinks = (reqData) => {
let postData = {
...reqData,
}
return null //postAuxEnd(`/panel/account/links-update`, postData, false)
}
// FUNCTION TO GET PRODUCT BY ID // FUNCTION TO GET PRODUCT BY ID
export const MyProductData = (reqData) => { export const MyProductData = (reqData) => {
let postData = { let postData = {
@@ -98,7 +131,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,
@@ -106,6 +139,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 = {
@@ -114,6 +155,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 = {
@@ -173,6 +222,14 @@ 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 // FUNCTION TO GET SETTINGS DATA
export const getProductTemplateData = (reqData) => { export const getProductTemplateData = (reqData) => {
let postData = { let postData = {
@@ -181,6 +238,36 @@ export const getProductTemplateData = (reqData) => {
return postAuxEnd(`/panel/account/products/templates`, postData, false) return postAuxEnd(`/panel/account/products/templates`, postData, false)
} }
export const getProductColorStyles = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/products/color-styles`, postData, false)
}
export const getTemplateConfig = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/myproduct/template-config`, postData, false)
}
// FUNCTION TO ACTIVATE TEMPLATE
export const activateTemplate = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/template/activate`, postData, false)
}
export const activateColorStyle = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/colorstyle/activate`, postData, false)
}
// FUNCTION TO GET PRODUCT SUBSCRIPTIONS // FUNCTION TO GET PRODUCT SUBSCRIPTIONS
export const completeProfile = (reqData) => { export const completeProfile = (reqData) => {
let postData = { let postData = {
@@ -197,6 +284,51 @@ export const getSubscriptions = (reqData) => {
return postAuxEnd(`/panel/subscription/products`, postData, false) return postAuxEnd(`/panel/subscription/products`, postData, false)
} }
export const getMediaFileList = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/account/media-files`, postData, false)
}
export const uploadFile = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/upload/webfiles`, postData, true)
}
export const getReportsTopicsList = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/report/topics`, postData, false)
}
// FUNCTION TO GET PAYMENT REPORTS
export const getPaymentReports = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/report/item/payment`, postData, false)
}
// FUNCTION TO GET PRODUCT REPORTS
export const getProductReports = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/report/item/product`, postData, false)
}
// FUNCTION TO GET SYSTEM REPORTS
export const getSystemReports = (reqData) => {
let postData = {
...reqData,
}
return postAuxEnd(`/panel/report/item/system`, postData, false)
}
// FUNCTION TO GET COMMON PRACTICE // FUNCTION TO GET COMMON PRACTICE
export const getCommonPractice = (reqData) => { export const getCommonPractice = (reqData) => {
let postData = { let postData = {
@@ -205,6 +337,23 @@ export const getCommonPractice = (reqData) => {
return postAuxEnd(`/panel/common/practice`, postData, false) return postAuxEnd(`/panel/common/practice`, postData, false)
} }
// FUNCTION TO SET EXTERNAL URL
export const setExternalURL = (reqData) => {
let postData = {
...reqData
}
return postAuxEnd('/panel/myproduct/external-url', postData, false)
}
// FUNCTION TO UPLOAD PROFILE IMAGE
export const uploadProfileImg = (reqData) => {
let postData = {
...reqData,
}
throw new Error('Opps')
// return postAuxEnd(`/panel/account/profile-update`, postData, true)
}
+8
View File
@@ -0,0 +1,8 @@
import React from 'react'
import MyMedia from "../component/mymedia/MyMedia";
export default function MyMediaPage() {
return (
<MyMedia />
)
}
+7
View File
@@ -0,0 +1,7 @@
import Start from "../component/start/Start";
export default function StartPage(){
return <Start />
}
+8
View File
@@ -0,0 +1,8 @@
import React from 'react'
import Traffic from "../component/traffic/Traffic";
export default function TrafficPage() {
return (
<Traffic />
)
}