Skip to content

Commit

Permalink
updated dashboard widgets
Browse files Browse the repository at this point in the history
  • Loading branch information
jsbroks committed Nov 29, 2024
1 parent e4f58e4 commit 8a1b142
Show file tree
Hide file tree
Showing 18 changed files with 1,264 additions and 776 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,15 @@ export const DeploymentResourceDrawer: React.FC = () => {
};

const { data: releaseWithTriggersData, ...releaseWithTriggersQ } =
api.release.list.useQuery({
deploymentId: deploymentId ?? "",
filter: releaseFilter,
jobFilter,
limit: 100,
});
api.release.list.useQuery(
{
deploymentId: deploymentId ?? "",
filter: releaseFilter,
jobFilter,
limit: 100,
},
{ enabled: deploymentId != null },
);
const releaseWithTriggers = releaseWithTriggersData?.items ?? [];

const loading =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"use client";

import { useRouter } from "next/navigation";

import { Button } from "@ctrlplane/ui/button";

import { api } from "~/trpc/react";

export const CreateDashboardButton: React.FC<{
workspace: { id: string; slug: string };
}> = ({ workspace }) => {
const createDashboard = api.dashboard.create.useMutation();
const router = useRouter();
return (
<Button
onClick={async () => {
const ds = await createDashboard.mutateAsync({
name: "New Dashboard",
description: "New Dashboard",
workspaceId: workspace.id,
});
router.push(`/${workspace.slug}/dashboards/${ds.id}`);
}}
>
Create Dashboard
</Button>
);
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
"use client";

/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
import type { Workspace } from "@ctrlplane/db/schema";
import type { Layout } from "react-grid-layout";
import { useCallback, useEffect, useMemo, useState } from "react";
import { produce } from "immer";
import { clamp } from "lodash";
import { v4 as uuidv4 } from "uuid";

import { cn } from "@ctrlplane/ui";

import type { WidgetLayout } from "./DashboardContext";
import type { Widget } from "./widgets";
import { api } from "~/trpc/react";
Expand Down Expand Up @@ -40,11 +43,12 @@ type DashboardEvents = {
};

const RenderWidget: React.FC<{
workspace: Workspace;
id: string;
spec: Widget;
config: any;
events?: DashboardEvents;
}> = ({ id, spec, config, events }) => {
}> = ({ id, spec, config, events, workspace }) => {
const { editMode } = useDashboardContext();
const { deleteWidget, updateConfig } = useDashboardLayout(events);
const { Component } = spec;
Expand All @@ -54,6 +58,7 @@ const RenderWidget: React.FC<{
isEditMode={editMode}
config={config}
updateConfig={(c) => updateConfig(id, c)}
workspace={workspace}
/>
);
};
Expand Down Expand Up @@ -191,8 +196,10 @@ export const useDashboardGrid = (events?: DashboardEvents) => {
};
};

export const Dashboard: React.FC = () => {
const { dashboardId, setEditMode, layout } = useDashboardContext();
export const Dashboard: React.FC<{ workspace: Workspace }> = ({
workspace,
}) => {
const { dashboardId, setEditMode, editMode, layout } = useDashboardContext();

const create = api.dashboard.widget.create.useMutation();
const del = api.dashboard.widget.delete.useMutation();
Expand Down Expand Up @@ -263,7 +270,13 @@ export const Dashboard: React.FC = () => {
const grid = useDashboardGrid(events);

return (
<DashboardGrid {...grid} className="m-10 mb-16">
<DashboardGrid
{...grid}
className={cn(
"m-10 mb-16 rounded-md border border-dotted",
editMode ? "border-neutral-800" : "border-transparent",
)}
>
{layout.map((item) => {
const { i, widget } = item;
const spec = widgets[widget];
Expand All @@ -272,6 +285,7 @@ export const Dashboard: React.FC = () => {
<div key={i} className="relative h-full w-full">
<RenderWidget
id={i}
workspace={workspace}
spec={spec}
config={item.config ?? {}}
events={events}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useParams } from "next/navigation";
import { IconAlertSmall, IconDashboard, IconEdit } from "@tabler/icons-react";
import { IconLayout, IconX } from "@tabler/icons-react";

import { cn } from "@ctrlplane/ui";
import {
Expand All @@ -11,31 +11,28 @@ import {
BreadcrumbList,
BreadcrumbSeparator,
} from "@ctrlplane/ui/breadcrumb";
import { Button } from "@ctrlplane/ui/button";

import { api } from "~/trpc/react";
import { useDashboardContext } from "../DashboardContext";

const EditButton: React.FC = () => {
const { editMode, setEditMode } = useDashboardContext();
return (
<button
<Button
size="sm"
variant={editMode ? "outline" : "default"}
onClick={() => setEditMode(!editMode)}
aria-label={editMode ? "Disable editing mode" : "Enable editing mode"}
className={cn(
"shrink-0 rounded-full p-1 text-xs",
editMode
? "bg-yellow-400/20 text-yellow-300 hover:bg-yellow-400/25"
: "text-neutral-300 hover:bg-neutral-800 hover:text-white",
"flex shrink-0 items-center gap-1 rounded-full p-1 px-2 pr-2 text-xs",
)}
>
{editMode ? (
<div className="flex items-center gap-1 pr-2">
<IconAlertSmall className="h-4 w-4" />
<span className="grow">Editing mode enabled</span>
</div>
) : (
<IconEdit className="h-4 w-4" />
)}
</button>
<IconX
className={cn("h-4 w-4 transition-transform", !editMode && "rotate-45")}
/>
<span className="grow">Add widgets</span>
</Button>
);
};

Expand All @@ -54,7 +51,7 @@ export const DashboardTitle: React.FC = () => {
href={`/${workspaceSlug}/systems`}
className="flex items-center gap-2 text-white"
>
<IconDashboard className="text-muted-foreground" />
<IconLayout className="text-muted-foreground" />
Dashboard
</BreadcrumbLink>
</BreadcrumbItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
"use client";

import { IconX } from "@tabler/icons-react";

import { Button } from "@ctrlplane/ui/button";
import { Input } from "@ctrlplane/ui/input";

import { useMatchSorterWithSearch } from "~/utils/useMatchSorter";
import { useDashboardContext } from "../DashboardContext";
import { widgets } from "../widgets";

export const WidgetMenu: React.FC = () => {
const { setDroppingItem, editMode } = useDashboardContext();
const { setDroppingItem, editMode, setEditMode } = useDashboardContext();
const { search, setSearch, result } = useMatchSorterWithSearch(
Object.entries(widgets),
{ keys: ["0", "1.displayName"] },
);
if (!editMode) return null;
return (
<div className="z-10 shrink-0 space-y-6 border-b py-6">
<div className="fixed bottom-0 right-0 top-[56px] z-10 m-2 w-[400px] space-y-6 rounded-md border bg-background/70 py-6 drop-shadow-2xl backdrop-blur-lg">
<div className="absolute right-4 top-4">
<Button
variant="ghost"
size="icon"
className="h-6 w-6 p-0"
onClick={() => setEditMode(false)}
>
<IconX className="h-4 w-4 text-muted-foreground hover:text-foreground" />
</Button>
</div>
<div className="px-6">
<p className="mb-2 text-sm text-muted-foreground">
Drag and Drop new widgets onto your dashboard.
Expand All @@ -26,9 +39,9 @@ export const WidgetMenu: React.FC = () => {
/>
</div>

<div className="flex items-center gap-4 overflow-x-auto px-6">
<div className="grid grid-cols-3 gap-2 px-4">
{result.map(([name, widget]) => {
const { ComponentPreview } = widget;
const { Icon, displayName } = widget;
return (
<div
key={name}
Expand All @@ -48,9 +61,17 @@ export const WidgetMenu: React.FC = () => {
setDroppingItem(item);
e.dataTransfer.setData("text/plain", "");
}}
className="relative h-[130px] w-[160px] shrink-0 cursor-grab select-none rounded-md border px-4 py-1.5"
className="group relative w-full cursor-grab select-none space-y-1"
>
<ComponentPreview />
<div className="h-[110px] w-full rounded-md border group-hover:bg-neutral-500/5">
<div className="flex h-full items-center justify-center">
<Icon />
</div>
</div>

<div className="text-center text-xs text-muted-foreground group-hover:text-white">
{displayName}
</div>
</div>
);
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ export default async function DashboardPage({
}: {
params: { workspaceSlug: string; dashboardId: string };
}) {
const workspace = await api.workspace
.bySlug(params.workspaceSlug)
.catch(() => null);
if (workspace == null) notFound();

const dashboard = await api.dashboard.get(params.dashboardId);
if (dashboard == null) notFound();
return (
Expand All @@ -22,7 +27,7 @@ export default async function DashboardPage({
<WidgetMenu />

<ScrollArea className="flex-grow">
<Dashboard />
<Dashboard workspace={workspace} />
</ScrollArea>
</div>
</DashboardProvider>
Expand Down
28 changes: 26 additions & 2 deletions apps/webservice/src/app/[workspaceSlug]/(app)/dashboards/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
export default function DasboardsPage() {
return null;
import Link from "next/link";
import { notFound } from "next/navigation";

import { api } from "~/trpc/server";
import { CreateDashboardButton } from "./CreateDashboardButton";

export default async function DasboardsPage({
params,
}: {
params: { workspaceSlug: string };
}) {
const workspace = await api.workspace
.bySlug(params.workspaceSlug)
.catch(() => null);
if (workspace == null) return notFound();
const dashboards = await api.dashboard.byWorkspaceId(workspace.id);
return (
<div className="flex flex-col gap-4">
<CreateDashboardButton workspace={workspace} />
{dashboards.map((d) => (
<Link key={d.id} href={`/${workspace.slug}/dashboards/${d.id}`}>
{d.name}
</Link>
))}
</div>
);
}
Loading

0 comments on commit 8a1b142

Please sign in to comment.