diff --git a/dashboard/package.json b/dashboard/package.json
index fc5e975..5dd27b5 100644
--- a/dashboard/package.json
+++ b/dashboard/package.json
@@ -22,6 +22,7 @@
"@mui/x-charts": "^7.7.1",
"@radix-ui/react-collapsible": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.1",
+ "@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-navigation-menu": "^1.2.0",
@@ -44,6 +45,7 @@
"react-intl": "^6.6.8",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
+ "vaul": "^0.9.1",
"vite": "^5.3.1"
},
"devDependencies": {
diff --git a/dashboard/pnpm-lock.yaml b/dashboard/pnpm-lock.yaml
index 6e3ba84..d32b0f2 100644
--- a/dashboard/pnpm-lock.yaml
+++ b/dashboard/pnpm-lock.yaml
@@ -26,6 +26,9 @@ importers:
'@radix-ui/react-collapsible':
specifier: ^1.1.0
version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-dialog':
+ specifier: ^1.1.1
+ version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-dropdown-menu':
specifier: ^2.1.1
version: 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -92,6 +95,9 @@ importers:
tailwindcss-animate:
specifier: ^1.0.7
version: 1.0.7(tailwindcss@3.4.4)
+ vaul:
+ specifier: ^0.9.1
+ version: 0.9.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
vite:
specifier: ^5.3.1
version: 5.3.3(@types/node@20.14.10)(terser@5.31.2)
@@ -1353,6 +1359,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-dialog@1.1.1':
+ resolution: {integrity: sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-direction@1.1.0':
resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==}
peerDependencies:
@@ -4883,6 +4902,12 @@ packages:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
+ vaul@0.9.1:
+ resolution: {integrity: sha512-fAhd7i4RNMinx+WEm6pF3nOl78DFkAazcN04ElLPFF9BMCNGbY/kou8UMhIcicm0rJCNePJP0Yyza60gGOD0Jw==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+
vite@5.3.3:
resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -6368,6 +6393,28 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-dialog@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ aria-hidden: 1.2.4
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-remove-scroll: 2.5.7(@types/react@18.3.3)(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-direction@1.1.0(@types/react@18.3.3)(react@18.3.1)':
dependencies:
react: 18.3.1
@@ -10360,6 +10407,15 @@ snapshots:
vary@1.1.2: {}
+ vaul@0.9.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ '@radix-ui/react-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+
vite@5.3.3(@types/node@20.14.10)(terser@5.31.2):
dependencies:
esbuild: 0.21.5
diff --git a/dashboard/src/components/Filter/Drawer.tsx b/dashboard/src/components/Filter/Drawer.tsx
new file mode 100644
index 0000000..ab01492
--- /dev/null
+++ b/dashboard/src/components/Filter/Drawer.tsx
@@ -0,0 +1,117 @@
+import React from 'react';
+import { FormattedMessage } from 'react-intl';
+
+import { IoClose } from 'react-icons/io5';
+
+import { Button } from '../ui/button';
+
+import {
+ Drawer as UIDrawer,
+ DrawerContent,
+ DrawerFooter,
+ DrawerClose,
+ DrawerTrigger,
+} from '../ui/drawer';
+import { Separator } from '../ui/separator';
+
+interface IDrawerLink {
+ treeURL: string;
+ onRefresh: () => void;
+}
+
+interface IFilterDrawer extends IDrawerLink {
+ children?: React.ReactNode;
+ onCancel: () => void;
+ onFilter: () => void;
+}
+
+const DrawerHeader = (): JSX.Element => {
+ return (
+
+ );
+};
+
+const DrawerLink = ({ treeURL, onRefresh }: IDrawerLink): JSX.Element => {
+ return (
+
+ );
+};
+
+const Drawer = ({
+ treeURL,
+ children,
+ onRefresh,
+ onCancel,
+ onFilter,
+}: IFilterDrawer): JSX.Element => {
+ return (
+
+
+
+
+
+
+
+
+
+
+ {React.Children.map(children, (child, idx) => (
+ <>
+ {idx != 0 &&
}
+
{child}
+ >
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Drawer;
diff --git a/dashboard/src/components/Filter/SummarySection.tsx b/dashboard/src/components/Filter/SummarySection.tsx
new file mode 100644
index 0000000..492e8f1
--- /dev/null
+++ b/dashboard/src/components/Filter/SummarySection.tsx
@@ -0,0 +1,42 @@
+import { useMemo } from 'react';
+
+interface ISummarySectionColumn {
+ title: string;
+ value: string;
+}
+
+export interface ISummarySection {
+ title: string;
+ columns: ISummarySectionColumn[];
+}
+
+const SummarySectioncolumn = ({
+ title,
+ value,
+}: ISummarySectionColumn): JSX.Element => {
+ return (
+
+
{title}
+ {value}
+
+ );
+};
+
+const SummarySection = ({ title, columns }: ISummarySection): JSX.Element => {
+ const columnComponents = useMemo(
+ () =>
+ columns.map(column => (
+
+ )),
+ [columns],
+ );
+
+ return (
+
+
{title}
+
{columnComponents}
+
+ );
+};
+
+export default SummarySection;
diff --git a/dashboard/src/components/Filter/TimeRangeSection.tsx b/dashboard/src/components/Filter/TimeRangeSection.tsx
index 19f51f1..33288ee 100644
--- a/dashboard/src/components/Filter/TimeRangeSection.tsx
+++ b/dashboard/src/components/Filter/TimeRangeSection.tsx
@@ -3,6 +3,8 @@ import { FormattedMessage } from 'react-intl';
import { Input } from '../ui/input';
interface TimeRangeSection {
+ title: string;
+ subtitle: string;
min: number;
max: number;
onMinChange: (e: React.FormEvent) => void;
@@ -13,13 +15,17 @@ const inputContainerClass = 'flex gap-x-4 items-center';
const inputClass = 'max-w-20';
const TimeRangeSection = ({
+ title,
+ subtitle,
min,
max,
onMinChange,
onMaxChange,
}: TimeRangeSection): JSX.Element => {
return (
-
+
+
{title}
+
{subtitle}
:
diff --git a/dashboard/src/components/Filter/CheckboxSection.stories.tsx b/dashboard/src/components/Filter/stories/CheckboxSection.stories.tsx
similarity index 95%
rename from dashboard/src/components/Filter/CheckboxSection.stories.tsx
rename to dashboard/src/components/Filter/stories/CheckboxSection.stories.tsx
index a457599..a0d6e22 100644
--- a/dashboard/src/components/Filter/CheckboxSection.stories.tsx
+++ b/dashboard/src/components/Filter/stories/CheckboxSection.stories.tsx
@@ -1,7 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
-import CheckboxSection from './CheckboxSection';
+import CheckboxSection from '../CheckboxSection';
const ActionsData = {
onClickItem: fn(),
diff --git a/dashboard/src/components/Filter/stories/FilterDrawer.stories.tsx b/dashboard/src/components/Filter/stories/FilterDrawer.stories.tsx
new file mode 100644
index 0000000..eafbaf7
--- /dev/null
+++ b/dashboard/src/components/Filter/stories/FilterDrawer.stories.tsx
@@ -0,0 +1,85 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+
+import { IntlProvider } from 'react-intl';
+import { flatten } from 'flat';
+
+import { LOCALES } from '../../../locales/constants';
+
+import { messages } from '../../../locales/messages';
+
+import FilterDrawer from '../Drawer';
+
+import CheckboxSection from '../CheckboxSection';
+import TimeRangeSection from '../TimeRangeSection';
+import SummarySection from '../SummarySection';
+
+const ActionsData = {
+ onRefresh: fn(),
+ onCancel: fn(),
+ onFilter: fn(),
+};
+
+const meta: Meta = {
+ title: 'FilterDrawer',
+ component: FilterDrawer,
+ parameters: {
+ layout: 'centered',
+ },
+ tags: ['autodocs'],
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ decorators: [
+ (): JSX.Element => (
+
+
+
+
+
+
+
+
+ ),
+ ],
+};
+
+const summarySectionProps = {
+ title: 'Tree',
+ columns: [
+ { title: 'Tree', value: 'stable-rc' },
+ { title: 'Matainer', value: 'Shannon Nelson' },
+ {
+ title: 'Commit/tag',
+ value: '5.15.150-rc1 - 3ab4d9c9e190217ee7e974c70b96795cd2f74611',
+ },
+ ],
+};
+
+const checkboxSectionProps = {
+ items: ['linux-5.15.y', 'Status:failed', 'Status: Warnings'],
+ title: 'Branch',
+ subtitle: 'Please select one or more Branches',
+ onClickItem: (idx: number, isChecked: boolean): void =>
+ console.log(idx, isChecked),
+};
+
+const onChangeM = (e: React.FormEvent): void =>
+ console.log(e.target);
+const timeRangeSectionProps = {
+ title: 'Timing',
+ subtitle: 'Please select a range of timing:',
+ min: 0,
+ max: 100,
+ onMinChange: onChangeM,
+ onMaxChange: onChangeM,
+};
diff --git a/dashboard/src/components/Filter/TimeRangeSection.stories.tsx b/dashboard/src/components/Filter/stories/TimeRangeSection.stories.tsx
similarity index 74%
rename from dashboard/src/components/Filter/TimeRangeSection.stories.tsx
rename to dashboard/src/components/Filter/stories/TimeRangeSection.stories.tsx
index 9a449d9..ba7d99c 100644
--- a/dashboard/src/components/Filter/TimeRangeSection.stories.tsx
+++ b/dashboard/src/components/Filter/stories/TimeRangeSection.stories.tsx
@@ -4,11 +4,11 @@ import { flatten } from 'flat';
import { fn } from '@storybook/test';
-import { LOCALES } from '../../locales/constants';
+import { LOCALES } from '../../../locales/constants';
-import { messages } from '../../locales/messages';
+import { messages } from '../../../locales/messages';
-import TimeRangeSection from './TimeRangeSection';
+import TimeRangeSection from '../TimeRangeSection';
const ActionsData = {
onMinChange: fn(),
@@ -31,7 +31,12 @@ export default meta;
type Story = StoryObj;
export const Default: Story = {
- args: { min: 0, max: 10 },
+ args: {
+ title: 'Timing',
+ subtitle: 'Please select a range of timing:',
+ min: 0,
+ max: 10,
+ },
decorators: [
(story): JSX.Element => (
) => (
+
+)
+Drawer.displayName = "Drawer"
+
+const DrawerTrigger = DrawerPrimitive.Trigger
+
+const DrawerPortal = DrawerPrimitive.Portal
+
+const DrawerClose = DrawerPrimitive.Close
+
+const DrawerOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName
+
+const DrawerContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+))
+DrawerContent.displayName = "DrawerContent"
+
+const DrawerHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DrawerHeader.displayName = "DrawerHeader"
+
+const DrawerFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DrawerFooter.displayName = "DrawerFooter"
+
+const DrawerTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DrawerTitle.displayName = DrawerPrimitive.Title.displayName
+
+const DrawerDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DrawerDescription.displayName = DrawerPrimitive.Description.displayName
+
+export {
+ Drawer,
+ DrawerPortal,
+ DrawerOverlay,
+ DrawerTrigger,
+ DrawerClose,
+ DrawerContent,
+ DrawerHeader,
+ DrawerFooter,
+ DrawerTitle,
+ DrawerDescription,
+}
diff --git a/dashboard/src/locales/messages/index.ts b/dashboard/src/locales/messages/index.ts
index 16a7003..da937d9 100644
--- a/dashboard/src/locales/messages/index.ts
+++ b/dashboard/src/locales/messages/index.ts
@@ -27,6 +27,9 @@ export const messages = {
filter: {
min: 'Min',
max: 'Max',
+ filtering: 'Filtering',
+ treeURL: 'Tree URL',
+ refresh: 'Refresh',
},
},
};
diff --git a/dashboard/tailwind.config.js b/dashboard/tailwind.config.js
index 6e75cf3..6b935bd 100644
--- a/dashboard/tailwind.config.js
+++ b/dashboard/tailwind.config.js
@@ -32,14 +32,16 @@ module.exports = {
"accordion-up": "accordion-up 0.2s ease-out",
},
colors: {
- 'bgSecondary' : '#343638',
- 'lightBlue': '#11B3E6',
+ 'bgSecondary': '#343638',
+ 'lightBlue': '#11B3E6',
'onSecondary-10': '#FFFFFF1A',
"white": '#FFFFFF',
"lightGray": '#F4F4F4',
"mediumGray": '#EAEAEA',
"darkGray": '#D6D6D6',
+ "darkGray2": '#767676',
"dimGray": '#454545',
+ "dimBlack": '#333333',
"black": '#000000',
"lightRed": '#FFBBBB',
"yellow": '#FFD27C',