(function () {
'use strict';
var App = {
state: {
currentStep: 1,
totalSteps: 8,
isBusy: false,
leadId: null,
leadRef: null,
formCacheKey: 'agpal_registration_form',
leadIdKey: 'agpal_registration_lead_id',
leadRefKey: 'agpal_registration_lead_ref',
practitionerRowCount: 0
},
ctx: window.portalContext || {},
/* Config */
config: {
fee: parseFloat((window.portalContext || {}).fee) || 1200,
ccSurchargeRate: parseFloat((window.portalContext || {}).ccSurchargeRate) || 0.01927,
gstRate: parseFloat((window.portalContext || {}).gstRate) || 0.10,
contactTypeId: (window.portalContext || {}).contactTypeId || '',
businessId: (window.portalContext || {}).businessId || '',
operationalTatDays: parseInt((window.portalContext || {}).operationalTatDays, 10) || 14,
practiceTypeGuids: {
gp: (window.portalContext || {}).practiceTypeGP || '',
ahs: (window.portalContext || {}).practiceTypeAHS || '',
mds: (window.portalContext || {}).practiceTypeMDS || '',
aboriginal: (window.portalContext || {}).practiceTypeAboriginal || ''
},
practitionerTypeMap: { registrar: 1, vocational: 2, other: 10 }
},
STATE_MAP: {
'Australian Capital Territory': 107150000,
'New South Wales': 107150001,
'Northern Territory': 107150002,
'Queensland': 107150003,
'South Australia': 107150004,
'Tasmania': 107150005,
'Victoria': 107150006,
'Western Australia': 107150007,
/* short-code fallbacks */
'ACT': 107150000, 'NSW': 107150001, 'NT': 107150002,
'QLD': 107150003, 'SA': 107150004, 'TAS': 107150005,
'VIC': 107150006, 'WA': 107150007
},
mapState: function (v) {
if (!v) return undefined;
var n = this.STATE_MAP[v];
if (n !== undefined) return n;
n = this.STATE_MAP[(v + '').trim()];
return n !== undefined ? n : undefined;
},
/* Pending Registration — 14-day threshold
Returns true when:
- accred-status is "no" (new practice, not yet accredited)
- commencement date is more than 14 days from today
*/
isPendingRegistration: function () {
var accred = this.val('accred-status');
if (accred !== 'no') return false;
var dateStr = this.val('commencement-date');
if (!dateStr) return false;
var commenceDate = new Date(dateStr);
if (isNaN(commenceDate.getTime())) return false;
var today = new Date();
today.setHours(0, 0, 0, 0);
var diffMs = commenceDate.getTime() - today.getTime();
var diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
return diffDays > this.config.operationalTatDays;
},
dom: {},
/* Init */
init: function () {
this.injectSpinnerOverlay();
this.cacheDom();
this.clearSession();
this.bindEvents();
this.handleConditionalUI();
this.updateFeeTable();
this.updateSubmitEnabled();
console.log('[AGPAL] App initialized');
},
cacheDom: function () {
var ids = [
'stepTitle', 'stepCount', 'progressFill',
'btn-submit', 'btn-submit-agpal',
'lead-status-msg', 'lead-card', 'lead-card-content',
'ref-number', 'toast', 'toast-msg',
'payment-method', 'practice-name', 'practice-email',
'group-accred', 'corporate-entity-name',
'practitioners', 'practitioner-dynamic-section',
'practitioner-over10-section', 'practitioner-rows-container',
'surcharge-row', 'gst-row', 'total-row'
];
for (var i = 0; i < ids.length; i++) {
this.dom[ids[i]] = document.getElementById(ids[i]);
}
},
byId: function (id) { return document.getElementById(id); },
val: function (id) {
var el = this.byId(id);
return el && typeof el.value === 'string' ? el.value.trim() : '';
},
/* ── Toast ──────────────────────────────────────────────── */
showToast: function (message, isError) {
if (!this.dom.toast || !this.dom['toast-msg']) return;
this.dom['toast-msg'].textContent = message;
this.dom.toast.classList.toggle('toast-error', !!isError);
this.dom.toast.classList.add('show');
setTimeout(function () {
if (App.dom.toast) App.dom.toast.classList.remove('show');
}, 4000);
},
injectSpinnerOverlay: function () {
if (document.getElementById('agpal-spinner-overlay')) return;
var style = document.createElement('style');
style.textContent = '@keyframes agpal-spin{to{transform:rotate(360deg)}}'
+ '#agpal-spinner-overlay{display:none;position:fixed;inset:0;background:rgba(26,46,74,0.60);'
+ 'z-index:99999;align-items:center;justify-content:center;flex-direction:column;gap:18px;}';
document.head.appendChild(style);
var overlay = document.createElement('div');
overlay.id = 'agpal-spinner-overlay';
overlay.setAttribute('aria-live', 'assertive');
overlay.innerHTML =
'
'
+ '
Please wait…
';
document.body.appendChild(overlay);
},
/* Busy */
setBusy: function (busy, buttonText) {
this.state.isBusy = busy;
var overlay = document.getElementById('agpal-spinner-overlay');
var msgEl = document.getElementById('agpal-spinner-msg');
if (overlay) overlay.style.display = busy ? 'flex' : 'none';
if (msgEl && buttonText) msgEl.textContent = buttonText;
if (msgEl && !busy) msgEl.textContent = 'Please wait…';
var buttons = document.querySelectorAll('button');
buttons.forEach(function (btn) {
if (busy) {
btn.setAttribute('data-was-disabled', btn.disabled ? '1' : '0');
btn.disabled = true;
} else {
btn.disabled = btn.getAttribute('data-was-disabled') === '1';
}
});
if (!busy && this.dom['btn-submit']) {
this.dom['btn-submit'].disabled = !this.areDeclarationsChecked();
}
},
/* CSRF */
waitForToken: function () {
return new Promise(function (resolve, reject) {
var cached = (window.portalContext || {}).csrfToken
|| (window.portalContext || {}).requestVerificationToken;
if (cached && cached.length > 20) { resolve(cached); return; }
var domEl = document.querySelector('input[name="__RequestVerificationToken"]');
if (domEl && domEl.value) { resolve(domEl.value); return; }
var csrfEl = document.getElementById('csrf-token-value');
if (csrfEl && csrfEl.value && csrfEl.value.length > 20) { resolve(csrfEl.value); return; }
var attempts = 0;
var poll = setInterval(function () {
attempts++;
var pc = (window.portalContext || {}).csrfToken;
if (pc && pc.length > 20) { clearInterval(poll); resolve(pc); return; }
var el = document.getElementById('csrf-token-value');
if (el && el.value && el.value.length > 20) { clearInterval(poll); resolve(el.value); return; }
if (attempts >= 30) {
clearInterval(poll);
fetch('/Account/Login', { credentials: 'same-origin', headers: { 'Accept': 'text/html' } })
.then(function (r) { return r.text(); })
.then(function (html) {
var m = html.match(/name="__RequestVerificationToken"[^>]*value="([^"]+)"/);
if (!m) m = html.match(/value="([^"]{40,})"[^>]*name="__RequestVerificationToken"/);
if (m && m[1]) { resolve(m[1]); }
else { reject(new Error('CSRF token not available.')); }
})
.catch(function (e) { reject(new Error('CSRF: ' + e.message)); });
}
}, 150);
});
},
/* API helpers */
parseApiError: function (xhr) {
try { if (xhr && xhr.responseJSON && xhr.responseJSON.error) return xhr.responseJSON.error.message || 'Unknown error'; } catch (e1) { }
try { if (xhr && xhr.responseText) { var p = JSON.parse(xhr.responseText); return (p && p.error && p.error.message) || 'Unknown error'; } } catch (e2) { }
return 'Unknown error';
},
extractGuidFromOdata: function (data) {
var id = (data && data['@odata.id']) || '';
var m = id.match(/\(([0-9a-f-]{36})\)/i);
return m ? m[1] : null;
},
extractEntityId: function (data, response) {
if (data && typeof data === 'object') {
var fromBody = data.leadid || data.accountid || data.contactid
|| data.ongc_practitionerid || data.ongc_paymentevidenceid
|| this.extractGuidFromOdata(data);
if (fromBody) return fromBody;
}
var hv = '';
if (response) {
if (typeof response.headers === 'object' && typeof response.headers.get === 'function') {
hv = response.headers.get('OData-EntityId') || '';
}
if (!hv && typeof response.getResponseHeader === 'function') {
hv = response.getResponseHeader('OData-EntityId') || response.getResponseHeader('entityid') || '';
}
}
if (hv) { var m = hv.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i); if (m) return m[0]; }
return null;
},
/* POST */
dataversePost: async function (entitySet, payload) {
var url = '/_api/' + entitySet;
if (window.webapi && typeof window.webapi.safeAjax === 'function') {
return new Promise(function (resolve, reject) {
window.webapi.safeAjax({
type: 'POST', url: url, contentType: 'application/json',
data: JSON.stringify(payload),
success: function (data, status, xhr) { resolve({ id: App.extractEntityId(data, xhr), data: data || {} }); },
error: function (xhr) { reject(new Error(App.parseApiError(xhr))); }
});
});
}
var token = await this.waitForToken();
var resp = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json', '__RequestVerificationToken': token,
'OData-MaxVersion': '4.0', 'OData-Version': '4.0'
},
body: JSON.stringify(payload)
});
if (!resp.ok) {
var em = 'API error ' + resp.status;
try { var ej = await resp.json(); em = (ej && ej.error && ej.error.message) || em; } catch (e) { }
throw new Error(em);
}
var data = {};
try { data = await resp.json(); } catch (e) { }
return { id: this.extractEntityId(data, resp), data: data };
},
/* GET — used for existence lookups */
dataverseGet: async function (url) {
try {
var token = await this.waitForToken();
var resp = await fetch(url, {
method: 'GET',
headers: {
'__RequestVerificationToken': token,
'OData-MaxVersion': '4.0', 'OData-Version': '4.0', 'Accept': 'application/json'
}
});
if (!resp.ok) return null;
return await resp.json();
} catch (e) { return null; }
},
/* Existence checks */
/* Contact: check emailaddress1 OR mobilephone OR telephone1 */
/* Contact lookup — OR across 2 fields:
emailaddress1 eq, mobilephone eq */
findExistingContact: async function (email, mobile) {
var filters = [];
function esc(v) { return (v || '').replace(/'/g, "''"); }
if (email) filters.push("emailaddress1 eq '" + esc(email) + "'");
if (mobile) filters.push("mobilephone eq '" + esc(mobile) + "'");
if (!filters.length) return null;
var data = await this.dataverseGet(
'/_api/contacts?$filter=' + encodeURIComponent(filters.join(' or ')) + '&$top=1&$select=contactid'
);
var id = (data && data.value && data.value[0]) ? data.value[0].contactid : null;
console.log('[AGPAL] findExistingContact:', id || 'not found');
return id;
},
/* Account — OR across 5 fields:
emailaddress1 eq, name eq, emailaddress2 eq,
ongc_clientid contains, ongc_practiceabn eq */
findExistingAccount: async function (email, name, email2, clientId, abn) {
var filters = [];
function esc(v) { return (v || '').replace(/'/g, "''"); }
if (email) filters.push("emailaddress1 eq '" + esc(email) + "'");
if (name) filters.push("name eq '" + esc(name) + "'");
if (clientId) filters.push("contains(ongc_clientid,'" + esc(clientId) + "')");
if (abn) filters.push("ongc_abn eq '" + esc(abn) + "'");
if (!filters.length) return null;
var data = await this.dataverseGet(
'/_api/accounts?$filter=' + encodeURIComponent(filters.join(' or ')) + '&$top=1&$select=accountid'
);
var id = (data && data.value && data.value[0]) ? data.value[0].accountid : null;
console.log('[AGPAL] findExistingAccount:', id || 'not found');
return id;
},
/* Misc */
round2: function (n) { return Math.round(n * 100) / 100; },
clearSession: function () {
try {
sessionStorage.removeItem(this.state.formCacheKey);
sessionStorage.removeItem(this.state.leadIdKey);
sessionStorage.removeItem(this.state.leadRefKey);
} catch (e) { }
this.state.leadId = null;
this.state.leadRef = null;
},
saveState: function () {
try {
sessionStorage.setItem(this.state.formCacheKey, JSON.stringify(this.collectFormData()));
if (this.state.leadId) sessionStorage.setItem(this.state.leadIdKey, this.state.leadId);
if (this.state.leadRef) sessionStorage.setItem(this.state.leadRefKey, this.state.leadRef);
} catch (e) { }
},
resetForm: function () {
try {
sessionStorage.removeItem(this.state.formCacheKey);
sessionStorage.removeItem(this.state.leadIdKey);
sessionStorage.removeItem(this.state.leadRefKey);
} catch (e) { }
document.querySelectorAll('input, select, textarea').forEach(function (el) {
if (el.type === 'checkbox' || el.type === 'radio') { el.checked = el.defaultChecked; }
else { el.value = el.defaultValue || ''; }
});
var c = document.getElementById('practitioner-rows-container');
if (c) c.innerHTML = '';
document.querySelectorAll('.error-msg').forEach(function (el) { el.classList.remove('visible'); });
if (this.dom['lead-card']) this.dom['lead-card'].style.display = 'none';
if (this.dom['lead-status-msg']) this.dom['lead-status-msg'].style.display = 'none';
if (this.dom['ref-number']) this.dom['ref-number'].textContent = '\u2014';
if (this.dom['btn-submit']) {
this.dom['btn-submit'].disabled = true;
this.dom['btn-submit'].textContent = 'Proceed to Payment';
}
this.state.currentStep = 1;
this.state.isBusy = false;
this.state.leadId = null;
this.state.leadRef = null;
this.handleConditionalUI();
this.updateFeeTable();
this.goTo(1);
},
calcFees: function (isCC) {
var base = this.config.fee;
var sur = isCC ? this.round2(base * this.config.ccSurchargeRate) : 0;
var gst = this.round2((base + sur) * this.config.gstRate);
return { base: base, surcharge: sur, gst: gst, total: this.round2(base + sur + gst) };
},
updateFeeTable: function () {
var fees = this.calcFees(this.val('payment-method') === 'cc');
if (this.dom['surcharge-row']) this.dom['surcharge-row'].textContent = '$' + fees.surcharge.toFixed(2);
if (this.dom['gst-row']) this.dom['gst-row'].textContent = '$' + fees.gst.toFixed(2);
if (this.dom['total-row']) this.dom['total-row'].textContent = '$' + fees.total.toFixed(2);
},
showError: function (id) { var el = this.byId(id); if (el) el.classList.add('visible'); },
hideError: function (id) { var el = this.byId(id); if (el) el.classList.remove('visible'); },
focusFirstError: function () {
var err = document.querySelector('.error-msg.visible');
if (!err) return;
var grp = err.closest('.field-group');
if (!grp) return;
var inp = grp.querySelector('input, select, textarea');
if (inp) inp.focus();
},
/* Validation */
validateStep: function (step) {
var ok = true;
function req(id, errId) {
if (!App.val(id)) { App.showError(errId); ok = false; }
else { App.hideError(errId); }
}
if (step === 1) {
req('accred-status', 'err-accred');
var accred = this.val('accred-status');
if (accred === 'yes-other') { req('other-org-name', 'err-other-org-name'); req('other-expiry-date', 'err-other-expiry-date'); }
if (accred === 'no') { req('commencement-date', 'err-commencement-date'); }
}
if (step === 2) {
req('practice-name', 'err-practice-name'); req('practice-abn', 'err-practice-abn');
req('practice-phone', 'err-practice-phone'); req('practice-email', 'err-practice-email');
req('standards', 'err-standards');
}
if (step === 3) {
req('contact-first', 'err-contact-name'); req('contact-last', 'err-contact-name');
req('contact-position', 'err-contact-position'); req('contact-phone', 'err-contact-phone');
req('contact-email', 'err-contact-email'); req('gp-first', 'err-gp-name');
req('gp-last', 'err-gp-name');
}
if (step === 4) {
req('addr1', 'err-address'); req('city', 'err-address');
req('state', 'err-address'); req('postcode', 'err-address');
var postalYes = document.querySelector('input[name="postal"][value="yes"]:checked');
if (postalYes) {
req('postal-addr1', 'err-postal-address'); req('postal-city', 'err-postal-address');
req('postal-state', 'err-postal-address'); req('postal-postcode', 'err-postal-address');
} else { this.hideError('err-postal-address'); }
}
if (step === 5) {
req('phn', 'err-phn'); req('practice-type', 'err-practice-type'); req('group-accred', 'err-group-accred');
var ga = this.val('group-accred');
if (ga === 'yes-corporate' || ga === 'yes-group') { req('corporate-entity-name', 'err-corporate-entity'); }
else { this.hideError('err-corporate-entity'); }
}
if (step === 6) {
var pCount = this.val('practitioners');
if (!pCount) { this.showError('err-practitioners'); ok = false; }
else { this.hideError('err-practitioners'); }
if (pCount && pCount !== 'over-10') {
var n = parseInt(pCount, 10);
for (var i = 1; i <= n; i++) {
if (!this.val('prac-first-' + i) || !this.val('prac-last-' + i) || !this.val('prac-hours-' + i) || !this.val('prac-type-' + i)) {
this.showError('err-practitioner-rows'); ok = false; break;
} else { this.hideError('err-practitioner-rows'); }
}
}
}
/* Step 7 — payment method only required for non-pending registrations */
if (step === 7) {
if (!this.isPendingRegistration()) {
req('payment-method', 'err-payment');
} else {
this.hideError('err-payment');
}
}
if (!ok) this.focusFirstError();
return ok;
},
areDeclarationsChecked: function () {
var basicChecked = !!(
this.byId('cb-auth') && this.byId('cb-auth').checked &&
this.byId('cb-terms') && this.byId('cb-terms').checked &&
this.byId('cb-prof') && this.byId('cb-prof').checked
);
if (!basicChecked) return false;
/* For pending registrations the additional acknowledgment checkbox is mandatory */
if (this.isPendingRegistration()) {
return !!(this.byId('cb-pending-ack') && this.byId('cb-pending-ack').checked);
}
return true;
},
updateSubmitEnabled: function () {
if (!this.dom['btn-submit']) return;
/* Pending path: enabled purely by checkbox state — lead created on submit.
Normal path: also requires leadId to exist (set after page 7 creates the lead). */
var declarationsOk = this.areDeclarationsChecked();
var canSubmit = this.isPendingRegistration()
? declarationsOk && !this.state.isBusy
: declarationsOk && !this.state.isBusy && !!this.state.leadId;
this.dom['btn-submit'].disabled = !canSubmit;
},
/* Data collection */
collectPractitioners: function () {
var result = [];
var v = this.val('practitioners');
if (!v || v === 'over-10') return result;
var count = parseInt(v, 10);
for (var i = 1; i <= count; i++) {
var firstName = this.val('prac-first-' + i);
var lastName = this.val('prac-last-' + i);
result.push({
title: this.val('prac-title-' + i),
firstName: firstName,
lastName: lastName,
name: (firstName + ' ' + lastName).trim(),
hours: this.val('prac-hours-' + i),
type: this.val('prac-type-' + i)
});
}
return result;
},
collectFormData: function () {
var accredVal = this.val('accred-status');
var postalSameAsStreet = !document.querySelector('input[name="postal"][value="yes"]:checked');
return {
accredStatus: accredVal,
otherOrgName: accredVal === 'yes-other' ? this.val('other-org-name') : '',
otherExpiry: accredVal === 'yes-other' ? this.val('other-expiry-date') : '',
commenceDate: accredVal === 'no' ? this.val('commencement-date') : '',
isPending: this.isPendingRegistration(),
practiceName: this.val('practice-name'),
practiceAbn: this.val('practice-abn'),
practicePhone: this.val('practice-phone'),
practiceFax: this.val('practice-fax'),
practiceEmail: this.val('practice-email'),
standards: this.val('standards'),
contactPrefix: this.val('contact-prefix'),
contactFirst: this.val('contact-first'),
contactLast: this.val('contact-last'),
contactPosition: this.val('contact-position'),
contactPhone: this.val('contact-phone'),
contactEmail: this.val('contact-email'),
contactMethod: this.val('contact-method'),
gpPrefix: this.val('gp-prefix'), gpFirst: this.val('gp-first'), gpLast: this.val('gp-last'),
addr1: this.val('addr1'), addr2: this.val('addr2'), city: this.val('city'),
state: this.val('state'), postcode: this.val('postcode'),
postalSameAsStreet: postalSameAsStreet,
postalAddr1: postalSameAsStreet ? '' : this.val('postal-addr1'),
postalAddr2: postalSameAsStreet ? '' : this.val('postal-addr2'),
postalCity: postalSameAsStreet ? '' : this.val('postal-city'),
postalState: postalSameAsStreet ? '' : this.val('postal-state'),
postalPostcode: postalSameAsStreet ? '' : this.val('postal-postcode'),
phn: this.val('phn'), pipId: this.val('pip-id'),
practiceType: this.val('practice-type'), groupAccred: this.val('group-accred'),
corporateEntity: this.val('corporate-entity-name'),
practitionerCount: this.val('practitioners'),
practitioners: this.collectPractitioners(),
paymentMethod: this.val('payment-method')
};
},
/* Payloads */
buildLeadPayload: function (d) {
var isPracticeAccredited = d.accredStatus === 'yes-agpal' ? 107150001
: d.accredStatus === 'yes-other' ? 107150002 : 107150000;
var groupAccredValue = d.groupAccred === 'yes-corporate' ? 107150001
: d.groupAccred === 'yes-group' ? 107150002 : 107150000;
var practiceTypeGuid = this.config.practiceTypeGuids[d.practiceType] || null;
/* For pending registrations, flag the subject and status so
can route the notification to adminbd@agpal.com.au (BCC marketingteam@agpal.com.au)
and NOT progress into downstream systems Internally. */
var subjectLine = d.isPending
? 'AGPAL Registration (ON HOLD \u2013 Pending Commencement) \u2013 ' + d.contactFirst + ' ' + d.contactLast
: 'AGPAL Registration \u2013 ' + d.contactFirst + ' ' + d.contactLast;
var payload = {
subject: subjectLine,
firstname: d.contactFirst,
lastname: d.contactLast || d.practiceName,
jobtitle: d.contactPosition,
emailaddress1: d.contactEmail || d.practiceEmail,
telephone1: d.contactPhone,
mobilephone: d.practicePhone,
fax: d.practiceFax,
companyname: d.practiceName,
leadsourcecode: 8,
address1_line1: d.addr1,
address1_line2: d.addr2,
address1_city: d.city,
address1_stateorprovince: d.state,
address1_postalcode: d.postcode,
address1_country: 'Australia',
ongc_state: App.mapState(d.state), /* integer choice */
ongc_country: 107150000, /* Australia */
address2_line1: d.postalAddr1,
address2_line2: d.postalAddr2,
address2_city: d.postalCity,
address2_stateorprovince: d.postalState,
address2_postalcode: d.postalPostcode,
address2_country: d.postalAddr1 ? 'Australia' : '',
ongc_enquirytype: 8,
ongc_practicename: d.practiceName,
ongc_practiceabn: d.practiceAbn,
ongc_practicephone: d.practicePhone,
ongc_pipid: d.pipId,
ongc_corporateentity: d.corporateEntity,
ongc_groupname: d.corporateEntity,
ongc_postalsameasstreet: d.postalSameAsStreet,
ongc_ispracticeaccredited: isPracticeAccredited,
ongc_grouppracticesalreadyaccredited: groupAccredValue,
ongc_accreditationexpirydate: d.otherExpiry || null,
ongc_duedate: d.commenceDate || null,
ongc_operationalcommencementdate: d.commenceDate || null,
ongc_accreditationorganisationname: d.otherOrgName,
statuscode: d.isPending ? 107150002 : undefined,
ongc_webresponse: App.buildAgpalWebResponseHtml(d)
};
if (practiceTypeGuid) {
payload['ongc_PracticeType@odata.bind'] = '/ongc_practicetypes(' + practiceTypeGuid + ')';
}
if (App.config.businessId) {
payload['ongc_Business@odata.bind'] = '/ongc_businesses(' + App.config.businessId + ')';
}
Object.keys(payload).forEach(function (k) {
if (payload[k] === '' || payload[k] === null || payload[k] === undefined) delete payload[k];
});
return payload;
},
buildAccountPayload: function (d) {
var payload = {
name: d.practiceName,
telephone1: d.practicePhone,
emailaddress1: d.practiceEmail,
fax: d.practiceFax,
address1_line1: d.addr1,
address1_line2: d.addr2,
address1_city: d.city,
address1_stateorprovince: d.state,
address1_postalcode: d.postcode,
address1_country: 'Australia',
ongc_state: App.mapState(d.state),
ongc_country: 107150000,
ongc_practiceabn: d.practiceAbn
};
Object.keys(payload).forEach(function (k) {
if (payload[k] === '' || payload[k] === null || payload[k] === undefined) delete payload[k];
});
return payload;
},
buildContactPayload: function (d) {
var payload = {
firstname: d.contactFirst,
lastname: d.contactLast,
jobtitle: d.contactPosition,
emailaddress1: d.contactEmail,
telephone1: d.contactPhone,
mobilephone: d.practicePhone,
address1_line1: d.addr1,
address1_line2: d.addr2,
address1_city: d.city,
address1_stateorprovince: d.state,
address1_postalcode: d.postcode,
address1_country: 'Australia',
ongc_state: App.mapState(d.state),
ongc_country: 107150000
};
if (this.state.leadId) {
payload['originatingleadid@odata.bind'] = '/leads(' + this.state.leadId + ')';
}
if (this.config.contactTypeId) {
payload['ongc_ContactType@odata.bind'] = '/ongc_contacttypes(' + this.config.contactTypeId + ')';
}
Object.keys(payload).forEach(function (k) {
if (payload[k] === '' || payload[k] === null || payload[k] === undefined) delete payload[k];
});
return payload;
},
/* Practitioners: ongc_Lead@odata.bind is the lookup the
subgrid on the lead form. do not change to N:N intersect approach. */
buildPractitionerPayloads: function (d) {
var rows = d.practitioners || [];
return rows.map(function (prac) {
var hours = parseFloat(prac.hours);
var fullName = (prac.firstName + ' ' + prac.lastName).trim();
var payload = {
ongc_title: prac.title || '',
ongc_firstname: prac.firstName || '',
ongc_lastname: prac.lastName || '',
ongc_name: fullName || prac.name || '',
ongc_type: App.config.practitionerTypeMap[prac.type] !== undefined
? App.config.practitionerTypeMap[prac.type] : 10,
ongc_hoursperweek: isNaN(hours) ? null : hours
};
/* No lookup bind here — N:N association created separately via $ref */
Object.keys(payload).forEach(function (k) {
if (k === 'ongc_type') return;
if (payload[k] === null || payload[k] === undefined || payload[k] === '') delete payload[k];
});
return payload;
});
},
/* Lead card */
renderLeadCard: function (d) {
if (!this.dom['lead-card-content']) return;
function row(label, value) {
return '
' + label + '' + (value || '\u2014') + '
';
}
this.dom['lead-card-content'].innerHTML =
row('Reference', App.state.leadRef || App.state.leadId || '\u2014') +
row('Practice', d.practiceName) +
row('ABN', d.practiceAbn) +
row('Contact', (d.contactFirst + ' ' + d.contactLast).trim()) +
row('Email', d.contactEmail || d.practiceEmail) +
row('Phone', d.contactPhone) +
row('Payment', 'Credit Card');
if (this.dom['lead-card']) this.dom['lead-card'].style.display = 'block';
if (this.dom['ref-number']) this.dom['ref-number'].textContent = App.state.leadRef || App.state.leadId || '\u2014';
},
/* Create lead + children */
/* Associate practitioner to lead via N:N $ref endpoint
Relationship name: ongc_Lead_ongc_Practitioner_ongc_Practitioner
Intersect table: ongc_lead_ongc_practitioner */
associatePractitionerToLead: async function (leadId, practitionerId) {
var token = await this.waitForToken();
var baseUrl = window.location.origin;
var resp = await fetch(
'/_api/leads(' + leadId + ')/ongc_Lead_ongc_Practitioner_ongc_Practitioner/$ref',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'__RequestVerificationToken': token,
'OData-MaxVersion': '4.0',
'OData-Version': '4.0'
},
body: JSON.stringify({
'@odata.id': baseUrl + '/_api/ongc_practitioners(' + practitionerId + ')'
})
}
);
if (!resp.ok && resp.status !== 204) {
var em = 'N:N associate HTTP ' + resp.status;
try {
var ej = await resp.json();
em = (ej && ej.error && ej.error.message) || em;
} catch (e) { }
throw new Error(em);
}
console.log('[AGPAL] N:N associated practitioner', practitionerId, '→ lead', leadId);
},
/* Fetch ongc_leadrefid from the created lead record */
fetchLeadRef: async function (leadId) {
try {
var token = await this.waitForToken();
var r = await fetch(
'/_api/leads(' + leadId + ')?$select=ongc_leadrefid,ongc_clientid',
{
method: 'GET',
headers: {
'__RequestVerificationToken': token,
'OData-MaxVersion': '4.0',
'OData-Version': '4.0',
'Accept': 'application/json'
}
}
);
if (!r.ok) { console.warn('[AGPAL] fetchLeadRef HTTP', r.status); return null; }
var d = await r.json();
var ref = (d && (d.ongc_leadrefid || d.ongc_clientid)) || null;
console.log('[AGPAL] fetchLeadRef:', ref);
return ref;
} catch (e) { console.warn('[AGPAL] fetchLeadRef error:', e.message); return null; }
},
createLeadAndChildren: async function () {
var d = this.collectFormData();
this.setBusy(true, 'Submitting registration...10% \u2026');
this.saveState();
try {
/* 1 — Lead */
var lead = await this.dataversePost('leads', this.buildLeadPayload(d));
this.state.leadId = lead.id;
/* Fetch ongc_leadrefid — the human-readable reference number */
this.state.leadRef = await this.fetchLeadRef(this.state.leadId) || this.state.leadId;
if (!this.state.leadId) throw new Error('Lead GUID not returned. Check API permissions.');
console.log('[AGPAL] Lead:', this.state.leadId);
this.saveState();
/* 2 — Account: find existing or create */
this.setBusy(true, 'Submitting registration...30% \u2026');
var accountId = null;
try {
accountId = await this.findExistingAccount(d.practiceEmail, d.practiceName, null, d.corporateEntity, d.practiceAbn);
if (accountId) {
console.log('[AGPAL] Existing account:', accountId);
/* Link existing account to lead */
var at = await this.waitForToken();
await fetch('/_api/leads(' + this.state.leadId + ')', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json', '__RequestVerificationToken': at,
'OData-MaxVersion': '4.0', 'OData-Version': '4.0', 'If-Match': '*'
},
body: JSON.stringify({ 'parentaccountid@odata.bind': '/accounts(' + accountId + ')' })
});
} else {
var acct = await this.dataversePost('accounts', this.buildAccountPayload(d));
accountId = acct.id;
console.log('[AGPAL] New account:', accountId);
}
} catch (e) { console.warn('[AGPAL] Account step failed:', e.message); }
/* 3 — Contact: find existing or create */
this.setBusy(true, 'Submitting registration...55% \u2026');
try {
var existingContactId = await this.findExistingContact(d.contactEmail, d.practicePhone);
if (existingContactId) {
console.log('[AGPAL] Existing contact:', existingContactId);
/* Link existing contact to lead via both directions */
var ct = await this.waitForToken();
await fetch('/_api/leads(' + this.state.leadId + ')', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json', '__RequestVerificationToken': ct,
'OData-MaxVersion': '4.0', 'OData-Version': '4.0', 'If-Match': '*'
},
body: JSON.stringify({ 'parentcontactid@odata.bind': '/contacts(' + existingContactId + ')' })
});
/* Also set originatingleadid on the contact */
var ct2 = await this.waitForToken();
await fetch('/_api/contacts(' + existingContactId + ')', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json', '__RequestVerificationToken': ct2,
'OData-MaxVersion': '4.0', 'OData-Version': '4.0', 'If-Match': '*'
},
body: JSON.stringify({ 'originatingleadid@odata.bind': '/leads(' + this.state.leadId + ')' })
});
} else {
var contactPayload = this.buildContactPayload(d);
if (accountId) {
contactPayload['parentcustomerid_account@odata.bind'] = '/accounts(' + accountId + ')';
}
var cr = await this.dataversePost('contacts', contactPayload);
console.log('[AGPAL] New contact:', cr.id);
}
} catch (e) { console.warn('[AGPAL] Contact step failed:', e.message); }
/* 4 — Practitioners: create each, then N:N associate to lead
via ongc_Lead_ongc_Practitioner_ongc_Practitioner relationship */
var pracPayloads = this.buildPractitionerPayloads(d);
if (pracPayloads.length) {
this.setBusy(true, 'Submitting registration...80% \u2026');
for (var pi = 0; pi < pracPayloads.length; pi++) {
try {
var pracResult = await App.dataversePost('ongc_practitioners', pracPayloads[pi]);
var pracId = pracResult && pracResult.id;
console.log('[AGPAL] Practitioner ' + (pi + 1) + ' created:', pracId);
if (pracId && App.state.leadId) {
await App.associatePractitionerToLead(App.state.leadId, pracId);
}
} catch (e) {
console.warn('[AGPAL] Practitioner ' + (pi + 1) + ' failed:', e.message);
}
}
}
/* Done */
if (d.isPending) {
/* Pending path: navigate to success page, render lead card there */
this.goToSuccess(true, d);
this.showToast('Registration submitted successfully.');
} else {
/* Normal path: show lead card on page 8, user proceeds to payment */
this.renderLeadCard(d);
if (this.dom['lead-status-msg']) {
this.dom['lead-status-msg'].style.display = 'block';
this.dom['lead-status-msg'].className = 'lead-status-ready';
this.dom['lead-status-msg'].textContent = 'Registration record submitted. You may now proceed to payment.';
}
this.goTo(8);
/* Re-apply pending UI now that we are on page 8 */
this.handleConditionalUI();
this.showToast('Registration submitted successfully.');
}
} catch (err) {
console.error('[AGPAL] Lead creation failed', err);
if (this.dom['lead-status-msg']) {
this.dom['lead-status-msg'].style.display = 'block';
this.dom['lead-status-msg'].className = 'lead-status-error';
this.dom['lead-status-msg'].textContent = 'Registration could not be submitted. Please try again.';
}
this.showToast(err.message || 'Registration creation failed.', true);
} finally {
this.setBusy(false);
this.updateSubmitEnabled();
}
},
/* Payment evidence */
createPaymentEvidence: async function (stripeResult) {
stripeResult = stripeResult || {};
var isCC = this.val('payment-method') === 'cc';
var fees = this.calcFees(isCC);
var now = new Date().toISOString();
var payload = {
ongc_payername: stripeResult.fullName || (this.val('contact-first') + ' ' + this.val('contact-last')).trim(),
ongc_paymentamount: fees.base,
ongc_surchargefee: fees.surcharge,
ongc_gst: fees.gst,
ongc_totalamount: fees.total,
ongc_paymentreference: stripeResult.paymentIntentId || stripeResult.chargeId || '',
ongc_paymentstatus: stripeResult.status || (isCC ? 'succeeded' : 'pending-eft'),
ongc_paymentdate: stripeResult.paymentDate || now,
ongc_responsemessage: JSON.stringify(stripeResult.rawResponse || stripeResult || {}, null, 2)
};
if (this.state.leadId) {
payload['ongc_Lead@odata.bind'] = '/leads(' + this.state.leadId + ')';
}
Object.keys(payload).forEach(function (k) {
if (payload[k] === '' || payload[k] === null || payload[k] === undefined) delete payload[k];
});
return this.dataversePost('ongc_paymentevidences', payload);
},
/* Submit */
handleSubmit: async function () {
if (!this.areDeclarationsChecked()) { this.showError('err-submit'); this.focusFirstError(); return; }
this.hideError('err-submit');
this.hideError('err-pending-ack');
/* Pending path: create lead record NOW (on page 8 "Confirm and Submit").
createLeadAndChildren detects isPending and routes to goToSuccess(true). */
if (this.isPendingRegistration()) {
await this.createLeadAndChildren();
return;
}
/* Normal path: lead already created on page 7, proceed to Stripe payment */
this.setBusy(true, 'Processing\u2026');
this.saveState();
try {
if (!this.state.leadId) {
throw new Error('Registration record not found. Please go back and recreate the registration.');
}
await this.initiateStripePayment();
} catch (err) {
console.error('[AGPAL] Submit failed', err);
this.showToast(err.message || 'Submission failed.', true);
this.setBusy(false);
this.updateSubmitEnabled();
}
},
initiateStripePayment: function () {
var fees = this.calcFees(true);
var params = new URLSearchParams({
leadId: this.state.leadId || '',
leadRef: this.state.leadRef || '',
amount: fees.total.toFixed(2),
base: fees.base.toFixed(2),
surcharge: fees.surcharge.toFixed(2),
gst: fees.gst.toFixed(2),
email: this.val('contact-email') || this.val('practice-email'),
name: (this.val('contact-first') + ' ' + this.val('contact-last')).trim(),
ref: 'AGPAL Registration \u2013 ' + this.val('practice-name'),
returnUrl: window.location.href.split('?')[0]
});
window.location.href = '/agpal-stripe-checkout?' + params.toString();
},
/* Navigation */
goTo: function (step) {
document.querySelectorAll('.step-page').forEach(function (p) { p.classList.remove('active'); });
var page = this.byId('page-' + step);
if (page) page.classList.add('active');
document.querySelectorAll('.step-dot').forEach(function (d, i) {
d.classList.toggle('active', i + 1 === step);
d.classList.toggle('completed', i + 1 < step);
});
var titles = [
'Step 1: Accreditation Status', 'Step 2: Practice Information',
'Step 3: Practice Contacts', 'Step 4: Practice Address',
'Step 5: Further Practice Details', 'Step 6: Practitioner Details',
'Step 7: Self Assessment Fee', 'Step 8: Authorisation & Submission'
];
if (step <= this.state.totalSteps) {
if (this.dom.stepTitle) this.dom.stepTitle.textContent = titles[step - 1];
if (this.dom.stepCount) this.dom.stepCount.textContent = 'Page ' + step + ' of ' + this.state.totalSteps;
if (this.dom.progressFill) this.dom.progressFill.style.width = ((step / this.state.totalSteps) * 100) + '%';
}
this.state.currentStep = step;
window.scrollTo(0, 0);
},
goToSuccess: function (isPending, d) {
document.querySelectorAll('.step-page').forEach(function (p) { p.classList.remove('active'); });
var s = this.byId('page-success');
if (s) s.classList.add('active');
/* Show the appropriate success message depending on registration type */
var msgNormal = this.byId('success-msg-normal');
var msgPending = this.byId('success-msg-pending');
if (msgNormal) msgNormal.style.display = isPending ? 'none' : 'block';
if (msgPending) msgPending.style.display = isPending ? 'block' : 'none';
/* Reference number */
if (this.dom['ref-number']) {
this.dom['ref-number'].textContent = App.state.leadRef || App.state.leadId || '\u2014';
}
/* Pending path: render lead status + card inside the success page */
if (isPending && d) {
var statusEl = this.byId('pending-lead-status-msg');
if (statusEl) {
statusEl.textContent = 'Your details have been recorded. A member of the AGPAL Client Liaison team will contact you closer to your opening date to progress your registration.';
statusEl.style.display = 'block';
}
this.renderLeadCardPending(d);
}
this.setBusy(false);
window.scrollTo(0, 0);
},
/* Render lead card on the success page */
renderLeadCardPending: function (d) {
var container = this.byId('pending-lead-card-content');
if (!container) return;
function row(label, value) {
return '
' + label + '' + (value || '\u2014') + '
';
}
container.innerHTML =
row('REFERENCE', App.state.leadRef || App.state.leadId) +
row('PRACTICE', d.practiceName) +
row('ABN', d.practiceAbn) +
row('CONTACT', (d.contactFirst + ' ' + d.contactLast).trim()) +
row('EMAIL', d.contactEmail) +
row('PHONE', d.contactPhone);
var card = this.byId('pending-lead-card');
if (card) card.style.display = 'block';
},
/* Conditional UI */
handleConditionalUI: function () {
var accred = this.val('accred-status');
var yesAgpal = this.byId('group-yes-agpal');
var yesOther = this.byId('group-yes-other');
var noGroup = this.byId('group-no');
var btnNext1 = this.byId('btn-next-1');
var btnSubmitAg = this.byId('btn-submit-agpal');
if (yesAgpal) yesAgpal.style.display = accred === 'yes-agpal' ? 'block' : 'none';
if (yesOther) yesOther.style.display = accred === 'yes-other' ? 'block' : 'none';
if (noGroup) noGroup.style.display = accred === 'no' ? 'block' : 'none';
if (btnNext1) btnNext1.style.display = accred === 'yes-agpal' ? 'none' : 'inline-block';
if (btnSubmitAg) btnSubmitAg.style.display = accred === 'yes-agpal' ? 'inline-block' : 'none';
var ga = this.val('group-accred');
var corpWrap = this.byId('group-corporate-entity');
if (corpWrap) corpWrap.style.display = (ga === 'yes-corporate' || ga === 'yes-group') ? 'block' : 'none';
var postalYes = document.querySelector('input[name="postal"][value="yes"]:checked');
var postalWrap = this.byId('postal-address-group');
if (postalWrap) postalWrap.style.display = postalYes ? 'block' : 'none';
var v = this.val('practitioners');
if (this.dom['practitioner-dynamic-section']) this.dom['practitioner-dynamic-section'].style.display = 'none';
if (this.dom['practitioner-over10-section']) this.dom['practitioner-over10-section'].style.display = 'none';
if (v === 'over-10') {
if (this.dom['practitioner-over10-section']) this.dom['practitioner-over10-section'].style.display = 'block';
this.state.practitionerRowCount = 0;
if (this.dom['practitioner-rows-container']) this.dom['practitioner-rows-container'].innerHTML = '';
} else if (v) {
var count = parseInt(v, 10);
if (!isNaN(count) && count >= 1 && count <= 10) {
this.buildPractitionerRows(count);
if (this.dom['practitioner-dynamic-section']) this.dom['practitioner-dynamic-section'].style.display = 'block';
}
}
/* Pending Registration UI — driven by isPendingRegistration() */
var isPending = this.isPendingRegistration();
/* Page 1 — pending notice */
var pendingNoticeP1 = this.byId('pending-notice-p1');
if (pendingNoticeP1) pendingNoticeP1.style.display = isPending ? 'block' : 'none';
/* Page 7 — fee info box (two versions) */
var feeInfoNormal = this.byId('fee-info-normal');
var feeInfoPending = this.byId('fee-info-pending');
if (feeInfoNormal) feeInfoNormal.style.display = isPending ? 'none' : 'block';
if (feeInfoPending) feeInfoPending.style.display = isPending ? 'block' : 'none';
/* Page 7 — payment method dropdown (hidden for pending) */
var paymentMethodGroup = this.byId('payment-method-group');
if (paymentMethodGroup) paymentMethodGroup.style.display = isPending ? 'none' : 'block';
/* Page 7 — "What happens next?" box (two versions) */
var nextStepsNormal = this.byId('next-steps-normal');
var nextStepsPending = this.byId('next-steps-pending');
if (nextStepsNormal) nextStepsNormal.style.display = isPending ? 'none' : 'block';
if (nextStepsPending) nextStepsPending.style.display = isPending ? 'block' : 'none';
/* Page 7 — Next button label */
var btnNext7 = this.byId('btn-next-7');
if (btnNext7) btnNext7.textContent = isPending ? 'Confirm and Next' : 'Next';
/* Page 8 — "Save and return" notice (only relevant if payment is happening) */
var saveReturnNotice = this.byId('save-return-notice');
if (saveReturnNotice) saveReturnNotice.style.display = isPending ? 'none' : 'block';
/* Page 8 — pending notice at top */
var pendingNoticeP8 = this.byId('pending-notice-p8');
if (pendingNoticeP8) pendingNoticeP8.style.display = isPending ? 'block' : 'none';
/* Page 8 — pending acknowledgment checkbox group */
var pendingAckGroup = this.byId('pending-ack-group');
if (pendingAckGroup) pendingAckGroup.style.display = isPending ? 'block' : 'none';
/* Page 8 — fee table (hidden for pending — no payment being made) */
var feeTableWrap = this.byId('fee-table-wrap');
if (feeTableWrap) feeTableWrap.style.display = isPending ? 'none' : 'block';
/* Page 8 — lead-card/status-msg wrap:
Normal path → visible (populated after page 7 Next creates the lead).
Pending path → hidden here; shown on the success page instead after Confirm and Submit. */
var p8LeadWrap = this.byId('p8-lead-wrap');
if (p8LeadWrap) p8LeadWrap.style.display = isPending ? 'none' : 'block';
/* Page 8 — submit button label and enabled state for pending
Pending: enabled by declarations alone (no lead pre-creation required).
Normal: enabled only after lead GUID confirmed (updateSubmitEnabled handles this). */
var submitBtn = this.dom['btn-submit'];
if (submitBtn) {
submitBtn.textContent = isPending ? 'Confirm and Submit' : 'Proceed to Payment';
if (isPending) {
submitBtn.disabled = !this.areDeclarationsChecked();
}
}
},
buildPractitionerRows: function (count) {
if (!this.dom['practitioner-rows-container']) return;
if (this.state.practitionerRowCount === count) return;
this.state.practitionerRowCount = count;
this.dom['practitioner-rows-container'].innerHTML = '';
for (var i = 1; i <= count; i++) {
var row = document.createElement('div');
row.className = 'practitioner-row';
row.style.cssText = 'display:grid;grid-template-columns:0.6fr 1fr 1fr 1fr 0.7fr;gap:12px;margin-bottom:20px;align-items:end;';
row.innerHTML =
'
' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
';
this.dom['practitioner-rows-container'].appendChild(row);
}
},
/* Event binding */
bindEvents: function () {
var self = this;
['accred-status', 'group-accred', 'practitioners', 'payment-method'].forEach(function (id) {
var el = self.byId(id);
if (!el) return;
el.addEventListener('change', function () {
self.handleConditionalUI(); self.updateFeeTable(); self.saveState();
});
});
/* Commencement date drives the pending-registration logic —
re-evaluate conditional UI whenever it changes */
var commenceDateEl = self.byId('commencement-date');
if (commenceDateEl) {
commenceDateEl.addEventListener('change', function () {
self.handleConditionalUI(); self.updateFeeTable(); self.saveState();
});
}
document.querySelectorAll('input[name="postal"]').forEach(function (el) {
el.addEventListener('change', function () { self.handleConditionalUI(); self.saveState(); });
});
[1, 2, 3, 4, 5, 6].forEach(function (step) {
var btn = self.byId('btn-next-' + step);
if (!btn) return;
btn.addEventListener('click', function () {
if (self.validateStep(step)) { self.saveState(); self.goTo(step + 1); }
});
});
var btnNext7 = this.byId('btn-next-7');
if (btnNext7) {
btnNext7.addEventListener('click', function () {
if (!self.validateStep(7)) return;
if (self.isPendingRegistration()) {
/* Pending path: page 7 just navigates to page 8.
Lead is created later on page 8 "Confirm and Submit". */
self.saveState();
self.goTo(8);
self.handleConditionalUI();
} else {
/* Normal path: create lead + children, then navigate to page 8 */
self.createLeadAndChildren();
}
});
}
[2, 3, 4, 5, 6, 7, 8].forEach(function (step) {
var btn = self.byId('btn-prev-' + step);
if (!btn) return;
btn.addEventListener('click', function () { if (!self.state.isBusy) self.goTo(step - 1); });
});
var submit = this.byId('btn-submit');
if (submit) submit.addEventListener('click', function () { self.handleSubmit(); });
/* Close button on success page — closes tab or falls back to portal home */
var btnClose = this.byId('btn-close-success');
if (btnClose) {
btnClose.addEventListener('click', function () {
try {
window.close();
/* window.close() silently fails if page wasn't opened by script;
fall back to home after a short delay */
setTimeout(function () {
window.location.href = window.location.origin + '/';
}, 300);
} catch (e) {
window.location.href = window.location.origin + '/';
}
});
}
['cb-auth', 'cb-terms', 'cb-prof', 'cb-pending-ack'].forEach(function (id) {
var cb = self.byId(id);
if (!cb) return;
cb.addEventListener('change', function () { self.updateSubmitEnabled(); self.saveState(); });
});
var btnSubmitAgpal = this.byId('btn-submit-agpal');
if (btnSubmitAgpal) {
btnSubmitAgpal.addEventListener('click', function () {
self.showToast('Thank you. Please call 1300 362 111 to re-accredit.');
});
}
document.querySelectorAll('input, select, textarea').forEach(function (el) {
el.addEventListener('change', function () { self.saveState(); });
});
},
/* ── Web response HTML ──────────────────────────────────── */
buildAgpalWebResponseHtml: function (d) {
var now = new Date().toLocaleString('en-AU', {
timeZone: 'Australia/Sydney', dateStyle: 'medium', timeStyle: 'short'
});
function row(label, value) {
return '
'
+ '
' + (label || '') + '
'
+ '
' + (value || '\u2014') + '
'
+ '
';
}
function section(title) {
return '
' + title + '
';
}
var html = '
'
+ '
'
+ '
AGPAL Registration — Accreditation Scope
'
+ '
Submitted: ' + now + '
';
if (d.isPending) {
html += '
'
+ '⚠ PENDING REGISTRATION — Practice not yet operational. DO NOT progress into Site Admin. '
+ 'Notify adminbd@agpal.com.au (BCC marketingteam@agpal.com.au). Hold until practice opens.'
+ '
';
}
html += section('1. Accreditation Status');
var aL = { 'yes-agpal': 'Yes \u2014 currently accredited with AGPAL', 'yes-other': 'Yes \u2014 accredited with another organisation', 'no': 'No \u2014 not currently accredited' };
html += row('Status', aL[d.accredStatus] || d.accredStatus);
if (d.otherOrgName) html += row('Other Organisation', d.otherOrgName);
if (d.otherExpiry) html += row('Expiry Date', d.otherExpiry);
if (d.commenceDate) html += row('Desired Commencement Date', (function (iso) {
var p = iso.split('-'); return p.length === 3 ? p[2] + '-' + p[1] + '-' + p[0] : iso;
})(d.commenceDate));
if (d.standards) html += row('Standards', d.standards);
html += section('2. Practice Information');
html += row('Practice Name', d.practiceName); html += row('ABN', d.practiceAbn);
html += row('Phone', d.practicePhone); html += row('Fax', d.practiceFax);
html += row('Email', d.practiceEmail);
html += section('3. Practice Contacts');
html += row('Preferred Contact', [d.contactPrefix, d.contactFirst, d.contactLast].filter(Boolean).join(' '));
html += row('Position', d.contactPosition); html += row('Contact Phone', d.contactPhone);
html += row('Contact Email', d.contactEmail); html += row('Preferred Method', d.contactMethod);
html += row('Principal GP', [d.gpPrefix, d.gpFirst, d.gpLast].filter(Boolean).join(' '));
html += section('4. Practice Address');
html += row('Street Address', [d.addr1, d.addr2, d.city, d.state, d.postcode, 'Australia'].filter(Boolean).join(', '));
if (!d.postalSameAsStreet) {
html += row('Postal Address', [d.postalAddr1, d.postalAddr2, d.postalCity, d.postalState, d.postalPostcode, 'Australia'].filter(Boolean).join(', '));
} else {
html += row('Postal Address', 'Same as street address');
}
html += section('5. Further Practice Details');
html += row('PHN', d.phn); html += row('PIP ID', d.pipId);
var ptL = { gp: 'General Practice', ahs: 'After Hours Service', mds: 'Medical Deputising Service', aboriginal: 'Aboriginal Health Service' };
html += row('Practice Type', ptL[d.practiceType] || d.practiceType);
var gaL = { 'yes-corporate': 'Yes \u2014 Corporate Entity', 'yes-group': 'Yes \u2014 Group', 'no': 'No' };
html += row('Group Practices Accredited', gaL[d.groupAccred] || d.groupAccred);
if (d.corporateEntity) html += row('Corporate / Group Name', d.corporateEntity);
html += section('6. Practitioners');
html += row('Count', d.practitionerCount === 'over-10' ? 'Over 10' : (d.practitionerCount || '\u2014'));
if (d.practitioners && d.practitioners.length) {
d.practitioners.forEach(function (p, i) {
var displayName = [p.title, p.firstName, p.lastName].filter(Boolean).join(' ') || p.name || '\u2014';
html += '