Upgrade Build My Route into a personal report generator with export and data hook
Adds role, faculty (typed), and budget fields; the configurator now navigates to a new per-language results page (my-route/) carrying the answers in the URL. The results page renders a branded, printable report: profile chips, tailored ordered steps, city/budget/role/level focus tips, key page links, a disclaimer, and a Save-as-PDF/print button (print CSS shows only the report). my-route.js also has a Google Form hook to collect the profile for demographics once configured. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f88a9bb6b4
commit
b486a4a70d
9 files changed
+1177
-132
No files matched your search
+47
-123
@@ -1,189 +1,113 @@
|
||||
/* ==========================================================================
|
||||
Student Welcome Guide — "Build my route"
|
||||
Client-side personalised checklist for the guide landing page.
|
||||
The landing page sets <div class="guide-route" data-lang="en|si|mk">.
|
||||
Links point to sibling guide pages (../<slug>/) and only reference pages
|
||||
that exist, so there are no dead links.
|
||||
Student Welcome Guide — "Build my personal route" (configurator)
|
||||
Fills the selects on the landing page and, on submit, sends the visitor to
|
||||
the personal results page (my-route/) with their answers in the URL.
|
||||
The results page (my-route.js) renders the tailored, printable report.
|
||||
========================================================================== */
|
||||
(function () {
|
||||
var root = document.querySelector('.guide-route');
|
||||
if (!root) return;
|
||||
var lang = root.getAttribute('data-lang') || 'en';
|
||||
|
||||
// Slugs of pages that exist and are safe to link to.
|
||||
var S = {
|
||||
journey: 'complete-student-journey',
|
||||
bachelor: 'bachelor-application',
|
||||
master: 'master-application',
|
||||
deadlines: 'application-deadlines',
|
||||
evs: 'evs-application-guide'
|
||||
};
|
||||
|
||||
var T = {
|
||||
en: {
|
||||
heading: 'Build my route',
|
||||
sub: 'Answer a few questions and we will outline your next steps.',
|
||||
build: 'Show my route',
|
||||
reset: 'Reset',
|
||||
need: 'Please choose at least your level and your current stage.',
|
||||
resultHead: 'Your route',
|
||||
contextParts: { level: 'Level', phase: 'Stage', uni: 'Institution', city: 'City', year: 'Academic year' },
|
||||
fields: {
|
||||
level: 'Level of study', phase: 'Where are you now?', uni: 'University', city: 'Study city', year: 'Academic year'
|
||||
},
|
||||
pick: 'Select',
|
||||
roles: { student: 'Student', parent: 'Parent or family member' },
|
||||
levels: { bachelor: 'Bachelor', master: 'Master' },
|
||||
phases: { exploring: 'I am exploring', applying: 'I am applying', accepted: 'I have been accepted', moving: 'I am preparing to move', arrived: 'I am already in Slovenia' },
|
||||
unis: { ul: 'University of Ljubljana', um: 'University of Maribor', up: 'University of Primorska', ung: 'University of Nova Gorica', other: 'Another institution' },
|
||||
cities: { ljubljana: 'Ljubljana', maribor: 'Maribor', koper: 'Koper', 'nova-gorica': 'Nova Gorica', celje: 'Celje', other: 'Another city' },
|
||||
yourUni: 'your chosen university', yourCity: 'your study city',
|
||||
link: { journey: 'the full journey', bachelor: 'the bachelor application', master: 'the master application', deadlines: 'current deadlines', evs: 'the eVŠ guide' }
|
||||
budgets: { 'under-400': 'Under €400 per month', '400-600': '€400 to €600 per month', '600-800': '€600 to €800 per month', 'over-800': 'Over €800 per month', 'unsure': 'Not sure yet' }
|
||||
},
|
||||
si: {
|
||||
heading: 'Ustvari mojo pot',
|
||||
sub: 'Odgovorite na nekaj vprašanj in orisali bomo vaše naslednje korake.',
|
||||
build: 'Prikaži mojo pot',
|
||||
reset: 'Ponastavi',
|
||||
need: 'Izberite vsaj stopnjo študija in vašo trenutno fazo.',
|
||||
resultHead: 'Vaša pot',
|
||||
contextParts: { level: 'Stopnja', phase: 'Faza', uni: 'Institucija', city: 'Mesto', year: 'Študijsko leto' },
|
||||
fields: {
|
||||
level: 'Stopnja študija', phase: 'Kje ste zdaj?', uni: 'Univerza', city: 'Študijsko mesto', year: 'Študijsko leto'
|
||||
},
|
||||
pick: 'Izberite',
|
||||
roles: { student: 'Študent', parent: 'Starš ali družinski član' },
|
||||
levels: { bachelor: 'Dodiplomski', master: 'Magistrski' },
|
||||
phases: { exploring: 'Šele raziskujem', applying: 'Prijavljam se', accepted: 'Sprejet sem', moving: 'Pripravljam se na selitev', arrived: 'Sem že v Sloveniji' },
|
||||
unis: { ul: 'Univerza v Ljubljani', um: 'Univerza v Mariboru', up: 'Univerza na Primorskem', ung: 'Univerza v Novi Gorici', other: 'Druga ustanova' },
|
||||
cities: { ljubljana: 'Ljubljana', maribor: 'Maribor', koper: 'Koper', 'nova-gorica': 'Nova Gorica', celje: 'Celje', other: 'Drugo mesto' },
|
||||
yourUni: 'izbrano univerzo', yourCity: 'vaše študijsko mesto',
|
||||
link: { journey: 'celotno pot', bachelor: 'prijavo na dodiplomski študij', master: 'prijavo na magistrski študij', deadlines: 'trenutne roke', evs: 'navodila za eVŠ' }
|
||||
budgets: { 'under-400': 'Manj kot 400 € na mesec', '400-600': '400 do 600 € na mesec', '600-800': '600 do 800 € na mesec', 'over-800': 'Več kot 800 € na mesec', 'unsure': 'Še nisem prepričan' }
|
||||
},
|
||||
mk: {
|
||||
heading: 'Создади го мојот пат',
|
||||
sub: 'Одговорете на неколку прашања и ќе ги оцртаме вашите следни чекори.',
|
||||
build: 'Прикажи го мојот пат',
|
||||
reset: 'Ресетирај',
|
||||
need: 'Изберете барем степен на студии и вашата тековна фаза.',
|
||||
resultHead: 'Вашиот пат',
|
||||
contextParts: { level: 'Степен', phase: 'Фаза', uni: 'Институција', city: 'Град', year: 'Академска година' },
|
||||
fields: {
|
||||
level: 'Степен на студии', phase: 'Каде сте сега?', uni: 'Универзитет', city: 'Град на студирање', year: 'Академска година'
|
||||
},
|
||||
pick: 'Изберете',
|
||||
roles: { student: 'Студент', parent: 'Родител или член на семејство' },
|
||||
levels: { bachelor: 'Додипломски', master: 'Магистерски' },
|
||||
phases: { exploring: 'Сè уште истражувам', applying: 'Се пријавувам', accepted: 'Примен сум', moving: 'Се подготвувам за преселба', arrived: 'Веќе сум во Словенија' },
|
||||
unis: { ul: 'Универзитет во Љубљана', um: 'Универзитет во Марибор', up: 'Универзитет на Приморска', ung: 'Универзитет во Нова Горица', other: 'Друга институција' },
|
||||
cities: { ljubljana: 'Љубљана', maribor: 'Марибор', koper: 'Копер', 'nova-gorica': 'Нова Горица', celje: 'Целје', other: 'Друг град' },
|
||||
yourUni: 'избраниот универзитет', yourCity: 'вашиот град на студирање',
|
||||
link: { journey: 'целосниот пат', bachelor: 'пријавата за додипломски', master: 'пријавата за магистерски', deadlines: 'тековните рокови', evs: 'водичот за eVŠ' }
|
||||
budgets: { 'under-400': 'Помалку од 400 € месечно', '400-600': '400 до 600 € месечно', '600-800': '600 до 800 € месечно', 'over-800': 'Над 800 € месечно', 'unsure': 'Сè уште не сум сигурен' }
|
||||
}
|
||||
};
|
||||
|
||||
// Step templates per phase. {uni}/{city} are interpolated; `link` is a key of S (or null).
|
||||
// Each step text is provided per language.
|
||||
var STEPS = {
|
||||
exploring: [
|
||||
{ link: 'journey', en: 'Compare programmes and universities before you settle on a city.', si: 'Primerjajte programe in univerze, preden se odločite za mesto.', mk: 'Споредете програми и универзитети пред да се одлучите за град.' },
|
||||
{ link: 'deadlines', en: 'Check the current application deadlines for {uni}.', si: 'Preverite trenutne prijavne roke za {uni}.', mk: 'Проверете ги тековните рокови за пријавување за {uni}.' },
|
||||
{ link: '{levelPage}', en: 'Read the {levelWord} application overview.', si: 'Preberite pregled prijave na {levelWord} študij.', mk: 'Прочитајте го прегледот за пријавување на {levelWord} студии.' },
|
||||
{ link: null, en: 'Confirm whether any special-status exception applies to you before you rely on the standard pathway.', si: 'Preverite, ali za vas velja katera od izjem s posebnim statusom, preden se zanesete na običajno pot.', mk: 'Потврдете дали за вас важи некој исклучок со посебен статус пред да се потпрете на стандардниот пат.' }
|
||||
],
|
||||
applying: [
|
||||
{ link: '{levelPage}', en: 'Prepare your document package for {uni}.', si: 'Pripravite paket dokumentov za {uni}.', mk: 'Подгответе го пакетот документи за {uni}.' },
|
||||
{ link: 'evs', en: 'Submit your application through eVŠ and save the confirmation.', si: 'Oddajte prijavo prek eVŠ in shranite potrdilo.', mk: 'Поднесете ја пријавата преку eVŠ и зачувајте ја потврдата.' },
|
||||
{ link: 'journey', en: 'Start recognition of your foreign education inside the application.', si: 'Znotraj prijave začnite postopek priznavanja tujega izobraževanja.', mk: 'Во рамките на пријавата започнете го признавањето на странското образование.' },
|
||||
{ link: 'deadlines', en: 'Track the document deadline and result date for {uni}.', si: 'Spremljajte rok za dokumente in datum rezultatov za {uni}.', mk: 'Следете го рокот за документи и датумот на резултати за {uni}.' }
|
||||
],
|
||||
accepted: [
|
||||
{ link: 'journey', en: 'Read the faculty enrolment instructions and complete your enrolment.', si: 'Preberite navodila fakultete za vpis in dokončajte vpis.', mk: 'Прочитајте ги упатствата на факултетот за упис и завршете го уписот.' },
|
||||
{ link: null, en: 'Request your certificate of enrolment (potrdilo o vpisu).', si: 'Zaprosite za potrdilo o vpisu.', mk: 'Побарајте потврда за упис (potrdilo o vpisu).' },
|
||||
{ link: 'journey', en: 'Begin residence, health (RM/SI 3), and housing steps in parallel.', si: 'Vzporedno začnite urejati prebivanje, zdravstvo (RM/SI 3) in nastanitev.', mk: 'Паралелно започнете ги чекорите за престој, здравство (RM/SI 3) и сместување.' },
|
||||
{ link: null, en: 'Arrange accommodation in {city} as early as possible.', si: 'Čim prej uredite nastanitev v mestu {city}.', mk: 'Средете сместување во {city} што е можно поскоро.' }
|
||||
],
|
||||
moving: [
|
||||
{ link: 'journey', en: 'Arrange your temporary residence permit for study before or on arrival.', si: 'Uredite dovoljenje za začasno prebivanje za študij pred prihodom ali ob njem.', mk: 'Средете дозвола за привремен престој за студии пред или при пристигнувањето.' },
|
||||
{ link: null, en: 'Obtain the Macedonian RM/SI 3 health-insurance form where eligible.', si: 'Pridobite makedonski obrazec RM/SI 3 za zdravstveno zavarovanje, če ste upravičeni.', mk: 'Обезбедете го македонскиот образец РМ/СИ 3 за здравствено осигурување ако имате право.' },
|
||||
{ link: null, en: 'Organise accommodation and plan address registration in {city}.', si: 'Uredite nastanitev in načrtujte prijavo prebivališča v mestu {city}.', mk: 'Организирајте сместување и планирајте пријава на адреса во {city}.' },
|
||||
{ link: 'journey', en: 'Pack originals of your education and residence documents in hand luggage.', si: 'V ročno prtljago spravite izvirnike izobraževalnih in bivalnih dokumentov.', mk: 'Спакувајте ги оригиналите од образовните и документите за престој во рачен багаж.' }
|
||||
],
|
||||
arrived: [
|
||||
{ link: null, en: 'Register your address at the administrative unit within the deadline.', si: 'V roku prijavite prebivališče na upravni enoti.', mk: 'Пријавете ја адресата во управната единица во рокот.' },
|
||||
{ link: null, en: 'Submit RM/SI 3 to ZZZS and activate your health insurance.', si: 'Oddajte RM/SI 3 na ZZZS in aktivirajte zdravstveno zavarovanje.', mk: 'Поднесете РМ/СИ 3 до ЗЗЗС и активирајте го здравственото осигурување.' },
|
||||
{ link: null, en: 'Activate student meals and student transport in {city}.', si: 'Aktivirajte študentsko prehrano in študentski prevoz v mestu {city}.', mk: 'Активирајте студентска исхрана и студентски превоз во {city}.' },
|
||||
{ link: 'journey', en: 'Track your residence and insurance expiry dates through the year.', si: 'Skozi leto spremljajte datume poteka prebivanja in zavarovanja.', mk: 'Во текот на годината следете ги датумите на истекување на престојот и осигурувањето.' }
|
||||
]
|
||||
};
|
||||
|
||||
var t = T[lang] || T.en;
|
||||
|
||||
function opt(value, label) {
|
||||
var o = document.createElement('option');
|
||||
o.value = value; o.textContent = label; return o;
|
||||
}
|
||||
function opt(value, label) { var o = document.createElement('option'); o.value = value; o.textContent = label; return o; }
|
||||
function fillSelect(sel, obj, withPick) {
|
||||
if (!sel) return;
|
||||
if (withPick) sel.appendChild(opt('', t.pick + '…'));
|
||||
Object.keys(obj).forEach(function (k) { sel.appendChild(opt(k, obj[k])); });
|
||||
}
|
||||
|
||||
var selRole = root.querySelector('[data-role="role"]');
|
||||
var selLevel = root.querySelector('[data-role="level"]');
|
||||
var selPhase = root.querySelector('[data-role="phase"]');
|
||||
var selUni = root.querySelector('[data-role="uni"]');
|
||||
var facInput = root.querySelector('[data-role="faculty"]');
|
||||
var selCity = root.querySelector('[data-role="city"]');
|
||||
var selBudget = root.querySelector('[data-role="budget"]');
|
||||
var selYear = root.querySelector('[data-role="year"]');
|
||||
var result = root.querySelector('.guide-route-result');
|
||||
|
||||
fillSelect(selRole, t.roles, true);
|
||||
fillSelect(selLevel, t.levels, true);
|
||||
fillSelect(selPhase, t.phases, true);
|
||||
fillSelect(selUni, t.unis, true);
|
||||
fillSelect(selCity, t.cities, true);
|
||||
[ '2026/2027', '2027/2028' ].forEach(function (y) { selYear.appendChild(opt(y, y)); });
|
||||
fillSelect(selBudget, t.budgets, true);
|
||||
['2026/2027', '2027/2028'].forEach(function (y) { selYear.appendChild(opt(y, y)); });
|
||||
|
||||
function esc(s) { return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); }
|
||||
|
||||
function collect() {
|
||||
return {
|
||||
role: selRole ? selRole.value : '',
|
||||
level: selLevel.value,
|
||||
phase: selPhase.value,
|
||||
uni: selUni.value,
|
||||
faculty: facInput ? facInput.value.replace(/^\s+|\s+$/g, '') : '',
|
||||
city: selCity.value,
|
||||
budget: selBudget ? selBudget.value : '',
|
||||
year: selYear.value
|
||||
};
|
||||
}
|
||||
|
||||
// Exposed for the test harness; returns the results URL, or null if incomplete.
|
||||
function buildUrl() {
|
||||
var v = collect();
|
||||
if (!v.level || !v.phase) return null;
|
||||
var parts = [];
|
||||
Object.keys(v).forEach(function (k) { if (v[k]) parts.push(k + '=' + encodeURIComponent(v[k])); });
|
||||
parts.push('lang=' + encodeURIComponent(lang));
|
||||
return 'my-route/?' + parts.join('&');
|
||||
}
|
||||
window.__msosRouteUrl = buildUrl;
|
||||
|
||||
function build() {
|
||||
var level = selLevel.value, phase = selPhase.value;
|
||||
if (!level || !phase) {
|
||||
var url = buildUrl();
|
||||
if (!url) {
|
||||
result.hidden = false;
|
||||
result.innerHTML = '<p class="guide-route-context">' + esc(t.need) + '</p>';
|
||||
return;
|
||||
}
|
||||
var uniText = selUni.value ? t.unis[selUni.value] : t.yourUni;
|
||||
var cityText = selCity.value ? t.cities[selCity.value] : t.yourCity;
|
||||
var levelWord = t.levels[level].toLowerCase();
|
||||
var levelPage = level === 'master' ? S.master : S.bachelor;
|
||||
|
||||
var ctx = [];
|
||||
ctx.push(t.contextParts.level + ': ' + t.levels[level]);
|
||||
ctx.push(t.contextParts.phase + ': ' + t.phases[phase]);
|
||||
if (selUni.value) ctx.push(t.contextParts.uni + ': ' + t.unis[selUni.value]);
|
||||
if (selCity.value) ctx.push(t.contextParts.city + ': ' + t.cities[selCity.value]);
|
||||
if (selYear.value) ctx.push(t.contextParts.year + ': ' + selYear.value);
|
||||
|
||||
var items = STEPS[phase].map(function (step) {
|
||||
var text = step[lang] || step.en;
|
||||
text = text.replace('{uni}', uniText).replace('{city}', cityText).replace('{levelWord}', levelWord);
|
||||
var linkKey = step.link;
|
||||
if (linkKey === '{levelPage}') linkKey = level === 'master' ? 'master' : 'bachelor';
|
||||
var slug = linkKey && S[linkKey];
|
||||
var li = esc(text);
|
||||
if (slug) {
|
||||
var label = t.link[linkKey] || linkKey;
|
||||
li += ' <a href="' + slug + '/">' + esc(label) + ' →</a>';
|
||||
}
|
||||
return '<li>' + li + '</li>';
|
||||
}).join('');
|
||||
|
||||
result.hidden = false;
|
||||
result.innerHTML =
|
||||
'<h3>' + esc(t.resultHead) + '</h3>' +
|
||||
'<p class="guide-route-context">' + esc(ctx.join(' · ')) + '</p>' +
|
||||
'<ol>' + items + '</ol>';
|
||||
result.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
root.querySelector('[data-action="build"]').addEventListener('click', build);
|
||||
root.querySelector('[data-action="reset"]').addEventListener('click', function () {
|
||||
[selLevel, selPhase, selUni, selCity].forEach(function (s) { s.value = ''; });
|
||||
[selRole, selLevel, selPhase, selUni, selCity, selBudget].forEach(function (s) { if (s) s.value = ''; });
|
||||
if (facInput) facInput.value = '';
|
||||
selYear.value = '2026/2027';
|
||||
result.hidden = true; result.innerHTML = '';
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user