diff --git a/.env.local.example b/.env.local.example new file mode 100644 index 0000000..bad9298 --- /dev/null +++ b/.env.local.example @@ -0,0 +1,8 @@ +# Jeśli używasz docker-compose.yaml z tego repo (root/example), użyj: +# MONGODB_URI=mongodb://root:example@localhost:27017/?authSource=admin +MONGODB_URI=mongodb://localhost:27017 +MONGODB_DB=knur +GARMIN_EMAIL= +GARMIN_PASSWORD= +ANTHROPIC_API_KEY= +ANTHROPIC_MODEL=claude-sonnet-4-6 diff --git a/.gitignore b/.gitignore index 5ef6a52..3639bc5 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* +!.env*.example # vercel .vercel diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..84c183b --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,12 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Zeppelin ignored files +/ZeppelinRemoteNotebooks/ diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..03d9549 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/knur-app.iml b/.idea/knur-app.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/knur-app.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5004494 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3e4e89c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index e215bc4..32b218f 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,41 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# Knur -## Getting Started +Aplikacja do analizy treningów biegowych (Garmin Connect) i siłowych (import ze Strong), z analizą AI (Claude). -First, run the development server: +## Wymagania + +- Node 22 (`nvm use 22`) +- pnpm +- MongoDB (lokalnie lub Atlas) + +## Konfiguracja + +Skopiuj `.env.local.example` do `.env.local` i wypełnij wartości: ```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +cp .env.local.example .env.local ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +- `MONGODB_URI`, `MONGODB_DB` — połączenie z MongoDB (domyślnie `mongodb://localhost:27017`, baza `knur`). +- `GARMIN_EMAIL`, `GARMIN_PASSWORD` — dane logowania do Garmin Connect, używane do synchronizacji biegów (nieoficjalne API). +- `ANTHROPIC_API_KEY`, `ANTHROPIC_MODEL` — klucz API Claude i model używany do generowania analiz potreningowych (domyślnie `claude-sonnet-4-6`). -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +Status konfiguracji widoczny jest na stronie `/settings`. -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +## Uruchomienie -## Learn More +```bash +nvm use 22 +pnpm install +pnpm dev +``` -To learn more about Next.js, take a look at the following resources: +Otwórz [http://localhost:3000](http://localhost:3000). -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +## Funkcje -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +- **Panel** (`/`) — statystyki tygodniowe, ostatni bieg, ostatni trening siłowy, ostatnia analiza AI. +- **Bieganie** (`/running`) — lista biegów zsynchronizowanych z Garmin Connect (przycisk „Synchronizuj z Garmin”), szczegóły aktywności. +- **Siłownia** (`/strength`) — lista treningów, import (`/strength/import`) przez wklejenie tekstu z funkcji „Share workout” w aplikacji Strong, szczegóły treningu. +- **Analiza AI** — na stronach szczegółów biegu i treningu siłowego, przycisk „Generuj analizę” wywołuje Claude i zapisuje podsumowanie ze wskazówkami. +- **Ustawienia** (`/settings`) — status konfiguracji (MongoDB, Garmin, Claude) i ostatnia synchronizacja Garmin. diff --git a/app/ai/actions.ts b/app/ai/actions.ts new file mode 100644 index 0000000..9d0b617 --- /dev/null +++ b/app/ai/actions.ts @@ -0,0 +1,33 @@ +"use server"; + +import { revalidatePath } from "next/cache"; +import { generateAnalysis, generateDashboardAnalysis } from "@/lib/ai/claude"; +import type { AiAnalysisTargetType } from "@/lib/models/analysis"; + +export type GenerateAnalysisState = { error: string } | { success: true } | null; + +export async function generateAnalysisAction( + targetType: AiAnalysisTargetType, + targetId: string +): Promise { + try { + await generateAnalysis(targetType, targetId); + } catch (error) { + return { error: error instanceof Error ? error.message : "Nie udało się wygenerować analizy." }; + } + + revalidatePath(`/${targetType}/${targetId}`); + revalidatePath("/"); + return { success: true }; +} + +export async function generateDashboardAnalysisAction(): Promise { + try { + await generateDashboardAnalysis(); + } catch (error) { + return { error: error instanceof Error ? error.message : "Nie udało się wygenerować analizy." }; + } + + revalidatePath("/"); + return { success: true }; +} diff --git a/app/globals.css b/app/globals.css index a2dc41e..01df336 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,26 +1,29 @@ @import "tailwindcss"; :root { - --background: #ffffff; - --foreground: #171717; + --color-bg: #2b2d42; + --color-surface: #363850; + --color-fg: #f7f3e9; + --color-accent: #fb4617; + --color-muted: #434247; + --color-secondary: #2e162e; + --color-sand: #d4cbbb; } @theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); + --color-bg: var(--color-bg); + --color-surface: var(--color-surface); + --color-fg: var(--color-fg); + --color-accent: var(--color-accent); + --color-muted: var(--color-muted); + --color-secondary: var(--color-secondary); + --color-sand: var(--color-sand); --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + background: var(--color-bg); + color: var(--color-fg); + font-family: var(--font-sans), Arial, Helvetica, sans-serif; } diff --git a/app/icon.svg b/app/icon.svg new file mode 100644 index 0000000..f013553 --- /dev/null +++ b/app/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index 976eb90..b3c0654 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; +import { Nav } from "@/components/nav"; import "./globals.css"; const geistSans = Geist({ @@ -13,8 +14,8 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "KNUR - Książka Notowań Udźwigów i Rezultatów", + description: "Analiza treningów biegowych i siłowych", }; export default function RootLayout({ @@ -24,10 +25,15 @@ export default function RootLayout({ }>) { return ( - {children} + +