feat(chat): Klikalne linki do osób + wzmocnione instrukcje AI

- Dodano person_id i profile URL do kontekstu osób w chatbocie
- Zaktualizowano system prompt: OBOWIĄZKOWE linki dla firm i osób
- Dodano CSS dla linków do osób (zielony badge)
- Rozszerzono JavaScript o wykrywanie linków /osoba/

Kolory badge:
- 🏢 Firmy: pomarańczowy (#c2410c)
- 👤 Osoby: zielony (#047857)
- 🔗 Zewnętrzne: niebieski (#1d4ed8)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-01-27 12:34:19 +01:00
parent 95ac4c79e4
commit 09084aa18a
6 changed files with 278 additions and 14 deletions

236
docs/PLAN_UWAGI_JACKA.md Normal file
View File

@ -0,0 +1,236 @@
# Plan implementacji uwag Jacka Pomieczyńskiego
> **Data utworzenia:** 2026-01-27
> **Źródło:** Forum post /forum/18 z 18.01.2026
> **Deadline:** Prezentacja 30.01.2026 godz. 19:00
---
## Podsumowanie propozycji Jacka
| # | Propozycja | Status obecny |
|---|------------|---------------|
| 1 | Logo Nordy w szczegółach | ❌ Brak logo - tylko ikona SVG |
| 2 | Więcej zakładek na pasku głównym | ⚠️ Dropdown "Społeczność" ukrywa 8 pozycji |
| 3 | Zamieszczanie fotek z wydarzeń | ❌ Brak funkcji upload |
| 4 | Moderowanie treści przez Zarząd | ⚠️ Częściowo (admin może) |
| 5 | Responsywność (smartfon) | ✅ Podstawowa (breakpoint 768px) |
---
## PLAN DZIAŁANIA
### 🟢 PRIORYTET 1: Logo Nordy (przed prezentacją)
**Cel:** Dodać oficjalne logo Izby Norda Biznes
**Obecny stan:**
- Ikona SVG (geometryczna) w nagłówku
- Brak pliku logo w `/static/img/`
**Wymagane:**
- [ ] Uzyskać plik logo od Izby (PNG/SVG, min. 200x200px)
- [ ] Zapisać jako `/static/img/norda-logo.png` lub `.svg`
**Zmiany w kodzie:**
```
Plik: templates/base.html
Linia: 936-941
Obecne:
<a href="..." class="nav-brand">
<svg width="32" height="32">...</svg>
<span>Norda Biznes Hub</span>
</a>
Proponowane:
<a href="..." class="nav-brand">
<img src="/static/img/norda-logo.png" alt="Norda Biznes" height="40">
<span>Norda Biznes Hub</span>
</a>
```
**Czas:** ~30 min (po otrzymaniu logo)
**Ryzyko:** 🟢 Minimalne
---
### 🟢 PRIORYTET 2: Reorganizacja nawigacji (przed prezentacją)
**Cel:** Uprościć dostęp do najważniejszych funkcji
**Obecna struktura:**
```
Firmy | Społeczność ▾ | Raporty | [Powiadomienia] | [User]
└── Aktualności
└── Kalendarz
└── Forum
└── Tablica B2B
└── NordaGPT
└── ZOP Kaszubia
└── Kontakty zewnętrzne
└── Mapa Powiązań
```
**Propozycja Jacka:**
```
NORDA GPT | FIRMY | KALENDARIUM | B2B | FORUM | LOKALNE PROJEKTY
```
**Moja rekomendacja (kompromis):**
```
Firmy | NordaGPT | Kalendarz | B2B | Forum | Więcej ▾ | [Powiadomienia] | [User]
└── Aktualności
└── ZOP Kaszubia
└── Kontakty zewnętrzne
└── Raporty
└── Mapa Powiązań
```
**Uzasadnienie:**
- Najczęściej używane funkcje bezpośrednio widoczne
- "Więcej" zamiast "Społeczność" - jaśniejsza nazwa
- Zachowujemy porządek (nie za dużo pozycji w głównym menu)
- "LOKALNE PROJEKTY" = ZOP Kaszubia (już istnieje)
**Zmiany w kodzie:**
```
Plik: templates/base.html
Linie: 949-969
Zmiana struktury <ul class="nav-menu">
```
**Czas:** ~2-3h
**Ryzyko:** 🟢 Niskie (tylko HTML/CSS, łatwy rollback)
---
### 🟡 PRIORYTET 3: Weryfikacja responsywności (przed prezentacją)
**Cel:** Upewnić się, że portal działa dobrze na smartfonach
**Obecny stan:**
- Breakpoint: 768px
- Menu mobilne: hamburger → rozwijane menu
- Admin bar: ukryty na mobile
**Plan testów:**
1. [ ] Test na iPhone (Safari)
2. [ ] Test na Android (Chrome)
3. [ ] Sprawdzić: katalog firm, profil firmy, chat, kalendarz, forum
4. [ ] Zidentyfikować problemy z układem
**Potencjalne poprawki:**
- Rozmiary fontów na mobile
- Padding/margin w kartach firm
- Formularze (szerokość inputów)
- Tabele (horizontal scroll)
**Czas:** ~2-4h (test + poprawki)
**Ryzyko:** 🟢 Niskie
---
### 🔴 PRIORYTET 4: Upload fotek (PO prezentacji)
**Cel:** Możliwość dodawania zdjęć do wydarzeń/forum
**Wymagane komponenty:**
1. **Backend:**
- Endpoint `/api/upload/image`
- Walidacja: typ pliku, rozmiar (max 5MB)
- Kompresja/resize obrazów
- Storage: `/static/uploads/` lub S3
2. **Frontend:**
- Komponent drag & drop
- Podgląd przed wysłaniem
- Progress bar
3. **Integracja:**
- Forum: galeria w poście
- Kalendarz: zdjęcia z wydarzenia
**Zależności:**
- Biblioteka: Pillow (Python) do przetwarzania obrazów
- Może wymagać: dodatkowego storage na serwerze
**Czas:** ~8-12h
**Ryzyko:** 🟡 Średnie (nowa funkcjonalność)
---
### 🟡 PRIORYTET 5: Moderowanie treści (PO prezentacji)
**Cel:** Kierownik Izby i Zarząd mogą moderować posty
**Obecny stan:**
- Tylko admin (`is_admin=True`) może edytować/usuwać
- Brak roli "moderator" lub "zarząd"
**Propozycja:**
1. Dodać pole `role` w tabeli `users`:
- `member` (domyślnie)
- `board` (zarząd)
- `manager` (kierownik)
- `admin`
2. Uprawnienia:
| Akcja | member | board | manager | admin |
|-------|--------|-------|---------|-------|
| Czytanie | ✅ | ✅ | ✅ | ✅ |
| Tworzenie | ✅ | ✅ | ✅ | ✅ |
| Edycja własnych | ✅ | ✅ | ✅ | ✅ |
| Edycja cudzych | ❌ | ✅ | ✅ | ✅ |
| Usuwanie | ❌ | ✅ | ✅ | ✅ |
| Pin/Unpin | ❌ | ✅ | ✅ | ✅ |
**Zmiany:**
- Migracja SQL: ALTER TABLE users ADD COLUMN role
- Dekorator: `@moderator_required`
- UI: przyciski moderacji dla uprawnionych
**Czas:** ~4-6h
**Ryzyko:** 🟡 Średnie (zmiany w bazie danych)
---
## HARMONOGRAM
### Do prezentacji (27-29.01.2026):
| Dzień | Zadanie | Czas |
|-------|---------|------|
| **Pon 27.01** | Uzyskać logo Nordy | - |
| **Pon 27.01** | Plan (ten dokument) ✅ | 1h |
| **Wt 28.01** | Logo + nawigacja (DEV) | 3h |
| **Wt 28.01** | Testy responsywności | 2h |
| **Śr 29.01** | Poprawki responsywności | 2h |
| **Śr 29.01** | Deploy na PROD | 1h |
| **Śr 29.01** | Testy końcowe PROD | 1h |
### Po prezentacji (luty 2026):
| Tydzień | Zadanie |
|---------|---------|
| 1 | Upload fotek (podstawowa wersja) |
| 2 | Moderowanie treści |
| 3 | Testy i poprawki |
---
## PYTANIA DO WŁAŚCICIELA
1. **Logo:** Czy masz plik logo Izby Norda Biznes? (PNG/SVG)
2. **Nawigacja:** Czy proponowany układ jest OK, czy preferujesz dokładnie jak Jacek?
3. **LOKALNE PROJEKTY:** Czy ZOP Kaszubia wystarczy, czy chcesz osobną sekcję?
4. **Moderatorzy:** Kto konkretnie ma mieć uprawnienia moderatora? (imiona/emaile)
---
## DECYZJA
- [ ] Zatwierdzam plan - rozpoczynamy implementację
- [ ] Zmiany w planie: _______________
- [ ] Odkładamy do po prezentacji

BIN
downloaded/Harmonogram.pdf Normal file

Binary file not shown.

BIN
downloaded/Regulamin.pdf Normal file

Binary file not shown.

View File

@ -444,15 +444,17 @@ class NordaBizChatEngine:
people_by_company = {} people_by_company = {}
for cp in company_people: for cp in company_people:
company_name = cp.company.name if cp.company else 'Nieznana' company_name = cp.company.name if cp.company else 'Nieznana'
company_profile = f"https://nordabiznes.pl/company/{cp.company.slug}" if cp.company and cp.company.slug else None
if company_name not in people_by_company: if company_name not in people_by_company:
people_by_company[company_name] = [] people_by_company[company_name] = {'profile': company_profile, 'people': []}
person_info = { person_info = {
'name': cp.person.full_name() if cp.person else '', 'name': cp.person.full_name() if cp.person else '',
'profile': f"https://nordabiznes.pl/osoba/{cp.person.id}" if cp.person else None,
'role': cp.role[:30] if cp.role else '' 'role': cp.role[:30] if cp.role else ''
} }
if cp.shares_percent: if cp.shares_percent:
person_info['shares'] = f"{cp.shares_percent}%" person_info['shares'] = f"{cp.shares_percent}%"
people_by_company[company_name].append(person_info) people_by_company[company_name]['people'].append(person_info)
context['company_people'] = people_by_company context['company_people'] = people_by_company
@ -886,14 +888,20 @@ class NordaBizChatEngine:
WAŻNE: WAŻNE:
- ZAWSZE podawaj nazwę firmy i kontakt (tel/web/mail jeśli dostępne) - ZAWSZE podawaj nazwę firmy i kontakt (tel/web/mail jeśli dostępne)
🔗 KLIKALNE LINKI (OBOWIĄZKOWE!): 🔗 KLIKALNE LINKI (BEZWZGLĘDNIE OBOWIĄZKOWE!):
Nazwy firm ZAWSZE formatuj jako linki markdown używając pola "profile":
- PRAWIDŁOWO: [Pixlab Softwarehouse](https://nordabiznes.pl/company/pixlab-sp-z-o-o)
- BŁĘDNIE: Pixlab Softwarehouse (bez linku)
- BŁĘDNIE: **Pixlab Softwarehouse** (pogrubienie bez linku)
Nazwy osób (zarząd/wspólnicy) formatuj pogrubieniem + firma w nawiasie z linkiem: KRYTYCZNE - KAŻDA nazwa firmy MUSI być linkiem markdown:
- PRAWIDŁOWO: **Michał Bogdan Roszman** ([Pixlab Softwarehouse](https://nordabiznes.pl/company/pixlab-sp-z-o-o)) - JEDYNY PRAWIDŁOWY FORMAT: [Nazwa Firmy](URL z pola profile)
- NIEDOPUSZCZALNE: Nazwa Firmy (bez linku)
- NIEDOPUSZCZALNE: **Nazwa Firmy** (pogrubienie bez linku)
- NIEDOPUSZCZALNE: "Nazwa Firmy" (cudzysłowy bez linku)
Przykład: [Pixlab Softwarehouse](https://nordabiznes.pl/company/pixlab-sp-z-o-o)
👤 OSOBY - każda osoba (zarząd/wspólnik) też MUSI być linkiem:
- PRAWIDŁOWO: [Michał Bogdan Roszman](https://nordabiznes.pl/osoba/123)
- BŁĘDNIE: **Michał Bogdan Roszman** (pogrubienie bez linku)
- BŁĘDNIE: Michał Bogdan Roszman (tekst bez linku)
W sekcji ZARZĄD I WSPÓLNICY każda osoba ma pole "profile" z URL - UŻYJ GO!
Inne linki które MUSISZ dołączać gdy dostępne: Inne linki które MUSISZ dołączać gdy dostępne:
Strona www firmy (pole "web" lub "url") Strona www firmy (pole "web" lub "url")

View File

@ -309,6 +309,18 @@
box-shadow: 0 2px 4px rgba(194, 65, 12, 0.2); box-shadow: 0 2px 4px rgba(194, 65, 12, 0.2);
} }
/* 👤 Linki do OSÓB (nordabiznes.pl/osoba/) - zielony */
.message-content a.person-link {
background: #ecfdf5;
color: #047857;
}
.message-content a.person-link:hover {
background: #d1fae5;
color: #065f46;
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(4, 120, 87, 0.2);
}
/* 🔗 Linki ZEWNĘTRZNE (www, social media, maps) - niebieski */ /* 🔗 Linki ZEWNĘTRZNE (www, social media, maps) - niebieski */
.message-content a.external-link { .message-content a.external-link {
background: #eff6ff; background: #eff6ff;
@ -1509,17 +1521,25 @@ function formatMessage(text) {
text = escapeHtml(text); text = escapeHtml(text);
// Convert markdown links [text](url) to <a> tags with appropriate class // Convert markdown links [text](url) to <a> tags with appropriate class
// Links to company profiles get 'company-link' class, others get 'external-link' // Links: company (orange), person (green), external (blue)
text = text.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, function(match, linkText, url) { text = text.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, function(match, linkText, url) {
const isCompanyLink = url.includes('nordabiznes.pl/company/'); let linkClass = 'external-link';
const linkClass = isCompanyLink ? 'company-link' : 'external-link'; if (url.includes('nordabiznes.pl/company/')) {
linkClass = 'company-link';
} else if (url.includes('nordabiznes.pl/osoba/')) {
linkClass = 'person-link';
}
return '<a href="' + url + '" target="_blank" rel="noopener" class="' + linkClass + '">' + linkText + '</a>'; return '<a href="' + url + '" target="_blank" rel="noopener" class="' + linkClass + '">' + linkText + '</a>';
}); });
// Convert raw URLs to links (only those not already in <a> tags) // Convert raw URLs to links (only those not already in <a> tags)
text = text.replace(/(?<!href="|">)(https?:\/\/[^\s<]+)(?![^<]*<\/a>)/g, function(match, url) { text = text.replace(/(?<!href="|">)(https?:\/\/[^\s<]+)(?![^<]*<\/a>)/g, function(match, url) {
const isCompanyLink = url.includes('nordabiznes.pl/company/'); let linkClass = 'external-link';
const linkClass = isCompanyLink ? 'company-link' : 'external-link'; if (url.includes('nordabiznes.pl/company/')) {
linkClass = 'company-link';
} else if (url.includes('nordabiznes.pl/osoba/')) {
linkClass = 'person-link';
}
return '<a href="' + url + '" target="_blank" rel="noopener" class="' + linkClass + '">' + url + '</a>'; return '<a href="' + url + '" target="_blank" rel="noopener" class="' + linkClass + '">' + url + '</a>';
}); });