From 720282c817b328f5a69c6bff36b46f7fc4377d50 Mon Sep 17 00:00:00 2001 From: Ryan Pandya Date: Fri, 6 Dec 2024 14:36:52 -0800 Subject: [PATCH] Create metrics and measurements. --- .direnv/flake-profile | 2 +- .direnv/flake-profile-1-link | 1 - .direnv/flake-profile-2-link | 1 + apps/web/app/dashboard/metrics/page.tsx | 9 + .../dashboard/categories/AddMetricDialog.tsx | 198 ++++ .../dashboard/categories/EditMetricDialog.tsx | 183 ++++ .../dashboard/categories/MetricsView.tsx | 86 ++ .../components/dashboard/days/DayMetrics.tsx | 17 + .../web/components/dashboard/days/DayView.tsx | 19 +- .../components/dashboard/sidebar/Sidebar.tsx | 8 +- flake.nix | 2 + packages/db/migrations/0001_chemical_toro.sql | 28 + .../db/migrations/meta/0001_snapshot.json | 955 ++++++++++++++++++ packages/db/migrations/meta/_journal.json | 7 + packages/db/schema.ts | 74 +- packages/shared/types/metrics.ts | 20 + packages/trpc/routers/_app.ts | 2 + packages/trpc/routers/metrics.ts | 84 ++ scripts/backup_days_hours.sh | 7 + 19 files changed, 1690 insertions(+), 13 deletions(-) delete mode 120000 .direnv/flake-profile-1-link create mode 120000 .direnv/flake-profile-2-link create mode 100644 apps/web/app/dashboard/metrics/page.tsx create mode 100644 apps/web/components/dashboard/categories/AddMetricDialog.tsx create mode 100644 apps/web/components/dashboard/categories/EditMetricDialog.tsx create mode 100644 apps/web/components/dashboard/categories/MetricsView.tsx create mode 100644 apps/web/components/dashboard/days/DayMetrics.tsx create mode 100644 packages/db/migrations/0001_chemical_toro.sql create mode 100644 packages/db/migrations/meta/0001_snapshot.json create mode 100644 packages/shared/types/metrics.ts create mode 100644 packages/trpc/routers/metrics.ts create mode 100755 scripts/backup_days_hours.sh diff --git a/.direnv/flake-profile b/.direnv/flake-profile index 0c05709..c7ae5b7 120000 --- a/.direnv/flake-profile +++ b/.direnv/flake-profile @@ -1 +1 @@ -flake-profile-1-link \ No newline at end of file +flake-profile-2-link \ No newline at end of file diff --git a/.direnv/flake-profile-1-link b/.direnv/flake-profile-1-link deleted file mode 120000 index c64a019..0000000 --- a/.direnv/flake-profile-1-link +++ /dev/null @@ -1 +0,0 @@ -/nix/store/pd0dcksai0adl5a0xpprm2wb02dy6jry-nix-shell-env \ No newline at end of file diff --git a/.direnv/flake-profile-2-link b/.direnv/flake-profile-2-link new file mode 120000 index 0000000..d6e9d0a --- /dev/null +++ b/.direnv/flake-profile-2-link @@ -0,0 +1 @@ +/nix/store/w46qqqb484a76v5k0lzv524yp9s89vly-nix-shell-env \ No newline at end of file diff --git a/apps/web/app/dashboard/metrics/page.tsx b/apps/web/app/dashboard/metrics/page.tsx new file mode 100644 index 0000000..fe20cf8 --- /dev/null +++ b/apps/web/app/dashboard/metrics/page.tsx @@ -0,0 +1,9 @@ +import MetricsView from "@/components/dashboard/categories/MetricsView"; + +export default async function MetricsPage() { + return ( + <> + + + ); +} diff --git a/apps/web/components/dashboard/categories/AddMetricDialog.tsx b/apps/web/components/dashboard/categories/AddMetricDialog.tsx new file mode 100644 index 0000000..403aee9 --- /dev/null +++ b/apps/web/components/dashboard/categories/AddMetricDialog.tsx @@ -0,0 +1,198 @@ +import { useEffect, useState } from "react"; +import { ActionButton } from "@/components/ui/action-button"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { toast } from "@/components/ui/use-toast"; +import { api } from "@/lib/trpc"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { TRPCClientError } from "@trpc/client"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { Textarea } from "@/components/ui/textarea"; +import { zMetricSchema } from "@lifetracker/shared/types/metrics"; + +type CreateMetricSchema = z.infer; + +export default function AddMetricDialog({ + initialMetric = undefined, + children, +}: { + initialMetric?: CreateMetricSchema; + children?: React.ReactNode; +}) { + const apiUtils = api.useUtils(); + const [isOpen, onOpenChange] = useState(false); + const form = useForm({ + resolver: zodResolver(zMetricSchema), + defaultValues: initialMetric + }); + const handleSuccess = (message: string) => { + toast({ + description: message, + }); + apiUtils.metrics.list.invalidate(); + onOpenChange(false); + }; + + const handleError = (error: any, defaultMessage: string) => { + if (error instanceof TRPCClientError) { + toast({ + variant: "destructive", + description: error.message, + }); + } else { + toast({ + variant: "destructive", + description: defaultMessage, + }); + } + }; + + const { mutate, isPending } = initialMetric + ? api.metrics.update.useMutation({ + onSuccess: () => handleSuccess("Metric updated successfully"), + onError: (error) => handleError(error, "Failed to update metric"), + }) + : api.metrics.create.useMutation({ + onSuccess: () => handleSuccess("New metric created successfully"), + onError: (error) => handleError(error, "Failed to create metric"), + }); + useEffect(() => { + if (!isOpen) { + form.reset(); + } + }, [isOpen, form]); + + return ( + + {children} + + + Track a New Metric + +
+ mutate(val))}> +
+
+ ( + + + + + + + )} + /> + + ( + + + + + + + )} + /> + {/* Select field with "timeseries" and "count" */} + + ( + + + + + )} + /> + +
+ ( + + {/* Description */} + +