Add Student Welcome Guide, legal pages, and working newsletter

Student Welcome Guide (Phase 1):
- Landing + 5 topic pages (journey, bachelor, master, deadlines, eVS)
  in EN/SI/MK, reusing the article auto-TOC
- New css/pages/_guide.css components and a 3:1 hero image placeholder
- Functional client-side "Build my route" tool (student-guide.js)
- "Student Welcome Guide" menu link wired site-wide

Legal pages:
- Trilingual Privacy Policy and Cookie Policy (GDPR-oriented,
  MailerLite disclosed, IP RS complaint route)
- Footer legal strip + Organisation column links wired site-wide,
  depth-aware, plus header/footer templates

Newsletter:
- MailerLite subscribe handler in main.js upgrades both form variants
  on every page: validation, double opt-in, inline trilingual status
- Subscribe status styles in css/pages/_projects.css

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
popovskikandClaude Opus 4.8 committed 2026-07-30 02:17:35 +02:00
1 parent bf70a10a14
commit cee9f1da10
113 files changed
+9043 -346

No files matched your search

+86 -1
View File
@@ -648,10 +648,95 @@ document.addEventListener('DOMContentLoaded', function() {
/**
* ===================================================================
* 7. ZAGON FUNKCIJ PO NALOŽITVI STRANI
* 7. NEWSLETTER (MailerLite)
* ===================================================================
*/
function setupNewsletter() {
// --- MailerLite configuration -----------------------------------
// Fill these in from your MailerLite embedded form
// (Forms > Embedded forms > your form > Embed code). Both values
// appear in the embed's subscribe URL:
// https://assets.mailerlite.com/jsonp/<ACCOUNT>/forms/<FORM>/subscribe
const ML_ACCOUNT = '1253161';
const ML_FORM = '194374752800868123';
// ----------------------------------------------------------------
const forms = document.querySelectorAll('form.subscribe-form, form.subscribe-form-bottom');
if (!forms.length) return;
let lang = (document.documentElement.getAttribute('lang') || 'en').toLowerCase();
if (['en', 'si', 'mk'].indexOf(lang) === -1) lang = 'en';
const T = {
invalid: { en: 'Please enter a valid email address.', si: 'Vnesite veljaven e-poštni naslov.', mk: 'Внесете важечка е-адреса.' },
sending: { en: 'Sending…', si: 'Pošiljanje…', mk: 'Се испраќа…' },
success: { en: 'Thank you! Please check your inbox to confirm your subscription.', si: 'Hvala! Za potrditev naročnine preverite svoj e-poštni predal.', mk: 'Ви благодариме! Проверете го вашето сандаче за да ја потврдите претплатата.' },
error: { en: 'Something went wrong. Please try again later.', si: 'Nekaj je šlo narobe. Poskusite znova pozneje.', mk: 'Нешто не успеа. Обидете се повторно подоцна.' },
notready: { en: 'The newsletter is being set up. Please check back soon.', si: 'Novice se pravkar nastavljajo. Preverite kmalu znova.', mk: 'Билтенот се поставува. Проверете повторно наскоро.' }
};
const t = (k) => (T[k][lang] || T[k].en);
forms.forEach((form) => {
const input = form.querySelector('input[type="email"], input[type="text"], input');
if (!input) return;
input.setAttribute('type', 'email');
input.setAttribute('name', 'fields[email]');
input.setAttribute('required', '');
input.setAttribute('autocomplete', 'email');
const status = document.createElement('p');
status.className = 'subscribe-status';
status.setAttribute('role', 'status');
status.setAttribute('aria-live', 'polite');
status.hidden = true;
form.parentNode.insertBefore(status, form.nextSibling);
const show = (msg, type) => {
status.textContent = msg;
status.className = 'subscribe-status is-' + type;
status.hidden = false;
};
form.addEventListener('submit', (e) => {
e.preventDefault();
const email = (input.value || '').trim();
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
show(t('invalid'), 'error');
input.focus();
return;
}
if (!ML_ACCOUNT || !ML_FORM) { show(t('notready'), 'error'); return; }
const button = form.querySelector('button[type="submit"], button');
if (button) button.disabled = true;
show(t('sending'), 'info');
const url = 'https://assets.mailerlite.com/jsonp/' + ML_ACCOUNT + '/forms/' + ML_FORM + '/subscribe';
const body = new URLSearchParams();
body.append('fields[email]', email);
body.append('ml-submit', '1');
body.append('anticsrf', 'true');
fetch(url, { method: 'POST', body: body, mode: 'no-cors' })
.then(() => {
// Double opt-in: MailerLite emails a confirmation link, so we
// optimistically confirm here (the opt-in email is the real check).
form.reset();
show(t('success'), 'success');
})
.catch(() => show(t('error'), 'error'))
.finally(() => { if (button) button.disabled = false; });
});
});
}
/**
* ===================================================================
* 8. ZAGON FUNKCIJ PO NALOŽITVI STRANI
* ===================================================================
*/
highlightActiveLink();
setupLanguageSwitcher();
setupNewsletter();
});