eb09da9e4d
- Zmiana kolekcji Directus z Laboratorium_AI na LabAI - Usunięcie fallbacku na fake dane — brak wpisów = komunikat - revalidate=0 na stronie głównej (zawsze świeże dane) - BlogPosting przeniesiony do @graph (poprawna spec Schema.org) - Inbox Libredesk zaktualizowany - Hero image-wrap powiększony (scale 1.56) - Dokumentacja projektu w CLAUDE.md Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
220 lines
7.3 KiB
Markdown
220 lines
7.3 KiB
Markdown
## SEO — JSON-LD Schema.org
|
|
|
|
Każda nowa strona (`src/app/*/page.jsx`) musi mieć odpowiedni JSON-LD w `<head>`.
|
|
|
|
- Strona główna → `WebSite` + `WebPage` + `Organization` + `blogPost[]` (komponent `SchemaOrg` w `layout.jsx`)
|
|
- Strona artykułu → `BlogPosting` (headline, url, datePublished, author, image)
|
|
- Strona kontakt → `ContactPage`
|
|
- Strona "wkrótce" → `WebPage` z `name` i `description`
|
|
|
|
JSON-LD idzie do `<head>` przez `<script type="application/ld+json" dangerouslySetInnerHTML=.../>` — nie przez `useEffect`. Dane do schematu pobieraj po stronie serwera (Server Component).
|
|
|
|
## Coding Guidelines (Karpathy)
|
|
|
|
Behavioral guidelines to reduce common LLM coding mistakes.
|
|
**Tradeoff:** Bias toward caution over speed. For trivial tasks, use judgment.
|
|
|
|
### 1. Think Before Coding
|
|
|
|
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
|
|
|
Before implementing:
|
|
|
|
- State assumptions explicitly. If uncertain, ask.
|
|
- If multiple interpretations exist, present them — don't pick silently.
|
|
- If a simpler approach exists, say so. Push back when warranted.
|
|
- If something is unclear, stop. Name what's confusing. Ask.
|
|
|
|
### 2. Simplicity First
|
|
|
|
**Minimum code that solves the problem. Nothing speculative.**
|
|
|
|
- No features beyond what was asked.
|
|
- No abstractions for single-use code.
|
|
- No "flexibility" or "configurability" that wasn't requested.
|
|
- No error handling for impossible scenarios.
|
|
- If you write 200 lines and it could be 50, rewrite it.
|
|
|
|
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
|
|
|
### 3. Surgical Changes
|
|
|
|
**Touch only what you must. Clean up only your own mess.**
|
|
|
|
When editing existing code:
|
|
|
|
- Don't "improve" adjacent code, comments, or formatting.
|
|
- Don't refactor things that aren't broken.
|
|
- Match existing style, even if you'd do it differently.
|
|
- If you notice unrelated dead code, mention it — don't delete it.
|
|
|
|
When your changes create orphans:
|
|
|
|
- Remove imports/variables/functions that YOUR changes made unused.
|
|
- Don't remove pre-existing dead code unless asked.
|
|
|
|
The test: Every changed line should trace directly to the user's request.
|
|
|
|
### 4. Goal-Driven Execution
|
|
|
|
**Define success criteria. Loop until verified.**
|
|
|
|
Transform tasks into verifiable goals:
|
|
|
|
- "Add validation" → "Write tests for invalid inputs, then make them pass"
|
|
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
|
- "Refactor X" → "Ensure tests pass before and after"
|
|
|
|
For multi-step tasks, state a brief plan:
|
|
|
|
```
|
|
1. [Step] → verify: [check]
|
|
2. [Step] → verify: [check]
|
|
3. [Step] → verify: [check]
|
|
```
|
|
|
|
---
|
|
|
|
## Projekt: dexterlab.pl website
|
|
|
|
### Stack
|
|
|
|
- **Next.js 14** (App Router) — `src/app/`
|
|
- **React 18** — Server Components domyślnie, `'use client'` tylko gdy trzeba
|
|
- **Directus CMS** — `https://cms.dexterlab.pl` (headless, REST API)
|
|
- **CSS** — własny, w `src/index.css`
|
|
- **Fonty** — Inter przez `next/font/google`
|
|
|
|
> **Uwaga:** W katalogu istnieje też stary `src/App.jsx` i `src/main.jsx` (pozostałości po Vite). Są nieużywane — Next.js korzysta wyłącznie z `src/app/`.
|
|
|
|
---
|
|
|
|
### Struktura katalogów
|
|
|
|
```
|
|
src/
|
|
├── app/ # Next.js App Router
|
|
│ ├── layout.jsx # Root layout: Nav, Footer, Cookie, Schema, skrypty
|
|
│ ├── page.jsx # Strona główna (/)
|
|
│ ├── produkty/page.jsx # Strona produktów (/produkty)
|
|
│ ├── kontakt/page.jsx # Strona kontaktu (/kontakt)
|
|
│ └── dla-zaawansowanych/ # Placeholder "coming soon"
|
|
│ └── page.jsx
|
|
├── components/
|
|
│ ├── Nav.jsx # Nawigacja (client — używa usePathname)
|
|
│ ├── BentoCard.jsx # Karta artykułu w siatce Bento
|
|
│ ├── BentoSlideshow.jsx # Slideshow featured artykułów
|
|
│ ├── ProductCard.jsx # Karta produktu na /produkty
|
|
│ ├── SubscribeBox.jsx # Formularz zapisu do newslettera
|
|
│ ├── CookieBanner.jsx # Banner RODO
|
|
│ ├── Footer.jsx # Stopka
|
|
│ └── SchemaOrg.jsx # JSON-LD (server component, async)
|
|
├── sections/
|
|
│ ├── Hero.jsx # Sekcja hero na stronie głównej
|
|
│ ├── Benefits.jsx # Sekcja korzyści
|
|
│ ├── ProductsPreview.jsx # Podgląd produktów na stronie głównej
|
|
│ ├── ComingSoon.jsx # Placeholder dla niegotowych stron
|
|
│ └── ContactPage.jsx # Formularz kontaktowy
|
|
├── lib/
|
|
│ ├── directus.js # Fetch z Directus + mapowanie danych
|
|
│ └── productSchema.js # Generuje JSON-LD schema.org dla produktów
|
|
└── data/
|
|
└── content.js # Statyczne dane: news, videos (sidebar)
|
|
```
|
|
|
|
---
|
|
|
|
### Strony
|
|
|
|
| URL | Plik | Dane |
|
|
|-----|------|------|
|
|
| `/` | `src/app/page.jsx` | Directus `LabAI` (artykuły + slideshow) |
|
|
| `/produkty` | `src/app/produkty/page.jsx` | Directus `products` |
|
|
| `/kontakt` | `src/app/kontakt/page.jsx` | statyczna |
|
|
| `/dla-zaawansowanych` | `src/app/dla-zaawansowanych/page.jsx` | Coming soon |
|
|
|
|
---
|
|
|
|
### Directus CMS
|
|
|
|
**URL:** `https://cms.dexterlab.pl`
|
|
|
|
#### Kolekcje
|
|
|
|
| Kolekcja | Używana przez | Filtr |
|
|
|----------|---------------|-------|
|
|
| `LabAI` | `src/lib/directus.js` | `status = published` |
|
|
| `products` | `src/app/produkty/page.jsx` | `site = labai.pro`, `status = published` |
|
|
|
|
#### Pola kolekcji `LabAI`
|
|
|
|
| Pole | Opis |
|
|
|------|------|
|
|
| `Title` | Tytuł artykułu |
|
|
| `Badge` | HOT / NEW / TUTORIAL / TIPS |
|
|
| `Category` | Kategoria (string) |
|
|
| `Cover_Image` | UUID — obrazek przez `${DIRECTUS_URL}/assets/{id}` |
|
|
| `Is_Featured` | Boolean — czy w slideshow |
|
|
| `status` | `published` = widoczny |
|
|
|
|
#### Pola kolekcji `products`
|
|
|
|
| Pole | Opis |
|
|
|------|------|
|
|
| `title` | Nazwa produktu |
|
|
| `short_description` | Opis |
|
|
| `price` | Cena bazowa |
|
|
| `discount_price` | Cena promocyjna (opcjonalne) |
|
|
| `currency` | np. `PLN` |
|
|
| `url` | Link zakupowy |
|
|
| `product_image` | UUID obrazka |
|
|
| `sku` | Opcjonalne |
|
|
| `site` | `labai.pro` |
|
|
| `status` | `published` |
|
|
| `sort` | Kolejność wyświetlania |
|
|
|
|
#### Uprawnienia
|
|
|
|
Kolekcje `LabAI` i `products` muszą mieć **Public role → Read** włączone w Directus. Bez tego API zwraca 403.
|
|
|
|
---
|
|
|
|
### Cache / revalidacja
|
|
|
|
| Strona | `revalidate` | Zachowanie |
|
|
|--------|-------------|------------|
|
|
| `/` | `0` | Zawsze świeże (fetch przy każdym requescie) |
|
|
| `/produkty` | `300` | ISR — odświeżenie co 5 minut |
|
|
|
|
Gdy ruch urośnie: wdrożyć on-demand revalidation przez `POST /api/revalidate` + webhook w Directus.
|
|
|
|
---
|
|
|
|
### Zewnętrzne integracje
|
|
|
|
| Serwis | Gdzie | Co robi |
|
|
|--------|-------|---------|
|
|
| Libredesk | `src/app/layout.jsx` | Widget czatu (inbox: `bffd72d7-a528-42ca-bfed-377670d49364`) |
|
|
| Umami | `src/app/layout.jsx` | Analityka (`stat.dexterlab.pl`) |
|
|
| Google Fonts | `layout.jsx` (next/font) | Font Inter |
|
|
|
|
---
|
|
|
|
### Uruchamianie lokalnie
|
|
|
|
```bash
|
|
npm run dev # http://localhost:3000
|
|
npm run build # build produkcyjny
|
|
npm run start # serwer produkcyjny
|
|
```
|
|
|
|
---
|
|
|
|
### Dodawanie nowej strony
|
|
|
|
1. Utwórz `src/app/<slug>/page.jsx` — Server Component (async)
|
|
2. Dodaj `export const metadata = { title, description }`
|
|
3. Jeśli pobiera dane z Directus — dodaj `export const revalidate = 0` lub `300`
|
|
4. Dodaj JSON-LD jeśli strona ma unikalny typ treści
|
|
5. Dodaj link w `src/components/Nav.jsx`
|