Compare commits

...

134 Commits

Author SHA1 Message Date
victorAnumudu be8460c20b login bug fixed 2026-05-11 18:08:43 +01:00
ameye 0794dfb8e0 templAte fix 2026-05-03 13:55:52 -04:00
ameye d950988e62 Clean code 2026-04-26 14:22:19 -04:00
ameye 295473d416 Commnented Image 2026-04-26 14:19:47 -04:00
ameye acc2c6eab4 comment fixed node version 2026-04-26 04:50:01 -04:00
ameye 9f6db5d13d removed yarn 2026-04-26 03:42:26 -04:00
ameye 71e66d3783 FROM alpine:3.22 2026-04-26 03:32:14 -04:00
ameye 2c7085b04b Fix messages 2026-04-26 03:14:54 -04:00
ameye 2c7584c00f Merge branch 'signup-endpoint-fix' of MERMS/MermsPanelReactJS into master 2026-04-22 21:35:50 +00:00
victorAnumudu 4d00178d94 signup endpoint bug fixed 2026-04-22 18:19:54 +01:00
tokslaw 863da32f93 Merge branch 'link-with-https' of MERMS/MermsPanelReactJS into master 2026-03-21 16:53:52 +00:00
tokslaw b9ad9e922e link added to terms conditions and privacy 2026-03-21 12:42:37 -04:00
victorAnumudu 5ad75e412b https validator added 2026-02-25 19:49:31 +01:00
ameye e9d6689418 Merge branch 'link-update-validation-removed' of MERMS/MermsPanelReactJS into master 2026-02-25 07:46:25 +00:00
victorAnumudu ed38e0db98 made update link params optional 2026-02-24 09:18:06 +01:00
ameye 351f027a6f Merge branch 'payment-table-update' of MERMS/MermsPanelReactJS into master 2026-02-23 22:50:07 +00:00
victorAnumudu 20d56ee307 payment table updated 2026-02-23 19:14:36 +01:00
ameye f5c798c6c1 Merge branch 'header-profile-image' of MERMS/MermsPanelReactJS into master 2026-02-16 11:27:02 +00:00
victorAnumudu a11f5100ba header profile image bug fixed 2026-02-16 11:32:16 +01:00
CHIEFSOFT\ameye 7628a561f8 Merge branch 'master' of https://gitlab.chiefsoft.net/MERMS/MermsPanelReactJS 2026-02-15 17:33:56 -05:00
CHIEFSOFT\ameye 615acff0b7 fixed reports 2026-02-15 17:33:34 -05:00
ameye aea161ccaf Merge branch 'profile-picture-upload' of MERMS/MermsPanelReactJS into master 2026-02-14 11:00:42 +00:00
victorAnumudu 66a2bcacd5 profile image upload added 2026-02-10 18:17:44 +01:00
ameye ec9d84b779 Merge branch 'truncating-text' of MERMS/MermsPanelReactJS into master 2026-02-10 17:00:38 +00:00
victorAnumudu 8ee793d11e text truncated 2026-02-09 19:11:17 +01:00
ameye 46f17cbdfe Merge branch 'template-payload-fix' of MERMS/MermsPanelReactJS into master 2026-01-21 09:45:48 +00:00
victorAnumudu 283994e43a pauload fixed 2026-01-21 10:25:56 +01:00
ameye b7d242ff0a Merge branch 'template-media-set' of MERMS/MermsPanelReactJS into master 2026-01-21 08:44:57 +00:00
victorAnumudu f6bae30bdd btn name changed 2026-01-21 04:19:05 +01:00
victorAnumudu a7e2e865de added template media set endpoint 2026-01-21 03:27:48 +01:00
ameye 51887cf0d6 Merge branch 'upload-btn-bug' of MERMS/MermsPanelReactJS into master 2026-01-20 17:11:43 +00:00
victorAnumudu 5ea1047356 image pop up added 2026-01-20 17:15:58 +01:00
victorAnumudu 9b9a7cc5da fixed upload btn display 2026-01-20 14:31:55 +01:00
ameye 290e1fbd7e Merge branch 'image-list-resized' of MERMS/MermsPanelReactJS into master 2026-01-19 20:44:45 +00:00
victorAnumudu 6884aa19b2 fixed image size 2026-01-19 19:36:21 +01:00
CHIEFSOFT\ameye 597a45dcba site image 2026-01-18 18:13:58 -05:00
CHIEFSOFT\ameye 4f7274c30c load template settings 2026-01-17 19:05:12 -05:00
ameye 290356780c Merge branch 'config-temp' of MERMS/MermsPanelReactJS into master 2026-01-13 08:14:09 +00:00
victorAnumudu bd1450887b added config template page 2026-01-13 05:01:23 +01:00
CHIEFSOFT\ameye 923a2483ed Added report starter 2026-01-12 12:08:13 -05:00
ameye 7f0ccf35b2 Merge branch 'report-endpoints' of MERMS/MermsPanelReactJS into master 2026-01-09 19:09:59 +00:00
victorAnumudu af2fdcede6 added endpoint for payment, product and system reports 2026-01-09 18:10:34 +01:00
ameye a9f2136125 Merge branch 'system-table' of MERMS/MermsPanelReactJS into master 2026-01-09 12:24:14 +00:00
victorAnumudu 5d04abc0db added system table report 2026-01-09 09:01:22 +01:00
CHIEFSOFT\ameye 26d095afda System table added 2026-01-08 11:52:24 -05:00
CHIEFSOFT\ameye cd030d8d12 Merge branch 'master' of https://gitlab.chiefsoft.net/MERMS/MermsPanelReactJS 2026-01-07 04:02:14 -05:00
ameye 4a0b7925d0 Merge branch 'report-tab' of MERMS/MermsPanelReactJS into master 2026-01-07 09:01:59 +00:00
victorAnumudu 9d947f19b9 fixed report tab bug 2026-01-06 20:27:07 +01:00
CHIEFSOFT\ameye 7a99beab57 Merge branch 'master' of https://gitlab.chiefsoft.net/MERMS/MermsPanelReactJS 2026-01-05 15:08:17 -05:00
ameye 02731c021a Merge branch 'file-list-refresh' of MERMS/MermsPanelReactJS into master 2026-01-05 20:07:37 +00:00
victorAnumudu fecae9d626 file list refresh and scroll bar added 2026-01-05 14:37:19 +01:00
CHIEFSOFT\ameye a4867a1b73 Added report starter 2026-01-03 12:09:35 -05:00
CHIEFSOFT\ameye 51c2e4b568 Added report starter 2026-01-03 12:05:42 -05:00
CHIEFSOFT\ameye 756c084059 Added report starter 2026-01-03 11:04:15 -05:00
CHIEFSOFT\ameye 064973e190 Added report starter 2026-01-03 10:24:34 -05:00
CHIEFSOFT\ameye d165e9dc0f upload page 2026-01-01 12:19:36 -05:00
CHIEFSOFT\ameye 77109e8369 Added Media starter icons 2026-01-01 11:23:16 -05:00
CHIEFSOFT\ameye 2924401c9b media files 2026-01-01 01:05:48 -05:00
CHIEFSOFT\ameye f6c2b1129d server path 2025-12-28 14:34:21 -05:00
CHIEFSOFT\ameye 8ba167a17c panel values 2025-12-28 14:13:16 -05:00
CHIEFSOFT\ameye 7f65b286b1 Media upload 2025-12-28 10:58:56 -05:00
CHIEFSOFT\ameye afc9e5be0f Added Media starter 2025-12-14 14:26:11 -05:00
ameye 7aefb555ed Merge branch 'user-picture' of MERMS/MermsPanelReactJS into master 2025-12-02 22:58:40 +00:00
victorAnumudu 325d28c1a8 user profile picture link added 2025-12-02 21:42:41 +01:00
ameye 25269a44c3 Merge branch 'calendar-refresh' of MERMS/MermsPanelReactJS into master 2025-11-28 23:15:35 +00:00
victorAnumudu 47df35d076 calendar cache time removed 2025-11-28 20:50:41 +01:00
CHIEFSOFT\ameye 1deb15029d added h 2025-11-28 10:20:01 -05:00
ameye 30b284064d Merge branch 'username-length' of MERMS/MermsPanelReactJS into master 2025-11-27 20:37:49 +00:00
CHIEFSOFT\ameye 4cbe78efc3 report pssge 2025-11-27 15:36:41 -05:00
victorAnumudu 92ac7d74f4 added update profile endpoint 2025-11-27 21:27:03 +01:00
ameye f0382cea9e Merge branch 'scroll-bar' of MERMS/MermsPanelReactJS into master 2025-11-25 18:46:09 +00:00
victorAnumudu 2123af0abe template max height added 2025-11-25 18:57:46 +01:00
ameye a3c306bf89 Merge branch 'footer-content' of MERMS/MermsPanelReactJS into master 2025-11-25 05:09:37 +00:00
victorAnumudu a61abe718a added footer content to forget pwd page 2025-11-24 19:48:40 +01:00
ameye 2b91506c61 Merge branch 'login-error-fix' of MERMS/MermsPanelReactJS into master 2025-11-23 19:28:20 +00:00
victorAnumudu 253cace3fe fixed login error issue 2025-11-20 20:08:01 +01:00
CHIEFSOFT\ameye aa55a1a4e0 Logi mesage 2025-11-16 07:34:34 -05:00
ameye 8b763882fa Merge branch 'app-download-switch' of MERMS/MermsPanelReactJS into master 2025-10-30 17:02:20 +00:00
victorAnumudu 30540e46ba added app download show/hide env value 2025-10-30 17:53:16 +01:00
victorAnumudu b195b1f787 added app download show/hide env value 2025-10-30 17:52:13 +01:00
ameye 2ddd04a1a1 Merge branch 'auth-footer-component' of MERMS/MermsPanelReactJS into master 2025-10-20 19:27:38 +00:00
victorAnumudu 6ea26740a4 added auth footer component 2025-10-20 17:44:47 +01:00
CHIEFSOFT\ameye f334ca49f0 opacity adk=just 2025-10-19 13:06:19 -04:00
CHIEFSOFT\ameye fa9d7f69e4 Color configre cleanup 2025-10-19 13:04:10 -04:00
CHIEFSOFT\ameye a4db58ba97 react panel fix 2025-10-19 08:46:45 -04:00
CHIEFSOFT\ameye ddc747d9ca provision data 2025-10-19 08:37:58 -04:00
ameye ac337eb693 Merge branch 'text-truncate' of MERMS/MermsPanelReactJS into master 2025-10-17 18:17:28 +00:00
victorAnumudu f2c3415b1d text nowrap 2025-10-17 17:46:08 +01:00
ameye 37450925e1 Merge branch 'id-number-reduction' of MERMS/MermsPanelReactJS into master 2025-10-16 15:18:04 +00:00
victorAnumudu 3b20fcec68 last four characters displayed 2025-10-15 17:53:37 +01:00
ameye d87a083c3e Merge branch 'product-url-scrollbar' of MERMS/MermsPanelReactJS into master 2025-10-14 20:03:06 +00:00
victorAnumudu 3f8a7a6b3b added scrollbar to producturl table 2025-10-14 17:55:10 +01:00
ameye 91d82db40c Merge branch 'menu-close' of MERMS/MermsPanelReactJS into master 2025-10-08 21:24:40 +00:00
victorAnumudu 2dc12d7d0a menu close fixed 2025-10-08 20:27:14 +01:00
ameye 164195d4cc Merge branch 'refresh-msg' of MERMS/MermsPanelReactJS into master 2025-10-06 17:43:31 +00:00
victorAnumudu 7ad1a585ea added refresh message display 2025-10-06 17:13:28 +01:00
CHIEFSOFT\ameye a8c2dd84f1 provison room 2025-10-06 11:52:38 -04:00
CHIEFSOFT\ameye ddcc6f0cd2 Rebuild site text change 2025-10-05 14:00:30 -04:00
CHIEFSOFT\ameye 683f81e8a6 customn temaplte 2025-10-05 13:53:11 -04:00
CHIEFSOFT\ameye 8e09c30c5c fix error on display 2025-10-05 13:18:16 -04:00
ameye af0d4db5de Merge branch 'external-url-populate' of MERMS/MermsPanelReactJS into master 2025-10-04 10:02:39 +00:00
victorAnumudu 6f3dae4116 populated externl URL 2025-10-04 10:23:29 +01:00
CHIEFSOFT\ameye 13900793af added external URL 2025-10-03 20:34:31 -04:00
CHIEFSOFT\ameye 698c89edfc fix URL page 2025-10-03 15:46:03 -04:00
CHIEFSOFT\ameye 4f5a383c99 Url configuration 2025-10-03 15:16:16 -04:00
ameye a5b6a11880 Merge branch 'set-url-endpoint' of MERMS/MermsPanelReactJS into master 2025-10-03 18:30:58 +00:00
victorAnumudu 0f58da3dce added set external url endpoint 2025-10-03 17:36:10 +01:00
ameye 1101e80d91 Merge branch 'set-url' of MERMS/MermsPanelReactJS into master 2025-09-30 20:17:32 +00:00
victorAnumudu b39a7ab58c url display fixed 2025-09-30 20:58:27 +01:00
ameye ad90def3c9 Merge branch 'url-alphanumeric-validator' of MERMS/MermsPanelReactJS into master 2025-09-30 09:54:33 +00:00
CHIEFSOFT\ameye 737430bf04 text transform 2025-09-30 05:38:08 -04:00
CHIEFSOFT\ameye d371ada805 configure url page 2025-09-30 05:21:01 -04:00
CHIEFSOFT\ameye b77f1d6213 display fix 2025-09-28 22:43:39 -04:00
victorAnumudu 64085c6be5 added alphanumeric validation for url name 2025-09-28 06:58:21 +01:00
ameye 3f8b45b6d6 Merge branch 'validation-fix' of MERMS/MermsPanelReactJS into master 2025-09-23 12:06:58 +00:00
victorAnumudu 4b6c927efc fixed validation bug 2025-09-23 05:56:33 +01:00
CHIEFSOFT\ameye 8a8adcbbc7 url name 2025-09-22 19:26:28 -04:00
CHIEFSOFT\ameye 706baadb33 url_name 2025-09-22 19:18:07 -04:00
ameye 58128fdd96 Merge branch 'url-name-payload' of MERMS/MermsPanelReactJS into master 2025-09-22 16:33:02 +00:00
victorAnumudu 14e8b1b01d made url name a required payload 2025-09-22 17:30:49 +01:00
ameye 504bfbcae4 Merge branch 'url-length' of MERMS/MermsPanelReactJS into master 2025-09-22 15:51:46 +00:00
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
60 changed files with 3438 additions and 948 deletions
+7
View File
@@ -16,3 +16,10 @@ REACT_APP_TERMS_LINK='https://qa-www.mermsemr.com/terms'
# Inactivity timeout/logout AT 10MINS
REACT_APP_TIMEOUT=600000
# show download button
REACT_APP_SHOW_DOWNLOAD=0
REACT_APP_DNS1='dns1.mermsemr.net'
REACT_APP_DNS2='dns1.mermsemr.net'
REACT_APP_DNS_LINK='https://qa-www.mermsemr.com/info/dns'
+7
View File
@@ -17,3 +17,10 @@ REACT_APP_TERMS_LINK='https://qa-www.mermsemr.com/terms'
# Inactivity timeout/logout AT 10MINS
REACT_APP_TIMEOUT=600000
# show download button
REACT_APP_SHOW_DOWNLOAD=0
REACT_APP_DNS1='dns1.mermsemr.net'
REACT_APP_DNS2='dns1.mermsemr.net'
REACT_APP_DNS_LINK='https://qa-www.mermsemr.com/info/dns'
+8 -1
View File
@@ -14,4 +14,11 @@ REACT_APP_CONTACTS_LINK='https://www.mermsemr.com/contacts'
REACT_APP_TERMS_LINK='https://www.mermsemr.com/terms'
# Inactivity timeout/logout AT 10MINS
REACT_APP_TIMEOUT=600000
REACT_APP_TIMEOUT=600000
# show download button
REACT_APP_SHOW_DOWNLOAD=0
REACT_APP_DNS1='dns1.mermsemr.net'
REACT_APP_DNS2='dns1.mermsemr.net'
REACT_APP_DNS_LINK='https://www.mermsemr.com/info/dns'
+4
View File
@@ -15,3 +15,7 @@ REACT_APP_TERMS_LINK='https://qa-www.mermsemr.com/terms'
# Inactivity timeout/logout AT 10MINS
REACT_APP_TIMEOUT=600000
REACT_APP_DNS1='dns1.mermsemr.net'
REACT_APP_DNS2='dns1.mermsemr.net'
REACT_APP_DNS_LINK='https://qa-www.mermsemr.com/info/dns'
+3 -3
View File
@@ -1,12 +1,12 @@
version: '3'
services:
merms-panel:
image: registry.chiefsoft.net/merms-panel-reactjs:latest
# image: registry.chiefsoft.net/merms-panel-reactjs:latest
build:
context: .
dockerfile: docker/Dockerfile
args:
- NODE_ENV=development
# args:
# - NODE_ENV=development
restart: unless-stopped
ports:
- 8090:3000
+22 -22
View File
@@ -1,12 +1,12 @@
# pull the base image
# FROM node:alpine
FROM alpine:3.15
FROM alpine:3.22
# Build args
ARG NODE_ENV
ENV NODE_VERSION 14.19.0
ENV NODE_VERSION=14.19.0
ENV NODE_ENV=$NODE_ENV
# install nginx
@@ -82,26 +82,26 @@ RUN addgroup -g 1000 node \
&& node --version \
&& npm --version
ENV YARN_VERSION 1.22.17
# ENV YARN_VERSION 1.22.17
RUN apk add --no-cache --virtual .build-deps-yarn curl gnupg tar \
&& for key in \
6A010C5166006599AA17F08146C2130DFD2497F5 \
; do \
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" || \
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" ; \
done \
&& curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
&& curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz.asc" \
&& gpg --batch --verify yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \
&& mkdir -p /opt \
&& tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \
&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \
&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \
&& rm yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \
&& apk del .build-deps-yarn \
# smoke test
&& yarn --version
# RUN apk add --no-cache --virtual .build-deps-yarn curl gnupg tar \
# && for key in \
# 6A010C5166006599AA17F08146C2130DFD2497F5 \
# ; do \
# gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" || \
# gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" ; \
# done \
# && curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
# && curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz.asc" \
# && gpg --batch --verify yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \
# && mkdir -p /opt \
# && tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \
# && ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \
# && ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \
# && rm yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \
# && apk del .build-deps-yarn \
# # smoke test
# && yarn --version
# set working directory
# WORKDIR /app
@@ -109,7 +109,7 @@ RUN apk add --no-cache --virtual .build-deps-yarn curl gnupg tar \
# add `/app/node_modules/.bin` to $PATH
# ENV PATH /app/node_modules/.bin:$PATH
ENV PATH /usr/src/app/node_modules/.bin:$PATH
ENV PATH=/usr/src/app/node_modules/.bin:$PATH
COPY nginx.conf ./
+537 -60
View File
@@ -1,6 +1,6 @@
{
"name": "samatze-com",
"version": "0.1.0",
"name": "merms-panel",
"version": "0.1.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1500,6 +1500,37 @@
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
"integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q=="
},
"@fullcalendar/core": {
"version": "6.1.20",
"resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.20.tgz",
"integrity": "sha512-1cukXLlePFiJ8YKXn/4tMKsy0etxYLCkXk8nUCFi11nRONF2Ba2CD5b21/ovtOO2tL6afTJfwmc1ed3HG7eB1g==",
"requires": {
"preact": "~10.12.1"
}
},
"@fullcalendar/daygrid": {
"version": "6.1.20",
"resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.20.tgz",
"integrity": "sha512-AO9vqhkLP77EesmJzuU+IGXgxNulsA8mgQHynclJ8U70vSwAVnbcLG9qftiTAFSlZjiY/NvhE7sflve6cJelyQ=="
},
"@fullcalendar/interaction": {
"version": "6.1.20",
"resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.20.tgz",
"integrity": "sha512-p6txmc5txL0bMiPaJxe2ip6o0T384TyoD2KGdsU6UjZ5yoBlaY+dg7kxfnYKpYMzEJLG58n+URrHr2PgNL2fyA=="
},
"@fullcalendar/react": {
"version": "6.1.20",
"resolved": "https://registry.npmjs.org/@fullcalendar/react/-/react-6.1.20.tgz",
"integrity": "sha512-1w0pZtceaUdfAnxMSCGHCQalhi+mR1jOe76sXzyAXpcPz/Lf0zHSdcGK/U2XpZlnQgQtBZW+d+QBnnzVQKCxAA=="
},
"@fullcalendar/timegrid": {
"version": "6.1.20",
"resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.20.tgz",
"integrity": "sha512-4H+/MWbz3ntA50lrPif+7TsvMeX3R1GSYjiLULz0+zEJ7/Yfd9pupZmAwUs/PBpA6aAcFmeRr0laWfcz1a9V1A==",
"requires": {
"@fullcalendar/daygrid": "~6.1.20"
}
},
"@humanwhocodes/config-array": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
@@ -1543,6 +1574,19 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@@ -1553,6 +1597,36 @@
"strip-ansi": "^7.0.1"
}
},
"string-width-cjs": {
"version": "npm:string-width@4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"requires": {
"ansi-regex": "^5.0.1"
}
}
}
},
"strip-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
@@ -1561,6 +1635,21 @@
"ansi-regex": "^6.0.1"
}
},
"strip-ansi-cjs": {
"version": "npm:strip-ansi@6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"requires": {
"ansi-regex": "^5.0.1"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
}
}
},
"wrap-ansi": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
@@ -1570,6 +1659,54 @@
"string-width": "^5.0.1",
"strip-ansi": "^7.0.1"
}
},
"wrap-ansi-cjs": {
"version": "npm:wrap-ansi@7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"requires": {
"ansi-regex": "^5.0.1"
}
}
}
}
}
},
@@ -2265,6 +2402,31 @@
"source-map": "^0.7.3"
}
},
"@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
},
"@reduxjs/toolkit": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz",
"integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==",
"requires": {
"@standard-schema/spec": "^1.0.0",
"@standard-schema/utils": "^0.3.0",
"immer": "^11.0.0",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
},
"dependencies": {
"immer": {
"version": "11.1.4",
"resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz",
"integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw=="
}
}
},
"@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -2344,6 +2506,34 @@
"@sinonjs/commons": "^1.7.0"
}
},
"@socket.io/component-emitter": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
},
"@standard-schema/spec": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="
},
"@standard-schema/utils": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="
},
"@stripe/react-stripe-js": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-3.10.0.tgz",
"integrity": "sha512-UPqHZwMwDzGSax0ZI7XlxR3tZSpgIiZdk3CiwjbTK978phwR/fFXeAXQcN/h8wTAjR4ZIAzdlI9DbOqJhuJdeg==",
"requires": {
"prop-types": "^15.7.2"
}
},
"@stripe/stripe-js": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.9.0.tgz",
"integrity": "sha512-ggs5k+/0FUJcIgNY08aZTqpBTtbExkJMYMLSMwyucrhtWexVOEY1KJmhBsxf+E/Q15f5rbwBpj+t0t2AW2oCsQ=="
},
"@surma/rollup-plugin-off-main-thread": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
@@ -2492,6 +2682,19 @@
"loader-utils": "^2.0.0"
}
},
"@tanstack/query-core": {
"version": "5.100.5",
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.100.5.tgz",
"integrity": "sha512-t20KrhKkf0HXzqQkPbJ5erhFesup68BAbwFgYmTrS7bxMF7O5MdmL8jUkik4thsG7Hg00fblz30h6yF1d5TxGg=="
},
"@tanstack/react-query": {
"version": "5.100.5",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.100.5.tgz",
"integrity": "sha512-aNwj1mi2v2bQ9IxkyR1grLOUkv3BYWoykHy9KDyLNbjC3tsahbOHJibK+Wjtr1wRhG59/AvJhiJG5OlthaCgJA==",
"requires": {
"@tanstack/query-core": "5.100.5"
}
},
"@testing-library/dom": {
"version": "8.20.1",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz",
@@ -2796,6 +2999,14 @@
"@types/node": "*"
}
},
"@types/hoist-non-react-statics": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz",
"integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==",
"requires": {
"hoist-non-react-statics": "^3.3.0"
}
},
"@types/html-minifier-terser": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@@ -3167,6 +3378,11 @@
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="
},
"@types/use-sync-external-store": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="
},
"@types/ws": {
"version": "8.5.13",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
@@ -3800,6 +4016,86 @@
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz",
"integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w=="
},
"axios": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz",
"integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==",
"requires": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^2.1.0"
},
"dependencies": {
"es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="
},
"es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"requires": {
"es-errors": "^1.3.0"
}
},
"es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"requires": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
}
},
"follow-redirects": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="
},
"form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
}
},
"get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"requires": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
}
},
"gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
},
"has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
}
}
},
"axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@@ -4198,6 +4494,15 @@
"set-function-length": "^1.2.1"
}
},
"call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"requires": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
}
},
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -5095,6 +5400,23 @@
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="
},
"dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"requires": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"dependencies": {
"gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
}
}
},
"duplexer": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@@ -5143,6 +5465,38 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="
},
"engine.io-client": {
"version": "6.6.4",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz",
"integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==",
"requires": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.4.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.18.3",
"xmlhttprequest-ssl": "~2.1.1"
},
"dependencies": {
"debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"requires": {
"ms": "^2.1.3"
}
},
"ws": {
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="
}
}
},
"engine.io-parser": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="
},
"enhanced-resolve": {
"version": "5.17.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
@@ -6372,6 +6726,33 @@
"mime-types": "^2.1.12"
}
},
"formik": {
"version": "2.4.9",
"resolved": "https://registry.npmjs.org/formik/-/formik-2.4.9.tgz",
"integrity": "sha512-5nI94BMnlFDdQRBY4Sz39WkhxajZJ57Fzs8wVbtsQlm5ScKIR1QLYqv/ultBnobObtlUyxpxoLodpixrsf36Og==",
"requires": {
"@types/hoist-non-react-statics": "^3.3.1",
"deepmerge": "^2.1.1",
"hoist-non-react-statics": "^3.3.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"react-fast-compare": "^2.0.1",
"tiny-warning": "^1.0.2",
"tslib": "^2.0.0"
},
"dependencies": {
"deepmerge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
},
"tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
}
}
},
"forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -6466,6 +6847,15 @@
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q=="
},
"get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"requires": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
}
},
"get-stream": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
@@ -6647,6 +7037,14 @@
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
}
},
"hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -9018,6 +9416,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash-es": {
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
"integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="
},
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@@ -9110,6 +9513,11 @@
"tmpl": "1.0.5"
}
},
"math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="
},
"mdn-data": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
@@ -10557,6 +10965,11 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"preact": {
"version": "10.12.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
"integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg=="
},
"prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
@@ -10630,6 +11043,11 @@
"react-is": "^16.13.1"
}
},
"property-expr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
"integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA=="
},
"proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -10646,6 +11064,11 @@
}
}
},
"proxy-from-env": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="
},
"psl": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.13.0.tgz",
@@ -10883,6 +11306,11 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
},
"react-fast-compare": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
},
"react-icons": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.4.0.tgz",
@@ -10893,6 +11321,15 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-redux": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
"requires": {
"@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.4.0"
}
},
"react-refresh": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
@@ -11022,6 +11459,16 @@
"strip-indent": "^3.0.0"
}
},
"redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
},
"redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="
},
"reflect.getprototypeof": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.7.tgz",
@@ -11188,6 +11635,11 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
},
"reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="
},
"resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@@ -11660,6 +12112,46 @@
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
},
"socket.io-client": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz",
"integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==",
"requires": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.4.1",
"engine.io-client": "~6.6.1",
"socket.io-parser": "~4.2.4"
},
"dependencies": {
"debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"requires": {
"ms": "^2.1.3"
}
}
}
},
"socket.io-parser": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz",
"integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==",
"requires": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.4.1"
},
"dependencies": {
"debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"requires": {
"ms": "^2.1.3"
}
}
}
},
"sockjs": {
"version": "0.3.24",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
@@ -11833,23 +12325,6 @@
}
}
},
"string-width-cjs": {
"version": "npm:string-width@4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"dependencies": {
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
}
}
},
"string.prototype.includes": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
@@ -11952,14 +12427,6 @@
"ansi-regex": "^5.0.1"
}
},
"strip-ansi-cjs": {
"version": "npm:strip-ansi@6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"requires": {
"ansi-regex": "^5.0.1"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
@@ -12316,6 +12783,16 @@
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
},
"tiny-case": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
"integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="
},
"tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -12334,6 +12811,11 @@
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
},
"toposort": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
},
"tough-cookie": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
@@ -12592,6 +13074,11 @@
"requires-port": "^1.0.0"
}
},
"use-sync-external-store": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -13247,39 +13734,6 @@
}
}
},
"wrap-ansi-cjs": {
"version": "npm:wrap-ansi@7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
}
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -13311,6 +13765,11 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
},
"xmlhttprequest-ssl": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="
},
"y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -13349,6 +13808,24 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
},
"yup": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/yup/-/yup-1.7.1.tgz",
"integrity": "sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==",
"requires": {
"property-expr": "^2.0.5",
"tiny-case": "^1.0.3",
"toposort": "^2.0.2",
"type-fest": "^2.19.0"
},
"dependencies": {
"type-fest": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
"integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="
}
}
}
}
}
+11
View File
@@ -14,6 +14,10 @@
border-radius: 10px;
}
.border-radius-10 {
border-radius: 10px;
}
.login-links{
margin-top: 50px;
display: flex;
@@ -47,4 +51,11 @@ button{
.accordion-button, .accordion-button:not(.collapsed) {
background-color: transparent!important;
}
@media (min-width: 992px) {
.modal-50 {
min-width: 50%;
max-width: 50%;
}
}
+2
View File
@@ -26,6 +26,7 @@ import ProfileCompletePage from './views/ProfileCompletePage';
import SubscribePage from './views/Subscribe'
import StartPage from "./views/StartPage";
import TrafficPage from "./views/TrafficPage";
import MyMediaPage from './views/MyMediaPage.jsx';
function AppRouters() {
return (
@@ -52,6 +53,7 @@ function AppRouters() {
<Route path={siteLinks.profile_complete} element={<ProfileCompletePage/>}/>
<Route path={siteLinks.product} element={<ProductPage/>}/>
<Route path={siteLinks.reports} element={<ReportsPage/>}/>
<Route path={siteLinks.my_media} element={<MyMediaPage />}/>
<Route path={siteLinks.comments} element={<CommentsPage/>}/>
<Route path={siteLinks.contacts} element={<ContactsPage/>}/>
<Route path={siteLinks.user} element={<UserPage/>}/>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

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