Skip to content

Commit ab3b40e

Browse files
committed
Add reset AI model button
1 parent 35cdbd1 commit ab3b40e

3 files changed

Lines changed: 152 additions & 1 deletion

File tree

backend/src/main/java/com/apexgrid/transformertracker/ai/AiParameterService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ public synchronized double adjustValue(AiParameterKey key, double delta) {
7676
return setValue(key, updated);
7777
}
7878

79+
public synchronized void resetToDefaults() {
80+
for (AiParameterKey key : AiParameterKey.values()) {
81+
setValue(key, key.getDefaultValue());
82+
}
83+
}
84+
7985
public ObjectNode buildConfigNode(ObjectMapper mapper) {
8086
Assert.notNull(mapper, "ObjectMapper is required");
8187
ObjectNode node = mapper.createObjectNode();

backend/src/main/java/com/apexgrid/transformertracker/web/InspectionController.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.apexgrid.transformertracker.web;
22

3+
import com.apexgrid.transformertracker.ai.AiParameterService;
34
import com.apexgrid.transformertracker.ai.ParameterTuningService;
45
import com.apexgrid.transformertracker.ai.PythonAnalyzerService;
56
import com.apexgrid.transformertracker.model.Inspection;
@@ -44,15 +45,18 @@ public class InspectionController {
4445
private final TransformerRepo transformerRepo;
4546
private final PythonAnalyzerService pythonAnalyzerService;
4647
private final ParameterTuningService parameterTuningService;
48+
private final AiParameterService aiParameterService;
4749

4850
public InspectionController(InspectionRepo repo,
4951
TransformerRepo transformerRepo,
5052
PythonAnalyzerService pythonAnalyzerService,
51-
ParameterTuningService parameterTuningService) {
53+
ParameterTuningService parameterTuningService,
54+
AiParameterService aiParameterService) {
5255
this.repo = repo;
5356
this.transformerRepo = transformerRepo;
5457
this.pythonAnalyzerService = pythonAnalyzerService;
5558
this.parameterTuningService = parameterTuningService;
59+
this.aiParameterService = aiParameterService;
5660
}
5761

5862
@GetMapping
@@ -1286,5 +1290,21 @@ public ResponseEntity<?> bulkUpdateBoxes(@PathVariable String id,
12861290
}).orElse(ResponseEntity.notFound().build());
12871291
}
12881292

1293+
@PostMapping("/model/reset")
1294+
public ResponseEntity<?> resetModelParameters(@RequestHeader(value = "x-username", required = false) String username) {
1295+
try {
1296+
aiParameterService.resetToDefaults();
1297+
Map<String, Double> parameters = aiParameterService.getAllParameters();
1298+
return ResponseEntity.ok(Map.of(
1299+
"ok", Boolean.TRUE,
1300+
"parameters", parameters,
1301+
"resetBy", (username == null || username.isBlank()) ? "user" : username
1302+
));
1303+
} catch (Exception ex) {
1304+
return ResponseEntity.internalServerError()
1305+
.body(Map.of("error", "Failed to reset model parameters"));
1306+
}
1307+
}
1308+
12891309
// No-op
12901310
}

frontend/components/InspectionDetailsPanel.tsx

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,10 @@ const InspectionDetailsPanel = ({
464464
h: number;
465465
} | null>(null);
466466
const [awaitingRedraw, setAwaitingRedraw] = useState(false);
467+
const [showResetModelConfirm, setShowResetModelConfirm] = useState(false);
468+
const [isResettingModel, setIsResettingModel] = useState(false);
469+
const [resetModelMessage, setResetModelMessage] = useState<string | null>(null);
470+
const [resetModelError, setResetModelError] = useState<string | null>(null);
467471

468472
const transformer = useMemo(
469473
() =>
@@ -935,6 +939,59 @@ const InspectionDetailsPanel = ({
935939
}
936940
};
937941

942+
const handleResetModelClick = () => {
943+
setResetModelMessage(null);
944+
setResetModelError(null);
945+
setShowResetModelConfirm(true);
946+
};
947+
948+
const closeResetModelModal = () => {
949+
if (isResettingModel) return;
950+
setShowResetModelConfirm(false);
951+
};
952+
953+
const handleConfirmResetModel = async () => {
954+
setIsResettingModel(true);
955+
setResetModelError(null);
956+
try {
957+
const username =
958+
typeof window !== "undefined" ? localStorage.getItem("username") || "" : "";
959+
const response = await fetch(apiUrl(`/api/inspections/model/reset`), {
960+
method: "POST",
961+
headers: {
962+
"content-type": "application/json",
963+
"x-username": username,
964+
},
965+
});
966+
if (!response.ok) {
967+
const text = await response.text();
968+
throw new Error(text || `Reset failed (${response.status})`);
969+
}
970+
const payload: unknown = await response
971+
.json()
972+
.catch(() => null);
973+
let actor: string | undefined;
974+
if (typeof payload === "object" && payload !== null) {
975+
const candidate = (payload as { resetBy?: unknown }).resetBy;
976+
if (typeof candidate === "string") {
977+
actor = candidate;
978+
}
979+
}
980+
setShowResetModelConfirm(false);
981+
setResetModelMessage(
982+
actor && actor.trim().length > 0
983+
? `AI parameters reset to defaults by ${actor}.`
984+
: "AI parameters reset to defaults."
985+
);
986+
} catch (error) {
987+
const message =
988+
error instanceof Error ? error.message : "Failed to reset model parameters.";
989+
setResetModelError(message);
990+
} finally {
991+
setIsResettingModel(false);
992+
}
993+
};
994+
938995
const closeFaultModal = () => {
939996
setShowFaultModal(false);
940997
setPendingRect(null);
@@ -1337,12 +1394,36 @@ const InspectionDetailsPanel = ({
13371394
type="checkbox"
13381395
className="peer sr-only"
13391396
checked={tuneModelEnabled}
1397+
disabled={isResettingModel}
13401398
onChange={(event) => setTuneModelEnabled(event.target.checked)}
13411399
/>
13421400
<span className="pointer-events-none absolute inset-0 rounded-full bg-gray-400 transition-colors peer-checked:bg-green-500" />
13431401
<span className="pointer-events-none absolute left-1 top-1 h-3 w-3 rounded-full bg-white transition-transform peer-checked:translate-x-5" />
13441402
</span>
13451403
</label>
1404+
<button
1405+
type="button"
1406+
onClick={handleResetModelClick}
1407+
disabled={isResettingModel}
1408+
className="inline-flex items-center gap-2 px-3 py-1 text-sm border rounded custombutton disabled:opacity-60 disabled:cursor-not-allowed"
1409+
title="Reset AI parameters to their defaults"
1410+
>
1411+
<svg
1412+
className="w-4 h-4"
1413+
fill="none"
1414+
stroke="currentColor"
1415+
strokeWidth={1.5}
1416+
strokeLinecap="round"
1417+
strokeLinejoin="round"
1418+
viewBox="0 0 24 24"
1419+
>
1420+
<path d="M4 4v6h6" />
1421+
<path d="M20 20v-6h-6" />
1422+
<path d="M20 9.5A7.5 7.5 0 0 0 11 2h-1" />
1423+
<path d="M4 14.5A7.5 7.5 0 0 0 13 22h1" />
1424+
</svg>
1425+
<span>{isResettingModel ? "Resetting…" : "Reset model"}</span>
1426+
</button>
13461427
<button onClick={flushAndClose} className="text-gray-400 hover:text-gray-600" disabled={isClosing}>
13471428
<svg
13481429
className="w-6 h-6"
@@ -1361,6 +1442,13 @@ const InspectionDetailsPanel = ({
13611442
</div>
13621443
</div>
13631444

1445+
{!showResetModelConfirm && resetModelMessage && (
1446+
<div className="mb-4 text-sm text-green-600">{resetModelMessage}</div>
1447+
)}
1448+
{!showResetModelConfirm && resetModelError && (
1449+
<div className="mb-4 text-sm text-red-600">{resetModelError}</div>
1450+
)}
1451+
13641452
<div className="grid grid-cols-2 gap-4 mb-6">
13651453
<div>
13661454
<label className="block text-sm font-bold">Inspection Number</label>
@@ -2279,6 +2367,43 @@ const InspectionDetailsPanel = ({
22792367
</div>
22802368
</div>
22812369
</div>
2370+
{/* Reset model confirmation */}
2371+
{showResetModelConfirm && (
2372+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
2373+
<div className="details-panel bg-white dark:bg-[#111] rounded shadow-lg p-5 w-96">
2374+
<h3 className="font-semibold text-gray-900 dark:text-white mb-2">
2375+
Reset AI parameters?
2376+
</h3>
2377+
<p className="text-sm text-gray-700 dark:text-gray-300 mb-4">
2378+
This will restore all model tuning values to the factory defaults
2379+
and cannot be undone. Continue?
2380+
</p>
2381+
{resetModelError && (
2382+
<div className="mb-3 text-sm text-red-600" role="alert">
2383+
{resetModelError}
2384+
</div>
2385+
)}
2386+
<div className="flex justify-end gap-2">
2387+
<button
2388+
type="button"
2389+
className="px-3 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded text-gray-900 dark:text-white disabled:opacity-60"
2390+
onClick={closeResetModelModal}
2391+
disabled={isResettingModel}
2392+
>
2393+
Cancel
2394+
</button>
2395+
<button
2396+
type="button"
2397+
className="px-3 py-1 text-sm rounded bg-red-600 text-white disabled:opacity-60"
2398+
onClick={handleConfirmResetModel}
2399+
disabled={isResettingModel}
2400+
>
2401+
{isResettingModel ? "Resetting…" : "Yes, reset"}
2402+
</button>
2403+
</div>
2404+
</div>
2405+
</div>
2406+
)}
22822407
{/* Fault selection modal */}
22832408
{showFaultModal && pendingRect && (
22842409
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">

0 commit comments

Comments
 (0)