Add token reveal toggle and reposition refresh button

This commit is contained in:
2025-10-16 17:14:36 -06:00
parent ab295478b6
commit 34f3ca58ea

View File

@@ -10,6 +10,9 @@ import {
Typography Typography
} from "@mui/material"; } from "@mui/material";
import RefreshIcon from "@mui/icons-material/Refresh"; import RefreshIcon from "@mui/icons-material/Refresh";
import SaveIcon from "@mui/icons-material/Save";
import VisibilityIcon from "@mui/icons-material/Visibility";
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
const paperSx = { const paperSx = {
m: 2, m: 2,
@@ -42,6 +45,7 @@ export default function GithubAPIToken({ isAdmin = false }) {
const [token, setToken] = useState(""); const [token, setToken] = useState("");
const [inputValue, setInputValue] = useState(""); const [inputValue, setInputValue] = useState("");
const [fetchError, setFetchError] = useState(""); const [fetchError, setFetchError] = useState("");
const [showToken, setShowToken] = useState(false);
const [verification, setVerification] = useState({ const [verification, setVerification] = useState({
message: "", message: "",
valid: null, valid: null,
@@ -62,6 +66,7 @@ export default function GithubAPIToken({ isAdmin = false }) {
const storedToken = typeof data?.token === "string" ? data.token : ""; const storedToken = typeof data?.token === "string" ? data.token : "";
setToken(storedToken); setToken(storedToken);
setInputValue(storedToken); setInputValue(storedToken);
setShowToken(false);
setVerification({ setVerification({
message: typeof data?.message === "string" ? data.message : "", message: typeof data?.message === "string" ? data.message : "",
valid: data?.valid === true, valid: data?.valid === true,
@@ -101,6 +106,7 @@ export default function GithubAPIToken({ isAdmin = false }) {
const storedToken = typeof data?.token === "string" ? data.token : ""; const storedToken = typeof data?.token === "string" ? data.token : "";
setToken(storedToken); setToken(storedToken);
setInputValue(storedToken); setInputValue(storedToken);
setShowToken(false);
setVerification({ setVerification({
message: typeof data?.message === "string" ? data.message : "", message: typeof data?.message === "string" ? data.message : "",
valid: data?.valid === true, valid: data?.valid === true,
@@ -135,6 +141,10 @@ export default function GithubAPIToken({ isAdmin = false }) {
return { text: message, color: "#ff8080" }; return { text: message, color: "#ff8080" };
}, [dirty, verification]); }, [dirty, verification]);
const toggleReveal = useCallback(() => {
setShowToken((prev) => !prev);
}, []);
if (!isAdmin) { if (!isAdmin) {
return ( return (
<Paper sx={{ m: 2, p: 3, bgcolor: "#1e1e1e" }}> <Paper sx={{ m: 2, p: 3, bgcolor: "#1e1e1e" }}>
@@ -197,16 +207,6 @@ export default function GithubAPIToken({ isAdmin = false }) {
</Box> </Box>
</Typography> </Typography>
</Box> </Box>
<Button
variant="outlined"
size="small"
startIcon={<RefreshIcon />}
sx={{ borderColor: "#58a6ff", color: "#58a6ff" }}
onClick={hydrate}
disabled={loading || saving}
>
Refresh
</Button>
</Box> </Box>
<Box sx={{ px: 2, py: 2, display: "flex", flexDirection: "column", gap: 1.5 }}> <Box sx={{ px: 2, py: 2, display: "flex", flexDirection: "column", gap: 1.5 }}>
<TextField <TextField
@@ -217,17 +217,36 @@ export default function GithubAPIToken({ isAdmin = false }) {
variant="outlined" variant="outlined"
sx={fieldSx} sx={fieldSx}
disabled={saving || loading} disabled={saving || loading}
type={showToken ? "text" : "password"}
InputProps={{ InputProps={{
endAdornment: ( endAdornment: (
<InputAdornment position="end" sx={{ mr: -1 }}> <InputAdornment
position="end"
sx={{ mr: -1, display: "flex", alignItems: "center", gap: 1 }}
>
<Button
variant="contained"
size="small"
onClick={toggleReveal}
disabled={loading || saving}
startIcon={showToken ? <VisibilityOffIcon /> : <VisibilityIcon />}
sx={{
bgcolor: "#3a3a3a",
color: "#f5f7fa",
minWidth: 96,
"&:hover": { bgcolor: "#4a4a4a" }
}}
>
{showToken ? "Hide" : "Reveal"}
</Button>
<Button <Button
variant="contained" variant="contained"
size="small" size="small"
onClick={handleSave} onClick={handleSave}
disabled={saving || loading} disabled={saving || loading}
startIcon={!saving ? <SaveIcon /> : null}
sx={{ sx={{
bgcolor: "#58a6ff", bgcolor: "#58a6ff",
mr: 1,
color: "#0b0f19", color: "#0b0f19",
minWidth: 88, minWidth: 88,
"&:hover": { bgcolor: "#7db7ff" } "&:hover": { bgcolor: "#7db7ff" }
@@ -240,35 +259,64 @@ export default function GithubAPIToken({ isAdmin = false }) {
}} }}
/> />
{(verificationMessage.text || (!dirty && verification.rateLimit)) && (
<Typography variant="body2" sx={{ display: "inline", color: verificationMessage.color || "#7db7ff" }}>
{verificationMessage.text && `${verificationMessage.text} `}
{!dirty && verification.rateLimit && `- Hourly Request Rate Limit: ${verification.rateLimit.toLocaleString()}`}
</Typography>
)}
{loading && (
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
gap: 1, justifyContent: "space-between",
color: "#7db7ff", gap: 2
px: 2,
py: 1.5,
borderBottom: "1px solid #2a2a2a"
}} }}
> >
<CircularProgress size={18} sx={{ color: "#58a6ff" }} /> <Button
<Typography variant="body2">Loading token</Typography> variant="outlined"
size="small"
startIcon={<RefreshIcon />}
sx={{ borderColor: "#58a6ff", color: "#58a6ff" }}
onClick={hydrate}
disabled={loading || saving}
>
Refresh
</Button>
{(verificationMessage.text || (!dirty && verification.rateLimit)) && (
<Typography
variant="body2"
sx={{
display: "inline-flex",
alignItems: "center",
color: verificationMessage.color || "#7db7ff",
textAlign: "right"
}}
>
{verificationMessage.text && `${verificationMessage.text} `}
{!dirty &&
verification.rateLimit &&
`- Hourly Request Rate Limit: ${verification.rateLimit.toLocaleString()}`}
</Typography>
)}
</Box> </Box>
)}
{fetchError && ( {loading && (
<Box sx={{ px: 2, py: 1.5, color: "#ff8080", borderBottom: "1px solid #2a2a2a" }}> <Box
<Typography variant="body2">{fetchError}</Typography> sx={{
</Box> display: "flex",
)} alignItems: "center",
gap: 1,
color: "#7db7ff",
px: 2,
py: 1.5,
borderBottom: "1px solid #2a2a2a"
}}
>
<CircularProgress size={18} sx={{ color: "#58a6ff" }} />
<Typography variant="body2">Loading token</Typography>
</Box>
)}
{fetchError && (
<Box sx={{ px: 2, py: 1.5, color: "#ff8080", borderBottom: "1px solid #2a2a2a" }}>
<Typography variant="body2">{fetchError}</Typography>
</Box>
)}
</Box> </Box>
</Paper> </Paper>
); );