- Add /admin/ai-usage/user/<id> route for detailed AI usage per user - Add ai_usage_user.html template with stats, usage breakdown, logs - Make user names clickable in AI usage dashboard ranking - Replace all native browser dialogs (alert, confirm) with styled modals/toasts: - admin/fees.html, forum.html, recommendations.html, announcements.html, debug.html - calendar/admin.html, event.html - company_detail.html, company/recommend.html - forum/new_topic.html, topic.html - classifieds/view.html - auth/reset_password.html Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
346 lines
10 KiB
HTML
Executable File
346 lines
10 KiB
HTML
Executable File
{% extends "base.html" %}
|
||
|
||
{% block title %}Polec firmę - {{ company.name }} - Norda Biznes Hub{% endblock %}
|
||
|
||
{% block extra_css %}
|
||
<style>
|
||
.recommend-container {
|
||
max-width: 800px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.recommend-breadcrumb {
|
||
margin-bottom: var(--spacing-lg);
|
||
font-size: var(--font-size-sm);
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.recommend-breadcrumb a {
|
||
color: var(--primary);
|
||
text-decoration: none;
|
||
}
|
||
|
||
.recommend-breadcrumb a:hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.recommend-form {
|
||
background: var(--surface);
|
||
border-radius: var(--radius-xl);
|
||
padding: var(--spacing-2xl);
|
||
box-shadow: var(--shadow-lg);
|
||
}
|
||
|
||
.form-header {
|
||
margin-bottom: var(--spacing-xl);
|
||
}
|
||
|
||
.form-header h1 {
|
||
font-size: var(--font-size-2xl);
|
||
color: var(--text-primary);
|
||
margin-bottom: var(--spacing-sm);
|
||
}
|
||
|
||
.form-header p {
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.company-info {
|
||
background: #f0f9ff;
|
||
border: 1px solid #bae6fd;
|
||
border-radius: var(--radius);
|
||
padding: var(--spacing-lg);
|
||
margin-bottom: var(--spacing-xl);
|
||
}
|
||
|
||
.company-info h3 {
|
||
font-size: var(--font-size-base);
|
||
font-weight: 600;
|
||
color: #0369a1;
|
||
margin-bottom: var(--spacing-sm);
|
||
}
|
||
|
||
.company-info p {
|
||
margin: 0;
|
||
color: #0c4a6e;
|
||
font-size: var(--font-size-sm);
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: var(--spacing-lg);
|
||
}
|
||
|
||
.form-label {
|
||
display: block;
|
||
font-weight: 500;
|
||
margin-bottom: var(--spacing-sm);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.form-label .required {
|
||
color: var(--error);
|
||
}
|
||
|
||
.form-input {
|
||
width: 100%;
|
||
padding: var(--spacing-md);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
font-size: var(--font-size-base);
|
||
font-family: var(--font-family);
|
||
transition: var(--transition);
|
||
}
|
||
|
||
.form-input:focus {
|
||
outline: none;
|
||
border-color: var(--primary);
|
||
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
||
}
|
||
|
||
.form-textarea {
|
||
min-height: 200px;
|
||
resize: vertical;
|
||
}
|
||
|
||
.form-hint {
|
||
font-size: var(--font-size-sm);
|
||
color: var(--text-secondary);
|
||
margin-top: var(--spacing-xs);
|
||
}
|
||
|
||
.form-checkbox {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: var(--spacing-sm);
|
||
}
|
||
|
||
.form-checkbox input[type="checkbox"] {
|
||
margin-top: 4px;
|
||
width: 18px;
|
||
height: 18px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.form-checkbox label {
|
||
cursor: pointer;
|
||
font-weight: normal;
|
||
}
|
||
|
||
.form-actions {
|
||
display: flex;
|
||
gap: var(--spacing-md);
|
||
margin-top: var(--spacing-xl);
|
||
}
|
||
|
||
.guidelines {
|
||
background: #fef3c7;
|
||
border: 1px solid #fde68a;
|
||
border-radius: var(--radius);
|
||
padding: var(--spacing-lg);
|
||
margin-bottom: var(--spacing-xl);
|
||
}
|
||
|
||
.guidelines h3 {
|
||
font-size: var(--font-size-base);
|
||
font-weight: 600;
|
||
color: #92400e;
|
||
margin-bottom: var(--spacing-sm);
|
||
}
|
||
|
||
.guidelines ul {
|
||
margin: 0;
|
||
padding-left: var(--spacing-lg);
|
||
color: #78350f;
|
||
font-size: var(--font-size-sm);
|
||
}
|
||
|
||
.guidelines li {
|
||
margin-bottom: var(--spacing-xs);
|
||
}
|
||
|
||
.char-counter {
|
||
font-size: var(--font-size-sm);
|
||
color: var(--text-secondary);
|
||
text-align: right;
|
||
margin-top: var(--spacing-xs);
|
||
}
|
||
|
||
.char-counter.error {
|
||
color: var(--error);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.recommend-form {
|
||
padding: var(--spacing-lg);
|
||
}
|
||
|
||
.form-actions {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.form-actions .btn {
|
||
width: 100%;
|
||
}
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="recommend-container">
|
||
<nav class="recommend-breadcrumb">
|
||
<a href="{{ url_for('index') }}">Katalog</a> »
|
||
<a href="{{ url_for('company_detail', company_id=company.id) }}">{{ company.name }}</a> »
|
||
Polec firmę
|
||
</nav>
|
||
|
||
<div class="recommend-form">
|
||
<div class="form-header">
|
||
<h1>Polec firmę</h1>
|
||
<p>Podziel się swoją opinią i pomóż innym członkom Norda Biznes</p>
|
||
</div>
|
||
|
||
<div class="company-info">
|
||
<h3>📋 Polecaną firmą</h3>
|
||
<p><strong>{{ company.name }}</strong></p>
|
||
<p>{{ company.description_short[:150] }}{% if company.description_short and company.description_short|length > 150 %}...{% endif %}</p>
|
||
</div>
|
||
|
||
<div class="guidelines">
|
||
<h3>💡 Wskazówki</h3>
|
||
<ul>
|
||
<li>Opisz swoje doświadczenia współpracy z firmą</li>
|
||
<li>Wspomnij konkretne usługi lub projekty</li>
|
||
<li>Bądź szczery i konstruktywny</li>
|
||
<li>Twoja rekomendacja będzie widoczna dla innych członków</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<form method="POST" action="{{ url_for('company_recommend', slug=company.slug) }}" novalidate>
|
||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||
|
||
<div class="form-group">
|
||
<label for="recommendation_text" class="form-label">
|
||
Rekomendacja <span class="required">*</span>
|
||
</label>
|
||
<textarea
|
||
id="recommendation_text"
|
||
name="recommendation_text"
|
||
class="form-input form-textarea"
|
||
placeholder="Opisz swoje doświadczenia z firmą, zrealizowane projekty, jakość współpracy..."
|
||
required
|
||
minlength="50"
|
||
maxlength="2000"
|
||
autofocus
|
||
></textarea>
|
||
<p class="form-hint">Minimum 50 znaków, maksimum 2000 znaków.</p>
|
||
<div class="char-counter" id="charCounter">0 / 2000 znaków</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="service_category" class="form-label">
|
||
Kategoria usługi (opcjonalnie)
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="service_category"
|
||
name="service_category"
|
||
class="form-input"
|
||
placeholder="np. Tworzenie stron WWW, Usługi prawne, Produkcja..."
|
||
maxlength="200"
|
||
>
|
||
<p class="form-hint">Jeśli polecanienie dotyczy konkretnej usługi, możesz ją tutaj wskazać.</p>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<div class="form-checkbox">
|
||
<input
|
||
type="checkbox"
|
||
id="show_contact"
|
||
name="show_contact"
|
||
value="1"
|
||
checked
|
||
>
|
||
<label for="show_contact">
|
||
Udostępnij moje dane kontaktowe (email i telefon) innym członkom, aby mogli zadać pytania o szczegóły współpracy
|
||
</label>
|
||
</div>
|
||
<p class="form-hint" style="margin-left: 26px;">
|
||
Domyślnie zaznaczone. Transparentność wzmacnia zaufanie w sieci Norda Biznes.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="submit" class="btn btn-primary btn-lg">
|
||
Wyślij rekomendację
|
||
</button>
|
||
<a href="{{ url_for('company_detail', company_id=company.id) }}" class="btn btn-outline btn-lg">
|
||
Anuluj
|
||
</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="toastContainer" style="position: fixed; top: 80px; right: 20px; z-index: 1100; display: flex; flex-direction: column; gap: 10px;"></div>
|
||
<style>
|
||
.toast { padding: 12px 20px; border-radius: var(--radius); background: var(--surface); border-left: 4px solid var(--primary); box-shadow: 0 4px 12px rgba(0,0,0,0.15); display: flex; align-items: center; gap: 10px; animation: toastIn 0.3s ease; }
|
||
.toast.success { border-left-color: var(--success); }
|
||
.toast.error { border-left-color: var(--error); }
|
||
.toast.warning { border-left-color: var(--warning); }
|
||
@keyframes toastIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
||
@keyframes toastOut { from { opacity: 1; } to { opacity: 0; } }
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block extra_js %}
|
||
function showToast(message, type = 'info', duration = 4000) {
|
||
const container = document.getElementById('toastContainer');
|
||
const icons = { success: '✓', error: '✕', warning: '⚠', info: 'ℹ' };
|
||
const toast = document.createElement('div');
|
||
toast.className = `toast ${type}`;
|
||
toast.innerHTML = `<span style="font-size:1.2em">${icons[type]||'ℹ'}</span><span>${message}</span>`;
|
||
container.appendChild(toast);
|
||
setTimeout(() => { toast.style.animation = 'toastOut 0.3s ease forwards'; setTimeout(() => toast.remove(), 300); }, duration);
|
||
}
|
||
|
||
// Character counter
|
||
const textarea = document.getElementById('recommendation_text');
|
||
const counter = document.getElementById('charCounter');
|
||
|
||
textarea.addEventListener('input', function() {
|
||
const length = this.value.length;
|
||
counter.textContent = `${length} / 2000 znaków`;
|
||
|
||
if (length < 50) {
|
||
counter.classList.add('error');
|
||
counter.textContent = `${length} / 2000 znaków (minimum 50)`;
|
||
} else if (length > 2000) {
|
||
counter.classList.add('error');
|
||
} else {
|
||
counter.classList.remove('error');
|
||
}
|
||
});
|
||
|
||
// Client-side validation
|
||
document.querySelector('form').addEventListener('submit', function(e) {
|
||
const text = textarea.value.trim();
|
||
let valid = true;
|
||
|
||
if (text.length < 50) {
|
||
textarea.style.borderColor = 'var(--error)';
|
||
showToast('Rekomendacja musi mieć co najmniej 50 znaków.', 'error');
|
||
valid = false;
|
||
} else if (text.length > 2000) {
|
||
textarea.style.borderColor = 'var(--error)';
|
||
showToast('Rekomendacja może mieć maksymalnie 2000 znaków.', 'error');
|
||
valid = false;
|
||
} else {
|
||
textarea.style.borderColor = '';
|
||
}
|
||
|
||
if (!valid) {
|
||
e.preventDefault();
|
||
}
|
||
});
|
||
{% endblock %}
|