David Headrick 0e099bfd07 Enhance authentication and logging mechanisms
Updated authentication handling in controllers, added JWT support, and improved error logging. Introduced centralized API calls with customFetch for better token management. Added Grafana's Faro SDK for monitoring and tracing. Refactored project files for improved structure and maintainability.
2025-05-19 17:26:37 -05:00

152 lines
5.1 KiB
TypeScript

import { useState, useEffect } from "react";
import {
Dialog,
DialogTitle,
DialogContent,
TextField,
DialogActions,
Button,
} from "@mui/material";
import Server from "@/types/server";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { useCustomFetch } from "@/utils/customFetch";
type ServerEditProps = {
open: boolean;
server: Server | null;
onClose: () => void;
onSave: (updatedServer: Server, password: string) => void;
};
const schema = yup.object().shape({
id: yup.number().default(0),
name: yup
.string()
.required("Name is required")
.test("unique-name", "Name must be unique", function (value) {
const setupData = this.options.context?.setupData as { servers: Server[] };
if (!setupData) return true;
return !setupData.servers.some(
(d) => d.name.toLowerCase() === value?.toLowerCase() && (d.id === 0 || d.id !== this.parent.id)
);
}),
serverName: yup.string().required("Server name is required"),
port: yup.number().default(1433),
username: yup.string().required("Username is required"),
password: yup.string().default("")
.test("required-if-new", "Password is required", function (value) {
if (this.parent.id > 0) return true;
else return value.length > 0;
})
});
const defaultServer: Server = {
id: 0,
name: "",
serverName: "",
port: 1433,
username: "",
password: "",
};
const ServerEdit = ({ open, server, onClose, onSave }: ServerEditProps) => {
const customFetch = useCustomFetch();
const isNew = !server || server.id === 0;
const originalServer: Server | null = server ? { ...server } : null;
//const [formData, setFormData] = useState<Server>({ ...server });
const { register, handleSubmit, reset, formState: { errors } } = useForm<Server>({
mode: "onBlur",
defaultValues: server || defaultServer,
resolver: yupResolver(schema)
});
const [loading, setLoading] = useState(false);
useEffect(() => {
if (open) {
reset(server || defaultServer, { keepDefaultValues: true });
}
}, [open, server, reset]);
const handleSave = async (formData: Server) => {
const apiUrl = isNew ? "/api/servers" : `/api/servers/${formData.id}`;
const method = isNew ? "POST" : "PUT";
setLoading(true);
try {
const response = await customFetch(apiUrl, {
method: method,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(formData),
});
if (!response.ok) throw new Error("Failed to update");
const updatedServer = await response.json();
onSave(updatedServer, isNew || formData.password ? formData.password : originalServer?.password ?? "");
onClose();
} catch (error) {
console.error("Update error:", error);
} finally {
setLoading(false);
}
};
return (
<Dialog open={open} onClose={onClose} maxWidth="sm" fullWidth>
<DialogTitle>{isNew ? "Add Server" : "Edit Server id=" + server?.id}</DialogTitle>
<DialogContent>
<TextField
{...register("name")}
label="Name"
fullWidth
margin="dense"
error={!!errors.name}
helperText={errors.name?.message}
/>
<TextField
{...register("serverName")}
label="Server Name"
fullWidth
margin="dense"
error={!!errors.serverName}
helperText={errors.serverName?.message}
/>
<TextField
{...register("port")}
label="Port"
fullWidth
margin="dense"
error={!!errors.port}
helperText={errors.port?.message}
/>
<TextField
{...register("username")}
label="Username"
fullWidth
margin="dense"
error={!!errors.username}
helperText={errors.username?.message}
/>
<TextField
{...register("password")}
label="Password"
fullWidth
margin="dense"
error={!!errors.password}
helperText={errors.password?.message}
/>
</DialogContent>
<DialogActions>
<Button onClick={onClose} disabled={loading}>Cancel</Button>
<Button onClick={handleSubmit(handleSave)} color="primary" disabled={loading}>
{loading ? "Saving..." : "Save"}
</Button>
</DialogActions>
</Dialog>
);
};
export default ServerEdit;