Frontend updates to Day and Month views

This commit is contained in:
Ryan Pandya 2024-12-04 12:20:32 -08:00
parent 4a434b59e4
commit 020b75b72e
10 changed files with 135 additions and 109 deletions

View File

@ -4,7 +4,7 @@ import Sidebar from "@/components/dashboard/sidebar/Sidebar";
import DemoModeBanner from "@/components/DemoModeBanner";
import { Separator } from "@/components/ui/separator";
import ValidAccountCheck from "@/components/utils/ValidAccountCheck";
import { cn } from "@/lib/utils";
import serverConfig from "@lifetracker/shared/config";
export default async function Dashboard({
@ -29,7 +29,7 @@ export default async function Dashboard({
<Separator />
</div>
{modal}
<div className="min-h-30 container p-4">{children}</div>
<div className={cn("min-h-30 p-4 container")}>{children}</div>
</main>
</div>
</div>

View File

@ -6,6 +6,7 @@ import { EditableHourCode } from "@/components/dashboard/hours/EditableHourCode"
import { useUpdateHour } from "@lifetracker/shared-react/hooks/days";
import { useToast } from "@/components/ui/use-toast";
import { getHourFromTime } from "@lifetracker/shared/utils/hours";
import MonthView from "@/components/dashboard/timelines/MonthView";
async function fetchDays(view: string, dateQuery: string) {
const timezone = await api.users.getTimezone();
@ -16,7 +17,7 @@ async function fetchDays(view: string, dateQuery: string) {
const days = [];
for (let i = 0; i < 20; i++) {
const dayDate = firstDay.add(i, "day").format("iso-short");
console.log(dayDate);
// console.log(dayDate);
const dayRes = await api.days.get({
dateQuery: dayDate,
timezone: timezone,
@ -33,44 +34,7 @@ export default async function TimelinePage({ params }: { params: { view: string,
return (
<>
<div className="grid" style={{ "gridTemplateColumns": "repeat(25, 1fr)" }}>
<div className="flex items-center">Date</div>
{Array.from({ length: 24 }, (_, i) => (
<div key={i} className="flex text-center items-center justify-center font-mono">
{getHourFromTime(i, "twelves")}
</div>
))}
</div>
{days.map((day, index) => (
<div
key={index}
className="grid"
style={{ "gridTemplateColumns": "repeat(25, 1fr)" }}
>
<div className="text-right font-mono">
{spacetime(day.date).format("{iso-month}/{date-pad}")}
</div>
{day.hours.map((hour, i) => (
<EditableHourCode
key={i}
originalText={hour.categoryCode}
hour={hour}
i={i}
/>
// <div key={i}
// datetime={day.date + "T" + hour.time + ":00:00"}
// className="flex items-center justify-center font-mono"
// style={{
// backgroundColor: hour.background ?? "white",
// color: hour.foreground ?? "black",
// }}
// >
// {hour.categoryCode ?? "_"}
// </div>
))}
</div>
))}
<MonthView days={days} />
</>
);
}

View File

@ -26,7 +26,7 @@ export default async function DayView({
return (
<div className="flex flex-col gap-3">
<div className="flex justify-between pr-4">
<div className="flex justify-between pr-4 flex-col gap-4 md:flex-row">
<div className="flex">
<Link
href={`/dashboard/day/${prevDay}`}
@ -38,9 +38,12 @@ export default async function DayView({
<ArrowLeftSquare size={18} />
</div>
</Link>
<span className="text-2xl flex-1">
<span className="text-2xl flex-1 hidden lg:block">
{spacetime(day.date).format("{day}, {month} {date}, {year}")}
</span>
<span className="text-2xl flex-1 lg:hidden block text-center">
{spacetime(day.date).format("{day-short}, {month-short} {date}, {year}")}
</span>
<Link
href={`/dashboard/day/${nextDay}`}
className={cn(
@ -52,7 +55,7 @@ export default async function DayView({
</div>
</Link>
</div>
<div className="flex items-center">
<div className="flex justify-center">
<MoodStars
day={day}
/>

View File

@ -10,7 +10,7 @@ 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 { MessageCircle } from "lucide-react";
import { MessageCircle, Pencil } from "lucide-react";
import { ButtonWithTooltip } from "@/components/ui/button";
import { EditableHourCode } from "./EditableHourCode";
import { EditableHourComment } from "./EditableHourComment";
@ -27,22 +27,23 @@ export default function EditableHour({
i: number,
className?: string;
}) {
const router = useRouter();
const currentPath = usePathname();
const [hour, setHour] = useState(initialHour);
const { mutate: updateHour, isPending } = useUpdateHour({
onSuccess: (res, req, meta) => {
const { categoryCode: oldCode, comment: oldComment } = hour;
const newHour = {
categoryCode: req.code,
categoryCode: parseInt(req.code!),
comment: oldComment,
...res,
};
console.log(res);
setHour(newHour);
toast({
description: "Hour updated!",
});
// Only show toast if client screen is larger than mobile
if (window.innerWidth > 640) {
toast({
description: "Hour updated!",
});
}
},
});
const tzOffset = spacetime().offset() / 60;
@ -64,28 +65,29 @@ export default function EditableHour({
}
return (
<div
hourid={hour.id}
data-hourid={hour.id}
className={cn(
"p-4 grid justify-between",
)}
style={{
background: hour.background, color: hour.foreground, fontFamily: "inherit",
gridTemplateColumns: "100px 100px 1fr 200px"
background: hour.background!, color: hour.foreground!, fontFamily: "inherit",
gridTemplateColumns: window.innerWidth > 640 ? "50px 100px 1fr 50px" : "50px 100px 1fr", // Known issue: This won't work if the screen is resized, only on reload
}}
>
<span className="text-right">
{isActiveHour(hour) && "--> "}
{/* {isActiveHour(hour) && "--> "} */}
{hour.datetime}
</span>
<div className="flex justify-center">
<EditableHourCode
className={"w-8 border-b"}
originalText={hour.categoryCode}
hour={hour}
onSubmit={updateHour}
i={i}
/>
</div>
<span>
<span className="hidden sm:block">
<EditableHourComment
originalText={hour.categoryDesc}
hour={hour}
@ -93,18 +95,26 @@ export default function EditableHour({
i={i}
/>
</span>
<div className="text-right">
<span className="block sm:hidden">
<div className="w-full text-left edit-hour-comment"
style={{
background: hour.background ?? "inherit", color: hour.foreground ?? "inherit", fontFamily: "inherit",
}}>
{hour.categoryName}
</div>
</span>
<div className="text-right hidden sm:block">
<ButtonWithTooltip
delayDuration={500}
tooltip="Add Comment"
tooltip="Edit"
size="none"
variant="ghost"
className="align-middle text-gray-400"
className="align-middle text-gray-400 hover:bg-active"
onClick={() => {
console.log("Pushed edit")
}}
>
<MessageCircle className="size-4" />
<Pencil className="size-4" />
</ButtonWithTooltip>
</div>
</div>

View File

@ -1,8 +1,10 @@
"use client";
import { or } from "drizzle-orm";
import { useEffect, useRef } from "react";
import {
cn
} from "@/lib/utils";
function selectHourCode(time: number) {
document.getElementById("hour-" + (time).toString())?.getElementsByClassName("edit-hour-code")[0].focus();
}
@ -11,6 +13,7 @@ function selectHourComment(time: number) {
}
export function EditableHourCode({
className = "",
originalText,
hour,
onSubmit,
@ -50,6 +53,7 @@ export function EditableHourCode({
dayId: hour.dayId,
code: newCode ?? "",
comment: hour.comment,
i: i
})
selectHourCode(i + 1);
};
@ -62,7 +66,7 @@ export function EditableHourCode({
</div>
<input
className="w-8 border-b text-center edit-hour-code"
className={cn("text-center edit-hour-code", className)}
style={{
background: hour.background ?? "inherit", color: hour.foreground ?? "inherit", fontFamily: "inherit",
borderColor: hour.foreground ?? "inherit"

View File

@ -54,9 +54,10 @@ export function EditableHourComment({
};
return (
<input
className="w-full text-left edit-hour-comment"
className="w-full text-left edit-hour-comment hover:border-b hover:border-dashed"
style={{
background: hour.background ?? "inherit", color: hour.foreground ?? "inherit", fontFamily: "inherit",
borderColor: hour.foreground ?? "inherit"
}}
ref={ref}
value={originalText ?? ""}

View File

@ -1,18 +1,18 @@
import MobileSidebarItem from "@/components/shared/sidebar/ModileSidebarItem";
import HoarderLogoIcon from "@/public/icons/logo-icon.svg";
import { ClipboardList, Search, Tag } from "lucide-react";
import { CheckCheck, ClipboardList, GaugeCircleIcon, Home, HomeIcon, Search, Tag } from "lucide-react";
export default async function MobileSidebar() {
return (
<aside className="w-full">
<ul className="flex justify-between space-x-2 border-b-black px-5 py-2 pt-5">
<MobileSidebarItem
logo={<HoarderLogoIcon className="w-5 fill-foreground" />}
path="/dashboard/bookmarks"
logo={<Home />}
path="/dashboard/day/today"
/>
<MobileSidebarItem logo={<Search />} path="/dashboard/search" />
<MobileSidebarItem logo={<ClipboardList />} path="/dashboard/lists" />
<MobileSidebarItem logo={<Tag />} path="/dashboard/tags" />
<MobileSidebarItem logo={<Tag />} path="/dashboard/categories" />
<MobileSidebarItem logo={<CheckCheck />} path="/dashboard/habits" />
<MobileSidebarItem logo={<GaugeCircleIcon />} path="/analytics" />
</ul>
</aside>
);

View File

@ -3,7 +3,7 @@ import SidebarItem from "@/components/shared/sidebar/SidebarItem";
import { Separator } from "@/components/ui/separator";
import { api } from "@/server/api/client";
import { getServerAuthSession } from "@/server/auth";
import { Archive, Calendar, Home, Search, Tag } from "lucide-react";
import { Archive, Calendar, CheckCheck, Home, Search, Tag } from "lucide-react";
import serverConfig from "@lifetracker/shared/config";
import AllLists from "./AllLists";
@ -43,6 +43,11 @@ export default async function Sidebar() {
path: "/dashboard/timeline/month",
},
...searchItem,
{
name: "Habits",
icon: <CheckCheck size={18} />,
path: "/dashboard/habits",
},
{
name: "Categories",
icon: <Tag size={18} />,

View File

@ -1,57 +1,96 @@
'use client';
import { redirect } from "next/navigation";
import { Separator } from "@/components/ui/separator";
import { getServerAuthSession } from "@/server/auth";
import { ZDay } from "@lifetracker/shared/types/days";
import EditableDayComment from "./EditableDayComment";
import { MoodStars } from "./MoodStars";
import { EditableHourCode } from "../hours/EditableHourCode";
import { getHourFromTime } from "@lifetracker/shared/utils/hours";
import Link from "next/link";
import { cn } from "@/lib/utils";
import { ArrowLeftSquare, ArrowRightSquare } from "lucide-react";
import spacetime from "spacetime";
import EditableHour from "@/components/dashboard/hours/EditableHour";
import { useUpdateHour } from "@lifetracker/shared-react/hooks/days";
import { useState } from "react";
export default async function MonthView({
dateRange,
export default function MonthView({
days: initialDays,
}: {
dateRange: string[];
days: ZDay[];
}) {
const session = await getServerAuthSession();
if (!session) {
redirect("/");
// Hacky, but it works
// Remove "container" class from parent div
const parent = document.querySelector(".container");
if (parent) {
parent.classList.remove("container");
}
const [days, setDays] = useState(initialDays);
const { mutate: updateHour, isPending } = useUpdateHour({
onSuccess: (res, req, meta) => {
const hourDay = days.find(day => day.date === req.date);
// Replace the relevant hour within hourDay
hourDay.hours[req.i] = res;
// Place the new hourDay within the days array
setDays([...days]);
},
});
return (
<div className="flex flex-col gap-3">
<div className="flex justify-between pr-4">
<div className="flex">
<Link
href={`/dashboard/timeline/month/last`}
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">
{spacetime().format("{day}, {month} {date}, {year}")}
</span>
<Link
href={`/dashboard/timeline/month/next`}
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 className="grid font-mono border-b-2" style={{ gridTemplateColumns: "repeat(26, 1fr)", borderColor: "white" }}>
<div className="flex text-center justify-center">DATE</div>
<div className="flex text-center justify-center border-r-2 border-inherit">DAY</div>
{Array.from({ length: 24 }, (_, i) => (
<div key={i} className={cn("flex text-center items-center justify-center",
i == spacetime().hour() ? "bg-white text-black" : ""
)}>
{getHourFromTime(i, "all")}
</div>
))}
</div>
{
days.map((day, index) => (
<div
key={index}
className="grid font-mono"
style={{ gridTemplateColumns: "repeat(26, 1fr)", borderColor: "white" }}
>
<div className={cn("text-center",
spacetime(day.date).diff(spacetime.now()).hours < 24 && spacetime(day.date).diff(spacetime.now()).hours > 0 ? "bg-white text-black" : ""
)}>
{spacetime(day.date).format("{iso-month}/{date-pad}")}
</div>
<div className={cn("text-center",
spacetime(day.date).diff(spacetime.now()).hours < 24 && spacetime(day.date).diff(spacetime.now()).hours > 0 ? "bg-white text-black" : ""
)}>
{spacetime(day.date).format("{day-short}")}
</div>
{day.hours.map((hour, i) => (
<EditableHourCode
className="w-full h-full hover:cursor-default"
key={i}
originalText={hour.categoryCode}
hour={hour}
i={i}
onSubmit={updateHour}
/>
// <div key={i}
// datetime={day.date + "T" + hour.time + ":00:00"}
// className="flex items-center justify-center font-mono"
// style={{
// backgroundColor: hour.background ?? "white",
// color: hour.foreground ?? "black",
// }}
// >
// {hour.categoryCode ?? "_"}
// </div>
))}
</div>
<Separator />
</div>
))
}
</>
);
}

View File

@ -24,7 +24,7 @@ export function dateFromInput(input: { dateQuery: string, timezone: string }) {
throw new Error("Invalid dateQuery");
}
}
console.log(`dateFromInput(${input.dateQuery}, ${input.timezone}) = ${t}`);
// console.log(`dateFromInput(${input.dateQuery}, ${input.timezone}) = ${t}`);
return t;
}