diff options
| author | Ellie Huxtable <ellie@atuin.sh> | 2024-07-23 13:18:54 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-23 13:18:54 +0100 |
| commit | f8c963c7d668fc57680f25413f20bc207d4bf64a (patch) | |
| tree | c7f952ddc0220cded24f5447d03b3bff46fb1d45 /ui/src/pages/Home.tsx | |
| parent | fix(themes): Restore default theme, refactor (#2294) (diff) | |
| download | atuin-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/Home.tsx')
| -rw-r--r-- | ui/src/pages/Home.tsx | 253 |
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> ); |
