Loading CPD Manager…
// For now we attempt a nonce-less request (works for GET, fails for write without nonce) return ''; } // ── WordPress REST API helpers ──────────────────────────────── function wpFetch(path, opts = {}) { return fetch(SITE_URL + path, { ...opts, headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': S.nonce, ...(opts.headers || {}), }, credentials: 'same-origin', }); } async function loadAllData() { const res = await wpFetch(`/wp-json/wp/v2/users/me?context=edit`); const user = await res.json(); S.user = user; const raw = user.meta?.[META_KEY]; if (raw) { try { const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw; S.data = parsed.years || {}; S.profile = { ...S.profile, ...(parsed.profile || {}) }; S.profile.display_name = user.name || ''; } catch(e) { S.data = {}; } } else { S.profile.display_name = user.name || ''; } // Ensure current year exists if (!S.data[S.year]) S.data[S.year] = { entries:[], plan:{ review_text:'', plan_text:'' } }; } async function saveAllData() { const payload = { years: S.data, profile: S.profile }; const res = await wpFetch(`/wp-json/wp/v2/users/${S.user.id}`, { method: 'PUT', body: JSON.stringify({ meta: { [META_KEY]: JSON.stringify(payload) } }), }); return res.ok; } // ── Render ──────────────────────────────────────────────────── function render() { document.getElementById('cpd-app').innerHTML = ` ${renderHeader()} ${renderNav()}
${renderTab()}
`; bindEvents(); } function renderHeader() { const p = S.profile; return `
Optometry Ireland Member Services
CPD Record Manager Continuing Professional Development
${esc(p.display_name || S.user?.name || 'Member')} ${p.coru_number ? 'CORU: ' + esc(p.coru_number) : 'Complete your profile ↗'}
`; } function renderNav() { const tabs = [ { id:'records', e:'📋', l:'CPD Records' }, { id:'portfolio', e:'📄', l:'Portfolio View' }, { id:'profile', e:'👤', l:'My Profile' }, ]; return ``; } function renderTab() { if (S.tab === 'records') return renderRecords(); if (S.tab === 'portfolio') return renderPortfolio(); if (S.tab === 'profile') return renderProfile(); return ''; } // ── Records Tab ─────────────────────────────────────────────── function renderRecords() { const yd = yearData(); const total = yd.entries.reduce((a,e) => a + (+e.credits||0), 0).toFixed(1); return `

📋 CPD Entries

${S.year} CPD Year
ℹ️ This is a recording tool only. Registrants are NOT required to submit this template as part of their CPD Audit. Use it to populate fields on the CORU registrant portal if called for audit.
${renderYearBar()}
${total}Total Credits
${yd.entries.length}Activities
${S.year}CPD Year
${renderTable(yd.entries)}

🔍 Review & Plan

`; } function renderYearBar() { const years = [...new Set([...Object.keys(S.data).map(Number), S.year])].sort((a,b)=>b-a); return `
${years.map(y => ``).join('')}
`; } function renderTable(entries) { if (!entries.length) return `

No CPD activities recorded for ${S.year}

Click "Add CPD Activity" to record your first entry.

`; return `${entries.map((e,i) => ``).join('')}
DateActivityTypeTime (hrs)CreditsActions
${fmtDate(e.activity_date)} ${esc(e.activity_name)}${e.learning_outcome?`
${esc(trunc(e.learning_outcome,75))}`:''}
${typeBadge(e.learning_type)} ${e.time_spent||0} ${(+e.credits||0).toFixed(1)}
`; } function typeBadge(v) { const t = TYPES.find(x=>x.v===v) || { l:v||'Other', c:'b-other' }; return `${esc(t.l)}`; } // ── Portfolio Tab ───────────────────────────────────────────── function renderPortfolio() { const p = S.profile; const yd = yearData(); const total = yd.entries.reduce((a,e)=>a+(+e.credits||0),0).toFixed(1); const today = new Date().toLocaleDateString('en-IE',{day:'numeric',month:'long',year:'numeric'}); return `

📄 CPD Portfolio

${renderYearBar()}
Use your browser's print dialog — choose "Save as PDF"
Optometry Ireland Member CPD Record
Continuing Professional Development Record
Name: ${esc(p.display_name||'')}
CORU Registration Number: ${esc(p.coru_number||'—')}
Practice: ${esc(p.practice_name||'—')}
Practice Address: ${esc(p.practice_address||'—')}
CPD Year: ${S.year}  |  Audit Period: 1 Jan ${S.year} – 31 Dec ${S.year}
Registration Board: Optometrists Registration Board (CORU)
${yd.entries.length ? yd.entries.map(e=>``).join('') : `` }
DateActivity NameType Time (hrs)CPD Credits Learning OutcomeImpact on Practice
${fmtDate(e.activity_date)} ${esc(e.activity_name)} ${TYPES.find(t=>t.v===e.learning_type)?.l||esc(e.learning_type)||''} ${e.time_spent||0} ${(+e.credits||0).toFixed(1)} ${esc(e.learning_outcome||'')} ${esc(e.impact_on_practice||'')}
No entries recorded for ${S.year}
Total CPD Credits for ${S.year}: ${total}

Review — What do I want or need to learn in the next 12 months?

${esc(yd.plan?.review_text||'—')}

Plan — What learning activities will I do to achieve this?

${esc(yd.plan?.plan_text||'—')}

This document is produced using the Optometry Ireland CPD Manager. It is a recording tool only — registrants are NOT required to submit this template as part of their CPD Audit. Generated ${today}.
`; } // ── Profile Tab ─────────────────────────────────────────────── function renderProfile() { const p = S.profile; const initials = (p.display_name||'M').split(' ').map(w=>w[0]).join('').substring(0,2).toUpperCase(); return `

👤 My Profile

${initials}
${esc(p.display_name||S.user?.name||'')}
${esc(S.user?.email||'')}

This information appears on your printed CPD portfolio. Your name is taken from your WordPress account.

`; } // ── Entry Modal ─────────────────────────────────────────────── function showModal(entry = null, idx = -1) { const isEdit = idx >= 0; const e = entry || { activity_date:'', activity_name:'', learning_type:'formal', time_spent:'', credits:'', learning_outcome:'', impact_on_practice:'' }; const opts = TYPES.map(t=>``).join(''); document.body.insertAdjacentHTML('beforeend', ` `); const close = () => document.getElementById('modal-overlay')?.remove(); document.getElementById('m-close').onclick = close; document.getElementById('m-cancel').onclick = close; document.getElementById('modal-overlay').onclick = ev => { if (ev.target.id==='modal-overlay') close(); }; document.getElementById('modal-box').onclick = ev => ev.stopPropagation(); document.getElementById('m-save').onclick = async () => { const date = document.getElementById('e-date').value; const name = document.getElementById('e-name').value.trim(); if (!date || !name) { toast('Please fill in the required fields.', 'err'); return; } const entry = { activity_date: date, activity_name: name, learning_type: document.getElementById('e-type').value, time_spent: parseFloat(document.getElementById('e-time').value||0), credits: parseFloat(document.getElementById('e-credits').value||0), learning_outcome: document.getElementById('e-outcome').value.trim(), impact_on_practice: document.getElementById('e-impact').value.trim(), _id: isEdit ? e._id : Date.now(), }; const yd = yearData(); if (isEdit) { yd.entries[idx] = entry; } else { yd.entries.push(entry); } // Sort by date yd.entries.sort((a,b) => a.activity_date.localeCompare(b.activity_date)); const ok = await saveAllData(); close(); if (ok) { toast(isEdit?'Entry updated!':'Entry added!'); render(); } else { toast('Error saving — please try again.','err'); } }; } function showNewYearModal() { document.body.insertAdjacentHTML('beforeend', ` `); const close = () => document.getElementById('modal-overlay')?.remove(); document.getElementById('m-close').onclick = close; document.getElementById('m-cancel').onclick = close; document.getElementById('m-confirm').onclick = () => { const yr = parseInt(document.getElementById('new-yr').value); if (yr >= 2020 && yr <= 2040) { S.year = yr; if (!S.data[yr]) S.data[yr] = { entries:[], plan:{ review_text:'', plan_text:'' } }; close(); render(); } }; } // ── Event binding ───────────────────────────────────────────── function bindEvents() { // Tab nav document.querySelectorAll('.nav-btn').forEach(b => b.addEventListener('click', () => { S.tab = b.dataset.tab; render(); })); // Year chips document.querySelectorAll('.yr-chip:not(.new)').forEach(b => b.addEventListener('click', () => { S.year = parseInt(b.dataset.year); render(); })); document.getElementById('btn-new-year')?.addEventListener('click', showNewYearModal); // Add entry document.getElementById('btn-add')?.addEventListener('click', () => showModal()); // Edit / delete document.querySelectorAll('[data-edit]').forEach(b => b.addEventListener('click', () => { const i = +b.dataset.edit; showModal(yearData().entries[i], i); })); document.querySelectorAll('[data-del]').forEach(b => b.addEventListener('click', async () => { if (!confirm('Delete this CPD entry? This cannot be undone.')) return; yearData().entries.splice(+b.dataset.del, 1); const ok = await saveAllData(); if (ok) { toast('Entry deleted.'); render(); } else { toast('Error deleting entry.','err'); } })); // Save plan document.getElementById('btn-save-plan')?.addEventListener('click', async () => { yearData().plan = { review_text: document.getElementById('plan-review').value, plan_text: document.getElementById('plan-plan').value, }; const ok = await saveAllData(); toast(ok ? 'Review & Plan saved!' : 'Error saving.', ok ? 'ok' : 'err'); }); // Save profile document.getElementById('btn-save-profile')?.addEventListener('click', async () => { S.profile.coru_number = document.getElementById('p-coru').value.trim(); S.profile.practice_name = document.getElementById('p-pname').value.trim(); S.profile.practice_address = document.getElementById('p-paddr').value.trim(); const ok = await saveAllData(); if (ok) { toast('Profile saved!'); render(); } else { toast('Error saving profile.','err'); } }); } // ── Helpers ─────────────────────────────────────────────────── function yearData() { if (!S.data[S.year]) S.data[S.year] = { entries:[], plan:{ review_text:'', plan_text:'' } }; return S.data[S.year]; } function esc(s) { return String(s||'').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } function trunc(s,n) { return s.length>n ? s.slice(0,n)+'…' : s; } function fmtDate(d) { if (!d) return ''; const [y,m,dy] = d.split('-'); return `${dy}/${m}/${y}`; } function toast(msg, type='ok') { const el = document.createElement('div'); el.className = `toast ${type}`; el.innerHTML = `${type==='ok'?'✅':'❌'} ${esc(msg)}`; document.body.appendChild(el); setTimeout(() => el.style.opacity='0', 2800); setTimeout(() => el.remove(), 3200); } function showLoginWall() { document.getElementById('cpd-app').innerHTML = `

Members CPD Manager

Please log in to access your CPD records.

Log In
`; }

Welcome to Optometry Ireland

Optometry Ireland (OI) is the non-profit professional representative body advocating on behalf of both optical professionals and the public for 120 years. We represent the vast majority of practising Optometrists in Ireland, over 800 members in 350 locations nationwide. OI works to ensure the highest possible standards in the provision of clinical and dispensing eye-care services are available to the Irish public.

 

An optometrist examines a patient using a slit lamp in an examination room.
A smiling man with glasses is embraced by two children, a curly-haired girl and a boy with tousled hair, both also wearing glasses. They are indoors, displaying a happy family moment.

Eye Health Information

From the instant we are born and right throughout our lives, our eyes send information to the brain that is vital for our survival. 80% of our world around us is processed through our eyes. This makes vision our most important sense.

Our eyes use 6 -10% of our body’s energy to process what we see.  Tired eyes that have to work too hard will affect all aspects of our quality of life.

Explore the Site

Find an Optical Practice

Find an optical practice near you.

Four hands holding colourful jigsaw puzzle pieces fitting together. Each piece is a different colour: orange, white, green, and red, symbolising teamwork and collaboration. The hands belong to people wearing business attire.

Join Optometry Ireland

Optometry Ireland is the professional representative body for Optometrists in Ireland.

Adult Eye Conditions

Learn more about adult eye conditions.

A woman with blonde hair and glasses is adjusting the glasses on a young girl with long brown hair. They are in an eyewear store with shelves of glasses in the background. The woman is wearing a denim shirt, and the girl is in a white t-shirt.

Children's Eye Health

Learn more about children’s eye health.

A young girl with curly brown hair and blue eyes smiles. There's a white geometric square overlay on her left eye, with an eye chart  in the background.

Our Vision

Optometry Ireland is committed to the improvement of public eye-care, nation-wide.

The word vision is in sharp focus in red letters at the centre, surrounded by blurred black text, creating a zoom effect that emphasises the central word.

Career Vision Requirements

Learn more about the vision requirements for different occupations in Ireland.

Platinum Sponsors

Logo of Hakim Group featuring a dark circle with HG in the center. The word HAKIM is written in large letters below, with GROUP in smaller letters underneath. The overall color scheme is dark blue.
The image features the logo of Bausch + Lomb with the text See better. Live better. in teal on a white background.

Get in Touch

Email us using the form below

Or call — 01 453 8850

Tick to consent to privacy policy