diff --git a/backend/alert/service.go b/backend/alert/service.go index 96745d7..e4fe8af 100644 --- a/backend/alert/service.go +++ b/backend/alert/service.go @@ -141,6 +141,7 @@ func (s *Service) SendTest(ctx context.Context, alertType, name, webhook string) }) if err != nil { log.Error("Failed to send test alert via %s: %v", service.GetServiceName(), err) + return err } break } diff --git a/client/src/components/header/BlockingTimer.tsx b/client/src/components/header/BlockingTimer.tsx new file mode 100644 index 0000000..aa66933 --- /dev/null +++ b/client/src/components/header/BlockingTimer.tsx @@ -0,0 +1,51 @@ +import { GetRequest } from "@/util"; +import { useEffect, useState } from "react"; +import { toast } from "sonner"; + +export default function BlockingTimer() { + const [timeLeft, setTimeLeft] = useState(0); + + useEffect(() => { + async function fetchNotifications() { + try { + const [code, response] = await GetRequest("pause"); + if (code !== 200) { + toast.warning("Unable to fetch blocking status", { + id: "fetch-notifications-error" + }); + return; + } + + setTimeLeft(response.timeLeft || 0); + } catch { + toast.error("Error while fetching notifications"); + } + } + + fetchNotifications(); + + const intervalId = setInterval(() => { + fetchNotifications(); + }, 1000); + + return () => clearInterval(intervalId); + }, []); + + return ( +
+ {timeLeft === 0 ? ( +
+ Blocking active +
+ ) : ( +
+ Blocking paused:{" "} + {Math.floor(timeLeft / 60) + .toString() + .padStart(2, "0")} + :{(timeLeft % 60).toString().padStart(2, "0")} +
+ )} +
+ ); +} diff --git a/client/src/components/nav-actions.tsx b/client/src/components/header/nav-actions.tsx similarity index 81% rename from client/src/components/nav-actions.tsx rename to client/src/components/header/nav-actions.tsx index 7a74efa..d057994 100644 --- a/client/src/components/nav-actions.tsx +++ b/client/src/components/header/nav-actions.tsx @@ -6,7 +6,6 @@ import { DialogClose, DialogContent, DialogDescription, - DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; @@ -38,8 +37,9 @@ import { import { compare } from "compare-versions"; import { JSX, useEffect, useState } from "react"; import { toast } from "sonner"; -import { Metrics } from "./server-statistics"; -import { Input } from "./ui/input"; +import { Metrics } from "../server-statistics"; +import { Input } from "../ui/input"; +import { ToggleGroup, ToggleGroupItem } from "../ui/toggle-group"; const data = [ [ @@ -290,28 +290,74 @@ export default function PauseBlockingDialog({ } }; + const formatTime = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${mins}:${secs.toString().padStart(2, "0")}`; + }; + return ( - + {pauseStatus?.paused ? "Blocking Paused" : "Pause Blocking"} - + {pauseStatus?.paused - ? `Blocking is currently paused. Remaining time: ${remainingTime} seconds.` - : "This will temporarily pause domain blocking, allowing all traffic to pass through."} + ? "Blocking is currently paused" + : "Temporarily allow all traffic through"} - {!pauseStatus?.paused ? ( - <> -
-
+ ) : ( +
+
+ + - Duration (seconds) + setPauseTime(10)}> + 10s + + setPauseTime(30)}> + 30s + + setPauseTime(60)}> + 1m + + setPauseTime(300)}> + 5m + + setPauseTime(600)}> + 10m + + +
+ +
+ setPauseTime(e.target.valueAsNumber)} - className="w-full" />
- - - - - ) : ( - - - +
+ )}
); diff --git a/client/src/components/notifications.tsx b/client/src/components/header/notifications.tsx similarity index 100% rename from client/src/components/notifications.tsx rename to client/src/components/header/notifications.tsx diff --git a/client/src/app/theme/theme-context.tsx b/client/src/components/header/theme/theme-context.tsx similarity index 100% rename from client/src/app/theme/theme-context.tsx rename to client/src/components/header/theme/theme-context.tsx diff --git a/client/src/app/theme/theme-provider.tsx b/client/src/components/header/theme/theme-provider.tsx similarity index 100% rename from client/src/app/theme/theme-provider.tsx rename to client/src/components/header/theme/theme-provider.tsx diff --git a/client/src/app/theme/toggle-theme.tsx b/client/src/components/header/theme/toggle-theme.tsx similarity index 100% rename from client/src/app/theme/toggle-theme.tsx rename to client/src/components/header/theme/toggle-theme.tsx diff --git a/client/src/app/theme/use-theme.ts b/client/src/components/header/theme/use-theme.ts similarity index 100% rename from client/src/app/theme/use-theme.ts rename to client/src/components/header/theme/use-theme.ts diff --git a/client/src/components/site-header.tsx b/client/src/components/site-header.tsx index 81aa6ed..6e625ff 100644 --- a/client/src/components/site-header.tsx +++ b/client/src/components/site-header.tsx @@ -1,9 +1,10 @@ import { Separator } from "@/components/ui/separator"; import { SidebarTrigger } from "@/components/ui/sidebar"; import { useLocation } from "react-router-dom"; -import { NavActions } from "./nav-actions"; -import Notifications from "./notifications"; -import { ModeToggle } from "@/app/theme/toggle-theme"; +import { NavActions } from "./header/nav-actions"; +import Notifications from "./header/notifications"; +import { ModeToggle } from "@/components/header/theme/toggle-theme"; +import BlockingTimer from "./header/BlockingTimer"; interface PageInfo { title: string; @@ -96,6 +97,7 @@ export function SiteHeader() {
+ diff --git a/client/src/pages/app.tsx b/client/src/pages/app.tsx index 56970c6..0fcf9ec 100644 --- a/client/src/pages/app.tsx +++ b/client/src/pages/app.tsx @@ -15,7 +15,7 @@ import { Whitelist } from "./whitelist"; import { GenerateQuote } from "@/quotes"; import Login from "./login"; import { FileXIcon } from "@phosphor-icons/react"; -import { ThemeProvider } from "@/app/theme/theme-provider"; +import { ThemeProvider } from "@/components/header/theme/theme-provider"; function NotFound() { return (