Park Naukowo-Technologiczny w OpoluPolski Klaster Baterii Elektrycznych

Instrukcja - Tworzenie Treści

Przewodnik krok po kroku: od nowego obiektu w panelu Directusa, przez uprawnienia publiczne, po komponent na stronie. Spis istniejących kolekcji i ich pól jest w pliku directus/SCHEMA.md w repozytorium.

Słowniczek: kolekcja = tabela (np. Artykul), pole = kolumna, element = rekord, singleton = kolekcja z jednym elementem (np. Strona_Glowna).

1. Tworzenie kolekcji w panelu

Panel dev: http://localhost:8055 (lub http://cms.<slug>.localhost). Zaloguj się jako admin (dane w pliku .env).

  1. Settings (ikona koła zębatego w lewym pasku) → Data Model → przycisk + (Create Collection).
  2. Name: nazwa techniczna kolekcji. Zasady:
    • po polsku, bez polskich znaków i spacji: Artykul, Stopka_Linki, Partner;
    • liczba pojedyncza, pierwsza litera wielka, słowa łączone _;
    • ta nazwa trafia do kodu frontendu, więc nie zmieniaj jej później.
  3. Primary Key: zostaw Generated UUID.
  4. Zakładka Optional System Fields: zaznacz status (Published/Draft/Archived; frontend pokazuje tylko published), sort (ręczna kolejność), date_created i date_updated (daty do sitemap).
  5. Zapisz. Dla singletonu (np. ustawienia strony głównej) wejdź w kolekcję → ikona ołówka (Collection Setup) → włącz Singleton.

Dodawanie pól

W kolekcji kliknij Create Field i wybierz typ interfejsu. Najczęściej używane:

Chcesz mieć Wybierz w kreatorze Nazwa techniczna (przykład) Uwagi
krótki tekst Input (String) tytul, nazwa zaznacz Required dla tytułu
adres URL podstrony Input (String) slug zaznacz Unique; małe litery, myślniki
dłuższy tekst bez formatowania Textarea opis, krotki_opis do kart i meta description
treść z formatowaniem WYSIWYG tresc, artykul obrazki z edytora działają przez /assets/
jeden obraz Image thumbnail, baner, emblemat w bazie zapisuje się id pliku
wiele plików do pobrania Files (relacja M2M) pliki Directus utworzy tabelę <Kolekcja>_files
data / data i godzina / godzina Datetime z typem date / timestamp / time data_rozpoczecia, Data_publikacji, czas_rozpoczecia  
wybór z listy Dropdown Typ, widocznosc, typ wpisz dozwolone wartości
tak / nie Dropdown z wartościami tak/nie wymagane_zapisy w szablonie nie używamy Boolean (spójność z PNT)
liczba (kwota) Input (Decimal) wartosc_projektu  
punkt na mapie Map (Geometry, Point) mapa wymaga PostGIS (jest w stacku)
link zewnętrzny Input (String) link, strona_wydarzenia pełny URL z https://

Wskazówki:

  • Nazwa techniczna pola (Key) to to, co czyta frontend. Etykietę i opis ustawiasz dowolnie w zakładce Field.
  • Nie da się zmienić Key po utworzeniu. Jeśli się pomylisz, usuń pole i utwórz ponownie.
  • Pola z plikami (Image, Files) wymagają uprawnienia Public na directus_files (w szablonie już jest).

Dodawanie treści

Content (ikona listy w lewym pasku) → kolekcja → +. Ustaw status na Published, inaczej frontend elementu nie pokaże. W polu slug wpisz np. jesienny-piknik.

2. Uprawnienia publiczne (bez tego frontend dostaje 403)

Frontend pobiera dane bez logowania, więc rola Public musi mieć odczyt każdej kolekcji, którą ma wyświetlać, oraz jej tabeli łączącej z plikami.

W panelu

  1. Settings → Access Policies → polityka Public.
  2. W sekcji Permissions kliknij Add Collection i wybierz kolekcję (np. Partner).
  3. W wierszu kolekcji kliknij ikonę w kolumnie Read (oko) → All Access.
  4. Jeśli kolekcja ma pole pliki, powtórz dla tabeli <Kolekcja>_files (np. Partner_files). Tabele łączące są ukryte; w oknie Add Collection włącz System Collections lub wyszukaj po nazwie.
  5. directus_files → Read jest już nadane. Nie nadawaj Public żadnych praw Create / Update / Delete.

Szybki test z terminala (oczekiwane 200):

curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8055/items/Partner

Przez API (gdy wolisz skrypt)

# token admina
TOKEN=$(curl -s -X POST http://localhost:8055/auth/login -H 'Content-Type: application/json' \
  -d '{"email":"<ADMIN_EMAIL>","password":"<ADMIN_PASSWORD>"}' | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['access_token'])")

# id polityki Public (nazwa wyświetla się jako "$t:public_label")
curl -s -H "Authorization: Bearer $TOKEN" 'http://localhost:8055/policies?fields=id,name'

# nadanie odczytu (podstaw PUBLIC_POLICY_ID)
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  http://localhost:8055/permissions -d '[
    {"policy":"PUBLIC_POLICY_ID","collection":"Partner","action":"read","fields":["*"]},
    {"policy":"PUBLIC_POLICY_ID","collection":"Partner_files","action":"read","fields":["*"]}
  ]'

Uprawnienia są zapisane w bazie, więc trafiają do seeda (seed/make-seed.sh) i do każdej nowej instalacji.

3. Przykład od początku do końca: kolekcja Partner → sekcja „Partnerzy” na stronie głównej

Krok A. Kolekcja w Directusie

  1. Data Model → + → Name: Partner, pola systemowe: status, sort, date_created, date_updated.
  2. Pola:
Key Interfejs Ustawienia
nazwa Input (String) Required
logo Image  
link Input (String) pełny URL
opis Textarea opcjonalny
  1. Access Policies → Public → Add Collection Partner → Read: All Access.
  2. Content → Partner → dodaj 3–4 elementy z logo, status Published.
  3. Test: curl -s http://localhost:8055/items/Partner zwraca JSON z elementami.

Krok B. Zgłoszenie dla programisty (lub asystenta AI)

Wystarczy komunikat w tej formie:

Utworzyłem w Directusie kolekcję Partner (pola: nazwa string wymagane, logo image, link url, opis textarea, systemowe status/sort). Public ma odczyt. Chcę sekcję „Partnerzy” na stronie głównej pod projektami: siatka logotypów, kliknięcie otwiera link w nowej karcie, sortowanie po sort.

Z tego wynika wszystko, co trzeba zakodować. Jeśli kolekcja ma mieć podstrony (/cos/[slug]), dopisz to i dodaj pole slug.

Krok C. Frontend (co powstaje z takiego zgłoszenia)

1. Typ w frontend/src/lib/types.ts:

/** Kolekcja `Partner`. */
export interface Partner {
  id: string;
  status: Status;
  sort?: number | null;
  nazwa: string;
  logo?: string | null;   // id pliku
  link?: string | null;
  opis?: string | null;
}

2. Komponent frontend/src/components/PartnersSection.tsx (serwerowy, bez stanu):

import ScrollReveal from "./ScrollReveal";
import { assetUrl } from "@/lib/directus";
import type { Partner } from "@/lib/types";

export default function PartnersSection({ partners, title = "Partnerzy" }: { partners: Partner[]; title?: string }) {
  if (partners.length === 0) return null;
  return (
    <section id="partnerzy" className="py-24 bg-gray-50 border-t border-gray-100">
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <ScrollReveal>
          <h2 className="text-3xl md:text-4xl font-bold text-primary text-center mb-16">{title}</h2>
        </ScrollReveal>
        <div className="grid grid-cols-2 md:grid-cols-4 gap-8">
          {partners.map((p, idx) => (
            <ScrollReveal key={p.id} delay={idx * 80}>
              <a href={p.link || "#"} target="_blank" rel="noreferrer" title={p.opis || p.nazwa}
                 className="flex items-center justify-center bg-white rounded-2xl border border-gray-200 p-6 h-32 hover:shadow-lg hover:-translate-y-1 transition-all">
                {p.logo ? (
                  // eslint-disable-next-line @next/next/no-img-element
                  <img src={assetUrl(p.logo, { width: 400, format: "webp" })} alt={p.nazwa} className="max-h-16 w-auto object-contain" />
                ) : (
                  <span className="font-bold text-primary text-center">{p.nazwa}</span>
                )}
              </a>
            </ScrollReveal>
          ))}
        </div>
      </div>
    </section>
  );
}

3. Pobranie danych i wstawienie sekcji w frontend/src/app/page.tsx:

import PartnersSection from "@/components/PartnersSection";
import type { Partner } from "@/lib/types";
// ...
const partners = await getPublished<Partner>("Partner", { sort: "sort", fields: ["id", "nazwa", "logo", "link", "opis"] });
// ...w JSX, np. pod <ProjectsSection />:
<PartnersSection partners={partners} />

4. Dokumentacja: dopisz kolekcję do directus/SCHEMA.md. Jeśli ma podstrony, dodaj trasę w frontend/src/app/sitemap.ts.

5. Sprawdzenie: cd frontend && npm run lint && npx tsc --noEmit, potem npm run dev i podgląd strony głównej.

Krok D. Seed

Gdy kolekcje i treści przykładowe są gotowe: seed/make-seed.sh w katalogu głównym projektu i commit katalogu seed/. Nowe instalacje szablonu dostaną kolekcję, uprawnienia i dane od razu.

4. Lista kontrolna dla nowej kolekcji

  • Nazwa kolekcji po polsku, bez znaków diakrytycznych, nie będzie zmieniana.
  • Pola systemowe: status, sort, date_created, date_updated.
  • slug (Unique), jeśli kolekcja ma podstrony.
  • Public → Read na kolekcji i na <Kolekcja>_files (jeśli jest pole pliki).
  • Przynajmniej jeden element w statusie Published do testów.
  • curl http://localhost:8055/items/<Kolekcja> zwraca 200 z danymi.
  • Zgłoszenie dla programisty: nazwa, pola, gdzie i jak ma się wyświetlać.
  • Po wdrożeniu na froncie: wpis w SCHEMA.md, ewentualnie sitemap.ts, odświeżony seed.
Instrukcja - Tworzenie Treści | Polski Klaster Baterii Elektrycznych