diff --git a/docker-compose.yml b/docker-compose.yml index d3e70c8..9ac0b20 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,7 @@ services: - ./:/usr/src/app - ./src/:/usr/src/app/src - ./run.sh:/usr/src/app/run.sh + - ./node_modules/:/usr/src/app/node_modules extra_hosts: - api.mermsemr.com:10.10.33.15 - devapi.mermsemr.com:10.10.33.15 diff --git a/package.json b/package.json index 64c8218..06fc1ed 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,11 @@ "version": "0.1.1", "private": true, "dependencies": { + "@fullcalendar/core": "^6.1.15", + "@fullcalendar/daygrid": "^6.1.15", + "@fullcalendar/interaction": "^6.1.15", + "@fullcalendar/react": "^6.1.15", + "@fullcalendar/timegrid": "^6.1.15", "@popperjs/core": "^2.11.8", "@reduxjs/toolkit": "^2.4.0", "@tanstack/react-query": "^5.62.3", @@ -12,11 +17,9 @@ "apexcharts": "^4.1.0", "axios": "^1.7.9", "bootstrap": "^5.3.3", - "dayjs": "^1.11.13", "formik": "^2.4.6", "react": "^18.3.1", "react-apexcharts": "^1.7.0", - "react-big-calendar": "^1.17.0", "react-dom": "^18.3.1", "react-icons": "^5.4.0", "react-redux": "^9.1.2", diff --git a/public/index.html b/public/index.html index a45580d..db07959 100644 --- a/public/index.html +++ b/public/index.html @@ -13,8 +13,19 @@ MERMS-Panel + + +
+ diff --git a/src/App.css b/src/App.css index 79280ae..b389f38 100644 --- a/src/App.css +++ b/src/App.css @@ -6,6 +6,6 @@ .signup-bg { background-image: url('./assets/bg/signup_bg.jpg') !important; - background-size: 100%; + background-size: cover; background-repeat: no-repeat; } \ No newline at end of file diff --git a/src/assets/img/widget/coming-soon.jpg b/src/assets/img/widget/coming-soon.jpg new file mode 100644 index 0000000..48315e5 Binary files /dev/null and b/src/assets/img/widget/coming-soon.jpg differ diff --git a/src/assets/img/widget/working.jpg b/src/assets/img/widget/working.jpg new file mode 100644 index 0000000..ce8c5e3 Binary files /dev/null and b/src/assets/img/widget/working.jpg differ diff --git a/src/component/auth/CSignup.jsx b/src/component/auth/CSignup.jsx index c38db83..9fa2746 100644 --- a/src/component/auth/CSignup.jsx +++ b/src/component/auth/CSignup.jsx @@ -2,35 +2,27 @@ import React, { useEffect, useState } from 'react' import { Form, Formik } from "formik"; import * as Yup from "yup"; -// import LoginImg from '../../assets/bg/login.svg' - -import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom' +import { Link, useNavigate, useParams } from 'react-router-dom' import siteLinks from '../../links/siteLinks' import { useMutation } from '@tanstack/react-query'; -import { signUpUser } from '../../services/services'; +import { completeRegistration, verifyEmail } from '../../services/services'; + +import { IoMdArrowDropdown } from "react-icons/io"; 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") + country: Yup.string().required("Username is required"), + username: Yup.string().min(3, "Minimum 3 characters") .max(50, "Maximum 50 characters") - .required("Email is required"), + .required("Username is required"), password: Yup.string().required("Password is required"), confirmpassword: Yup.string().required("Confirm Password is required").oneOf([Yup.ref('password')], 'Passwords must match') - // lastname: Yup.string().required("Lastname is required"), - // isChecked: Yup.bool().oneOf([true], "Please accept the terms & policy"), // use bool instead of boolean }) const initialValues = { - email: '', + username: '', password: '', + country: '', confirmpassword: '', - // lastname: '', - // isChecked: false, }; export default function CSignup() { @@ -39,27 +31,56 @@ export default function CSignup() { const navigate = useNavigate() - const mutation = useMutation({ + const [user, setUser] = useState(null) + +// API to verify email link + const verifyLink = useMutation({ mutationFn: (fields) => { - return signUpUser(fields) + return verifyEmail(fields) }, onSuccess: (res) => { - console.log('res', res) - } + setUser(res.data) + }, + // onError: (err) => { + // console.log('err', err) + // } }) - const CSignUp = (values) => { - // helpers.resetForm() - // console.log('values', values, helpers) - // mutation.mutate(values) - console.log('values', values) + const cSignup = useMutation({ + mutationFn: (fields) => { + return completeRegistration(fields) + }, + onSuccess: (res) => { + const {token, room} = res?.data?.data + if(token){ + localStorage.setItem('token', token) + localStorage.setItem('room', room) + // const data = {token} + // dispatch(updateUserDetails({ ...data })); + navigate('/dash') // later add redux to dispatch state + } + } + // onError: (err) => { + // console.log('err', err) + // } + }) + + const completeSignup = (values) => { + let reqData = { + country : values.country, + username: values.username, + password: values.password, + verify_link: user.verify_link + } + cSignup.mutate(reqData) } useEffect(()=>{ if(!jwt){ return navigate(siteLinks.login, {replace: true}) } - }) + verifyLink.mutate({verify_link: jwt}) + }, []) return (
@@ -72,87 +93,118 @@ export default function CSignup() {

MERMS Panel

-

Welcome, Enter your password.

- Welcome, Enter your password.

*/} +
- {(props) => { - return ( -
-
- {!mutation.isSuccess ? - <> - {/*
-
- - -
-
*/} -
-
- - -
-
-
-
- - -
-
-
-
- - -
-
- - {/*
-
- - -
- {props.errors.isChecked} -
*/} - - {mutation.error && - <> -
-

{mutation.error.message}

-
- - } - -
- -
- - : -
-
-

Check your email to continue.

- Home +
+
+ {verifyLink.isPending ? +
+
+
+

loading...

- } +
+ : verifyLink.isSuccess ? +
+ + {(props) => { + return ( + +
+ <> +
+
+ + {/* */} +
+
+
+
+ +
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ + {/*
+
+ + +
+ {props.errors.isChecked} +
*/} -
- Need help with logging in or signing up? -
- -
- {/*

Already have an account ? Sign In

*/} -

Ready our Privacy Statement

-
+ {cSignup.error && + <> +
+

{cSignup.error.message}

+
+ + } + +
+ +
+ +
+ + ); + }} +
+
+ : +
+
+
+

{verifyLink?.error?.message}

+
+
+
+ } + +
+ Need help with logging in or signing up?
- - ); - }} - + +
+

Read our Privacy Statement

+
+
+
+
diff --git a/src/component/auth/Login2.jsx b/src/component/auth/Login2.jsx index 1517044..ce99323 100644 --- a/src/component/auth/Login2.jsx +++ b/src/component/auth/Login2.jsx @@ -118,9 +118,7 @@ export default function Login() {
@@ -128,10 +126,7 @@ export default function Login() {
diff --git a/src/component/calendar/Calendar.jsx b/src/component/calendar/Calendar.jsx index 9aa4175..316c9c5 100644 --- a/src/component/calendar/Calendar.jsx +++ b/src/component/calendar/Calendar.jsx @@ -1,26 +1,44 @@ import React, { useCallback, useState } from "react"; import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; import EventCalendar from "./EventCalendar"; +import ExternalDraggable from "./ExternalDraggable"; export default function Calendar(){ - const [draggedEvent, setDraggedEvent] = useState('undroppable') - const handleDragStart = useCallback((event) => setDraggedEvent(event), []) + // const [draggedEvent, setDraggedEvent] = useState('undroppable') + // const handleDragStart = (event) => { + // setDraggedEvent(event) + // } - // const dummyEvents = [ - // {id: '1', title: 'Family Vacation', color: 'fc-event-primary', start: new Date('2024-12-18'), end: new Date('2024-12-18'), isAllDay: false, resource: ''}, - // {id: '2', title: 'Meeting In Office', color: 'fc-event-warning', start: new Date('2024-12-19'), end: new Date('2024-12-19'), isAllDay: false, resource: ''}, - // {id: '3', title: 'Client Call', color: 'fc-event-danger', start: new Date('2024-12-20'), end: new Date('2024-12-20'), isAllDay: false, resource: ''}, - // {id: '4', title: 'Interview', color: 'fc-event-success', start: new Date('2024-12-21'), end: new Date('2024-12-21'), isAllDay: false, resource: ''} - // ] - const dummyEvents = [ - {id: '1', title: 'Family Vacation', color: 'fc-event-primary', isAllDay: false}, - {id: '2', title: 'Meeting In Office', color: 'fc-event-warning', isAllDay: false}, - {id: '3', title: 'Client Call', color: 'fc-event-danger', isAllDay: false}, - {id: '4', title: 'Interview', color: 'fc-event-success', isAllDay: false} + const events = [ + {id: '1111', title: 'Family Vacation', color: 'fc-event-primary', start: new Date('2025-01-18')}, + {id: '2222', title: 'Meeting In Office', color: 'fc-event-warning', start: new Date('2025-01-19')}, + {id: '3333', title: 'Client Call', color: 'fc-event-danger', start: new Date('2025-01-22')}, + {id: '4444', title: 'Interview', color: 'fc-event-success', start: new Date('2025-01-1')} ] + const [dummyEvents, setDummyEvents] = useState(events) + + const [removeAfterDrop, setRemoveAfterDrop] = useState(false) + + const [newEvent, setNewEvent] = useState({ + title: '', color: '' + }) + + const handleEditEvent = ({target:{name,value}}) => { + setNewEvent(prev => ({...prev, [name]:value})) + } + + const handleAddNewEvent = () => { + if(newEvent.title && newEvent.color){ + const eventToAdd = {...newEvent} + setDummyEvents(prev => ([...prev, eventToAdd])) + setNewEvent({title: '', color: ''}) + } + } + + return( <> @@ -42,7 +60,7 @@ export default function Calendar(){

Drag and drop your event or click in the calendar.

- {dummyEvents.map((item, index) => ( + {/* {dummyEvents.map((item, index) => (
@@ -51,10 +69,11 @@ export default function Calendar(){ > {item.title}
- ))} + ))} */} +
- + setRemoveAfterDrop(prev => !prev)} /> @@ -63,7 +82,8 @@ export default function Calendar(){
- + {/* */} +
@@ -87,15 +107,16 @@ export default function Calendar(){
- +
- + + + + +
@@ -103,7 +124,7 @@ export default function Calendar(){
- +
diff --git a/src/component/calendar/EventCalendar.jsx b/src/component/calendar/EventCalendar.jsx index 5d03708..145d1bc 100644 --- a/src/component/calendar/EventCalendar.jsx +++ b/src/component/calendar/EventCalendar.jsx @@ -1,130 +1,125 @@ -import React, { useCallback, useState } from 'react' -import { Calendar, dayjsLocalizer } from 'react-big-calendar' -import dayjs from 'dayjs' +import React, { useCallback, useState } from 'react'; +import { formatDate } from '@fullcalendar/core'; +import FullCalendar from '@fullcalendar/react'; +import dayGridPlugin from '@fullcalendar/daygrid'; +import timeGridPlugin from '@fullcalendar/timegrid'; +import interactionPlugin from '@fullcalendar/interaction'; +import { INITIAL_EVENTS, createEventId } from './event-utils'; -import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop' +export default function EventCalendar({draggedEvent, setDraggedEvent, removeAfterDrop, setDummyEvents}) { + const [weekendsVisible, setWeekendsVisible] = useState(true); + const [currentEvents, setCurrentEvents] = useState(INITIAL_EVENTS); + + function handleWeekendsToggle() { + setWeekendsVisible(!weekendsVisible); + } + + function handleDateSelect(selectInfo) { + let title = prompt('Please enter a new title for your event'); + // let calendarApi = selectInfo.view.calendar; + + // calendarApi.unselect(); // clear date selection + + if (title) { + // calendarApi.addEvent({ + // id: createEventId(), + // title, + // start: selectInfo.startStr, + // end: selectInfo.endStr, + // allDay: selectInfo.allDay, + // }); + let newEvent = { + id: createEventId(), + title, + start: selectInfo.startStr, + end: selectInfo.endStr, + allDay: selectInfo.allDay, + } + setCurrentEvents(prev => ([...prev, newEvent])) + } + } + + const onDrop = (event) => { + // console.log('event', event) + if(event){ + let newEvent = { + id: createEventId(), + title: event.draggedEl.innerText, + start: event.startStr, + // end: event.endStr, + allDay: event.allDay, + ...event + } + setCurrentEvents(prev => ([...prev, newEvent])) + if(removeAfterDrop){ + setDummyEvents(prev => prev.filter(item => item.title != newEvent.title)) + } + } + } + + const removeEvent = (event) => { + let eventToRemove = event?.event?._def?.publicId + let remainingEvent = currentEvents.filter(item => item.id != eventToRemove) + setCurrentEvents(remainingEvent) + } - -const localizer = dayjsLocalizer(dayjs) - -const DnDCalendar = withDragAndDrop(Calendar) - -export default function EventCalendar({draggedEvent, setDraggedEvent}) { - const myEventsList = [] - const [myEvents, setMyEvents] = useState(myEventsList) - - const moveEvent = useCallback( - ({ event, start, end, isAllDay: droppedOnAllDaySlot = false }) => { - // const { isAllDay } = event - // if (!allDay && droppedOnAllDaySlot) { - // event.allDay = true - // } - // if (allDay && !droppedOnAllDaySlot) { - // event.allDay = false; - // } - - setMyEvents((prev) => { - const existing = prev.find((ev) => ev.id === event.id) ?? {} - const filtered = prev.filter((ev) => ev.id !== event.id) - return [...filtered, { ...existing, start, end, allDay: event.allDay }] - }) - }, - [setMyEvents] - ) - const [displayDragItemInCell, setDisplayDragItemInCell] = useState(true) - - - const dragFromOutsideItem = useCallback(() => draggedEvent === 'undroppable' ? null : draggedEvent, [draggedEvent]) - - const customOnDragOverFromOutside = useCallback( - (dragEvent) => { - // check for undroppable is specific to this example - // and not part of API. This just demonstrates that - // onDragOver can optionally be passed to conditionally - // allow draggable items to be dropped on cal, based on - // whether event.preventDefault is called - if (draggedEvent !== 'undroppable') { - console.log('preventDefault') - dragEvent.preventDefault() - } - }, - [draggedEvent] + function handleEventClick(clickInfo) { + if ( + confirm( + `Are you sure you want to delete the event '${clickInfo.event.title}'` ) + ) { + clickInfo.event.remove(); + } + } - - const eventPropGetter = useCallback( - (event) => ({ - ...(event.isDraggable - ? { className: 'isDraggable' } - : { className: 'nonDraggable' }), - }), - [] - ) - - const newEvent = useCallback( - (event) => { - setMyEvents((prev) => { - const idList = prev.map((item) => item.id) - const newId = Math.max(...idList) + 1 - // return [...prev, { ...event, id: newId }] - return [...prev, { ...event}] - }) - }, - [setMyEvents] - ) - - const onDropFromOutside = useCallback( - ({ start, end, allDay: isAllDay }) => { - if (draggedEvent === 'undroppable') { - setDraggedEvent(null) - return - } - - const { title, id } = draggedEvent - const event = { - title: title, - start, - end, - isAllDay, - id - } - setDraggedEvent(null) - newEvent(event) - }, - [draggedEvent, setDraggedEvent, newEvent] - ) - - const resizeEvent = useCallback( - ({ event, start, end }) => { - setMyEvents((prev) => { - const existing = prev.find((ev) => ev.id === event.id) ?? {} - const filtered = prev.filter((ev) => ev.id !== event.id) - return [...filtered, { ...existing, start, end }] - }) - }, - [setMyEvents] - ) + function handleEvents(events) { + // setCurrentEvents(events); + } return ( -
- +
+ +
- ) -} \ No newline at end of file + ); +} + +function renderEventContent(eventInfo) { + return ( + <> + {/* {eventInfo.timeText} */} + {eventInfo.event.title} + + ); +} diff --git a/src/component/calendar/EventCalendarOld.jsx b/src/component/calendar/EventCalendarOld.jsx new file mode 100644 index 0000000..5634a54 --- /dev/null +++ b/src/component/calendar/EventCalendarOld.jsx @@ -0,0 +1,138 @@ +// import React, { useCallback, useState } from 'react' +// import { Calendar, dayjsLocalizer } from 'react-big-calendar' +// import dayjs from 'dayjs' + +// import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop' + + + +// const localizer = dayjsLocalizer(dayjs) + +// const DnDCalendar = withDragAndDrop(Calendar) + +// export default function EventCalendar({draggedEvent, setDraggedEvent}) { +// const myEventsList = [] +// const [myEvents, setMyEvents] = useState(myEventsList) + +// const moveEvent = useCallback( +// ({ event, start, end, isAllDay: droppedOnAllDaySlot = false }) => { +// // const { isAllDay } = event +// // if (!allDay && droppedOnAllDaySlot) { +// // event.allDay = true +// // } +// // if (allDay && !droppedOnAllDaySlot) { +// // event.allDay = false; +// // } + +// setMyEvents((prev) => { +// const existing = prev.find((ev) => ev.id === event.id) ?? {} +// const filtered = prev.filter((ev) => ev.id !== event.id) +// return [...filtered, { ...existing, start, end, allDay: event.allDay }] +// }) +// }, +// [setMyEvents] +// ) +// const [displayDragItemInCell, setDisplayDragItemInCell] = useState(true) + + +// const dragFromOutsideItem = useCallback(() => draggedEvent === 'undroppable' ? null : draggedEvent, [draggedEvent]) + +// const customOnDragOverFromOutside = useCallback( +// (dragEvent) => { +// // check for undroppable is specific to this example +// // and not part of API. This just demonstrates that +// // onDragOver can optionally be passed to conditionally +// // allow draggable items to be dropped on cal, based on +// // whether event.preventDefault is called +// if (draggedEvent !== 'undroppable') { +// console.log('preventDefault') +// dragEvent.preventDefault() +// } +// }, +// [draggedEvent] +// ) + + +// const eventPropGetter = useCallback( +// (event) => ({ +// ...(event.isDraggable +// ? { className: 'isDraggable' } +// : { className: 'nonDraggable' }), +// }), +// [] +// ) + +// const newEvent = useCallback( +// (event) => { +// setMyEvents((prev) => { +// const idList = prev.map((item) => item.id) +// const newId = Math.max(...idList) + 1 +// // return [...prev, { ...event, id: newId }] +// return [...prev, { ...event}] +// }) +// }, +// [setMyEvents] +// ) + +// const onDropFromOutside = useCallback( +// ({ start, end, allDay: isAllDay }) => { +// if (draggedEvent === 'undroppable') { +// setDraggedEvent(null) +// return +// } + +// const { title, id } = draggedEvent +// const event = { +// title: title, +// start, +// end, +// isAllDay, +// id +// } +// setDraggedEvent(null) +// newEvent(event) +// }, +// [draggedEvent, setDraggedEvent, newEvent] +// ) + +// const resizeEvent = useCallback( +// ({ event, start, end }) => { +// setMyEvents((prev) => { +// const existing = prev.find((ev) => ev.id === event.id) ?? {} +// const filtered = prev.filter((ev) => ev.id !== event.id) +// return [...filtered, { ...existing, start, end }] +// }) +// }, +// [setMyEvents] +// ) + +// return ( +//
+// +//
+// ) +// } + +import React from 'react' + +export default function EventCalendar() { + return ( +
EventCalendar
+ ) +} diff --git a/src/component/calendar/ExternalDraggable.jsx b/src/component/calendar/ExternalDraggable.jsx new file mode 100644 index 0000000..fff304d --- /dev/null +++ b/src/component/calendar/ExternalDraggable.jsx @@ -0,0 +1,33 @@ +import React, { useEffect, useRef } from "react"; +import { Draggable } from "@fullcalendar/interaction"; + +const ExternalDraggable = ({dummyEvents}) => { + const eventContainerRef = useRef(null); + + useEffect(() => { + // Make the external events draggable + const draggable = new Draggable(eventContainerRef.current, { + itemSelector: ".fc-event", + eventData: (eventEl) => ({ + title: eventEl.innerText.trim(), + }), + }); + + // Cleanup the Draggable instance on unmount + return () => { + draggable.destroy(); + }; + }, []); + + return ( +
+ {dummyEvents.map((item, index) => ( +
+ {item.title} +
+ ))} +
+ ); +}; + +export default ExternalDraggable; diff --git a/src/component/calendar/event-utils.js b/src/component/calendar/event-utils.js new file mode 100644 index 0000000..b4453d8 --- /dev/null +++ b/src/component/calendar/event-utils.js @@ -0,0 +1,22 @@ +let eventGuid = 0; +let todayStr = new Date().toISOString().replace(/T.*$/, ''); // YYYY-MM-DD of today + +export const INITIAL_EVENTS = [ + // { + // id: createEventId(), + // title: 'All-day event', + // start: new Date('2025-01-19'), + // end: new Date('2025-01-20'), + // // color: 'blue' + // }, + // { + // id: createEventId(), + // title: 'Timed event', + // start: todayStr + 'T12:00:00', + // // color: 'red' + // }, +]; + +export function createEventId() { + return String(eventGuid++); +} diff --git a/src/component/context/SocketIOContext.jsx b/src/component/context/SocketIOContext.jsx index 1643d74..1ca2b8d 100644 --- a/src/component/context/SocketIOContext.jsx +++ b/src/component/context/SocketIOContext.jsx @@ -49,6 +49,12 @@ export default function SocketIOContextProvider({children}) { }) }); + socket.on(socketOnEvents.refresh_provision, (data) => { + queryClient.refetchQueries({ // refetches productProvision API call + queryKey: [...queryKeys.myproduct_provision], + }) + }); + // client-side socket.on("connect", () => { console.log(socket.id); diff --git a/src/component/context/socketEvents.js b/src/component/context/socketEvents.js index d3b198c..42eee18 100644 --- a/src/component/context/socketEvents.js +++ b/src/component/context/socketEvents.js @@ -5,5 +5,6 @@ export const socketEmitEvents = { export const socketOnEvents = { - receive_message: 'receive_message' + receive_message: 'receive_message', + refresh_provision: 'refresh_provision_actions' } \ No newline at end of file diff --git a/src/component/home/HomeSections.jsx b/src/component/home/HomeSections.jsx index c525ec9..d7727e6 100644 --- a/src/component/home/HomeSections.jsx +++ b/src/component/home/HomeSections.jsx @@ -52,7 +52,7 @@ export default function HomeSections(){
-
+
diff --git a/src/component/home/Products.jsx b/src/component/home/Products.jsx index 67d3893..e4eba8a 100644 --- a/src/component/home/Products.jsx +++ b/src/component/home/Products.jsx @@ -10,8 +10,20 @@ export default function Products() { queryKey: queryKeys.product, queryFn: () => productData() }) - - const products = data?.data?.products_data?.products +/* + { + "banner": "p1.jpg", + "description": "Your personal professional web presence", + "id": 1, + "name": "Personal Website", + "product_id": "A000001", + "product_uid": "e92282b4-3ee1-4026-92ac-12cfd214b43a", + "status": 5, + "status_text": "Activate Now" + }, + */ + //const products = data?.data?.products_list?.products + const products = data?.data?.products_list return ( <> @@ -38,31 +50,20 @@ export default function Products() {
{products && products.map((product, index) => (
- +
-

{product?.status}

-

{product?.description}

+

{product?.status_text}

+

{product?.name}

))} - {/*
*/} - {/*
*/} - {/*
*/} - {/* */} - {/*
*/} - {/*
*/} - {/*

Annual Revenue

*/} - {/*

$40,516

*/} - {/*
*/} - {/*
*/} - {/*
*/}
}
diff --git a/src/component/home/ProductsURL.jsx b/src/component/home/ProductsURL.jsx index baafc4e..8538731 100644 --- a/src/component/home/ProductsURL.jsx +++ b/src/component/home/ProductsURL.jsx @@ -53,11 +53,12 @@ export default function ProductsURL() { {urlData && urlData.map((item, index) => { - let statusColor = item?.status == 'Active' ? 'badge-success-inverse' : item?.status == 'Updating' ? 'badge-success-inverse' : item?.status == 'Refreshing' ? 'badge-danger-inverse' : 'badge-info-inverse' + let statusColor = item?.status === 'Preparing' ? 'badge-success-inverse' : item?.status === 'Active' ? 'badge-success-inverse' : item?.status == 'Refreshing' ? 'badge-danger-inverse' : 'badge-info-inverse' + let productUrl = '/product/'+ item?.product_id return ( {Number(item?.no) + Number(index)} - {item?.description} - {item?.url} + {item?.description} - {item?.url} {item?.status} diff --git a/src/component/home/RecentActions.jsx b/src/component/home/RecentActions.jsx index 5ec76a2..a3a0b49 100644 --- a/src/component/home/RecentActions.jsx +++ b/src/component/home/RecentActions.jsx @@ -83,20 +83,22 @@ export default function RecentActions() { #. Description - Date - Status + {/* Date */} + Date + Status {actionData && actionData?.actions.map((action, index) => { - let bgColor = action?.status == 'completed' ? 'badge-success-inverse' : action?.status == 'verifying' ? 'badge-info-inverse' : action?.status == 'processing' ? 'badge-warning-inverse' : 'badge-primary-inverse' + let bgColor = action?.status == '5' ? 'badge-success-inverse' : action?.status == '3' ? 'badge-info-inverse' : action?.status == '0' ? 'badge-warning-inverse' : 'badge-primary-inverse' + let text = action?.status == '5' ? 'completed' : action?.status == '3' ? 'verifying' : action?.status == '0' ? 'processing' : 'processing' return ( {action?.no} {action?.description} - {action?.date} + {new Date(action?.date).toDateString()} - + ) diff --git a/src/component/home/TopBar.jsx b/src/component/home/TopBar.jsx index f2f1fd9..7d2b83c 100644 --- a/src/component/home/TopBar.jsx +++ b/src/component/home/TopBar.jsx @@ -11,7 +11,6 @@ export default function TopBar() { }) const topData = data?.data?.bar_data?.top_bar - console.log('TOP', topData) return ( <> diff --git a/src/component/layout/layoutcom/UserHeader.jsx b/src/component/layout/layoutcom/UserHeader.jsx index bf0aff0..88d210c 100644 --- a/src/component/layout/layoutcom/UserHeader.jsx +++ b/src/component/layout/layoutcom/UserHeader.jsx @@ -12,6 +12,7 @@ export default function UserHeader(){ const toggleSidebar = (e) => { e.preventDefault() document.body.classList.toggle('sidebar-toggled') + document.querySelector('.navbar-collapse').classList.remove('show') } const removeSidebar = (e) => { @@ -64,230 +65,12 @@ export default function UserHeader(){
    - {/*
  • - - -
  • */} - {/*
  • - - -
  • */} - {/*
  • - - - -
    -
    - -
    -
    - -
    - - -
    - -
    -
    -
  • */}
  • - avtar-img -
    +
    diff --git a/src/component/product/ProductActive.jsx b/src/component/product/ProductActive.jsx new file mode 100644 index 0000000..423869a --- /dev/null +++ b/src/component/product/ProductActive.jsx @@ -0,0 +1,140 @@ +import React from "react"; +import getImage from "../../utils/getImage"; +import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; + + +export default function ProductActive(){ + + return( + <> + + {/*
    */} + {/*
    Coming Soon
    */} + {/*
    */} + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + users-avatar +
    +
    +

    Product Short Name

    +

    last Update : 00:00:0000

    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    Edit Your Product Settings
    +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    +
    + + +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    Your External Link
    +
    +
    +
    +
    + + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + ) +} \ No newline at end of file diff --git a/src/component/product/ProductFactory.jsx b/src/component/product/ProductFactory.jsx index 9efeea5..ec17ee7 100644 --- a/src/component/product/ProductFactory.jsx +++ b/src/component/product/ProductFactory.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, {useState} from "react"; import { useQuery } from '@tanstack/react-query' import BreadcrumbComBS from "../breadcrumb/BreadcrumbComBS"; // import getImage from "../../utils/getImage"; @@ -6,25 +6,36 @@ import ProductStart from "./ProductStart"; import { useLocation } from 'react-router-dom'; import {MyProductData, productData} from "../../services/services"; import queryKeys from "../../services/queryKeys"; +import ProductActive from "./ProductActive"; +import ProductProvision from "./ProductProvision"; +import {productConst} from "../../constants/products"; export default function ProductFactory(){ const location = useLocation(); const pathname = location.pathname; + const [productStatus, setProductStatus] = useState(0); + + //productConst.PRODUCT_ACTIVE + - // Split the pathname by '/' and get the last element const lastPart = pathname.split('/').pop(); - console.log(lastPart) + // console.log(lastPart) const {data, isFetching, isError, error} = useQuery({ queryKey: queryKeys.product, queryFn: () => MyProductData(lastPart) }) + + const myproduct_data = data?.data?.myproduct_data + //setProductStatus(myproduct_data?.status) const product_name = myproduct_data?.product_name; + const product_status = myproduct_data?.status; + return( <> @@ -45,7 +56,18 @@ export default function ProductFactory(){
    :
    - + {(product_status <= productConst.PRODUCT_AVAILABLE)? + + :<> } + + {(product_status === productConst.PRODUCT_PROVISIONING)? + + :<> } + + {(product_status === productConst.PRODUCT_ACTIVE)? + + :<> } +
    }
    diff --git a/src/component/product/ProductProvision.jsx b/src/component/product/ProductProvision.jsx new file mode 100644 index 0000000..ef545a2 --- /dev/null +++ b/src/component/product/ProductProvision.jsx @@ -0,0 +1,120 @@ +import { useEffect } from "react"; +import { useQuery } from "@tanstack/react-query"; +import queryKeys from "../../services/queryKeys"; +import { productProvision } from "../../services/services"; +import getImage from "../../utils/getImage"; +import { SocketContextValues } from "../context/SocketIOContext"; + + +export default function ProductProvision(props){ + const {joinRoom} = SocketContextValues() // Destructures values from socket context + + const productTitle = props?.productData?.title; + const productDescription = props?.productData?.description; + const productID = props?.productData?.product_id + const productSubUID = props?.productData?.product_subscription_uid + + const {data:provision, isFetching, isError, error} = useQuery({ + queryKey: queryKeys.myproduct_provision, + queryFn: () => productProvision(productID) + }) + + const provisionData = provision?.data?.provision + + useEffect(()=>{ + joinRoom(productSubUID); // provision subscription room + },[]) + + return ( + <> + {isFetching ? + <> +
    +
    +

    Loading...

    +
    +
    + + : isError ? +
    +
    +

    {error.message}

    +
    +
    + : + <> +
    +
    +
    +
    +
    +

    Creating - {productTitle}

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    +
    +

    Progress Information

    +
    +
    +
    +
    + + + + + + + + + + {provisionData?.activities?.map(item => ( + + + + + + ))} + +
    #Action
    {item.id}{item.action}
    +
    +
    +
    +
    + +
    +
    +

    Started creating your selection

    + Card image cap +
    +

    + {productDescription} +

    +
    +
    + +
    +
    + + } + + ) +} \ No newline at end of file diff --git a/src/component/product/ProductStart.jsx b/src/component/product/ProductStart.jsx index 256ebc9..172e777 100644 --- a/src/component/product/ProductStart.jsx +++ b/src/component/product/ProductStart.jsx @@ -1,22 +1,54 @@ -import React, { useRef } from "react"; +import React, { useRef, useState } from "react"; import getImage from "../../utils/getImage"; -import { Modal } from "bootstrap"; +// import { Modal } from "bootstrap"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { subscribe } from '../../services/services' +import queryKeys from "../../services/queryKeys"; + export default function ProductStart(props){ - console.log(props) - const productBanner = "product/"+props.productData.banner; - const productTitle = props.productData.title; - const productDescription = props.productData.description; - const promotion_text = props.productData.promotion_text; + const queryClient = useQueryClient() + const [requestStatus, setRequestStatus] = useState({status:false, message: ''}) + + const product_uid = props?.productData?.product_uid; + const product_id = props?.productData?.product_id; + const productBanner = "product/"+props.productData?.banner; + const productTitle = props.productData?.title; + const productDescription = props.productData?.description; + const promotion_text = props.productData?.promotion_text; + const product_status = props.productData?.status; const modalRef = useRef() - const hideModal = () => { - // modalRef.current.hide() + const refetch = () => { + queryClient.refetchQueries({ + queryKey: [...queryKeys.product], + // type: 'active', + // exact: true, + }) + } - // document.body.classList.remove('modal-open') - // const modal = new Modal(document.querySelector('.modal')) - // modal.hide() + const mutation = useMutation({ + mutationFn: (fields) => { + return subscribe(fields) + }, + onError: (error) => { + setRequestStatus({status:false, message:'failed, try again'}) + console.log('ERROR IS', error) + }, + onSuccess: (res) => { + setRequestStatus({status:true, message:'successful'}) + console.log(res) + }, + onSettled: () => { + setTimeout(()=>{ + setRequestStatus({status:false, message:''}) + },4000) + } + }) + + const handleSubscribe = () => { + mutation.mutate({product_id: product_id}) } return ( @@ -32,25 +64,43 @@ export default function ProductStart(props){
    -
    - {/*Card image cap*/} -
    -

    Subscription

    -

    Start with your goals in mind and then work possible.ith yand Goals. If the plan doesn’t support the vision then change it!

    -
    -
      -
    • {promotion_text}

    • -
    • 90 days free and 3.95/Month
    • -
    • -
    - {/*
    */} - {/* Card link*/} - {/* Another link*/} - {/*
    */} -
    - -
    -
    + { + (product_status===1) ? <> +
    + <> + Card image cap + + +
    +

    Start with your goals in mind and then work possible.

    +
    +
      +
    • Coming soon!!!

    • +
    • +
    +
    + : <> +
    + {/*Card image cap*/} +
    +

    Subscription

    +

    Start with your goals in mind and then work possible.ith yand Goals. If the plan doesn’t support the vision then change it!

    +
    +
      +
    • {promotion_text}

    • +
    • 90 days free and 3.95/Month
    • +
    • +
    + {/*
    */} + {/* Card link*/} + {/* Another link*/} + {/*
    */} +
    + +
    +
    + + }
    @@ -59,8 +109,8 @@ export default function ProductStart(props){
    -
    Modal title
    -
    @@ -68,19 +118,36 @@ export default function ProductStart(props){

    Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus +

    +

    sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla. Cras mattis consectetur purus sit amet fermentum. Cras justo odio, - dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur - ac, vestibulum at eros. Praesent commodo cursus magna, vel scelerisque nisl consectetur - et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean - lacinia bibendum nulla sed consectetur.

    + {/* {mutation.error && + <> +
    +

    {mutation.error.message}

    +
    + + } + {mutation.isSuccess && + <> +
    +

    {'subscription is successful'}

    +
    + + } */} + {requestStatus.message && ( +
    +

    {requestStatus.message}

    +
    + )}
    - - + +
diff --git a/src/component/settings/Settings.jsx b/src/component/settings/Settings.jsx index 27ad758..c79b29d 100644 --- a/src/component/settings/Settings.jsx +++ b/src/component/settings/Settings.jsx @@ -7,9 +7,292 @@ export default function Settings(){ return( <> -
-
Coming Soon
+ {/*
*/} + {/*
Coming Soon
*/} + {/*
*/} + + +
+
+
+
+
+
+
+
+
+
+ users-avatar +
+
+

Alice Williams

+

Enthusiast

+
+
+
+ +
+
    +
  • +
    +

    90

    +

    Post

    +
    +
  • + +
  • +
    +

    1.5K

    +

    Messages

    +
    +
  • + +
  • +
    +

    4.4K

    +

    Members

    +
    +
  • +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
Edit Your Personal Settings
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ +
+
+
+
+
+
+
+
Your External Link
+
+
+
+
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+
+
+
+
+ ) } \ No newline at end of file diff --git a/src/component/users/Users.jsx b/src/component/users/Users.jsx index b5b7129..ff62058 100644 --- a/src/component/users/Users.jsx +++ b/src/component/users/Users.jsx @@ -7,9 +7,511 @@ export default function Users(){ return( <> -
-
Coming Soon
-
+ <> + {/*
*/} + {/*
Coming Soon
*/} + {/*
*/} +
+
+
+
+
+
+ +
+
+

Lizzy Halfman

+

Home

+
+
+
+
    +
  • +
    +
  • +
  • +

    021-843-8478

    +
  • +
+
    +
  • +
    +
  • +
  • +

    40-1440-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    Lizzy.Halfman@gmail.com

    +
  • +
+
+
+
+
+
+
+
+
+
+ +
+
+

Samuel Woods

+

Friends

+
+
+
+
    +
  • +
    +
    +
    +
  • +
  • +

    026-123-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    80-1230-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    Samuel.Woods@gmail.com

    +
  • +
+
+
+
+
+
+
+
+
+
+ +
+
+

Garettdon

+

Office

+
+
+
+
    +
  • +
    +
  • +
  • +

    026-123-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    80-1230-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    Garettdon@gmail.com

    +
  • +
+
+
+
+
+
+
+
+
+
+ +
+
+

Garynice

+

Home

+
+
+
+
    +
  • +
    +
  • +
  • +

    026-123-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    80-1230-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    Garynice@gmail.com

    +
  • +
+
+
+
+
+
+
+
+
+
+ +
+
+

Andrew nico

+

Friends

+
+
+
+
    +
  • +
    +
  • +
  • +

    026-123-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    80-1230-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    Andrew.nico@gmail.com

    +
  • +
+
+
+
+
+
+
+
+
+
+ +
+
+

Michaelveld

+

Office

+
+
+
+
    +
  • +
    +
  • +
  • +

    026-123-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    80-1230-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    Michaelveld@gmail.com

    +
  • +
+
+
+
+
+
+
+
+
+
+ +
+
+

Jimmy Falicon

+

Home

+
+
+
+
    +
  • +
    +
  • +
  • +

    026-123-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    80-1230-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    Jimmy.Falicon@gmail.com

    +
  • +
+
+
+
+
+
+
+
+
+
+ +
+
+

Richardket

+

Friends

+
+
+
+
    +
  • +
    +
  • +
  • +

    026-123-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    80-1230-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    Richardket@gmail.com

    +
  • +
+
+
+
+
+
+
+
+
+
+ +
+
+

Jenny Smithnig

+

Office

+
+
+
+
    +
  • +
    +
  • +
  • +

    026-123-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    80-1230-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    Jenny Smithnig@gmail.com

    +
  • +
+
+
+
+
+
+
+
+
+
+ +
+
+

Garettdon

+

Home

+
+
+
+
    +
  • +
    +
  • +
  • +

    026-123-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    80-1230-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    Garettdon@gmail.com

    +
  • +
+
+
+
+
+
+
+
+
+
+ +
+
+

Lizzy Halfman

+

Friends

+
+
+
+
    +
  • +
    +
    +
    +
  • +
  • +

    026-123-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    80-1230-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    your@gmail.com

    +
  • +
+
+
+
+
+
+
+
+
+
+ +
+
+

Brian Joedon

+

Office

+
+
+
+
    +
  • +
    +
  • +
  • +

    026-123-8546

    +
  • +
+
    +
  • +
    +
  • +
  • +

    80-1205-8156

    +
  • +
+
    +
  • +
    +
  • +
  • +

    Brian.Joedon@gmail.com

    +
  • +
+
+
+
+
+
+ + + ) } \ No newline at end of file diff --git a/src/constants/products.js b/src/constants/products.js new file mode 100644 index 0000000..6742e1e --- /dev/null +++ b/src/constants/products.js @@ -0,0 +1,6 @@ +export const productConst = { + PRODUCT_AVAILABLE: 5, + PRODUCT_PROVISIONING: 6, + PRODUCT_ACTIVE: 7, + PRODUCT_DEACTIVATED: 9, +}; \ No newline at end of file diff --git a/src/css/style.scss b/src/css/style.scss index 61ea7ca..39f9815 100644 --- a/src/css/style.scss +++ b/src/css/style.scss @@ -91,9 +91,6 @@ $btn-bg: #8e54e9; $btn-border: #8e54e9; $event-padding: 10px; -@import 'react-big-calendar/lib/sass/styles'; -@import 'react-big-calendar/lib/addons/dragAndDrop/styles'; // if using DnD - .extraProductCard{ background-color: aliceblue; border-radius: 5px; @@ -104,7 +101,10 @@ $event-padding: 10px; padding: 10px; text-align: center; } - +.panel_coming_soon_c3{ + background-color: aliceblue; + border-radius: 10px; +} .panel_round_c1{ background-color: #e6f5f4; border-radius: 10px; @@ -122,30 +122,30 @@ $event-padding: 10px; /* CALENDER STYLE HERE */ -// .rbc-today{ -// background-color: '#fcf8e3' !important; -// } -.rbc-toolbar button { - background: #eceef3; +.fc-next-button.fc-button, .fc-prev-button.fc-button, +.fc-timeGridWeek-button.fc-button, .fc-timeGridDay-button.fc-button, .fc-dayGridMonth-button.fc-button { + background: #eceef3!important; border: none; color: #a6a9b7 !important; - text-transform: capitalize; - box-shadow: none!important; - text-shadow: none!important; - border-radius: 3px!important; - margin: 0 3px!important; - padding: 6px 12px!important; - height: auto!important; + text-transform: capitalize !important; + // box-shadow: none!important; + // text-shadow: none!important; + // border-radius: 3px!important; + // margin: 0 3px!important; + // padding: 6px 12px!important; + // height: auto!important; } -.rbc-toolbar-label{ - color: black !important; - font-size: 1.5rem; - font-weight: 700; -} -// .rbc-month-view{ -// border: .5px solid #e8edf1!important; -// } -.rbc-toolbar button.rbc-active, .rbc-toolbar button:active, .rbc-toolbar button:hover{ + +.fc-today-button.fc-button, +.fc-timeGridWeek-button.fc-button-active, .fc-timeGridDay-button.fc-button-active, .fc-dayGridMonth-button.fc-button-active{ color: #fff!important; + border: none; + background-color: #8E54E9!important; + text-transform: capitalize !important; +} + +.fc-event.fc-event-draggable.fc-event-start.fc-event-end.fc-daygrid-event, +.fc-event.fc-event-draggable.fc-event-start.fc-event-end.fc-daygrid-event{ + padding: 10px 2px !important; } /* END OF CALENDER STYLE */ \ No newline at end of file diff --git a/src/services/queryKeys.js b/src/services/queryKeys.js index 3a25f02..374cebe 100644 --- a/src/services/queryKeys.js +++ b/src/services/queryKeys.js @@ -3,7 +3,8 @@ const queryKeys = { topBar: ['top-bar'], recentAction: ['recent-action'], product: ['product-data'], - product_url: ['product_url'] + product_url: ['product_url'], + myproduct_provision: ['myproduct_provision'] } export default queryKeys \ No newline at end of file diff --git a/src/services/services.js b/src/services/services.js index 700be83..c843224 100644 --- a/src/services/services.js +++ b/src/services/services.js @@ -58,6 +58,30 @@ export const signUpUser = (reqData) => { return postAuxEnd('/panel/auth/register', postData, false) } +// FUNCTION TO VERIFY EMAIL +export const verifyEmail = (reqData) => { + let postData = { + ...reqData + } + return postAuxEnd('/panel/auth/register/verify', postData, false) +} + +// FUNCTION TO COMPLETE REGISTRATION +export const completeRegistration = (reqData) => { + let postData = { + ...reqData + } + return postAuxEnd('/panel/auth/register/complete', postData, false) +} + +// FUNCTION TO SUBSCRIBE +export const subscribe = (reqData) => { + let postData = { + ...reqData + } + return postAuxEnd('/panel/myproduct/subscription', postData, false) +} + // FUNCTION TO RESET USER PASSWORD export const recoverPWD = (reqData) => { @@ -82,6 +106,12 @@ export const recentActions = () => { return getAuxEnd(`/panel/account/actions`) } +// FUNCTION TO GET MY PRODUCT PROVISION DATA +export const productProvision = (productID) => { + const reqData = { product_id : productID} + return getAuxEnd(`/panel/myproduct/provision`,reqData) +} + // FUNCTION TO GET DASHBOARD PRODUCT DATA SECTION export const productData = () => { return getAuxEnd(`/panel/account/products`)