- Add is_ai_generated column to ForumTopic, ForumReply, NordaEvent, Classified - Display AI badge next to author name for AI-generated content - Purple gradient badge with tooltip 'Wygenerowano przez AI'
888 lines
26 KiB
HTML
Executable File
888 lines
26 KiB
HTML
Executable File
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ topic.title }} - Forum - Norda Biznes Hub{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.topic-breadcrumb {
|
|
margin-bottom: var(--spacing-lg);
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.topic-breadcrumb a {
|
|
color: var(--primary);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.topic-breadcrumb a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.topic-header {
|
|
background: var(--surface);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--spacing-xl);
|
|
margin-bottom: var(--spacing-xl);
|
|
box-shadow: var(--shadow);
|
|
}
|
|
|
|
.topic-header.pinned {
|
|
border-left: 4px solid var(--primary);
|
|
background: linear-gradient(135deg, #eff6ff, var(--surface));
|
|
}
|
|
|
|
.topic-header.locked {
|
|
border-left: 4px solid var(--secondary);
|
|
}
|
|
|
|
.topic-title-row {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: var(--spacing-md);
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.topic-title {
|
|
font-size: var(--font-size-2xl);
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.topic-badge {
|
|
font-size: var(--font-size-sm);
|
|
padding: 4px 10px;
|
|
border-radius: var(--radius-sm);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.badge-pinned {
|
|
background: var(--primary);
|
|
color: white;
|
|
}
|
|
|
|
.badge-locked {
|
|
background: var(--secondary);
|
|
color: white;
|
|
}
|
|
|
|
/* Category badges */
|
|
.badge-category {
|
|
border: 1px solid;
|
|
}
|
|
|
|
.badge-feature_request {
|
|
background: #dbeafe;
|
|
color: #1e40af;
|
|
border-color: #93c5fd;
|
|
}
|
|
|
|
.badge-bug {
|
|
background: #fee2e2;
|
|
color: #991b1b;
|
|
border-color: #fca5a5;
|
|
}
|
|
|
|
.badge-question {
|
|
background: #dcfce7;
|
|
color: #166534;
|
|
border-color: #86efac;
|
|
}
|
|
|
|
.badge-announcement {
|
|
background: #fef3c7;
|
|
color: #92400e;
|
|
border-color: #fcd34d;
|
|
}
|
|
|
|
/* Status badges */
|
|
.badge-status {
|
|
font-size: var(--font-size-xs);
|
|
padding: 2px 8px;
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
.badge-new {
|
|
background: #f3f4f6;
|
|
color: #374151;
|
|
}
|
|
|
|
.badge-in_progress {
|
|
background: #dbeafe;
|
|
color: #1e40af;
|
|
}
|
|
|
|
.badge-resolved {
|
|
background: #dcfce7;
|
|
color: #166534;
|
|
}
|
|
|
|
.badge-rejected {
|
|
background: #fee2e2;
|
|
color: #991b1b;
|
|
}
|
|
|
|
.topic-meta {
|
|
display: flex;
|
|
gap: var(--spacing-lg);
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-sm);
|
|
margin-bottom: var(--spacing-lg);
|
|
}
|
|
|
|
.topic-meta span {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-xs);
|
|
}
|
|
|
|
.topic-content {
|
|
line-height: 1.8;
|
|
color: var(--text-primary);
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
/* Attachments */
|
|
.topic-attachment {
|
|
margin-top: var(--spacing-lg);
|
|
padding-top: var(--spacing-lg);
|
|
border-top: 1px solid var(--border);
|
|
}
|
|
|
|
.attachment-image {
|
|
max-width: 100%;
|
|
max-height: 500px;
|
|
border-radius: var(--radius);
|
|
cursor: pointer;
|
|
transition: var(--transition);
|
|
}
|
|
|
|
.attachment-image:hover {
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.attachment-info {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-secondary);
|
|
margin-top: var(--spacing-sm);
|
|
}
|
|
|
|
.replies-section {
|
|
margin-top: var(--spacing-xl);
|
|
}
|
|
|
|
.replies-header {
|
|
font-size: var(--font-size-xl);
|
|
font-weight: 600;
|
|
margin-bottom: var(--spacing-lg);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.replies-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.reply-card {
|
|
background: var(--surface);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--spacing-lg);
|
|
box-shadow: var(--shadow-sm);
|
|
border-left: 3px solid var(--border);
|
|
}
|
|
|
|
.reply-card:hover {
|
|
border-left-color: var(--primary);
|
|
}
|
|
|
|
.reply-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: var(--spacing-md);
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.reply-author {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
font-weight: 500;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.reply-avatar {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 50%;
|
|
background: var(--primary);
|
|
color: white;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: 600;
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
/* AI generated indicator */
|
|
.ai-indicator {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 2px;
|
|
padding: 2px 6px;
|
|
background: linear-gradient(135deg, #e0e7ff, #c7d2fe);
|
|
border-radius: var(--radius-sm);
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
color: #4338ca;
|
|
cursor: help;
|
|
margin-left: var(--spacing-xs);
|
|
}
|
|
|
|
.ai-indicator svg {
|
|
width: 12px;
|
|
height: 12px;
|
|
}
|
|
|
|
.reply-content {
|
|
line-height: 1.7;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.reply-attachments-container {
|
|
margin-top: var(--spacing-md);
|
|
padding-top: var(--spacing-md);
|
|
border-top: 1px solid var(--border);
|
|
}
|
|
|
|
.reply-attachments-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.reply-attachment {
|
|
position: relative;
|
|
}
|
|
|
|
.reply-attachment img {
|
|
width: 100%;
|
|
height: 120px;
|
|
object-fit: cover;
|
|
border-radius: var(--radius);
|
|
cursor: pointer;
|
|
transition: transform 0.2s;
|
|
}
|
|
|
|
.reply-attachment img:hover {
|
|
transform: scale(1.02);
|
|
}
|
|
|
|
.reply-attachment .attachment-info {
|
|
font-size: 10px;
|
|
color: var(--text-secondary);
|
|
margin-top: 4px;
|
|
text-align: center;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
/* Single attachment - larger display */
|
|
.reply-attachments-grid.single-attachment {
|
|
grid-template-columns: 1fr;
|
|
max-width: 400px;
|
|
}
|
|
|
|
.reply-attachments-grid.single-attachment .reply-attachment img {
|
|
height: auto;
|
|
max-height: 300px;
|
|
object-fit: contain;
|
|
}
|
|
|
|
.reply-form {
|
|
background: var(--surface);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--spacing-xl);
|
|
margin-top: var(--spacing-xl);
|
|
box-shadow: var(--shadow);
|
|
}
|
|
|
|
.reply-form h3 {
|
|
margin-bottom: var(--spacing-lg);
|
|
font-size: var(--font-size-lg);
|
|
}
|
|
|
|
.reply-form textarea {
|
|
width: 100%;
|
|
min-height: 120px;
|
|
padding: var(--spacing-md);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
font-family: var(--font-family);
|
|
font-size: var(--font-size-base);
|
|
resize: vertical;
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.reply-form textarea:focus {
|
|
outline: none;
|
|
border-color: var(--primary);
|
|
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
}
|
|
|
|
/* Upload dropzone in reply form */
|
|
.upload-dropzone-mini {
|
|
border: 2px dashed var(--border);
|
|
border-radius: var(--radius);
|
|
padding: var(--spacing-md);
|
|
text-align: center;
|
|
background: var(--background);
|
|
transition: var(--transition);
|
|
cursor: pointer;
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.upload-dropzone-mini:hover,
|
|
.upload-dropzone-mini.drag-over {
|
|
border-color: var(--primary);
|
|
background: rgba(37, 99, 235, 0.05);
|
|
}
|
|
|
|
.upload-dropzone-mini p {
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-sm);
|
|
margin: 0;
|
|
}
|
|
|
|
.upload-preview-mini {
|
|
display: none;
|
|
margin-bottom: var(--spacing-md);
|
|
padding: var(--spacing-sm);
|
|
background: var(--background);
|
|
border-radius: var(--radius);
|
|
border: 1px solid var(--border);
|
|
}
|
|
|
|
.upload-preview-mini.active {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.upload-preview-mini img {
|
|
max-width: 80px;
|
|
max-height: 60px;
|
|
border-radius: var(--radius-sm);
|
|
object-fit: cover;
|
|
}
|
|
|
|
.upload-preview-mini .file-info {
|
|
flex: 1;
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.upload-preview-mini .file-name {
|
|
font-weight: 500;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.upload-preview-mini .file-size {
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.upload-preview-mini .remove-file {
|
|
color: var(--error);
|
|
cursor: pointer;
|
|
padding: var(--spacing-xs);
|
|
}
|
|
|
|
.upload-preview-mini .remove-file:hover {
|
|
background: rgba(239, 68, 68, 0.1);
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
/* Multi-file upload preview grid */
|
|
.upload-previews-container {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
gap: var(--spacing-sm);
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.upload-preview-item {
|
|
position: relative;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: var(--spacing-xs);
|
|
background: var(--surface);
|
|
}
|
|
|
|
.upload-preview-item img {
|
|
width: 100%;
|
|
height: 80px;
|
|
object-fit: cover;
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
.upload-preview-item .preview-info {
|
|
font-size: 10px;
|
|
color: var(--text-secondary);
|
|
margin-top: 4px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.upload-preview-item .remove-preview {
|
|
position: absolute;
|
|
top: -6px;
|
|
right: -6px;
|
|
width: 20px;
|
|
height: 20px;
|
|
background: var(--error);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 14px;
|
|
line-height: 1;
|
|
}
|
|
|
|
.upload-preview-item .remove-preview:hover {
|
|
background: #c53030;
|
|
}
|
|
|
|
.upload-counter {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-secondary);
|
|
margin-bottom: var(--spacing-xs);
|
|
}
|
|
|
|
.upload-counter.limit-reached {
|
|
color: var(--warning);
|
|
}
|
|
|
|
.form-actions {
|
|
display: flex;
|
|
gap: var(--spacing-md);
|
|
align-items: center;
|
|
}
|
|
|
|
.locked-notice {
|
|
background: #fef3c7;
|
|
border: 1px solid #f59e0b;
|
|
border-radius: var(--radius);
|
|
padding: var(--spacing-md);
|
|
margin-top: var(--spacing-xl);
|
|
text-align: center;
|
|
color: #92400e;
|
|
}
|
|
|
|
.empty-replies {
|
|
text-align: center;
|
|
padding: var(--spacing-xl);
|
|
color: var(--text-secondary);
|
|
background: var(--background);
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
/* Lightbox for images */
|
|
.lightbox {
|
|
display: none;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.9);
|
|
z-index: 1000;
|
|
justify-content: center;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.lightbox.active {
|
|
display: flex;
|
|
}
|
|
|
|
.lightbox img {
|
|
max-width: 90%;
|
|
max-height: 90%;
|
|
object-fit: contain;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.topic-title-row {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.topic-meta {
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.form-actions {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<nav class="topic-breadcrumb">
|
|
<a href="{{ url_for('forum_index') }}">Forum</a> » {{ topic.title[:50] }}{% if topic.title|length > 50 %}...{% endif %}
|
|
</nav>
|
|
|
|
<article class="topic-header {% if topic.is_pinned %}pinned{% endif %} {% if topic.is_locked %}locked{% endif %}">
|
|
<div class="topic-title-row">
|
|
<h1 class="topic-title">
|
|
{% if topic.is_pinned %}
|
|
<span class="topic-badge badge-pinned">Przypiety</span>
|
|
{% endif %}
|
|
{% if topic.is_locked %}
|
|
<span class="topic-badge badge-locked">Zamkniety</span>
|
|
{% endif %}
|
|
<span class="topic-badge badge-category badge-{{ topic.category or 'question' }}">
|
|
{{ category_labels.get(topic.category, 'Pytanie') }}
|
|
</span>
|
|
<span class="topic-badge badge-status badge-{{ topic.status or 'new' }}">
|
|
{{ status_labels.get(topic.status, 'Nowy') }}
|
|
</span>
|
|
{{ topic.title }}
|
|
</h1>
|
|
</div>
|
|
|
|
<div class="topic-meta">
|
|
<span>
|
|
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
|
<circle cx="12" cy="7" r="4"></circle>
|
|
</svg>
|
|
{{ topic.author.name or topic.author.email.split('@')[0] }}
|
|
{% if topic.is_ai_generated %}
|
|
<span class="ai-indicator" title="Wygenerowano przez AI">
|
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
|
AI
|
|
</span>
|
|
{% endif %}
|
|
</span>
|
|
<span>
|
|
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
<circle cx="12" cy="12" r="10"></circle>
|
|
<polyline points="12 6 12 12 16 14"></polyline>
|
|
</svg>
|
|
{{ topic.created_at.strftime('%d.%m.%Y %H:%M') }}
|
|
</span>
|
|
<span>
|
|
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
|
<circle cx="12" cy="12" r="3"></circle>
|
|
</svg>
|
|
{{ topic.views_count }} wyswietlen
|
|
</span>
|
|
</div>
|
|
|
|
<div class="topic-content">{{ topic.content }}</div>
|
|
|
|
{% if topic.attachments %}
|
|
{% for attachment in topic.attachments %}
|
|
<div class="topic-attachment">
|
|
<img src="{{ url_for('static', filename='uploads/forum/topics/' ~ topic.created_at.strftime('%Y/%m/') ~ attachment.stored_filename) }}"
|
|
alt="{{ attachment.original_filename }}"
|
|
class="attachment-image"
|
|
onclick="openLightbox(this.src)">
|
|
<div class="attachment-info">
|
|
{{ attachment.original_filename }} ({{ (attachment.file_size / 1024)|int }} KB)
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
</article>
|
|
|
|
<section class="replies-section">
|
|
<h2 class="replies-header">
|
|
Odpowiedzi ({{ topic.replies|length }})
|
|
</h2>
|
|
|
|
{% if topic.replies %}
|
|
<div class="replies-list">
|
|
{% for reply in topic.replies %}
|
|
<article class="reply-card">
|
|
<div class="reply-header">
|
|
<div class="reply-author">
|
|
<div class="reply-avatar">
|
|
{{ (reply.author.name or reply.author.email)[0].upper() }}
|
|
</div>
|
|
{{ reply.author.name or reply.author.email.split('@')[0] }}
|
|
{% if reply.is_ai_generated %}
|
|
<span class="ai-indicator" title="Wygenerowano przez AI">
|
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
|
AI
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
<span>{{ reply.created_at.strftime('%d.%m.%Y %H:%M') }}</span>
|
|
</div>
|
|
<div class="reply-content">{{ reply.content }}</div>
|
|
|
|
{% if reply.attachments %}
|
|
<div class="reply-attachments-container">
|
|
<div class="reply-attachments-grid {% if reply.attachments|length == 1 %}single-attachment{% endif %}">
|
|
{% for attachment in reply.attachments %}
|
|
<div class="reply-attachment">
|
|
<img src="{{ url_for('static', filename='uploads/forum/replies/' ~ reply.created_at.strftime('%Y/%m/') ~ attachment.stored_filename) }}"
|
|
alt="{{ attachment.original_filename }}"
|
|
onclick="openLightbox(this.src)">
|
|
<div class="attachment-info">
|
|
{{ attachment.original_filename|truncate(20) }} ({{ (attachment.file_size / 1024)|int }} KB)
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</article>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="empty-replies">
|
|
Brak odpowiedzi. Badz pierwszy!
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
|
|
{% if topic.is_locked %}
|
|
<div class="locked-notice">
|
|
Ten temat jest zamkniety. Nie mozna dodawac nowych odpowiedzi.
|
|
</div>
|
|
{% else %}
|
|
<form class="reply-form" method="POST" action="{{ url_for('forum_reply', topic_id=topic.id) }}" enctype="multipart/form-data">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<h3>Dodaj odpowiedz</h3>
|
|
<textarea name="content" id="replyContent" placeholder="Twoja odpowiedz..." required></textarea>
|
|
|
|
<div class="upload-counter" id="uploadCounter"></div>
|
|
<div class="upload-previews-container" id="previewsContainer"></div>
|
|
<div class="upload-dropzone-mini" id="dropzone">
|
|
<p>Przeciagnij obrazy lub kliknij tutaj (max 10 plikow, mozesz tez wkleic Ctrl+V)</p>
|
|
<input type="file" id="attachmentInput" name="attachments[]" accept="image/jpeg,image/png,image/gif" multiple style="display: none;">
|
|
</div>
|
|
|
|
<div class="form-actions">
|
|
<button type="submit" class="btn btn-primary">Wyslij odpowiedz</button>
|
|
</div>
|
|
</form>
|
|
{% endif %}
|
|
|
|
<!-- Lightbox for enlarged images -->
|
|
<div class="lightbox" id="lightbox" onclick="closeLightbox()">
|
|
<img id="lightboxImage" src="" alt="Enlarged image">
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
// Lightbox functions
|
|
function openLightbox(src) {
|
|
document.getElementById('lightboxImage').src = src;
|
|
document.getElementById('lightbox').classList.add('active');
|
|
}
|
|
|
|
function closeLightbox() {
|
|
document.getElementById('lightbox').classList.remove('active');
|
|
}
|
|
|
|
// Close lightbox with Escape key
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape') {
|
|
closeLightbox();
|
|
}
|
|
});
|
|
|
|
// Multi-file upload handling (only if form exists)
|
|
const dropzone = document.getElementById('dropzone');
|
|
if (dropzone) {
|
|
const fileInput = document.getElementById('attachmentInput');
|
|
const previewsContainer = document.getElementById('previewsContainer');
|
|
const uploadCounter = document.getElementById('uploadCounter');
|
|
const replyContent = document.getElementById('replyContent');
|
|
|
|
const MAX_FILES = 10;
|
|
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
|
|
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif'];
|
|
|
|
// Store files in a Map for easy removal
|
|
let filesMap = new Map();
|
|
let fileIdCounter = 0;
|
|
|
|
// Click to upload
|
|
dropzone.addEventListener('click', () => fileInput.click());
|
|
|
|
// Drag and drop
|
|
dropzone.addEventListener('dragover', (e) => {
|
|
e.preventDefault();
|
|
dropzone.classList.add('drag-over');
|
|
});
|
|
|
|
dropzone.addEventListener('dragleave', () => {
|
|
dropzone.classList.remove('drag-over');
|
|
});
|
|
|
|
dropzone.addEventListener('drop', (e) => {
|
|
e.preventDefault();
|
|
dropzone.classList.remove('drag-over');
|
|
const droppedFiles = Array.from(e.dataTransfer.files).filter(f => f.type.startsWith('image/'));
|
|
addFiles(droppedFiles);
|
|
});
|
|
|
|
// File input change
|
|
fileInput.addEventListener('change', (e) => {
|
|
const selectedFiles = Array.from(e.target.files);
|
|
addFiles(selectedFiles);
|
|
// Reset input to allow selecting same files again
|
|
fileInput.value = '';
|
|
});
|
|
|
|
// Paste from clipboard (Ctrl+V)
|
|
document.addEventListener('paste', (e) => {
|
|
// Only handle paste if reply textarea is focused
|
|
if (document.activeElement !== replyContent && !replyContent.contains(document.activeElement)) {
|
|
return;
|
|
}
|
|
|
|
const items = e.clipboardData?.items;
|
|
if (!items) return;
|
|
|
|
const pastedFiles = [];
|
|
for (let i = 0; i < items.length; i++) {
|
|
if (items[i].type.startsWith('image/')) {
|
|
e.preventDefault();
|
|
const file = items[i].getAsFile();
|
|
if (file) {
|
|
pastedFiles.push(file);
|
|
}
|
|
}
|
|
}
|
|
if (pastedFiles.length > 0) {
|
|
addFiles(pastedFiles);
|
|
}
|
|
});
|
|
|
|
function addFiles(newFiles) {
|
|
const currentCount = filesMap.size;
|
|
const availableSlots = MAX_FILES - currentCount;
|
|
|
|
if (availableSlots <= 0) {
|
|
alert('Osiagnieto limit ' + MAX_FILES + ' plikow');
|
|
return;
|
|
}
|
|
|
|
const filesToAdd = newFiles.slice(0, availableSlots);
|
|
const errors = [];
|
|
|
|
filesToAdd.forEach(file => {
|
|
// Validate size
|
|
if (file.size > MAX_SIZE) {
|
|
errors.push(file.name + ': za duzy (max 5MB)');
|
|
return;
|
|
}
|
|
|
|
// Validate type
|
|
if (!ALLOWED_TYPES.includes(file.type)) {
|
|
errors.push(file.name + ': niedozwolony format');
|
|
return;
|
|
}
|
|
|
|
const fileId = 'file_' + (fileIdCounter++);
|
|
filesMap.set(fileId, file);
|
|
createPreview(fileId, file);
|
|
});
|
|
|
|
if (errors.length > 0) {
|
|
alert('Bledy:\n' + errors.join('\n'));
|
|
}
|
|
|
|
updateCounter();
|
|
syncFilesToInput();
|
|
}
|
|
|
|
function createPreview(fileId, file) {
|
|
const preview = document.createElement('div');
|
|
preview.className = 'upload-preview-item';
|
|
preview.dataset.fileId = fileId;
|
|
|
|
const img = document.createElement('img');
|
|
const info = document.createElement('div');
|
|
info.className = 'preview-info';
|
|
info.textContent = file.name.substring(0, 15) + (file.name.length > 15 ? '...' : '') + ' (' + formatFileSize(file.size) + ')';
|
|
|
|
const removeBtn = document.createElement('button');
|
|
removeBtn.type = 'button';
|
|
removeBtn.className = 'remove-preview';
|
|
removeBtn.innerHTML = '×';
|
|
removeBtn.title = 'Usun';
|
|
removeBtn.onclick = () => removeFile(fileId);
|
|
|
|
preview.appendChild(img);
|
|
preview.appendChild(info);
|
|
preview.appendChild(removeBtn);
|
|
previewsContainer.appendChild(preview);
|
|
|
|
// Load image preview
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
img.src = e.target.result;
|
|
};
|
|
reader.readAsDataURL(file);
|
|
}
|
|
|
|
function removeFile(fileId) {
|
|
filesMap.delete(fileId);
|
|
const preview = previewsContainer.querySelector('[data-file-id="' + fileId + '"]');
|
|
if (preview) {
|
|
preview.remove();
|
|
}
|
|
updateCounter();
|
|
syncFilesToInput();
|
|
}
|
|
|
|
function updateCounter() {
|
|
const count = filesMap.size;
|
|
if (count === 0) {
|
|
uploadCounter.textContent = '';
|
|
uploadCounter.classList.remove('limit-reached');
|
|
dropzone.style.display = 'block';
|
|
} else {
|
|
uploadCounter.textContent = 'Wybrano: ' + count + '/' + MAX_FILES + ' plikow';
|
|
uploadCounter.classList.toggle('limit-reached', count >= MAX_FILES);
|
|
dropzone.style.display = count >= MAX_FILES ? 'none' : 'block';
|
|
}
|
|
}
|
|
|
|
function syncFilesToInput() {
|
|
// Create DataTransfer and add all files from Map
|
|
const dataTransfer = new DataTransfer();
|
|
filesMap.forEach(file => {
|
|
dataTransfer.items.add(file);
|
|
});
|
|
fileInput.files = dataTransfer.files;
|
|
}
|
|
|
|
function formatFileSize(bytes) {
|
|
if (bytes < 1024) return bytes + ' B';
|
|
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
|
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
|
}
|
|
}
|
|
{% endblock %}
|