Źródło wymagań: FEATURES.md. Ten dokument rozbija je na fazy, ze szczegółowym planem dla zrealizowanych fundamentów i backlogiem dla kolejnych modułów.
- Stack: Next.js 16 (App Router — uwaga: ma breaking changes względem danych treningowych, przed pisaniem kodu sprawdzać
node_modules/next/dist/docs/), TypeScript, Tailwind v4, shadcn/ui, Drizzle ORM + Postgres (docker-compose.yml), better-auth,@solvro/config. - Formularze: zawsze shadcn + react-hook-form + zod, zgodnie z
AGENTS.md(Field/FieldLabel/FieldError+Controller). - Integracje (GitHub, Discord, Solvro Auth/Keycloak) — dostępy są gotowe po stronie organizacji, ale plan zakłada generyczne nazwy zmiennych środowiskowych jako placeholdery do uzupełnienia w
.env(patrz sekcja Konfiguracja). - Eventownik (import zgłoszeń rekrutacyjnych) — świadomie odłożone na backlog, nie wchodzi w zakres MVP.
- „Grupka" (auto-dodawanie do grupy mailingowej w onboardingu) — cel nieokreślony, w MVP zostaje jako jawne TODO/no-op hook w pipeline onboardingu, bez konkretnej integracji.
- USOS — logowanie przez plugin
better-auth-usos. Przed użyciem w Fazie 1 zweryfikować aktualne API pluginu (nazwy opcji, callbacki) w jego dokumentacji/README, bo nie sprawdzałem jego źródeł. - Plan pisany pod model danych, który da się rozbudowywać — ale bez budowania na zapas rzeczy spoza Fazy 1 (np. tabel pod Eventownik czy ranking aktywności trafiają dopiero do Fazy 2/3, gdzie faktycznie są potrzebne).
- Migracja z Excela/Google Sheets — obecne dane źródłowe są w
data/*.tsv. Appka ma przejąć rolę source of truth, więc pola statusu, notatek HR, danych uczelnianych i historii projektów muszą odpowiadać arkuszom bez utraty kluczowego kontekstu. - Zastępowanie małych arkuszy — kolejne moduły, szczególnie finanse i planowanie budżetu, mają przenosić dane z rozproszonych Exceli/Google Sheets do modelu danych HRownika, z kontrolą uprawnień i historią zmian.
- Logo aplikacji — statyczne logo trzymamy w
src/app/icon.png, zgodnie z Next.js App Router file convention dla app icons. Nie przenosić dopublic/.
Struktura plików: server actions w src/actions/<domena>.ts (siostrzany katalog src/app/), komponenty (formularze, dialogi, panele) w src/components/<domena>/, zod schematy w src/lib/schemas/<domena>.ts. Foldery tras pod src/app/ zawierają tylko page.tsx/layout.tsx. Szczegóły w AGENTS.md.
- Konfiguracja środowiska — rozszerzyć
src/env.tso zmienne (placeholdery, bez realnych wartości):GITHUB_APP_ID,GITHUB_APP_PRIVATE_KEY,GITHUB_ORG(dostęp do org Solvro: tworzenie repo, teamów, issues)DISCORD_BOT_TOKEN,DISCORD_GUILD_ID(zaproszenia, role)OIDC_ISSUER_URL,OIDC_CLIENT_ID,OIDC_CLIENT_SECRET(Solvro Auth / self-hosted Keycloak)USOS_CONSUMER_KEY,USOS_CONSUMER_SECRET,USOS_BASE_URL(better-auth-usos)- Zaktualizować
.env.example(jeśli nie istnieje — utworzyć).
- Auth — rozszerzenie
src/lib/auth.ts:- dodać
genericOAuth/OIDC plugin better-auth wskazujący na Solvro Auth (Keycloak), - dodać plugin
better-auth-usos(po weryfikacji API), - zostawić
emailAndPasswordjako fallback do kont bootstrapowych zarządu (do potwierdzenia czy zostaje na stałe, czy tylko na czas wdrożenia).
- dodać
- Struktura schematu DB — rozbić
src/db/schema.tsna katalogsrc/db/schema/z osobnymi plikami (members.ts,sections.ts,roles.ts,projects.ts,github.ts),schema.tsjako barrel re-eksportujący wszystko (zachowującauth-schema.tsbez zmian). - Dodać brakujące komponenty shadcn potrzebne w Fazie 1:
table,dialog,dropdown-menu,badge,tabs,avatar,command,popover,combobox(multi-select sekcji/ról przezcommand+popover+badge, bo shadcn nie ma natywnego multi-selecta).
member (src/db/schema/members.ts)
id, user_id -> user.id (nullable, unique; powiązany dopiero po pierwszym logowaniu)
full_name, github_username, discord_id, facebook_url
student_index (nullable), study_department, study_field, study_year
bio, hr_notes (widoczne tylko dla zarządu)
status: enum('new','active','inactive','honorary')
created_at, updated_at
member_email (src/db/schema/members.ts)
id, member_id -> member.id
email, kind: enum('login','notification')
verified_at (nullable)
section (src/db/schema/sections.ts)
id, name, description
member_section (src/db/schema/sections.ts)
member_id, section_id, joined_at, left_at (nullable)
role_definition (src/db/schema/roles.ts)
id, scope: enum('section','project','board')
name (np. "przewodniczący", "prezes")
permission_level: enum('board','project_lead','member')
github_team_slug (nullable), discord_role_id (nullable)
role_assignment (src/db/schema/roles.ts)
id, member_id -> member.id
role_definition_id -> role_definition.id
section_id (nullable, fk -> section.id), project_id (legacy/nullable, nieużywane dla nowych ról)
-- nowe role projektowe nie są tu tworzone; są częścią team_member.role
started_at, ended_at (nullable = aktualna rola)
-- źródło timeline'u ról organizacyjnych i sekcyjnych na profilu
project (src/db/schema/projects.ts)
id, name, slug, status: enum('active','completed','suspended')
visibility: enum('internal','public')
production_url (nullable)
drive_folder_url (nullable)
project_card_drive_url (nullable)
report_drive_url (nullable, uzupełniane przy zakończeniu)
ended_at (nullable)
created_at, updated_at
project_repository (src/db/schema/github.ts)
id, project_id -> project.id
github_repo_full_name, github_repo_id
added_at
team (src/db/schema/projects.ts)
id, project_id -> project.id
name (frontend/backend/devops/...)
github_team_slug (nullable), discord_role_id (nullable)
team_member (src/db/schema/projects.ts)
team_id -> team.id, member_id -> member.id
role (np. PM, PO, techlead, TS, programista, UI/UX designer, członek zespołu)
joined_at, left_at (nullable)
Uwagi:
- Role zarządu i sekcji są w
role_assignment+role_definition. - Role projektowe są w
team_member.role, razem z historiąjoined_at/left_at. To konsoliduje poprzedni duplikat: project-scopedrole_assignment+ osobneteam_member. - Uprawnienia zarządu wynikają z aktywnych ról board w
role_assignment. Uprawnienia liderów projektów wynikają z aktywnegoteam_memberz rolą leadową (PM,PO,techlead,TS). - Dane ToPWR (
TOPWR_API_BASE_URL) są pobierane server-side i cache'owane przez Nextfetch(..., { next: { revalidate: 86400 } }).
- Funkcja pomocnicza
getMemberPermissions(userId)(src/lib/permissions.ts):- zwraca
{ isBoard: boolean, leadProjectIds: string[], memberId: string }. isBoardpochodzi z aktywnych ról board wrole_assignment.leadProjectIdspochodzi z aktywnych członkostwteam_member(left_at IS NULL) z rolą leadową.- używana w server actions / route handlers do bramkowania zapisu (board → wszystko, project_lead → tylko swoje projekty/zespoły, member → tylko własny rekord
memberi tylko pola sociali + dane studiów).
- zwraca
- Middleware/
auth()helper sprawdzający sesję better-auth i mapującyuser.id→member(pomember.user_id, a przy braku powiązania — po dopasowaniu e-maila/indeksu przy pierwszym logowaniu).
/login— logowanie (Solvro Auth OIDC + USOS; email/password jako fallback do potwierdzenia)./members— lista członków z polskim statusem (nowy,aktywny,nieaktywny,honorowy)./members/new— formularz onboardingu (RHF+zod+shadcn), dostępny dla zarządu./members/import— import członków z danych tabelarycznych, używany przy migracji z arkuszy./members/[id]— profil: dane, GitHub activity, osobny timeline projektów (team_member) oraz historia ról organizacyjnych/sekcyjnych (role_assignment). Notatki HR są widoczne tylko dla zarządu./members/[id]/edit— edycja: pełna dla zarządu, ograniczona (sociale + dane studiów) dla właściciela profilu. Notatki HR i status są board-only./members/[id]/activity— dedykowany widok aktywności GitHub członka./sections,/sections/[id]— lista i widok sekcji z członkami./projects,/projects/[id]— lista i widok projektu: atrybuty, repozytoria, aktywność, ranking, zespoły, członkowie zespołów i ich role projektowe./projects/new— utworzenie projektu z wyborem istniejących repozytoriów (lista z GitHub API danej organizacji)./projects/[id]/edit— edycja danych projektu, zespołów, linków i repozytoriów zgodnie z uprawnieniami./projects/[id]/activity— dedykowany widok aktywności GitHub projektu./projects/[id]/repos/[repoId]— placeholder na podgląd issues/PR (pełna integracja w Fazie 2), na razie link do repo na GitHubie.
Po zapisaniu nowego członka:
- Zaproszenie do GitHub org (GitHub API, wymaga
GITHUB_APP_ID/PAT z uprawnieniemmembers:write). - Nadanie roli/zaproszenie na Discordzie (Discord API, wymaga bota na serwerze z uprawnieniem
Manage Roles/Create Instant Invite). - TODO jawne, no-op: dodanie do „grupki" — zostawić wywołanie funkcji
inviteToGroup(member)wsrc/lib/onboarding.tsjako pusty stub z komentarzem, do podpięcia gdy ustalony zostanie cel integracji.
Sync zespołów (zmiana składu team_member):
- przy dodaniu członka do zespołu → nadanie GitHub team membership + roli Discord zespołu,
- przy zakończeniu członkostwa (
left_at) → odwrotnie (revoke), ale historia zostaje w bazie. - Zaimplementować jako funkcje w
src/lib/integrations/github.tsidiscord.ts, wywoływane z server actions zarządzającychteam_member.
- Import zgłoszeń z Eventownika (rekrutacja) — brak integracji, na razie ręczne tworzenie członków.
- Generowanie nowych repozytoriów z szablonów tech-stacków.
- GitHub activity timeline (per profil i per projekt) + ranking top-5 kontrybutorów.
- Pełny podgląd issues/PR repozytorium w HRowniku + prefillowane akcje tworzenia issue.
- Workflow zakończenia projektu (auto-task w GitHub „KN Solvro" + 4-krokowy checklist sprawozdania).
- Integracja „grupki".
Ostatecznie webhooki zamiast pollingu w cronie (zmiana względem pierwszej wersji tego planu):
- Tabela
github_activity_event(project_repository_id,project_idzdenormalizowane,member_idnullable,github_login,type: commit/pull_request/issue,external_iddo dedupe,occurred_at,url), unique na(project_repository_id, type, external_id). POST /api/webhooks/github— odbiornik zdarzeń GitHub App (push,pull_requestaction=opened,issuesaction=opened), weryfikacja podpisu HMAC SHA-256 (GITHUB_WEBHOOK_SECRET, ta sama wartość co "Webhook secret" w ustawieniach apki). To główne, bieżące źródło danych.- REST-owy backfill (
src/lib/integrations/github-activity.ts: syncRepositories) — nie jako cron, tylko: (a) automatycznie raz przy podpięciu repo do projektu (createProject, uruchamiane w tle przezafter()żeby nie blokować redirecta), (b) na żądanie przez przycisk „Synchronizuj aktywność" na stronie projektu (zarząd/liderzy) — pokrywa initial data collection i ręczne doganianie historii. - Filtrowanie botów:
isBotLogin— login kończący się na[bot]. - Timeline na profilu członka (segmentowany per projekt) i timeline zagregowany na stronie projektu.
- Ranking top 5 kontrybutorów (tydzień/miesiąc) per projekt —
getContributorRanking, agregacja pogithub_activity_event. - Podgląd issues/PR na stronie repozytorium (live z GitHub API, nie cache'owane) +
AssignIssuePicker— wybór członka z listy → linkgithub.com/.../issues/new?assignees=....
- Wybór tech-stacku przy
/projects/new→ tworzenie repo w organizacji Solvro z template repo (GitHub APIgenerateRepositoryz template), wstępnie skonfigurowanego@solvro/config. - Wymaga katalogu znanych template-repo per stack (do ustalenia z zarządem, jakie stacki/szablony istnieją).
- Po zmianie statusu projektu na
completed: utworzenie issue w repo „KN Solvro" przypisanego do liderów projektu („wypełnij sprawozdanie projektu"), z checklistą 4 kroków śledzoną w HRowniku (project.report_drive_url+ status checklisty).
- Integracja API Eventownika (do ustalenia: dostępność/dokumentacja API) — import zgłoszeń jako kandydatury, konwersja zaakceptowanej kandydatury na onboarding członka.
Celem tej fazy jest dalsze ograniczenie rozproszonych Exceli, dokumentów i notatek. HRownik ma stać się miejscem, w którym zarząd i kierownicy projektów utrzymują wiedzę operacyjną, budżety oraz historię spotkań/decyzji.
Zakres:
- Baza wiedzy z dokumentami/notatkami tworzonymi w HRowniku, zamiast trzymania kluczowych instrukcji w prywatnych plikach.
- Kategorie lub tagi, np.
zarząd,sekcje,projekty,finanse,rekrutacja,wydarzenia,infrastruktura. - Widoczność dokumentów: wszyscy członkowie, tylko zarząd, tylko liderzy projektów albo osoby przypisane do konkretnego projektu.
- Dokumenty projektowe przypięte do projektu: decyzje, linki, ustalenia, instrukcje wdrożeniowe, retrospekcje.
- Wyszukiwanie po tytule i treści.
- Metadane: autor, ostatni edytujący,
created_at,updated_at.
Proponowany model danych:
knowledge_article
id, title, slug, content
visibility: enum('all_members','board','project_leads','project')
project_id (nullable, fk -> project.id)
author_member_id -> member.id
last_editor_member_id -> member.id
created_at, updated_at
knowledge_tag
id, name, slug
knowledge_article_tag
article_id -> knowledge_article.id
tag_id -> knowledge_tag.id
Trasy:
/knowledge— lista i wyszukiwarka artykułów./knowledge/new— tworzenie artykułu./knowledge/[slug]— widok artykułu./knowledge/[slug]/edit— edycja zgodnie z uprawnieniami./projects/[id]— sekcja z artykułami przypiętymi do projektu.
Uprawnienia:
- Zarząd tworzy i edytuje wszystkie artykuły.
- Liderzy projektów tworzą i edytują artykuły przypięte do swoich projektów.
- Członkowie czytają tylko artykuły, do których mają widoczność.
Zakres:
- Moduł ma zastąpić małe Excelki używane do budżetów, rozliczeń i planowania kosztów.
- Budżet organizacji obejmuje wpływy i wydatki całego koła.
- Budżet projektu obejmuje wpływy i wydatki konkretnego projektu.
- Każda pozycja budżetu rozróżnia plan i wykonanie: kwotę planowaną, kwotę rzeczywistą, datę planowaną, datę realizacji.
- Wpisy mają kategorie, opis, status i opcjonalny link do faktury, potwierdzenia, dokumentu Drive albo innego załącznika.
- Kierownicy projektów mogą edytować budżety swoich projektów.
- Zarząd widzi i edytuje budżet organizacji oraz wszystkie budżety projektowe.
- Widoki pokazują podsumowania: suma planowanych wpływów, suma planowanych wydatków, wykonanie, różnica względem planu, nierozliczone pozycje.
Proponowany model danych:
budget
id, name
scope: enum('organization','project')
project_id (nullable, fk -> project.id)
fiscal_year (nullable)
created_at, updated_at
budget_category
id, name
type: enum('income','expense')
budget_entry
id, budget_id -> budget.id
category_id -> budget_category.id
type: enum('income','expense')
status: enum('planned','approved','incurred','settled','rejected')
title, description
planned_amount, actual_amount
planned_at, occurred_at
document_url (nullable)
created_by_member_id -> member.id
updated_by_member_id -> member.id
created_at, updated_at
Trasy:
/budget— budżet organizacji, dostępny dla zarządu i osób finansowych./budget/projects— zbiorczy widok budżetów projektowych./projects/[id]/budget— budżet konkretnego projektu dla zarządu i kierowników projektu./projects/[id]/budget/new— dodanie pozycji budżetowej projektu./budget/entries/[id]/edit— edycja pozycji budżetowej.
Otwarte decyzje:
- Czy kwoty mają być tylko w PLN, czy potrzebne są waluty?
- Czy potrzebny jest workflow akceptacji pozycji przez skarbnika/zarząd przed oznaczeniem jako
approved? - Czy załączniki mają być tylko URL-em do Drive'a, czy pliki mają być uploadowane do aplikacji?
Zakres:
- Rejestr spotkań zarządu i spotkań strategicznych.
- Agenda jako lista punktów przygotowywanych przed spotkaniem.
- Notatki, decyzje i action itemy uzupełniane po spotkaniu.
- Obecność członków na spotkaniach, w tym informacja „kto pojawił się na strategicznym”.
- Action itemy mają właściciela, termin i status, żeby dało się wracać do ustaleń z poprzednich spotkań.
Proponowany model danych:
board_meeting
id, title
type: enum('board','strategic','operational','other')
status: enum('planned','completed','cancelled')
scheduled_at, completed_at
summary
created_by_member_id -> member.id
created_at, updated_at
board_meeting_attendee
meeting_id -> board_meeting.id
member_id -> member.id
presence: enum('present','absent','excused')
board_meeting_agenda_item
id, meeting_id -> board_meeting.id
title, description
owner_member_id (nullable, fk -> member.id)
position
decision_notes
board_meeting_action_item
id, meeting_id -> board_meeting.id
agenda_item_id (nullable, fk -> board_meeting_agenda_item.id)
title, description
owner_member_id -> member.id
due_at
status: enum('open','done','cancelled')
Trasy:
/board/meetings— lista spotkań zarządu./board/meetings/new— utworzenie spotkania i agendy./board/meetings/[id]— agenda, obecność, notatki, decyzje i action itemy./board/meetings/[id]/edit— edycja spotkania./members/[id]— sekcja z historią obecności na spotkaniach zarządu/strategicznych, widoczna zgodnie z uprawnieniami.
Uprawnienia:
- Zarząd zarządza spotkaniami, agendami, obecnością i action itemami.
- Członek przypisany do action itemu może aktualizować status własnego zadania, jeśli zarząd zdecyduje, że ma to być dostępne poza zarządem.
- Dane ze spotkań strategicznych domyślnie są board-only, dopóki konkretny typ notatek nie zostanie oznaczony jako widoczny szerzej.
- Czy
emailAndPasswordw better-auth zostaje na stałe (np. dla kont serwisowych) czy docelowo tylko OIDC + USOS? - Jaki jest faktyczny cel „grupki" w onboardingu?
- Jakie tech-stacki/template-repo mają być dostępne przy tworzeniu nowego projektu (Faza 3)?
- Dostępność i kształt API Eventownika (Faza 5).
- Czy moduł finansów ma obsługiwać tylko PLN, czy też wiele walut?
- Kto formalnie zatwierdza pozycje budżetowe: zarząd, skarbnik, czy kierownik projektu do określonego limitu?
- Czy dokumenty finansowe mają być przechowywane jako linki do Drive'a, czy jako upload plików do aplikacji?
- Czy notatki ze spotkań zarządu mają mieć granularną widoczność per punkt agendy, czy wystarczy widoczność całego spotkania?