Access is restricted to authorised Repair & Assure users.
Prototype access control: suitable for developer handoff and testing. Live security still requires server-side authentication and protected APIs.
Initial setup
Create Super Admin
This appears once on first use. Create the first administrator account for the management suite.
Credentials are stored locally in this prototype file. Your developers should replace this storage layer with your production identity system before launch.
Management Reporting
Repair & Assure Management Dashboard
Central access point for operational and commercial reporting.
Management Hub Active
Not signed in
Overall management snapshot
Automatically calculated from the SmartDebit, Manufacturer Repair Partnership and Estate Agent Partnership reports.
Monthly SmartDebit Collections
—
Loading report data…
SmartDebit · 12x12 + 83x3
Collection Success Rate
—
Loading report data…
SmartDebit · 12x12 + 83x3
Manufacturer Repair Revenue
—
Loading report data…
Manufacturer Partnerships
Direct Engineer Usage
—
Loading report data…
Manufacturer Partnerships
Available management reports
Select a dashboard below to open its detailed reporting view.
↻
Repair & Assure SmartDebit Tracker
Monitor recurring service-plan collections, collection performance, run-off trends and the 12x12 / 83x3 reporting views.
Developer handoff mode: this interface is complete, but users/passwords are currently stored in this browser. Production should move authentication, sessions and permission checks to the server.
Users
${state.users.length}
Total user accounts
${active}
Active users
${admins}
Active Super Admins
User
Role
Status
Reports
Last login
${rows||'
No users.
'}
`;
}
function buildPermissionEditor(perms){
return `
`;
}
function buildMigrationPayload(includeAudit=false){
const state=getAuthState();
return {
schema:'repair-assure-management-suite-user-migration',
schemaVersion:1,
exportedAt:nowIso(),
source:'Repair & Assure Management Suite prototype',
passwordMigration:false,
passwordInstruction:'Passwords are intentionally excluded. Create/reset passwords securely on the production system.',
reports:REPORT_META,
roles:Object.keys(ROLE_DEFAULTS),
settings:{
sessionMinutes:state.settings?.sessionMinutes||SESSION_TIMEOUT_MINUTES
},
users:state.users.map(u=>({
legacyId:u.id,
name:u.name,
email:u.email,
role:u.role,
active:!!u.active,
permissions:userPermissions(u),
createdAt:u.createdAt||null,
lastLogin:u.lastLogin||null,
requiresPasswordSetup:true
})),
...(includeAudit?{audit:state.audit||[]}:{})
};
}
function downloadJsonFile(filename,obj){
const blob=new Blob([JSON.stringify(obj,null,2)],{type:'application/json'});
const url=URL.createObjectURL(blob);
const a=document.createElement('a');
a.href=url;a.download=filename;
document.body.appendChild(a);a.click();a.remove();
setTimeout(()=>URL.revokeObjectURL(url),1000);
}
function exportUsersPermissions(){
const payload=buildMigrationPayload(false);
const stamp=new Date().toISOString().slice(0,10);
downloadJsonFile(`repair_assure_users_permissions_${stamp}.json`,payload);
audit('User migration export',`${payload.users.length} users exported without passwords`);
renderMigration();
}
function exportDeveloperMigration(){
const payload=buildMigrationPayload(true);
payload.developerNotes={
recommendedImportOrder:[
'Create roles/report registry',
'Create users by email',
'Apply active/inactive status',
'Apply per-report permissions',
'Require each imported user to set a new password',
'Enable secure server-side sessions and MFA for administrators'
],
requiredServerControls:[
'Store passwords with a production password-hashing algorithm such as Argon2id or bcrypt',
'Use HTTPS',
'Use secure HTTP-only session cookies',
'Enforce permissions on server/API routes, not only in the browser',
'Do not expose report data in unauthorised HTML/JavaScript',
'Log authentication and permission changes server-side'
]
};
const stamp=new Date().toISOString().slice(0,10);
downloadJsonFile(`repair_assure_developer_migration_${stamp}.json`,payload);
audit('Developer migration export',`${payload.users.length} users + permissions + audit exported`);
renderMigration();
}
function triggerUserImport(){
document.getElementById('migrationImportFile').click();
}
async function importUsersPermissions(input){
if(!canAdmin(currentUser)) return;
const file=input.files?.[0];
if(!file) return;
try{
const text=await file.text();
const payload=JSON.parse(text);
if(payload.schema!=='repair-assure-management-suite-user-migration'||!Array.isArray(payload.users)){
throw new Error('This is not a recognised Repair & Assure migration file.');
}
const state=getAuthState();
let added=0,updated=0;
for(const incoming of payload.users){
if(!incoming.email) continue;
const email=String(incoming.email).trim().toLowerCase();
let u=state.users.find(x=>x.email===email);
if(u){
u.name=incoming.name||u.name;
u.role=incoming.role||u.role;
u.active=incoming.active!==false;
u.permissions=incoming.permissions||u.permissions;
updated++;
}else{
u={
id:uid(),
name:incoming.name||email,
email,
role:incoming.role||'Read Only',
active:incoming.active!==false,
permissions:incoming.permissions||clone(ROLE_DEFAULTS[incoming.role]||ROLE_DEFAULTS['Read Only']),
passwordHash:'',
createdAt:nowIso(),
lastLogin:null,
importedRequiresPasswordSetup:true
};
state.users.push(u);
added++;
}
}
if(payload.settings?.sessionMinutes){
state.settings=state.settings||{};
state.settings.sessionMinutes=payload.settings.sessionMinutes;
}
saveAuthState(state);
audit('User migration import',`${added} added · ${updated} updated · passwords not imported`);
alert(`Import complete.\n\n${added} users added\n${updated} users updated\n\nPasswords were not imported. New/imported users require secure password setup.`);
renderMigration();renderAdminUsers();applyAccessUI();
}catch(err){
alert('Import failed: '+err.message);
}finally{
input.value='';
}
}
function renderMigration(){
const state=getAuthState();
const payload=buildMigrationPayload(false);
const preview={
schema:payload.schema,
schemaVersion:payload.schemaVersion,
userCount:payload.users.length,
users:payload.users.map(u=>({
name:u.name,email:u.email,role:u.role,active:u.active,permissions:u.permissions,requiresPasswordSetup:true
}))
};
document.getElementById('adminMigrationTab').innerHTML=`
Migration & Backup
Designed for handoff: create your users and permissions here now, then export them for your developers to import into the production server. Passwords are deliberately excluded from migration.
Export Users & Permissions
Exports names, emails, roles, account status and individual report allowances. Best for transferring the account setup to your developers.
Developer Migration Package
Includes the user/permission structure, report registry, session settings, audit history and implementation notes for the production backend.
Import / Restore User Setup
Restore a previous export or transfer a configured user set into another copy of this management suite. Existing users are matched by email.
Production Password Setup
Passwords are not exported. Imported users are flagged to require password setup. Your live system should send each user a secure setup/reset link.
Migration preview
${state.users.length} user account${state.users.length===1?'':'s'} currently configured. This preview excludes password hashes and audit details.
${escHtml(JSON.stringify(preview,null,2))}
`;
}
function renderSettings(){
const state=getAuthState();
document.getElementById('adminSettingsTab').innerHTML=`
Settings
Session security
Production handoff requirements
Use the Migration & Backup export to seed the production users/permissions. Replace localStorage user records with a server-side users table or identity provider;
replace sessionStorage with secure HTTP-only sessions; enforce report permissions on every API route; serve dashboards/data only after authentication;
use HTTPS; add password-setup/reset email and MFA for administrators.
`;
}
function saveSecuritySettings(){
const state=getAuthState();
state.settings=state.settings||{};
state.settings.sessionMinutes=+document.getElementById('sessionMinutesSetting').value||45;
saveAuthState(state);audit('Security settings updated',`Session timeout ${state.settings.sessionMinutes} minutes`);
renderSettings();
}
function showHome(){
document.body.classList.remove("report-open");
document.getElementById("reportFrame").srcdoc="";
window.scrollTo(0,0);
}
function showReport(key){
if(!REPORTS[key]||!currentUser) return;
const perm=userPermissions(currentUser)[key];
if(!perm?.view){
audit('Access denied',REPORT_META[key]?.name||key);
alert('You do not have permission to view this report.');
return;
}
document.body.classList.add("report-open");
const reportDoc=injectReportPolicy(decodeB64Utf8(REPORTS[key]),key,perm);
document.getElementById("reportFrame").srcdoc=reportDoc;
audit('Report opened',REPORT_META[key]?.name||key);
window.scrollTo(0,0);
}
function openSelectedReport(){
showReport(document.getElementById("reportSelect").value);
}
window.addEventListener("message",e=>{
if(e.data==="ra-main-home"){
showHome();
return;
}
},false);
document.querySelectorAll(".card").forEach(card=>{
card.setAttribute("tabindex","0");
card.setAttribute("role","button");
card.addEventListener("keydown",e=>{
if(e.key==="Enter"||e.key===" "){e.preventDefault();card.click();}
});
});
const updated=document.getElementById("updated");
if(updated){
const d=new Date();
updated.textContent="Hub updated: "+d.toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"});
}
restoreSession();