Day hours are timezone-adjusted and taken from adjacent days as needed
This commit is contained in:
parent
aaf351d328
commit
b2e2770e91
@ -17,6 +17,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@date-fns/tz": "^1.2.0",
|
"@date-fns/tz": "^1.2.0",
|
||||||
|
"@date-fns/utc": "^2.1.0",
|
||||||
"@lifetracker/db": "workspace:*",
|
"@lifetracker/db": "workspace:*",
|
||||||
"@lifetracker/shared": "workspace:^",
|
"@lifetracker/shared": "workspace:^",
|
||||||
"@lifetracker/trpc": "workspace:^",
|
"@lifetracker/trpc": "workspace:^",
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { Command } from "@commander-js/extra-typings";
|
|||||||
import { getBorderCharacters, table } from "table";
|
import { getBorderCharacters, table } from "table";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { TZDate } from "@date-fns/tz";
|
import { TZDate } from "@date-fns/tz";
|
||||||
|
import { utc } from "@date-fns/utc";
|
||||||
import { getHourFromTime, getTimeFromHour } from "@lifetracker/shared/utils/hours";
|
import { getHourFromTime, getTimeFromHour } from "@lifetracker/shared/utils/hours";
|
||||||
|
|
||||||
function moodToStars(mood: number) {
|
function moodToStars(mood: number) {
|
||||||
@ -41,13 +42,14 @@ export const daysCmd = new Command()
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
const moodStr = moodToStars(day.mood);
|
const moodStr = moodToStars(day.mood);
|
||||||
const dateStr = format(day.date, "EEEE, MMMM do");
|
const dateStr = format(day.date, "EEEE, MMMM do", { in: utc });
|
||||||
const data: string[][] = [[dateStr, '', moodStr], [day.comment ?? "No comment", '', ''],
|
const data: string[][] = [[dateStr, '', moodStr], [day.comment ?? "No comment", '', ''],
|
||||||
["Time", "Category", "Comment"]
|
["Time", "Category", "Comment"]
|
||||||
];
|
];
|
||||||
|
|
||||||
day.hours.forEach((h) => {
|
day.hours.forEach((h, i) => {
|
||||||
data.push([getHourFromTime(h.time), h.categoryName ?? "--", h.comment ?? ""]);
|
data.push([getHourFromTime(h.time),
|
||||||
|
h.categoryName ?? "--", h.comment ?? ""]);
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(table(data, {
|
console.log(table(data, {
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { notFound } from "next/navigation";
|
|||||||
import { api } from "@/server/api/client";
|
import { api } from "@/server/api/client";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import DayView from "@/components/dashboard/days/DayView";
|
import DayView from "@/components/dashboard/days/DayView";
|
||||||
import LoadingSpinner from "@/components/ui/spinner";
|
|
||||||
|
|
||||||
export default async function DayPage({
|
export default async function DayPage({
|
||||||
params,
|
params,
|
||||||
@ -10,9 +9,10 @@ export default async function DayPage({
|
|||||||
params: { dateQuery: string };
|
params: { dateQuery: string };
|
||||||
}) {
|
}) {
|
||||||
let day;
|
let day;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
day = await api.days.get({ dateQuery: params.dateQuery });
|
day = await api.days.get({
|
||||||
|
dateQuery: params.dateQuery,
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof TRPCError) {
|
if (e instanceof TRPCError) {
|
||||||
if (e.code == "NOT_FOUND") {
|
if (e.code == "NOT_FOUND") {
|
||||||
@ -23,11 +23,8 @@ export default async function DayPage({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
params.dateQuery === undefined ?
|
<DayView
|
||||||
<LoadingSpinner />
|
day={day}
|
||||||
:
|
/>
|
||||||
<DayView
|
|
||||||
day={day}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,31 +5,55 @@ import { getServerAuthSession } from "@/server/auth";
|
|||||||
import { ZDay } from "@lifetracker/shared/types/days";
|
import { ZDay } from "@lifetracker/shared/types/days";
|
||||||
import EditableDayComment from "./EditableDayComment";
|
import EditableDayComment from "./EditableDayComment";
|
||||||
import { MoodStars } from "./MoodStars";
|
import { MoodStars } from "./MoodStars";
|
||||||
import { format } from "date-fns";
|
import { format, addDays } from "date-fns";
|
||||||
|
import { ButtonWithTooltip } from "@/components/ui/button";
|
||||||
|
import { router } from "next/navigation";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { ArrowLeftSquare, ArrowRightSquare } from "lucide-react";
|
||||||
|
import { UTCDate, utc } from "@date-fns/utc";
|
||||||
export default async function DayView({
|
export default async function DayView({
|
||||||
day,
|
day,
|
||||||
header,
|
|
||||||
showDivider,
|
|
||||||
showEditorCard = false,
|
|
||||||
}: {
|
}: {
|
||||||
day: ZDay;
|
day: ZDay;
|
||||||
header?: React.ReactNode;
|
|
||||||
showDivider?: boolean;
|
|
||||||
showEditorCard?: boolean;
|
|
||||||
}) {
|
}) {
|
||||||
const session = await getServerAuthSession();
|
const session = await getServerAuthSession();
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect("/");
|
redirect("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const prevDay = format(addDays(day.date, -1), "yyyy-MM-dd");
|
||||||
|
const nextDay = format(addDays(day.date, 1), "yyyy-MM-dd");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
|
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-2xl">
|
<div className="flex">
|
||||||
{format(day.date, "EEEE, MMMM do")}
|
<Link
|
||||||
</span>
|
href={`/dashboard/day/${prevDay}`}
|
||||||
|
className={cn(
|
||||||
|
"flex-0 items-center rounded-[inherit] px-3 py-2",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex w-full justify-between">
|
||||||
|
<ArrowLeftSquare size={18} />
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
<span className="text-2xl flex-1">
|
||||||
|
{format(day.date, "EEEE, MMMM do", { in: utc })}
|
||||||
|
</span>
|
||||||
|
<Link
|
||||||
|
href={`/dashboard/day/${nextDay}`}
|
||||||
|
className={cn(
|
||||||
|
"flex-0 items-center rounded-[inherit] px-3 py-2",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex w-full justify-between">
|
||||||
|
<ArrowRightSquare size={18} />
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<MoodStars
|
<MoodStars
|
||||||
day={day}
|
day={day}
|
||||||
@ -45,7 +69,13 @@ export default async function DayView({
|
|||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{day.hours.map((hour) => (
|
||||||
|
<li key={hour.time}>
|
||||||
|
{hour.time}: {hour.categoryName} {hour.comment}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -41,7 +41,6 @@ export default function EditableDayComment({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onError: (e) => {
|
onError: (e) => {
|
||||||
console.log(e);
|
|
||||||
toast({
|
toast({
|
||||||
description: e.message,
|
description: e.message,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export default async function Header() {
|
|||||||
return (
|
return (
|
||||||
<header className="sticky left-0 right-0 top-0 z-50 flex h-16 items-center justify-between overflow-x-auto overflow-y-hidden bg-background p-4 shadow">
|
<header className="sticky left-0 right-0 top-0 z-50 flex h-16 items-center justify-between overflow-x-auto overflow-y-hidden bg-background p-4 shadow">
|
||||||
<div className="hidden items-center sm:flex">
|
<div className="hidden items-center sm:flex">
|
||||||
<Link href={"/dashboard/bookmarks"} className="w-56">
|
<Link href={"/dashboard/day/today"} className="w-56">
|
||||||
<HoarderLogo height={20} gap="8px" />
|
<HoarderLogo height={20} gap="8px" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export default async function Sidebar() {
|
|||||||
{
|
{
|
||||||
name: "Home",
|
name: "Home",
|
||||||
icon: <Home size={18} />,
|
icon: <Home size={18} />,
|
||||||
path: "/dashboard/today",
|
path: "/dashboard/day/today",
|
||||||
},
|
},
|
||||||
...searchItem,
|
...searchItem,
|
||||||
{
|
{
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export const settingsSidebarItems: {
|
|||||||
{
|
{
|
||||||
name: "Back To App",
|
name: "Back To App",
|
||||||
icon: <ArrowLeft size={18} />,
|
icon: <ArrowLeft size={18} />,
|
||||||
path: "/dashboard/today",
|
path: "/dashboard/day/today",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "User Info",
|
name: "User Info",
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/drizzle-adapter": "^1.4.2",
|
"@auth/drizzle-adapter": "^1.4.2",
|
||||||
"@date-fns/tz": "^1.2.0",
|
"@date-fns/tz": "^1.2.0",
|
||||||
|
"@date-fns/utc": "^2.1.0",
|
||||||
"@emoji-mart/data": "^1.1.2",
|
"@emoji-mart/data": "^1.1.2",
|
||||||
"@emoji-mart/react": "^1.1.1",
|
"@emoji-mart/react": "^1.1.1",
|
||||||
"@hookform/resolvers": "^3.3.4",
|
"@hookform/resolvers": "^3.3.4",
|
||||||
|
|||||||
@ -105,6 +105,5 @@ CREATE UNIQUE INDEX `apiKey_name_userId_unique` ON `apiKey` (`name`,`userId`);--
|
|||||||
CREATE UNIQUE INDEX `category_userId_code_unique` ON `category` (`userId`,`code`);--> statement-breakpoint
|
CREATE UNIQUE INDEX `category_userId_code_unique` ON `category` (`userId`,`code`);--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX `color_userId_name_unique` ON `color` (`userId`,`name`);--> statement-breakpoint
|
CREATE UNIQUE INDEX `color_userId_name_unique` ON `color` (`userId`,`name`);--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX `day_date_unique` ON `day` (`date`);--> statement-breakpoint
|
CREATE UNIQUE INDEX `day_date_unique` ON `day` (`date`);--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX `hour_time_unique` ON `hour` (`time`);--> statement-breakpoint
|
|
||||||
CREATE UNIQUE INDEX `hour_dayId_time_unique` ON `hour` (`dayId`,`time`);--> statement-breakpoint
|
CREATE UNIQUE INDEX `hour_dayId_time_unique` ON `hour` (`dayId`,`time`);--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);
|
CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);
|
||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"dialect": "sqlite",
|
"dialect": "sqlite",
|
||||||
"id": "170be9e2-c822-4d3a-a0b1-18c8468f1c5d",
|
"id": "ebffb4c7-5ecf-46d0-93c6-68f8e48a9fc4",
|
||||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
"tables": {
|
"tables": {
|
||||||
"account": {
|
"account": {
|
||||||
@ -524,13 +524,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexes": {
|
"indexes": {
|
||||||
"hour_time_unique": {
|
|
||||||
"name": "hour_time_unique",
|
|
||||||
"columns": [
|
|
||||||
"time"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
|
||||||
},
|
|
||||||
"hour_dayId_time_unique": {
|
"hour_dayId_time_unique": {
|
||||||
"name": "hour_dayId_time_unique",
|
"name": "hour_dayId_time_unique",
|
||||||
"columns": [
|
"columns": [
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
{
|
{
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1732706352404,
|
"when": 1732766704666,
|
||||||
"tag": "0000_exotic_dakota_north",
|
"tag": "0000_cold_golden_guardian",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -21,7 +21,6 @@ function createdAtField() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function calcInverseColor(hexcode: string): string {
|
export function calcInverseColor(hexcode: string): string {
|
||||||
console.log(hexcode);
|
|
||||||
const hex = hexcode.replace("#", "");
|
const hex = hexcode.replace("#", "");
|
||||||
const r = parseInt(hex.substr(0, 2), 16);
|
const r = parseInt(hex.substr(0, 2), 16);
|
||||||
const g = parseInt(hex.substr(2, 2), 16);
|
const g = parseInt(hex.substr(2, 2), 16);
|
||||||
@ -148,7 +147,7 @@ export const hours = sqliteTable(
|
|||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
comment: text("comment"),
|
comment: text("comment"),
|
||||||
time: integer("time").unique(),
|
time: integer("time"),
|
||||||
dayId: text("dayId").notNull().references(() => days.id, { onDelete: "cascade" }),
|
dayId: text("dayId").notNull().references(() => days.id, { onDelete: "cascade" }),
|
||||||
categoryId: text("categoryId").references(() => categories.id),
|
categoryId: text("categoryId").references(() => categories.id),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { api } from "../trpc";
|
|||||||
export function useUpdateUserTimezone(
|
export function useUpdateUserTimezone(
|
||||||
...opts: Parameters<typeof api.users.changeTimezone.useMutation>
|
...opts: Parameters<typeof api.users.changeTimezone.useMutation>
|
||||||
) {
|
) {
|
||||||
const apiUtils = api.useUtils();
|
|
||||||
return api.users.changeTimezone.useMutation({
|
return api.users.changeTimezone.useMutation({
|
||||||
...opts[0],
|
...opts[0],
|
||||||
onSuccess: (res, req, meta) => {
|
onSuccess: (res, req, meta) => {
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@date-fns/tz": "^1.2.0",
|
"@date-fns/tz": "^1.2.0",
|
||||||
|
"@date-fns/utc": "^2.1.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
|
|||||||
@ -6,10 +6,10 @@ export const zHourSchema = z.object({
|
|||||||
dayId: z.string(),
|
dayId: z.string(),
|
||||||
date: z.string().optional(),
|
date: z.string().optional(),
|
||||||
time: z.number(),
|
time: z.number(),
|
||||||
categoryCode: z.coerce.number().nullable(),
|
categoryCode: z.coerce.number().nullish(),
|
||||||
categoryId: z.string().nullable(),
|
categoryId: z.string().nullish(),
|
||||||
categoryName: z.string().nullable(),
|
categoryName: z.string().nullish(),
|
||||||
comment: z.string().nullable(),
|
comment: z.string().nullish(),
|
||||||
});
|
});
|
||||||
export type ZHour = z.infer<typeof zHourSchema>;
|
export type ZHour = z.infer<typeof zHourSchema>;
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,32 @@
|
|||||||
import { format } from "date-fns";
|
import { format, addHours } from "date-fns";
|
||||||
import { TZDate } from "@date-fns/tz";
|
import { TZDate } from "@date-fns/tz";
|
||||||
|
import { UTCDate, utc } from "@date-fns/utc";
|
||||||
|
|
||||||
export function dateFromInput(input: { dateQuery: string, timezone: string }) {
|
export function dateFromInput(input: { dateQuery: string, timezone: string }) {
|
||||||
let t: TZDate;
|
let t: string;
|
||||||
if (input.dateQuery == "today") {
|
if (input.dateQuery == "today") {
|
||||||
t = TZDate.tz(input.timezone);
|
t = new Date();
|
||||||
|
return format(t, "yyyy-MM-dd", { in: input.timezone });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
t = new TZDate(input.dateQuery, input.timezone);
|
t = new UTCDate(input.dateQuery);
|
||||||
|
return format(t, "yyyy-MM-dd", { in: utc });
|
||||||
}
|
}
|
||||||
return format(t, "yyyy-MM-dd") + "T00:00:00";
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateHour(d, t) {
|
||||||
|
const dt: TZDate = addHours(d, t);
|
||||||
|
return {
|
||||||
|
date: format(dt, 'yyyy-MM-dd'),
|
||||||
|
time: parseInt(format(dt, 'H')),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hoursListInTimezone(input: { dateQuery: string, timezone: string }) {
|
||||||
|
const dateStr = dateFromInput(input);
|
||||||
|
const d = new TZDate(dateStr, input.timezone);
|
||||||
|
return Array.from({ length: 24 }, (_, t) =>
|
||||||
|
generateHour(d, t)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@ -27,7 +27,6 @@ async function createCategory(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
return ctx.db.transaction(async (trx) => {
|
return ctx.db.transaction(async (trx) => {
|
||||||
console.log("Creating a category", input);
|
|
||||||
try {
|
try {
|
||||||
const result = await trx
|
const result = await trx
|
||||||
.insert(categories)
|
.insert(categories)
|
||||||
@ -54,7 +53,6 @@ async function createCategory(
|
|||||||
};
|
};
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
|
||||||
if (e instanceof SqliteError) {
|
if (e instanceof SqliteError) {
|
||||||
if (e.code == "SQLITE_CONSTRAINT_UNIQUE") {
|
if (e.code == "SQLITE_CONSTRAINT_UNIQUE") {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
@ -196,8 +194,6 @@ export const categoriesAppRouter = router({
|
|||||||
category.parentId = categoryId;
|
category.parentId = categoryId;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(category);
|
|
||||||
|
|
||||||
return category;
|
return category;
|
||||||
}),
|
}),
|
||||||
create: authedProcedure
|
create: authedProcedure
|
||||||
|
|||||||
@ -2,16 +2,17 @@ import { experimental_trpcMiddleware, TRPCError } from "@trpc/server";
|
|||||||
import { and, desc, eq, inArray, notExists } from "drizzle-orm";
|
import { and, desc, eq, inArray, notExists } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { SqliteError } from "@lifetracker/db";
|
import { db, SqliteError } from "@lifetracker/db";
|
||||||
import { categories, days, hours, } from "@lifetracker/db/schema";
|
import { categories, days, hours, users } from "@lifetracker/db/schema";
|
||||||
import {
|
import {
|
||||||
zDaySchema, ZDay
|
zDaySchema, ZDay
|
||||||
} from "@lifetracker/shared/types/days";
|
} from "@lifetracker/shared/types/days";
|
||||||
import type { Context } from "../index";
|
import type { Context } from "../index";
|
||||||
import { authedProcedure, router } from "../index";
|
import { authedProcedure, router } from "../index";
|
||||||
import { dateFromInput } from "@lifetracker/shared/utils/days";
|
import { dateFromInput, hoursListInTimezone } from "@lifetracker/shared/utils/days";
|
||||||
import { format } from "date-fns";
|
import { closestIndexTo, format } from "date-fns";
|
||||||
import { TZDate } from "@date-fns/tz";
|
import { TZDate } from "@date-fns/tz";
|
||||||
|
import { hoursAppRouter } from "./hours";
|
||||||
|
|
||||||
async function createDay(date: string, ctx: Context) {
|
async function createDay(date: string, ctx: Context) {
|
||||||
return await ctx.db.transaction(async (trx) => {
|
return await ctx.db.transaction(async (trx) => {
|
||||||
@ -30,21 +31,8 @@ async function createDay(date: string, ctx: Context) {
|
|||||||
comment: days.comment,
|
comment: days.comment,
|
||||||
});
|
});
|
||||||
|
|
||||||
const dayId = dayRes[0].id;
|
|
||||||
|
|
||||||
// Generate 24 "hour" objects
|
|
||||||
const hoursData = Array.from({ length: 24 }, (_, hour) => ({
|
|
||||||
userId: ctx.user!.id,
|
|
||||||
dayId: dayId,
|
|
||||||
time: hour
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Insert the "hour" objects
|
|
||||||
await trx.insert(hours).values(hoursData);
|
|
||||||
|
|
||||||
return dayRes;
|
return dayRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
|
||||||
if (e instanceof SqliteError) {
|
if (e instanceof SqliteError) {
|
||||||
if (e.code == "SQLITE_CONSTRAINT_UNIQUE") {
|
if (e.code == "SQLITE_CONSTRAINT_UNIQUE") {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
@ -61,6 +49,59 @@ async function createDay(date: string, ctx: Context) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createHour(day, time, ctx,) {
|
||||||
|
const newHour = (await ctx.db.insert(hours).values({
|
||||||
|
dayId: day.id,
|
||||||
|
time: time,
|
||||||
|
userId: ctx.user!.id,
|
||||||
|
}).returning());
|
||||||
|
console.log(newHour);
|
||||||
|
return newHour[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTimezone(ctx: Context) {
|
||||||
|
const dbTimezone = await ctx.db.select({
|
||||||
|
timezone: users.timezone
|
||||||
|
}).from(users).where(eq(users.id, ctx.user!.id));
|
||||||
|
|
||||||
|
return dbTimezone[0].timezone as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDay(input: { dateQuery: string }, ctx: Context, date: string) {
|
||||||
|
const dayRes = await ctx.db.select({
|
||||||
|
id: days.id,
|
||||||
|
date: days.date,
|
||||||
|
mood: days.mood,
|
||||||
|
comment: days.comment,
|
||||||
|
})
|
||||||
|
.from(days)
|
||||||
|
.where(eq(days.date, date));
|
||||||
|
|
||||||
|
const day = dayRes.length == 0
|
||||||
|
? (await createDay(date, ctx))[0]
|
||||||
|
: (dayRes[0]);
|
||||||
|
|
||||||
|
const dayHours = await Promise.all(
|
||||||
|
Array.from({ length: 24 }).map(async function (_, i) {
|
||||||
|
const existing = await ctx.db.select({
|
||||||
|
dayId: hours.dayId,
|
||||||
|
time: hours.time,
|
||||||
|
userId: hours.userId,
|
||||||
|
}).from(hours)
|
||||||
|
.where(and(
|
||||||
|
eq(hours.userId, ctx.user!.id),
|
||||||
|
eq(hours.dayId, day.id),
|
||||||
|
eq(hours.time, i)
|
||||||
|
))
|
||||||
|
return existing.length == 0 ? createHour(day, i, ctx) : existing[0];
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
hours: dayHours,
|
||||||
|
...day
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const daysAppRouter = router({
|
export const daysAppRouter = router({
|
||||||
get: authedProcedure
|
get: authedProcedure
|
||||||
.input(z.object({
|
.input(z.object({
|
||||||
@ -68,47 +109,48 @@ export const daysAppRouter = router({
|
|||||||
}))
|
}))
|
||||||
.output(zDaySchema)
|
.output(zDaySchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
const date = dateFromInput({ timezone: ctx.user.timezone, ...input });
|
|
||||||
console.log(ctx.user.timezone);
|
|
||||||
// Fetch the day data
|
|
||||||
let dayRes;
|
|
||||||
dayRes = await ctx.db
|
|
||||||
.select({
|
|
||||||
id: days.id,
|
|
||||||
date: days.date,
|
|
||||||
mood: days.mood,
|
|
||||||
comment: days.comment,
|
|
||||||
})
|
|
||||||
.from(days)
|
|
||||||
.where(eq(days.date, date));
|
|
||||||
|
|
||||||
if (dayRes.length === 0) {
|
const timezone = await getTimezone(ctx);
|
||||||
dayRes = await createDay(date, ctx);
|
const date = dateFromInput({
|
||||||
}
|
dateQuery: input.dateQuery,
|
||||||
|
timezone: timezone
|
||||||
|
});
|
||||||
|
|
||||||
const day = dayRes[0];
|
const allHours = hoursListInTimezone({
|
||||||
|
timezone,
|
||||||
|
...input
|
||||||
|
});
|
||||||
|
|
||||||
// Fetch the hours data for the corresponding dayId
|
const dayRange = [...new Set(allHours.map(({ date: date, time: _time }) => date))];
|
||||||
const hoursRes = await ctx.db
|
|
||||||
.select({
|
|
||||||
id: hours.id,
|
|
||||||
dayId: hours.dayId,
|
|
||||||
time: hours.time,
|
|
||||||
categoryId: hours.categoryId,
|
|
||||||
categoryCode: categories.code,
|
|
||||||
categoryName: categories.name,
|
|
||||||
comment: hours.comment,
|
|
||||||
})
|
|
||||||
.from(hours)
|
|
||||||
.where(eq(hours.dayId, day.id))
|
|
||||||
.leftJoin(categories, eq(categories.id, hours.categoryId))
|
|
||||||
|
|
||||||
// Combine the day and hours data
|
const allDayIds = await Promise.all(dayRange.map(async function (date) {
|
||||||
const result = {
|
const dayObj = await getDay(input, ctx, date);
|
||||||
...day,
|
return {
|
||||||
hours: hoursRes,
|
id: dayObj.id,
|
||||||
|
date: dayObj.date
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const dayHours = await Promise.all(allHours.map(async function ({ date, time }) {
|
||||||
|
const dayId = allDayIds.find((_value: { id, date }) => date == date)!.id;
|
||||||
|
|
||||||
|
|
||||||
|
const hourMatch = await ctx.db.select().from(hours)
|
||||||
|
.where(and(
|
||||||
|
eq(hours.time, time),
|
||||||
|
eq(hours.dayId, dayId)));
|
||||||
|
|
||||||
|
// console.log("Search values:: ", `d: ${date}, t: ${time}, dayId: ${dayId}`)
|
||||||
|
// console.log("hourMatch", hourMatch[0]);
|
||||||
|
|
||||||
|
return hourMatch;
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...await getDay(input, ctx, date),
|
||||||
|
hours: dayHours.flat(),
|
||||||
};
|
};
|
||||||
return result;
|
|
||||||
}),
|
}),
|
||||||
update: authedProcedure
|
update: authedProcedure
|
||||||
.input(
|
.input(
|
||||||
|
|||||||
@ -22,7 +22,6 @@ export const hoursAppRouter = router({
|
|||||||
.output(zHourSchema)
|
.output(zHourSchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
const date = dateFromInput(input);
|
const date = dateFromInput(input);
|
||||||
console.log(input);
|
|
||||||
const hourRes = await ctx.db
|
const hourRes = await ctx.db
|
||||||
.select({
|
.select({
|
||||||
id: hours.id,
|
id: hours.id,
|
||||||
@ -37,8 +36,6 @@ export const hoursAppRouter = router({
|
|||||||
.leftJoin(days, eq(days.id, hours.dayId)) // Ensure days table is joined first
|
.leftJoin(days, eq(days.id, hours.dayId)) // Ensure days table is joined first
|
||||||
.leftJoin(categories, eq(categories.id, hours.categoryId))
|
.leftJoin(categories, eq(categories.id, hours.categoryId))
|
||||||
.where(and(eq(hours.time, input.time), eq(days.date, date))) // Use correct alias for days table
|
.where(and(eq(hours.time, input.time), eq(days.date, date))) // Use correct alias for days table
|
||||||
|
|
||||||
console.log(hourRes);
|
|
||||||
return {
|
return {
|
||||||
date: format(date, "yyyy-MM-dd"),
|
date: format(date, "yyyy-MM-dd"),
|
||||||
...hourRes[0]
|
...hourRes[0]
|
||||||
|
|||||||
@ -198,8 +198,6 @@ export const usersAppRouter = router({
|
|||||||
timezone: input.newTimezone,
|
timezone: input.newTimezone,
|
||||||
})
|
})
|
||||||
.where(eq(users.id, ctx.user.id));
|
.where(eq(users.id, ctx.user.id));
|
||||||
|
|
||||||
console.log("changeTimezone input", input.newTimezone);
|
|
||||||
return input.newTimezone;
|
return input.newTimezone;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
14
pnpm-lock.yaml
generated
14
pnpm-lock.yaml
generated
@ -23,6 +23,9 @@ importers:
|
|||||||
'@date-fns/tz':
|
'@date-fns/tz':
|
||||||
specifier: ^1.2.0
|
specifier: ^1.2.0
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
|
'@date-fns/utc':
|
||||||
|
specifier: ^2.1.0
|
||||||
|
version: 2.1.0
|
||||||
'@lifetracker/db':
|
'@lifetracker/db':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/db
|
version: link:../../packages/db
|
||||||
@ -148,6 +151,9 @@ importers:
|
|||||||
'@date-fns/tz':
|
'@date-fns/tz':
|
||||||
specifier: ^1.2.0
|
specifier: ^1.2.0
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
|
'@date-fns/utc':
|
||||||
|
specifier: ^2.1.0
|
||||||
|
version: 2.1.0
|
||||||
'@emoji-mart/data':
|
'@emoji-mart/data':
|
||||||
specifier: ^1.1.2
|
specifier: ^1.1.2
|
||||||
version: 1.2.1
|
version: 1.2.1
|
||||||
@ -478,6 +484,9 @@ importers:
|
|||||||
'@date-fns/tz':
|
'@date-fns/tz':
|
||||||
specifier: ^1.2.0
|
specifier: ^1.2.0
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
|
'@date-fns/utc':
|
||||||
|
specifier: ^2.1.0
|
||||||
|
version: 2.1.0
|
||||||
date-fns:
|
date-fns:
|
||||||
specifier: ^4.1.0
|
specifier: ^4.1.0
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
@ -1935,6 +1944,9 @@ packages:
|
|||||||
'@date-fns/tz@1.2.0':
|
'@date-fns/tz@1.2.0':
|
||||||
resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==}
|
resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==}
|
||||||
|
|
||||||
|
'@date-fns/utc@2.1.0':
|
||||||
|
resolution: {integrity: sha512-176grgAgU2U303rD2/vcOmNg0kGPbhzckuH1TEP2al7n0AQipZIy9P15usd2TKQCG1g+E1jX/ZVQSzs4sUDwgA==}
|
||||||
|
|
||||||
'@discoveryjs/json-ext@0.5.7':
|
'@discoveryjs/json-ext@0.5.7':
|
||||||
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
|
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
@ -14384,6 +14396,8 @@ snapshots:
|
|||||||
|
|
||||||
'@date-fns/tz@1.2.0': {}
|
'@date-fns/tz@1.2.0': {}
|
||||||
|
|
||||||
|
'@date-fns/utc@2.1.0': {}
|
||||||
|
|
||||||
'@discoveryjs/json-ext@0.5.7': {}
|
'@discoveryjs/json-ext@0.5.7': {}
|
||||||
|
|
||||||
'@docsearch/css@3.6.2': {}
|
'@docsearch/css@3.6.2': {}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user