refactor: setting modals to pages
This commit is contained in:
@@ -1,46 +0,0 @@
|
||||
// import React from "react";
|
||||
import { useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useNavigate, useMatch } from "react-router-dom";
|
||||
import Modal from "../Modal";
|
||||
// import BASE_URL from "../../app/config";
|
||||
import { useLazyRemoveChannelQuery } from "../../../app/services/channel";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
|
||||
export default function DeleteConfirmModal({ id, closeModal }) {
|
||||
const navigateTo = useNavigate();
|
||||
const pathMatched = useMatch(`/chat/channel/${id}`);
|
||||
const [deleteChannel, { isLoading, isSuccess }] = useLazyRemoveChannelQuery();
|
||||
const handleDelete = () => {
|
||||
deleteChannel(id);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
toast.success("delete channel successfully!");
|
||||
if (pathMatched) {
|
||||
navigateTo("/chat");
|
||||
}
|
||||
closeModal();
|
||||
}
|
||||
}, [isSuccess, id, pathMatched]);
|
||||
if (!id) return null;
|
||||
return (
|
||||
<Modal id="modal-modal">
|
||||
<StyledModal
|
||||
title="Delete Channel"
|
||||
description="Are you sure want to delete this channel?"
|
||||
buttons={
|
||||
<>
|
||||
{" "}
|
||||
<Button onClick={closeModal.bind(null, undefined)}>Cancel</Button>
|
||||
<Button onClick={handleDelete} className="danger">
|
||||
{isLoading ? "Deleting" : `Delete`}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
// className="animate__animated animate__fadeInDown animate__faster"
|
||||
></StyledModal>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import toast from "react-hot-toast";
|
||||
import {
|
||||
useGetChannelQuery,
|
||||
useUpdateChannelMutation,
|
||||
} from "../../../app/services/channel";
|
||||
import Input from "../styled/Input";
|
||||
import Label from "../styled/Label";
|
||||
import Textarea from "../styled/Textarea";
|
||||
import SaveTip from "../SaveTip";
|
||||
import channelIcon from "../../../assets/icons/channel.svg?url";
|
||||
import { useSelector } from "react-redux";
|
||||
const StyledWrapper = styled.div`
|
||||
position: relative;
|
||||
width: 512px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
.inputs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 24px;
|
||||
.input {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
.name {
|
||||
padding-left: 36px;
|
||||
background-image: url(${channelIcon});
|
||||
background-size: 20px;
|
||||
background-position-x: 8px;
|
||||
background-position-y: 8px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function Overview({ id = 0 }) {
|
||||
const loginUser = useSelector(
|
||||
(store) => store.contacts.byId[store.authData.uid]
|
||||
);
|
||||
const { data, refetch } = useGetChannelQuery(id);
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [values, setValues] = useState(null);
|
||||
const [updateChannel, { isSuccess: updated }] = useUpdateChannelMutation();
|
||||
const handleUpdate = () => {
|
||||
const { name, description } = values;
|
||||
updateChannel({ id, name, description });
|
||||
};
|
||||
const handleChange = (evt) => {
|
||||
const newValue = evt.target.value;
|
||||
const { type } = evt.target.dataset;
|
||||
setValues((prev) => {
|
||||
return { ...prev, [type]: newValue };
|
||||
});
|
||||
};
|
||||
const handleReset = () => {
|
||||
console.log("reset", data);
|
||||
setValues(data);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setValues(data);
|
||||
}
|
||||
}, [data]);
|
||||
useEffect(() => {
|
||||
if (data && values) {
|
||||
const { name, description } = values;
|
||||
const { name: oName, description: oDescription } = data;
|
||||
if (oName !== name || oDescription !== description) {
|
||||
setChanged(true);
|
||||
} else {
|
||||
setChanged(false);
|
||||
}
|
||||
}
|
||||
}, [data, values]);
|
||||
|
||||
useEffect(() => {
|
||||
if (updated) {
|
||||
toast.success("Channel updated!");
|
||||
refetch();
|
||||
}
|
||||
}, [updated]);
|
||||
|
||||
if (!values || !id) return null;
|
||||
const { name, description } = values;
|
||||
const isAdmin = loginUser.is_admin;
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="inputs">
|
||||
<div className="input">
|
||||
<Label htmlFor="name">Channel Name</Label>
|
||||
<Input
|
||||
disabled={!isAdmin}
|
||||
className="name"
|
||||
data-type="name"
|
||||
onChange={handleChange}
|
||||
value={name}
|
||||
name="name"
|
||||
id="name"
|
||||
placeholder="Channel Name"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Channel Topic</Label>
|
||||
<Textarea
|
||||
disabled={!isAdmin}
|
||||
data-type="description"
|
||||
onChange={handleChange}
|
||||
value={description ?? ""}
|
||||
rows={4}
|
||||
name="name"
|
||||
id="name"
|
||||
placeholder="Let everyone know how to use this channel."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{changed && (
|
||||
<SaveTip saveHandler={handleUpdate} resetHandler={handleReset} />
|
||||
)}
|
||||
{/* <button onClick={handleUpdate} className="btn">update</button> */}
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleChannelSetting } from "../../../app/slices/ui";
|
||||
import StyledSettingContainer from "../StyledSettingContainer";
|
||||
import DeleteConfirmModal from "./DeleteConfirmModal";
|
||||
import useNavs from "./navs";
|
||||
export default function ChannelSetting({ id = 0 }) {
|
||||
const navs = useNavs(id);
|
||||
const flatenNavs = navs
|
||||
.map(({ items }) => {
|
||||
return items;
|
||||
})
|
||||
.flat();
|
||||
const [currNav, setCurrNav] = useState(flatenNavs[0]);
|
||||
const [deleteConfirm, setDeleteConfirm] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const close = () => {
|
||||
dispatch(toggleChannelSetting());
|
||||
};
|
||||
const toggleDeleteConfrim = () => {
|
||||
setDeleteConfirm((prev) => !prev);
|
||||
};
|
||||
const updateNav = (name) => {
|
||||
const tmp = flatenNavs.find((n) => n.name == name);
|
||||
if (tmp) {
|
||||
setCurrNav(tmp);
|
||||
}
|
||||
};
|
||||
if (!id) return null;
|
||||
return (
|
||||
<>
|
||||
<StyledSettingContainer
|
||||
updateNav={updateNav}
|
||||
nav={currNav}
|
||||
closeModal={close}
|
||||
title="Channel Setting"
|
||||
navs={navs}
|
||||
dangers={[
|
||||
{
|
||||
title: "Delete Channel",
|
||||
handler: toggleDeleteConfrim,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{currNav.component}
|
||||
</StyledSettingContainer>
|
||||
{deleteConfirm && (
|
||||
<DeleteConfirmModal closeModal={toggleDeleteConfrim} id={id} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import Overview from "./Overview";
|
||||
import ManageMembers from "../ManageMembers";
|
||||
const useNavs = (channelId) => {
|
||||
const navs = [
|
||||
{
|
||||
title: "General",
|
||||
items: [
|
||||
{
|
||||
name: "overview",
|
||||
title: "Overview",
|
||||
component: <Overview id={channelId} />,
|
||||
},
|
||||
{
|
||||
name: "members",
|
||||
title: "Members",
|
||||
component: <ManageMembers cid={channelId} />,
|
||||
},
|
||||
// {
|
||||
// name: "permissions",
|
||||
// title: "Permissions",
|
||||
// },
|
||||
// {
|
||||
// name: "invites",
|
||||
// title: "Invites",
|
||||
// },
|
||||
// {
|
||||
// name: "integrations",
|
||||
// title: "Integrations",
|
||||
// },
|
||||
],
|
||||
},
|
||||
// {
|
||||
// title: "User Management",
|
||||
// items: [
|
||||
|
||||
// ],
|
||||
// },
|
||||
];
|
||||
return navs;
|
||||
};
|
||||
|
||||
export default useNavs;
|
||||
@@ -1,80 +0,0 @@
|
||||
// import React from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { toggleSetting } from "../../../app/slices/ui";
|
||||
// import BASE_URL from "../../app/config";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
import Checkbox from "../styled/Checkbox";
|
||||
import useLogout from "../../hook/useLogout";
|
||||
const StyledConfirm = styled(StyledModal)`
|
||||
.clear {
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #6b7280;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
.txt {
|
||||
cursor: pointer;
|
||||
color: orange;
|
||||
margin-right: 12px;
|
||||
}
|
||||
input {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
`;
|
||||
import Modal from "../Modal";
|
||||
export default function LogoutConfirmModal({ closeModal }) {
|
||||
const dispatch = useDispatch();
|
||||
const [clearLocal, setClearLocal] = useState(false);
|
||||
const { logout, exited, exiting, clearLocalData } = useLogout();
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
};
|
||||
const handleCheck = (evt) => {
|
||||
setClearLocal(evt.target.checked);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (exited) {
|
||||
if (clearLocal) {
|
||||
console.log("clear all store");
|
||||
clearLocalData();
|
||||
}
|
||||
// closeModal();
|
||||
dispatch(toggleSetting());
|
||||
}
|
||||
}, [exited, clearLocal]);
|
||||
return (
|
||||
<Modal id="modal-modal">
|
||||
<StyledConfirm
|
||||
title="Log Out"
|
||||
description="Are you sure want to log out this account?"
|
||||
buttons={
|
||||
<>
|
||||
<Button onClick={closeModal}>Cancel</Button>
|
||||
<Button onClick={handleLogout} className="danger">
|
||||
{exiting ? "Logging out" : `Log Out`}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
// className="animate__animated animate__fadeInDown animate__faster"
|
||||
>
|
||||
<div className="clear">
|
||||
<label htmlFor="clear_cb" className="txt">
|
||||
Clear local data
|
||||
</label>
|
||||
<Checkbox
|
||||
name="clear_cb"
|
||||
checked={clearLocal}
|
||||
onChange={handleCheck}
|
||||
/>
|
||||
</div>
|
||||
</StyledConfirm>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import toast from "react-hot-toast";
|
||||
import { useUpdateAvatarMutation } from "../../../app/services/contact";
|
||||
import AvatarUploader from "../AvatarUploader";
|
||||
import ProfileBasicEditModal from "./ProfileBasicEditModal";
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
.card {
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 512px;
|
||||
background: #f3f4f6;
|
||||
border-radius: 20px;
|
||||
.name {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 64px;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
line-height: 28px;
|
||||
color: #27272a;
|
||||
.uid {
|
||||
font-weight: normal;
|
||||
color: #52525b;
|
||||
}
|
||||
}
|
||||
.row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24px;
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.label {
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
text-transform: uppercase;
|
||||
color: #52525b;
|
||||
}
|
||||
.txt {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #52525b;
|
||||
.gray {
|
||||
color: #78787c;
|
||||
}
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
background: #1fe1f9;
|
||||
border: 1px solid #1fe1f9;
|
||||
}
|
||||
}
|
||||
}
|
||||
.danger {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
.head {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #374151;
|
||||
}
|
||||
.desc {
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: #616161;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.btn {
|
||||
background: #ef4444;
|
||||
border: 1px solid #ef4444;
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
color: #fff;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
padding: 8px 14px;
|
||||
background: #1fe1f9;
|
||||
border: 1px solid #1fe1f9;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 8px;
|
||||
}
|
||||
`;
|
||||
const EditModalInfo = {
|
||||
name: {
|
||||
label: "Username",
|
||||
title: "Change your username",
|
||||
intro: "Enter a new username.",
|
||||
},
|
||||
email: {
|
||||
label: "Email",
|
||||
title: "Change your email",
|
||||
intro: "Enter a new email.",
|
||||
},
|
||||
};
|
||||
export default function MyAccount() {
|
||||
const [editModal, setEditModal] = useState(null);
|
||||
const [
|
||||
uploadAvatar,
|
||||
{ isSuccess: uploadSuccess },
|
||||
] = useUpdateAvatarMutation();
|
||||
const loginUser = useSelector((store) => {
|
||||
return store.contacts.byId[store.authData.uid];
|
||||
});
|
||||
useEffect(() => {
|
||||
if (uploadSuccess) {
|
||||
toast.success("update avatar successfully!");
|
||||
}
|
||||
}, [uploadSuccess]);
|
||||
const handleBasicEdit = (evt) => {
|
||||
const { edit } = evt.target.dataset;
|
||||
setEditModal(edit);
|
||||
};
|
||||
const closeBasicEditModal = () => {
|
||||
setEditModal(null);
|
||||
};
|
||||
if (!loginUser) return null;
|
||||
const { uid, avatar, name, email } = loginUser;
|
||||
return (
|
||||
<>
|
||||
<StyledWrapper>
|
||||
<div className="card">
|
||||
<AvatarUploader url={avatar} name={name} uploadImage={uploadAvatar} />
|
||||
<div className="name">
|
||||
{name} <span className="uid">#{uid}</span>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="info">
|
||||
<span className="label">username</span>
|
||||
<span className="txt">
|
||||
{name} <span className="gray"> #{uid}</span>
|
||||
</span>
|
||||
</div>
|
||||
{/* <div className="right"> */}
|
||||
<button data-edit="name" onClick={handleBasicEdit} className="btn">
|
||||
Edit
|
||||
</button>
|
||||
{/* </div> */}
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="info">
|
||||
<span className="label">email</span>
|
||||
<span className="txt">{email}</span>
|
||||
</div>
|
||||
{/* <div className="right"> */}
|
||||
<button data-edit="email" onClick={handleBasicEdit} className="btn">
|
||||
Edit
|
||||
</button>
|
||||
{/* </div> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="danger">
|
||||
<h4 className="head">Account Removal</h4>
|
||||
<div className="desc">
|
||||
Disabling your account means you can recover it at any time after
|
||||
taking this action.
|
||||
</div>
|
||||
<button className="btn">Delete Account</button>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
{editModal && (
|
||||
<ProfileBasicEditModal
|
||||
valueKey={editModal}
|
||||
{...EditModalInfo[editModal]}
|
||||
value={eval(editModal)}
|
||||
closeModal={closeBasicEditModal}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// import React from "react";
|
||||
import styled from "styled-components";
|
||||
import useNotification from "../../hook/useNotification";
|
||||
import StyledToggle from "../styled/Toggle";
|
||||
import Label from "../styled/Label";
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
export default function Notifications() {
|
||||
const { status, enableNotification } = useNotification();
|
||||
const handleEnableNotify = () => {
|
||||
if (status !== "granted") {
|
||||
enableNotification();
|
||||
}
|
||||
};
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<Label>Notification Setting:</Label>
|
||||
<StyledToggle
|
||||
data-checked={status == "granted"}
|
||||
onClick={handleEnableNotify}
|
||||
/>
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import {
|
||||
useGetServerQuery,
|
||||
useUpdateServerMutation,
|
||||
useUpdateLogoMutation,
|
||||
} from "../../../app/services/server";
|
||||
import LogoUploader from "../AvatarUploader";
|
||||
import Input from "../styled/Input";
|
||||
import Label from "../styled/Label";
|
||||
import Textarea from "../styled/Textarea";
|
||||
import SaveTip from "../SaveTip";
|
||||
import toast from "react-hot-toast";
|
||||
const StyledWrapper = styled.div`
|
||||
position: relative;
|
||||
width: 512px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
.logo {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
.preview {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
}
|
||||
.upload {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
.tip {
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #374151;
|
||||
}
|
||||
.btn {
|
||||
padding: 8px 14px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #1fe1f9;
|
||||
background: #ecfeff;
|
||||
border: 1px solid #ecfeff;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.inputs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 24px;
|
||||
.input {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function Overview() {
|
||||
const loginUser = useSelector((store) => {
|
||||
return store.contacts.byId[store.authData.uid];
|
||||
});
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [values, setValues] = useState(null);
|
||||
const { data, refetch } = useGetServerQuery();
|
||||
const [updateServer, { isSuccess: updated }] = useUpdateServerMutation();
|
||||
const [uploadLogo, { isSuccess: uploadSuccess }] = useUpdateLogoMutation();
|
||||
const handleUpdate = () => {
|
||||
const { name, description } = values;
|
||||
updateServer({ name, description });
|
||||
};
|
||||
const handleChange = (evt) => {
|
||||
const newValue = evt.target.value;
|
||||
const { type } = evt.target.dataset;
|
||||
setValues((prev) => {
|
||||
return { ...prev, [type]: newValue };
|
||||
});
|
||||
};
|
||||
const handleReset = () => {
|
||||
console.log("reset", data);
|
||||
setValues(data);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (uploadSuccess) {
|
||||
toast.success("update logo successfully");
|
||||
refetch();
|
||||
}
|
||||
}, [uploadSuccess]);
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setValues(data);
|
||||
}
|
||||
}, [data]);
|
||||
useEffect(() => {
|
||||
if (data && values) {
|
||||
const { name, description } = values;
|
||||
const { name: oName, description: oDescription } = data;
|
||||
if (oName !== name || oDescription !== description) {
|
||||
setChanged(true);
|
||||
} else {
|
||||
setChanged(false);
|
||||
}
|
||||
}
|
||||
}, [data, values]);
|
||||
|
||||
useEffect(() => {
|
||||
if (updated) {
|
||||
toast.success("Server updated!");
|
||||
refetch();
|
||||
}
|
||||
}, [updated]);
|
||||
|
||||
if (!values) return null;
|
||||
const { name, description, logo } = values;
|
||||
const isAdmin = loginUser?.is_admin;
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="logo">
|
||||
<div className="preview">
|
||||
<LogoUploader
|
||||
disabled={!isAdmin}
|
||||
url={uploadSuccess ? `${logo}?t=${new Date().getTime()}` : logo}
|
||||
name={name}
|
||||
uploadImage={uploadLogo}
|
||||
/>
|
||||
</div>
|
||||
{isAdmin && (
|
||||
<div className="upload">
|
||||
<div className="tip">
|
||||
Minimum size is 128x128, We recommend at least 512x512 for the
|
||||
server. Max size limited to 5M.
|
||||
</div>
|
||||
<button className="btn">Upload Image</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="inputs">
|
||||
<div className="input">
|
||||
<Label htmlFor="name">Server Name</Label>
|
||||
<Input
|
||||
disabled={!isAdmin}
|
||||
data-type="name"
|
||||
onChange={handleChange}
|
||||
value={name}
|
||||
name="name"
|
||||
id="name"
|
||||
placeholder="Server Name"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Server Description</Label>
|
||||
<Textarea
|
||||
disabled={!isAdmin}
|
||||
data-type="description"
|
||||
onChange={handleChange}
|
||||
value={description ?? ""}
|
||||
rows={4}
|
||||
name="name"
|
||||
id="name"
|
||||
placeholder="Tell the world a bit about this server"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{changed && (
|
||||
<SaveTip saveHandler={handleUpdate} resetHandler={handleReset} />
|
||||
)}
|
||||
{/* <button onClick={handleUpdate} className="btn">update</button> */}
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
// import React from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
// import { useDispatch } from "react-redux";
|
||||
import Input from "../styled/Input";
|
||||
// import BASE_URL from "../../app/config";
|
||||
import { useUpdateInfoMutation } from "../../../app/services/contact";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
const StyledEdit = styled(StyledModal)`
|
||||
.input {
|
||||
margin: 48px 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
label {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #6b7280;
|
||||
}
|
||||
}
|
||||
`;
|
||||
import Modal from "../Modal";
|
||||
import toast from "react-hot-toast";
|
||||
export default function ProfileBasicEditModal({
|
||||
label = "Username",
|
||||
valueKey = "name",
|
||||
value = "",
|
||||
title = "Change your username",
|
||||
intro = "Enter a new username and your existing password.",
|
||||
closeModal,
|
||||
}) {
|
||||
const [input, setInput] = useState(value);
|
||||
// const dispatch = useDispatch();
|
||||
const [update, { isLoading, isSuccess }] = useUpdateInfoMutation();
|
||||
const handleChange = (evt) => {
|
||||
setInput(evt.target.value);
|
||||
};
|
||||
const handleUpdate = () => {
|
||||
update({ [valueKey]: input });
|
||||
};
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
// todo
|
||||
toast.success("update user info successfully");
|
||||
closeModal();
|
||||
}
|
||||
}, [isSuccess]);
|
||||
return (
|
||||
<Modal id="modal-modal">
|
||||
<StyledEdit
|
||||
title={title}
|
||||
description={intro}
|
||||
buttons={
|
||||
<>
|
||||
<Button onClick={closeModal}>Cancel</Button>
|
||||
<Button onClick={handleUpdate}>
|
||||
{isLoading ? "Updating" : `Done`}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
className="animate__animated animate__fadeInDown animate__faster"
|
||||
>
|
||||
<div className="input">
|
||||
<label htmlFor={valueKey}>{label}</label>
|
||||
<Input name={valueKey} value={input} onChange={handleChange}></Input>
|
||||
</div>
|
||||
</StyledEdit>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import StyledContainer from "./StyledContainer";
|
||||
import Input from "../../styled/Input";
|
||||
import Textarea from "../../styled/Textarea";
|
||||
import Label from "../../styled/Label";
|
||||
import Toggle from "../../styled/Toggle";
|
||||
import SaveTip from "../../SaveTip";
|
||||
import useConfig from "./useConfig";
|
||||
export default function ConfigAgora() {
|
||||
const {
|
||||
changed,
|
||||
reset,
|
||||
values,
|
||||
setValues,
|
||||
toggleEnable,
|
||||
updateConfig,
|
||||
} = useConfig("agora");
|
||||
const handleUpdate = () => {
|
||||
// const { token_url, description } = values;
|
||||
updateConfig(values);
|
||||
};
|
||||
const handleChange = (evt) => {
|
||||
const newValue = evt.target.value;
|
||||
const { type } = evt.target.dataset;
|
||||
setValues((prev) => {
|
||||
return { ...prev, [type]: newValue };
|
||||
});
|
||||
};
|
||||
// if (!values) return null;
|
||||
const {
|
||||
url,
|
||||
project_id,
|
||||
app_id,
|
||||
app_certificate,
|
||||
rtm_key,
|
||||
rtm_secret,
|
||||
enabled = false,
|
||||
} = values ?? {};
|
||||
return (
|
||||
<StyledContainer>
|
||||
<div className="inputs">
|
||||
<div className="input row">
|
||||
<Label>Enable</Label>
|
||||
<Toggle onClick={toggleEnable} data-checked={enabled}></Toggle>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="url">Agora Url</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="url"
|
||||
onChange={handleChange}
|
||||
value={url || "https://api.agora.io"}
|
||||
name="url"
|
||||
placeholder="Agora URL"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="project_id">Project ID</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
// type={"number"}
|
||||
data-type="project_id"
|
||||
onChange={handleChange}
|
||||
value={project_id}
|
||||
name="project_id"
|
||||
placeholder="Project ID"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="app_id">App ID</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="app_id"
|
||||
onChange={handleChange}
|
||||
value={app_id}
|
||||
name="app_id"
|
||||
placeholder="APP ID"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="app_certificate">APP Certificate</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="app_certificate"
|
||||
onChange={handleChange}
|
||||
value={app_certificate}
|
||||
name="app_certificate"
|
||||
placeholder="APP Certificate"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="rtm_key">RTM Key</Label>
|
||||
<Textarea
|
||||
disabled={!enabled}
|
||||
data-type="rtm_key"
|
||||
onChange={handleChange}
|
||||
value={rtm_key}
|
||||
name="rtm_key"
|
||||
placeholder="RTM Key"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="rtm_secret">RTM Secret</Label>
|
||||
<Textarea
|
||||
disabled={!enabled}
|
||||
data-type="rtm_secret"
|
||||
onChange={handleChange}
|
||||
value={rtm_secret}
|
||||
name="rtm_secret"
|
||||
placeholder="RTM Secret"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{changed && <SaveTip saveHandler={handleUpdate} resetHandler={reset} />}
|
||||
{/* <button onClick={handleUpdate} className="btn">update</button> */}
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import StyledContainer from "./StyledContainer";
|
||||
import Input from "../../styled/Input";
|
||||
import Textarea from "../../styled/Textarea";
|
||||
import Toggle from "../../styled/Toggle";
|
||||
import Label from "../../styled/Label";
|
||||
import SaveTip from "../../SaveTip";
|
||||
import useConfig from "./useConfig";
|
||||
|
||||
export default function ConfigFirebase() {
|
||||
const {
|
||||
values,
|
||||
toggleEnable,
|
||||
updateConfig,
|
||||
setValues,
|
||||
reset,
|
||||
changed,
|
||||
} = useConfig("firebase");
|
||||
const handleUpdate = () => {
|
||||
// const { token_url, description } = values;
|
||||
updateConfig(values);
|
||||
};
|
||||
const handleChange = (evt) => {
|
||||
const newValue = evt.target.value;
|
||||
const { type } = evt.target.dataset;
|
||||
setValues((prev) => {
|
||||
return { ...prev, [type]: newValue };
|
||||
});
|
||||
};
|
||||
// if (!values) return null;
|
||||
const { token_url, project_id, private_key, client_email, enabled = false } =
|
||||
values ?? {};
|
||||
return (
|
||||
<StyledContainer>
|
||||
<div className="inputs">
|
||||
<div className="input row">
|
||||
<Label>Enable</Label>
|
||||
<Toggle onClick={toggleEnable} data-checked={enabled}></Toggle>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="name">Token Url</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="token_url"
|
||||
onChange={handleChange}
|
||||
value={token_url || "https://oauth2.googleapis.com/token"}
|
||||
name="token_url"
|
||||
placeholder="Token URL"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Project ID</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="project_id"
|
||||
onChange={handleChange}
|
||||
value={project_id}
|
||||
name="project_id"
|
||||
placeholder="Project ID"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Private Key</Label>
|
||||
<Textarea
|
||||
rows={10}
|
||||
disabled={!enabled}
|
||||
data-type="private_key"
|
||||
onChange={handleChange}
|
||||
value={private_key}
|
||||
name="private_key"
|
||||
placeholder="Private key"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Client Email</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="client_email"
|
||||
onChange={handleChange}
|
||||
value={client_email}
|
||||
name="client_email"
|
||||
placeholder="Client Email address"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{changed && <SaveTip saveHandler={handleUpdate} resetHandler={reset} />}
|
||||
{/* <button onClick={handleUpdate} className="btn">update</button> */}
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import StyledContainer from './StyledContainer';
|
||||
// import Input from "../../styled/Input";
|
||||
import Textarea from '../../styled/Textarea';
|
||||
import Toggle from '../../styled/Toggle';
|
||||
import Label from '../../styled/Label';
|
||||
import SaveTip from '../../SaveTip';
|
||||
import useConfig from './useConfig';
|
||||
|
||||
export default function Logins() {
|
||||
const { values, updateConfig, setValues, reset, changed } = useConfig('login');
|
||||
const handleUpdate = () => {
|
||||
// const { token_url, description } = values;
|
||||
updateConfig(values);
|
||||
};
|
||||
const handleChange = (evt) => {
|
||||
const newValue = evt.target.value;
|
||||
const { type } = evt.target.dataset;
|
||||
const items = newValue ? newValue.split('\n') : [];
|
||||
setValues((prev) => {
|
||||
return { ...prev, [type]: items };
|
||||
});
|
||||
};
|
||||
const handleToggle = (val) => {
|
||||
setValues((prev) => {
|
||||
return { ...prev, ...val };
|
||||
});
|
||||
};
|
||||
if (!values) return null;
|
||||
const { google, metamask, password, oidc = [] } = values ?? {};
|
||||
return (
|
||||
<StyledContainer>
|
||||
<div className="inputs">
|
||||
<div className="input row">
|
||||
<Label>Password</Label>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { password: !password })}
|
||||
data-checked={password}
|
||||
></Toggle>
|
||||
</div>
|
||||
<div className="input row">
|
||||
<Label>Google</Label>
|
||||
<Toggle onClick={handleToggle.bind(null, { google: !google })} data-checked={google}></Toggle>
|
||||
</div>
|
||||
<div className="input row">
|
||||
<Label>Metamask</Label>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { metamask: !metamask })}
|
||||
data-checked={metamask}
|
||||
></Toggle>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">OIDC</Label>
|
||||
<Textarea
|
||||
rows={10}
|
||||
data-type="oidc"
|
||||
onChange={handleChange}
|
||||
value={oidc.join('\n')}
|
||||
name="oidc"
|
||||
placeholder="Input issuer list, one line, one issuer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* <div className="input">
|
||||
<Label htmlFor="name">Token Url</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="token_url"
|
||||
onChange={handleChange}
|
||||
value={token_url || "https://oauth2.googleapis.com/token"}
|
||||
name="token_url"
|
||||
placeholder="Token URL"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Project ID</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
type={"number"}
|
||||
data-type="project_id"
|
||||
onChange={handleChange}
|
||||
value={project_id}
|
||||
name="project_id"
|
||||
placeholder="Project ID"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Private Key</Label>
|
||||
<Textarea
|
||||
rows={10}
|
||||
disabled={!enabled}
|
||||
data-type="private_key"
|
||||
onChange={handleChange}
|
||||
value={private_key}
|
||||
name="private_key"
|
||||
placeholder="Private key"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Client Email</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="client_email"
|
||||
onChange={handleChange}
|
||||
value={client_email}
|
||||
name="client_email"
|
||||
placeholder="Client Email address"
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
{changed && <SaveTip saveHandler={handleUpdate} resetHandler={reset} />}
|
||||
{/* <button onClick={handleUpdate} className="btn">update</button> */}
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
const StyledTest = styled.div`
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
white-space: nowrap;
|
||||
margin-top: 24px;
|
||||
`;
|
||||
import { useSendTestEmailMutation } from "../../../../app/services/server";
|
||||
import iconQuestion from "../../../../assets/icons/question.svg?url";
|
||||
import useConfig from "./useConfig";
|
||||
import StyledContainer from "./StyledContainer";
|
||||
import Input from "../../styled/Input";
|
||||
import Button from "../../styled/Button";
|
||||
import Toggle from "../../styled/Toggle";
|
||||
import Label from "../../styled/Label";
|
||||
import SaveTip from "../../SaveTip";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function ConfigSMTP() {
|
||||
const [testEmail, setTestEmail] = useState("");
|
||||
const [sendTestEmail, { isSuccess, isError }] = useSendTestEmailMutation();
|
||||
const {
|
||||
reset,
|
||||
updateConfig,
|
||||
values,
|
||||
setValues,
|
||||
changed,
|
||||
toggleEnable,
|
||||
} = useConfig("smtp");
|
||||
|
||||
const handleUpdate = () => {
|
||||
// const { token_url, description } = values;
|
||||
updateConfig({ ...values, port: Number(values.port ?? 0) });
|
||||
};
|
||||
const handleChange = (evt) => {
|
||||
const newValue = evt.target.value;
|
||||
const { type } = evt.target.dataset;
|
||||
setValues((prev) => {
|
||||
return { ...prev, [type]: newValue };
|
||||
});
|
||||
};
|
||||
const handleTestEmailChange = (evt) => {
|
||||
const newValue = evt.target.value;
|
||||
setTestEmail(newValue);
|
||||
};
|
||||
const handleTestClick = () => {
|
||||
console.log("test");
|
||||
sendTestEmail({
|
||||
to: testEmail,
|
||||
subject: "test title",
|
||||
content: "test content",
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
toast.success("Send Test Email Successfully");
|
||||
}
|
||||
if (isError) {
|
||||
toast.error("Send Test Email Fail");
|
||||
}
|
||||
}, [isSuccess, isError]);
|
||||
|
||||
// if (!values) return null;
|
||||
const { host, port, from, username, password, enabled = false } =
|
||||
values ?? {};
|
||||
console.log("values", values);
|
||||
return (
|
||||
<StyledContainer>
|
||||
<div className="inputs">
|
||||
<div className="input row">
|
||||
<Label>Enable</Label>
|
||||
<Toggle onClick={toggleEnable} data-checked={enabled}></Toggle>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="name">Host</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="host"
|
||||
onChange={handleChange}
|
||||
value={host}
|
||||
name="host"
|
||||
placeholder="SMTP Host"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Port</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
type={"number"}
|
||||
data-type="port"
|
||||
onChange={handleChange}
|
||||
value={port}
|
||||
name="port"
|
||||
placeholder="SMTP Port"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">From</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="from"
|
||||
onChange={handleChange}
|
||||
value={from}
|
||||
name="from"
|
||||
placeholder="SMTP From"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Username</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="username"
|
||||
onChange={handleChange}
|
||||
value={username}
|
||||
name="username"
|
||||
placeholder="SMTP Username"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Password</Label>
|
||||
<Input
|
||||
type={"password"}
|
||||
disabled={!enabled}
|
||||
data-type="password"
|
||||
onChange={handleChange}
|
||||
value={password}
|
||||
name="password"
|
||||
placeholder="SMTP Password"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tip">
|
||||
<img src={iconQuestion} alt="question icon" />
|
||||
<a
|
||||
href="https://rustchat.com/doc/smtp-setting"
|
||||
target="_blank"
|
||||
className="link"
|
||||
rel="noreferrer"
|
||||
>
|
||||
How to set up SMTP?
|
||||
</a>
|
||||
</div>
|
||||
<StyledTest>
|
||||
<Input
|
||||
type={"email"}
|
||||
disabled={!enabled}
|
||||
onChange={handleTestEmailChange}
|
||||
value={testEmail}
|
||||
name="email"
|
||||
placeholder="test@email.com"
|
||||
/>
|
||||
<Button disabled={!enabled || !testEmail} onClick={handleTestClick}>
|
||||
Send Test Email
|
||||
</Button>
|
||||
</StyledTest>
|
||||
{changed && <SaveTip saveHandler={handleUpdate} resetHandler={reset} />}
|
||||
{/* <button onClick={handleUpdate} className="btn">update</button> */}
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import styled from "styled-components";
|
||||
const StyledContainer = styled.div`
|
||||
position: relative;
|
||||
width: 512px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
.inputs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 24px;
|
||||
.input {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
&.row {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tip {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
.link {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #06b6d4;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledContainer;
|
||||
@@ -1,92 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isObjectEqual } from '../../../utils';
|
||||
import toast from 'react-hot-toast';
|
||||
import {
|
||||
useGetAgoraConfigQuery,
|
||||
useGetFirebaseConfigQuery,
|
||||
useGetSMTPConfigQuery,
|
||||
useGetLoginConfigQuery,
|
||||
useUpdateLoginConfigMutation,
|
||||
useUpdateSMTPConfigMutation,
|
||||
useUpdateAgoraConfigMutation,
|
||||
useUpdateFirebaseConfigMutation
|
||||
} from '../../../../app/services/server';
|
||||
export default function useConfig(config = 'smtp') {
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [values, setValues] = useState({});
|
||||
const { data: Login, refetch: refetchLogin } = useGetLoginConfigQuery();
|
||||
const [updateLoginConfig, { isSuccess: LoginUpdated }] = useUpdateLoginConfigMutation();
|
||||
const { data: SMTP, refetch: refetchSMTP } = useGetSMTPConfigQuery();
|
||||
const [updateSMTPConfig, { isSuccess: SMTPUpdated }] = useUpdateSMTPConfigMutation();
|
||||
const { data: Agora, refetch: refetchAgora } = useGetAgoraConfigQuery();
|
||||
const [updateAgoraConfig, { isSuccess: AgoraUpdated }] = useUpdateAgoraConfigMutation();
|
||||
const { data: Firebase, refetch: refetchFirebase } = useGetFirebaseConfigQuery();
|
||||
const [updateFirebaseConfig, { isSuccess: FirebaseUpdated }] = useUpdateFirebaseConfigMutation();
|
||||
|
||||
const datas = {
|
||||
login: Login,
|
||||
smtp: SMTP,
|
||||
agora: Agora,
|
||||
firebase: Firebase
|
||||
};
|
||||
const updateFns = {
|
||||
login: updateLoginConfig,
|
||||
smtp: updateSMTPConfig,
|
||||
agora: updateAgoraConfig,
|
||||
firebase: updateFirebaseConfig
|
||||
};
|
||||
const refetchs = {
|
||||
smtp: refetchSMTP,
|
||||
agora: refetchAgora,
|
||||
firebase: refetchFirebase,
|
||||
login: refetchLogin
|
||||
};
|
||||
const updateds = {
|
||||
login: LoginUpdated,
|
||||
smtp: SMTPUpdated,
|
||||
agora: AgoraUpdated,
|
||||
firebase: FirebaseUpdated
|
||||
};
|
||||
const data = datas[config];
|
||||
const updateConfig = updateFns[config];
|
||||
const refetch = refetchs[config];
|
||||
const updated = updateds[config];
|
||||
const reset = () => {
|
||||
setValues(data ?? {});
|
||||
};
|
||||
|
||||
const toggleEnable = () => {
|
||||
setValues((prev) => {
|
||||
return { ...prev, enabled: !prev.enabled };
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
if (updated) {
|
||||
toast.success('Configuration Updated!');
|
||||
refetch();
|
||||
}
|
||||
}, [updated]);
|
||||
useEffect(() => {
|
||||
console.log('wtf', data);
|
||||
// if (data) {
|
||||
setValues(data ?? {});
|
||||
// }
|
||||
}, [data]);
|
||||
useEffect(() => {
|
||||
// if (data && values) {
|
||||
if (!isObjectEqual(data, values)) {
|
||||
setChanged(true);
|
||||
} else {
|
||||
setChanged(false);
|
||||
}
|
||||
// }
|
||||
}, [data, values]);
|
||||
return {
|
||||
reset,
|
||||
changed,
|
||||
updateConfig,
|
||||
values,
|
||||
setValues,
|
||||
toggleEnable
|
||||
};
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleSetting } from "../../../app/slices/ui";
|
||||
import StyledSettingContainer from "../StyledSettingContainer";
|
||||
import useNavs from "./navs";
|
||||
import LogoutConfirmModal from "./LogoutConfirmModal";
|
||||
|
||||
export default function Setting() {
|
||||
const navs = useNavs();
|
||||
const flatenNavs = navs
|
||||
.map(({ items }) => {
|
||||
return items;
|
||||
})
|
||||
.flat();
|
||||
const [currNav, setCurrNav] = useState(flatenNavs[0]);
|
||||
const [logoutConfirm, setLogoutConfirm] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const close = () => {
|
||||
dispatch(toggleSetting());
|
||||
};
|
||||
const toggleLogoutConfrim = () => {
|
||||
setLogoutConfirm((prev) => !prev);
|
||||
};
|
||||
const updateNav = (name) => {
|
||||
const tmp = flatenNavs.find((n) => n.name == name);
|
||||
if (tmp) {
|
||||
setCurrNav(tmp);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<StyledSettingContainer
|
||||
updateNav={updateNav}
|
||||
nav={currNav}
|
||||
closeModal={close}
|
||||
title="Settings"
|
||||
navs={navs}
|
||||
dangers={[{ title: "Log Out", handler: toggleLogoutConfrim }]}
|
||||
>
|
||||
{currNav.component}
|
||||
</StyledSettingContainer>
|
||||
{logoutConfirm && <LogoutConfirmModal closeModal={toggleLogoutConfrim} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
import MyAccount from './MyAccount';
|
||||
import Overview from './Overview';
|
||||
import Logins from './config/Logins';
|
||||
import ConfigFirebase from './config/Firebase';
|
||||
import ConfigSMTP from './config/SMTP';
|
||||
import Notifications from './Notifications';
|
||||
import ManageMembers from '../ManageMembers';
|
||||
import FAQ from '../FAQ';
|
||||
import ConfigAgora from './config/Agora';
|
||||
const navs = [
|
||||
{
|
||||
title: 'General',
|
||||
items: [
|
||||
{
|
||||
name: 'overview',
|
||||
title: 'Overview',
|
||||
component: <Overview />
|
||||
},
|
||||
{
|
||||
name: 'members',
|
||||
title: 'Members',
|
||||
component: <ManageMembers />,
|
||||
admin: true
|
||||
},
|
||||
{
|
||||
name: 'notification',
|
||||
title: 'Notification',
|
||||
component: <Notifications />
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'User',
|
||||
items: [
|
||||
{
|
||||
name: 'my_account',
|
||||
title: 'My Account',
|
||||
component: <MyAccount />
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Configuration',
|
||||
items: [
|
||||
{
|
||||
name: 'firebase',
|
||||
title: 'Firebase',
|
||||
component: <ConfigFirebase />
|
||||
},
|
||||
{
|
||||
name: 'agora',
|
||||
title: 'Agora',
|
||||
component: <ConfigAgora />
|
||||
},
|
||||
{
|
||||
name: 'smtp',
|
||||
title: 'SMTP',
|
||||
component: <ConfigSMTP />
|
||||
},
|
||||
{
|
||||
name: 'social_login',
|
||||
title: 'Social Login',
|
||||
component: <Logins />
|
||||
}
|
||||
],
|
||||
admin: true
|
||||
},
|
||||
{
|
||||
title: 'About',
|
||||
items: [
|
||||
{
|
||||
name: 'faq',
|
||||
title: 'FAQ',
|
||||
component: <FAQ />
|
||||
},
|
||||
{
|
||||
name: 'terms',
|
||||
title: 'Terms & Privacy',
|
||||
component: 'Terms & Privacy'
|
||||
},
|
||||
{
|
||||
name: 'feedback',
|
||||
title: 'Feedback',
|
||||
component: 'feedback'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
const useNavs = () => {
|
||||
const loginUser = useSelector((store) => {
|
||||
return store.contacts.byId[store.authData.uid];
|
||||
});
|
||||
const Navs = navs.filter((nav) => {
|
||||
if (loginUser.is_admin) {
|
||||
return true;
|
||||
} else {
|
||||
return !nav.admin;
|
||||
}
|
||||
});
|
||||
return Navs;
|
||||
};
|
||||
|
||||
export default useNavs;
|
||||
@@ -1,10 +1,9 @@
|
||||
// import React from 'react'
|
||||
import styled from "styled-components";
|
||||
import Modal from "./Modal";
|
||||
import backIcon from "../../assets/icons/arrow.left.svg?url";
|
||||
const StyledWrapper = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
> .left {
|
||||
max-height: 100vh;
|
||||
@@ -89,47 +88,45 @@ export default function StyledSettingContainer({
|
||||
updateNav(name);
|
||||
};
|
||||
return (
|
||||
<Modal>
|
||||
<StyledWrapper>
|
||||
<div className="left">
|
||||
<h2 onClick={closeModal} className="title">
|
||||
{title}
|
||||
</h2>
|
||||
{navs.map(({ title, items }) => {
|
||||
return (
|
||||
<ul key={title} data-title={title} className="items">
|
||||
{items.map(({ name, title }) => {
|
||||
return (
|
||||
<li
|
||||
key={name}
|
||||
onClick={handleNavClick.bind(null, name)}
|
||||
className={`item ${name == nav?.name ? "curr" : ""}`}
|
||||
>
|
||||
{title}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
})}
|
||||
{dangers.length ? (
|
||||
<ul className="items danger">
|
||||
{dangers.map((d) => {
|
||||
const { title, handler } = d;
|
||||
<StyledWrapper>
|
||||
<div className="left">
|
||||
<h2 onClick={closeModal} className="title">
|
||||
{title}
|
||||
</h2>
|
||||
{navs.map(({ title, items }) => {
|
||||
return (
|
||||
<ul key={title} data-title={title} className="items">
|
||||
{items.map(({ name, title }) => {
|
||||
return (
|
||||
<li key={title} onClick={handler} className="item">
|
||||
<li
|
||||
key={name}
|
||||
onClick={handleNavClick.bind(null, name)}
|
||||
className={`item ${name == nav?.name ? "curr" : ""}`}
|
||||
>
|
||||
{title}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="right">
|
||||
{nav && <h4 className="title">{nav.title}</h4>}
|
||||
{children}
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
</Modal>
|
||||
);
|
||||
})}
|
||||
{dangers.length ? (
|
||||
<ul className="items danger">
|
||||
{dangers.map((d) => {
|
||||
const { title, handler } = d;
|
||||
return (
|
||||
<li key={title} onClick={handler} className="item">
|
||||
{title}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="right">
|
||||
{nav && <h4 className="title">{nav.title}</h4>}
|
||||
{children}
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user