Improve validation and error handling on form field

This commit is contained in:
Burak Arslan committed 2025-08-06 00:53:35 +03:00
1 parent 0ea3379397
commit 4c2c452b91
9 files changed
+35 -13

No files matched your search

@@ -50,6 +50,7 @@ const Select = ({
onChange,
onBlur,
sx,
error = false,
name = "",
labelControlSpacing = 6,
maxWidth,
@@ -93,6 +94,7 @@ const Select = ({
onChange={onChange}
onBlur={onBlur}
displayEmpty
error={error}
name={name}
inputProps={{ id: id }}
IconComponent={KeyboardArrowDownIcon}
@@ -172,6 +174,7 @@ Select.propTypes = {
label: PropTypes.string,
placeholder: PropTypes.string,
isHidden: PropTypes.bool,
error: PropTypes.bool,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool])
.isRequired,
items: PropTypes.arrayOf(
+9 -1
View File
@@ -270,6 +270,11 @@ const UptimeCreate = ({ isClone = false }) => {
setMonitor((prev) => ({ ...prev, [name]: value }));
if (name === "type") {
setErrors({});
return;
}
const { error } = monitorValidation.validate(
{ type: monitor.type, [name]: value },
{ abortEarly: false }
@@ -277,7 +282,9 @@ const UptimeCreate = ({ isClone = false }) => {
setErrors((prev) => ({
...prev,
...(error ? { [name]: error.details[0].message } : { [name]: undefined }),
...(error && error.details[0].path[0] === name
? { [name]: error.details[0].message }
: { [name]: undefined }),
}));
};
@@ -593,6 +600,7 @@ const UptimeCreate = ({ isClone = false }) => {
placeholder={t("chooseGame")}
onChange={onChange}
items={GAMELIST}
error={errors["gameId"] ? true : false}
/>
)}
<TextInput
+11 -4
View File
@@ -184,12 +184,12 @@ const monitorValidation = joi.object({
.min(1)
.max(65535)
.when("type", {
is: "port",
then: joi.number().messages({
is: joi.valid("port", "game"),
then: joi.required().messages({
"number.base": "Port must be a number.",
"number.min": "Port must be at least 1.",
"number.max": "Port must be at most 65535.",
"any.required": "Port is required for port monitors.",
"any.required": "Port is required for port and game monitors.",
}),
otherwise: joi.optional(),
}),
@@ -205,7 +205,14 @@ const monitorValidation = joi.object({
expectedValue: joi.string().allow(null, ""),
jsonPath: joi.string().allow(null, ""),
matchMethod: joi.string().allow(null, ""),
gameId: joi.string().allow(null, ""),
gameId: joi.when("type", {
is: "game",
then: joi.string().required().messages({
"string.empty": "Game selection is required for game monitors.",
"any.required": "Game selection is required for game monitors.",
}),
otherwise: joi.string().allow(null, ""),
}),
});
const imageValidation = joi.object({
+2
View File
@@ -31,6 +31,7 @@ const { compile } = pkg;
import mjml2html from "mjml";
import jwt from "jsonwebtoken";
import crypto from "crypto";
import { games } from "gamedig";
import { fileURLToPath } from "url";
import { ObjectId } from "mongodb";
@@ -201,6 +202,7 @@ export const initializeServices = async ({ logger, envSettings, settingsService
papaparse,
logger,
errorService,
games,
});
const services = {
+1 -2
View File
@@ -15,7 +15,6 @@ import {
import sslChecker from "ssl-checker";
import { fetchMonitorCertificate } from "./controllerUtils.js";
import BaseController from "./baseController.js";
import { games } from "gamedig";
const SERVICE_NAME = "monitorController";
class MonitorController extends BaseController {
@@ -447,7 +446,7 @@ class MonitorController extends BaseController {
async (req, res) => {
return res.success({
msg: "OK",
data: games,
data: this.monitorService.getAllGames(),
});
},
SERVICE_NAME,
+1 -3
View File
@@ -25,9 +25,6 @@ class MonitorRoutes {
// Hardware routes
this.router.get("/hardware/details/:monitorId", this.monitorController.getHardwareDetailsById);
// Game routes
this.router.get("/games", this.monitorController.getAllGames);
// General monitor routes
this.router.post("/pause/:monitorId", isAllowed(["admin", "superadmin"]), this.monitorController.pauseMonitor);
this.router.get("/stats/:monitorId", this.monitorController.getMonitorStatsById);
@@ -47,6 +44,7 @@ class MonitorRoutes {
this.router.get("/export", isAllowed(["admin", "superadmin"]), this.monitorController.exportMonitorsToCSV);
this.router.post("/bulk", isAllowed(["admin", "superadmin"]), upload.single("csvFile"), this.monitorController.createBulkMonitors);
this.router.post("/test-email", isAllowed(["admin", "superadmin"]), this.monitorController.sendTestEmail);
this.router.get("/games", this.monitorController.getAllGames);
// Individual monitor CRUD routes
this.router.get("/:monitorId", this.monitorController.getMonitorById);
@@ -4,7 +4,7 @@ const SERVICE_NAME = "MonitorService";
class MonitorService {
static SERVICE_NAME = SERVICE_NAME;
constructor({ db, settingsService, jobQueue, stringService, emailService, papaparse, logger, errorService }) {
constructor({ db, settingsService, jobQueue, stringService, emailService, papaparse, logger, errorService, games }) {
this.db = db;
this.settingsService = settingsService;
this.jobQueue = jobQueue;
@@ -13,6 +13,7 @@ class MonitorService {
this.papaparse = papaparse;
this.logger = logger;
this.errorService = errorService;
this.games = games;
}
get serviceName() {
@@ -262,6 +263,10 @@ class MonitorService {
const csv = this.papaparse.unparse(csvData);
return csv;
};
getAllGames = () => {
return this.games;
};
}
export default MonitorService;
@@ -503,7 +503,7 @@ class NetworkService {
host: url,
port: port,
}).catch((error) => {
return false;
null;
});
if (!state) {
+1 -1
View File
@@ -171,7 +171,7 @@ const createMonitorBodyValidation = joi.object({
jsonPath: joi.string().allow(""),
expectedValue: joi.string().allow(""),
matchMethod: joi.string(),
gameId: joi.string(),
gameId: joi.string().allow(""),
});
const createMonitorsBodyValidation = joi.array().items(