refactor: setting modals to pages

This commit is contained in:
zerosoul
2022-04-12 18:21:59 +08:00
parent bcfadfb2d4
commit 20373c4376
28 changed files with 473 additions and 477 deletions
+77
View File
@@ -0,0 +1,77 @@
// import React from "react";
import { useEffect, useState } from "react";
import styled from "styled-components";
import { useNavigate } from "react-router-dom";
import StyledModal from "../../common/component/styled/Modal";
import Button from "../../common/component/styled/Button";
import Checkbox from "../../common/component/styled/Checkbox";
import useLogout from "../../common/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 "../../common/component/Modal";
export default function LogoutConfirmModal({ closeModal }) {
const navigate = useNavigate();
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();
}
navigate("/");
}
}, [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>
);
}
+186
View File
@@ -0,0 +1,186 @@
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 "../../common/component/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}
/>
)}
</>
);
}
+26
View File
@@ -0,0 +1,26 @@
// import React from "react";
import styled from "styled-components";
import useNotification from "../../common/hook/useNotification";
import StyledToggle from "../../common/component/styled/Toggle";
import Label from "../../common/component/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>
);
}
+179
View File
@@ -0,0 +1,179 @@
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 "../../common/component/AvatarUploader";
import Input from "../../common/component/styled/Input";
import Label from "../../common/component/styled/Label";
import Textarea from "../../common/component/styled/Textarea";
import SaveTip from "../../common/component/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>
);
}
@@ -0,0 +1,71 @@
// import React from "react";
import { useEffect, useState } from "react";
import styled from "styled-components";
import Input from "../../common/component/styled/Input";
import { useUpdateInfoMutation } from "../../app/services/contact";
import StyledModal from "../../common/component/styled/Modal";
import Button from "../../common/component/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 "../../common/component/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>
);
}
+118
View File
@@ -0,0 +1,118 @@
// import { useState, useEffect } from "react";
import StyledContainer from "./StyledContainer";
import Input from "../../../common/component/styled/Input";
import Textarea from "../../../common/component/styled/Textarea";
import Label from "../../../common/component/styled/Label";
import Toggle from "../../../common/component/styled/Toggle";
import SaveTip from "../../../common/component/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>
);
}
+90
View File
@@ -0,0 +1,90 @@
// import { useState, useEffect } from "react";
import StyledContainer from "./StyledContainer";
import Input from "../../../common/component/styled/Input";
import Textarea from "../../../common/component/styled/Textarea";
import Toggle from "../../../common/component/styled/Toggle";
import Label from "../../../common/component/styled/Label";
import SaveTip from "../../../common/component/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>
);
}
+119
View File
@@ -0,0 +1,119 @@
// import { useState, useEffect } from "react";
import StyledContainer from "./StyledContainer";
import Textarea from "../../../common/component/styled/Textarea";
import Toggle from "../../../common/component/styled/Toggle";
import Label from "../../../common/component/styled/Label";
import SaveTip from "../../../common/component/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>
);
}
+161
View File
@@ -0,0 +1,161 @@
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 "../../../common/component/styled/Input";
import Button from "../../../common/component/styled/Button";
import Toggle from "../../../common/component/styled/Toggle";
import Label from "../../../common/component/styled/Label";
import SaveTip from "../../../common/component/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>
);
}
@@ -0,0 +1,40 @@
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;
+107
View File
@@ -0,0 +1,107 @@
import { useEffect, useState } from "react";
import { isObjectEqual } from "../../../common/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,
};
}
+46
View File
@@ -0,0 +1,46 @@
import { useState } from "react";
// import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import StyledSettingContainer from "../../common/component/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 navgateTo = useNavigate();
const close = () => {
// dispatch(toggleSetting());
navgateTo(-1);
};
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} />}
</>
);
}
+104
View File
@@ -0,0 +1,104 @@
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 "../../common/component/ManageMembers";
import FAQ from "../../common/component/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;