mirror of
https://github.com/9technologygroup/patchmon.net.git
synced 2025-11-03 05:23:45 +00:00
Fixed auto-update toggle to refresh upon save
This commit is contained in:
@@ -1073,7 +1073,7 @@ router.patch(
|
||||
});
|
||||
|
||||
res.json({
|
||||
message: `Host auto-update ${autoUpdate ? "enabled" : "disabled"} successfully`,
|
||||
message: `Host auto-update ${auto_update ? "enabled" : "disabled"} successfully`,
|
||||
host: {
|
||||
id: host.id,
|
||||
friendlyName: host.friendly_name,
|
||||
|
||||
106
frontend/src/components/InlineToggle.jsx
Normal file
106
frontend/src/components/InlineToggle.jsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import { Check, Edit2, X } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const InlineToggle = ({
|
||||
value,
|
||||
onSave,
|
||||
onCancel,
|
||||
className = "",
|
||||
disabled = false,
|
||||
trueLabel = "Yes",
|
||||
falseLabel = "No",
|
||||
}) => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
// Auto-save when value changes during editing
|
||||
if (isEditing && !isLoading) {
|
||||
handleSave(!value);
|
||||
}
|
||||
}, [isEditing]);
|
||||
|
||||
const handleEdit = () => {
|
||||
if (disabled) return;
|
||||
setIsEditing(true);
|
||||
setError("");
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setIsEditing(false);
|
||||
setError("");
|
||||
if (onCancel) onCancel();
|
||||
};
|
||||
|
||||
const handleSave = async (newValue) => {
|
||||
if (disabled || isLoading) return;
|
||||
|
||||
// Check if value actually changed
|
||||
if (newValue === value) {
|
||||
setIsEditing(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setError("");
|
||||
|
||||
try {
|
||||
await onSave(newValue);
|
||||
setIsEditing(false);
|
||||
} catch (err) {
|
||||
setError(err.message || "Failed to save");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggle = () => {
|
||||
if (disabled || isLoading) return;
|
||||
handleSave(!value);
|
||||
};
|
||||
|
||||
const displayValue = (
|
||||
<span
|
||||
className={`text-sm font-medium ${
|
||||
value
|
||||
? "text-green-600 dark:text-green-400"
|
||||
: "text-red-600 dark:text-red-400"
|
||||
}`}
|
||||
>
|
||||
{value ? trueLabel : falseLabel}
|
||||
</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={`flex items-center gap-2 group ${className}`}>
|
||||
{displayValue}
|
||||
{!disabled && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleToggle}
|
||||
disabled={isLoading}
|
||||
className={`relative inline-flex h-5 w-9 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed ${
|
||||
value
|
||||
? "bg-primary-600 dark:bg-primary-500"
|
||||
: "bg-secondary-200 dark:bg-secondary-600"
|
||||
}`}
|
||||
title={`Toggle ${value ? "off" : "on"}`}
|
||||
>
|
||||
<span
|
||||
className={`inline-block h-3 w-3 transform rounded-full bg-white transition-transform ${
|
||||
value ? "translate-x-5" : "translate-x-1"
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
{error && (
|
||||
<span className="text-xs text-red-600 dark:text-red-400">
|
||||
{error}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InlineToggle;
|
||||
@@ -27,6 +27,7 @@ import { useEffect, useId, useMemo, useState } from "react";
|
||||
import { Link, useNavigate, useSearchParams } from "react-router-dom";
|
||||
import InlineEdit from "../components/InlineEdit";
|
||||
import InlineGroupEdit from "../components/InlineGroupEdit";
|
||||
import InlineToggle from "../components/InlineToggle";
|
||||
import {
|
||||
adminHostsAPI,
|
||||
dashboardAPI,
|
||||
@@ -471,6 +472,14 @@ const Hosts = () => {
|
||||
},
|
||||
});
|
||||
|
||||
const toggleAutoUpdateMutation = useMutation({
|
||||
mutationFn: ({ hostId, autoUpdate }) =>
|
||||
adminHostsAPI.toggleAutoUpdate(hostId, autoUpdate).then((res) => res.data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(["hosts"]);
|
||||
},
|
||||
});
|
||||
|
||||
const bulkDeleteMutation = useMutation({
|
||||
mutationFn: (hostIds) => adminHostsAPI.deleteBulk(hostIds),
|
||||
onSuccess: (data) => {
|
||||
@@ -818,15 +827,17 @@ const Hosts = () => {
|
||||
);
|
||||
case "auto_update":
|
||||
return (
|
||||
<span
|
||||
className={`text-sm font-medium ${
|
||||
host.auto_update
|
||||
? "text-green-600 dark:text-green-400"
|
||||
: "text-red-600 dark:text-red-400"
|
||||
}`}
|
||||
>
|
||||
{host.auto_update ? "Yes" : "No"}
|
||||
</span>
|
||||
<InlineToggle
|
||||
value={host.auto_update}
|
||||
onSave={(autoUpdate) =>
|
||||
toggleAutoUpdateMutation.mutate({
|
||||
hostId: host.id,
|
||||
autoUpdate: autoUpdate,
|
||||
})
|
||||
}
|
||||
trueLabel="Yes"
|
||||
falseLabel="No"
|
||||
/>
|
||||
);
|
||||
case "status":
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user