feat: backend for delete functionality
Signed-off-by: Shrutim1505 <shrutimurthy2103@gmail.com>
This commit is contained in:
parent
3e76177d7f
commit
31c5d7cc1a
|
|
@ -277,21 +277,34 @@ app.get("/api/pods", async (req, res) => {
|
|||
|
||||
let filteredPods = response.items || [];
|
||||
|
||||
// Apply search filter
|
||||
// Improved search filter to search in both name and namespace
|
||||
if (searchTerm) {
|
||||
filteredPods = filteredPods.filter((pod) =>
|
||||
pod.metadata.name
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase()),
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
filteredPods = filteredPods.filter((pod) => {
|
||||
const podName = pod.metadata.name.toLowerCase();
|
||||
const podNamespace = pod.metadata.namespace.toLowerCase();
|
||||
return (
|
||||
podName.includes(searchLower) ||
|
||||
podNamespace.includes(searchLower)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if (statusFilter && statusFilter !== "All") {
|
||||
filteredPods = filteredPods.filter(
|
||||
(pod) => pod.status.phase === statusFilter,
|
||||
);
|
||||
}
|
||||
|
||||
// Sort pods by creation timestamp (newest first)
|
||||
filteredPods.sort((a, b) => {
|
||||
return (
|
||||
new Date(b.metadata.creationTimestamp) -
|
||||
new Date(a.metadata.creationTimestamp)
|
||||
);
|
||||
});
|
||||
|
||||
res.json({
|
||||
items: filteredPods,
|
||||
totalCount: filteredPods.length,
|
||||
|
|
@ -392,6 +405,65 @@ app.get("/api/all-queues", async (req, res) => {
|
|||
res.status(500).json({ error: "Failed to fetch all queues" });
|
||||
}
|
||||
});
|
||||
app.patch("/api/jobs/:namespace/:name", async (req, res) => {
|
||||
try {
|
||||
const { namespace, name } = req.params;
|
||||
const patchData = req.body;
|
||||
|
||||
const options = {
|
||||
headers: { "Content-Type": "application/merge-patch+json" },
|
||||
};
|
||||
|
||||
const response = await k8sApi.patchNamespacedCustomObject(
|
||||
"batch.volcano.sh",
|
||||
"v1alpha1",
|
||||
namespace,
|
||||
"jobs",
|
||||
name,
|
||||
patchData,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
options,
|
||||
);
|
||||
|
||||
res.json({ message: "Job updated successfully", data: response.body });
|
||||
} catch (error) {
|
||||
console.error("Error updating job:", error);
|
||||
res.status(500).json({ error: "Failed to update job" });
|
||||
}
|
||||
});
|
||||
app.patch("/api/queues/:namespace/:name", async (req, res) => {
|
||||
try {
|
||||
const { namespace, name } = req.params;
|
||||
const patchData = req.body;
|
||||
|
||||
const options = {
|
||||
headers: { "Content-Type": "application/merge-patch+json" },
|
||||
};
|
||||
|
||||
const response = await k8sApi.patchNamespacedCustomObject(
|
||||
"scheduling.volcano.sh",
|
||||
"v1alpha1",
|
||||
namespace,
|
||||
"queues",
|
||||
name,
|
||||
patchData,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
options,
|
||||
);
|
||||
|
||||
res.json({
|
||||
message: "Queue updated successfully",
|
||||
data: response.body,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error updating queue:", error);
|
||||
res.status(500).json({ error: "Failed to update queue" });
|
||||
}
|
||||
});
|
||||
|
||||
// Get all Pods (no pagination)
|
||||
app.get("/api/all-pods", async (req, res) => {
|
||||
|
|
@ -407,6 +479,62 @@ app.get("/api/all-pods", async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
app.delete("/api/queues/:name", async (req, res) => {
|
||||
const { name } = req.params;
|
||||
const queueName = name.toLowerCase();
|
||||
|
||||
try {
|
||||
await k8sApi.getClusterCustomObject({
|
||||
group: "scheduling.volcano.sh",
|
||||
version: "v1beta1",
|
||||
plural: "queues",
|
||||
name: queueName,
|
||||
});
|
||||
|
||||
const { body } = await k8sApi.deleteClusterCustomObject({
|
||||
group: "scheduling.volcano.sh",
|
||||
version: "v1beta1",
|
||||
plural: "queues",
|
||||
name: queueName,
|
||||
body: { propagationPolicy: "Foreground" },
|
||||
});
|
||||
|
||||
return res.json({ message: "Queue deleted successfully", data: body });
|
||||
} catch (err) {
|
||||
const statusCode = err?.statusCode || err?.response?.statusCode || 500;
|
||||
|
||||
let message = "An unexpected error occurred.";
|
||||
|
||||
try {
|
||||
const rawBody = err?.body || err?.response?.body;
|
||||
|
||||
// 🔹 Print the raw error body from Kubernetes
|
||||
console.error("Kubernetes Error Raw Body:", rawBody);
|
||||
|
||||
const parsedBody =
|
||||
typeof rawBody === "string" ? JSON.parse(rawBody) : rawBody;
|
||||
|
||||
// 🔹 Print the parsed error body
|
||||
console.error("Kubernetes Error Parsed Body:", parsedBody);
|
||||
|
||||
if (parsedBody?.message) {
|
||||
message = parsedBody.message;
|
||||
}
|
||||
} catch (parseErr) {
|
||||
console.error("Error parsing Kubernetes error body:", parseErr);
|
||||
message = err?.message || message;
|
||||
}
|
||||
|
||||
// 🔹 Also print the full error object for debugging
|
||||
console.error("Full Kubernetes Error Object:", err);
|
||||
|
||||
return res.status(statusCode).json({
|
||||
error: "Kubernetes Error",
|
||||
details: message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const verifyVolcanoSetup = async () => {
|
||||
try {
|
||||
// Verify CRD access
|
||||
|
|
|
|||
|
|
@ -16,6 +16,6 @@ COPY --from=builder /app/dist /usr/share/nginx/html
|
|||
|
||||
COPY frontend/nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
EXPOSE 80
|
||||
EXPOSE 8080
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
|
@ -16,15 +16,32 @@ spec:
|
|||
labels:
|
||||
app: volcano-dashboard
|
||||
spec:
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
level: s0:c123,c456
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
serviceAccountName: volcano-dashboard
|
||||
containers:
|
||||
- image: volcanosh/vc-dashboard-frontend:latest
|
||||
imagePullPolicy: Always
|
||||
name: frontend
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- containerPort: 8080
|
||||
name: frontend
|
||||
protocol: TCP
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
volumeMounts:
|
||||
- mountPath: /var/cache/nginx
|
||||
name: nginx-cache
|
||||
- mountPath: /run
|
||||
name: nginx-run
|
||||
- image: volcanosh/vc-dashboard-backend:latest
|
||||
imagePullPolicy: Always
|
||||
name: backend
|
||||
|
|
@ -32,6 +49,18 @@ spec:
|
|||
- containerPort: 3001
|
||||
name: backend
|
||||
protocol: TCP
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
volumes:
|
||||
- name: nginx-cache
|
||||
emptyDir: {}
|
||||
- name: nginx-run
|
||||
emptyDir: {}
|
||||
---
|
||||
|
||||
# volcano dashboard serviceAccount
|
||||
|
|
@ -98,6 +127,7 @@ rules:
|
|||
- get
|
||||
- list
|
||||
- watch
|
||||
- delete
|
||||
---
|
||||
|
||||
# volcano dashboard service
|
||||
|
|
@ -115,6 +145,6 @@ spec:
|
|||
- name: frontend
|
||||
port: 80
|
||||
protocol: TCP
|
||||
targetPort: 80
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: volcano-dashboard
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ events {}
|
|||
http {
|
||||
include mime.types;
|
||||
server {
|
||||
listen 80;
|
||||
listen 8080;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
import React from "react";
|
||||
import { Button, Menu, MenuItem } from "@mui/material";
|
||||
import { FilterList } from "@mui/icons-material";
|
||||
|
||||
const JobFilters = ({
|
||||
filterType,
|
||||
currentValue,
|
||||
options,
|
||||
handleFilterClick,
|
||||
handleFilterClose,
|
||||
anchorEl,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
size="small"
|
||||
startIcon={<FilterList />}
|
||||
onClick={(e) => handleFilterClick(filterType, e)}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
padding: 0,
|
||||
minWidth: "auto",
|
||||
}}
|
||||
>
|
||||
Filter: {currentValue}
|
||||
</Button>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={() => handleFilterClose(filterType, currentValue)}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<MenuItem
|
||||
key={option}
|
||||
onClick={() => handleFilterClose(filterType, option)}
|
||||
selected={option === currentValue}
|
||||
>
|
||||
{option}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default JobFilters;
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import React from "react";
|
||||
import { Box, IconButton, InputAdornment, TextField } from "@mui/material";
|
||||
import { Clear, Search } from "@mui/icons-material";
|
||||
|
||||
const JobSearchBar = ({
|
||||
searchText,
|
||||
setSearchText,
|
||||
handleSearch,
|
||||
handleClearSearch,
|
||||
fetchJobs,
|
||||
}) => {
|
||||
return (
|
||||
<Box sx={{ display: "flex", gap: 1, alignItems: "center" }}>
|
||||
<TextField
|
||||
placeholder="Search jobs"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
value={searchText}
|
||||
onChange={handleSearch}
|
||||
sx={{ width: 200 }}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => fetchJobs()}
|
||||
sx={{ padding: "4px" }}
|
||||
>
|
||||
<Search />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
endAdornment: searchText && (
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={handleClearSearch}
|
||||
sx={{ padding: "4px" }}
|
||||
>
|
||||
<Clear />
|
||||
</IconButton>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default JobSearchBar;
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
import React from "react";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Paper,
|
||||
Typography,
|
||||
Button,
|
||||
} from "@mui/material";
|
||||
import { ArrowDownward, ArrowUpward, UnfoldMore } from "@mui/icons-material";
|
||||
import JobStatusChip from "./JobStatusChip";
|
||||
import JobFilters from "./JobFilters";
|
||||
|
||||
const JobTable = ({
|
||||
jobs,
|
||||
handleJobClick,
|
||||
filters,
|
||||
uniqueStatuses,
|
||||
allNamespaces,
|
||||
allQueues,
|
||||
anchorEl,
|
||||
handleFilterClick,
|
||||
handleFilterClose,
|
||||
sortDirection,
|
||||
toggleSortDirection,
|
||||
}) => {
|
||||
return (
|
||||
<TableContainer
|
||||
component={Paper}
|
||||
sx={{ maxHeight: "calc(100vh - 200px)", overflow: "auto" }}
|
||||
>
|
||||
<Table stickyHeader>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
padding: "8px 16px",
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Name</Typography>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
padding: "8px 16px",
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Namespace</Typography>
|
||||
<JobFilters
|
||||
filterType="namespace"
|
||||
currentValue={filters.namespace}
|
||||
options={allNamespaces}
|
||||
handleFilterClick={handleFilterClick}
|
||||
handleFilterClose={handleFilterClose}
|
||||
anchorEl={anchorEl.namespace}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
padding: "8px 16px",
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Queue</Typography>
|
||||
<JobFilters
|
||||
filterType="queue"
|
||||
currentValue={filters.queue}
|
||||
options={allQueues}
|
||||
handleFilterClick={handleFilterClick}
|
||||
handleFilterClose={handleFilterClose}
|
||||
anchorEl={anchorEl.queue}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
padding: "8px 16px",
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Creation Time</Typography>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={toggleSortDirection}
|
||||
startIcon={
|
||||
sortDirection === "desc" ? (
|
||||
<ArrowDownward />
|
||||
) : sortDirection === "asc" ? (
|
||||
<ArrowUpward />
|
||||
) : (
|
||||
<UnfoldMore />
|
||||
)
|
||||
}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
padding: 0,
|
||||
minWidth: "auto",
|
||||
}}
|
||||
>
|
||||
Sort
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
padding: "8px 16px",
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Status</Typography>
|
||||
<JobFilters
|
||||
filterType="status"
|
||||
currentValue={filters.status}
|
||||
options={uniqueStatuses}
|
||||
handleFilterClick={handleFilterClick}
|
||||
handleFilterClose={handleFilterClose}
|
||||
anchorEl={anchorEl.status}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{jobs.map((job) => (
|
||||
<TableRow
|
||||
key={`${job.metadata.namespace}-${job.metadata.name}`}
|
||||
sx={{
|
||||
"&:nth-of-type(odd)": {
|
||||
bgcolor: "action.hover",
|
||||
},
|
||||
"&:hover": {
|
||||
bgcolor: "action.hover",
|
||||
color: "primary.main",
|
||||
boxShadow: "0px 4px 6px rgba(0, 0, 0, 0.1)",
|
||||
},
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => handleJobClick(job)}
|
||||
>
|
||||
<TableCell>{job.metadata.name}</TableCell>
|
||||
<TableCell>{job.metadata.namespace}</TableCell>
|
||||
<TableCell>{job.spec.queue || "N/A"}</TableCell>
|
||||
<TableCell>
|
||||
{new Date(
|
||||
job.metadata.creationTimestamp,
|
||||
).toLocaleString()}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<JobStatusChip
|
||||
status={
|
||||
job.status
|
||||
? job.status.state.phase
|
||||
: "Unknown"
|
||||
}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default JobTable;
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
ToggleButton,
|
||||
ToggleButtonGroup,
|
||||
} from "@mui/material";
|
||||
import Editor from "@monaco-editor/react";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
const JobEditDialog = ({ open, job, onClose, onSave }) => {
|
||||
const [editorValue, setEditorValue] = useState("");
|
||||
const [editMode, setEditMode] = useState("yaml");
|
||||
|
||||
useEffect(() => {
|
||||
if (open && job) {
|
||||
const initialContent = yaml.dump(job); // Always keep YAML content
|
||||
setEditorValue(initialContent);
|
||||
}
|
||||
}, [open, job]);
|
||||
|
||||
const handleModeChange = (event, newMode) => {
|
||||
if (newMode !== null) {
|
||||
setEditMode(newMode); // Only change syntax highlighting
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
try {
|
||||
const updatedJob = yaml.load(editorValue); // Always parse as YAML
|
||||
onSave(updatedJob);
|
||||
onClose();
|
||||
} catch (err) {
|
||||
console.error("Parsing error:", err);
|
||||
alert("Invalid YAML format. Please check your input.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
|
||||
<DialogTitle
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
Edit Job
|
||||
<ToggleButtonGroup
|
||||
value={editMode}
|
||||
exclusive
|
||||
onChange={handleModeChange}
|
||||
color="primary"
|
||||
>
|
||||
<ToggleButton value="yaml">YAML</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ height: "500px" }}>
|
||||
<Editor
|
||||
height="100%"
|
||||
language={editMode} // Just controls syntax highlight
|
||||
value={editorValue}
|
||||
onChange={(val) => setEditorValue(val || "")}
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
automaticLayout: true,
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose} color="primary" variant="contained">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default JobEditDialog;
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import React from "react";
|
||||
import { Menu, MenuItem } from "@mui/material";
|
||||
|
||||
const JobFilters = ({
|
||||
filterType,
|
||||
currentValue,
|
||||
options,
|
||||
handleFilterClick,
|
||||
handleFilterClose,
|
||||
anchorEl,
|
||||
}) => {
|
||||
return (
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleFilterClose}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<MenuItem
|
||||
key={option}
|
||||
onClick={() => handleFilterClick(filterType, option)}
|
||||
>
|
||||
{option}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export default JobFilters;
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import React from "react";
|
||||
import {
|
||||
TableContainer,
|
||||
Table,
|
||||
TableBody,
|
||||
Paper,
|
||||
useTheme,
|
||||
alpha,
|
||||
} from "@mui/material";
|
||||
import JobTableHeader from "./JobTableHeader";
|
||||
import JobTableRow from "./JobTableRow";
|
||||
|
||||
const JobTable = ({
|
||||
jobs,
|
||||
handleJobClick,
|
||||
filters,
|
||||
uniqueStatuses,
|
||||
allNamespaces,
|
||||
allQueues,
|
||||
anchorEl,
|
||||
handleFilterClick,
|
||||
handleFilterClose,
|
||||
sortDirection,
|
||||
toggleSortDirection,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<TableContainer
|
||||
component={Paper}
|
||||
sx={{
|
||||
maxHeight: "calc(100vh - 200px)",
|
||||
overflow: "auto",
|
||||
borderRadius: "16px",
|
||||
boxShadow: "0 10px 30px rgba(0, 0, 0, 0.08)",
|
||||
background: `linear-gradient(to bottom, ${alpha(theme.palette.background.paper, 0.9)}, ${theme.palette.background.paper})`,
|
||||
backdropFilter: "blur(10px)",
|
||||
border: `1px solid ${alpha(theme.palette.divider, 0.1)}`,
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "10px",
|
||||
height: "10px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: alpha(theme.palette.primary.main, 0.2),
|
||||
borderRadius: "5px",
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(theme.palette.primary.main, 0.3),
|
||||
},
|
||||
},
|
||||
"&::-webkit-scrollbar-track": {
|
||||
backgroundColor: alpha(theme.palette.primary.main, 0.05),
|
||||
borderRadius: "5px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Table stickyHeader>
|
||||
<JobTableHeader
|
||||
filters={filters}
|
||||
uniqueStatuses={uniqueStatuses}
|
||||
allNamespaces={allNamespaces}
|
||||
allQueues={allQueues}
|
||||
anchorEl={anchorEl}
|
||||
handleFilterClick={handleFilterClick}
|
||||
handleFilterClose={handleFilterClose}
|
||||
sortDirection={sortDirection}
|
||||
toggleSortDirection={toggleSortDirection}
|
||||
/>
|
||||
<TableBody>
|
||||
{jobs.map((job) => (
|
||||
<JobTableRow
|
||||
key={`${job.metadata.namespace}-${job.metadata.name}`}
|
||||
job={job}
|
||||
handleJobClick={handleJobClick}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default JobTable;
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
import React from "react";
|
||||
import {
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
Typography,
|
||||
Button,
|
||||
Box,
|
||||
useTheme,
|
||||
alpha,
|
||||
} from "@mui/material";
|
||||
import { ArrowDownward, ArrowUpward, UnfoldMore } from "@mui/icons-material";
|
||||
import JobFilters from "./JobFilters";
|
||||
|
||||
const JobTableHeader = ({
|
||||
filters,
|
||||
uniqueStatuses,
|
||||
allNamespaces,
|
||||
allQueues,
|
||||
anchorEl,
|
||||
handleFilterClick,
|
||||
handleFilterClose,
|
||||
sortDirection,
|
||||
toggleSortDirection,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: alpha(
|
||||
theme.palette.background.paper,
|
||||
0.8,
|
||||
),
|
||||
backdropFilter: "blur(8px)",
|
||||
padding: "16px 24px",
|
||||
minWidth: 140,
|
||||
borderBottom: `2px solid ${alpha(theme.palette.primary.main, 0.2)}`,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
fontWeight="700"
|
||||
color="text.primary"
|
||||
>
|
||||
Name
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
{["Namespace", "Queue"].map((field) => (
|
||||
<TableCell
|
||||
key={field}
|
||||
sx={{
|
||||
backgroundColor: alpha(
|
||||
theme.palette.background.paper,
|
||||
0.8,
|
||||
),
|
||||
backdropFilter: "blur(8px)",
|
||||
padding: "16px 24px",
|
||||
borderBottom: `2px solid ${alpha(theme.palette.primary.main, 0.2)}`,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
fontWeight="700"
|
||||
color="text.primary"
|
||||
>
|
||||
{field}
|
||||
</Typography>
|
||||
<JobFilters
|
||||
filterType={field.toLowerCase()}
|
||||
currentValue={filters[field.toLowerCase()]}
|
||||
options={
|
||||
field === "Namespace"
|
||||
? allNamespaces
|
||||
: allQueues
|
||||
}
|
||||
handleFilterClick={handleFilterClick}
|
||||
handleFilterClose={handleFilterClose}
|
||||
anchorEl={anchorEl[field.toLowerCase()]}
|
||||
/>
|
||||
</Box>
|
||||
</TableCell>
|
||||
))}
|
||||
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: alpha(
|
||||
theme.palette.background.paper,
|
||||
0.8,
|
||||
),
|
||||
backdropFilter: "blur(8px)",
|
||||
padding: "16px 24px",
|
||||
minWidth: 140,
|
||||
borderBottom: `2px solid ${alpha(theme.palette.primary.main, 0.2)}`,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
fontWeight="700"
|
||||
color="text.primary"
|
||||
>
|
||||
Creation Time
|
||||
</Typography>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={toggleSortDirection}
|
||||
startIcon={
|
||||
sortDirection === "desc" ? (
|
||||
<ArrowDownward fontSize="small" />
|
||||
) : sortDirection === "asc" ? (
|
||||
<ArrowUpward fontSize="small" />
|
||||
) : (
|
||||
<UnfoldMore fontSize="small" />
|
||||
)
|
||||
}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
padding: "4px 12px",
|
||||
minWidth: "auto",
|
||||
borderRadius: "20px",
|
||||
marginTop: "8px",
|
||||
fontSize: "0.8rem",
|
||||
fontWeight: 500,
|
||||
letterSpacing: "0.02em",
|
||||
backgroundColor: alpha(
|
||||
theme.palette.primary.main,
|
||||
0.1,
|
||||
),
|
||||
color: theme.palette.primary.main,
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(
|
||||
theme.palette.primary.main,
|
||||
0.15,
|
||||
),
|
||||
transform: "translateY(-2px)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Sort
|
||||
</Button>
|
||||
</TableCell>
|
||||
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: alpha(
|
||||
theme.palette.background.paper,
|
||||
0.8,
|
||||
),
|
||||
backdropFilter: "blur(8px)",
|
||||
padding: "16px 24px",
|
||||
minWidth: 140,
|
||||
borderBottom: `2px solid ${alpha(theme.palette.primary.main, 0.2)}`,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
fontWeight="700"
|
||||
color="text.primary"
|
||||
>
|
||||
Status
|
||||
</Typography>
|
||||
<JobFilters
|
||||
filterType="status"
|
||||
currentValue={filters.status}
|
||||
options={uniqueStatuses}
|
||||
handleFilterClick={handleFilterClick}
|
||||
handleFilterClose={handleFilterClose}
|
||||
anchorEl={anchorEl.status}
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
{/* New Actions Column */}
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: alpha(
|
||||
theme.palette.background.paper,
|
||||
0.8,
|
||||
),
|
||||
backdropFilter: "blur(8px)",
|
||||
padding: "16px 24px",
|
||||
minWidth: 140,
|
||||
borderBottom: `2px solid ${alpha(theme.palette.primary.main, 0.2)}`,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
fontWeight="700"
|
||||
color="text.primary"
|
||||
sx={{ letterSpacing: "0.02em" }}
|
||||
>
|
||||
Actions
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
);
|
||||
};
|
||||
|
||||
export default JobTableHeader;
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
import React, { useState } from "react";
|
||||
import {
|
||||
TableRow,
|
||||
TableCell,
|
||||
Box,
|
||||
IconButton,
|
||||
useTheme,
|
||||
alpha,
|
||||
} from "@mui/material";
|
||||
import { Edit, Delete } from "@mui/icons-material";
|
||||
import JobStatusChip from "../JobStatusChip";
|
||||
import JobEditDialog from "./JobEditDialog"; // Create or import this component
|
||||
|
||||
const JobTableRow = ({
|
||||
job,
|
||||
handleJobClick,
|
||||
handleOpenDeleteDialog,
|
||||
onJobUpdate, // Function to update job after edit
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
|
||||
|
||||
const handleOpenEditDialog = (e) => {
|
||||
e.stopPropagation();
|
||||
setIsEditDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleCloseEditDialog = () => {
|
||||
setIsEditDialogOpen(false);
|
||||
};
|
||||
|
||||
const handleSaveJob = (updatedJob) => {
|
||||
onJobUpdate(updatedJob);
|
||||
handleCloseEditDialog();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableRow
|
||||
hover
|
||||
onClick={() => handleJobClick(job)}
|
||||
sx={{
|
||||
height: "60px",
|
||||
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
"&:hover": {
|
||||
bgcolor: alpha(theme.palette.primary.main, 0.08),
|
||||
"& .MuiTableCell-root": {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.08)",
|
||||
transform: "translateY(-2px)",
|
||||
},
|
||||
cursor: "pointer",
|
||||
"&:last-child td, &:last-child th": {
|
||||
borderBottom: 0,
|
||||
},
|
||||
"& td": {
|
||||
borderBottom: `1px solid ${alpha(theme.palette.divider, 0.1)}`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TableCell
|
||||
sx={{
|
||||
padding: "16px 24px",
|
||||
fontWeight: 600,
|
||||
color: theme.palette.text.primary,
|
||||
letterSpacing: "0.01em",
|
||||
}}
|
||||
>
|
||||
{job.metadata.name}
|
||||
</TableCell>
|
||||
|
||||
<TableCell
|
||||
sx={{
|
||||
padding: "16px 24px",
|
||||
fontWeight: 500,
|
||||
fontSize: "0.95rem",
|
||||
}}
|
||||
>
|
||||
{job.metadata.namespace}
|
||||
</TableCell>
|
||||
|
||||
<TableCell
|
||||
sx={{
|
||||
padding: "16px 24px",
|
||||
fontWeight: 500,
|
||||
fontSize: "0.95rem",
|
||||
}}
|
||||
>
|
||||
{job.spec.queue || "N/A"}
|
||||
</TableCell>
|
||||
|
||||
<TableCell
|
||||
sx={{
|
||||
padding: "16px 24px",
|
||||
fontSize: "0.9rem",
|
||||
color: alpha(theme.palette.text.primary, 0.85),
|
||||
}}
|
||||
>
|
||||
{new Date(job.metadata.creationTimestamp).toLocaleString()}
|
||||
</TableCell>
|
||||
|
||||
<TableCell sx={{ padding: "16px 24px" }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "inline-block",
|
||||
transition: "all 0.3s ease",
|
||||
"&:hover": {
|
||||
transform: "translateY(-2px)",
|
||||
filter: "brightness(1.05)",
|
||||
},
|
||||
boxShadow: "0 3px 6px rgba(0, 0, 0, 0.15)",
|
||||
borderRadius: "15px",
|
||||
}}
|
||||
>
|
||||
<JobStatusChip
|
||||
status={
|
||||
job.status ? job.status.state.phase : "Unknown"
|
||||
}
|
||||
sx={{
|
||||
height: "30px",
|
||||
fontWeight: 600,
|
||||
fontSize: "0.8rem",
|
||||
padding: "0 12px",
|
||||
color: "common.white",
|
||||
borderRadius: "15px",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</TableCell>
|
||||
|
||||
<TableCell sx={{ padding: "16px 24px" }}>
|
||||
<Box display="flex" alignItems="center" gap={2}>
|
||||
<IconButton
|
||||
onClick={handleOpenEditDialog}
|
||||
size="small"
|
||||
sx={{
|
||||
color: theme.palette.primary.main,
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(
|
||||
theme.palette.primary.main,
|
||||
0.1,
|
||||
),
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Edit fontSize="small" />
|
||||
</IconButton>
|
||||
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleOpenDeleteDialog(job.metadata.name);
|
||||
}}
|
||||
size="small"
|
||||
sx={{
|
||||
color: theme.palette.error.main,
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(
|
||||
theme.palette.error.main,
|
||||
0.1,
|
||||
),
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Delete fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
{/* Edit Dialog */}
|
||||
<JobEditDialog
|
||||
open={isEditDialogOpen}
|
||||
job={job}
|
||||
onClose={handleCloseEditDialog}
|
||||
onSave={handleSaveJob}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default JobTableRow;
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
import React from "react";
|
||||
import { TableCell, Typography, Button, useTheme } from "@mui/material";
|
||||
import { ArrowDownward, ArrowUpward, UnfoldMore } from "@mui/icons-material";
|
||||
|
||||
const JobTableHeader = ({ sortDirection, setSortDirection }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const toggleSortDirection = () => {
|
||||
setSortDirection((prev) => (prev === "asc" ? "desc" : "asc"));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
padding: "8px 16px",
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Name</Typography>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
padding: "8px 16px",
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Namespace</Typography>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
padding: "8px 16px",
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Queue</Typography>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
padding: "8px 16px",
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Creation Time</Typography>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={toggleSortDirection}
|
||||
startIcon={
|
||||
sortDirection === "desc" ? (
|
||||
<ArrowDownward />
|
||||
) : sortDirection === "asc" ? (
|
||||
<ArrowUpward />
|
||||
) : (
|
||||
<UnfoldMore />
|
||||
)
|
||||
}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
padding: 0,
|
||||
minWidth: "auto",
|
||||
}}
|
||||
>
|
||||
Sort
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
padding: "8px 16px",
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Status</Typography>
|
||||
</TableCell>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default JobTableHeader;
|
||||
|
|
@ -3,7 +3,7 @@ import { Box, Button, Typography, useTheme } from "@mui/material";
|
|||
import axios from "axios";
|
||||
import TitleComponent from "../Titlecomponent";
|
||||
import { fetchAllNamespaces, fetchAllQueues } from "../utils";
|
||||
import JobTable from "./JobTable";
|
||||
import JobTable from "./JobTable/JobTable";
|
||||
import JobPagination from "./JobPagination";
|
||||
import JobDialog from "./JobDialog";
|
||||
import SearchBar from "../Searchbar";
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import axios from "axios";
|
|||
import SearchBar from "../Searchbar";
|
||||
import TitleComponent from "../Titlecomponent";
|
||||
import { fetchAllNamespaces } from "../utils";
|
||||
import PodsTable from "./PodsTable";
|
||||
import PodsTable from "./PodsTable/PodsTable";
|
||||
import PodsPagination from "./PodsPagination";
|
||||
import PodDetailsDialog from "./PodDetailsDialog";
|
||||
|
||||
|
|
@ -69,8 +69,8 @@ const Pods = () => {
|
|||
setPods(cachedPods.slice(startIndex, endIndex));
|
||||
}, [cachedPods, pagination]);
|
||||
|
||||
const handleSearch = (value) => {
|
||||
setSearchText(value);
|
||||
const handleSearch = (event) => {
|
||||
setSearchText(event.target.value);
|
||||
setPagination((prev) => ({ ...prev, page: 1 }));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,251 +0,0 @@
|
|||
import React, { useMemo, useState, useCallback } from "react";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Chip,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
ArrowDownward,
|
||||
ArrowUpward,
|
||||
FilterList,
|
||||
UnfoldMore,
|
||||
} from "@mui/icons-material";
|
||||
import { calculateAge } from "../utils";
|
||||
|
||||
const FilterMenu = ({ anchorEl, handleClose, items, filterType }) => (
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={() => handleClose(filterType, null)}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<MenuItem key={item} onClick={() => handleClose(filterType, item)}>
|
||||
{item}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
const TableHeader = ({
|
||||
filters,
|
||||
anchorEl,
|
||||
handleFilterClick,
|
||||
handleFilterClose,
|
||||
allNamespaces,
|
||||
onSortDirectionToggle,
|
||||
sortDirection,
|
||||
}) => (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{["Name", "Namespace", "Creation Time", "Status", "Age"].map(
|
||||
(header) => (
|
||||
<TableCell
|
||||
key={header}
|
||||
sx={{
|
||||
backgroundColor: "background.paper",
|
||||
padding: "8px 16px",
|
||||
minWidth: 120,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">{header}</Typography>
|
||||
{(header === "Namespace" || header === "Status") && (
|
||||
<Button
|
||||
size="small"
|
||||
startIcon={<FilterList />}
|
||||
onClick={(e) =>
|
||||
handleFilterClick(header.toLowerCase(), e)
|
||||
}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
padding: 0,
|
||||
minWidth: "auto",
|
||||
}}
|
||||
>
|
||||
Filter: {filters[header.toLowerCase()]}
|
||||
</Button>
|
||||
)}
|
||||
{header === "Creation Time" && (
|
||||
<Button
|
||||
size="small"
|
||||
onClick={onSortDirectionToggle}
|
||||
startIcon={
|
||||
sortDirection === "desc" ? (
|
||||
<ArrowDownward />
|
||||
) : sortDirection === "asc" ? (
|
||||
<ArrowUpward />
|
||||
) : (
|
||||
<UnfoldMore />
|
||||
)
|
||||
}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
padding: 0,
|
||||
minWidth: "auto",
|
||||
}}
|
||||
>
|
||||
Sort
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
),
|
||||
)}
|
||||
</TableRow>
|
||||
<FilterMenu
|
||||
anchorEl={anchorEl.namespace}
|
||||
handleClose={handleFilterClose}
|
||||
items={allNamespaces}
|
||||
filterType="namespace"
|
||||
/>
|
||||
<FilterMenu
|
||||
anchorEl={anchorEl.status}
|
||||
handleClose={handleFilterClose}
|
||||
items={["All", "Running", "Pending", "Succeeded", "Failed"]}
|
||||
filterType="status"
|
||||
/>
|
||||
</TableHead>
|
||||
);
|
||||
|
||||
const PodRow = ({ pod, getStatusColor, onPodClick }) => (
|
||||
<TableRow
|
||||
key={`${pod.metadata.namespace}-${pod.metadata.name}`}
|
||||
sx={{
|
||||
"&:nth-of-type(odd)": { bgcolor: "action.hover" },
|
||||
"&:hover": {
|
||||
bgcolor: "action.hover",
|
||||
color: "primary.main",
|
||||
boxShadow: "0px 4px 6px rgba(0, 0, 0, 0.1)",
|
||||
},
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => onPodClick(pod)}
|
||||
>
|
||||
<TableCell>{pod.metadata.name}</TableCell>
|
||||
<TableCell>{pod.metadata.namespace}</TableCell>
|
||||
<TableCell>
|
||||
{new Date(pod.metadata.creationTimestamp).toLocaleString()}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Chip
|
||||
label={pod.status ? pod.status.phase : "Unknown"}
|
||||
sx={{
|
||||
bgcolor: getStatusColor(
|
||||
pod.status ? pod.status.phase : "Unknown",
|
||||
),
|
||||
color: "common.white",
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>{calculateAge(pod.metadata.creationTimestamp)}</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
const PodsTable = ({
|
||||
pods,
|
||||
filters,
|
||||
allNamespaces,
|
||||
sortDirection,
|
||||
onSortDirectionToggle,
|
||||
onFilterChange,
|
||||
onPodClick,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [anchorEl, setAnchorEl] = useState({ status: null, namespace: null });
|
||||
|
||||
const handleFilterClick = useCallback((filterType, event) => {
|
||||
setAnchorEl((prev) => ({ ...prev, [filterType]: event.currentTarget }));
|
||||
}, []);
|
||||
|
||||
const handleFilterClose = useCallback(
|
||||
(filterType, value) => {
|
||||
onFilterChange(filterType, value);
|
||||
setAnchorEl((prev) => ({ ...prev, [filterType]: null }));
|
||||
},
|
||||
[onFilterChange],
|
||||
);
|
||||
|
||||
const filteredPods = useMemo(
|
||||
() =>
|
||||
pods.filter(
|
||||
(pod) =>
|
||||
(filters.status === "All" ||
|
||||
(pod.status && pod.status.phase === filters.status)) &&
|
||||
(!filters.queue ||
|
||||
filters.queue === "All" ||
|
||||
pod.spec.queue === filters.queue),
|
||||
),
|
||||
[pods, filters],
|
||||
);
|
||||
|
||||
const sortedPods = useMemo(
|
||||
() =>
|
||||
[...filteredPods].sort((a, b) => {
|
||||
const compareResult =
|
||||
new Date(b.metadata.creationTimestamp) -
|
||||
new Date(a.metadata.creationTimestamp);
|
||||
return sortDirection === "desc"
|
||||
? compareResult
|
||||
: -compareResult;
|
||||
}),
|
||||
[filteredPods, sortDirection],
|
||||
);
|
||||
|
||||
const getStatusColor = useCallback(
|
||||
(status) => {
|
||||
switch (status) {
|
||||
case "Failed":
|
||||
return theme.palette.error.main;
|
||||
case "Pending":
|
||||
return theme.palette.warning.main;
|
||||
case "Running":
|
||||
return theme.palette.success.main;
|
||||
case "Succeeded":
|
||||
return theme.palette.info.main;
|
||||
default:
|
||||
return theme.palette.grey[500];
|
||||
}
|
||||
},
|
||||
[theme],
|
||||
);
|
||||
|
||||
return (
|
||||
<TableContainer
|
||||
component={Paper}
|
||||
sx={{ maxHeight: "calc(100vh - 200px)", overflow: "auto" }}
|
||||
>
|
||||
<Table stickyHeader>
|
||||
<TableHeader
|
||||
filters={filters}
|
||||
anchorEl={anchorEl}
|
||||
handleFilterClick={handleFilterClick}
|
||||
handleFilterClose={handleFilterClose}
|
||||
allNamespaces={allNamespaces}
|
||||
onSortDirectionToggle={onSortDirectionToggle}
|
||||
sortDirection={sortDirection}
|
||||
/>
|
||||
<TableBody>
|
||||
{sortedPods.map((pod) => (
|
||||
<PodRow
|
||||
key={pod.metadata.name}
|
||||
pod={pod}
|
||||
getStatusColor={getStatusColor}
|
||||
onPodClick={onPodClick}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default PodsTable;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import React from "react";
|
||||
import { Menu, MenuItem } from "@mui/material";
|
||||
|
||||
const FilterMenu = ({ anchorEl, handleClose, items, filterType }) => (
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={() => handleClose(filterType, null)}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<MenuItem key={item} onClick={() => handleClose(filterType, item)}>
|
||||
{item}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
export default FilterMenu;
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import React from "react";
|
||||
import { TableRow, TableCell, Chip, useTheme, alpha } from "@mui/material";
|
||||
import { calculateAge } from "../../utils";
|
||||
|
||||
const PodRow = ({ pod, getStatusColor, onPodClick }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
onClick={(e) => onPodClick(pod)}
|
||||
sx={{
|
||||
height: "60px",
|
||||
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
"&:hover": {
|
||||
bgcolor: alpha(theme.palette.primary.main, 0.08),
|
||||
"& .MuiTableCell-root": {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.08)",
|
||||
transform: "translateY(-2px)",
|
||||
},
|
||||
cursor: "pointer",
|
||||
"&:last-child td, &:last-child th": {
|
||||
borderBottom: 0,
|
||||
},
|
||||
"& td": {
|
||||
borderBottom: `1px solid ${alpha(theme.palette.divider, 0.1)}`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TableCell
|
||||
sx={{
|
||||
padding: "16px 24px",
|
||||
fontWeight: 600,
|
||||
color: theme.palette.text.primary,
|
||||
letterSpacing: "0.01em",
|
||||
}}
|
||||
>
|
||||
{pod.metadata.name}
|
||||
</TableCell>
|
||||
|
||||
<TableCell
|
||||
sx={{
|
||||
padding: "16px 24px",
|
||||
fontSize: "0.95rem",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{pod.metadata.namespace}
|
||||
</TableCell>
|
||||
|
||||
<TableCell
|
||||
sx={{
|
||||
padding: "16px 24px",
|
||||
fontSize: "0.9rem",
|
||||
color: alpha(theme.palette.text.primary, 0.85),
|
||||
}}
|
||||
>
|
||||
{new Date(pod.metadata.creationTimestamp).toLocaleString()}
|
||||
</TableCell>
|
||||
|
||||
<TableCell sx={{ padding: "16px 24px" }}>
|
||||
<Chip
|
||||
label={pod.status?.phase || "Unknown"}
|
||||
sx={{
|
||||
bgcolor: getStatusColor(pod.status?.phase || "Unknown"),
|
||||
color: "common.white",
|
||||
height: "30px",
|
||||
fontWeight: 600,
|
||||
fontSize: "0.8rem",
|
||||
letterSpacing: "0.02em",
|
||||
borderRadius: "15px",
|
||||
boxShadow: "0 3px 6px rgba(0, 0, 0, 0.15)",
|
||||
padding: "0 12px",
|
||||
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
"&:hover": {
|
||||
transform: "translateY(-2px)",
|
||||
boxShadow: "0 5px 10px rgba(0, 0, 0, 0.2)",
|
||||
filter: "brightness(1.05)",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
<TableCell
|
||||
sx={{
|
||||
padding: "16px 24px",
|
||||
fontSize: "0.95rem",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{calculateAge(pod.metadata.creationTimestamp)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
|
||||
export default PodRow;
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
import React from "react";
|
||||
import {
|
||||
TableContainer,
|
||||
Table,
|
||||
TableBody,
|
||||
Paper,
|
||||
useTheme,
|
||||
alpha,
|
||||
} from "@mui/material";
|
||||
import TableHeader from "./TableHeader";
|
||||
import PodRow from "./PodRow";
|
||||
|
||||
const PodsTable = ({
|
||||
pods,
|
||||
filters,
|
||||
allNamespaces,
|
||||
sortDirection,
|
||||
onSortDirectionToggle,
|
||||
onFilterChange,
|
||||
onPodClick,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [anchorEl, setAnchorEl] = React.useState({
|
||||
status: null,
|
||||
namespace: null,
|
||||
});
|
||||
|
||||
const handleFilterClick = React.useCallback((filterType, event) => {
|
||||
setAnchorEl((prev) => ({ ...prev, [filterType]: event.currentTarget }));
|
||||
}, []);
|
||||
|
||||
const handleFilterClose = React.useCallback(
|
||||
(filterType, value) => {
|
||||
onFilterChange(filterType, value);
|
||||
setAnchorEl((prev) => ({ ...prev, [filterType]: null }));
|
||||
},
|
||||
[onFilterChange],
|
||||
);
|
||||
|
||||
const filteredPods = React.useMemo(
|
||||
() =>
|
||||
pods.filter(
|
||||
(pod) =>
|
||||
(filters.status === "All" ||
|
||||
(pod.status && pod.status.phase === filters.status)) &&
|
||||
(!filters.queue ||
|
||||
filters.queue === "All" ||
|
||||
pod.spec.queue === filters.queue),
|
||||
),
|
||||
[pods, filters],
|
||||
);
|
||||
|
||||
const sortedPods = React.useMemo(
|
||||
() =>
|
||||
[...filteredPods].sort((a, b) => {
|
||||
const compareResult =
|
||||
new Date(b.metadata.creationTimestamp) -
|
||||
new Date(a.metadata.creationTimestamp);
|
||||
return sortDirection === "desc"
|
||||
? compareResult
|
||||
: -compareResult;
|
||||
}),
|
||||
[filteredPods, sortDirection],
|
||||
);
|
||||
|
||||
const getStatusColor = React.useCallback(
|
||||
(status) => {
|
||||
switch (status) {
|
||||
case "Failed":
|
||||
return theme.palette.error.main;
|
||||
case "Pending":
|
||||
return theme.palette.warning.main;
|
||||
case "Running":
|
||||
return theme.palette.success.main;
|
||||
case "Succeeded":
|
||||
return theme.palette.info.main;
|
||||
default:
|
||||
return theme.palette.grey[500];
|
||||
}
|
||||
},
|
||||
[theme],
|
||||
);
|
||||
|
||||
return (
|
||||
<TableContainer
|
||||
component={Paper}
|
||||
sx={{
|
||||
maxHeight: "calc(100vh - 200px)",
|
||||
overflow: "auto",
|
||||
borderRadius: "16px",
|
||||
boxShadow: "0 10px 30px rgba(0, 0, 0, 0.08)",
|
||||
background: `linear-gradient(to bottom, ${alpha(theme.palette.background.paper, 0.9)}, ${theme.palette.background.paper})`,
|
||||
backdropFilter: "blur(10px)",
|
||||
border: `1px solid ${alpha(theme.palette.divider, 0.1)}`,
|
||||
"&::-webkit-scrollbar": {
|
||||
width: "10px",
|
||||
height: "10px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: alpha(theme.palette.primary.main, 0.2),
|
||||
borderRadius: "5px",
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(theme.palette.primary.main, 0.3),
|
||||
},
|
||||
},
|
||||
"&::-webkit-scrollbar-track": {
|
||||
backgroundColor: alpha(theme.palette.primary.main, 0.05),
|
||||
borderRadius: "5px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Table stickyHeader>
|
||||
<TableHeader
|
||||
filters={filters}
|
||||
anchorEl={anchorEl}
|
||||
handleFilterClick={handleFilterClick}
|
||||
handleFilterClose={handleFilterClose}
|
||||
allNamespaces={allNamespaces}
|
||||
onSortDirectionToggle={onSortDirectionToggle}
|
||||
sortDirection={sortDirection}
|
||||
/>
|
||||
<TableBody>
|
||||
{sortedPods.map((pod) => (
|
||||
<PodRow
|
||||
key={`${pod.metadata.namespace}-${pod.metadata.name}`}
|
||||
pod={pod}
|
||||
getStatusColor={getStatusColor}
|
||||
onPodClick={onPodClick}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default PodsTable;
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
import React from "react";
|
||||
import {
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
Button,
|
||||
Typography,
|
||||
useTheme,
|
||||
alpha,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
ArrowDownward,
|
||||
ArrowUpward,
|
||||
FilterList,
|
||||
UnfoldMore,
|
||||
} from "@mui/icons-material";
|
||||
import FilterMenu from "./FilterMenu";
|
||||
|
||||
const TableHeader = ({
|
||||
filters,
|
||||
anchorEl,
|
||||
handleFilterClick,
|
||||
handleFilterClose,
|
||||
allNamespaces,
|
||||
onSortDirectionToggle,
|
||||
sortDirection,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{["Name", "Namespace", "Creation Time", "Status", "Age"].map(
|
||||
(header) => (
|
||||
<TableCell
|
||||
key={header}
|
||||
sx={{
|
||||
backgroundColor: alpha(
|
||||
theme.palette.background.paper,
|
||||
0.8,
|
||||
),
|
||||
backdropFilter: "blur(8px)",
|
||||
padding: "16px 24px",
|
||||
minWidth: 140,
|
||||
borderBottom: `2px solid ${alpha(
|
||||
theme.palette.primary.main,
|
||||
0.2,
|
||||
)}`,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
fontWeight="700"
|
||||
color="text.primary"
|
||||
sx={{ letterSpacing: "0.02em" }}
|
||||
>
|
||||
{header}
|
||||
</Typography>
|
||||
{(header === "Namespace" ||
|
||||
header === "Status") && (
|
||||
<Button
|
||||
size="small"
|
||||
startIcon={<FilterList fontSize="small" />}
|
||||
onClick={(e) =>
|
||||
handleFilterClick(
|
||||
header.toLowerCase(),
|
||||
e,
|
||||
)
|
||||
}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
padding: "4px 12px",
|
||||
minWidth: "auto",
|
||||
borderRadius: "20px",
|
||||
marginTop: "8px",
|
||||
fontSize: "0.8rem",
|
||||
fontWeight: 500,
|
||||
letterSpacing: "0.02em",
|
||||
transition:
|
||||
"all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
backgroundColor:
|
||||
filters[header.toLowerCase()] !==
|
||||
"All"
|
||||
? alpha(
|
||||
theme.palette.primary
|
||||
.main,
|
||||
0.2,
|
||||
)
|
||||
: alpha(
|
||||
theme.palette.primary
|
||||
.main,
|
||||
0.1,
|
||||
),
|
||||
color: theme.palette.primary.main,
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(
|
||||
theme.palette.primary.main,
|
||||
0.15,
|
||||
),
|
||||
transform: "translateY(-2px)",
|
||||
boxShadow: `0 4px 8px ${alpha(
|
||||
theme.palette.primary.main,
|
||||
0.2,
|
||||
)}`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Filter: {filters[header.toLowerCase()]}
|
||||
</Button>
|
||||
)}
|
||||
{header === "Creation Time" && (
|
||||
<Button
|
||||
size="small"
|
||||
onClick={onSortDirectionToggle}
|
||||
startIcon={
|
||||
sortDirection === "desc" ? (
|
||||
<ArrowDownward fontSize="small" />
|
||||
) : sortDirection === "asc" ? (
|
||||
<ArrowUpward fontSize="small" />
|
||||
) : (
|
||||
<UnfoldMore fontSize="small" />
|
||||
)
|
||||
}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
padding: "4px 12px",
|
||||
minWidth: "auto",
|
||||
borderRadius: "20px",
|
||||
marginTop: "8px",
|
||||
fontSize: "0.8rem",
|
||||
fontWeight: 500,
|
||||
letterSpacing: "0.02em",
|
||||
transition:
|
||||
"all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
backgroundColor: alpha(
|
||||
theme.palette.primary.main,
|
||||
0.1,
|
||||
),
|
||||
color: theme.palette.primary.main,
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(
|
||||
theme.palette.primary.main,
|
||||
0.15,
|
||||
),
|
||||
transform: "translateY(-2px)",
|
||||
boxShadow: `0 4px 8px ${alpha(
|
||||
theme.palette.primary.main,
|
||||
0.2,
|
||||
)}`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Sort
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
),
|
||||
)}
|
||||
</TableRow>
|
||||
<FilterMenu
|
||||
anchorEl={anchorEl.namespace}
|
||||
handleClose={handleFilterClose}
|
||||
items={allNamespaces}
|
||||
filterType="namespace"
|
||||
/>
|
||||
<FilterMenu
|
||||
anchorEl={anchorEl.status}
|
||||
handleClose={handleFilterClose}
|
||||
items={["All", "Running", "Pending", "Succeeded", "Failed"]}
|
||||
filterType="status"
|
||||
/>
|
||||
</TableHead>
|
||||
);
|
||||
};
|
||||
|
||||
export default TableHeader;
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
import React from "react";
|
||||
import { Box, IconButton, InputAdornment, TextField } from "@mui/material";
|
||||
import { Clear, Search } from "@mui/icons-material";
|
||||
|
||||
const SearchBar = ({ searchText, onSearch, onClear, onSearchSubmit }) => {
|
||||
const handleSearch = (event) => {
|
||||
onSearch(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ display: "flex", gap: 1, alignItems: "center" }}>
|
||||
<TextField
|
||||
placeholder="Search pods"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
value={searchText}
|
||||
onChange={handleSearch}
|
||||
sx={{ width: 200 }}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={onSearchSubmit}
|
||||
sx={{ padding: "4px" }}
|
||||
>
|
||||
<Search />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
endAdornment: searchText && (
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={onClear}
|
||||
sx={{ padding: "4px" }}
|
||||
>
|
||||
<Clear />
|
||||
</IconButton>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchBar;
|
||||
|
|
@ -15,57 +15,27 @@ const EditQueueDialog = ({ open, queue, onClose, onSave }) => {
|
|||
const [editorValue, setEditorValue] = useState("");
|
||||
const [editMode, setEditMode] = useState("yaml");
|
||||
|
||||
const convertContent = (content, fromMode, toMode) => {
|
||||
try {
|
||||
if (fromMode === "yaml" && toMode === "json") {
|
||||
const parsedContent = yaml.load(content);
|
||||
return JSON.stringify(parsedContent, null, 2);
|
||||
} else if (fromMode === "json" && toMode === "yaml") {
|
||||
const parsedContent = JSON.parse(content);
|
||||
return yaml.dump(parsedContent);
|
||||
}
|
||||
return content;
|
||||
} catch (error) {
|
||||
console.error("Conversion error:", error);
|
||||
alert("Error converting content. Please check your input.");
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open && queue) {
|
||||
const content =
|
||||
editMode === "yaml"
|
||||
? yaml.dump(queue)
|
||||
: JSON.stringify(queue, null, 2);
|
||||
const content = yaml.dump(queue); // Always YAML
|
||||
setEditorValue(content);
|
||||
}
|
||||
}, [open, queue, editMode]);
|
||||
}, [open, queue]);
|
||||
|
||||
const handleModeChange = (event, newMode) => {
|
||||
if (newMode !== null) {
|
||||
const convertedContent = convertContent(
|
||||
editorValue,
|
||||
editMode,
|
||||
newMode,
|
||||
);
|
||||
setEditMode(newMode);
|
||||
setEditorValue(convertedContent);
|
||||
setEditMode(newMode); // Only for syntax highlighting
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
try {
|
||||
const updatedQueue =
|
||||
editMode === "yaml"
|
||||
? yaml.load(editorValue)
|
||||
: JSON.parse(editorValue);
|
||||
|
||||
const updatedQueue = yaml.load(editorValue); // Always parse as YAML
|
||||
onSave(updatedQueue);
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error("Error parsing edited content:", error);
|
||||
alert("Invalid YAML/JSON format. Please check your input.");
|
||||
alert("Invalid YAML format. Please check your input.");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -86,13 +56,12 @@ const EditQueueDialog = ({ open, queue, onClose, onSave }) => {
|
|||
color="primary"
|
||||
>
|
||||
<ToggleButton value="yaml">YAML</ToggleButton>
|
||||
<ToggleButton value="json">JSON</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ height: "500px" }}>
|
||||
<Editor
|
||||
height="100%"
|
||||
language={editMode}
|
||||
language={editMode} // only affects syntax highlighting
|
||||
value={editorValue}
|
||||
onChange={(value) => setEditorValue(value || "")}
|
||||
options={{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
TableContainer,
|
||||
Table,
|
||||
TableBody,
|
||||
Paper,
|
||||
TableRow,
|
||||
TableCell,
|
||||
useTheme,
|
||||
alpha,
|
||||
} from "@mui/material";
|
||||
|
|
@ -23,12 +25,20 @@ const QueueTable = ({
|
|||
uniqueStates,
|
||||
handleFilterClose,
|
||||
setAnchorEl,
|
||||
handleDelete,
|
||||
onQueueUpdate,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const [queues, setQueues] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
setQueues(sortedQueues);
|
||||
}, [sortedQueues]);
|
||||
|
||||
const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
|
||||
const [queueToDelete, setQueueToDelete] = useState(null);
|
||||
|
||||
const [deleteError, setDeleteError] = useState(null);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const handleOpenDeleteDialog = (queueName) => {
|
||||
setQueueToDelete(queueName);
|
||||
setOpenDeleteDialog(true);
|
||||
|
|
@ -37,13 +47,77 @@ const QueueTable = ({
|
|||
const handleCloseDeleteDialog = () => {
|
||||
setOpenDeleteDialog(false);
|
||||
setQueueToDelete(null);
|
||||
setDeleteError(null);
|
||||
setIsDeleting(false);
|
||||
};
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (handleDelete && queueToDelete) {
|
||||
handleDelete(queueToDelete);
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
setIsDeleting(true);
|
||||
|
||||
const response = await fetch(
|
||||
`/api/queues/${encodeURIComponent(queueToDelete)}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
let data = {};
|
||||
const contentType = response.headers.get("content-type");
|
||||
const text = await response.text();
|
||||
|
||||
let isJsonResponse = false;
|
||||
try {
|
||||
if (
|
||||
(contentType && contentType.includes("application/json")) ||
|
||||
(text && !text.trim().startsWith("<"))
|
||||
) {
|
||||
data = text ? JSON.parse(text) : {};
|
||||
isJsonResponse = true;
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.warn("Failed to parse response as JSON:", parseError);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
let customMessage = `queues.scheduling.volcano.sh "${queueToDelete}" is forbidden.`;
|
||||
let errorType = "UnknownError";
|
||||
|
||||
if (
|
||||
isJsonResponse &&
|
||||
typeof data === "object" &&
|
||||
(data.message || data.details)
|
||||
) {
|
||||
customMessage = data.message || data.details;
|
||||
if (customMessage.toLowerCase().includes("denied")) {
|
||||
errorType = "ValidationError";
|
||||
} else {
|
||||
errorType = "KubernetesError";
|
||||
}
|
||||
}
|
||||
|
||||
const fullMessage = `Cannot delete "${queueToDelete}". Error message: ${customMessage}`;
|
||||
const error = new Error(fullMessage);
|
||||
error.type = errorType;
|
||||
error.status = response.status;
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Success
|
||||
setQueues((prev) =>
|
||||
prev.filter((queue) => queue.metadata.name !== queueToDelete),
|
||||
);
|
||||
handleCloseDeleteDialog();
|
||||
} catch (error) {
|
||||
console.error("Error deleting queue:", error);
|
||||
setDeleteError(error.message || "An unexpected error occurred.");
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -94,23 +168,39 @@ const QueueTable = ({
|
|||
setAnchorEl={setAnchorEl}
|
||||
/>
|
||||
<TableBody>
|
||||
{sortedQueues.map((queue) => (
|
||||
{queues.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={allocatedFields.length + 2}
|
||||
align="center"
|
||||
>
|
||||
No queues found.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
queues.map((queue) => (
|
||||
<QueueTableRow
|
||||
key={queue.metadata.name}
|
||||
queue={queue}
|
||||
allocatedFields={allocatedFields}
|
||||
handleQueueClick={handleQueueClick}
|
||||
handleOpenDeleteDialog={handleOpenDeleteDialog}
|
||||
handleOpenDeleteDialog={
|
||||
handleOpenDeleteDialog
|
||||
}
|
||||
onQueueUpdate={onQueueUpdate}
|
||||
/>
|
||||
))}
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<QueueTableDeleteDialog
|
||||
open={openDeleteDialog}
|
||||
onClose={handleCloseDeleteDialog}
|
||||
onConfirm={confirmDelete}
|
||||
onConfirm={handleDelete}
|
||||
queueToDelete={queueToDelete}
|
||||
error={deleteError}
|
||||
isDeleting={isDeleting}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,110 +3,60 @@ import {
|
|||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogActions,
|
||||
IconButton,
|
||||
Button,
|
||||
useTheme,
|
||||
alpha,
|
||||
Alert,
|
||||
} from "@mui/material";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
|
||||
const QueueTableDeleteDialog = ({
|
||||
open,
|
||||
onClose,
|
||||
onConfirm,
|
||||
queueToDelete,
|
||||
error,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const showOnlyError = Boolean(error);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
borderRadius: "16px",
|
||||
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.15)",
|
||||
border: `1px solid ${alpha(theme.palette.divider, 0.1)}`,
|
||||
backgroundImage: `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.95)}, ${theme.palette.background.paper})`,
|
||||
backdropFilter: "blur(10px)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Dialog open={open} onClose={onClose}>
|
||||
<DialogTitle
|
||||
sx={{
|
||||
fontWeight: 700,
|
||||
color: theme.palette.error.main,
|
||||
fontSize: "1.2rem",
|
||||
textAlign: "center",
|
||||
borderBottom: `1px solid ${alpha(theme.palette.divider, 0.1)}`,
|
||||
padding: "24px 32px",
|
||||
}}
|
||||
>
|
||||
Confirm Deletion
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ padding: "24px 32px" }}>
|
||||
<DialogContentText
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
marginBottom: "24px",
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: "1rem",
|
||||
}}
|
||||
>
|
||||
Are you sure you want to delete queue "{queueToDelete}"?
|
||||
This action cannot be undone.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
padding: "0 32px 24px",
|
||||
borderTop: `1px solid ${alpha(theme.palette.divider, 0.1)}`,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={onClose}
|
||||
variant="outlined"
|
||||
color="error"
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
fontSize: "0.9rem",
|
||||
fontWeight: 500,
|
||||
padding: "8px 24px",
|
||||
borderRadius: "8px",
|
||||
border: `1px solid ${theme.palette.error.main}`,
|
||||
color: theme.palette.error.main,
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(
|
||||
theme.palette.error.main,
|
||||
0.08,
|
||||
),
|
||||
borderColor: theme.palette.error.dark,
|
||||
},
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
{showOnlyError ? "Error" : "Delete Queue"}
|
||||
<IconButton onClick={onClose} size="small">
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
{showOnlyError ? (
|
||||
<Alert severity="error">{error}</Alert>
|
||||
) : (
|
||||
`Are you sure you want to delete queue "${queueToDelete}"? This action cannot be undone.`
|
||||
)}
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
{!showOnlyError && (
|
||||
<Button onClick={onClose} color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
{!showOnlyError && (
|
||||
<Button
|
||||
onClick={onConfirm}
|
||||
variant="contained"
|
||||
color="error"
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
fontSize: "0.9rem",
|
||||
fontWeight: 600,
|
||||
padding: "8px 24px",
|
||||
marginLeft: "16px",
|
||||
borderRadius: "8px",
|
||||
backgroundColor: theme.palette.error.main,
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.error.dark,
|
||||
},
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
)}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
import React from "react";
|
||||
import {
|
||||
Container,
|
||||
Row,
|
||||
Col,
|
||||
Form,
|
||||
Button,
|
||||
InputGroup,
|
||||
Card,
|
||||
} from "react-bootstrap";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faSearch,
|
||||
faTimes,
|
||||
faSyncAlt,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
const QueueToolbar = ({
|
||||
searchText,
|
||||
handleSearch,
|
||||
handleClearSearch,
|
||||
handleRefresh,
|
||||
fetchQueues,
|
||||
isRefreshing,
|
||||
}) => {
|
||||
return (
|
||||
<Card className="mb-4 border-0 bg-light shadow-sm rounded-xl">
|
||||
<Card.Body className="py-3">
|
||||
<Container fluid className="px-0">
|
||||
<Row className="align-items-center g-3">
|
||||
<Col xs={12} md={5} lg={4} xl={3}>
|
||||
<div className="position-relative">
|
||||
<InputGroup
|
||||
className="border rounded-pill overflow-hidden shadow-sm bg-white"
|
||||
style={{ height: "40px" }}
|
||||
>
|
||||
<Button
|
||||
variant="outline-white"
|
||||
className="border-0 bg-transparent text-primary position-absolute start-0 d-flex align-items-center px-3"
|
||||
onClick={() => fetchQueues()}
|
||||
disabled={isRefreshing}
|
||||
style={{ height: "100%" }}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faSearch}
|
||||
className="me-2 text-black"
|
||||
/>
|
||||
{isRefreshing && (
|
||||
<span
|
||||
className="spinner-border spinner-border-sm text-primary"
|
||||
role="status"
|
||||
></span>
|
||||
)}
|
||||
</Button>
|
||||
<Form.Control
|
||||
placeholder="Search queues..."
|
||||
value={searchText}
|
||||
onChange={handleSearch}
|
||||
className="border-0 shadow-none px-4 ps-5"
|
||||
style={{
|
||||
fontSize: "0.9rem",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
{searchText && (
|
||||
<Button
|
||||
variant="outline-white"
|
||||
className="border-0 bg-transparent position-absolute end-0 d-flex align-items-center px-3"
|
||||
onClick={handleClearSearch}
|
||||
disabled={isRefreshing}
|
||||
style={{ height: "100%" }}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faTimes}
|
||||
className="text-secondary"
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</InputGroup>
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="d-flex justify-content-md-end">
|
||||
<Button
|
||||
variant="outline-danger"
|
||||
size="lg"
|
||||
className="rounded-pill px-5 py-2 d-flex align-items-center justify-content-center shadow-sm fw-medium border-2"
|
||||
onClick={handleRefresh}
|
||||
disabled={isRefreshing}
|
||||
style={{
|
||||
transition: "all 0.3s ease",
|
||||
height: "40px",
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faSyncAlt}
|
||||
className="me-2"
|
||||
spin={isRefreshing}
|
||||
/>
|
||||
<span>
|
||||
{isRefreshing
|
||||
? "Refreshing..."
|
||||
: "Refresh Queue Status"}
|
||||
</span>
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default QueueToolbar;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import axios from "axios";
|
||||
import { parseCPU, parseMemoryToMi } from "../utils"; // Adjust this path based on your project structure
|
||||
import { parseCPU, parseMemoryToMi } from "../utils";
|
||||
import SearchBar from "../Searchbar";
|
||||
import QueueTable from "./QueueTable/QueueTable";
|
||||
import QueuePagination from "./QueuePagination";
|
||||
|
|
@ -12,20 +12,13 @@ const Queues = () => {
|
|||
const [queues, setQueues] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [filters, setFilters] = useState({
|
||||
status: "All",
|
||||
});
|
||||
const [filters, setFilters] = useState({ status: "All" });
|
||||
const [selectedQueueYaml, setSelectedQueueYaml] = useState("");
|
||||
const [openDialog, setOpenDialog] = useState(false);
|
||||
const [anchorEl, setAnchorEl] = useState({
|
||||
status: null,
|
||||
});
|
||||
const [anchorEl, setAnchorEl] = useState({ status: null });
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [selectedQueueName, setSelectedQueueName] = useState("");
|
||||
const [pagination, setPagination] = useState({
|
||||
page: 1,
|
||||
rowsPerPage: 10,
|
||||
});
|
||||
const [pagination, setPagination] = useState({ page: 1, rowsPerPage: 10 });
|
||||
const [totalQueues, setTotalQueues] = useState(0);
|
||||
const [sortConfig, setSortConfig] = useState({
|
||||
field: null,
|
||||
|
|
@ -35,7 +28,6 @@ const Queues = () => {
|
|||
const fetchQueues = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await axios.get(`/api/queues`, {
|
||||
params: {
|
||||
|
|
@ -65,16 +57,19 @@ const Queues = () => {
|
|||
fetchQueues();
|
||||
}, [fetchQueues]);
|
||||
|
||||
const handleSearch = (event) => {
|
||||
const handleSearch = useCallback((event) => {
|
||||
setSearchText(event.target.value);
|
||||
setPagination((prev) => ({ ...prev, page: 1 }));
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleClearSearch = () => {
|
||||
const handleClearSearch = useCallback(() => {
|
||||
setSearchText("");
|
||||
setPagination((prev) => ({ ...prev, page: 1 }));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchQueues();
|
||||
};
|
||||
}, [searchText, pagination.page, filters]);
|
||||
|
||||
const handleRefresh = useCallback(() => {
|
||||
setPagination((prev) => ({ ...prev, page: 1 }));
|
||||
|
|
@ -225,7 +220,7 @@ const Queues = () => {
|
|||
handleClearSearch={handleClearSearch}
|
||||
handleRefresh={handleRefresh}
|
||||
fetchData={fetchQueues}
|
||||
isRefreshing={false} // Update if needed
|
||||
isRefreshing={loading}
|
||||
placeholder="Search queues..."
|
||||
refreshLabel="Refresh Queues"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
"concurrently": "^9.1.2",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^15.4.3",
|
||||
"prettier": "^3.5.1",
|
||||
"prettier": "^3.5.3",
|
||||
"vitest": "^3.0.8"
|
||||
}
|
||||
},
|
||||
|
|
@ -118,20 +118,20 @@
|
|||
}
|
||||
},
|
||||
"frontend/node_modules/eslint": {
|
||||
"version": "9.24.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz",
|
||||
"integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==",
|
||||
"version": "9.25.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz",
|
||||
"integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.20.0",
|
||||
"@eslint/config-helpers": "^0.2.0",
|
||||
"@eslint/core": "^0.12.0",
|
||||
"@eslint/config-helpers": "^0.2.1",
|
||||
"@eslint/core": "^0.13.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.24.0",
|
||||
"@eslint/plugin-kit": "^0.2.7",
|
||||
"@eslint/js": "9.25.1",
|
||||
"@eslint/plugin-kit": "^0.2.8",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.2",
|
||||
|
|
@ -212,14 +212,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@asamuzakjp/css-color": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.2.tgz",
|
||||
"integrity": "sha512-nwgc7jPn3LpZ4JWsoHtuwBsad1qSSLDDX634DdG0PBJofIuIEtSWk4KkRmuXyu178tjuHAbwiMNNzwqIyLYxZw==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.4.tgz",
|
||||
"integrity": "sha512-SeuBV4rnjpFNjI8HSgKUwteuFdkHwkboq31HWzznuqgySQir+jSTczoWVVL4jvOjKjuH80fMDG0Fvg1Sb+OJsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@csstools/css-calc": "^2.1.2",
|
||||
"@csstools/css-color-parser": "^3.0.8",
|
||||
"@csstools/css-calc": "^2.1.3",
|
||||
"@csstools/css-color-parser": "^3.0.9",
|
||||
"@csstools/css-parser-algorithms": "^3.0.4",
|
||||
"@csstools/css-tokenizer": "^3.0.3",
|
||||
"lru-cache": "^10.4.3"
|
||||
|
|
@ -2017,9 +2017,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@csstools/css-calc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz",
|
||||
"integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==",
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.3.tgz",
|
||||
"integrity": "sha512-XBG3talrhid44BY1x3MHzUx/aTG8+x/Zi57M4aTKK9RFB4aLlF3TTSzfzn8nWVHWL3FgAXAxmupmDd6VWww+pw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2041,9 +2041,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@csstools/css-color-parser": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz",
|
||||
"integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==",
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.9.tgz",
|
||||
"integrity": "sha512-wILs5Zk7BU86UArYBJTPy/FMPPKVKHMj1ycCEyf3VUptol0JNRLFU/BZsJ4aiIHJEbSLiizzRrw8Pc1uAEDrXw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2058,7 +2058,7 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@csstools/color-helpers": "^5.0.2",
|
||||
"@csstools/css-calc": "^2.1.2"
|
||||
"@csstools/css-calc": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
|
@ -2264,9 +2264,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz",
|
||||
"integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz",
|
||||
"integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
|
|
@ -2281,9 +2281,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz",
|
||||
"integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz",
|
||||
"integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -2298,9 +2298,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2315,9 +2315,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2332,9 +2332,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2349,9 +2349,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2366,9 +2366,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2383,9 +2383,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2400,9 +2400,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz",
|
||||
"integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz",
|
||||
"integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -2417,9 +2417,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2434,9 +2434,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz",
|
||||
"integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz",
|
||||
"integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
|
|
@ -2451,9 +2451,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz",
|
||||
"integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz",
|
||||
"integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
|
|
@ -2468,9 +2468,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz",
|
||||
"integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz",
|
||||
"integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
|
|
@ -2485,9 +2485,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz",
|
||||
"integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz",
|
||||
"integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
|
|
@ -2502,9 +2502,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz",
|
||||
"integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz",
|
||||
"integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
|
|
@ -2519,9 +2519,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz",
|
||||
"integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz",
|
||||
"integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
|
|
@ -2536,9 +2536,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2553,9 +2553,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2570,9 +2570,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2587,9 +2587,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2604,9 +2604,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2621,9 +2621,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2638,9 +2638,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2655,9 +2655,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz",
|
||||
"integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz",
|
||||
"integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
|
|
@ -2672,9 +2672,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2756,9 +2756,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
|
||||
"integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
|
||||
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
|
@ -2806,9 +2806,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz",
|
||||
"integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==",
|
||||
"version": "9.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz",
|
||||
"integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
@ -2839,19 +2839,6 @@
|
|||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
|
||||
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-common-types": {
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz",
|
||||
|
|
@ -3450,6 +3437,19 @@
|
|||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
|
@ -3488,6 +3488,16 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@paralleldrive/cuid2": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz",
|
||||
"integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^1.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
|
|
@ -4375,9 +4385,9 @@
|
|||
"peer": true
|
||||
},
|
||||
"node_modules/@vitejs/plugin-react": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.0.tgz",
|
||||
"integrity": "sha512-x/EztcTKVj+TDeANY1WjNeYsvZjZdfWRMP/KXi5Yn8BoTzpa13ZltaQqKfvWYbX8CE10GOHHdC5v86jY9x8i/g==",
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz",
|
||||
"integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -4395,16 +4405,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/browser": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-3.1.1.tgz",
|
||||
"integrity": "sha512-A+A69mMtrj1RPh96LfXGc309KSXhy2MslvyL+cp9+Y5EVdoJD4KfXDx/3SSlRGN70+hIoJ3RRbTidTvj18PZ/A==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-3.1.2.tgz",
|
||||
"integrity": "sha512-dwL6hQg3NSDP3Z4xzIZL0xHq/AkQAPQ4StFpWVlY2zbRJtK3Y2YqdFZ7YmZjszTETN1BDQZRn/QOrcP+c8ATgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@vitest/mocker": "3.1.1",
|
||||
"@vitest/utils": "3.1.1",
|
||||
"@vitest/mocker": "3.1.2",
|
||||
"@vitest/utils": "3.1.2",
|
||||
"magic-string": "^0.30.17",
|
||||
"sirv": "^3.0.1",
|
||||
"tinyrainbow": "^2.0.0",
|
||||
|
|
@ -4415,7 +4425,7 @@
|
|||
},
|
||||
"peerDependencies": {
|
||||
"playwright": "*",
|
||||
"vitest": "3.1.1",
|
||||
"vitest": "3.1.2",
|
||||
"webdriverio": "^7.0.0 || ^8.0.0 || ^9.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
|
|
@ -4431,14 +4441,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz",
|
||||
"integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.2.tgz",
|
||||
"integrity": "sha512-O8hJgr+zREopCAqWl3uCVaOdqJwZ9qaDwUP7vy3Xigad0phZe9APxKhPcDNqYYi0rX5oMvwJMSCAXY2afqeTSA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.1.1",
|
||||
"@vitest/utils": "3.1.1",
|
||||
"@vitest/spy": "3.1.2",
|
||||
"@vitest/utils": "3.1.2",
|
||||
"chai": "^5.2.0",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
|
|
@ -4447,13 +4457,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz",
|
||||
"integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.2.tgz",
|
||||
"integrity": "sha512-kOtd6K2lc7SQ0mBqYv/wdGedlqPdM/B38paPY+OwJ1XiNi44w3Fpog82UfOibmHaV9Wod18A09I9SCKLyDMqgw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.1.1",
|
||||
"@vitest/spy": "3.1.2",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.17"
|
||||
},
|
||||
|
|
@ -4474,9 +4484,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz",
|
||||
"integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.2.tgz",
|
||||
"integrity": "sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -4487,13 +4497,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz",
|
||||
"integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.2.tgz",
|
||||
"integrity": "sha512-bhLib9l4xb4sUMPXnThbnhX2Yi8OutBMA8Yahxa7yavQsFDtwY/jrUZwpKp2XH9DhRFJIeytlyGpXCqZ65nR+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "3.1.1",
|
||||
"@vitest/utils": "3.1.2",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
|
|
@ -4501,13 +4511,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz",
|
||||
"integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.2.tgz",
|
||||
"integrity": "sha512-Q1qkpazSF/p4ApZg1vfZSQ5Yw6OCQxVMVrLjslbLFA1hMDrT2uxtqMaw8Tc/jy5DLka1sNs1Y7rBcftMiaSH/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.1.1",
|
||||
"@vitest/pretty-format": "3.1.2",
|
||||
"magic-string": "^0.30.17",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
|
|
@ -4516,9 +4526,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz",
|
||||
"integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.2.tgz",
|
||||
"integrity": "sha512-OEc5fSXMws6sHVe4kOFyDSj/+4MSwst0ib4un0DlcYgQvRuYQ0+M2HyqGaauUMnjq87tmUaMNDxKQx7wNfVqPA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -4529,35 +4539,35 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/ui": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-3.1.1.tgz",
|
||||
"integrity": "sha512-2HpiRIYg3dlvAJBV9RtsVswFgUSJK4Sv7QhpxoP0eBGkYwzGIKP34PjaV00AULQi9Ovl6LGyZfsetxDWY5BQdQ==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-3.1.2.tgz",
|
||||
"integrity": "sha512-+YPgKiLpFEyBVJNHDkRcSDcLrrnr20lyU4HQoI9Jtq1MdvoX8usql9h38mQw82MBU1Zo5BPC6sw+sXZ6NS18CQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "3.1.1",
|
||||
"@vitest/utils": "3.1.2",
|
||||
"fflate": "^0.8.2",
|
||||
"flatted": "^3.3.3",
|
||||
"pathe": "^2.0.3",
|
||||
"sirv": "^3.0.1",
|
||||
"tinyglobby": "^0.2.12",
|
||||
"tinyglobby": "^0.2.13",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vitest": "3.1.1"
|
||||
"vitest": "3.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz",
|
||||
"integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.2.tgz",
|
||||
"integrity": "sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.1.1",
|
||||
"@vitest/pretty-format": "3.1.2",
|
||||
"loupe": "^3.1.3",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
|
|
@ -5224,9 +5234,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001714",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001714.tgz",
|
||||
"integrity": "sha512-mtgapdwDLSSBnCI3JokHM7oEQBLxiJKVRtg10AxM1AyeiKcM96f0Mkbqeq+1AbiCtvMcHRulAAEMu693JrSWqg==",
|
||||
"version": "1.0.30001715",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz",
|
||||
"integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -5717,13 +5727,13 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cssstyle": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.0.tgz",
|
||||
"integrity": "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.1.tgz",
|
||||
"integrity": "sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@asamuzakjp/css-color": "^3.1.1",
|
||||
"@asamuzakjp/css-color": "^3.1.2",
|
||||
"rrweb-cssom": "^0.8.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -6035,9 +6045,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.138",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.138.tgz",
|
||||
"integrity": "sha512-FWlQc52z1dXqm+9cCJ2uyFgJkESd+16j6dBEjsgDNuHjBpuIzL8/lRc0uvh1k8RNI6waGo6tcy2DvwkTBJOLDg==",
|
||||
"version": "1.5.140",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.140.tgz",
|
||||
"integrity": "sha512-o82Rj+ONp4Ip7Cl1r7lrqx/pXhbp/lh9DpKcMNscFJdh8ebyRofnc7Sh01B4jx403RI0oqTBvlZ7OBIZLMr2+Q==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
|
|
@ -6057,9 +6067,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz",
|
||||
"integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
|
|
@ -6204,9 +6214,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
|
||||
"integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
|
||||
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
|
@ -6269,9 +6279,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz",
|
||||
"integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz",
|
||||
"integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
|
|
@ -6282,31 +6292,31 @@
|
|||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.2",
|
||||
"@esbuild/android-arm": "0.25.2",
|
||||
"@esbuild/android-arm64": "0.25.2",
|
||||
"@esbuild/android-x64": "0.25.2",
|
||||
"@esbuild/darwin-arm64": "0.25.2",
|
||||
"@esbuild/darwin-x64": "0.25.2",
|
||||
"@esbuild/freebsd-arm64": "0.25.2",
|
||||
"@esbuild/freebsd-x64": "0.25.2",
|
||||
"@esbuild/linux-arm": "0.25.2",
|
||||
"@esbuild/linux-arm64": "0.25.2",
|
||||
"@esbuild/linux-ia32": "0.25.2",
|
||||
"@esbuild/linux-loong64": "0.25.2",
|
||||
"@esbuild/linux-mips64el": "0.25.2",
|
||||
"@esbuild/linux-ppc64": "0.25.2",
|
||||
"@esbuild/linux-riscv64": "0.25.2",
|
||||
"@esbuild/linux-s390x": "0.25.2",
|
||||
"@esbuild/linux-x64": "0.25.2",
|
||||
"@esbuild/netbsd-arm64": "0.25.2",
|
||||
"@esbuild/netbsd-x64": "0.25.2",
|
||||
"@esbuild/openbsd-arm64": "0.25.2",
|
||||
"@esbuild/openbsd-x64": "0.25.2",
|
||||
"@esbuild/sunos-x64": "0.25.2",
|
||||
"@esbuild/win32-arm64": "0.25.2",
|
||||
"@esbuild/win32-ia32": "0.25.2",
|
||||
"@esbuild/win32-x64": "0.25.2"
|
||||
"@esbuild/aix-ppc64": "0.25.3",
|
||||
"@esbuild/android-arm": "0.25.3",
|
||||
"@esbuild/android-arm64": "0.25.3",
|
||||
"@esbuild/android-x64": "0.25.3",
|
||||
"@esbuild/darwin-arm64": "0.25.3",
|
||||
"@esbuild/darwin-x64": "0.25.3",
|
||||
"@esbuild/freebsd-arm64": "0.25.3",
|
||||
"@esbuild/freebsd-x64": "0.25.3",
|
||||
"@esbuild/linux-arm": "0.25.3",
|
||||
"@esbuild/linux-arm64": "0.25.3",
|
||||
"@esbuild/linux-ia32": "0.25.3",
|
||||
"@esbuild/linux-loong64": "0.25.3",
|
||||
"@esbuild/linux-mips64el": "0.25.3",
|
||||
"@esbuild/linux-ppc64": "0.25.3",
|
||||
"@esbuild/linux-riscv64": "0.25.3",
|
||||
"@esbuild/linux-s390x": "0.25.3",
|
||||
"@esbuild/linux-x64": "0.25.3",
|
||||
"@esbuild/netbsd-arm64": "0.25.3",
|
||||
"@esbuild/netbsd-x64": "0.25.3",
|
||||
"@esbuild/openbsd-arm64": "0.25.3",
|
||||
"@esbuild/openbsd-x64": "0.25.3",
|
||||
"@esbuild/sunos-x64": "0.25.3",
|
||||
"@esbuild/win32-arm64": "0.25.3",
|
||||
"@esbuild/win32-ia32": "0.25.3",
|
||||
"@esbuild/win32-x64": "0.25.3"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
|
|
@ -6441,9 +6451,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react-refresh": {
|
||||
"version": "0.4.19",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz",
|
||||
"integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==",
|
||||
"version": "0.4.20",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz",
|
||||
"integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
|
|
@ -7574,16 +7584,6 @@
|
|||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/hexoid": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hexoid/-/hexoid-2.0.0.tgz",
|
||||
"integrity": "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
|
|
@ -9295,9 +9295,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.1.9",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
|
||||
"integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
||||
"integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -9686,13 +9686,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/parse5": {
|
||||
"version": "7.2.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
|
||||
"integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
|
||||
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"entities": "^4.5.0"
|
||||
"entities": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||
|
|
@ -11742,16 +11742,19 @@
|
|||
}
|
||||
},
|
||||
"node_modules/supertest/node_modules/formidable": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.2.tgz",
|
||||
"integrity": "sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==",
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz",
|
||||
"integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"dezalgo": "^1.0.4",
|
||||
"hexoid": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/tunnckoCore/commissions"
|
||||
}
|
||||
|
|
@ -11883,13 +11886,13 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
|
||||
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
||||
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.3",
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -11900,9 +11903,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/fdir": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
|
|
@ -12481,9 +12484,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz",
|
||||
"integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.2.tgz",
|
||||
"integrity": "sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -12504,9 +12507,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/fdir": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
|
|
@ -12532,31 +12535,32 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz",
|
||||
"integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.2.tgz",
|
||||
"integrity": "sha512-WaxpJe092ID1C0mr+LH9MmNrhfzi8I65EX/NRU/Ld016KqQNRgxSOlGNP1hHN+a/F8L15Mh8klwaF77zR3GeDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "3.1.1",
|
||||
"@vitest/mocker": "3.1.1",
|
||||
"@vitest/pretty-format": "^3.1.1",
|
||||
"@vitest/runner": "3.1.1",
|
||||
"@vitest/snapshot": "3.1.1",
|
||||
"@vitest/spy": "3.1.1",
|
||||
"@vitest/utils": "3.1.1",
|
||||
"@vitest/expect": "3.1.2",
|
||||
"@vitest/mocker": "3.1.2",
|
||||
"@vitest/pretty-format": "^3.1.2",
|
||||
"@vitest/runner": "3.1.2",
|
||||
"@vitest/snapshot": "3.1.2",
|
||||
"@vitest/spy": "3.1.2",
|
||||
"@vitest/utils": "3.1.2",
|
||||
"chai": "^5.2.0",
|
||||
"debug": "^4.4.0",
|
||||
"expect-type": "^1.2.0",
|
||||
"expect-type": "^1.2.1",
|
||||
"magic-string": "^0.30.17",
|
||||
"pathe": "^2.0.3",
|
||||
"std-env": "^3.8.1",
|
||||
"std-env": "^3.9.0",
|
||||
"tinybench": "^2.9.0",
|
||||
"tinyexec": "^0.3.2",
|
||||
"tinyglobby": "^0.2.13",
|
||||
"tinypool": "^1.0.2",
|
||||
"tinyrainbow": "^2.0.0",
|
||||
"vite": "^5.0.0 || ^6.0.0",
|
||||
"vite-node": "3.1.1",
|
||||
"vite-node": "3.1.2",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -12572,8 +12576,8 @@
|
|||
"@edge-runtime/vm": "*",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||
"@vitest/browser": "3.1.1",
|
||||
"@vitest/ui": "3.1.1",
|
||||
"@vitest/browser": "3.1.2",
|
||||
"@vitest/ui": "3.1.2",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
"concurrently": "^9.1.2",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^15.4.3",
|
||||
"prettier": "^3.5.1",
|
||||
"prettier": "^3.5.3",
|
||||
"vitest": "^3.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue