diff --git a/apps/web/app/api/v1/measurements/route.ts b/apps/web/app/api/v1/measurements/route.ts index f6d5825..0b13df3 100644 --- a/apps/web/app/api/v1/measurements/route.ts +++ b/apps/web/app/api/v1/measurements/route.ts @@ -21,8 +21,7 @@ export const POST = (req: NextRequest) => // })).id; const hour = await api.hours.get({ - dateQuery: datetime.format("iso-short"), - time: datetime.hour(), + dateQuery: spacetime(datetime).startOf("hour").format("iso"), }); const obj = { diff --git a/apps/web/components/dashboard/hours/EditableHour.tsx b/apps/web/components/dashboard/hours/EditableHour.tsx index dd0dd8f..fa363bf 100644 --- a/apps/web/components/dashboard/hours/EditableHour.tsx +++ b/apps/web/components/dashboard/hours/EditableHour.tsx @@ -22,7 +22,7 @@ import { ZMetric } from "@lifetracker/shared/types/metrics"; import { Badge } from "@/components/ui/badge"; import { Icon } from "@/components/ui/icon"; import { titleCase } from "title-case"; -import { useDecrementCount } from "@lifetracker/shared-react/hooks/measurements"; +import { useDecrementCount, useDeleteMeasurement } from "@lifetracker/shared-react/hooks/measurements"; import { useTimezone } from "@/lib/userLocalSettings/client"; @@ -98,6 +98,26 @@ export default function EditableHour({ } }); + const { mutate: deleteMeasurement } = useDeleteMeasurement({ + onSuccess: (_res, req) => { + const newHour = { + ...hour, + measurements: hour.measurements.filter(m => m.metricId !== req.metricId), + }; + setHour(newHour); + reload(newHour); + toast({ + description: "Measurement deleted!", + }); + }, + onError: (error) => { + toast({ + description: error.message, + + }); + }, + }); + const tzOffset = spacetime.now(useTimezone()).offset() / 60; @@ -177,7 +197,10 @@ export default function EditableHour({ ( { + deleteMeasurement({ metricId: m.metricId, hourId: hour.id }); + }} />) : Array.from({ length: m.value }).map((_, index) =>
{ @@ -208,6 +231,6 @@ export default function EditableHour({
: ""} - + ); } \ No newline at end of file diff --git a/apps/web/components/dashboard/hours/HourMeasurementsDialog.tsx b/apps/web/components/dashboard/hours/HourMeasurementsDialog.tsx index c4686ad..b5f3e71 100644 --- a/apps/web/components/dashboard/hours/HourMeasurementsDialog.tsx +++ b/apps/web/components/dashboard/hours/HourMeasurementsDialog.tsx @@ -13,7 +13,7 @@ import { Icon } from "@/components/ui/icon"; import { titleCase } from "title-case"; import { Dialog, DialogClose, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { Trash, X } from "lucide-react"; -import { useDecrementCount, useIncrementCount, useSetValue, } from "@lifetracker/shared-react/hooks/measurements"; +import { useDecrementCount, useDeleteMeasurement, useIncrementCount, useSetValue, } from "@lifetracker/shared-react/hooks/measurements"; import { Separator } from "@radix-ui/react-dropdown-menu"; import { EditableText } from "../EditableText"; import spacetime from "spacetime"; @@ -105,6 +105,28 @@ export default function HourMeasurementsDialog({ }, }); + const { mutate: deleteMeasurement } = useDeleteMeasurement({ + onSuccess: (_res, req) => { + const newHour = { + ...hour, + measurements: hour.measurements.filter(m => m.metricId !== req.metricId), + }; + setHour(newHour); + reload(newHour); + toast({ + description: "Measurement deleted!", + }); + }, + onError: (error) => { + toast({ + description: error.message, + + }); + }, + }); + + + const { mutate: decrement } = useDecrementCount({ onSuccess: (res, req) => { const oldMeasurementIndex = hour.measurements.findIndex(m => m.metricId === req.metricId); @@ -195,7 +217,7 @@ export default function HourMeasurementsDialog({ }); }} /> { - decrement({ metricId: metric!.id!, hourId: hour.id! }); + deleteMeasurement({ metricId: metric!.id!, hourId: hour.id! }); }} /> diff --git a/packages/shared-react/hooks/measurements.ts b/packages/shared-react/hooks/measurements.ts index 20440b4..3b36c26 100644 --- a/packages/shared-react/hooks/measurements.ts +++ b/packages/shared-react/hooks/measurements.ts @@ -35,4 +35,16 @@ export function useDecrementCount( return opts[0]?.onSuccess?.(res, req, meta); }, }); +} + +export function useDeleteMeasurement( + ...opts: Parameters +) { + const apiUtils = api.useUtils(); + return api.measurements.delete.useMutation({ + ...opts[0], + onSuccess: (res, req, meta) => { + return opts[0]?.onSuccess?.(res, req, meta); + }, + }); } \ No newline at end of file diff --git a/packages/trpc/routers/measurements.ts b/packages/trpc/routers/measurements.ts index 42a025e..67715a5 100644 --- a/packages/trpc/routers/measurements.ts +++ b/packages/trpc/routers/measurements.ts @@ -168,6 +168,30 @@ export const measurementsAppRouter = router({ }); } }), + delete: authedProcedure + .input(z.object({ metricId: z.string(), hourId: z.string() })) + .mutation(async ({ input, ctx }) => { + const metric = await ctx.db.select().from(metrics).where(eq(metrics.id, input.metricId)); + if (!metric[0]) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Metric not found", + }); + } + // Check if there is a measurement for this metric in this hour, if so, delete it, if not, throw an error + const existingMeasurement = await ctx.db.select().from(measurements).where(and( + eq(measurements.metricId, input.metricId), + eq(measurements.hourId, input.hourId), + )); + if (existingMeasurement[0]) { + await ctx.db.delete(measurements).where(eq(measurements.id, existingMeasurement[0].id)); + } else { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Measurement not found", + }); + } + }), timeSinceLastMeasurement: authedProcedure .input(z.object({ metricId: z.string() })) .output(z.number()) diff --git a/todo b/todo new file mode 100644 index 0000000..4683829 --- /dev/null +++ b/todo @@ -0,0 +1,2 @@ +# Database +[x] Migrate to Postgres