This commit is contained in:
Dominik Klarkowski
2026-06-22 14:28:05 +02:00
parent d908965c95
commit 763321ec68
4 changed files with 51 additions and 17 deletions

View File

@@ -1,5 +1,5 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google"; import { Geist, Geist_Mono, Space_Grotesk } from "next/font/google";
import { Nav } from "@/components/nav"; import { Nav } from "@/components/nav";
import { auth } from "@/auth"; import { auth } from "@/auth";
import "./globals.css"; import "./globals.css";
@@ -14,6 +14,11 @@ const geistMono = Geist_Mono({
subsets: ["latin"], subsets: ["latin"],
}); });
const spaceGrotesk = Space_Grotesk({
variable: "--font-space-grotesk",
subsets: ["latin"],
});
export const metadata: Metadata = { export const metadata: Metadata = {
title: "KNUR - Książka Notowań Udźwigów i Rezultatów", title: "KNUR - Książka Notowań Udźwigów i Rezultatów",
description: "Analiza treningów biegowych i siłowych", description: "Analiza treningów biegowych i siłowych",
@@ -29,7 +34,7 @@ export default async function RootLayout({
return ( return (
<html <html
lang="pl" lang="pl"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`} className={`${geistSans.variable} ${geistMono.variable} ${spaceGrotesk.variable} h-full antialiased`}
> >
<body className="min-h-full flex flex-col bg-bg text-fg"> <body className="min-h-full flex flex-col bg-bg text-fg">
{session && <Nav />} {session && <Nav />}

View File

@@ -14,6 +14,8 @@ import {
isSameMonth, isSameMonth,
isSameDay, isSameDay,
isToday, isToday,
isAfter,
startOfDay,
} from "date-fns"; } from "date-fns";
import { pl } from "date-fns/locale"; import { pl } from "date-fns/locale";
import { Activity, ChevronLeft, ChevronRight, Dumbbell, Sparkles } from "lucide-react"; import { Activity, ChevronLeft, ChevronRight, Dumbbell, Sparkles } from "lucide-react";
@@ -64,9 +66,16 @@ export function DashboardAnalysisCard({ analyses, runs, workouts }: Props) {
const [currentMonth, setCurrentMonth] = useState(() => new Date()); const [currentMonth, setCurrentMonth] = useState(() => new Date());
const [selectedDate, setSelectedDate] = useState<Date>(() => new Date()); const [selectedDate, setSelectedDate] = useState<Date>(() => new Date());
function hasAnalysis(date: Date) { const runDays = useMemo(
return byDay.has(dayKey(date)); () => new Set(runs.map((r) => dayKey(new Date(r.startTime)))),
} [runs]
);
const workoutDays = useMemo(
() => new Set(workouts.map((w) => dayKey(new Date(w.date)))),
[workouts]
);
const todayStart = startOfDay(new Date());
const monthStart = startOfMonth(currentMonth); const monthStart = startOfMonth(currentMonth);
const monthEnd = endOfMonth(monthStart); const monthEnd = endOfMonth(monthStart);
@@ -142,12 +151,17 @@ export function DashboardAnalysisCard({ analyses, runs, workouts }: Props) {
<div className="grid grid-cols-7 gap-1"> <div className="grid grid-cols-7 gap-1">
{days.map((d, i) => { {days.map((d, i) => {
const key = dayKey(d);
const inMonth = isSameMonth(d, monthStart); const inMonth = isSameMonth(d, monthStart);
const has = hasAnalysis(d);
const today = isToday(d); const today = isToday(d);
const selectable = has || today; const future = isAfter(startOfDay(d), todayStart);
const selectable = !future;
const selected = isSameDay(d, selectedDate); const selected = isSameDay(d, selectedDate);
const hasRun = runDays.has(key);
const hasWorkout = workoutDays.has(key);
const hasAnalysis = byDay.has(key);
return ( return (
<button <button
type="button" type="button"
@@ -159,21 +173,37 @@ export function DashboardAnalysisCard({ analyses, runs, workouts }: Props) {
!inMonth && "opacity-30", !inMonth && "opacity-30",
selectable && !selected && "cursor-pointer text-fg hover:bg-accent/20", selectable && !selected && "cursor-pointer text-fg hover:bg-accent/20",
selected && "bg-accent font-semibold text-fg", selected && "bg-accent font-semibold text-fg",
!selectable && "cursor-default text-fg/40", !selectable && "cursor-not-allowed text-fg/30",
today && !selected && "ring-1 ring-accent/50", today && !selected && "ring-1 ring-accent/50",
] ]
.filter(Boolean) .filter(Boolean)
.join(" ")} .join(" ")}
> >
{d.getDate()} {d.getDate()}
{has && !selected && ( {!selected && (hasRun || hasWorkout || hasAnalysis) && (
<span className="absolute bottom-0.5 left-1/2 h-1 w-1 -translate-x-1/2 rounded-full bg-accent" /> <span className="absolute bottom-0.5 left-1/2 flex -translate-x-1/2 gap-0.5">
{hasRun && <span className="h-1 w-1 rounded-full bg-[#5b9bd5]" />}
{hasWorkout && <span className="h-1 w-1 rounded-full bg-[#70ad47]" />}
{hasAnalysis && <span className="h-1 w-1 rounded-full bg-accent" />}
</span>
)} )}
</button> </button>
); );
})} })}
</div> </div>
<div className="mt-3 flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-fg/55">
<span className="flex items-center gap-1">
<span className="h-1.5 w-1.5 rounded-full bg-[#5b9bd5]" /> Bieg
</span>
<span className="flex items-center gap-1">
<span className="h-1.5 w-1.5 rounded-full bg-[#70ad47]" /> Siłownia
</span>
<span className="flex items-center gap-1">
<span className="h-1.5 w-1.5 rounded-full bg-accent" /> Analiza
</span>
</div>
<div className="mt-4 flex flex-col gap-1.5"> <div className="mt-4 flex flex-col gap-1.5">
<p className="text-xs font-medium text-fg/50"> <p className="text-xs font-medium text-fg/50">
Treningi · {format(selectedDate, "d MMM yyyy", { locale: pl })} Treningi · {format(selectedDate, "d MMM yyyy", { locale: pl })}
@@ -250,7 +280,7 @@ export function DashboardAnalysisCard({ analyses, runs, workouts }: Props) {
</p> </p>
) : ( ) : (
<p className="text-sm text-fg/60"> <p className="text-sm text-fg/60">
Wybierz dzień oznaczony kropką, aby zobaczyć analizę z tego dnia. Brak analizy AI dla {format(selectedDate, "d MMMM yyyy", { locale: pl })}.
</p> </p>
)} )}
</div> </div>

View File

@@ -24,17 +24,15 @@ export async function Nav() {
<Link href="/" className="flex items-center gap-3 text-lg font-bold text-fg"> <Link href="/" className="flex items-center gap-3 text-lg font-bold text-fg">
{/* eslint-disable-next-line @next/next/no-img-element */} {/* eslint-disable-next-line @next/next/no-img-element */}
<img <img
src="/logo.svg" src="/logo-icon.svg"
alt="" alt=""
width={72} width={72}
height={72} height={72}
className="-mt-1 -mb-6 h-[72px] w-[72px] rounded-2xl shadow-lg ring-2 ring-surface" className="-mt-1 -mb-6 h-[72px] w-[72px] rounded-2xl shadow-lg ring-2 ring-surface"
/> />
<span className="hidden text-xs font-normal tracking-wide text-fg/50 sm:block"> <span className="hidden text-2xl tracking-tight text-white [font-family:var(--font-space-grotesk)] sm:block">
<span className="font-semibold text-accent">K</span>siążka{" "} <span className="font-bold [-webkit-text-stroke:0.7px_white]">KNUR</span>
<span className="font-semibold text-accent">N</span>otowań{" "} <span className="font-medium text-accent">.APP</span>
<span className="font-semibold text-accent">U</span>dźwigów i{" "}
<span className="font-semibold text-accent">R</span>ezultatów
</span> </span>
</Link> </Link>
<nav className="flex items-center gap-1 sm:gap-2"> <nav className="flex items-center gap-1 sm:gap-2">

1
public/logo-icon.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 4.9 KiB