diff --git a/apps/dashboard/src/app/(dashboard)/settings/private-locations/client.tsx b/apps/dashboard/src/app/(dashboard)/settings/private-locations/client.tsx
index 3e7c26319..a45d52bd1 100644
--- a/apps/dashboard/src/app/(dashboard)/settings/private-locations/client.tsx
+++ b/apps/dashboard/src/app/(dashboard)/settings/private-locations/client.tsx
@@ -1,7 +1,9 @@
"use client";
import type { RouterOutputs } from "@openstatus/api";
-import { Lock } from "@openstatus/icons";
+import { ExternalLink, Globe, Lock } from "@openstatus/icons";
+import { Button } from "@openstatus/ui/components/ui/button";
import { useQuery } from "@tanstack/react-query";
+import NextLink from "next/link";
import { useState } from "react";
import { Link } from "@/components/common/link";
@@ -11,6 +13,12 @@ import {
BillingOverlayContainer,
BillingOverlayDescription,
} from "@/components/content/billing-overlay";
+import {
+ EmptyStateActions,
+ EmptyStateDescription,
+ EmptyStateMedia,
+ EmptyStateTitle,
+} from "@/components/content/empty-state";
import {
Section,
SectionDescription,
@@ -20,7 +28,9 @@ import {
} from "@/components/content/section";
import { columns } from "@/components/data-table/private-locations/columns";
import { UpgradeDialog } from "@/components/dialogs/upgrade";
+import { FormSheetPrivateLocationCreate } from "@/components/forms/private-location/sheet-create";
import { DataTable } from "@/components/ui/data-table/data-table";
+import { DataTableEmpty } from "@/components/ui/data-table/data-table-empty";
import { DataTablePaginationSimple } from "@/components/ui/data-table/data-table-pagination";
import { useTRPC } from "@/lib/trpc/client";
@@ -60,6 +70,7 @@ export function Client() {
const { data: privateLocations } = useQuery(
trpc.privateLocation.list.queryOptions(),
);
+ const { data: monitors } = useQuery(trpc.monitor.list.queryOptions());
if (!privateLocations || !workspace) return null;
@@ -111,6 +122,34 @@ export function Client() {
paginationComponent={DataTablePaginationSimple}
columns={columns}
data={privateLocations}
+ emptyState={
+
+
+
+
+ No private locations
+
+ Run the checker inside your own network to monitor services
+ the public internet cannot reach. Create one, then start the
+ checker with its token.
+
+
+
+
+
+
+
+
+ }
/>
)}
diff --git a/apps/dashboard/src/app/(dashboard)/settings/private-locations/nav-actions.tsx b/apps/dashboard/src/app/(dashboard)/settings/private-locations/nav-actions.tsx
index 015cec18a..07ba80c1d 100644
--- a/apps/dashboard/src/app/(dashboard)/settings/private-locations/nav-actions.tsx
+++ b/apps/dashboard/src/app/(dashboard)/settings/private-locations/nav-actions.tsx
@@ -1,29 +1,19 @@
"use client";
import { Button } from "@openstatus/ui/components/ui/button";
-import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
+import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { UpgradeDialog } from "@/components/dialogs/upgrade";
-import { FormSheetPrivateLocation } from "@/components/forms/private-location/sheet";
+import { FormSheetPrivateLocationCreate } from "@/components/forms/private-location/sheet-create";
import { NavFeedback } from "@/components/nav/nav-feedback";
import { useTRPC } from "@/lib/trpc/client";
export function NavActions() {
const trpc = useTRPC();
- const queryClient = useQueryClient();
const [openDialog, setOpenDialog] = useState(false);
const { data: workspace } = useQuery(trpc.workspace.get.queryOptions());
const { data: monitors } = useQuery(trpc.monitor.list.queryOptions());
- const createPrivateLocationMutation = useMutation(
- trpc.privateLocation.new.mutationOptions({
- onSuccess: () => {
- queryClient.invalidateQueries({
- queryKey: trpc.privateLocation.list.queryKey(),
- });
- },
- }),
- );
if (!workspace || !monitors) return null;
@@ -44,21 +34,9 @@ export function NavActions() {
Create Private Location
) : (
-
{
- await createPrivateLocationMutation.mutateAsync({
- name: values.name,
- monitors: values.monitors,
- token: values.token,
- metadata: Object.fromEntries(
- values.metadata.map((m) => [m.key, m.value]),
- ),
- });
- }}
- >
+
-
+
)}
+
+
+
+ No status pages yet
+
+ A status page shows the health of your components to your users,
+ with every report and maintenance posted on it.
+
+
+
+
+
+
+ }
/>
diff --git a/apps/dashboard/src/app/(dashboard)/status-pages/[id]/history/client.tsx b/apps/dashboard/src/app/(dashboard)/status-pages/[id]/history/client.tsx
index 45eed1e1a..2f29d33ba 100644
--- a/apps/dashboard/src/app/(dashboard)/status-pages/[id]/history/client.tsx
+++ b/apps/dashboard/src/app/(dashboard)/status-pages/[id]/history/client.tsx
@@ -1,6 +1,6 @@
"use client";
-import { Info, Lock } from "@openstatus/icons";
+import { Calendar, Info, Lock } from "@openstatus/icons";
import { Tabs, TabsList, TabsTrigger } from "@openstatus/ui/components/ui/tabs";
import { useQuery } from "@tanstack/react-query";
import { format } from "date-fns";
@@ -17,8 +17,8 @@ import {
BillingOverlayDescription,
} from "@/components/content/billing-overlay";
import {
- EmptyStateContainer,
EmptyStateDescription,
+ EmptyStateMedia,
EmptyStateTitle,
} from "@/components/content/empty-state";
import {
@@ -47,6 +47,7 @@ import {
MetricCardValue,
} from "@/components/metric/metric-card";
import { DataTable } from "@/components/ui/data-table/data-table";
+import { DataTableEmpty } from "@/components/ui/data-table/data-table-empty";
import { DataTablePaginationSimple } from "@/components/ui/data-table/data-table-pagination";
import {
HISTORY_WINDOWS,
@@ -188,28 +189,28 @@ export function Client() {
limit="uptime-history"
/>
- ) : hasAnyData ? (
+ ) : (
+
+
+
+ No history yet
+
+ The first snapshot freezes on the 10th of next month. Until
+ then, the current month is served live.
+
+
+ }
/>
- ) : (
-
- No history yet
-
- The first{" "}
- snapshot{" "}
- freezes on the{" "}
-
- 10th of next month
- {" "}
- — until then, the current month is served live.
-
-
)}
diff --git a/apps/dashboard/src/app/(dashboard)/status-pages/[id]/maintenances/(list)/page.tsx b/apps/dashboard/src/app/(dashboard)/status-pages/[id]/maintenances/(list)/page.tsx
index 5f633c506..f6ffa60b7 100644
--- a/apps/dashboard/src/app/(dashboard)/status-pages/[id]/maintenances/(list)/page.tsx
+++ b/apps/dashboard/src/app/(dashboard)/status-pages/[id]/maintenances/(list)/page.tsx
@@ -1,11 +1,20 @@
"use client";
+import { Maintenance } from "@openstatus/icons";
import { Button } from "@openstatus/ui/components/ui/button";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
+import NextLink from "next/link";
import { useParams } from "next/navigation";
+import { useState } from "react";
import { toast } from "sonner";
import { Link } from "@/components/common/link";
+import {
+ EmptyStateActions,
+ EmptyStateDescription,
+ EmptyStateMedia,
+ EmptyStateTitle,
+} from "@/components/content/empty-state";
import {
Section,
SectionDescription,
@@ -18,6 +27,7 @@ import { columns } from "@/components/data-table/maintenances/columns";
import { FormSheetMaintenance } from "@/components/forms/maintenance/sheet";
import { toCheckboxTreeItems } from "@/components/ui/checkbox-tree";
import { DataTable } from "@/components/ui/data-table/data-table";
+import { DataTableEmpty } from "@/components/ui/data-table/data-table-empty";
import { DataTablePaginationSimple } from "@/components/ui/data-table/data-table-pagination";
import { useTRPC } from "@/lib/trpc/client";
import { errorMessage } from "@/lib/trpc/error";
@@ -26,6 +36,7 @@ export default function Page() {
const { id } = useParams<{ id: string }>();
const trpc = useTRPC();
const queryClient = useQueryClient();
+ const [openCreate, setOpenCreate] = useState(false);
const { data: statusPage } = useQuery(
trpc.page.get.queryOptions({ id: Number.parseInt(id) }),
);
@@ -59,6 +70,26 @@ export default function Page() {
if (!statusPage || !maintenances) return null;
+ const items = toCheckboxTreeItems(
+ statusPage.pageComponents,
+ statusPage.pageComponentGroups,
+ );
+ const createMaintenance = async (
+ values: Parameters<
+ React.ComponentProps["onSubmit"]
+ >[0],
+ ) => {
+ await createMaintenanceMutation.mutateAsync({
+ pageId: Number.parseInt(id),
+ title: values.title,
+ message: values.message ?? "",
+ startDate: values.startDate,
+ endDate: values.endDate,
+ pageComponents: values.pageComponents,
+ notifySubscribers: values.notifySubscribers,
+ });
+ };
+
return (
@@ -75,21 +106,10 @@ export default function Page() {
{
- await createMaintenanceMutation.mutateAsync({
- pageId: Number.parseInt(id),
- title: values.title,
- message: values.message ?? "",
- startDate: values.startDate,
- endDate: values.endDate,
- pageComponents: values.pageComponents,
- notifySubscribers: values.notifySubscribers,
- });
- }}
+ open={openCreate}
+ onOpenChange={setOpenCreate}
+ items={items}
+ onSubmit={createMaintenance}
>