Maybe have frontend web working with codes at least.
This commit is contained in:
parent
a46a8a9399
commit
5625e9a6fd
@ -2,22 +2,19 @@ import { notFound } from "next/navigation";
|
||||
import { api } from "@/server/api/client";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import DayView from "@/components/dashboard/days/DayView";
|
||||
import spacetime from "spacetime";
|
||||
import { useTimezone } from "@lifetracker/shared-react/hooks/timezones";
|
||||
import { date } from "drizzle-orm/pg-core";
|
||||
import LoadingSpinner from "@/components/ui/spinner";
|
||||
|
||||
export default async function DayPage({ params }: { params: { dateQuery: string }; }) {
|
||||
const { dateQuery } = await params;
|
||||
let day;
|
||||
const tzName = await api.users.getTimezone();
|
||||
// console.log(`(dashboard/day/[dateQuery]/page.tsx) Loading ${spacetime.now(tzName).format("yyyy-MM-dd")} in ${tzName}.`);
|
||||
const { dateQuery } = await params;
|
||||
const timezone = await api.users.getTimezone();
|
||||
|
||||
try {
|
||||
day = await api.days.get({
|
||||
dateQuery: spacetime(dateQuery, tzName).format("yyyy-MM-dd"),
|
||||
timezone: tzName,
|
||||
dateQuery: dateQuery,
|
||||
timezone: timezone,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("DATEQ", params);
|
||||
if (e instanceof TRPCError) {
|
||||
if (e.code == "NOT_FOUND") {
|
||||
notFound();
|
||||
@ -28,9 +25,13 @@ export default async function DayPage({ params }: { params: { dateQuery: string
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
day == undefined ?
|
||||
<LoadingSpinner /> :
|
||||
<DayView
|
||||
day={day}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,17 +1,12 @@
|
||||
import { redirect } from "next/navigation";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { api } from "@/server/api/client";
|
||||
import { getServerAuthSession } from "@/server/auth";
|
||||
import { ZDay } from "@lifetracker/shared/types/days";
|
||||
import EditableDayComment from "./EditableDayComment";
|
||||
import { MoodStars } from "./MoodStars";
|
||||
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";
|
||||
import spacetime from "spacetime";
|
||||
import EditableHour from "@/components/dashboard/hours/EditableHour";
|
||||
|
||||
@ -31,7 +26,7 @@ export default async function DayView({
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
|
||||
<div className="flex justify-between">
|
||||
<div className="flex justify-between pr-4">
|
||||
<div className="flex">
|
||||
<Link
|
||||
href={`/dashboard/day/${prevDay}`}
|
||||
@ -57,7 +52,7 @@ export default async function DayView({
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center">
|
||||
<MoodStars
|
||||
day={day}
|
||||
/>
|
||||
@ -65,16 +60,17 @@ export default async function DayView({
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="pl-4">
|
||||
<EditableDayComment day={day}
|
||||
className="text-xl"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<div className={cn(
|
||||
{/* <div className={cn(
|
||||
"p-4 grid justify-between",
|
||||
)}
|
||||
style={{
|
||||
@ -91,11 +87,11 @@ export default async function DayView({
|
||||
<div className="text-right">
|
||||
Actions
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
</li>
|
||||
{day.hours.map((hour) => (
|
||||
<li key={hour.time} id={"hour-" + hour.time.toString()}>
|
||||
<EditableHour hour={hour} />
|
||||
{day.hours.map((hour, i) => (
|
||||
<li key={hour.time} id={"hour-" + i.toString()}>
|
||||
<EditableHour hour={hour} i={i} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@ -3,129 +3,67 @@
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { toast } from "@/components/ui/use-toast";
|
||||
import { cn } from "@/lib/utils";
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
|
||||
import { useUpdateHour } from "@lifetracker/shared-react/hooks/days";
|
||||
import { EditableText } from "@/components/dashboard/EditableText";
|
||||
import { format } from "date-fns";
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
import { ZHour } from "@lifetracker/shared/types/days";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
function selectNext(time: number) {
|
||||
document.getElementById("hour-" + (time).toString())?.getElementsByClassName("edit-hour-code")[0].focus();
|
||||
}
|
||||
|
||||
function EditCode({
|
||||
originalText,
|
||||
hour,
|
||||
onSubmit
|
||||
}) {
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
ref.current.value = originalText;
|
||||
}
|
||||
}, [ref]);
|
||||
|
||||
const submit = () => {
|
||||
let newCode: string | null = ref.current?.value ?? null;
|
||||
if (originalText == newCode) {
|
||||
// Nothing to do here
|
||||
return;
|
||||
}
|
||||
if (newCode == "") {
|
||||
if (originalText == null) {
|
||||
// Set value to previous hour's value
|
||||
newCode = document.getElementById("hour-" + (hour.time - 1).toString())?.getElementsByClassName("edit-hour-code")[0].value;
|
||||
console.log(newCode);
|
||||
}
|
||||
else {
|
||||
newCode = null;
|
||||
}
|
||||
}
|
||||
// console.log(hour);
|
||||
onSubmit({
|
||||
dateQuery: hour.date,
|
||||
time: hour.time,
|
||||
code: newCode,
|
||||
})
|
||||
selectNext(hour.time + 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<input
|
||||
className="w-8 border-b-2 text-center edit-hour-code"
|
||||
style={{
|
||||
background: hour.background, color: hour.foreground, fontFamily: "inherit",
|
||||
}}
|
||||
ref={ref}
|
||||
defaultValue={originalText}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
submit();
|
||||
}
|
||||
if (e.key == "ArrowDown") {
|
||||
e.preventDefault();
|
||||
selectNext(hour.time + 1);
|
||||
}
|
||||
if (e.key == "ArrowUp") {
|
||||
e.preventDefault();
|
||||
selectNext(hour.time - 1);
|
||||
}
|
||||
}}
|
||||
onClick={(e) => {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(ref.current);
|
||||
const selection = window.getSelection();
|
||||
if (selection) {
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
// <input type="text"
|
||||
// className="w-10 bg-inherit border-b-2 text-center"
|
||||
// style={{ color: "inherit", borderColor: "inherit" }}
|
||||
// onKeyDown={(e) => {
|
||||
// if (e.key === "Enter") {
|
||||
// e.preventDefault();
|
||||
// onSave();
|
||||
// }
|
||||
// }}
|
||||
// value={originalText.originalText ?? ""}
|
||||
// />
|
||||
);
|
||||
}
|
||||
|
||||
import { MessageCircle } from "lucide-react";
|
||||
import { ButtonWithTooltip } from "@/components/ui/button";
|
||||
import { EditableHourCode } from "./EditableHourCode";
|
||||
import { EditableHourComment } from "./EditableHourComment";
|
||||
import { api } from "@/lib/trpc";
|
||||
import spacetime from 'spacetime';
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
export default function EditableHour({
|
||||
hour,
|
||||
hour: initialHour,
|
||||
i,
|
||||
className,
|
||||
}: {
|
||||
hour: ZHour,
|
||||
i: number,
|
||||
className?: string;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const currentPath = usePathname();
|
||||
const [hour, setHour] = useState(initialHour);
|
||||
const { mutate: updateHour, isPending } = useUpdateHour({
|
||||
onSuccess: () => {
|
||||
onSuccess: (res, req, meta) => {
|
||||
const { categoryCode: oldCode, comment: oldComment } = hour;
|
||||
const newHour = {
|
||||
categoryCode: req.code,
|
||||
comment: oldComment,
|
||||
...res,
|
||||
};
|
||||
console.log(res);
|
||||
setHour(newHour);
|
||||
toast({
|
||||
description: "Hour updated!",
|
||||
});
|
||||
if (currentPath.includes("dashboard")) {
|
||||
router.refresh();
|
||||
}
|
||||
},
|
||||
});
|
||||
const tzOffset = spacetime().offset() / 60;
|
||||
const localDateTime = spacetime(hour.date).add(hour.time + tzOffset, "hour");
|
||||
hour.datetime = `${localDateTime.format('{hour} {ampm}')}`;
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// console.log(hour.categoryDesc);
|
||||
}, [hour]);
|
||||
|
||||
|
||||
function isActiveHour(hour: ZHour) {
|
||||
const now = new TZDate();
|
||||
return (hour.date == format(now, "yyyy-MM-dd")) && (((now.getHours()) + (now.getTimezoneOffset() / 60) - (parseInt(hour.time))) == 0)
|
||||
}
|
||||
return (
|
||||
<div className={cn(
|
||||
<div
|
||||
hourid={hour.id}
|
||||
className={cn(
|
||||
"p-4 grid justify-between",
|
||||
)}
|
||||
style={{
|
||||
@ -138,20 +76,34 @@ export default function EditableHour({
|
||||
{hour.datetime}
|
||||
</span>
|
||||
<div className="flex justify-center">
|
||||
<EditCode
|
||||
<EditableHourCode
|
||||
originalText={hour.categoryCode}
|
||||
hour={hour}
|
||||
onSubmit={updateHour}
|
||||
i={i}
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
{hour.categoryDesc || " "}
|
||||
<EditableHourComment
|
||||
originalText={hour.categoryDesc}
|
||||
hour={hour}
|
||||
onSubmit={updateHour}
|
||||
i={i}
|
||||
/>
|
||||
</span>
|
||||
<div className="text-right">
|
||||
<EditableText
|
||||
originalText={hour.comment || " "}
|
||||
viewClassName="text-right"
|
||||
/>
|
||||
<ButtonWithTooltip
|
||||
delayDuration={500}
|
||||
tooltip="Add Comment"
|
||||
size="none"
|
||||
variant="ghost"
|
||||
className="align-middle text-gray-400"
|
||||
onClick={() => {
|
||||
console.log("Pushed edit")
|
||||
}}
|
||||
>
|
||||
<MessageCircle className="size-4" />
|
||||
</ButtonWithTooltip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
102
apps/web/components/dashboard/hours/EditableHourCode.tsx
Normal file
102
apps/web/components/dashboard/hours/EditableHourCode.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
"use client";
|
||||
|
||||
import { or } from "drizzle-orm";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
function selectNext(time: number) {
|
||||
document.getElementById("hour-" + (time).toString())?.getElementsByClassName("edit-hour-code")[0].focus();
|
||||
}
|
||||
|
||||
export function EditableHourCode({
|
||||
originalText,
|
||||
hour,
|
||||
onSubmit,
|
||||
i
|
||||
}) {
|
||||
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
ref.current.value = originalText;
|
||||
}
|
||||
}, [ref]);
|
||||
|
||||
const submit = () => {
|
||||
let newCode: string | null = ref.current?.value ?? null;
|
||||
console.log(`Original ${originalText}, new ${newCode}`);
|
||||
if (originalText === newCode) {
|
||||
// Nothing to do here
|
||||
console.log("Skipping.");
|
||||
selectNext(i + 1);
|
||||
return;
|
||||
}
|
||||
if (newCode == "") {
|
||||
if (originalText == null) {
|
||||
// Set value to previous hour's value
|
||||
newCode = document.getElementById("hour-" + (i - 1).toString())?.getElementsByClassName("edit-hour-code")[0].value;
|
||||
ref.current.value = newCode;
|
||||
}
|
||||
else {
|
||||
newCode = null;
|
||||
}
|
||||
}
|
||||
onSubmit({
|
||||
date: hour.date,
|
||||
hourTime: hour.time,
|
||||
dayId: hour.dayId,
|
||||
code: newCode ?? "",
|
||||
comment: hour.comment,
|
||||
})
|
||||
selectNext(i + 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* The below is a gross hack because I don't understand why including it makes the component refresh, but without it, it won't update */}
|
||||
<div className="hidden">
|
||||
{originalText}
|
||||
</div>
|
||||
|
||||
<input
|
||||
className="w-8 border-b-2 text-center edit-hour-code"
|
||||
style={{
|
||||
background: hour.background ?? "inherit", color: hour.foreground ?? "inherit", fontFamily: "inherit",
|
||||
}}
|
||||
ref={ref}
|
||||
defaultValue={originalText}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
submit();
|
||||
}
|
||||
if (e.key == "ArrowDown") {
|
||||
e.preventDefault();
|
||||
selectNext(i + 1);
|
||||
}
|
||||
if (e.key == "ArrowUp") {
|
||||
e.preventDefault();
|
||||
selectNext(i - 1);
|
||||
}
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.target.select();
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
e.target.select();
|
||||
}}
|
||||
/></>
|
||||
// <input type="text"
|
||||
// className="w-10 bg-inherit border-b-2 text-center"
|
||||
// style={{ color: "inherit", borderColor: "inherit" }}
|
||||
// onKeyDown={(e) => {
|
||||
// if (e.key === "Enter") {
|
||||
// e.preventDefault();
|
||||
// onSave();
|
||||
// }
|
||||
// }}
|
||||
// value={originalText.originalText ?? ""}
|
||||
// />
|
||||
);
|
||||
}
|
||||
|
||||
83
apps/web/components/dashboard/hours/EditableHourComment.tsx
Normal file
83
apps/web/components/dashboard/hours/EditableHourComment.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
"use client";
|
||||
|
||||
import { or } from "drizzle-orm";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
function selectNext(time: number) {
|
||||
console.log(time);
|
||||
document.getElementById("hour-" + (time).toString())?.getElementsByClassName("edit-hour-comment")[0].focus();
|
||||
}
|
||||
|
||||
export function EditableHourComment({
|
||||
originalText,
|
||||
hour,
|
||||
onSubmit,
|
||||
i
|
||||
}) {
|
||||
|
||||
// console.log(`Hello from ${hour.time}, where the categoryDesc is ${hour.categoryDesc}`);
|
||||
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
ref.current.value = originalText;
|
||||
}
|
||||
}, [ref]);
|
||||
|
||||
const submit = () => {
|
||||
let newComment: string | null = ref.current?.value ?? null;
|
||||
if (originalText == newComment) {
|
||||
// Nothing to do here
|
||||
selectNext(hour.time + 1);
|
||||
return;
|
||||
}
|
||||
if (newComment == "") {
|
||||
if (originalText == null) {
|
||||
// Set value to previous hour's value
|
||||
newComment = document.getElementById("hour-" + (i - 1).toString())?.getElementsByClassName("edit-hour-comment")[0].value;
|
||||
}
|
||||
else {
|
||||
newComment = null;
|
||||
}
|
||||
}
|
||||
onSubmit({
|
||||
date: hour.date,
|
||||
hourTime: hour.time,
|
||||
dayId: hour.dayId,
|
||||
comment: newComment,
|
||||
code: hour.categoryCode.toString(),
|
||||
})
|
||||
selectNext(hour.time + 1);
|
||||
};
|
||||
return (
|
||||
<input
|
||||
className="w-full border-b-2 text-left edit-hour-comment"
|
||||
style={{
|
||||
background: hour.background ?? "inherit", color: hour.foreground ?? "inherit", fontFamily: "inherit",
|
||||
}}
|
||||
ref={ref}
|
||||
value={originalText ?? ""}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
submit();
|
||||
}
|
||||
if (e.key == "ArrowDown") {
|
||||
e.preventDefault();
|
||||
selectNext(i + 1);
|
||||
}
|
||||
if (e.key == "ArrowUp") {
|
||||
e.preventDefault();
|
||||
selectNext(i - 1);
|
||||
}
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.target.select();
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
e.target.select();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -67,7 +67,7 @@ CREATE TABLE `hour` (
|
||||
`createdAt` integer NOT NULL,
|
||||
`userId` text NOT NULL,
|
||||
`comment` text,
|
||||
`time` integer,
|
||||
`time` integer NOT NULL,
|
||||
`dayId` text NOT NULL,
|
||||
`categoryId` text,
|
||||
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "ebffb4c7-5ecf-46d0-93c6-68f8e48a9fc4",
|
||||
"id": "a91a2a0e-7727-4187-8a75-c96d8e304f27",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"tables": {
|
||||
"account": {
|
||||
@ -505,7 +505,7 @@
|
||||
"name": "time",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"dayId": {
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1732766704666,
|
||||
"tag": "0000_cold_golden_guardian",
|
||||
"when": 1733261637429,
|
||||
"tag": "0000_sad_lionheart",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
||||
@ -4,7 +4,6 @@ export function useUpdateDay(
|
||||
...opts: Parameters<typeof api.days.update.useMutation>
|
||||
) {
|
||||
const apiUtils = api.useUtils();
|
||||
// console.log("UPDATING DAY");
|
||||
return api.days.update.useMutation({
|
||||
...opts[0],
|
||||
onSuccess: (res, req, meta) => {
|
||||
@ -18,7 +17,6 @@ export function useUpdateHour(
|
||||
...opts: Parameters<typeof api.hours.update.useMutation>
|
||||
) {
|
||||
const apiUtils = api.useUtils();
|
||||
// console.log(opts[0]);
|
||||
return api.hours.update.useMutation({
|
||||
...opts[0],
|
||||
onSuccess: (res, req, meta) => {
|
||||
|
||||
@ -13,6 +13,5 @@ export function useUpdateUserTimezone(
|
||||
|
||||
export function useTimezone() {
|
||||
const res = api.users.getTimezone.useQuery();
|
||||
console.log("react hook useTimezone", res);
|
||||
return res;
|
||||
}
|
||||
@ -4,19 +4,28 @@ import { UTCDate, utc } from "@date-fns/utc";
|
||||
import spacetime from "spacetime";
|
||||
|
||||
export function dateFromInput(input: { dateQuery: string, timezone: string }) {
|
||||
console.log(`Looking for ${input.dateQuery} in ${input.timezone}`);
|
||||
|
||||
|
||||
let t: string;
|
||||
if (input.dateQuery == "today") {
|
||||
t = spacetime(input.dateQuery, input.timezone).format("yyyy-MM-dd");
|
||||
try {
|
||||
t = format(new UTCDate(input.dateQuery), "yyyy-MM-dd", { in: utc });
|
||||
}
|
||||
catch (e) {
|
||||
const now_here = spacetime.now(input.timezone);
|
||||
switch (input.dateQuery) {
|
||||
case "today":
|
||||
t = now_here.format("yyyy-MM-dd");
|
||||
break;
|
||||
case "yesterday":
|
||||
t = now_here.subtract(1, "day").format("yyyy-MM-dd");
|
||||
break;
|
||||
case "tomorrow":
|
||||
t = now_here.add(1, "day").format("yyyy-MM-dd");
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid dateQuery");
|
||||
}
|
||||
}
|
||||
console.log(`dateFromInput(${input.dateQuery}, ${input.timezone}) = ${t}`);
|
||||
return t;
|
||||
}
|
||||
else {
|
||||
t = new UTCDate(input.dateQuery);
|
||||
return format(t, "yyyy-MM-dd", { in: utc });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function generateHour(d, t) {
|
||||
|
||||
@ -37,7 +37,6 @@ async function createColor(
|
||||
});
|
||||
return result[0];
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
if (e instanceof SqliteError) {
|
||||
if (e.code == "SQLITE_CONSTRAINT_UNIQUE") {
|
||||
throw new TRPCError({
|
||||
|
||||
@ -12,10 +12,9 @@ import { authedProcedure, router } from "../index";
|
||||
import { dateFromInput, hoursListInUTC } from "@lifetracker/shared/utils/days";
|
||||
import { closestIndexTo, format } from "date-fns";
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
import { hoursAppRouter } from "./hours";
|
||||
import { hoursAppRouter, hourColors, hourJoinsQuery } from "./hours";
|
||||
import spacetime from "spacetime";
|
||||
import { getHourFromTime, getTimeFromHour } from "@lifetracker/shared/utils/hours";
|
||||
import { hourColors } from "./hours";
|
||||
|
||||
async function createDay(date: string, ctx: Context) {
|
||||
return await ctx.db.transaction(async (trx) => {
|
||||
@ -52,13 +51,12 @@ async function createDay(date: string, ctx: Context) {
|
||||
});
|
||||
}
|
||||
|
||||
async function createHour(day, time, ctx,) {
|
||||
async function createHour(day: ZDay, time: number, ctx: Context,) {
|
||||
const newHour = (await ctx.db.insert(hours).values({
|
||||
dayId: day.id,
|
||||
time: time,
|
||||
userId: ctx.user!.id,
|
||||
}).returning());
|
||||
console.log(newHour);
|
||||
return newHour[0];
|
||||
}
|
||||
|
||||
@ -115,21 +113,27 @@ export const daysAppRouter = router({
|
||||
}))
|
||||
.output(zDaySchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
// Get a Day
|
||||
|
||||
// Use timezone and date string to get the local date
|
||||
const timezone = input.timezone ?? await getTimezone(ctx);
|
||||
const date = dateFromInput({
|
||||
dateQuery: input.dateQuery,
|
||||
timezone: timezone
|
||||
});
|
||||
|
||||
const allHours = hoursListInUTC({
|
||||
// Get the list of UTC hours corresponding to this day
|
||||
const utcHours = hoursListInUTC({
|
||||
timezone,
|
||||
...input
|
||||
});
|
||||
|
||||
const dayRange = [...new Set(allHours.map(({ date: date, time: _time }) => date))];
|
||||
// console.log(`utcHours:\n,${utcHours.map(({ date, time }) => `${date} ${time}\n`)}`);
|
||||
|
||||
const allDayIds = await Promise.all(dayRange.map(async function (date) {
|
||||
// Flatten the 24 hours to the 2 unique days
|
||||
const uniqueDays = [...new Set(utcHours.map(({ date: date, time: _time }) => date))];
|
||||
// ...and get their IDs
|
||||
const uniqueDayIds = await Promise.all(uniqueDays.map(async function (date) {
|
||||
const dayObj = await getDay(input, ctx, date);
|
||||
return {
|
||||
id: dayObj.id,
|
||||
@ -137,49 +141,13 @@ export const daysAppRouter = router({
|
||||
}
|
||||
}));
|
||||
|
||||
const dayHours = await Promise.all(allHours.map(async function (map: { date, time }, i) {
|
||||
const dayId = allDayIds.find((dayIds: { id, date }) => map.date == dayIds.date)!.id;
|
||||
// Finally, use the two unique day IDs and the 24 hours to get the actual Hour objects for each day
|
||||
const dayHours = await Promise.all(utcHours.map(async function (map: { date: string, time: number }, i) {
|
||||
const dayId = uniqueDayIds.find((dayIds: { id: string, date: string }) => map.date == dayIds.date)!.id;
|
||||
|
||||
const hourMatch = await ctx.db.select({
|
||||
id: hours.id,
|
||||
dayId: hours.dayId,
|
||||
time: hours.time,
|
||||
categoryId: hours.categoryId,
|
||||
categoryCode: categories.code,
|
||||
categoryName: categories.name,
|
||||
categoryDesc: categories.description,
|
||||
comment: hours.comment,
|
||||
date: days.date
|
||||
}).from(hours)
|
||||
.leftJoin(categories, eq(categories.id, hours.categoryId))
|
||||
.leftJoin(days, eq(days.id, hours.dayId))
|
||||
.where(and(
|
||||
eq(hours.time, map.time),
|
||||
eq(hours.dayId, dayId)));
|
||||
// console.log({
|
||||
// ...allHours,
|
||||
// dayId: dayId
|
||||
// });
|
||||
// console.log("Search values:: ", `allDayIds: ${allDayIds}, d: ${date}, t: ${time}, dayId: ${dayId}`)
|
||||
// console.log("hourMatch", hourMatch[0]);
|
||||
// console.log(hourMatch[0].categoryDesc);
|
||||
|
||||
const dayHour = {
|
||||
...hourMatch[0],
|
||||
...(await hourColors(hourMatch[0], ctx)),
|
||||
};
|
||||
|
||||
|
||||
const localDateTime = spacetime(date, timezone).add(i, "hour");
|
||||
|
||||
return {
|
||||
...dayHour,
|
||||
datetime: `${localDateTime.format('{hour} {ampm}')}`,
|
||||
// datetime: `${localDateTime.format('{nice}')} ${timezone} (${localDateTime.goto("UTC").format('{nice}')} UTC)`,
|
||||
};
|
||||
return hourJoinsQuery(ctx, dayId, map.time);
|
||||
}));
|
||||
|
||||
// console.log(dayHours.flat());
|
||||
|
||||
return {
|
||||
...await getDay(input, ctx, date),
|
||||
@ -206,6 +174,6 @@ export const daysAppRouter = router({
|
||||
await ctx.db
|
||||
.update(days)
|
||||
.set(updatedProps)
|
||||
.where(eq(days.date, dateFromInput({ dateQuery: dateQuery, timezone: timezone ?? ctx.user.timezone })));
|
||||
.where(eq(days.date, utcDateFromInput({ dateQuery: dateQuery, timezone: timezone ?? ctx.user.timezone })));
|
||||
}),
|
||||
});
|
||||
|
||||
@ -12,10 +12,9 @@ import { authedProcedure, router } from "../index";
|
||||
import { format } from "date-fns";
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
import { dateFromInput } from "@lifetracker/shared/utils/days";
|
||||
|
||||
import { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
|
||||
|
||||
export async function hourColors(hour: ZHour, ctx: Context) {
|
||||
|
||||
const categoryColor = await ctx.db.select()
|
||||
.from(colors)
|
||||
.leftJoin(categories, eq(categories.id, hour.categoryId))
|
||||
@ -24,7 +23,6 @@ export async function hourColors(hour: ZHour, ctx: Context) {
|
||||
eq(colors.userId, ctx.user!.id)
|
||||
))
|
||||
|
||||
// console.log(categoryColor);
|
||||
if (!categoryColor[0]) {
|
||||
return {
|
||||
background: "inherit",
|
||||
@ -39,17 +37,12 @@ export async function hourColors(hour: ZHour, ctx: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
export const hoursAppRouter = router({
|
||||
get: authedProcedure
|
||||
.input(z.object({
|
||||
dateQuery: z.string(),
|
||||
time: z.number()
|
||||
}))
|
||||
.output(zHourSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const date = dateFromInput(input);
|
||||
const hourRes = await ctx.db
|
||||
.select({
|
||||
export async function hourJoinsQuery(
|
||||
ctx: Context,
|
||||
dayId: string,
|
||||
time: number,
|
||||
) {
|
||||
const hourMatch = await ctx.db.select({
|
||||
id: hours.id,
|
||||
dayId: hours.dayId,
|
||||
time: hours.time,
|
||||
@ -58,11 +51,38 @@ export const hoursAppRouter = router({
|
||||
categoryName: categories.name,
|
||||
categoryDesc: categories.description,
|
||||
comment: hours.comment,
|
||||
})
|
||||
.from(hours)
|
||||
.leftJoin(days, eq(days.id, hours.dayId)) // Ensure days table is joined first
|
||||
date: days.date,
|
||||
}).from(hours)
|
||||
.leftJoin(categories, eq(categories.id, hours.categoryId))
|
||||
.where(and(eq(hours.time, input.time), eq(days.date, date))) // Use correct alias for days table
|
||||
.leftJoin(days, eq(days.id, hours.dayId))
|
||||
.where(and(
|
||||
eq(hours.time, time),
|
||||
eq(hours.dayId, dayId)
|
||||
));
|
||||
|
||||
const dayHour = {
|
||||
...hourMatch[0],
|
||||
...(await hourColors(hourMatch[0], ctx)),
|
||||
};
|
||||
|
||||
return dayHour;
|
||||
|
||||
};
|
||||
|
||||
|
||||
export const hoursAppRouter = router({
|
||||
get: authedProcedure
|
||||
.input(z.object({
|
||||
dateQuery: z.string(),
|
||||
time: z.number()
|
||||
}))
|
||||
.output(zHourSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const date = dateFromInput({ dateQuery: input.dateQuery });
|
||||
const hourRes = await getHourSelectQuery(ctx, date, input.time,
|
||||
and(eq(hours.time, input.time), eq(days.date, date))
|
||||
);
|
||||
|
||||
return {
|
||||
date: format(date, "yyyy-MM-dd"),
|
||||
...hourRes[0]
|
||||
@ -71,23 +91,23 @@ export const hoursAppRouter = router({
|
||||
update: authedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
hourId: z.string().optional(),
|
||||
dayId: z.string().optional(),
|
||||
dateQuery: z.string(),
|
||||
time: z.number(),
|
||||
code: z.string().optional(),
|
||||
date: z.string(),
|
||||
hourTime: z.number(),
|
||||
dayId: z.string(),
|
||||
code: z.string().nullish(),
|
||||
comment: z.string().nullable().optional(),
|
||||
}),
|
||||
)
|
||||
.output(zHourSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { dateQuery, time, code, ...updatedProps } = input;
|
||||
const { code, ...updatedProps } = input;
|
||||
let dateCondition;
|
||||
|
||||
if (input.dayId) {
|
||||
dateCondition = eq(days.id, input.dayId)
|
||||
}
|
||||
else {
|
||||
dateCondition = eq(days.date, dateFromInput({ dateQuery: dateQuery }));
|
||||
throw new TRPCError({ code: "BAD_REQUEST", message: "dayId is required" });
|
||||
}
|
||||
|
||||
const category =
|
||||
@ -110,15 +130,6 @@ export const hoursAppRouter = router({
|
||||
)
|
||||
);
|
||||
|
||||
const day = await ctx.db.select()
|
||||
.from(days)
|
||||
.where(
|
||||
and(
|
||||
dateCondition,
|
||||
eq(days.userId, ctx.user!.id),
|
||||
)
|
||||
);
|
||||
|
||||
const newProps = {
|
||||
categoryId: category[0].id,
|
||||
code: code,
|
||||
@ -132,18 +143,18 @@ export const hoursAppRouter = router({
|
||||
.set(newProps)
|
||||
.where(
|
||||
and(
|
||||
eq(hours.time, time),
|
||||
eq(hours.dayId, day[0].id),
|
||||
eq(hours.time, input.hourTime),
|
||||
eq(hours.dayId, input.dayId),
|
||||
eq(hours.userId, ctx.user!.id)
|
||||
)
|
||||
)
|
||||
.returning();
|
||||
).returning();
|
||||
|
||||
// return {
|
||||
// date: input.date,
|
||||
// ...hourRes[0]
|
||||
// };
|
||||
|
||||
return hourJoinsQuery(ctx, input.dayId, input.hourTime);
|
||||
|
||||
return {
|
||||
date: format(day[0].date, "yyyy-MM-dd"),
|
||||
categoryName: category[0].name,
|
||||
categoryDesc: category[0].description,
|
||||
...hourRes[0]
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
@ -22,7 +22,6 @@ export async function createUser(
|
||||
ctx: Context,
|
||||
role?: "user" | "admin",
|
||||
) {
|
||||
// console.log(ctx.db);
|
||||
|
||||
return ctx.db.transaction(async (trx) => {
|
||||
let userRole = role;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user