Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ad75e412b | |||
| e9d6689418 | |||
| ed38e0db98 | |||
| 351f027a6f | |||
| 20d56ee307 | |||
| f5c798c6c1 | |||
| a11f5100ba | |||
| 7628a561f8 | |||
| 615acff0b7 | |||
| aea161ccaf | |||
| 66a2bcacd5 | |||
| ec9d84b779 | |||
| 8ee793d11e | |||
| 46f17cbdfe | |||
| 283994e43a | |||
| b7d242ff0a | |||
| f6bae30bdd | |||
| a7e2e865de | |||
| 51887cf0d6 | |||
| 5ea1047356 | |||
| 9b9a7cc5da | |||
| 290e1fbd7e | |||
| 6884aa19b2 | |||
| 597a45dcba | |||
| 4f7274c30c | |||
| 290356780c | |||
| bd1450887b | |||
| 923a2483ed | |||
| 7f0ccf35b2 | |||
| af2fdcede6 | |||
| a9f2136125 | |||
| 5d04abc0db | |||
| 26d095afda | |||
| cd030d8d12 | |||
| 4a0b7925d0 | |||
| 9d947f19b9 | |||
| 7a99beab57 | |||
| 02731c021a | |||
| fecae9d626 | |||
| a4867a1b73 | |||
| 51c2e4b568 | |||
| 756c084059 | |||
| 064973e190 | |||
| d165e9dc0f | |||
| 77109e8369 | |||
| 2924401c9b | |||
| f6c2b1129d | |||
| 8ba167a17c | |||
| 7f65b286b1 | |||
| afc9e5be0f | |||
| 7aefb555ed | |||
| 325d28c1a8 | |||
| 25269a44c3 | |||
| 47df35d076 | |||
| 1deb15029d | |||
| 30b284064d | |||
| 4cbe78efc3 | |||
| 92ac7d74f4 | |||
| f0382cea9e |
@@ -52,3 +52,10 @@ button{
|
||||
.accordion-button, .accordion-button:not(.collapsed) {
|
||||
background-color: transparent!important;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.modal-50 {
|
||||
min-width: 50%;
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
@@ -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: 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 |
@@ -18,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"),
|
||||
})
|
||||
|
||||
@@ -66,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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`}>
|
||||
{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">
|
||||
<div className="report-details overflow-hidden">
|
||||
<p><span style={{fontWeight: 'bolder', color: '#00557A'}}>{product?.status_text}</span></p>
|
||||
<h4><span className='text-truncate' style={{paddingLeft: '10px'}}>{product?.name}</span></h4>
|
||||
<h4><span className='w-100 d-inline-block text-truncate' style={{paddingLeft: '10px'}}>{product?.name}</span></h4>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
<div className="apexchart-wrapper">
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function UserFooter(){
|
||||
<p>© 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,7 +74,11 @@ 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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -66,7 +66,7 @@ export default function ProductActive({productData}){
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card-body" style={{minHeight: '680px'}}>
|
||||
<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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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,12 +98,27 @@ 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'}`}
|
||||
@@ -112,6 +128,10 @@ const Settings = memo(({productData}) => {
|
||||
<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>
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import React, {memo, useState} from 'react'
|
||||
import getImage from "../../../utils/getImage";
|
||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
||||
import queryKeys from '../../../services/queryKeys';
|
||||
import {getTemplateConfig} from '../../../services/services';
|
||||
import {Link} from "react-router-dom";
|
||||
import siteLinks from "../../../links/siteLinks";
|
||||
import UploadModal from './UploadModal';
|
||||
|
||||
const TemplateConfigure = ({productData}) => {
|
||||
|
||||
const [selectedSectionDetails, setSelectedSectionDetails] = useState({})
|
||||
|
||||
// /panel/myproduct/template-config
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const {data: templateData, isFetching, isError, error} = useQuery({
|
||||
queryKey: queryKeys.myTemplateConfig,
|
||||
queryFn: () => {
|
||||
let reqData = {
|
||||
token: localStorage.getItem('token'), // USER TOKEN
|
||||
uid: localStorage.getItem('uid'), // USER UID
|
||||
product_id: productData?.product_id
|
||||
}
|
||||
return getTemplateConfig(reqData)
|
||||
},
|
||||
staleTime: 0
|
||||
})
|
||||
|
||||
const templateResponse = templateData?.data
|
||||
const templateImages = templateResponse?.template_images?.data;
|
||||
// debugger;
|
||||
console.log("templateResponse", templateResponse);
|
||||
// const currentColorUID = templateResponse?.current_colorstyle_uid
|
||||
// const color_styles = templateResponse?.color_styles
|
||||
// const custom_template_name = templateResponse?.custom_template_name
|
||||
|
||||
return <>
|
||||
<div className="card card-statistics">
|
||||
{isFetching ?
|
||||
<>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<p className='text-mute'>Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
: isError ?
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<p className='text-danger'>{error?.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
<>
|
||||
<div className="card-header">
|
||||
<div className="card-heading">
|
||||
<h4 className="card-title"
|
||||
style={{textTransform: 'none'}}>{templateResponse?.template_name}</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<div className="col-12">
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<h4>Image List</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<ul className="list-unstyled">
|
||||
{templateImages && templateImages.map(
|
||||
(item) => {
|
||||
const currImage = item?.default_val;
|
||||
return (
|
||||
<li className="media">
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
padding: '5px',
|
||||
backgroundColor: 'aliceblue',
|
||||
margin: '2px',
|
||||
maxHeight: '150px'
|
||||
}}>
|
||||
<div className='d-flex justify-content-center align-items-center' style={{padding: '6px', width: '120px', height: '100px'}}>
|
||||
<img className="mb-xxs-0 img-fluid"
|
||||
style={{height: 'auto', maxHeight: '100px'}}
|
||||
src={currImage} alt="image"/>
|
||||
</div>
|
||||
|
||||
<div className="media-body" style={{padding: '2px'}}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
}}>
|
||||
<div style={{textAlign: 'right',width: '100%'}}></div>
|
||||
{/* [Change Image] */}
|
||||
<label 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,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">×</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 */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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">×</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 */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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">×</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 */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 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: {},
|
||||
@@ -54,15 +32,6 @@ export default function Settings() {
|
||||
},[profileInfo])
|
||||
// console.log('INI', intialData)
|
||||
|
||||
|
||||
const updateProfile = (values, helpers) => {
|
||||
console.log('Values', values)
|
||||
}
|
||||
|
||||
const updateLinks = (values, helpers) => {
|
||||
console.log('Values', values)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbComBS title='Settings' paths={['Dashboard', 'Settings']}/>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -11,6 +11,7 @@ const siteLinks = {
|
||||
comments: '/comments',
|
||||
reports: '/reports',
|
||||
subscription: '/subscription',
|
||||
my_media: '/my-files',
|
||||
subscription_success:'/subscription-success',
|
||||
subscribe: '/subscribe',
|
||||
onboard: '/subscription',
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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,7 +22,16 @@ 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 => {
|
||||
// throw new Error(err.response.data.error_message);
|
||||
@@ -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 = {
|
||||
@@ -220,6 +244,14 @@ export const getProductColorStyles = (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 = {
|
||||
@@ -252,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 = {
|
||||
@@ -268,6 +353,15 @@ export const setExternalURL = (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')
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export const userSlice = createSlice({
|
||||
initialState,
|
||||
reducers: {
|
||||
updateUserDetails: (state, action) => {
|
||||
state.userDetails = { ...action.payload };
|
||||
state.userDetails = { ...state.userDetails, ...action.payload };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import React from 'react'
|
||||
import MyMedia from "../component/mymedia/MyMedia";
|
||||
|
||||
export default function MyMediaPage() {
|
||||
return (
|
||||
<MyMedia />
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user