Add topic colors — each wiki topic gets distinct color badge (blue, teal, purple, orange, green)
This commit is contained in:
+2
-2
@@ -1097,14 +1097,14 @@ body[data-lang="en"] .hide-de { display: none !important; }
|
|||||||
.wiki-topic-badge {
|
.wiki-topic-badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
background: #e7f3ff;
|
|
||||||
color: #1877f2;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
|
border-left: 4px solid transparent;
|
||||||
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wiki-subtopic-headline {
|
.wiki-subtopic-headline {
|
||||||
|
|||||||
+41
-220
@@ -657,8 +657,47 @@ function getRank(posts) {
|
|||||||
return { level: 'rookie', title: isDE() ? 'Anfänger' : 'Rookie', emoji: '🌱' };
|
return { level: 'rookie', title: isDE() ? 'Anfänger' : 'Rookie', emoji: '🌱' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// TOPIC COLORS — each topic gets a distinct color
|
||||||
|
// ========================================
|
||||||
|
const TOPIC_COLORS = {
|
||||||
|
'🌎 Internationales Geschäft': { bg: '#E0F2F1', text: '#00695C', border: '#00695C' },
|
||||||
|
'🏦 Banken & Finanzen Filialen': { bg: '#E3F2FD', text: '#1565C0', border: '#1565C0' },
|
||||||
|
'🏦 Banken & Finanzen allgmein': { bg: '#E8EAF6', text: '#283593', border: '#283593' },
|
||||||
|
'💰 Steuern': { bg: '#FFF3E0', text: '#E65100', border: '#E65100' },
|
||||||
|
'💻 Software & Digital Operations': { bg: '#EDE7F6', text: '#4527A0', border: '#4527A0' },
|
||||||
|
'📑 Buchhaltung': { bg: '#E8F5E9', text: '#2E7D32', border: '#2E7D32' }
|
||||||
|
};
|
||||||
|
|
||||||
|
function getTopicColor(topic) {
|
||||||
|
// Try exact match first
|
||||||
|
if (TOPIC_COLORS[topic]) return TOPIC_COLORS[topic];
|
||||||
|
// Try matching without emoji
|
||||||
|
const cleanTopic = topic.replace(/^[^\s]+\s/, '');
|
||||||
|
for (const key of Object.keys(TOPIC_COLORS)) {
|
||||||
|
const cleanKey = key.replace(/^[^\s]+\s/, '');
|
||||||
|
if (cleanTopic === cleanKey) return TOPIC_COLORS[key];
|
||||||
|
}
|
||||||
|
// Fallback: generate color from topic string
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < topic.length; i++) {
|
||||||
|
hash = topic.charCodeAt(i) + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
const hues = [200, 180, 240, 30, 140, 60]; // blue, teal, purple, orange, green, amber
|
||||||
|
const idx = Math.abs(hash) % hues.length;
|
||||||
|
const color = hues[idx];
|
||||||
|
return {
|
||||||
|
bg: `hsl(${color}, 70%, 92%)`,
|
||||||
|
text: `hsl(${color}, 60%, 35%)`,
|
||||||
|
border: `hsl(${color}, 60%, 35%)`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// WIKI
|
// WIKI
|
||||||
|
// ========================================
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
let wikiData = [];
|
let wikiData = [];
|
||||||
|
|
||||||
@@ -700,224 +739,6 @@ function renderWikiCards(data) {
|
|||||||
card.setAttribute('tabindex', '0');
|
card.setAttribute('tabindex', '0');
|
||||||
|
|
||||||
const topic = currentLang === 'en' && item.topic_en ? item.topic_en : (item.topic || 'Unknown');
|
const topic = currentLang === 'en' && item.topic_en ? item.topic_en : (item.topic || 'Unknown');
|
||||||
const subtopic = currentLang === 'en' && item.subtopic_en ? item.subtopic_en : (item.subtopic || '');
|
const topicColor = getTopicColor(topic);
|
||||||
const content = currentLang === 'en' && item.wiki_en ? item.wiki_en : (item.wiki || '');
|
|
||||||
|
|
||||||
// Get date — try updatedAt first, fall back to createdAt
|
// Get excerpt from first subtopic
|
||||||
const dateStr = formatDate(new Date(item.updatedAt || item.createdAt));
|
|
||||||
|
|
||||||
// Teaser: first 150 chars of content
|
|
||||||
const teaser = content.trim().substring(0, 180);
|
|
||||||
const teaserEnd = content.trim().length > 180 ? '...' : '';
|
|
||||||
|
|
||||||
card.innerHTML = `
|
|
||||||
<span class="wiki-topic-badge">${escapeHtml(topic)}</span>
|
|
||||||
<h3 class="wiki-subtopic-headline">${escapeHtml(subtopic)}</h3>
|
|
||||||
<p class="wiki-teaser">${escapeHtml(teaser)}${teaserEnd}</p>
|
|
||||||
<div class="wiki-card-meta">
|
|
||||||
<span class="wiki-date">${dateStr}</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Open modal on click
|
|
||||||
card.addEventListener('click', () => openWikiModalSingle(item));
|
|
||||||
card.addEventListener('keydown', (e) => {
|
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
|
||||||
e.preventDefault();
|
|
||||||
openWikiModalSingle(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
grid.appendChild(card);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDate(date) {
|
|
||||||
if (!(date instanceof Date) || isNaN(date)) return '';
|
|
||||||
const year = date.getFullYear();
|
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
|
||||||
return `${year}-${month}-${day}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openWikiModal(topicName, subtopics) {
|
|
||||||
// Create modal if not exists
|
|
||||||
let modalOverlay = document.getElementById('wiki-modal-overlay');
|
|
||||||
if (!modalOverlay) {
|
|
||||||
modalOverlay = document.createElement('div');
|
|
||||||
modalOverlay.id = 'wiki-modal-overlay';
|
|
||||||
modalOverlay.className = 'modal-overlay';
|
|
||||||
document.body.appendChild(modalOverlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
let html = '<div class="modal wiki-modal"><button class="modal-close" onclick="closeWikiModal()">×</button>';
|
|
||||||
html += '<div class="wiki-modal-content">';
|
|
||||||
html += `<h2 class="wiki-modal-title">${escapeHtml(topicName)}</h2>`;
|
|
||||||
|
|
||||||
subtopics.forEach(sub => {
|
|
||||||
const title = currentLang === 'en' && sub.subtopic_en ? sub.subtopic_en : (sub.subtopic || '');
|
|
||||||
const content = currentLang === 'en' && sub.wiki_en ? sub.wiki_en : (sub.wiki || '');
|
|
||||||
html += `<h3 class="wiki-modal-subtitle">${escapeHtml(title)}</h3>`;
|
|
||||||
html += `<div class="wiki-modal-text">${escapeHtml(content)}</div>`;
|
|
||||||
});
|
|
||||||
|
|
||||||
html += '</div></div>';
|
|
||||||
modalOverlay.innerHTML = html;
|
|
||||||
modalOverlay.classList.add('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeWikiModal() {
|
|
||||||
const overlay = document.getElementById('wiki-modal-overlay');
|
|
||||||
if (overlay) overlay.classList.remove('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
function openWikiModalSingle(item) {
|
|
||||||
let modalOverlay = document.getElementById('wiki-modal-overlay');
|
|
||||||
if (!modalOverlay) {
|
|
||||||
modalOverlay = document.createElement('div');
|
|
||||||
modalOverlay.id = 'wiki-modal-overlay';
|
|
||||||
modalOverlay.className = 'modal-overlay';
|
|
||||||
document.body.appendChild(modalOverlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
const topic = currentLang === 'en' && item.topic_en ? item.topic_en : (item.topic || 'Unknown');
|
|
||||||
const subtopic = currentLang === 'en' && item.subtopic_en ? item.subtopic_en : (item.subtopic || '');
|
|
||||||
const content = currentLang === 'en' && item.wiki_en ? item.wiki_en : (item.wiki || '');
|
|
||||||
|
|
||||||
// Get date
|
|
||||||
const dateStr = formatDate(new Date(item.updatedAt || item.createdAt));
|
|
||||||
|
|
||||||
let html = '<div class="modal wiki-modal"><button class="modal-close" onclick="closeWikiModal()">×</button>';
|
|
||||||
html += '<div class="wiki-modal-content">';
|
|
||||||
html += `<div class="wiki-modal-topic">${escapeHtml(topic)}</div>`;
|
|
||||||
html += `<h2 class="wiki-modal-title">${escapeHtml(subtopic)}</h2>`;
|
|
||||||
if (dateStr) html += `<div class="wiki-modal-date">${dateStr}</div>`;
|
|
||||||
html += `<div class="wiki-modal-text">${escapeHtml(content)}</div>`;
|
|
||||||
html += '</div></div>';
|
|
||||||
modalOverlay.innerHTML = html;
|
|
||||||
modalOverlay.classList.add('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
function searchWiki(term) {
|
|
||||||
if (!term.trim()) {
|
|
||||||
renderWikiCards(wikiData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const t = term.toLowerCase();
|
|
||||||
const filtered = wikiData.filter(item => {
|
|
||||||
const topic = ((item.topic_en && currentLang === 'en') ? item.topic_en : (item.topic || '')) +
|
|
||||||
' ' + ((item.subtopic_en && currentLang === 'en') ? item.subtopic_en : (item.subtopic || '')) +
|
|
||||||
' ' + ((item.wiki_en && currentLang === 'en') ? item.wiki_en : (item.wiki || ''));
|
|
||||||
return topic.toLowerCase().includes(t);
|
|
||||||
});
|
|
||||||
|
|
||||||
renderWikiCards(filtered);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// DATES / EVENTS
|
|
||||||
// ========================================
|
|
||||||
async function loadDates() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(SCHEDULE_URL);
|
|
||||||
if (!response.ok) throw new Error('Failed to fetch');
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (result.data && Array.isArray(result.data) && result.data.length > 0) {
|
|
||||||
renderDates(result.data);
|
|
||||||
} else {
|
|
||||||
document.getElementById('dates-list').innerHTML =
|
|
||||||
`<p class="no-events">${isDE() ? 'Keine Termine geplant' : 'No events scheduled'}</p>`;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
document.getElementById('dates-list').innerHTML =
|
|
||||||
`<p class="no-events">${isDE() ? 'Fehler beim Laden' : 'Error loading events'}</p>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderDates(events) {
|
|
||||||
const container = document.getElementById('dates-list');
|
|
||||||
container.innerHTML = '';
|
|
||||||
|
|
||||||
// Sort events by date
|
|
||||||
const sorted = events.sort((a, b) => new Date(a.start) - new Date(b.start));
|
|
||||||
|
|
||||||
sorted.forEach((event, idx) => {
|
|
||||||
const card = document.createElement('div');
|
|
||||||
card.className = 'date-card stagger-' + (idx % 4 + 1);
|
|
||||||
|
|
||||||
// Parse date/time from ISO string
|
|
||||||
const startDate = new Date(event.start);
|
|
||||||
const endDate = new Date(event.end);
|
|
||||||
|
|
||||||
// Format date: yyyy.mm.dd
|
|
||||||
const day = String(startDate.getDate()).padStart(2, '0');
|
|
||||||
const month = String(startDate.getMonth() + 1).padStart(2, '0');
|
|
||||||
const year = startDate.getFullYear();
|
|
||||||
const dateStr = `${year}.${month}.${day}`;
|
|
||||||
|
|
||||||
// Format time: HH:MM (local time)
|
|
||||||
const startHour = String(startDate.getHours()).padStart(2, '0');
|
|
||||||
const startMinute = String(startDate.getMinutes()).padStart(2, '0');
|
|
||||||
const endHour = String(endDate.getHours()).padStart(2, '0');
|
|
||||||
const endMinute = String(endDate.getMinutes()).padStart(2, '0');
|
|
||||||
const startStr = `${startHour}:${startMinute}`;
|
|
||||||
const endStr = `${endHour}:${endMinute}`;
|
|
||||||
|
|
||||||
card.innerHTML = `
|
|
||||||
<div class="date-header">
|
|
||||||
<div class="date-icon">📅</div>
|
|
||||||
<h3 class="date-headline">${escapeHtml(event.headline || 'Unbenanntes Event')}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="date-info">
|
|
||||||
<div class="date-row">
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
|
|
||||||
<span>${dateStr}</span>
|
|
||||||
</div>
|
|
||||||
<div class="date-row">
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
|
||||||
<span>${isDE() ? 'Start' : 'Start'} ${startStr}</span>
|
|
||||||
</div>
|
|
||||||
<div class="date-row">
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
|
||||||
<span>${isDE() ? 'Ende' : 'End'} ${endStr}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
container.appendChild(card);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// COOKIES
|
|
||||||
// ========================================
|
|
||||||
function getCookie(name) {
|
|
||||||
const localVal = localStorage.getItem('paraguay_' + name);
|
|
||||||
if (localVal) return localVal;
|
|
||||||
|
|
||||||
const value = `; ${document.cookie}`;
|
|
||||||
const parts = value.split(`; ${name}=`);
|
|
||||||
if (parts.length === 2) {
|
|
||||||
return parts.pop().split(';').shift().replace(/^"|"$/g, '');
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCookie(name, value, days) {
|
|
||||||
const expires = new Date();
|
|
||||||
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
|
|
||||||
document.cookie = name + '=' + encodeURIComponent(value) + ';expires=' + expires.toUTCString() + ';path=/';
|
|
||||||
localStorage.setItem('paraguay_' + name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// UTILS
|
|
||||||
// ========================================
|
|
||||||
function escapeHtml(text) {
|
|
||||||
if (!text) return '';
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.textContent = text;
|
|
||||||
return div.innerHTML;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user