Logging fixes, redeploy selection, Logo
This commit is contained in:
+86
-9
@@ -6,12 +6,13 @@ export default function Stacks() {
|
||||
const [stacks, setStacks] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
const [selectedStackIds, setSelectedStackIds] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = io("/", {
|
||||
path: "/socket.io",
|
||||
transports: ["websocket"]
|
||||
});
|
||||
const socket = io("/", {
|
||||
path: "/socket.io",
|
||||
transports: ["websocket"]
|
||||
});
|
||||
console.log("🔌 Socket connected");
|
||||
|
||||
socket.on("redeployStatus", async ({ stackId, status }) => {
|
||||
@@ -42,7 +43,8 @@ const socket = io("/", {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await axios.get("/api/stacks");
|
||||
setStacks(res.data.map(stack => ({ ...stack, redeploying: stack.redeploying || false })));
|
||||
const sortedStacks = [...res.data].sort((a, b) => a.Name.localeCompare(b.Name));
|
||||
setStacks(sortedStacks.map(stack => ({ ...stack, redeploying: stack.redeploying || false })));
|
||||
} catch (err) {
|
||||
console.error("❌ Fehler beim Abrufen der Stacks:", err);
|
||||
setError("Fehler beim Laden der Stacks");
|
||||
@@ -55,7 +57,24 @@ const socket = io("/", {
|
||||
fetchStacks();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedStackIds(prev => {
|
||||
const filtered = prev.filter(id => stacks.some(stack => stack.Id === id));
|
||||
return filtered.length === prev.length ? prev : filtered;
|
||||
});
|
||||
}, [stacks]);
|
||||
|
||||
const toggleStackSelection = (stackId, disabled) => {
|
||||
if (disabled) return;
|
||||
setSelectedStackIds(prev =>
|
||||
prev.includes(stackId)
|
||||
? prev.filter(id => id !== stackId)
|
||||
: [...prev, stackId]
|
||||
);
|
||||
};
|
||||
|
||||
const handleRedeploy = async (stackId) => {
|
||||
setSelectedStackIds(prev => prev.filter(id => id !== stackId));
|
||||
setStacks(prev =>
|
||||
prev.map(stack => stack.Id === stackId ? { ...stack, redeploying: true } : stack)
|
||||
);
|
||||
@@ -76,6 +95,7 @@ const socket = io("/", {
|
||||
|
||||
try {
|
||||
await axios.put("/api/stacks/redeploy-all");
|
||||
setSelectedStackIds([]);
|
||||
// Statusupdates kommen über Socket.IO
|
||||
} catch (err) {
|
||||
console.error("❌ Fehler beim Redeploy ALL:", err);
|
||||
@@ -83,6 +103,53 @@ const socket = io("/", {
|
||||
}
|
||||
};
|
||||
|
||||
const handleRedeploySelection = async () => {
|
||||
if (!selectedStackIds.length) return;
|
||||
|
||||
setStacks(prev =>
|
||||
prev.map(stack =>
|
||||
selectedStackIds.includes(stack.Id)
|
||||
? { ...stack, redeploying: true }
|
||||
: stack
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
await axios.put("/api/stacks/redeploy-selection", { stackIds: selectedStackIds });
|
||||
setSelectedStackIds([]);
|
||||
// Statusupdates kommen über Socket.IO
|
||||
} catch (err) {
|
||||
console.error("❌ Fehler beim Redeploy Auswahl:", err);
|
||||
setStacks(prev =>
|
||||
prev.map(stack =>
|
||||
selectedStackIds.includes(stack.Id)
|
||||
? { ...stack, redeploying: false }
|
||||
: stack
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const hasSelection = selectedStackIds.length > 0;
|
||||
const bulkButtonLabel = hasSelection
|
||||
? `Redeploy Auswahl (${selectedStackIds.length})`
|
||||
: 'Redeploy All';
|
||||
|
||||
const bulkActionDisabled = hasSelection
|
||||
? selectedStackIds.every(id => {
|
||||
const targetStack = stacks.find(stack => stack.Id === id);
|
||||
return targetStack?.redeploying;
|
||||
})
|
||||
: stacks.every(stack => stack.redeploying);
|
||||
|
||||
const handleBulkRedeploy = () => {
|
||||
if (hasSelection) {
|
||||
handleRedeploySelection();
|
||||
} else {
|
||||
handleRedeployAll();
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <p className="text-gray-400">Lade Stacks...</p>;
|
||||
if (error) return <p className="text-red-400">{error}</p>;
|
||||
|
||||
@@ -90,24 +157,34 @@ const socket = io("/", {
|
||||
<div className="p-6">
|
||||
<div className="flex justify-end mb-4">
|
||||
<button
|
||||
onClick={handleRedeployAll}
|
||||
className="px-5 py-2 rounded-lg font-medium transition bg-purple-500 hover:bg-purple-600"
|
||||
onClick={handleBulkRedeploy}
|
||||
disabled={bulkActionDisabled}
|
||||
className={`px-5 py-2 rounded-lg font-medium transition ${bulkActionDisabled ? 'bg-purple-900 cursor-not-allowed text-gray-400' : 'bg-purple-500 hover:bg-purple-600'}`}
|
||||
>
|
||||
Redeploy All
|
||||
{bulkButtonLabel}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{stacks.map(stack => {
|
||||
const isRedeploying = stack.redeploying;
|
||||
const isSelected = selectedStackIds.includes(stack.Id);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={stack.Id}
|
||||
className={`flex justify-between items-center p-5 rounded-xl shadow-lg transition
|
||||
className={`flex justify-between items-center p-5 rounded-xl shadow-lg transition border
|
||||
${isSelected ? 'border-purple-500 ring-1 ring-purple-500/40' : 'border-transparent'}
|
||||
${isRedeploying ? "bg-gray-700 cursor-not-allowed" : "bg-gray-800 hover:bg-gray-700"}`}
|
||||
>
|
||||
<div className="flex items-center space-x-4">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isSelected}
|
||||
onChange={() => toggleStackSelection(stack.Id, isRedeploying)}
|
||||
className="h-5 w-5 text-purple-500 focus:ring-purple-400 border-gray-600 bg-gray-900 rounded"
|
||||
disabled={isRedeploying}
|
||||
/>
|
||||
<div className={`w-12 h-12 flex items-center justify-center rounded-full
|
||||
${stack.updateStatus === "✅" ? "bg-green-500" :
|
||||
stack.updateStatus === "⚠️" ? "bg-yellow-500" :
|
||||
|
||||
Reference in New Issue
Block a user