UNPKG

@ministryofjustice/hmpps-digital-prison-reporting-frontend

Version:

The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.

1,087 lines (1,062 loc) 662 kB
function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var dayjs_min$1 = {exports: {}}; var dayjs_min = dayjs_min$1.exports; var hasRequiredDayjs_min; function requireDayjs_min () { if (hasRequiredDayjs_min) return dayjs_min$1.exports; hasRequiredDayjs_min = 1; (function (module, exports) { !function(t,e){module.exports=e();}(dayjs_min,(function(){var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",c="month",f="quarter",h="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return "["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return !r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return (e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()<n.date())return -t(n,e);var r=12*(n.year()-e.year())+(n.month()-e.month()),i=e.clone().add(r,c),s=n-i<0,u=e.clone().add(r+(s?-1:1),c);return +(-(r+(n-i)/(s?i-u:u-i))||0)},a:function(t){return t<0?Math.ceil(t)||0:Math.floor(t)},p:function(t){return {M:c,y:h,w:o,d:a,D:d,h:u,m:s,s:i,ms:r,Q:f}[t]||String(t||"").toLowerCase().replace(/s$/,"")},u:function(t){return void 0===t}},g="en",D={};D[g]=M;var p="$isDayjsObject",S=function(t){return t instanceof _||!(!t||!t[p])},w=function t(e,n,r){var i;if(!e)return g;if("string"==typeof e){var s=e.toLowerCase();D[s]&&(i=s),n&&(D[s]=n,i=s);var u=e.split("-");if(!i&&u.length>1)return t(u[0])}else {var a=e.name;D[a]=e,i=a;}return !r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,true),this.parse(t),this.$x=this.$x||t.x||{},this[p]=true;}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init();},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds();},m.$utils=function(){return b},m.isValid=function(){return !(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t)<this.startOf(e)},m.isBefore=function(t,e){return this.endOf(e)<O(t)},m.$g=function(t,e,n){return b.u(t)?this[e]:this.set(n,t)},m.unix=function(){return Math.floor(this.valueOf()/1e3)},m.valueOf=function(){return this.$d.getTime()},m.startOf=function(t,e){var n=this,r=!!b.u(e)||e,f=b.p(t),l=function(t,e){var i=b.w(n.$u?Date.UTC(n.$y,e,t):new Date(n.$y,e,t),n);return r?i:i.endOf(a)},$=function(t,e){return b.w(n.toDate()[t].apply(n.toDate("s"),(r?[0,0,0,0]:[23,59,59,999]).slice(e)),n)},y=this.$W,M=this.$M,m=this.$D,v="set"+(this.$u?"UTC":"");switch(f){case h:return r?l(1,0):l(31,11);case c:return r?l(1,M):l(0,M+1);case o:var g=this.$locale().weekStart||0,D=(y<g?y+7:y)-g;return l(r?m-D:m+(6-D),M);case a:case d:return $(v+"Hours",0);case u:return $(v+"Minutes",1);case s:return $(v+"Seconds",2);case i:return $(v+"Milliseconds",3);default:return this.clone()}},m.endOf=function(t){return this.startOf(t,false)},m.$set=function(t,e){var n,o=b.p(t),f="set"+(this.$u?"UTC":""),l=(n={},n[a]=f+"Date",n[d]=f+"Date",n[c]=f+"Month",n[h]=f+"FullYear",n[u]=f+"Hours",n[s]=f+"Minutes",n[i]=f+"Seconds",n[r]=f+"Milliseconds",n)[o],$=o===a?this.$D+(e-this.$W):e;if(o===c||o===h){var y=this.clone().set(d,1);y.$d[l]($),y.init(),this.$d=y.set(d,Math.min(this.$D,y.daysInMonth())).$d;}else l&&this.$d[l]($);return this.init(),this},m.set=function(t,e){return this.clone().$set(t,e)},m.get=function(t){return this[b.p(t)]()},m.add=function(r,f){var d,l=this;r=Number(r);var $=b.p(f),y=function(t){var e=O(l);return b.w(e.date(e.date()+Math.round(t*r)),l)};if($===c)return this.set(c,this.$M+r);if($===h)return this.set(h,this.$y+r);if($===a)return y(1);if($===o)return y(7);var M=(d={},d[s]=e,d[u]=n,d[i]=t,d)[$]||1,m=this.$d.getTime()+r*M;return b.w(m,this)},m.subtract=function(t,e){return this.add(-1*t,e)},m.format=function(t){var e=this,n=this.$locale();if(!this.isValid())return n.invalidDate||l;var r=t||"YYYY-MM-DDTHH:mm:ssZ",i=b.z(this),s=this.$H,u=this.$m,a=this.$M,o=n.weekdays,c=n.months,f=n.meridiem,h=function(t,n,i,s){return t&&(t[n]||t(e,r))||i[n].slice(0,s)},d=function(t){return b.s(s%12||12,t,"0")},$=f||function(t,e,n){var r=t<12?"AM":"PM";return n?r.toLowerCase():r};return r.replace(y,(function(t,r){return r||function(t){switch(t){case "YY":return String(e.$y).slice(-2);case "YYYY":return b.s(e.$y,4,"0");case "M":return a+1;case "MM":return b.s(a+1,2,"0");case "MMM":return h(n.monthsShort,a,c,3);case "MMMM":return h(c,a);case "D":return e.$D;case "DD":return b.s(e.$D,2,"0");case "d":return String(e.$W);case "dd":return h(n.weekdaysMin,e.$W,o,2);case "ddd":return h(n.weekdaysShort,e.$W,o,3);case "dddd":return o[e.$W];case "H":return String(s);case "HH":return b.s(s,2,"0");case "h":return d(1);case "hh":return d(2);case "a":return $(s,u,true);case "A":return $(s,u,false);case "m":return String(u);case "mm":return b.s(u,2,"0");case "s":return String(e.$s);case "ss":return b.s(e.$s,2,"0");case "SSS":return b.s(e.$ms,3,"0");case "Z":return i}return null}(t)||i.replace(":","")}))},m.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},m.diff=function(r,d,l){var $,y=this,M=b.p(d),m=O(r),v=(m.utcOffset()-this.utcOffset())*e,g=this-m,D=function(){return b.m(y,m)};switch(M){case h:$=D()/12;break;case c:$=D();break;case f:$=D()/3;break;case o:$=(g-v)/6048e5;break;case a:$=(g-v)/864e5;break;case u:$=g/n;break;case s:$=g/e;break;case i:$=g/t;break;default:$=g;}return l?$:b.a($)},m.daysInMonth=function(){return this.endOf(c).$D},m.$locale=function(){return D[this.$L]},m.locale=function(t,e){if(!t)return this.$L;var n=this.clone(),r=w(t,e,true);return r&&(n.$L=r),n},m.clone=function(){return b.w(this.$d,this)},m.toDate=function(){return new Date(this.valueOf())},m.toJSON=function(){return this.isValid()?this.toISOString():null},m.toISOString=function(){return this.$d.toISOString()},m.toString=function(){return this.$d.toUTCString()},M}(),Y=_.prototype;return O.prototype=Y,[["$ms",r],["$s",i],["$m",s],["$H",u],["$W",a],["$M",c],["$y",h],["$D",d]].forEach((function(t){Y[t[1]]=function(e){return this.$g(e,t[0],t[1])};})),O.extend=function(t,e){return t.$i||(t(e,_,O),t.$i=true),O},O.locale=w,O.isDayjs=S,O.unix=function(t){return O(1e3*t)},O.en=D[g],O.Ls=D,O.p={},O})); } (dayjs_min$1)); return dayjs_min$1.exports; } var dayjs_minExports = requireDayjs_min(); var dayjs = /*@__PURE__*/getDefaultExportFromCjs(dayjs_minExports); var customParseFormat$1 = {exports: {}}; var customParseFormat = customParseFormat$1.exports; var hasRequiredCustomParseFormat; function requireCustomParseFormat () { if (hasRequiredCustomParseFormat) return customParseFormat$1.exports; hasRequiredCustomParseFormat = 1; (function (module, exports) { !function(e,t){module.exports=t();}(customParseFormat,(function(){var e={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},t=/(\[[^[]*\])|([-_:/.,()\s]+)|(A|a|Q|YYYY|YY?|ww?|MM?M?M?|Do|DD?|hh?|HH?|mm?|ss?|S{1,3}|z|ZZ?)/g,n=/\d/,r=/\d\d/,i=/\d\d?/,o=/\d*[^-_:/,()\s\d]+/,s={},a=function(e){return (e=+e)+(e>68?1900:2e3)};var f=function(e){return function(t){this[e]=+t;}},h=[/[+-]\d\d:?(\d\d)?|Z/,function(e){(this.zone||(this.zone={})).offset=function(e){if(!e)return 0;if("Z"===e)return 0;var t=e.match(/([+-]|\d\d)/g),n=60*t[1]+(+t[2]||0);return 0===n?0:"+"===t[0]?-n:n}(e);}],u=function(e){var t=s[e];return t&&(t.indexOf?t:t.s.concat(t.f))},d=function(e,t){var n,r=s.meridiem;if(r){for(var i=1;i<=24;i+=1)if(e.indexOf(r(i,0,t))>-1){n=i>12;break}}else n=e===(t?"pm":"PM");return n},c={A:[o,function(e){this.afternoon=d(e,false);}],a:[o,function(e){this.afternoon=d(e,true);}],Q:[n,function(e){this.month=3*(e-1)+1;}],S:[n,function(e){this.milliseconds=100*+e;}],SS:[r,function(e){this.milliseconds=10*+e;}],SSS:[/\d{3}/,function(e){this.milliseconds=+e;}],s:[i,f("seconds")],ss:[i,f("seconds")],m:[i,f("minutes")],mm:[i,f("minutes")],H:[i,f("hours")],h:[i,f("hours")],HH:[i,f("hours")],hh:[i,f("hours")],D:[i,f("day")],DD:[r,f("day")],Do:[o,function(e){var t=s.ordinal,n=e.match(/\d+/);if(this.day=n[0],t)for(var r=1;r<=31;r+=1)t(r).replace(/\[|\]/g,"")===e&&(this.day=r);}],w:[i,f("week")],ww:[r,f("week")],M:[i,f("month")],MM:[r,f("month")],MMM:[o,function(e){var t=u("months"),n=(u("monthsShort")||t.map((function(e){return e.slice(0,3)}))).indexOf(e)+1;if(n<1)throw new Error;this.month=n%12||n;}],MMMM:[o,function(e){var t=u("months").indexOf(e)+1;if(t<1)throw new Error;this.month=t%12||t;}],Y:[/[+-]?\d+/,f("year")],YY:[r,function(e){this.year=a(e);}],YYYY:[/\d{4}/,f("year")],Z:h,ZZ:h};function l(n){var r,i;r=n,i=s&&s.formats;for(var o=(n=r.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,(function(t,n,r){var o=r&&r.toUpperCase();return n||i[r]||e[r]||i[o].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,(function(e,t,n){return t||n.slice(1)}))}))).match(t),a=o.length,f=0;f<a;f+=1){var h=o[f],u=c[h],d=u&&u[0],l=u&&u[1];o[f]=l?{regex:d,parser:l}:h.replace(/^\[|\]$/g,"");}return function(e){for(var t={},n=0,r=0;n<a;n+=1){var i=o[n];if("string"==typeof i)r+=i.length;else {var s=i.regex,f=i.parser,h=e.slice(r),u=s.exec(h)[0];f.call(t,u),e=e.replace(u,"");}}return function(e){var t=e.afternoon;if(void 0!==t){var n=e.hours;t?n<12&&(e.hours+=12):12===n&&(e.hours=0),delete e.afternoon;}}(t),t}}return function(e,t,n){n.p.customParseFormat=true,e&&e.parseTwoDigitYear&&(a=e.parseTwoDigitYear);var r=t.prototype,i=r.parse;r.parse=function(e){var t=e.date,r=e.utc,o=e.args;this.$u=r;var a=o[1];if("string"==typeof a){var f=true===o[2],h=true===o[3],u=f||h,d=o[2];h&&(d=o[2]),s=this.$locale(),!f&&d&&(s=n.Ls[d]),this.$d=function(e,t,n,r){try{if(["x","X"].indexOf(t)>-1)return new Date(("X"===t?1e3:1)*e);var i=l(t)(e),o=i.year,s=i.month,a=i.day,f=i.hours,h=i.minutes,u=i.seconds,d=i.milliseconds,c=i.zone,m=i.week,M=new Date,Y=a||(o||s?1:M.getDate()),p=o||M.getFullYear(),v=0;o&&!s||(v=s>0?s-1:M.getMonth());var D,w=f||0,g=h||0,y=u||0,L=d||0;return c?new Date(Date.UTC(p,v,Y,w,g,y,L+60*c.offset*1e3)):n?new Date(Date.UTC(p,v,Y,w,g,y,L)):(D=new Date(p,v,Y,w,g,y,L),m&&(D=r(D).week(m).toDate()),D)}catch(e){return new Date("")}}(t,a,r,n),this.init(),d&&true!==d&&(this.$L=this.locale(d).$L),u&&t!=this.format(a)&&(this.$d=new Date("")),s={};}else if(a instanceof Array)for(var c=a.length,m=1;m<=c;m+=1){o[1]=a[m-1];var M=n.apply(this,o);if(M.isValid()){this.$d=M.$d,this.$L=M.$L,this.init();break}m===c&&(this.$d=new Date(""));}else i.call(this,e);};}})); } (customParseFormat$1)); return customParseFormat$1.exports; } var customParseFormatExports = requireCustomParseFormat(); var customParse = /*@__PURE__*/getDefaultExportFromCjs(customParseFormatExports); class DprClientClass { element; constructor(element) { this.element = element; } static getModuleName() { throw new Error('Module name not set'); } getElement() { return this.element; } initialise() { throw new Error('Initialisation not configured'); } } class CardGroup extends DprClientClass { static getModuleName() { return 'card-group'; } initialise() { const cards = this.getElement().querySelectorAll('[data-click-navigate-to]'); const wrapperClass = 'card-loading'; cards.forEach(card => { card.addEventListener('click', () => { card.classList.add(wrapperClass); cards.forEach(c => { if (!c.classList.contains('card-loading')) { const disabledClass = 'card-disabled'; c.classList.add(disabledClass); } }); const url = card.dataset['clickNavigateTo']; if (url) { window.location.href = url; } }); }); } } var BookmarkAction; (function (BookmarkAction) { BookmarkAction["ADD"] = "add"; BookmarkAction["REMOVE"] = "remove"; })(BookmarkAction || (BookmarkAction = {})); class BookmarkButton extends DprClientClass { csrfToken; reportId; id; linkType; reportType; endpoint; baseUrl; isRunning = false; static getModuleName() { return 'bookmark-button'; } initialise() { const element = this.getElement(); element.style.pointerEvents = ''; element.style.opacity = '1'; this.id = element.getAttribute('data-id'); this.reportId = element.getAttribute('data-report-id'); this.linkType = this.element.getAttribute('data-link-type') || BookmarkAction.ADD; this.setReportType(); this.endpoint = element.getAttribute('data-endpoint') || ''; this.csrfToken = element.getAttribute('data-csrf-token') || ''; this.initEventListener(); } /** * Updates the bookmark button UI so that it shows: * - the correct text * - toggles the bookmark on and off correctly * * @memberof BookmarkButton */ updateUI() { this.linkType = this.linkType === BookmarkAction.ADD ? BookmarkAction.REMOVE : BookmarkAction.ADD; const textContent = this.linkType === BookmarkAction.ADD ? 'Add bookmark' : 'Remove bookmark'; const element = this.getElement(); element.setAttribute('data-link-type', this.linkType); element.textContent = textContent; } /** * Inits the bookmark button click event * * @memberof BookmarkButton */ initEventListener() { this.getElement().addEventListener('click', (e) => this.activateBookmark(e)); this.getElement().addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { this.activateBookmark(e); } }); } async activateBookmark(e) { e.preventDefault(); if (this.isRunning) return; this.isRunning = true; this.getElement().classList.add('bookmark-disabled'); try { await fetch(this.endpoint, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', 'CSRF-Token': this.csrfToken, }, body: JSON.stringify({ type: this.linkType, id: this.id, reportId: this.reportId, reportType: this.reportType, }), }) .then(() => { // The page should not reload if on the report/dashboard page so not to have to reload the data and cause a delay // Should instead update the UI labels to signify the change has been made if (!window.location.href.includes('/report') && !window.location.href.includes('/dashboard')) { window.location.reload(); } else { this.updateUI(); } }) .catch(error => console.error('Error:', error)); } finally { this.isRunning = false; this.getElement().classList.remove('bookmark-disabled'); } } setReportType() { const rawReportTypeValue = this.element.getAttribute('data-report-type') || ''; if (!['dashboard', 'report'].includes(rawReportTypeValue)) { throw new Error(`Report type for bookmark setup was unexpected: ${rawReportTypeValue}`); } this.reportType = rawReportTypeValue; } } class dprTruncate extends DprClientClass { static getModuleName() { return 'dpr-truncate'; } initialise() { this.initShowMore(); } initShowMore() { const container = this.getElement(); const content = container.querySelector('.js-content'); const button = container.querySelector('.js-show-more'); if (!content || !button) { return; } const fullHtml = content.dataset['fullHtml'] ?? ''; const truncatedHtml = content.dataset['truncatedHtml'] ?? ''; button.addEventListener('click', () => { const expanded = container.dataset['expanded'] === 'true'; if (expanded) { content.innerHTML = truncatedHtml; button.textContent = 'Show more'; button.setAttribute('aria-expanded', String(!expanded)); } else { content.innerHTML = fullHtml; button.textContent = 'Show less'; button.setAttribute('aria-expanded', String(!expanded)); } container.dataset['expanded'] = String(!expanded); }); } } // @ts-nocheck /* eslint-disable class-methods-use-this */ /* eslint-disable no-param-reassign */ class DprCatalogueFilters extends DprClientClass { initTable() { this.table = document.getElementById(this.tableId).querySelector('tbody'); this.classes = [ 'dpr-search-option-hide', 'dpr-unauthorised-report-hide', 'dpr-live-report-hide', 'dpr-missing-report-hide', 'dpr-report-type-hide', ]; this.totalShown = document.getElementById('total-shown'); this.totalAmount = document.getElementById('total-amount'); this.totalRows = Array.from(this.table.rows).length; this.shownRows = this.totalRows; } updateTotals() { let count = 0; this.table = document.getElementById(this.tableId).querySelector('tbody'); Array.from(this.table.rows).forEach(row => { const hidden = []; this.classes.forEach(className => { hidden.push(Array.from(row.classList).includes(className)); }); if (hidden.every(h => h === false)) { count += 1; } }); this.shownRows = count; this.renderTotals(); } renderTotals() { if (this.totalShown) { this.totalShown.innerText = this.shownRows; } if (this.totalAmount) { this.totalAmount.innerText = this.totalRows; } } } // @ts-nocheck /* eslint-disable class-methods-use-this */ /* eslint-disable no-param-reassign */ class DprCatalogueSearch extends DprCatalogueFilters { static getModuleName() { return 'dpr-catalogue-search'; } initialise() { this.searchBox = this.getElement().querySelector('.dpr-search-box'); this.hideClass = 'dpr-search-option-hide'; if (this.searchBox) { this.tableId = this.searchBox.dataset.tableId; this.initTable(); this.initSeachBoxEvents(); this.updateTableRows(); this.initSearchInputFromQueryParams(); this.initProductCollectionSelect(); } } initProductCollectionSelect() { /** * @type {HTMLSelectElement | undefined} */ const productCollections = this.getElement().querySelector('#productCollection'); if (productCollections) { productCollections.addEventListener('change', e => { e.preventDefault(); productCollections.closest('form').submit(); }); } } initSeachBoxEvents() { this.searchBox.addEventListener('keyup', e => { this.updateTableRows(e.target.value); // Update Query Params const queryParams = new URLSearchParams(window.location.search); queryParams.set(this.searchBox.id, e.target.value); window.history.replaceState(null, null, `?${queryParams.toString()}`); }); } updateTableRows(value) { this.initSearchRows(value); this.updateSearchListing(value); this.updateTotals(); } initSearchRows(value) { const rows = Array.from(this.table.rows); if (value) { rows.forEach(row => row.classList.add(this.hideClass)); } } updateSearchListing(value) { Array.from(this.table.rows) .filter(row => { return (!value || value.length === 0 || Array.from(row.cells).find(cell => { const searchValue = value.toLowerCase(); return cell.innerText.toLowerCase().includes(searchValue.toLowerCase()); })); }) .forEach(row => { row.classList.remove(this.hideClass); }); } initSearchInputFromQueryParams() { const urlParams = new URLSearchParams(window.location.search); urlParams.forEach((value, key) => { const element = document.getElementById(key); if (element && element.classList.contains('dpr-search-box')) { element.value = value; this.updateTableRows(element.value); } }); } } // @ts-nocheck /* eslint-disable class-methods-use-this */ /* eslint-disable no-param-reassign */ class DprCatalogueUnauthorisedFilter extends DprCatalogueFilters { static getModuleName() { return 'dpr-catalogue-show-unauthorised'; } initialise() { this.toggleFilters = this.getElement().querySelector('#dpr-toggle-filters'); this.hideClasses = ['dpr-unauthorised-report-hide', 'dpr-live-report-hide', 'dpr-missing-report-hide']; if (this.toggleFilters) { this.tableId = this.toggleFilters.dataset.tableId; this.initTable(); this.initCheckboxToggleEvents(); this.initCheckboxesFromQueryParams(); this.updateTableRows(); } } initCheckboxToggleEvents() { this.toggleFilters.addEventListener('change', e => { const queryParams = new URLSearchParams(window.location.search); if (e.target.checked) { queryParams.set(e.target.id, e.target.value); } else { queryParams.delete(e.target.id, e.target.value); } window.history.replaceState(null, null, `?${queryParams.toString()}`); this.updateTableRows(); }); } updateTableRows() { const query = new URLSearchParams(window.location.search); this.updateUnauthorisedRows(query); this.updateMissingRows(query); this.updateLiveRows(query); this.updateTotals(); } updateUnauthorisedRows(queryParams) { const value = queryParams.get('show-unauthorised'); const hideClassName = this.hideClasses[0]; const tag = 'dpr-unauthorised-report'; this.updateRows(value, hideClassName, tag); } updateLiveRows(queryParams) { const value = queryParams.get('hide-live'); const hideClassName = this.hideClasses[1]; const tag = 'dpr-live-report'; this.updateRowsLive(value, hideClassName, tag); } updateMissingRows(queryParams) { const value = queryParams.get('show-missing'); const hideClassName = this.hideClasses[2]; const tag = 'dpr-missing-report'; this.updateRows(value, hideClassName, tag); } updateRowsLive(value, hideClassName, tag) { Array.from(this.table.rows) .filter(row => { return Array.from(row.cells).find(cell => { return cell.innerHTML.includes(tag); }); }) .forEach(row => { if (value) { row.classList.add(hideClassName); } else { row.classList.remove(hideClassName); } }); } updateRows(value, hideClassName, tag) { Array.from(this.table.rows) .filter(row => { return Array.from(row.cells).find(cell => { return cell.innerHTML.includes(tag); }); }) .forEach(row => { if (value) { row.classList.remove(hideClassName); } else if (!row.classList.contains(hideClassName)) { row.classList.add(hideClassName); } }); } initCheckboxesFromQueryParams() { const urlParams = new URLSearchParams(window.location.search); urlParams.forEach((value, key) => { const element = document.getElementById(key); if (element && element.id === 'show-unauthorised') { element.setAttribute('checked', ''); } if (element && element.id === 'show-missing') { element.setAttribute('checked', ''); } if (element && element.id === 'hide-live') { element.setAttribute('checked', ''); } }); } } // @ts-nocheck /* eslint-disable class-methods-use-this */ /* eslint-disable no-param-reassign */ class DprCatalogueTypeFilter extends DprCatalogueFilters { static getModuleName() { return 'dpr-catalogue-type-filter'; } initialise() { this.reportTypeFilter = this.getElement().querySelector('#dpr-report-type-filters'); if (this.reportTypeFilter) { this.tableId = this.reportTypeFilter.dataset.tableId; this.initTable(); this.initRadioButtonEvent(); this.updateTableRows(); } } initRadioButtonEvent() { this.reportTypeFilter.addEventListener('change', e => { const queryParams = new URLSearchParams(window.location.search); if (e.target.checked) { queryParams.set(e.target.name, e.target.value); } else { queryParams.delete(e.target.name, e.target.value); } window.history.replaceState(null, null, `?${queryParams.toString()}`); this.updateTableRows(); }); } updateTableRows() { const query = new URLSearchParams(window.location.search); this.updateReportTypeRowsRows(query); this.updateTotals(); } updateReportTypeRowsRows(queryParams) { const value = queryParams.get('report-type'); const hideClassName = `dpr-report-type-hide`; const tag = `dpr-type__${value}`; this.updateRows(value, hideClassName, tag); } updateRows(value, hideClassName, tag) { Array.from(this.table.rows).forEach(row => { row.classList.remove(hideClassName); }); Array.from(this.table.rows) .filter(row => { const isTypeRow = []; Array.from(row.cells).forEach(cell => { return isTypeRow.push(cell.innerHTML.includes(tag)); }); return isTypeRow.every(t => t === false); }) .forEach(row => { if (value && value !== 'all') { row.classList.add(hideClassName); } }); } } let Pagination$1 = class Pagination { pageSizeSelect; static getModuleName() { return 'pagination'; } initialise() { this.pageSizeSelect = document.getElementById('page-size-select'); this.initPageSizeSelectEvent(); } initPageSizeSelectEvent() { this.pageSizeSelect.addEventListener('change', () => { const { name, value } = this.pageSizeSelect; const queryParams = new URLSearchParams(window.location.search); queryParams.set(name, value); queryParams.set('selectedPage', '1'); window.history.replaceState(null, '', `?${queryParams.toString()}`); window.location.reload(); }); } }; class ReportActions extends DprClientClass { refreshButton; printButton; shareButton; copyButton; static getModuleName() { return 'report-actions'; } initialise() { this.refreshButton = document.getElementById('dpr-button-refresh'); this.printButton = document.getElementById('dpr-button-printable'); this.shareButton = document.getElementById('dpr-button-sharable'); this.copyButton = document.getElementById('dpr-button-copy'); this.initRefreshEvent(); this.initPrintButtonEvent(); this.initShareButtonEvent(); this.initCopyButtonEvent(); } initPrintButtonEvent() { if (this.printButton) { const disabled = this.printButton.getAttribute('data-disabled'); this.printButton.addEventListener('click', () => { if (disabled !== 'true') { window.print(); } }); this.printButton.addEventListener('keyup', e => { if (e.key === 'Enter') { if (disabled !== 'true') { window.print(); } } }); } } initShareButtonEvent() { if (this.shareButton) { const href = this.shareButton.getAttribute('data-href'); if (href) { this.shareButton.addEventListener('click', () => { window.location.href = href; }); this.shareButton.addEventListener('keyup', e => { if (e.key === 'Enter') { window.location.href = href; } }); } } } initCopyButtonEvent() { if (this.copyButton) { const href = this.copyButton.getAttribute('data-href'); if (href) { this.copyButton.addEventListener('click', () => { navigator.clipboard.writeText(href); }); this.copyButton.addEventListener('keyup', e => { if (e.key === 'Enter') { navigator.clipboard.writeText(href); } }); } } } initRefreshEvent() { if (this.refreshButton) { const href = this.refreshButton.getAttribute('data-href'); if (href) { this.refreshButton.addEventListener('click', () => { window.location.href = href; }); this.refreshButton.addEventListener('keyup', e => { if (e.key === 'Enter') { window.location.href = href; } }); } } } } // @ts-nocheck class DataTable extends DprClientClass { static getModuleName() { return 'data-table'; } initialise() { this.element = this.getElement(); this.tableContainer = this.element.querySelector('.dpr-table-wrapper'); this.table = this.element.querySelector('.dpr-data-table'); this.gradient = this.element.querySelector('.dpr-overflow-gradient'); this.createTableHeaderAndFooter(); this.initTableScrollListener(); } initTableScrollListener() { this.checkOffsetWidths(); window.addEventListener('resize', () => { this.checkOffsetWidths(); }); this.tableContainer.addEventListener('scroll', event => { const endOfScroll = this.table.offsetWidth; const currentScroll = event.target.offsetWidth + event.target.scrollLeft; if (endOfScroll === currentScroll) { this.removeGradient(); } else { this.addGradient(); } }); } removeGradient(gradient) { this.gradient.style.display = 'none'; } addGradient(gradient) { this.gradient.style.display = 'block'; } checkOffsetWidths() { if (this.tableContainer.offsetWidth >= this.table.offsetWidth) { this.removeGradient(); } else { this.addGradient(); } } createTableHeaderAndFooter() { if (this.table) { const classification = this.table.getAttribute('data-classification'); const headLength = Number(this.table.getAttribute('data-col-length')); const classificationContent = `<b>${classification}</b>`; // Headers const header = this.table.createTHead(); const classificationHeaderRow = header.insertRow(0); classificationHeaderRow.classList.add('print-header-footer'); const classificationHeaderCell = classificationHeaderRow.insertCell(0); classificationHeaderCell.outerHTML = `<th class="govuk-table__header govuk-table__cell--content print-header-footer print-header-footer--header" colspan=${headLength}>${classificationContent}</th>`; // Footers const footer = this.table.createTFoot(); const classificationFooterRow = footer.insertRow(0); classificationFooterRow.classList.add('print-header-footer'); const classificationFooterCell = classificationFooterRow.insertCell(0); classificationFooterCell.outerHTML = `<td class="govuk-table__cell govuk-table__cell--content print-header-footer table-row--no-border print-header-footer--footer" colspan=${headLength}>${classificationContent}</td>`; } } } class DownloadMessage extends DprClientClass { downloadMessage; static getModuleName() { return 'download-message'; } initialise() { this.downloadMessage = this.getElement(); if (window.location.href.includes('download-disabled')) { this.downloadMessage.classList.remove('dpr-download-message--hidden'); } } } class DprFiltersFormClass extends DprClientClass { form; static getModuleName() { return 'filters-form'; } initialise() { if (!(this.element instanceof HTMLFormElement)) { throw new Error('DprFormQuerySync must be initialised on a form element'); } this.form = this.element; this.bindInputEvents(); } // ---------------------------------- // Events // ---------------------------------- bindInputEvents() { this.getAllInputs().forEach(input => { input.addEventListener('change', () => { const params = new URLSearchParams(window.location.search); this.updateQueryFromInput(input, params); this.markPreventDefault(params, input.name); // TODO: Check if this is needed ? this.resetPagination(params); // TODO: Check if this is needed ? this.replaceUrl(params); }); }); } // ---------------------------------- // Query updates // ---------------------------------- updateQueryFromInput(input, params) { const { name } = input; if (!name) return; if (input instanceof HTMLInputElement) { if (input.type === 'checkbox') { this.updateCheckbox(params, input); } else if (input.type === 'radio') { this.updateRadio(params, input); } else { this.updateSingle(params, input); } } else { this.updateSingle(params, input); } } updateCheckbox(params, input) { const values = params.getAll(input.name); // Remove all existing values for this key params.delete(input.name); // Re‑add all values except the unchecked one values .filter(value => value !== input.value || input.checked) .forEach(value => { params.append(input.name, value); }); // Add the newly checked value if (input.checked && !values.includes(input.value)) { params.append(input.name, input.value); } } updateRadio(params, input) { if (!input.checked) return; const name = this.normaliseFilterName(input.name); params.set(name, input.value); } updateSingle(params, input) { const name = this.normaliseFilterName(input.name); let value = input.value; if (input instanceof HTMLInputElement && input.dataset['staticOptionNameValue']) { value = input.dataset['staticOptionNameValue']; } const isDateInput = input instanceof HTMLInputElement && input.classList.contains('moj-js-datepicker-input'); if (isDateInput) { const formatted = dayjs(value, 'D/M/YYYY').format('YYYY-MM-DD'); value = formatted !== 'Invalid Date' ? formatted : ''; } const trimmed = value.trim(); if (trimmed) { params.set(name, trimmed); } else { params.delete(name); } } // ---------------------------------- // Behaviour flags // ---------------------------------- /** * Signals to the server that defaults */ markPreventDefault(params, changedName) { if (changedName !== 'columns') { params.set('preventDefault', 'true'); } } /** * Reset pagination when filters change */ resetPagination(params) { if (params.has('selectedPage')) { params.set('selectedPage', '1'); } } replaceUrl(params) { const query = params.toString(); const url = query ? `?${query}` : window.location.pathname; window.history.replaceState(null, '', url); } // ---------------------------------- // DOM helpers // ---------------------------------- getAllInputs() { return Array.from(this.form.querySelectorAll('input, select, textarea')).filter(el => el.name); } normaliseFilterName(name) { return name.startsWith('label.') ? name.replace(/^label\./, '') : name; } } dayjs.extend(customParse); /** * -------------------------------------------- * Date formats * -------------------------------------------- */ const UI_INPUT_FORMATS = ['D/M/YYYY', 'DD/MM/YYYY']; const API_FORMAT = 'YYYY-MM-DD'; const UI_OUTPUT_FORMAT = 'DD/MM/YYYY'; /** * -------------------------------------------- * Transform helpers * -------------------------------------------- */ /** * DD/MM/YYYY -> YYYY-MM-DD * * @export * @param {string} [value] * @return {*} {(string | undefined)} */ function uiDateToApi(value) { if (!value) return undefined; const parsed = dayjs(value, [...UI_INPUT_FORMATS], true); return parsed.isValid() ? parsed.format(API_FORMAT) : undefined; } /** * -------------------------------------------- * Display helpers * -------------------------------------------- */ function formatDateOrUnset(value) { if (!value) return 'unset'; const parsed = dayjs(value, [...UI_INPUT_FORMATS, API_FORMAT], true); return parsed.isValid() ? parsed.format(UI_OUTPUT_FORMAT) : 'unset'; } class DprSelectedAsyncFilters extends DprClientClass { container; static getModuleName() { return 'dpr-selected-async-filters'; } initialise() { this.container = this.element; this.renderFromInputs(); this.bindInputEvents(); } // ---------------------------------- // Input observation // ---------------------------------- bindInputEvents() { this.getAllFilterControls().forEach(control => { control.addEventListener('change', () => { this.renderFromInputs(); }); }); } // ---------------------------------- // Rendering // ---------------------------------- renderFromInputs() { this.container.innerHTML = ''; const selectedFilters = this.buildSelectedFilters(); if (!selectedFilters.length) { this.renderEmptyState(); return; } selectedFilters.forEach(filter => { this.container.appendChild(this.createFilterElement(filter)); }); } renderEmptyState() { const p = document.createElement('p'); p.className = 'govuk-body-s govuk-!-margin-top-1 govuk-!-margin-bottom-0'; p.textContent = 'No filters selected'; this.container.appendChild(p); } // ---------------------------------- // Build selected filters from inputs // ---------------------------------- buildSelectedFilters() { const grouped = this.groupControlsByField(); return Object.values(grouped) .map(controls => this.buildFilterFromControls(controls)) .filter(Boolean); } groupControlsByField() { return this.getAllFilterControls().reduce((acc, control) => { const parts = control.name.split('.'); // label.filters.fieldX -> filters.fieldX const fieldKey = parts[0] === 'label' ? parts.slice(1, 3).join('.') : parts.slice(0, 2).join('.'); acc[fieldKey] ??= []; acc[fieldKey].push(control); return acc; }, {}); } // ---------------------------------- // Orchestration // ---------------------------------- buildFilterFromControls(controls) { const activeControls = this.getActiveControls(controls); if (!activeControls.length) return null; const displayName = this.getDisplayName(controls); if (this.isGranularDateRange(controls)) { return this.buildGranularDateRangeFilter(displayName, controls); } if (this.isDateRange(controls)) { return this.buildDateRangeFilter(displayName, controls); } if (this.isMultiSelect(activeControls)) { return this.buildMultiSelectFilter(displayName, activeControls, controls); } return this.buildSingleValueFilter(displayName, activeControls[0], controls); } // ---------------------------------- // Guards // ---------------------------------- getActiveControls(controls) { return controls.filter(control => { if (control instanceof HTMLSelectElement) { return Boolean(control.value); } if (control instanceof HTMLInputElement && (control.type === 'checkbox' || control.type === 'radio')) { return control.checked; } return Boolean(control.value); }); } isGranularDateRange(controls) { return controls.some(c => c.name.includes('granularity')); } isDateRange(controls) { return controls.some(c => c.name.endsWith('.start')) || controls.some(c => c.name.endsWith('.end')); } isMultiSelect(active) { return active.length > 1; } // ---------------------------------- // Builders // ---------------------------------- buildDateRangeFilter(displayName, controls) { const start = controls.find(c => c.name.endsWith('.start')); const end = controls.find(c => c.name.endsWith('.end')); const relativeDuration = controls.find(c => { if (!c.name.endsWith('.relative-duration')) return false; const input = c; return input.type === 'radio' && input.checked; }); const rangeDisplay = `${formatDateOrUnset(start?.value)} - ${formatDateOrUnset(end?.value)}`; const displayValue = relativeDuration && relativeDuration.value !== 'none' ? `${rangeDisplay} / ${this.getDisplayValue(relativeDuration)}` : rangeDisplay; return { displayName, displayValue, inputs: controls, }; } buildGranularDateRangeFilter(displayName, controls) { const quick = controls.find(c => c.name.includes('quick-filter') && c.value); const granularity = controls.find(c => c.name.includes('granularity')); const start = controls.find(c => c.name.endsWith('.start') && c.value); const end = controls.find(c => c.name.endsWith('.end') && c.value); let displayValue = ''; if (quick && quick.value !== 'None') { displayValue = `${this.humanise(quick.value)} / ${granularity?.value ?? 'unset'}`; } else { displayValue = `${formatDateOrUnset(start?.value)} - ${formatDateOrUnset(end?.value)}` + ` / ${granularity?.value ?? 'unset'}`; } return { displayName, displayValue, inputs: controls, }; } buildMultiSelectFilter(displayName, active, controls) { const values = active.map(c => this.getDisplayValue(c)); const displayValue = values.length > 3 ? `${values.slice(0, 3).join(', ')} + ${values.length - 3} more` : values.join(', '); return { displayName, displayValue, inputs: controls }; } buildSingleValueFilter(displayName, control, controls) { return { displayName, displayValue: this.getDisplayValue(control), inputs: controls, }; } // ---------------------------------- // Display helpers // ---------------------------------- getDisplayName(controls) { const legend = controls[0]?.closest('fieldset')?.querySelector('legend')?.textContent; if (legend) { return legend.trim(); } const isDateRange = controls.some(c => c.name.endsWith('.start')) && controls.some(c => c.name.endsWith('.end')); if (isDateRange) { const explicit = controls.map(c => c.getAttribute('display-name')).find(Boolean); if (explicit) { return explicit.replace(/\s+(start|end)$/i, '').trim(); } } const explicit = controls.map(c => c.getAttribute('display-name')).find(Boolean); if (explicit) { return explicit; } const label = controls .map(c => (c instanceof HTMLInputElement ? c.labels?.[0]?.innerText : undefined)) .find(Boolean); if (label) { return label; } return controls[0].name; } getDisplayValue(control) { if (control instanceof HTMLSelectElement) { const option = control.options[control.selectedIndex]; return option?.text ?? ''; } // Autocomplete & text inputs should use their value if (control instanceof HTMLInputElement && (control.type === 'search' || control.type === 'text')) { return control.value; } // Static option value for radios & checkboxes if (control.dataset['staticOptionNameValue']) { return control.dataset['staticOptionNameValue']; } if (control instanceof HTMLInputElement && (control.type === 'radio' || control.type === 'checkbox') && control.labels?.[0]?.innerText) { return control.labels[0].innerText; } return control.value; } // ---------------------------------- // Chip removal // ---------------------------------- createFilterElement(filter) { const button = document.createElement('button'); button.type = 'button'; button.className = 'dpr-clear-button-styles govuk-body dpr-selected-filter'; const name = document.createElement('strong'); name.className = 'dpr-selected-filter__name'; name.textContent = filter.displayName; const value = document.createTextNode(`: ${filter.displayValue}`); button.append(name, value); button.addEventListener('click', () => { const params = new URLSearchParams(window.location.search); // Remove ALL values for this filter from the query string filter.inputs.forEach(control => { params.delete(control.name); if (control instanceof HTMLSelectElement) { control.selectedIndex = 0; } else if (control instanceof HTMLInputElement) { if (control.type === 'checkbox' || control.type === 'radio') { control.checked = false; } else { control.value = ''; } } this.removeAutocompleteHiddenInput(control.name, params); control.dispatchEvent(new Event('change', { bubbles: true })); }); const query = params.toString(); const url = query ? `?${query}` : window.location.pathname; window.history.replaceState(null, '', url); }); return button; } removeAutocompleteHiddenInput(name, params) { if (!name.startsWith('label.')) { return; } const realFilterName = name.repla