Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 325d28c1a8 | |||
| 25269a44c3 | |||
| 47df35d076 | |||
| 1deb15029d | |||
| 30b284064d | |||
| 4cbe78efc3 | |||
| 92ac7d74f4 | |||
| f0382cea9e | |||
| 2123af0abe | |||
| a3c306bf89 | |||
| a61abe718a | |||
| 2b91506c61 | |||
| 253cace3fe | |||
| aa55a1a4e0 | |||
| 8b763882fa | |||
| 30540e46ba | |||
| b195b1f787 | |||
| 2ddd04a1a1 |
@@ -16,3 +16,6 @@ REACT_APP_TERMS_LINK='https://qa-www.mermsemr.com/terms'
|
||||
# Inactivity timeout/logout AT 10MINS
|
||||
REACT_APP_TIMEOUT=600000
|
||||
|
||||
# show download button
|
||||
REACT_APP_SHOW_DOWNLOAD=0
|
||||
|
||||
|
||||
@@ -17,3 +17,6 @@ REACT_APP_TERMS_LINK='https://qa-www.mermsemr.com/terms'
|
||||
# Inactivity timeout/logout AT 10MINS
|
||||
REACT_APP_TIMEOUT=600000
|
||||
|
||||
# show download button
|
||||
REACT_APP_SHOW_DOWNLOAD=0
|
||||
|
||||
|
||||
+4
-1
@@ -14,4 +14,7 @@ REACT_APP_CONTACTS_LINK='https://www.mermsemr.com/contacts'
|
||||
REACT_APP_TERMS_LINK='https://www.mermsemr.com/terms'
|
||||
|
||||
# Inactivity timeout/logout AT 10MINS
|
||||
REACT_APP_TIMEOUT=600000
|
||||
REACT_APP_TIMEOUT=600000
|
||||
|
||||
# show download button
|
||||
REACT_APP_SHOW_DOWNLOAD=0
|
||||
@@ -5,27 +5,31 @@ import IOSDownload from '../../assets/img/download/apple.jpg'
|
||||
export default function AuthFooter() {
|
||||
return (
|
||||
<div className='w-100'>
|
||||
<div className="row" style={{margin: '5px'}}>
|
||||
<hr />
|
||||
</div>
|
||||
<div className="row" style={{marginTop: '20px'}}>
|
||||
<div className="col-6">
|
||||
<div className="app-store-icons-wrap text-center">
|
||||
<a className="icon google"
|
||||
href='#' >
|
||||
<img src={IOSDownload} className='w-80 h-auto' alt='IOS Download' />
|
||||
</a>
|
||||
</div>
|
||||
{Number(process.env.REACT_APP_SHOW_DOWNLOAD) == 100 &&
|
||||
<>
|
||||
<div className="row" style={{margin: '5px'}}>
|
||||
<hr />
|
||||
</div>
|
||||
<div className="row" style={{marginTop: '20px'}}>
|
||||
<div className="col-6">
|
||||
<div className="app-store-icons-wrap text-center">
|
||||
<a className="icon google"
|
||||
href='#' >
|
||||
<img src={IOSDownload} className='w-80 h-auto' alt='IOS Download' />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-6">
|
||||
<div className="app-store-icons-wrap text-center">
|
||||
<a className="icon apple" href='#'>
|
||||
<img src={GoogleDownload} className='w-80 h-auto' alt='IOS Download' />
|
||||
</a>
|
||||
<div className="col-6">
|
||||
<div className="app-store-icons-wrap text-center">
|
||||
<a className="icon apple" href='#'>
|
||||
<img src={GoogleDownload} className='w-80 h-auto' alt='IOS Download' />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
<div className="login-links">
|
||||
<a href={process.env.REACT_APP_HOME_LINK}>Home</a>
|
||||
|
||||
@@ -8,6 +8,7 @@ import siteLinks from '../../links/siteLinks'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { recoverPWD } from '../../services/services';
|
||||
import getImage from '../../utils/getImage';
|
||||
import AuthFooter from './AuthFooter';
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
username: Yup.string()
|
||||
@@ -17,7 +18,7 @@ const validationSchema = Yup.object().shape({
|
||||
// "Invalid email format"
|
||||
// )
|
||||
.min(3, "Minimum 3 characters")
|
||||
.max(50, "Maximum 50 characters")
|
||||
.max(25, "Maximum 25 characters")
|
||||
.required("Email is required"),
|
||||
})
|
||||
|
||||
@@ -49,7 +50,7 @@ export default function Forgetpwd2() {
|
||||
<div className="row no-gutters justify-content-center">
|
||||
<div className="col-11 col-sm-6 col-lg-5 col-xxl-4 align-self-center order-2 order-sm-1h" style={{maxWidth: '520px'}}>
|
||||
<div className="mt-5 d-flex">
|
||||
<div className="bg-white register p-5">
|
||||
<div className="bg-white register px-5 pt-5 pb-3">
|
||||
<h1 className="mb-2">{process.env.REACT_APP_PANEL_NAME}</h1>
|
||||
{!mutation.isSuccess && <p>Please enter your username.</p>}
|
||||
<Formik
|
||||
@@ -65,7 +66,7 @@ export default function Forgetpwd2() {
|
||||
<>
|
||||
<div className="col-12">
|
||||
<div className="form-group">
|
||||
<label className={`text-black fw-bold control-label ${(props.errors.username && props.touched.username) && 'text-danger'}`}>Username*</label>
|
||||
<label className={`text-black fw-bold control-label`}>Username* <span className='text-danger' style={{fontSize: '12px'}}>{(props.errors.username && props.touched.username) && props.errors.username}</span></label>
|
||||
<input type="text" name='username' className="form-control" placeholder="Username" value={props.values.username} onChange={props.handleChange} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,6 +98,7 @@ export default function Forgetpwd2() {
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
<AuthFooter />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -46,12 +46,12 @@ export default function Login() {
|
||||
console.log(error)
|
||||
},
|
||||
onSuccess: (res) => {
|
||||
if(res?.data?.error_message){
|
||||
if(res?.data && res?.data?.error_message){
|
||||
throw({message: res?.data?.error_message})
|
||||
}
|
||||
const {token, room, uid} = res?.data
|
||||
if(!token || !room){
|
||||
throw({message: 'something went wrong, try again!'})
|
||||
throw({message: 'Unable to complete your login, Please try again!'})
|
||||
}
|
||||
localStorage.setItem('token', token)
|
||||
localStorage.setItem('room', room)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -103,7 +103,7 @@ const ColorStyleConfigure = memo(({name = 'Full Name', data, productData}) => {
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
<div className="row">
|
||||
<div className="row overflow-y-auto" style={{maxHeight: '550px'}}>
|
||||
<>
|
||||
{!color_styles?.length ?
|
||||
<p>No data Found</p>
|
||||
|
||||
@@ -98,7 +98,7 @@ const SiteTemplateSelector = memo(({name = 'Full Name', data, productData}) => {
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
<div className="row">
|
||||
<div className="row overflow-y-auto" style={{maxHeight: '550px'}}>
|
||||
<>
|
||||
{!templates?.length ?
|
||||
<p>No data Found</p>
|
||||
|
||||
@@ -8,7 +8,55 @@ export default function Reports(){
|
||||
<>
|
||||
<BreadcrumbComBS title='Reports' paths={['Dashboard', 'Reports']} />
|
||||
<div className="row">
|
||||
<div className="vh-100 col-12 flex align-items-center">Coming Soon</div>
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
<div>
|
||||
|
||||
<div className="card card-statistics" style={{minHeight:'550px'}}>
|
||||
{/*<div className="card-header">*/}
|
||||
{/* <div className="card-heading">*/}
|
||||
{/* <h4 className="card-title"> Tab vertical </h4>*/}
|
||||
{/* </div>*/}
|
||||
{/*</div>*/}
|
||||
<div className="card-body">
|
||||
<div className="tab tab-vertical">
|
||||
<ul className="nav nav-tabs" role="tablist">
|
||||
<li className="nav-item">
|
||||
<a className="nav-link active show" id="home-09-tab" data-toggle="tab" href="#home-09" role="tab" aria-controls="home-09" aria-selected="true"> Home</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a className="nav-link" id="profile-09-tab" data-toggle="tab" href="#profile-09" role="tab" aria-controls="profile-09" aria-selected="false"> Profile </a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a className="nav-link" id="portfolio-09-tab" data-toggle="tab" href="#portfolio-09" role="tab" aria-controls="portfolio-09" aria-selected="false">Portfolio </a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a className="nav-link" id="contact-09-tab" data-toggle="tab" href="#contact-09" role="tab" aria-controls="contact-09" aria-selected="false"> Contact </a>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="tab-content">
|
||||
<div className="tab-pane fade active show" id="home-09" role="tabpanel" aria-labelledby="home-09-tab">
|
||||
<p>Positive pleasure-oriented goals are much more powerful motivators than negative fear-based ones. Although each is successful separately, the right combination of both is the most powerful motivational force known to humankind.Make a list of your achievements toward your long-term goal and remind yourself that intentions don’t count, only action’s.</p>
|
||||
</div>
|
||||
<div className="tab-pane fade" id="profile-09" role="tabpanel" aria-labelledby="profile-09-tab">
|
||||
<p>Reflect and experiment until you find the right combination of motivators for your personality and your personal goals. Do it today. Remind yourself of someone you know who died suddenly and the fact that there is no guarantee that tomorrow will come.</p>
|
||||
</div>
|
||||
<div className="tab-pane fade" id="portfolio-09" role="tabpanel" aria-labelledby="portfolio-09-tab">
|
||||
<p>Commitment is something that comes from understanding that everything has its price and then having the willingness to pay that price. This is important because nobody wants to put significant effort into something, only to find out after the fact that the price was too high. We all know people who live this truth.Give yourself the power of responsibility.</p>
|
||||
</div>
|
||||
<div className="tab-pane fade" id="contact-09" role="tabpanel" aria-labelledby="contact-09-tab">
|
||||
<p>I truly believe Augustine’s words are true and if you look at history you know it is true. There are many people in the world with amazing talents who realize only a small percentage of their potential. We all know people who live this truth.Give yourself the power of responsibility. Remind yourself the only thing stopping you is yourself.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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().required("facebook is required"),
|
||||
// twitter_url: Yup.string().required("twitter is required"),
|
||||
// blogger_url: Yup.string().required("blog is required"),
|
||||
// google_url: Yup.string().required("google is required"),
|
||||
// linked_url: Yup.string().required("linkedin is required"),
|
||||
// website_url: Yup.string().required("website is required"),
|
||||
})
|
||||
|
||||
export default function LinksForm({data}) {
|
||||
const modalRef = useRef(null)
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const [intialData] = useState(data)
|
||||
|
||||
const [infoToUpdate, setInfoToUpdate] = useState({})
|
||||
|
||||
const dismissModal = () => {
|
||||
const body = document.querySelector('body')
|
||||
body.removeAttribute('style')
|
||||
// body.classList.toggle('modal-open')
|
||||
|
||||
const modalBackdrop = document.querySelectorAll('.modal-backdrop')
|
||||
modalBackdrop.forEach(item => {
|
||||
if (item) {
|
||||
item.remove();
|
||||
}
|
||||
})
|
||||
|
||||
const modal = Modal.getInstance(modalRef.current);
|
||||
modal && modal.hide();
|
||||
};
|
||||
|
||||
// UPDATE LINKS MUTATION
|
||||
const updateLinksMutation = useMutation({
|
||||
mutationFn: (fields) => {
|
||||
return updateLinks(fields)
|
||||
},
|
||||
onSuccess: (res) => {
|
||||
if(res.data.resultCode != '0'){
|
||||
throw({message: res?.data?.resultDescription ? res?.data?.resultDescription : 'En error occured'})
|
||||
}
|
||||
},
|
||||
onSettled: ()=>{
|
||||
setTimeout(() => {
|
||||
dismissModal() //CLOSE MODAL HERE
|
||||
queryClient.refetchQueries({
|
||||
queryKey: [...queryKeys.profile_data], // type: 'active', // exact: true,
|
||||
})
|
||||
updateLinksMutation.reset()
|
||||
}, 3000);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const handleSetInfoToUpdate = (values, helpers) => {
|
||||
setInfoToUpdate(values)
|
||||
var modal = new Modal(document.getElementById('modal_links'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
const proceed = () => {
|
||||
let reqData = {
|
||||
token: localStorage.getItem('token'), // USER TOKEN
|
||||
uid: localStorage.getItem('uid'), // USER UID
|
||||
...infoToUpdate
|
||||
}
|
||||
console.log(reqData)
|
||||
// updateLinksMutation.mutate(reqData)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Formik
|
||||
initialValues={intialData}
|
||||
validationSchema={linksValidationSchema}
|
||||
onSubmit={handleSetInfoToUpdate}
|
||||
>
|
||||
{(props) => {
|
||||
return (
|
||||
<Form className=''>
|
||||
<div className="form-group">
|
||||
<label htmlFor="fb">Facebook URL: {(props.errors.facebook_url && props.touched.facebook_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="facebook_url" value={props.values?.facebook_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="tr">Twitter URL: {(props.errors.twitter_url && props.touched.twitter_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="twitter_url" value={props.values?.twitter_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="br">Blogger URL: {(props.errors.blogger_url && props.touched.blogger_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="blogger_url" value={props.values?.blogger_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="go">Google+ URL: {(props.errors.google_url && props.touched.google_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="google_url" value={props.values?.google_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="li">LinkedIn URL: {(props.errors.linked_url && props.touched.linked_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="linked_url" value={props.values?.linked_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="we">Website URL: {(props.errors.website_url && props.touched.website_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="website_url" value={props.values?.website_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
<div style={{textAlign: "right"}}>
|
||||
<button type="submit" className="btn btn-primary">Update Links
|
||||
</button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
|
||||
{/* Vertical Center Modal */}
|
||||
<div ref={modalRef} className="modal fade" id="modal_links" tabIndex="-1" role="dialog" aria-hidden="true">
|
||||
<div className="modal-dialog modal-dialog-centered" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
{/* <h5 className="modal-title" style={{fontSize: '18px'}} id="verticalCenterTitle">{'productTitle'}</h5> */}
|
||||
<button type="button" className="close" data-bs-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<h5 className="text-center" style={{fontSize: '18px'}}>Are you sure, you want to update? gg</h5>
|
||||
{(updateLinksMutation.error || updateLinksMutation.isSuccess) && (
|
||||
<div className="col-12">
|
||||
<p className={`p-2 text-center ${updateLinksMutation.isSuccess ? 'text-success' : 'text-danger'}`}>
|
||||
{updateLinksMutation.isSuccess ? 'Updated Successfully' : updateLinksMutation.error.message}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" className="btn btn-primary" disabled={updateLinksMutation.isSuccess} onClick={proceed}>{updateLinksMutation.isPending ? 'Updating...' : 'Update'}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* END of Vertical Center Modal */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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,117 @@
|
||||
import React, { memo, useRef, useState } from 'react'
|
||||
// import { useSelector } from 'react-redux';
|
||||
import getImage from '../../utils/getImage';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { uploadProfileImg } from '../../services/services';
|
||||
import queryKeys from '../../services/queryKeys';
|
||||
|
||||
const ProfileImage = memo(({intialData}) => {
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const [selectedImg, setSelectedImg] = useState(null)
|
||||
|
||||
// const {userDetails} = useSelector((state) => state?.userDetails); // CHECKS FOR ACTIVE USER DETAILS
|
||||
const avtarImage = "avtar/merms-user.png";
|
||||
|
||||
// browser profile img
|
||||
const browserImg = useRef(null);
|
||||
const browseProfileImg = () => {
|
||||
browserImg.current.click();
|
||||
};
|
||||
|
||||
const profileImgChangeHandler = (event) => {
|
||||
setSelectedImg(event.target.files[0])
|
||||
}
|
||||
|
||||
const uploadProfileMutation = useMutation({
|
||||
mutationFn: (fields) => {
|
||||
if(!fields.img){
|
||||
throw new Error('Please, select an image')
|
||||
}
|
||||
return uploadProfileImg(fields)
|
||||
},
|
||||
onSuccess: (res) => {
|
||||
if(res.data.resultCode != '0'){
|
||||
throw({message: res?.data?.resultDescription ? res?.data?.resultDescription : 'An error occured'})
|
||||
}
|
||||
// const account_name = res?.data?.personal_data?.account_name
|
||||
// dispatch(updateUserDetails({ account_name }));
|
||||
},
|
||||
onSettled: ()=>{
|
||||
setTimeout(() => {
|
||||
queryClient.refetchQueries({
|
||||
queryKey: [...queryKeys.profile_data], // type: 'active', // exact: true,
|
||||
})
|
||||
uploadProfileMutation.reset()
|
||||
}, 3000);
|
||||
}
|
||||
})
|
||||
|
||||
const proceedToUpload = () => {
|
||||
let reqData = {
|
||||
token: localStorage.getItem('token'), // USER TOKEN
|
||||
uid: localStorage.getItem('uid'), // USER UID
|
||||
img: selectedImg
|
||||
}
|
||||
// console.log('reqData', reqData)
|
||||
uploadProfileMutation.mutate(reqData)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="col-xl-3 pb-xl-0 pb-5 border-right">
|
||||
<div className="page-account-profil pt-5">
|
||||
<div className="profile-img text-center rounded-circle">
|
||||
<div className="pt-5">
|
||||
<div className="bg-img m-auto">
|
||||
<img
|
||||
src={selectedImg ? URL.createObjectURL(selectedImg) : intialData?.personal_data?.picture ? intialData?.personal_data?.picture : getImage(avtarImage)}
|
||||
// src={getImage(avtarImage)}
|
||||
className="img-fluid" alt="user"
|
||||
/>
|
||||
<input
|
||||
ref={browserImg}
|
||||
className='d-none'
|
||||
type='file'
|
||||
accept="image/*"
|
||||
onChange={(e) => profileImgChangeHandler(e)}
|
||||
id='profile-image'
|
||||
/>
|
||||
</div>
|
||||
<div className="profile pt-4">
|
||||
<h4 className="mb-1">{intialData?.personal_data?.lastname} {intialData?.personal_data?.firstname}</h4>
|
||||
<div style={{padding: '10px'}}>
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="profile-btn text-center">
|
||||
<div>
|
||||
<button onClick={browseProfileImg} className="btn btn-light text-primary mb-2">Change
|
||||
</button>
|
||||
</div>
|
||||
{selectedImg &&
|
||||
<div>
|
||||
<button onClick={proceedToUpload} disabled={uploadProfileMutation.isSuccess || uploadProfileMutation.isPending} className="btn btn-light text-primary mb-2">
|
||||
{uploadProfileMutation.isPaused ? 'Upload...' : 'Upload New Avatar'}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
{/* success or error message */}
|
||||
{(uploadProfileMutation.isSuccess || uploadProfileMutation.isError) &&
|
||||
<div>
|
||||
<p className={`${uploadProfileMutation.isSuccess ? 'text-success' : 'text-danger'}`}>
|
||||
{uploadProfileMutation.isSuccess ? 'Uploaded successfully' : uploadProfileMutation.error.message}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default ProfileImage
|
||||
@@ -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: {},
|
||||
@@ -53,15 +31,6 @@ export default function Settings() {
|
||||
setInitialData({external_links: data?.external_links, personal_data: data?.personal_data})
|
||||
},[profileInfo])
|
||||
// console.log('INI', intialData)
|
||||
|
||||
|
||||
const updateProfile = (values, helpers) => {
|
||||
console.log('Values', values)
|
||||
}
|
||||
|
||||
const updateLinks = (values, helpers) => {
|
||||
console.log('Values', values)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -90,131 +59,14 @@ export default function Settings() {
|
||||
<div className="card card-statistics">
|
||||
<div className="card-body p-0" style={{backgroundColor: "#f9f9fb"}}>
|
||||
<div className="row no-gutters">
|
||||
<div className="col-xl-3 pb-xl-0 pb-5 border-right">
|
||||
<div className="page-account-profil pt-5">
|
||||
<div className="profile-img text-center rounded-circle">
|
||||
<div className="pt-5">
|
||||
<div className="bg-img m-auto">
|
||||
{/*<img src="assets/img/avtar/01.jpg" className="img-fluid"*/}
|
||||
{/* alt="users-avatar" />*/}
|
||||
<img src={getImage(avtarImage)}
|
||||
className="img-fluid" alt="user"/>
|
||||
</div>
|
||||
<div className="profile pt-4">
|
||||
<h4 className="mb-1">{intialData?.personal_data?.lastname} {intialData?.personal_data?.firstname}</h4>
|
||||
<div style={{padding: '10px'}}>
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="profile-btn text-center">
|
||||
<div>
|
||||
<button className="btn btn-light text-primary mb-2">Upload New Avatar
|
||||
</button>
|
||||
</div>
|
||||
{/*<div>*/}
|
||||
{/* <button className="btn btn-danger">Delete</button>*/}
|
||||
{/*</div>*/}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ProfileImage intialData={intialData} />
|
||||
<div className="col-xl-5 col-md-6 col-12 border-t border-right">
|
||||
<div className="page-account-form">
|
||||
<div className="form-titel border-bottom p-3">
|
||||
<h5 className="mb-0 py-2">Edit Your Personal Settings</h5>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<Formik
|
||||
initialValues={intialData?.personal_data}
|
||||
validationSchema={profileValidationSchema}
|
||||
onSubmit={updateProfile}
|
||||
>
|
||||
{(props) => {
|
||||
return (
|
||||
<Form className=''>
|
||||
<div className="form-row">
|
||||
<div className="form-group col-md-12">
|
||||
<label htmlFor="name1">First Name {(props.errors.firstname && props.touched.firstname) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="firstname" value={props.values?.firstname} onChange={props.handleChange} />
|
||||
</div>
|
||||
<div className="form-group col-md-12">
|
||||
<label htmlFor="name1">Last Name {(props.errors.lastname && props.touched.lastname) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="lastname" value={props.values?.lastname} onChange={props.handleChange} />
|
||||
</div>
|
||||
<div className="form-group col-md-12">
|
||||
<label htmlFor="name1">Account Name {(props.errors.account_name && props.touched.account_name) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="account_name" value={props.values?.account_name} onChange={props.handleChange} />
|
||||
</div>
|
||||
{/*<div className="form-group col-md-12">*/}
|
||||
{/* <label htmlFor="title1">Email</label>*/}
|
||||
{/* <input type="text" className="form-control" name="title1"*/}
|
||||
{/* value="email@email.com" />*/}
|
||||
{/*</div>*/}
|
||||
<div className="form-group col-md-12">
|
||||
<label htmlFor="phone1">Phone Number {(props.errors.phone && props.touched.phone) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="phone" value={props.values?.phone} onChange={props.handleChange} />
|
||||
</div>
|
||||
<div className="form-group col-md-12">
|
||||
<label htmlFor="email1">Email {(props.errors.email && props.touched.email) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="email" value={props.values?.email} onChange={props.handleChange} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="add1">Address {(props.errors.full_address && props.touched.full_address) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="full_address" value={props.values?.full_address} onChange={props.handleChange} />
|
||||
</div>
|
||||
{/*<div className="form-group">*/}
|
||||
{/* <label htmlFor="add2">Address 2</label>*/}
|
||||
{/* <input type="text" className="form-control" id="add2"*/}
|
||||
{/* value="1234 North Avenue Luke Lane, South Bend, IN 360001"/>*/}
|
||||
{/*</div>*/}
|
||||
|
||||
{/*<div className="form-row">*/}
|
||||
{/* <div className="form-group col-md-4">*/}
|
||||
{/* <label htmlFor="inputState3">City</label>*/}
|
||||
{/* <select id="inputState3" className="form-control">*/}
|
||||
{/* <option>Choose...</option>*/}
|
||||
{/* <option selected="">London</option>*/}
|
||||
{/* <option>Montreal</option>*/}
|
||||
{/* <option>Delhi</option>*/}
|
||||
{/* <option>Tokyo</option>*/}
|
||||
{/* </select>*/}
|
||||
{/* </div>*/}
|
||||
{/* <div className="form-group col-md-4">*/}
|
||||
{/* <label htmlFor="inputState4">State</label>*/}
|
||||
{/* <select id="inputState4" className="form-control">*/}
|
||||
{/* <option>Choose...</option>*/}
|
||||
{/* <option selected="">England</option>*/}
|
||||
{/* <option>California</option>*/}
|
||||
{/* <option>Texas</option>*/}
|
||||
{/* <option>Scotland</option>*/}
|
||||
{/* </select>*/}
|
||||
{/* </div>*/}
|
||||
{/* <div className="form-group col-md-4">*/}
|
||||
{/* <label htmlFor="inputZip">Zip</label>*/}
|
||||
{/* <input type="text" className="form-control" id="inputZip"*/}
|
||||
{/* value="EC1A 1BB" />*/}
|
||||
{/* </div>*/}
|
||||
{/*</div>*/}
|
||||
{/*<div className="form-group">*/}
|
||||
{/* <div className="form-check">*/}
|
||||
{/* <input className="form-check-input" type="checkbox"*/}
|
||||
{/* id="gridCheck" />*/}
|
||||
{/* <label className="form-check-label" htmlFor="gridCheck">*/}
|
||||
{/* I agree to receive email notification.*/}
|
||||
{/* </label>*/}
|
||||
{/* </div>*/}
|
||||
{/*</div>*/}
|
||||
<div style={{textAlign: "right"}}>
|
||||
<button type="submit" className="btn btn-primary">Update Profile
|
||||
</button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
<ProfileForm data={intialData.personal_data} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -224,50 +76,7 @@ export default function Settings() {
|
||||
<h5 className="mb-0 py-2">Your External Link</h5>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<Formik
|
||||
initialValues={intialData?.external_links}
|
||||
validationSchema={linksValidationSchema}
|
||||
onSubmit={updateLinks}
|
||||
>
|
||||
{(props) => {
|
||||
return (
|
||||
<Form className=''>
|
||||
<div className="form-group">
|
||||
<label htmlFor="fb">Facebook URL: {(props.errors.facebook_url && props.touched.facebook_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="facebook_url" value={props.values?.facebook_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="tr">Twitter URL: {(props.errors.twitter_url && props.touched.twitter_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="twitter_url" value={props.values?.twitter_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="br">Blogger URL: {(props.errors.blogger_url && props.touched.blogger_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="blogger_url" value={props.values?.blogger_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="go">Google+ URL: {(props.errors.google_url && props.touched.google_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="google_url" value={props.values?.google_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="li">LinkedIn URL: {(props.errors.linked_url && props.touched.linked_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="linked_url" value={props.values?.linked_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="we">Website URL: {(props.errors.website_url && props.touched.website_url) && <span className="text-danger">*</span>}</label>
|
||||
<input type="text" className="form-control" name="website_url" value={props.values?.website_url} onChange={props.handleChange} />
|
||||
</div>
|
||||
<div style={{textAlign: "right"}}>
|
||||
<button type="submit" className="btn btn-primary">Update Links
|
||||
</button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
<LinksForm data={intialData.external_links} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -23,11 +23,20 @@ axios.interceptors.request.use(
|
||||
|
||||
const postAuxEnd = (path, postData, media=false) => {
|
||||
const basePath = media ? process.env.REACT_APP_MAIN_API : process.env.REACT_APP_MAIN_API
|
||||
return axios.post(`${basePath}${path}`, postData).then(res => {
|
||||
let newPostData = {}
|
||||
if(!media){
|
||||
newPostData = {...postData}
|
||||
}else{
|
||||
newPostData = new FormData();
|
||||
for (let data in postData) {
|
||||
newPostData.append(data, postData[data]);
|
||||
}
|
||||
}
|
||||
return axios.post(`${basePath}${path}`, newPostData).then(res => {
|
||||
return res
|
||||
}).catch(err => {
|
||||
// console.log('res', err.response.data)
|
||||
throw new Error(err.response.data.error_message);
|
||||
// throw new Error(err.response.data.error_message);
|
||||
throw new Error(err);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -84,6 +93,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 null //postAuxEnd(`/panel/account/links-update`, postData, false)
|
||||
}
|
||||
|
||||
// FUNCTION TO GET PRODUCT BY ID
|
||||
export const MyProductData = (reqData) => {
|
||||
let postData = {
|
||||
@@ -268,6 +293,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,
|
||||
}
|
||||
throw new Error('Opps')
|
||||
// return postAuxEnd(`/panel/account/profile-update`, postData, true)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user