mirror of
https://github.com/gnmyt/myspeed.git
synced 2026-02-11 08:08:49 -06:00
Create PasswordDialog component
This commit is contained in:
134
client/src/common/components/PasswordDialog/PasswordDialog.jsx
Normal file
134
client/src/common/components/PasswordDialog/PasswordDialog.jsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import {Dialog, DialogHeader, DialogBody, DialogFooter} from "@/common/contexts/Dialog";
|
||||
import {t} from "i18next";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faCheck,
|
||||
faExclamationTriangle,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faKey,
|
||||
faShieldHalved,
|
||||
faLock,
|
||||
faBookOpen
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import "./styles.sass";
|
||||
import React, {useContext, useState} from "react";
|
||||
import {baseRequest, patchRequest} from "@/common/utils/RequestUtil";
|
||||
import {ConfigContext} from "@/common/contexts/Config";
|
||||
import {ToastNotificationContext} from "@/common/contexts/ToastNotification";
|
||||
import {NodeContext} from "@/common/contexts/Node";
|
||||
|
||||
export const PasswordDialog = ({open, onClose}) => {
|
||||
const [config, reloadConfig] = useContext(ConfigContext);
|
||||
const updateToast = useContext(ToastNotificationContext);
|
||||
const findNode = useContext(NodeContext)[4];
|
||||
const updateNodes = useContext(NodeContext)[1];
|
||||
const currentNode = useContext(NodeContext)[2];
|
||||
|
||||
const [password, setPassword] = useState("");
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [accessLevel, setAccessLevel] = useState(config.passwordLevel || "none");
|
||||
|
||||
const resetState = () => {
|
||||
setPassword("");
|
||||
setShowPassword(false);
|
||||
setAccessLevel(config.passwordLevel || "none");
|
||||
};
|
||||
|
||||
const handleClose = (close) => {
|
||||
resetState();
|
||||
close();
|
||||
};
|
||||
|
||||
const save = async (close) => {
|
||||
try {
|
||||
if (password) {
|
||||
await patchRequest("/config/password", {value: password});
|
||||
if (currentNode !== 0) {
|
||||
await baseRequest("/nodes/" + currentNode + "/password", "PATCH", {password});
|
||||
updateNodes();
|
||||
} else {
|
||||
localStorage.setItem("password", password);
|
||||
}
|
||||
}
|
||||
|
||||
if (accessLevel !== config.passwordLevel) {
|
||||
await patchRequest("/config/passwordLevel", {value: accessLevel});
|
||||
}
|
||||
|
||||
reloadConfig();
|
||||
updateToast(t("dropdown.changes_applied"), "green", faCheck);
|
||||
handleClose(close);
|
||||
} catch (e) {
|
||||
updateToast(t("dropdown.changes_unsaved"), "red", faExclamationTriangle);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} className="password-dialog">
|
||||
{({close}) => (
|
||||
<>
|
||||
<DialogHeader onClose={() => handleClose(close)}>{t("dropdown.password")}</DialogHeader>
|
||||
<DialogBody>
|
||||
<div className="password-content">
|
||||
<div className="password-section">
|
||||
<div className="password-label">
|
||||
<FontAwesomeIcon icon={faKey}/>
|
||||
<h3>{t("update.new_password")}</h3>
|
||||
</div>
|
||||
<div className="password-input-wrapper">
|
||||
<input
|
||||
type={showPassword ? "text" : "password"}
|
||||
className="dialog-input"
|
||||
placeholder={t("update.password_placeholder")}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="password-toggle"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
>
|
||||
<FontAwesomeIcon icon={showPassword ? faEyeSlash : faEye}/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="password-section">
|
||||
<div className="password-label">
|
||||
<FontAwesomeIcon icon={faShieldHalved}/>
|
||||
<h3>{t("update.level_title")}</h3>
|
||||
</div>
|
||||
<div className="access-options">
|
||||
<button
|
||||
className={`access-option${accessLevel === "none" ? " access-active" : ""}`}
|
||||
onClick={() => setAccessLevel("none")}
|
||||
>
|
||||
<FontAwesomeIcon icon={faLock}/>
|
||||
<div className="access-text">
|
||||
<span className="access-title">{t("options.level.no_access")}</span>
|
||||
<span className="access-desc">{t("password.no_access_desc")}</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
className={`access-option${accessLevel === "read" ? " access-active" : ""}`}
|
||||
onClick={() => setAccessLevel("read")}
|
||||
>
|
||||
<FontAwesomeIcon icon={faBookOpen}/>
|
||||
<div className="access-text">
|
||||
<span className="access-title">{t("options.level.read_access")}</span>
|
||||
<span className="access-desc">{t("password.read_access_desc")}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogBody>
|
||||
<DialogFooter>
|
||||
<button className="dialog-btn" onClick={() => save(close)}>{t("dialog.update")}</button>
|
||||
</DialogFooter>
|
||||
</>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
1
client/src/common/components/PasswordDialog/index.js
Normal file
1
client/src/common/components/PasswordDialog/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export {PasswordDialog as default} from './PasswordDialog';
|
||||
109
client/src/common/components/PasswordDialog/styles.sass
Normal file
109
client/src/common/components/PasswordDialog/styles.sass
Normal file
@@ -0,0 +1,109 @@
|
||||
@use "@/common/styles/colors" as *
|
||||
|
||||
.password-dialog
|
||||
width: 22rem
|
||||
|
||||
.dialog-main
|
||||
display: block
|
||||
|
||||
.password-content
|
||||
margin: 1rem 0.5rem
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 1.5rem
|
||||
user-select: none
|
||||
|
||||
.password-section
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 0.5rem
|
||||
|
||||
.password-label
|
||||
display: flex
|
||||
align-items: center
|
||||
gap: 0.5rem
|
||||
color: $accent-primary
|
||||
|
||||
svg
|
||||
font-size: 0.9rem
|
||||
|
||||
h3
|
||||
font-size: 0.9rem
|
||||
font-weight: 600
|
||||
margin: 0
|
||||
|
||||
.password-input-wrapper
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
|
||||
.dialog-input
|
||||
flex: 1
|
||||
padding-right: 2.5rem
|
||||
|
||||
.password-toggle
|
||||
position: absolute
|
||||
right: 0.75rem
|
||||
background: none
|
||||
border: none
|
||||
color: $subtext
|
||||
cursor: pointer
|
||||
padding: 0.5rem
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
transition: color 0.15s ease
|
||||
|
||||
&:hover
|
||||
color: $white
|
||||
|
||||
.access-options
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 0.5rem
|
||||
|
||||
.access-option
|
||||
display: flex
|
||||
align-items: center
|
||||
gap: 0.8rem
|
||||
padding: 0.6rem 1rem
|
||||
background: transparent
|
||||
border: 1px solid $light-gray
|
||||
border-radius: 0.5rem
|
||||
cursor: pointer
|
||||
transition: all 0.15s ease
|
||||
text-align: left
|
||||
|
||||
&:hover
|
||||
background-color: $darker-gray
|
||||
|
||||
svg
|
||||
font-size: 1.2rem
|
||||
color: $subtext
|
||||
width: 1.5rem
|
||||
|
||||
&.access-active
|
||||
background-color: $light-gray
|
||||
|
||||
svg, .access-title
|
||||
color: $white
|
||||
|
||||
&:hover
|
||||
background-color: $light-gray
|
||||
|
||||
.access-text
|
||||
display: flex
|
||||
flex-direction: column
|
||||
flex: 1
|
||||
|
||||
.access-title
|
||||
font-size: 0.95rem
|
||||
font-weight: 600
|
||||
color: $subtext
|
||||
|
||||
.access-desc
|
||||
font-size: 0.8rem
|
||||
color: $subtext
|
||||
opacity: 0.7
|
||||
margin-top: 0.15rem
|
||||
|
||||
Reference in New Issue
Block a user