Skip to content

Commit

Permalink
add board tool component and integrate with board layout
Browse files Browse the repository at this point in the history
  • Loading branch information
sokphaladam committed Jan 31, 2025
1 parent e869720 commit 2e61a66
Show file tree
Hide file tree
Showing 7 changed files with 568 additions and 61 deletions.
56 changes: 48 additions & 8 deletions src/app/storybook/board/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,59 @@
"use client";
import Board from "@/components/board";
import { BoardFilter } from "@/components/board/board-filter";
import { BoardFilterProps } from "@/components/board/board-filter-dialog";
import { BoardTool } from "@/components/board/board-tool";
import { useState } from "react";
import ReactGridLayout from "react-grid-layout";

interface DashboardProps {
layout: ReactGridLayout.Layout[];
data: {
filters: BoardFilterProps[];
};
}

export default function StorybookBoardPage() {
const [layout, setLayout] = useState<ReactGridLayout.Layout[]>([
{ x: 0, y: 0, w: 1, h: 1, i: "0" },
{ x: 1, y: 0, w: 1, h: 1, i: "1" },
{ x: 2, y: 0, w: 1, h: 1, i: "2" },
{ x: 3, y: 0, w: 1, h: 1, i: "3" },
]);
const [editMode, setEditMode] = useState<
"ADD_CHART" | "REARRANGING_CHART" | null
>(null);
const [value, setValue] = useState<DashboardProps>({
layout: [
{ x: 0, y: 0, w: 1, h: 1, i: "0" },
{ x: 1, y: 0, w: 1, h: 1, i: "1" },
{ x: 2, y: 0, w: 1, h: 1, i: "2" },
{ x: 3, y: 0, w: 1, h: 1, i: "3" },
],
data: { filters: [] },
});

console.log(value);

return (
<div className="bg-secondary min-h-full w-full flex-1">
<Board layout={layout} onChange={setLayout} />
<div className="min-h-full w-full flex-1">
<BoardFilter
filters={value.data.filters}
onFilters={(v) =>
setValue({
...value,
data: {
...value.data,
filters: v,
},
})
}
/>
<BoardTool editMode={editMode} setEditMode={setEditMode} />
<Board
layout={value.layout}
onChange={(v) =>
setValue({
...value,
layout: v,
})
}
editMode={editMode}
/>
</div>
);
}
6 changes: 5 additions & 1 deletion src/app/storybook/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SidebarMenuHeader, SidebarMenuItem } from "@/components/sidebar-menu";
import { Separator } from "@/components/ui/separator";
import { TooltipProvider } from "@/components/ui/tooltip";
import { Component, Layers2 } from "lucide-react";
import StorybookThemeSwitcher from "./storybook-theme-switcher";

Expand Down Expand Up @@ -55,8 +56,11 @@ export default function StorybookRootLayout({
text="Chart"
href="/storybook/chart"
/>
<SidebarMenuItem icon={Component} text="Board" href="/story/board" />
</div>
<div className="flex-1 overflow-y-auto p-4">
<TooltipProvider>{children}</TooltipProvider>
</div>
<div className="flex-1 overflow-y-auto p-4">{children}</div>
</div>
</body>
);
Expand Down
192 changes: 192 additions & 0 deletions src/components/board/board-filter-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { useCallback } from "react";
import { Button } from "../ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "../ui/dialog";
import { Input } from "../ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "../ui/select";

export interface BoardFilterProps {
type: string;
name: string;
default_value: string;
value: string;
new?: boolean;
}

interface Props {
onClose?: () => void;
filter: BoardFilterProps;
onFilter: (v: BoardFilterProps) => void;
onAddFilter?: (v: BoardFilterProps) => void;
}

const DEFAULT_EMPTY = {
type: "search",
name: "",
default_value: "",
value: "",
};

export const DEFAULT_DATE_FILTER = [
"Not timeframe override",
"Custom date range",
"Last 24 hours",
"Today",
"Yesterday",
"This week",
"This month",
"Last 7 days",
"Last 30 days",
"Last 90 days",
];

export function BoardFilterDialog(props: Props) {
let default_value = [...DEFAULT_DATE_FILTER];

if (props.filter.type === "enum" && !!props.filter.value) {
default_value = [...props.filter.value.split(",")];
}

let allowAddFilter = !!props.filter.name;

if (props.filter.type === "enum") {
allowAddFilter = !!props.filter.name && !!props.filter.value;
}

const onAddFilter = useCallback(() => {
props.onAddFilter && props.onAddFilter(props.filter);
}, [props]);

return (
<Dialog open onOpenChange={props.onClose}>
<DialogContent>
<DialogHeader>
<DialogTitle>New Filter</DialogTitle>
</DialogHeader>
<div className="flex flex-col gap-2">
<div className="mb-2">
<div className="mb-1 text-xs font-medium">Select filter type</div>
<Select
value={props.filter.type}
onValueChange={(v) =>
props.onFilter({ ...DEFAULT_EMPTY, type: v })
}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="search">Search</SelectItem>
<SelectItem value="enum">Multi-select ENUM</SelectItem>
<SelectItem value="date">Date Rang</SelectItem>
</SelectContent>
</Select>
</div>
<div className="mb-2">
<div className="mb-1 text-xs font-medium">Filter name*</div>
<Input
placeholder="Enter filter name"
value={props.filter.name}
onChange={(v) =>
props.onFilter({ ...props.filter, name: v.target.value })
}
/>
</div>
{props.filter.type === "enum" && (
<div className="mb-2">
<div className="mb-1 text-xs font-medium">
Values*
<div>
<small className="text-muted-foreground">
Enter values separated by comma
</small>
</div>
</div>
<Input
placeholder="value 1, value 2, value 3"
value={props.filter.value}
onChange={(v) =>
props.onFilter({ ...props.filter, value: v.target.value })
}
/>
</div>
)}
<div className="mb-2">
<div className="mb-1 text-xs font-medium">
Default value (optional)
<div>
<small className="text-muted-foreground">
If this field is left empty, no filter will be applied by
default
</small>
</div>
</div>
{props.filter.type === "search" ? (
<Input
placeholder="Enter default value"
value={props.filter.default_value}
onChange={(v) =>
props.onFilter({
...props.filter,
default_value: v.target.value,
})
}
/>
) : (
<Select
disabled={default_value.length === 0}
value={props.filter.default_value}
onValueChange={(v) =>
props.onFilter({ ...props.filter, default_value: v })
}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{default_value
.filter((x) => !!x)
.map((x) => {
return (
<SelectItem key={x} value={x}>
{x}
</SelectItem>
);
})}
</SelectContent>
</Select>
)}
</div>
{props.filter.type !== "date" && !!props.filter.name && (
<div>
{`Use the variable {{ ${props.filter.name} }} in your charts SQL queries.`}
</div>
)}
</div>
<DialogFooter>
<Button
type="button"
disabled={!allowAddFilter}
onClick={onAddFilter}
>
Add Filter
</Button>
<Button type="button" variant={"secondary"} onClick={props.onClose}>
Cancel
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
Loading

0 comments on commit 2e61a66

Please sign in to comment.