diff --git a/apps/web/components/dashboard/analytics/Timeseries.tsx b/apps/web/components/dashboard/analytics/Timeseries.tsx index 9412b1b..f50facb 100644 --- a/apps/web/components/dashboard/analytics/Timeseries.tsx +++ b/apps/web/components/dashboard/analytics/Timeseries.tsx @@ -2,6 +2,22 @@ import { LineChart, Line, YAxis, XAxis, CartesianGrid, Tooltip, Area, Label } from 'recharts'; import spacetime from 'spacetime'; import regression from 'regression'; +import { PureComponent } from 'react'; + +class CustomizedAxisTick extends PureComponent { + render() { + const { x, y, stroke, payload } = this.props; + if (spacetime(payload.value).day() == 1) { + return ( + + + {spacetime(payload.value).format('{date}')} + + + ); + } + } +} interface DataPoint { id: string; // measurement.id @@ -22,12 +38,8 @@ function polynomialTrendline(data: DataPoint[], degree: number) { } function formatXAxis(tickItem: [number, number]) { - const month = spacetime(tickItem).format('{month-short}'); - if (spacetime(tickItem).day() === 1) { - return spacetime(tickItem).format('{month-short} {date}'); - } else { - return spacetime(tickItem).format('{date}'); - } + + return spacetime(tickItem).format('{date}'); } function yAxisTicks(data: DataPoint[]) { @@ -38,6 +50,19 @@ function yAxisTicks(data: DataPoint[]) { return Array.from({ length: (upperBound - lowerBound) / 2 + 1 }, (_, i) => lowerBound + 2 * i); } +function xAxisTicks(data: DataPoint[]) { + const bounds = data.sort().filter((_v, i, a) => i === 0 || i === a.length - 1).map(dp => spacetime(dp.datetime)); + // Create array from bounds[0] to bounds[1] showing only Mondays + const ticks = []; + let tick = bounds[0].startOf('week').add(1, 'day'); + while (tick.isBefore(bounds[1])) { + ticks.push(tick.format('{date}')); + tick = tick.add(2, 'day'); + } + console.log(ticks); + return ticks; +} + export default function TimeseriesChart({ data, timeseriesName }: { data: DataPoint[], timeseriesName: string }) { const unit = data[0] ? data[0].unit : ''; @@ -63,8 +88,18 @@ export default function TimeseriesChart({ data, timeseriesName }: { data: DataPo formatter={(value, name, props) => [value, name === 'smoothed' ? 'Trend' : timeseriesName]} contentStyle={{ backgroundColor: 'hsl(var(--border))', color: 'black' }} /> - - i === 0 || i === yAxisTicks(data).length - 1)} + } + interval={0} + // tickFormatter={formatXAxis} + /> + i === 0 || i === yAxisTicks(data).length - 1)} allowDecimals={false} ticks={yAxisTicks(data)} /> diff --git a/apps/web/components/dashboard/categories/AddCategoryDialog.tsx b/apps/web/components/dashboard/categories/AddCategoryDialog.tsx index 38b55ce..5d10cb7 100644 --- a/apps/web/components/dashboard/categories/AddCategoryDialog.tsx +++ b/apps/web/components/dashboard/categories/AddCategoryDialog.tsx @@ -33,10 +33,10 @@ import { TRPCClientError } from "@trpc/client"; import { useForm } from "react-hook-form"; import { z } from "zod"; -import { zCategorySchema } from "@lifetracker/shared/types/categories"; +import { zCategorySchema, zCreateCategorySchema } from "@lifetracker/shared/types/categories"; import { Textarea } from "@/components/ui/textarea"; -type CreateCategorySchema = z.infer; +type CreateCategorySchema = z.infer; export default function AddCategoryDialog({ children, @@ -46,9 +46,9 @@ export default function AddCategoryDialog({ const apiUtils = api.useUtils(); const [isOpen, onOpenChange] = useState(false); const form = useForm({ - resolver: zodResolver(zCategorySchema), + resolver: zodResolver(zCreateCategorySchema), }); - const { mutate, isPending } = api.categories.createCategory.useMutation({ + const { mutate: createCategory, isPending } = api.categories.create.useMutation({ onSuccess: () => { toast({ description: "Category created successfully", @@ -84,7 +84,7 @@ export default function AddCategoryDialog({ Create Category
- mutate(val))}> + createCategory(val))}>
( {/* Color */} diff --git a/packages/shared/types/categories.ts b/packages/shared/types/categories.ts index 3588ee5..5e2eb20 100644 --- a/packages/shared/types/categories.ts +++ b/packages/shared/types/categories.ts @@ -14,7 +14,7 @@ export const zCreateCategorySchema = z.object({ code: z.coerce.number(), name: z.string(), description: z.string().optional(), - color: z.string(), + colorName: z.string(), parentId: z.string().optional(), }); export type ZCreateCategories = z.infer; diff --git a/packages/shared/types/colors.ts b/packages/shared/types/colors.ts index 7748999..5fcd7d5 100644 --- a/packages/shared/types/colors.ts +++ b/packages/shared/types/colors.ts @@ -3,7 +3,7 @@ import { z } from "zod"; export const zColorSchema = z.object({ name: z.string(), hexcode: z.string(), - inverse: z.string().optional(), - id: z.string().optional(), + inverse: z.string().nullish(), + id: z.string().nullish(), }); export type ZColor = z.infer; diff --git a/packages/trpc/routers/categories.ts b/packages/trpc/routers/categories.ts index 8f68ee6..8418154 100644 --- a/packages/trpc/routers/categories.ts +++ b/packages/trpc/routers/categories.ts @@ -1,5 +1,5 @@ import { experimental_trpcMiddleware, TRPCError } from "@trpc/server"; -import { and, desc, eq, inArray, notExists } from "drizzle-orm"; +import { and, asc, desc, eq, inArray, notExists } from "drizzle-orm"; import { z } from "zod"; import { DatabaseError } from "@lifetracker/db"; @@ -102,11 +102,15 @@ export const categoriesAppRouter = router({ .leftJoin(colors, eq(categories.colorId, colors.id)) ; + const categoryParents = dbCategories + .filter((category) => category.code == parseInt(category.code) && category.code < 11) + .sort((a, b) => a.code - b.code); + const sortedCategories = categoryParents.map((parent) => { + return dbCategories + .filter((child) => child.code.toString().startsWith(parent.code.toString())) + }); return { - categories: dbCategories.map(({ color, ...category }) => ({ - ...category, - color, - })), + categories: sortedCategories.flat(), }; }), get: authedProcedure @@ -173,7 +177,7 @@ export const categoriesAppRouter = router({ return category; }), create: authedProcedure - .input(zCategorySchema) + .input(zCreateCategorySchema) .output( z.object({ id: z.string(), @@ -184,7 +188,13 @@ export const categoriesAppRouter = router({ }), ) .mutation(async ({ input, ctx }) => { - return createCategory(input, ctx); + const color = await ctx.db.select().from(colors).where( + and( + eq(colors.name, input.colorName), + eq(colors.userId, ctx.user.id), + ) + ); + return createCategory({ ...input, color: color[0] }, ctx); }), update: authedProcedure .input(zUpdateCategoryRequestSchema)