aboutsummaryrefslogtreecommitdiffstats
path: root/ui/src/pages
diff options
context:
space:
mode:
authorEllie Huxtable <ellie@atuin.sh>2024-07-23 13:18:54 +0100
committerGitHub <noreply@github.com>2024-07-23 13:18:54 +0100
commitf8c963c7d668fc57680f25413f20bc207d4bf64a (patch)
treec7f952ddc0220cded24f5447d03b3bff46fb1d45 /ui/src/pages
parentfix(themes): Restore default theme, refactor (#2294) (diff)
downloadatuin-f8c963c7d668fc57680f25413f20bc207d4bf64a.zip
feat(gui): clean up home page, fix a few bugs (#2304)
* wip home screen changes * more * adjust * fixes and things * patch runbook pty check
Diffstat (limited to 'ui/src/pages')
-rw-r--r--ui/src/pages/Home.tsx253
1 files changed, 187 insertions, 66 deletions
diff --git a/ui/src/pages/Home.tsx b/ui/src/pages/Home.tsx
index 0f0b5dcf..2e93a893 100644
--- a/ui/src/pages/Home.tsx
+++ b/ui/src/pages/Home.tsx
@@ -2,32 +2,94 @@ import React, { useEffect } from "react";
import { formatRelative } from "date-fns";
import { Tooltip as ReactTooltip } from "react-tooltip";
-import { useStore } from "@/state/store";
+import { AtuinState, useStore } from "@/state/store";
import { useToast } from "@/components/ui/use-toast";
import { ToastAction } from "@/components/ui/toast";
import { invoke } from "@tauri-apps/api/core";
+import {
+ Card,
+ CardHeader,
+ CardBody,
+ Listbox,
+ ListboxItem,
+} from "@nextui-org/react";
+
+import {
+ Bar,
+ BarChart,
+ CartesianGrid,
+ LabelList,
+ XAxis,
+ YAxis,
+} from "recharts";
+import { ChartConfig, ChartContainer } from "@/components/ui/chart";
+
+import { Clock, Terminal } from "lucide-react";
import ActivityCalendar from "react-activity-calendar";
+import HistoryRow from "@/components/history/HistoryRow";
+import { ShellHistory } from "@/state/models";
-function Stats({ stats }: any) {
+function StatCard({ name, stat }: any) {
return (
- <div>
- <dl className="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
- {stats.map((item: any) => (
- <div
- key={item.name}
- className="overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6"
- >
- <dt className="truncate text-sm font-medium text-gray-500">
- {item.name}
- </dt>
- <dd className="mt-1 text-xl font-semibold tracking-tight text-gray-900">
- {item.stat}
- </dd>
- </div>
- ))}
- </dl>
- </div>
+ <Card shadow="sm">
+ <CardHeader>
+ <h3 className="uppercase text-gray-500">{name}</h3>
+ </CardHeader>
+ <CardBody>
+ <h2 className="font-bold text-xl">{stat}</h2>
+ </CardBody>
+ </Card>
+ );
+}
+
+function TopChart({ chartData }: any) {
+ const chartConfig = {
+ command: {
+ label: "Command",
+ color: "#c4edde",
+ },
+ } satisfies ChartConfig;
+
+ return (
+ <ChartContainer config={chartConfig} className="max-h-72">
+ <BarChart
+ accessibilityLayer
+ data={chartData}
+ layout="vertical"
+ margin={{
+ right: 16,
+ }}
+ >
+ <CartesianGrid horizontal={false} />
+ <YAxis
+ dataKey="command"
+ type="category"
+ tickLine={false}
+ tickMargin={10}
+ axisLine={false}
+ tickFormatter={(value) => value.slice(0, 3)}
+ hide
+ />
+ <XAxis dataKey="count" type="number" hide />
+ <Bar dataKey="count" layout="vertical" fill="#c4edde" radius={4}>
+ <LabelList
+ dataKey="command"
+ position="insideLeft"
+ offset={8}
+ className="fill-[--color-label]"
+ fontSize={12}
+ />
+ <LabelList
+ dataKey="count"
+ position="right"
+ offset={8}
+ className="fill-foreground"
+ fontSize={12}
+ />
+ </Bar>
+ </BarChart>
+ </ChartContainer>
);
}
@@ -62,14 +124,22 @@ const explicitTheme = {
};
export default function Home() {
- const homeInfo = useStore((state) => state.homeInfo);
- const user = useStore((state) => state.user);
- const calendar = useStore((state) => state.calendar);
- const weekStart = useStore((state) => state.weekStart);
+ const homeInfo = useStore((state: AtuinState) => state.homeInfo);
+ const user = useStore((state: AtuinState) => state.user);
+ const calendar = useStore((state: AtuinState) => state.calendar);
+ const runbooks = useStore((state: AtuinState) => state.runbooks);
+ const weekStart = useStore((state: AtuinState) => state.weekStart);
- const refreshHomeInfo = useStore((state) => state.refreshHomeInfo);
- const refreshUser = useStore((state) => state.refreshUser);
- const refreshCalendar = useStore((state) => state.refreshCalendar);
+ const refreshHomeInfo = useStore(
+ (state: AtuinState) => state.refreshHomeInfo,
+ );
+ const refreshUser = useStore((state: AtuinState) => state.refreshUser);
+ const refreshCalendar = useStore(
+ (state: AtuinState) => state.refreshCalendar,
+ );
+ const refreshRunbooks = useStore(
+ (state: AtuinState) => state.refreshRunbooks,
+ );
const { toast } = useToast();
@@ -77,6 +147,9 @@ export default function Home() {
refreshHomeInfo();
refreshUser();
refreshCalendar();
+ refreshRunbooks();
+
+ console.log(homeInfo);
let setup = async () => {
let installed = await invoke("is_cli_installed");
@@ -125,49 +198,97 @@ export default function Home() {
}
return (
- <div className="w-full flex-1 flex-col p-4">
- <div className="p-10">
+ <div className="w-full flex-1 flex-col p-4 overflow-y-auto">
+ <div className="pl-10">
<Header name={user.username} />
+ </div>
+ <div className="p-10 grid grid-cols-4 gap-4">
+ <StatCard
+ name="Last Sync"
+ stat={
+ (homeInfo.lastSyncTime &&
+ formatRelative(homeInfo.lastSyncTime, new Date())) ||
+ "Never"
+ }
+ />
+ <StatCard
+ name="Total Commands"
+ stat={homeInfo.historyCount.toLocaleString()}
+ />
+ <StatCard
+ name="Total Runbooks"
+ stat={runbooks.length.toLocaleString()}
+ />
+ <StatCard
+ name="Other Records"
+ stat={homeInfo.recordCount - homeInfo.historyCount}
+ />
- <div className="pt-10">
- <Stats
- stats={[
- {
- name: "Last Sync",
- stat:
- (homeInfo.lastSyncTime &&
- formatRelative(homeInfo.lastSyncTime, new Date())) ||
- "Never",
- },
- {
- name: "Total history records",
- stat: homeInfo.historyCount.toLocaleString(),
- },
- {
- name: "Other records",
- stat: homeInfo.recordCount - homeInfo.historyCount,
- },
- ]}
- />
- </div>
+ <Card shadow="sm" className="col-span-3">
+ <CardHeader>
+ <h2 className="uppercase text-gray-500">Activity graph</h2>
+ </CardHeader>
+ <CardBody>
+ <ActivityCalendar
+ hideTotalCount
+ theme={explicitTheme}
+ data={calendar}
+ weekStart={weekStart as any}
+ renderBlock={(block, activity) =>
+ React.cloneElement(block, {
+ "data-tooltip-id": "react-tooltip",
+ "data-tooltip-html": `${activity.count} commands on ${activity.date}`,
+ })
+ }
+ />
+ <ReactTooltip id="react-tooltip" />
+ </CardBody>
+ </Card>
- <div className="pt-10 flex justify-around">
- <ActivityCalendar
- theme={explicitTheme}
- data={calendar}
- weekStart={weekStart as any}
- renderBlock={(block, activity) =>
- React.cloneElement(block, {
- "data-tooltip-id": "react-tooltip",
- "data-tooltip-html": `${activity.count} commands on ${activity.date}`,
- })
- }
- labels={{
- totalCount: "{{count}} history records in the last year",
- }}
- />
- <ReactTooltip id="react-tooltip" />
- </div>
+ <Card shadow="sm">
+ <CardHeader>
+ <h2 className="uppercase text-gray-500">Quick actions </h2>
+ </CardHeader>
+
+ <CardBody>
+ <Listbox variant="flat" aria-label="Quick actions">
+ <ListboxItem
+ key="new-runbook"
+ description="Create an executable runbook"
+ startContent={<Terminal />}
+ >
+ New runbook
+ </ListboxItem>
+ <ListboxItem
+ key="shell-history"
+ description="Search and explore shell history"
+ startContent={<Clock />}
+ >
+ Shell History
+ </ListboxItem>
+ </Listbox>
+ </CardBody>
+ </Card>
+
+ <Card shadow="sm" className="col-span-2">
+ <CardHeader>
+ <h2 className="uppercase text-gray-500">Recent commands</h2>
+ </CardHeader>
+ <CardBody>
+ {homeInfo.recentCommands?.map((i: ShellHistory) => {
+ return <HistoryRow compact h={i} />;
+ })}
+ </CardBody>
+ </Card>
+
+ <Card shadow="sm" className="col-span-2">
+ <CardHeader>
+ <h2 className="uppercase text-gray-500">Top commands</h2>
+ </CardHeader>
+ <CardBody>
+ <TopChart chartData={homeInfo.topCommands} />
+ </CardBody>
+ </Card>
</div>
</div>
);