Skip to content

Commit

Permalink
Feat/dashboard (#198)
Browse files Browse the repository at this point in the history
* add basic dashboard for workflows with mocks

* fix: switch not animating thumb

* more shadcn components

* better my workflow page

* fix: fix some type problems.
  • Loading branch information
girl-loves-coding authored Nov 10, 2024
1 parent d073cd8 commit 3f46f10
Show file tree
Hide file tree
Showing 29 changed files with 2,175 additions and 14 deletions.
35 changes: 35 additions & 0 deletions packages/client/app/components/workflows/explore-filters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Search } from "lucide-react";
import { Input } from "@data-river/shared/ui/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@data-river/shared/ui/components/ui/select";

export function ExploreFilters() {
return (
<div className="flex gap-4">
<div className="relative w-64">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Search public workflows..."
className="pl-8"
// TODO: Add search functionality
/>
</div>
<Select defaultValue="popular">
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Sort by" />
</SelectTrigger>
<SelectContent>
<SelectItem value="popular">Most Popular</SelectItem>
<SelectItem value="recent">Recently Added</SelectItem>
<SelectItem value="runs">Most Runs</SelectItem>
<SelectItem value="remixes">Most Remixes</SelectItem>
</SelectContent>
</Select>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useState } from "react";
import { ChevronDown } from "lucide-react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@data-river/shared/ui/components/ui/collapsible";
import { cn } from "@data-river/shared/ui/utils";

interface FilterSectionProps {
title: string;
defaultOpen?: boolean;
children: React.ReactNode;
}

export function FilterSection({
title,
defaultOpen = false,
children,
}: FilterSectionProps) {
const [isOpen, setIsOpen] = useState(defaultOpen);

return (
<div className="border-b border-border/50 last:border-none">
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger className="flex w-full items-center justify-between py-4 text-sm font-medium">
{title}
<ChevronDown
className={cn(
"h-4 w-4 text-muted-foreground transition-transform",
{
"transform rotate-180": isOpen,
},
)}
/>
</CollapsibleTrigger>
<CollapsibleContent className="pb-4">{children}</CollapsibleContent>
</Collapsible>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { FilterSection } from "./filter-section";
import { Slider } from "@data-river/shared/ui/components/ui/slider";

interface RangeFilterProps {
title: string;
value: [number, number];
onChange: (value: [number, number]) => void;
max: number;
step: number;
}

export function RangeFilter({
title,
value,
onChange,
max,
step,
}: RangeFilterProps) {
return (
<FilterSection title={title}>
<div className="space-y-4">
<Slider
value={value}
onValueChange={(value) => onChange(value as [number, number])}
max={max}
step={step}
className="mt-2"
/>
<div className="flex justify-between text-xs text-muted-foreground">
<span>{value[0]}</span>
<span>{value[1]}</span>
</div>
</div>
</FilterSection>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Switch } from "@data-river/shared/ui/components/ui/switch";
import { Label } from "@data-river/shared/ui/components/ui/label";
import { FilterSection } from "./filter-section";

interface StatusFilterProps {
isPublic: boolean;
onChange: (isPublic: boolean) => void;
}

export function StatusFilter({ isPublic, onChange }: StatusFilterProps) {
return (
<FilterSection title="Status" defaultOpen>
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label htmlFor="public" className="text-sm">
Public
</Label>
<Switch id="public" checked={isPublic} onCheckedChange={onChange} />
</div>
</div>
</FilterSection>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { FilterSection } from "./filter-section";
import { cn } from "@data-river/shared/ui/utils";

const AVAILABLE_TAGS = [
{ id: "automation", count: 9 },
{ id: "api", count: 5 },
{ id: "data-processing", count: 5 },
{ id: "integration", count: 8 },
{ id: "ai", count: 3 },
{ id: "backup", count: 9 },
{ id: "notifications", count: 8 },
] as const;

interface TagsFilterProps {
selectedTags: string[];
onChange: (tags: string[]) => void;
}

export function TagsFilter({ selectedTags, onChange }: TagsFilterProps) {
const toggleTag = (tag: string) => {
const newTags = selectedTags.includes(tag)
? selectedTags.filter((t) => t !== tag)
: [...selectedTags, tag];
onChange(newTags);
};

return (
<FilterSection title="Tags" defaultOpen>
<div className="space-y-1">
{AVAILABLE_TAGS.map(({ id, count }) => (
<div
key={id}
className={cn(
"flex items-center gap-2 px-2 py-1.5 text-sm rounded-md cursor-pointer hover:bg-muted transition-colors",
selectedTags.includes(id) && "bg-muted",
)}
onClick={() => toggleTag(id)}
>
<div className="h-2 w-2 rounded-full bg-primary/60" />
<span className="flex-1">{id}</span>
<span className="text-xs text-muted-foreground">{count}</span>
</div>
))}
</div>
</FilterSection>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useState } from "react";
import { Calendar } from "@data-river/shared/ui/components/ui/calendar";
import { format, subDays, startOfToday } from "date-fns";
import { CalendarIcon } from "lucide-react";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@data-river/shared/ui/components/ui/popover";
import { Button } from "@data-river/shared/ui/components/ui/button";
import { cn } from "@data-river/shared/ui/utils";
import { FilterSection } from "./filter-section";

const TIME_RANGES = [
{ label: "Today", value: "today", shortcut: "T" },
{ label: "Yesterday", value: "yesterday", shortcut: "Y" },
{ label: "Last hour", value: "hour", shortcut: "H" },
{ label: "Last 7 days", value: "7days", shortcut: "W" },
{ label: "Last 14 days", value: "14days", shortcut: "B" },
{ label: "Last 30 days", value: "30days", shortcut: "M" },
] as const;

function getPresetRange(preset: (typeof TIME_RANGES)[number]["value"]) {
const today = startOfToday();

switch (preset) {
case "today":
return { from: today, to: new Date() };
case "yesterday":
return { from: subDays(today, 1), to: today };
case "hour":
return { from: subDays(new Date(), 1 / 24), to: new Date() };
case "7days":
return { from: subDays(today, 7), to: new Date() };
case "14days":
return { from: subDays(today, 14), to: new Date() };
case "30days":
return { from: subDays(today, 30), to: new Date() };
default:
return null;
}
}

interface TimeRangeFilterProps {
dateRange: { from?: Date; to?: Date } | null;
onChange: (range: { from?: Date; to?: Date } | null) => void;
}

export function TimeRangeFilter({ dateRange, onChange }: TimeRangeFilterProps) {
const [open, setOpen] = useState(false);

return (
<FilterSection title="Time Range">
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
className={cn(
"w-full justify-start text-left font-normal",
!dateRange && "text-muted-foreground",
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{dateRange?.from
? `${format(dateRange.from, "LLL dd, y")} - ${
dateRange.to ? format(dateRange.to, "LLL dd, y") : "..."
}`
: "Pick a date range"}
</Button>
</PopoverTrigger>
<PopoverContent className="w-full p-0" align="start">
<div className="flex">
<div className="w-[200px] border-r p-4 space-y-4">
<div className="font-medium text-sm">Quick Select</div>
{TIME_RANGES.map(({ label, value, shortcut }) => (
<div
key={value}
className={cn(
"flex items-center gap-3 text-sm cursor-pointer hover:text-primary transition-colors py-1",
dateRange === getPresetRange(value) && "text-primary",
)}
onClick={() => {
onChange(getPresetRange(value));
setOpen(false);
}}
>
<kbd className="pointer-events-none inline-flex h-6 select-none items-center gap-1 rounded border bg-muted px-2 font-mono text-[10px] font-medium text-muted-foreground">
{shortcut}
</kbd>
<span className="font-medium">{label}</span>
</div>
))}
</div>
<div className="p-4 flex flex-col">
<div className="font-medium text-sm mb-4">Custom Range</div>
<Calendar
mode="range"
// selected={{
// from: dateRange?.from || undefined,
// to: dateRange?.to || undefined,
// }}
// onSelect={(range) => {
// onChange(
// range?.from
// ? {
// from: range.from,
// to: range.to || range.from,
// }
// : null,
// );
// if (range?.to) setOpen(false);
// }}
numberOfMonths={1}
/>
</div>
</div>
</PopoverContent>
</Popover>
</FilterSection>
);
}
Loading

0 comments on commit 3f46f10

Please sign in to comment.