This commit is contained in:
root
2025-10-22 19:13:26 +00:00
parent ac57b932c2
commit 754b023435
10 changed files with 80 additions and 60 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -32,8 +32,8 @@
<!-- Nepcha Analytics (nepcha.com) --> <!-- Nepcha Analytics (nepcha.com) -->
<!-- Nepcha is a easy-to-use web analytics. No cookies and fully compliant with GDPR, CCPA and PECR. --> <!-- Nepcha is a easy-to-use web analytics. No cookies and fully compliant with GDPR, CCPA and PECR. -->
<script defer data-site="YOUR_DOMAIN_HERE" src="https://api.nepcha.com/js/nepcha-analytics.js"></script> <script defer data-site="YOUR_DOMAIN_HERE" src="https://api.nepcha.com/js/nepcha-analytics.js"></script>
<script type="module" crossorigin src="/assets/index-fb28742e.js"></script> <script type="module" crossorigin src="/assets/index-4797da57.js"></script>
<link rel="stylesheet" href="/assets/index-4cf91620.css"> <link rel="stylesheet" href="/assets/index-53559ba1.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+1 -1
View File
@@ -28,7 +28,7 @@ export function Dashboard() {
<IconButton <IconButton
size="lg" size="lg"
color="white" color="white"
className="fixed bottom-8 right-8 z-40 rounded-full shadow-blue-gray-900/10" className="fixed bottom-8 right-8 z-40 rounded-full shadow-blue-gray-900/10 xl:hidden"
ripple={false} ripple={false}
onClick={() => setOpenConfigurator(dispatch, true)} onClick={() => setOpenConfigurator(dispatch, true)}
> >
+39
View File
@@ -18,6 +18,17 @@ import {
useSelect useSelect
} from "@material-tailwind/react"; } from "@material-tailwind/react";
import { PaginationControls, usePage } from "@/components/PageProvider.jsx"; import { PaginationControls, usePage } from "@/components/PageProvider.jsx";
import { useMaintenance } from "@/components/MaintenanceProvider.jsx";
import { useToast } from "@/components/ToastProvider.jsx";
const UPDATE_STAGE_LABELS = {
initializing: "Vorbereitung",
"activating-maintenance": "Wartungsmodus aktivieren",
"executing-script": "Skript wird ausgeführt",
waiting: "Warte auf Portainer",
completed: "Abgeschlossen",
failed: "Fehlgeschlagen"
};
const StickyOption = React.forwardRef(({ value, onClick, onKeyDown, ...props }, ref) => { const StickyOption = React.forwardRef(({ value, onClick, onKeyDown, ...props }, ref) => {
const { setOpen } = useSelect(); const { setOpen } = useSelect();
@@ -137,6 +148,21 @@ export function Logs() {
const [optionsInitialized, setOptionsInitialized] = useState(false); const [optionsInitialized, setOptionsInitialized] = useState(false);
const [refreshSignal, setRefreshSignal] = useState(0); const [refreshSignal, setRefreshSignal] = useState(0);
const { showToast } = useToast();
const {
maintenance: maintenanceMeta,
update: updateState,
script: scriptConfig,
ssh: sshConfig,
} = useMaintenance();
const maintenanceActive = Boolean(maintenanceMeta?.active);
const maintenanceMessage = maintenanceMeta?.message;
const updateRunning = Boolean(updateState?.running);
const maintenanceLocked = maintenanceActive || updateRunning;
const updateStageLabel = updateState?.stage ? (UPDATE_STAGE_LABELS[updateState.stage] ?? updateState.stage) : "";
const { const {
page, page,
perPage, perPage,
@@ -595,6 +621,19 @@ export function Logs() {
const toggleOpen = () => setOpen((cur) => !cur); const toggleOpen = () => setOpen((cur) => !cur);
return ( return (
<div className="mt-12 mb-8 flex flex-col gap-12"> <div className="mt-12 mb-8 flex flex-col gap-12">
{(maintenanceActive || updateRunning) && (<div className="rounded-lg border border-cyan-500/60 bg-cyan-900/30 px-4 py-3 text-sm text-bluegray-100">
<div className="flex flex-col gap-1">
<span>
Wartungsmodus aktiv{maintenanceMessage ? ` ${maintenanceMessage}` : updateRunning ? " Portainer-Update läuft" : ""}.
</span>
{updateRunning && (
<span className="text-xs text-indigo-900">
Phase: {updateStageLabel}
</span>
)}
</div>
</div>
)}
<Card> <Card>
<CardHeader variant="gradient" color="gray" className="p-4 pt-2 pb-2"> <CardHeader variant="gradient" color="gray" className="p-4 pt-2 pb-2">
<Typography variant="h6" color="white"> <Typography variant="h6" color="white">
+7 -20
View File
@@ -12,14 +12,6 @@ import {
CardBody, CardBody,
Button, Button,
ButtonGroup, ButtonGroup,
IconButton,
Menu,
MenuHandler,
MenuList,
MenuItem,
Avatar,
Tooltip,
Progress,
Spinner, Spinner,
Input, Input,
Select, Select,
@@ -928,29 +920,22 @@ export function Stacks() {
)} )}
<div id="collect" className="mt-8 flex flex-col gap-4 md:flex-row md:items-start md:gap-6"> <div id="collect" className="mt-8 flex flex-col gap-4 md:flex-row md:items-start md:gap-6">
{selectedStackIds.length > 0 && ( {selectedStackIds.length > 0 && (
<div className="w-full md:w-3/4"> <div className="w-full md:order-first order-last md:w-3/4">
<div className="flex flex-col gap-3 text-sm text-gray-300"> <div className="flex flex-col gap-3 text-sm text-gray-300">
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
{selectedStackIds.map((id) => { {selectedStackIds.map((id) => {
const stack = stacksById.get(id); const stack = stacksById.get(id);
const name = stack?.Name || `Stack ${id}`; const name = stack?.Name || `Stack ${id}`;
const isFiltered = filteredStackIdSet.has(id);
const isVisibleOnPage = visiblePageStackIds.has(id);
return ( return (
<button <button
key={id} key={id}
type="button" type="button"
onClick={() => handleChipRemove(id)} onClick={() => handleChipRemove(id)}
className="inline-flex items-center gap-2 rounded-full border border-purple-500/60 bg-purple-500/10 px-3 py-1 text-purple-100 transition hover:border-purple-400 hover:bg-purple-500/20" className="inline-flex items-center gap-2 rounded-full bg-lavenderSmoke-600/80 px-2 py-0.5 text-white transition hover:bg-lavenderSmoke-600/90 focus:outline-none focus:ring-2 focus:ring-lavenderSmoke-400 cursor-pointer"
> >
<span>{name}</span> <span>{name}</span>
{!isFiltered && (
<span className="text-xs uppercase tracking-wide text-amber-300">Ausgefiltert</span> <span className="text-xs font-semibold text-lavenderSmoke-200">x</span>
)}
{isFiltered && !isVisibleOnPage && (
<span className="text-xs uppercase tracking-wide text-blue-300">Andere Seite</span>
)}
<span className="text-xs font-semibold text-purple-200">x</span>
</button> </button>
); );
})} })}
@@ -958,7 +943,7 @@ export function Stacks() {
<button <button
type="button" type="button"
onClick={clearSelection} onClick={clearSelection}
className="mt-1 text-xs font-medium text-gray-400 underline underline-offset-2 transition hover:text-gray-200" className="md:order-last self-start mt-1 text-xs font-medium text-gray-400 underline underline-offset-2 transition hover:text-gray-200"
> >
Auswahl aufheben Auswahl aufheben
</button> </button>
@@ -994,6 +979,8 @@ export function Stacks() {
<Card key={stack.Id}> <Card key={stack.Id}>
<CardBody <CardBody
className={`flex w-full text-stormGrey-500 items-center justify-between gap-4 rounded-xl shadow-lg transition border className={`flex w-full text-stormGrey-500 items-center justify-between gap-4 rounded-xl shadow-lg transition border
${isSelected ? 'border-purple-500 ring-1 ring-purple-500/40' : 'border-transparent'} ${isSelected ? 'border-purple-500 ring-1 ring-purple-500/40' : 'border-transparent'}
${!isSelectable || isBusy ? 'opacity-75 bg-stormGrey-200/20' : ''}`} ${!isSelectable || isBusy ? 'opacity-75 bg-stormGrey-200/20' : ''}`}
@@ -56,7 +56,7 @@ export function DashboardNavbar() {
> >
<Bars3Icon strokeWidth={3} className="h-6 w-6 text-blue-gray-500" /> <Bars3Icon strokeWidth={3} className="h-6 w-6 text-blue-gray-500" />
</IconButton> </IconButton>
<Link to="/auth/sign-in"> {/* <Link to="/auth/sign-in">
<Button <Button
variant="text" variant="text"
color="blue-gray" color="blue-gray"
@@ -150,13 +150,13 @@ export function DashboardNavbar() {
</div> </div>
</MenuItem> </MenuItem>
</MenuList> </MenuList>
</Menu> </Menu> */}
<IconButton <IconButton
variant="text" variant="text"
color="blue-gray" color="blue-gray"
onClick={() => setOpenConfigurator(dispatch, true)} onClick={() => setOpenConfigurator(dispatch, true)}
> >
<Cog6ToothIcon className="h-5 w-5 text-blue-gray-500" /> <Cog6ToothIcon className="h-5 w-5 text-blue-gray-500 " />
</IconButton> </IconButton>
</div> </div>
</div> </div>
+2 -8
View File
@@ -7,16 +7,10 @@ export function Footer() {
return ( return (
<footer> <footer>
<div className="flex w-full flex-wrap items-center justify-center md:justify-between"> <div className="flex w-full flex-wrap items-center justify-center md:justify-end">
<Typography variant="small" className="font-normal text-inherit"> <Typography variant="small" className="mt-1 text-xs text-stormGrey-500 block antialiased font-sans">
StackPulse: &copy; {year} by Michael Böhmländer StackPulse: &copy; {year} by Michael Böhmländer
</Typography> </Typography>
<Typography variant="small" className="font-normal text-inherit">
Tailwind Material Dashboard: &copy; {year} made with{" "}
<HeartIcon className="-mt-0.5 inline-block h-3.5 w-3.5 text-red-600" /> by Creative Tim
</Typography>
</div> </div>
</footer> </footer>
); );
+1 -1
View File
@@ -60,7 +60,7 @@ export function Navbar({ brandName, routes, action }) {
<IconButton <IconButton
variant="text" variant="text"
size="sm" size="sm"
className="ml-auto text-inherit hover:bg-transparent focus:bg-transparent active:bg-transparent lg:hidden" className="hml-auto text-inherit hover:bg-transparent focus:bg-transparent active:bg-transparent lg:hidden"
onClick={() => setOpenNav(!openNav)} onClick={() => setOpenNav(!openNav)}
> >
{openNav ? ( {openNav ? (