Merge branch 'tabellenspalten-ausblenden'
This commit is contained in:
commit
c6ca71f898
@ -4,9 +4,6 @@ import { I18n } from './services/i18n/i18n';
|
|||||||
import { UtilRegistry } from './services/util-registry/util-registry';
|
import { UtilRegistry } from './services/util-registry/util-registry';
|
||||||
import { isValidUtility } from './core/utility';
|
import { isValidUtility } from './core/utility';
|
||||||
|
|
||||||
// load window.fetch polyfill
|
|
||||||
import 'whatwg-fetch';
|
|
||||||
|
|
||||||
import './app.scss';
|
import './app.scss';
|
||||||
|
|
||||||
export class App {
|
export class App {
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
/* eslint-env node */
|
||||||
|
|
||||||
import { App } from './app';
|
import { App } from './app';
|
||||||
import { Utility } from './core/utility';
|
import { Utility } from './core/utility';
|
||||||
|
|
||||||
@ -13,52 +15,50 @@ const TEST_UTILS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
describe('App', () => {
|
describe('App', () => {
|
||||||
let app;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
app = new App();
|
global.App = new App();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(app).toBeTruthy();
|
expect(global.App).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should setup all utlites when page is done loading', () => {
|
it('should setup all utlites when page is done loading', () => {
|
||||||
spyOn(app.utilRegistry, 'setupAll');
|
spyOn(global.App.utilRegistry, 'setupAll');
|
||||||
document.dispatchEvent(new Event('DOMContentLoaded'));
|
document.dispatchEvent(new Event('DOMContentLoaded'));
|
||||||
expect(app.utilRegistry.setupAll).toHaveBeenCalled();
|
expect(global.App.utilRegistry.setupAll).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('provides services', () => {
|
describe('provides services', () => {
|
||||||
it('HttpClient as httpClient', () => {
|
it('HttpClient as httpClient', () => {
|
||||||
expect(app.httpClient).toBeTruthy();
|
expect(global.App.httpClient).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('HtmlHelpers as htmlHelpers', () => {
|
it('HtmlHelpers as htmlHelpers', () => {
|
||||||
expect(app.htmlHelpers).toBeTruthy();
|
expect(global.App.htmlHelpers).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('I18n as i18n', () => {
|
it('I18n as i18n', () => {
|
||||||
expect(app.i18n).toBeTruthy();
|
expect(global.App.i18n).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('UtilRegistry as utilRegistry', () => {
|
it('UtilRegistry as utilRegistry', () => {
|
||||||
expect(app.utilRegistry).toBeTruthy();
|
expect(global.App.utilRegistry).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('registerUtilities()', () => {
|
describe('registerUtilities()', () => {
|
||||||
it('should register the given utilities', () => {
|
it('should register the given utilities', () => {
|
||||||
spyOn(app.utilRegistry, 'register');
|
spyOn(global.App.utilRegistry, 'register');
|
||||||
app.registerUtilities(TEST_UTILS);
|
global.App.registerUtilities(TEST_UTILS);
|
||||||
expect(app.utilRegistry.register.calls.count()).toBe(TEST_UTILS.length);
|
expect(global.App.utilRegistry.register.calls.count()).toBe(TEST_UTILS.length);
|
||||||
expect(app.utilRegistry.register.calls.argsFor(0)).toEqual([TEST_UTILS[0]]);
|
expect(global.App.utilRegistry.register.calls.argsFor(0)).toEqual([TEST_UTILS[0]]);
|
||||||
expect(app.utilRegistry.register.calls.argsFor(1)).toEqual([TEST_UTILS[1]]);
|
expect(global.App.utilRegistry.register.calls.argsFor(1)).toEqual([TEST_UTILS[1]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if not passed an array of utilities', () => {
|
it('should throw an error if not passed an array of utilities', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
app.registerUtilities({});
|
global.App.registerUtilities({});
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
223
frontend/src/lib/storage-manager/storage-manager.js
Normal file
223
frontend/src/lib/storage-manager/storage-manager.js
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
/* global global:writable */
|
||||||
|
|
||||||
|
import * as semver from 'semver';
|
||||||
|
|
||||||
|
export const LOCATION = {
|
||||||
|
LOCAL: 'local',
|
||||||
|
WINDOW: 'window',
|
||||||
|
};
|
||||||
|
|
||||||
|
const LOCATION_SHADOWING = [ LOCATION.WINDOW, LOCATION.LOCAL ];
|
||||||
|
|
||||||
|
export class StorageManager {
|
||||||
|
|
||||||
|
namespace;
|
||||||
|
version;
|
||||||
|
_options;
|
||||||
|
_global;
|
||||||
|
|
||||||
|
constructor(namespace, version, options) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
this.version = semver.valid(version);
|
||||||
|
|
||||||
|
if (!namespace) {
|
||||||
|
throw new Error('Cannot setup StorageManager without namespace');
|
||||||
|
}
|
||||||
|
if (!this.version) {
|
||||||
|
throw new Error('Cannot setup StorageManager without valid semver version');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options !== undefined) {
|
||||||
|
this._options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global !== undefined)
|
||||||
|
this._global = global;
|
||||||
|
else if (window !== undefined)
|
||||||
|
this._global = window;
|
||||||
|
else
|
||||||
|
throw new Error('Cannot setup StorageManager without window or global');
|
||||||
|
}
|
||||||
|
|
||||||
|
save(key, value, options=this._options) {
|
||||||
|
if (!key) {
|
||||||
|
throw new Error('StorageManager.save called with invalid key');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options && options.location !== undefined && !Object.values(LOCATION).includes(options.location)) {
|
||||||
|
throw new Error('StorageManager.save called with unsupported location option');
|
||||||
|
}
|
||||||
|
|
||||||
|
const location = options && options.location !== undefined ? options.location : LOCATION_SHADOWING[0];
|
||||||
|
|
||||||
|
switch (location) {
|
||||||
|
case LOCATION.LOCAL: {
|
||||||
|
this._saveToLocalStorage({ ...this._getFromLocalStorage(), [key]: value });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LOCATION.WINDOW: {
|
||||||
|
this._saveToWindow({ ...this._getFromLocalStorage(), [key]: value });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
console.error('StorageManager.save cannot save item with unsupported location');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
load(key, options=this._options) {
|
||||||
|
if (options && options.location !== undefined && !Object.values(LOCATION).includes(options.location)) {
|
||||||
|
throw new Error('StorageManager.load called with unsupported location option');
|
||||||
|
}
|
||||||
|
|
||||||
|
let locations = options && options.location !== undefined ? [options.location] : LOCATION_SHADOWING;
|
||||||
|
|
||||||
|
while (locations.length > 0) {
|
||||||
|
const location = locations.shift();
|
||||||
|
let val;
|
||||||
|
|
||||||
|
switch (location) {
|
||||||
|
case LOCATION.LOCAL: {
|
||||||
|
val = this._getFromLocalStorage()[key];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LOCATION.WINDOW: {
|
||||||
|
val = this._getFromWindow()[key];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
console.error('StorageManager.load cannot load item with unsupported location');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val !== undefined || locations.length === 0) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(key, options=this._options) {
|
||||||
|
if (options && options.location !== undefined && !Object.values(LOCATION).includes(options.location)) {
|
||||||
|
throw new Error('StorageManager.load called with unsupported location option');
|
||||||
|
}
|
||||||
|
|
||||||
|
const locations = options && options.location !== undefined ? [options.location] : LOCATION_SHADOWING;
|
||||||
|
|
||||||
|
for (const location of locations) {
|
||||||
|
switch (location) {
|
||||||
|
case LOCATION.LOCAL: {
|
||||||
|
let val = this._getFromLocalStorage();
|
||||||
|
|
||||||
|
delete val[key];
|
||||||
|
|
||||||
|
return this._saveToLocalStorage(val);
|
||||||
|
}
|
||||||
|
case LOCATION.WINDOW: {
|
||||||
|
let val = this._getFromWindow();
|
||||||
|
|
||||||
|
delete val[key];
|
||||||
|
|
||||||
|
return this._saveToWindow(val);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
console.error('StorageManager.load cannot load item with unsupported location');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(options) {
|
||||||
|
if (options && options.location !== undefined && !Object.values(LOCATION).includes(options.location)) {
|
||||||
|
throw new Error('StorageManager.clear called with unsupported location option');
|
||||||
|
}
|
||||||
|
|
||||||
|
const locations = options && options.location !== undefined ? [options.location] : LOCATION_SHADOWING;
|
||||||
|
|
||||||
|
for (const location of locations) {
|
||||||
|
switch (location) {
|
||||||
|
case LOCATION.LOCAL:
|
||||||
|
return this._clearLocalStorage();
|
||||||
|
case LOCATION.WINDOW:
|
||||||
|
return this._clearWindow();
|
||||||
|
default:
|
||||||
|
console.error('StorageManager.clear cannot clear with unsupported location');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_getFromLocalStorage() {
|
||||||
|
let state;
|
||||||
|
|
||||||
|
try {
|
||||||
|
state = JSON.parse(window.localStorage.getItem(this.namespace));
|
||||||
|
} catch {
|
||||||
|
state = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === null || !state.version || !semver.satisfies(this.version, `^${state.version}`)) {
|
||||||
|
// remove item from localStorage if it stores an invalid state
|
||||||
|
this._clearLocalStorage();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('state' in state)
|
||||||
|
return state.state;
|
||||||
|
else {
|
||||||
|
delete state.version;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveToLocalStorage(state) {
|
||||||
|
if (!state)
|
||||||
|
return this._clearLocalStorage();
|
||||||
|
|
||||||
|
let versionedState;
|
||||||
|
|
||||||
|
if ('version' in state || 'state' in state) {
|
||||||
|
versionedState = { version: this.version, state: state };
|
||||||
|
} else {
|
||||||
|
versionedState = { version: this.version, ...state };
|
||||||
|
}
|
||||||
|
|
||||||
|
window.localStorage.setItem(this.namespace, JSON.stringify(versionedState));
|
||||||
|
}
|
||||||
|
|
||||||
|
_clearLocalStorage() {
|
||||||
|
window.localStorage.removeItem(this.namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_getFromWindow() {
|
||||||
|
if (!this._global || !this._global.App)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (!this._global.App.Storage)
|
||||||
|
this._global.App.Storage = {};
|
||||||
|
|
||||||
|
return this._global.App.Storage[this.namespace] || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveToWindow(value) {
|
||||||
|
if (!this._global || !this._global.App) {
|
||||||
|
throw new Error('StorageManager._saveToWindow called when window.App is not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value)
|
||||||
|
return this._clearWindow();
|
||||||
|
|
||||||
|
if (!this._global.App.Storage)
|
||||||
|
this._global.App.Storage = {};
|
||||||
|
|
||||||
|
this._global.App.Storage[this.namespace] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_clearWindow() {
|
||||||
|
if (!this._global || !this._global.App) {
|
||||||
|
throw new Error('StorageManager._saveToWindow called when window.App is not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._global.App.Storage) {
|
||||||
|
delete this._global.App.Storage[this.namespace];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
frontend/src/polyfill.js
Normal file
4
frontend/src/polyfill.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import 'whatwg-fetch';
|
||||||
|
|
||||||
|
import { ResizeObserver as Polyfill } from '@juggle/resize-observer';
|
||||||
|
window.ResizeObserver = window.ResizeObserver || Polyfill;
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import { Utility } from '../../core/utility';
|
import { Utility } from '../../core/utility';
|
||||||
|
import { StorageManager, LOCATION } from '../../lib/storage-manager/storage-manager';
|
||||||
import { Datepicker } from '../form/datepicker';
|
import { Datepicker } from '../form/datepicker';
|
||||||
import { HttpClient } from '../../services/http-client/http-client';
|
import { HttpClient } from '../../services/http-client/http-client';
|
||||||
import * as debounce from 'lodash.debounce';
|
import * as debounce from 'lodash.debounce';
|
||||||
@ -40,6 +41,8 @@ export class AsyncTable {
|
|||||||
};
|
};
|
||||||
_ignoreRequest = false;
|
_ignoreRequest = false;
|
||||||
|
|
||||||
|
_storageManager = new StorageManager(ASYNC_TABLE_LOCAL_STORAGE_KEY, '1.0.0', { location: LOCATION.WINDOW });
|
||||||
|
|
||||||
constructor(element, app) {
|
constructor(element, app) {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
throw new Error('Async Table utility cannot be setup without an element!');
|
throw new Error('Async Table utility cannot be setup without an element!');
|
||||||
@ -81,10 +84,10 @@ export class AsyncTable {
|
|||||||
this._setupPageSizeSelect();
|
this._setupPageSizeSelect();
|
||||||
this._setupTableFilter();
|
this._setupTableFilter();
|
||||||
|
|
||||||
this._processLocalStorage();
|
this._processStorage();
|
||||||
|
|
||||||
// clear currentTableUrl from previous requests
|
// clear currentTableUrl from previous requests
|
||||||
setLocalStorageParameter('currentTableUrl', null);
|
this._storageManager.remove('currentTableUrl');
|
||||||
|
|
||||||
// mark initialized
|
// mark initialized
|
||||||
this._element.classList.add(ASYNC_TABLE_INITIALIZED_CLASS);
|
this._element.classList.add(ASYNC_TABLE_INITIALIZED_CLASS);
|
||||||
@ -100,7 +103,7 @@ export class AsyncTable {
|
|||||||
|
|
||||||
this._ths.forEach((th) => {
|
this._ths.forEach((th) => {
|
||||||
th.clickHandler = (event) => {
|
th.clickHandler = (event) => {
|
||||||
setLocalStorageParameter('horizPos', (this._scrollTable || {}).scrollLeft);
|
this._storageManager.save('horizPos', (this._scrollTable || {}).scrollLeft);
|
||||||
this._linkClickHandler(event);
|
this._linkClickHandler(event);
|
||||||
};
|
};
|
||||||
th.element.addEventListener('click', th.clickHandler);
|
th.element.addEventListener('click', th.clickHandler);
|
||||||
@ -122,7 +125,7 @@ export class AsyncTable {
|
|||||||
left: this._scrollTable.offsetLeft || 0,
|
left: this._scrollTable.offsetLeft || 0,
|
||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
};
|
};
|
||||||
setLocalStorageParameter('scrollTo', scrollTo);
|
this._storageManager.save('scrollTo', scrollTo);
|
||||||
}
|
}
|
||||||
this._linkClickHandler(event);
|
this._linkClickHandler(event);
|
||||||
};
|
};
|
||||||
@ -225,7 +228,7 @@ export class AsyncTable {
|
|||||||
const prefix = findCssIdPrefix(focusedInput.id);
|
const prefix = findCssIdPrefix(focusedInput.id);
|
||||||
const focusId = focusedInput.id.replace(prefix, '');
|
const focusId = focusedInput.id.replace(prefix, '');
|
||||||
callback = function(wrapper) {
|
callback = function(wrapper) {
|
||||||
const idPrefix = getLocalStorageParameter('cssIdPrefix');
|
const idPrefix = this._storageManager.load('cssIdPrefix');
|
||||||
const toBeFocused = wrapper.querySelector('#' + idPrefix + focusId);
|
const toBeFocused = wrapper.querySelector('#' + idPrefix + focusId);
|
||||||
if (toBeFocused) {
|
if (toBeFocused) {
|
||||||
toBeFocused.focus();
|
toBeFocused.focus();
|
||||||
@ -238,7 +241,7 @@ export class AsyncTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_serializeTableFilterToURL(tableFilterForm) {
|
_serializeTableFilterToURL(tableFilterForm) {
|
||||||
const url = new URL(getLocalStorageParameter('currentTableUrl') || window.location.href);
|
const url = new URL(this._storageManager.load('currentTableUrl') || window.location.href);
|
||||||
|
|
||||||
// create new FormData and format any date values
|
// create new FormData and format any date values
|
||||||
const formData = Datepicker.unformatAll(this._massInputForm, new FormData(tableFilterForm));
|
const formData = Datepicker.unformatAll(this._massInputForm, new FormData(tableFilterForm));
|
||||||
@ -254,18 +257,18 @@ export class AsyncTable {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
_processLocalStorage() {
|
_processStorage() {
|
||||||
const scrollTo = getLocalStorageParameter('scrollTo');
|
const scrollTo = this._storageManager.load('scrollTo');
|
||||||
if (scrollTo && this._scrollTable) {
|
if (scrollTo && this._scrollTable) {
|
||||||
window.scrollTo(scrollTo);
|
window.scrollTo(scrollTo);
|
||||||
}
|
}
|
||||||
setLocalStorageParameter('scrollTo', null);
|
this._storageManager.remove('scrollTo');
|
||||||
|
|
||||||
const horizPos = getLocalStorageParameter('horizPos');
|
const horizPos = this._storageManager.load('horizPos');
|
||||||
if (horizPos && this._scrollTable) {
|
if (horizPos && this._scrollTable) {
|
||||||
this._scrollTable.scrollLeft = horizPos;
|
this._scrollTable.scrollLeft = horizPos;
|
||||||
}
|
}
|
||||||
setLocalStorageParameter('horizPos', null);
|
this._storageManager.remove('horizPos');
|
||||||
}
|
}
|
||||||
|
|
||||||
_removeListeners() {
|
_removeListeners() {
|
||||||
@ -300,7 +303,7 @@ export class AsyncTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_changePagesizeHandler = () => {
|
_changePagesizeHandler = () => {
|
||||||
const url = new URL(getLocalStorageParameter('currentTableUrl') || window.location.href);
|
const url = new URL(this._storageManager.load('currentTableUrl') || window.location.href);
|
||||||
|
|
||||||
// create new FormData and format any date values
|
// create new FormData and format any date values
|
||||||
const formData = Datepicker.unformatAll(this._pagesizeForm, new FormData(this._pagesizeForm));
|
const formData = Datepicker.unformatAll(this._pagesizeForm, new FormData(this._pagesizeForm));
|
||||||
@ -336,7 +339,7 @@ export class AsyncTable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLocalStorageParameter('currentTableUrl', url.href);
|
this._storageManager.save('currentTableUrl', url.href);
|
||||||
// reset table
|
// reset table
|
||||||
this._removeListeners();
|
this._removeListeners();
|
||||||
this._element.classList.remove(ASYNC_TABLE_INITIALIZED_CLASS);
|
this._element.classList.remove(ASYNC_TABLE_INITIALIZED_CLASS);
|
||||||
@ -346,9 +349,9 @@ export class AsyncTable {
|
|||||||
this._app.utilRegistry.setupAll(this._element);
|
this._app.utilRegistry.setupAll(this._element);
|
||||||
|
|
||||||
if (callback && typeof callback === 'function') {
|
if (callback && typeof callback === 'function') {
|
||||||
setLocalStorageParameter('cssIdPrefix', response.idPrefix);
|
this._storageManager.save('cssIdPrefix', response.idPrefix);
|
||||||
callback(this._element);
|
callback(this._element);
|
||||||
setLocalStorageParameter('cssIdPrefix', '');
|
this._storageManager.remove('cssIdPrefix');
|
||||||
}
|
}
|
||||||
}).catch((err) => console.error(err)
|
}).catch((err) => console.error(err)
|
||||||
).finally(() => this._element.classList.remove(ASYNC_TABLE_LOADING_CLASS));
|
).finally(() => this._element.classList.remove(ASYNC_TABLE_LOADING_CLASS));
|
||||||
@ -365,17 +368,3 @@ function findCssIdPrefix(id) {
|
|||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLocalStorageParameter(key, value) {
|
|
||||||
const currentLSState = JSON.parse(window.localStorage.getItem(ASYNC_TABLE_LOCAL_STORAGE_KEY)) || {};
|
|
||||||
if (value !== null) {
|
|
||||||
currentLSState[key] = value;
|
|
||||||
} else {
|
|
||||||
delete currentLSState[key];
|
|
||||||
}
|
|
||||||
window.localStorage.setItem(ASYNC_TABLE_LOCAL_STORAGE_KEY, JSON.stringify(currentLSState));
|
|
||||||
}
|
|
||||||
function getLocalStorageParameter(key) {
|
|
||||||
const currentLSState = JSON.parse(window.localStorage.getItem(ASYNC_TABLE_LOCAL_STORAGE_KEY)) || {};
|
|
||||||
return currentLSState[key];
|
|
||||||
}
|
|
||||||
|
|||||||
312
frontend/src/utils/hide-columns/hide-columns.js
Normal file
312
frontend/src/utils/hide-columns/hide-columns.js
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
import { Utility } from '../../core/utility';
|
||||||
|
import { StorageManager, LOCATION } from '../../lib/storage-manager/storage-manager';
|
||||||
|
import './hide-columns.scss';
|
||||||
|
|
||||||
|
const HIDE_COLUMNS_CONTAINER_IDENT = 'uw-hide-columns';
|
||||||
|
const TABLE_HEADER_IDENT = 'uw-hide-column-header';
|
||||||
|
|
||||||
|
const TABLE_UTILS_ATTR = 'table-utils';
|
||||||
|
const TABLE_UTILS_CONTAINER_SELECTOR = `[${TABLE_UTILS_ATTR}]`;
|
||||||
|
|
||||||
|
const TABLE_HIDER_CLASS = 'table-hider';
|
||||||
|
const TABLE_HIDER_VISIBLE_CLASS = 'table-hider--visible';
|
||||||
|
|
||||||
|
const TABLE_PILL_CLASS = 'table-pill';
|
||||||
|
|
||||||
|
const CELL_HIDDEN_CLASS = 'hide-columns--hidden-cell';
|
||||||
|
const CELL_ORIGINAL_COLSPAN = 'uw-hide-column-original-colspan';
|
||||||
|
|
||||||
|
@Utility({
|
||||||
|
selector: `[${HIDE_COLUMNS_CONTAINER_IDENT}] table`,
|
||||||
|
})
|
||||||
|
export class HideColumns {
|
||||||
|
|
||||||
|
_storageManager = new StorageManager('HIDE_COLUMNS', '1.0.0', { location: LOCATION.LOCAL });
|
||||||
|
|
||||||
|
_element;
|
||||||
|
_elementWrapper;
|
||||||
|
_tableUtilContainer;
|
||||||
|
|
||||||
|
_autoHide;
|
||||||
|
|
||||||
|
headerToHider = new Map();
|
||||||
|
hiderToHeader = new Map();
|
||||||
|
|
||||||
|
addHeaderHider(th, hider) {
|
||||||
|
this.headerToHider.set(th, hider);
|
||||||
|
this.hiderToHeader.set(hider, th);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(element) {
|
||||||
|
this._autoHide = this._storageManager.load('autoHide', {}) || false;
|
||||||
|
|
||||||
|
if (!element) {
|
||||||
|
throw new Error('Hide Columns utility cannot be setup without an element!');
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not provide hide-column ability in tables inside modals or async forms with response
|
||||||
|
if (element.closest('[uw-modal], .async-form__response')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._element = element;
|
||||||
|
|
||||||
|
const hideColumnsContainer = this._element.closest(`[${HIDE_COLUMNS_CONTAINER_IDENT}]`);
|
||||||
|
if (!hideColumnsContainer) {
|
||||||
|
throw new Error('Hide Columns utility needs to be setup on a table inside a hide columns container!');
|
||||||
|
}
|
||||||
|
this._elementWrapper = hideColumnsContainer;
|
||||||
|
|
||||||
|
// get or create table utils container
|
||||||
|
this._tableUtilContainer = hideColumnsContainer.querySelector(TABLE_UTILS_CONTAINER_SELECTOR);
|
||||||
|
if (!this._tableUtilContainer) {
|
||||||
|
this._tableUtilContainer = document.createElement('div');
|
||||||
|
this._tableUtilContainer.setAttribute(TABLE_UTILS_ATTR, '');
|
||||||
|
const tableContainer = this._element.closest(`[${HIDE_COLUMNS_CONTAINER_IDENT}] > *`);
|
||||||
|
hideColumnsContainer.insertBefore(this._tableUtilContainer, tableContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._element.querySelectorAll('th').forEach(th => this.setupHideButton(th));
|
||||||
|
}
|
||||||
|
|
||||||
|
setupHideButton(th) {
|
||||||
|
const preHidden = this.isHiddenColumn(th);
|
||||||
|
|
||||||
|
const hider = document.createElement('span');
|
||||||
|
|
||||||
|
const hiderIcon = document.createElement('i');
|
||||||
|
hiderIcon.classList.add('fas', 'fa-fw');
|
||||||
|
hider.appendChild(hiderIcon);
|
||||||
|
|
||||||
|
const hiderContent = document.createElement('span');
|
||||||
|
hiderContent.classList.add('table-hider__label');
|
||||||
|
hiderContent.innerHTML = th.innerText;
|
||||||
|
hider.appendChild(hiderContent);
|
||||||
|
|
||||||
|
this.addHeaderHider(th, hider);
|
||||||
|
|
||||||
|
th.addEventListener('mouseover', () => {
|
||||||
|
hider.classList.add(TABLE_HIDER_VISIBLE_CLASS);
|
||||||
|
});
|
||||||
|
th.addEventListener('mouseout', () => {
|
||||||
|
if (hider.classList.contains(TABLE_HIDER_CLASS)) {
|
||||||
|
hider.classList.remove(TABLE_HIDER_VISIBLE_CLASS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
hider.addEventListener('click', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
this.switchColumnDisplay(th, hider);
|
||||||
|
// this._tableHiderContainer.getElementsByClassName(TABLE_HIDER_CLASS).forEach(hider => this.hideHiderBehindHeader(hider));
|
||||||
|
});
|
||||||
|
|
||||||
|
hider.addEventListener('mouseover', () => {
|
||||||
|
hider.classList.add(TABLE_HIDER_VISIBLE_CLASS);
|
||||||
|
const currentlyHidden = this.isHiddenColumn(th);
|
||||||
|
this.updateHiderIcon(hider, !currentlyHidden);
|
||||||
|
});
|
||||||
|
hider.addEventListener('mouseout', () => {
|
||||||
|
if (hider.classList.contains(TABLE_HIDER_CLASS)) {
|
||||||
|
hider.classList.remove(TABLE_HIDER_VISIBLE_CLASS);
|
||||||
|
}
|
||||||
|
const currentlyHidden = this.isHiddenColumn(th);
|
||||||
|
this.updateHiderIcon(hider, currentlyHidden);
|
||||||
|
});
|
||||||
|
|
||||||
|
new ResizeObserver(() => { this.repositionHider(hider); }).observe(th);
|
||||||
|
|
||||||
|
// reposition hider on each window resize event
|
||||||
|
// window.addEventListener('resize', () => this.repositionHider(hider));
|
||||||
|
|
||||||
|
this.updateColumnDisplay(this.colIndex(th), preHidden);
|
||||||
|
this.updateHider(hider, preHidden);
|
||||||
|
|
||||||
|
if (preHidden) {
|
||||||
|
this._tableUtilContainer.appendChild(hider);
|
||||||
|
} else {
|
||||||
|
this.hideHiderBehindHeader(hider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switchColumnDisplay(th, hider) {
|
||||||
|
const hidden = !this.isHiddenColumn(th);
|
||||||
|
const originalColspan = Math.max(1, th.getAttribute(CELL_ORIGINAL_COLSPAN)) || 1;
|
||||||
|
const colspan = Math.max(1, th.colSpan) || 1;
|
||||||
|
const columnIndex = this.colIndex(th);
|
||||||
|
|
||||||
|
for (var i = 0; i < Math.max(colspan, originalColspan); i++) {
|
||||||
|
this.updateColumnDisplay(columnIndex + i, hidden);
|
||||||
|
}
|
||||||
|
this.updateHider(hider, hidden);
|
||||||
|
|
||||||
|
// persist new hidden setting for column
|
||||||
|
if ((hidden && this.isEmptyColumn(columnIndex) && this._autoHide) || (!hidden && (!this.isEmptyColumn(columnIndex) || !this._autoHide))) {
|
||||||
|
this._storageManager.remove(this.getStorageKey(th));
|
||||||
|
} else {
|
||||||
|
this._storageManager.save(this.getStorageKey(th), hidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateColumnDisplay(columnIndex, hidden) {
|
||||||
|
this._element.getElementsByTagName('tr').forEach(row => {
|
||||||
|
const cell = this.getCol(row, columnIndex);
|
||||||
|
|
||||||
|
if (cell) {
|
||||||
|
const originalColspan = cell.getAttribute(CELL_ORIGINAL_COLSPAN);
|
||||||
|
const colspan = Math.max(1, cell.colSpan) || 1;
|
||||||
|
|
||||||
|
if (hidden) {
|
||||||
|
if (colspan > 1) {
|
||||||
|
if (!originalColspan) {
|
||||||
|
cell.setAttribute(CELL_ORIGINAL_COLSPAN, colspan);
|
||||||
|
}
|
||||||
|
cell.colSpan--;
|
||||||
|
} else {
|
||||||
|
cell.classList.add(CELL_HIDDEN_CLASS);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cell.classList.contains(CELL_HIDDEN_CLASS)) {
|
||||||
|
cell.classList.remove(CELL_HIDDEN_CLASS);
|
||||||
|
} else if (originalColspan && colspan < originalColspan) {
|
||||||
|
cell.colSpan++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHider(hider, hidden) {
|
||||||
|
if (hidden) {
|
||||||
|
hider.classList.remove(TABLE_HIDER_CLASS);
|
||||||
|
hider.classList.add(TABLE_PILL_CLASS);
|
||||||
|
this._tableUtilContainer.appendChild(hider);
|
||||||
|
} else {
|
||||||
|
hider.classList.remove(TABLE_PILL_CLASS);
|
||||||
|
hider.classList.add(TABLE_HIDER_CLASS);
|
||||||
|
this.hideHiderBehindHeader(hider);
|
||||||
|
}
|
||||||
|
this.updateHiderIcon(hider, hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHiderIcon(hider, hidden) {
|
||||||
|
hider.getElementsByClassName('fas').forEach(hiderIcon => {
|
||||||
|
hiderIcon.classList.remove(hidden ? 'fa-eye' : 'fa-eye-slash');
|
||||||
|
hiderIcon.classList.add(hidden ? 'fa-eye-slash' : 'fa-eye');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hideHiderBehindHeader(hider) {
|
||||||
|
if (!this.hiderToHeader.get(hider).contains(hider)) {
|
||||||
|
this.hiderToHeader.get(hider).appendChild(hider);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.repositionHider(hider);
|
||||||
|
|
||||||
|
// remove visible class if necessary
|
||||||
|
hider.classList.remove(TABLE_HIDER_VISIBLE_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
repositionHider(hider) {
|
||||||
|
const thR = this.hiderToHeader.get(hider).getBoundingClientRect(),
|
||||||
|
hR = hider.getBoundingClientRect();
|
||||||
|
|
||||||
|
hider.style.left = (thR.width/2 - hR.width/2) + 'px';
|
||||||
|
hider.style.top = thR.height + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
getStorageKey(th) {
|
||||||
|
// get handler name
|
||||||
|
const handlerIdent = document.querySelector('[uw-handler]').getAttribute('uw-handler');
|
||||||
|
|
||||||
|
// get hide-columns container ident (if not present, use table index in document as fallback)
|
||||||
|
let tIdent = this._elementWrapper.getAttribute(HIDE_COLUMNS_CONTAINER_IDENT);
|
||||||
|
if (!tIdent) {
|
||||||
|
const tablesInDocument = document.getElementsByTagName('TABLE');
|
||||||
|
for (let i = 0; i < tablesInDocument.length; i++) {
|
||||||
|
if (tablesInDocument[i] === this._element) {
|
||||||
|
tIdent = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for unique table header ident from backend (if not present, use cell index as fallback)
|
||||||
|
let thIdent = th.getAttribute(TABLE_HEADER_IDENT);
|
||||||
|
if (!thIdent) {
|
||||||
|
thIdent = this.colIndex(th);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${handlerIdent}__${tIdent}__${thIdent}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyColumn(columnIndex) {
|
||||||
|
for (let row of this._element.getElementsByTagName('tr')) {
|
||||||
|
const cell = this.getCol(row, columnIndex);
|
||||||
|
if (cell.matches('th'))
|
||||||
|
continue;
|
||||||
|
if (cell.querySelector('.table__td-content')) {
|
||||||
|
for (let child of cell.children) {
|
||||||
|
if (!isEmptyElement(child))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return isEmptyElement(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isHiddenColumn(th) {
|
||||||
|
const hidden = this._storageManager.load(this.getStorageKey(th)),
|
||||||
|
emptyColumn = this.isEmptyColumn(this.colIndex(th));
|
||||||
|
return hidden === true || hidden === undefined && emptyColumn && this._autoHide;
|
||||||
|
}
|
||||||
|
|
||||||
|
colSpan(cell) {
|
||||||
|
if (!cell)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
const originalColspan = cell.getAttribute(CELL_ORIGINAL_COLSPAN);
|
||||||
|
const colspan = Math.max(1, cell.colSpan) || 1;
|
||||||
|
|
||||||
|
return originalColspan ? Math.max(colspan, originalColspan) : colspan;
|
||||||
|
}
|
||||||
|
|
||||||
|
colIndex(cell) {
|
||||||
|
if (!cell)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const rowParent = cell.closest('tr');
|
||||||
|
|
||||||
|
if (!rowParent)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
for (const sibling of Array.from(rowParent.cells).slice(0, cell.cellIndex)) {
|
||||||
|
i += this.colSpan(sibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCol(row, columnIndex) {
|
||||||
|
var c = 0;
|
||||||
|
|
||||||
|
for (const cell of row.cells) {
|
||||||
|
c += cell ? this.colSpan(cell) : 1;
|
||||||
|
|
||||||
|
if (columnIndex < c)
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmptyElement(element) {
|
||||||
|
for (let child of element.childNodes) {
|
||||||
|
if (child.nodeName !== '#comment')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
71
frontend/src/utils/hide-columns/hide-columns.scss
Normal file
71
frontend/src/utils/hide-columns/hide-columns.scss
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
.table-hider {
|
||||||
|
background-color: #fff;
|
||||||
|
color: var(--color-link);
|
||||||
|
padding: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 0 2px 0 rgba(0,0,0,0.6);
|
||||||
|
position: absolute;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform .2s ease;
|
||||||
|
transform: scaleY(0);
|
||||||
|
transform-origin: top;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-grey-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-hider__label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.table-hider--visible {
|
||||||
|
transform: scaleY(.4);
|
||||||
|
transition: none; /* TODO find better way to prevent transition on icons */
|
||||||
|
|
||||||
|
.fas {
|
||||||
|
transform: scaleY(2.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scaleY(1);
|
||||||
|
|
||||||
|
.fas {
|
||||||
|
transform: scaleY(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[table-utils] {
|
||||||
|
max-width: 85vw;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
min-height: 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
|
||||||
|
.table-pill {
|
||||||
|
background-color: var(--color-dark);
|
||||||
|
float: left;
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 20px / 50%;
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.table-hider__label {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-columns--hidden-cell {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import { Utility } from '../../core/utility';
|
import { Utility } from '../../core/utility';
|
||||||
|
import { StorageManager, LOCATION } from '../../lib/storage-manager/storage-manager';
|
||||||
import './show-hide.scss';
|
import './show-hide.scss';
|
||||||
|
|
||||||
const SHOW_HIDE_LOCAL_STORAGE_KEY = 'SHOW_HIDE';
|
const SHOW_HIDE_LOCAL_STORAGE_KEY = 'SHOW_HIDE';
|
||||||
@ -15,6 +16,8 @@ export class ShowHide {
|
|||||||
_showHideId;
|
_showHideId;
|
||||||
_element;
|
_element;
|
||||||
|
|
||||||
|
_storageManager = new StorageManager(SHOW_HIDE_LOCAL_STORAGE_KEY, '1.0.0', { location: LOCATION.LOCAL });
|
||||||
|
|
||||||
constructor(element) {
|
constructor(element) {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
throw new Error('ShowHide utility cannot be setup without an element!');
|
throw new Error('ShowHide utility cannot be setup without an element!');
|
||||||
@ -41,9 +44,9 @@ export class ShowHide {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this._showHideId) {
|
if (this._showHideId) {
|
||||||
let localStorageCollapsed = this._getLocalStorage()[this._showHideId];
|
let storageCollapsed = this._storageManager.load(this._showHideId);
|
||||||
if (typeof localStorageCollapsed !== 'undefined') {
|
if (typeof storageCollapsed !== 'undefined') {
|
||||||
collapsed = localStorageCollapsed;
|
collapsed = storageCollapsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._element.parentElement.classList.toggle(SHOW_HIDE_COLLAPSED_CLASS, collapsed);
|
this._element.parentElement.classList.toggle(SHOW_HIDE_COLLAPSED_CLASS, collapsed);
|
||||||
@ -70,18 +73,7 @@ export class ShowHide {
|
|||||||
const newState = this._element.parentElement.classList.toggle(SHOW_HIDE_COLLAPSED_CLASS);
|
const newState = this._element.parentElement.classList.toggle(SHOW_HIDE_COLLAPSED_CLASS);
|
||||||
|
|
||||||
if (this._showHideId) {
|
if (this._showHideId) {
|
||||||
this._setLocalStorage(this._showHideId, newState);
|
this._storageManager.save(this._showHideId, newState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe move these to a LocalStorageHelper?
|
|
||||||
_setLocalStorage(id, state) {
|
|
||||||
const lsData = this._getLocalStorage();
|
|
||||||
lsData[id] = state;
|
|
||||||
window.localStorage.setItem(SHOW_HIDE_LOCAL_STORAGE_KEY, JSON.stringify(lsData));
|
|
||||||
}
|
|
||||||
|
|
||||||
_getLocalStorage() {
|
|
||||||
return JSON.parse(window.localStorage.getItem(SHOW_HIDE_LOCAL_STORAGE_KEY)) || {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { Modal } from './modal/modal';
|
|||||||
import { Tooltip } from './tooltips/tooltips';
|
import { Tooltip } from './tooltips/tooltips';
|
||||||
import { CourseTeaser } from './course-teaser/course-teaser';
|
import { CourseTeaser } from './course-teaser/course-teaser';
|
||||||
import { NavbarUtils } from './navbar/navbar';
|
import { NavbarUtils } from './navbar/navbar';
|
||||||
|
import { HideColumns } from './hide-columns/hide-columns';
|
||||||
|
|
||||||
export const Utils = [
|
export const Utils = [
|
||||||
Alerts,
|
Alerts,
|
||||||
@ -27,4 +28,5 @@ export const Utils = [
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
CourseTeaser,
|
CourseTeaser,
|
||||||
...NavbarUtils,
|
...NavbarUtils,
|
||||||
|
HideColumns,
|
||||||
];
|
];
|
||||||
|
|||||||
82
package-lock.json
generated
82
package-lock.json
generated
@ -178,6 +178,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||||
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1907,6 +1913,14 @@
|
|||||||
"@babel/helper-plugin-utils": "^7.0.0",
|
"@babel/helper-plugin-utils": "^7.0.0",
|
||||||
"resolve": "^1.8.1",
|
"resolve": "^1.8.1",
|
||||||
"semver": "^5.5.1"
|
"semver": "^5.5.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"semver": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/plugin-transform-shorthand-properties": {
|
"@babel/plugin-transform-shorthand-properties": {
|
||||||
@ -2035,6 +2049,12 @@
|
|||||||
"lodash": "^4.17.13",
|
"lodash": "^4.17.13",
|
||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||||
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2541,6 +2561,11 @@
|
|||||||
"integrity": "sha512-EKKR4p0higjsIPKjSSkGqtweUwo/GgR/zKL4rCwzF5Z/BZ/ebJZaS8ZjGE7YUNEN63SYk2WhpJVI+l9dwfU7RQ==",
|
"integrity": "sha512-EKKR4p0higjsIPKjSSkGqtweUwo/GgR/zKL4rCwzF5Z/BZ/ebJZaS8ZjGE7YUNEN63SYk2WhpJVI+l9dwfU7RQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@juggle/resize-observer": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-Nmkeaj5LalJeciRVEqi9Uxi61r0LvGc2yhUCykhXuft9fMyb/6VkZbwJ+UmUl8bk2k6qhwd1qJw6S2YJ0joXlA=="
|
||||||
|
},
|
||||||
"@marionebl/sander": {
|
"@marionebl/sander": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@marionebl/sander/-/sander-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@marionebl/sander/-/sander-0.6.1.tgz",
|
||||||
@ -6277,6 +6302,14 @@
|
|||||||
"semver": "^5.5.0",
|
"semver": "^5.5.0",
|
||||||
"shebang-command": "^1.2.0",
|
"shebang-command": "^1.2.0",
|
||||||
"which": "^1.2.9"
|
"which": "^1.2.9"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"semver": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"crypto-browserify": {
|
"crypto-browserify": {
|
||||||
@ -6989,6 +7022,12 @@
|
|||||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
|
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"strip-ansi": {
|
"strip-ansi": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||||
@ -8608,6 +8647,12 @@
|
|||||||
"strip-indent": "^2.0.0"
|
"strip-indent": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"strip-bom": {
|
"strip-bom": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
|
||||||
@ -10348,6 +10393,14 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"pify": "^4.0.1",
|
"pify": "^4.0.1",
|
||||||
"semver": "^5.6.0"
|
"semver": "^5.6.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"semver": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mamacro": {
|
"mamacro": {
|
||||||
@ -10941,6 +10994,14 @@
|
|||||||
"resolve": "^1.10.0",
|
"resolve": "^1.10.0",
|
||||||
"semver": "2 || 3 || 4 || 5",
|
"semver": "2 || 3 || 4 || 5",
|
||||||
"validate-npm-package-license": "^3.0.1"
|
"validate-npm-package-license": "^3.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"semver": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"normalize-path": {
|
"normalize-path": {
|
||||||
@ -14352,6 +14413,12 @@
|
|||||||
"os-tmpdir": "^1.0.0"
|
"os-tmpdir": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"outdent": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/outdent/-/outdent-0.7.0.tgz",
|
||||||
|
"integrity": "sha512-Ue462G+UIFoyQmOzapGIKWS3d/9NHeD/018WGEDZIhN2/VaQpVXbofMcZX0socv1fw4/tmEn7Vd3McOdPZfKzQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"p-defer": {
|
"p-defer": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
|
||||||
@ -15468,9 +15535,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "5.7.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"semver-compare": {
|
"semver-compare": {
|
||||||
@ -17555,6 +17622,15 @@
|
|||||||
"tapable": "^1.0.0"
|
"tapable": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"webpack-plugin-hash-output": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webpack-plugin-hash-output/-/webpack-plugin-hash-output-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-Iu4Sox3/bdiqd6TdYwZAExuH+XNbnJStPrwh6yhzOflwc/hZUP9MdiZDbFwTXrmm9ZwoXNUmvn7C0Qj4qRez2A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"outdent": "^0.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"webpack-sources": {
|
"webpack-sources": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz",
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "run-p frontend:build:watch yesod:start",
|
"start": "npm-run-all frontend:build --parallel \"frontend:build:watch\" \"yesod:start\"",
|
||||||
"test": "run-s frontend:test yesod:test",
|
"test": "run-s frontend:test yesod:test",
|
||||||
"lint": "run-s frontend:lint yesod:lint",
|
"lint": "run-s frontend:lint yesod:lint",
|
||||||
"build": "run-s frontend:build yesod:build",
|
"build": "run-s frontend:build yesod:build",
|
||||||
@ -90,6 +90,7 @@
|
|||||||
"null-loader": "^2.0.0",
|
"null-loader": "^2.0.0",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss-loader": "^3.0.0",
|
||||||
"sass-loader": "^7.3.1",
|
"sass-loader": "^7.3.1",
|
||||||
|
"semver": "^6.3.0",
|
||||||
"standard-version": "^6.0.1",
|
"standard-version": "^6.0.1",
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^0.23.1",
|
||||||
"terser-webpack-plugin": "^2.2.3",
|
"terser-webpack-plugin": "^2.2.3",
|
||||||
@ -97,10 +98,12 @@
|
|||||||
"typeface-source-sans-pro": "0.0.75",
|
"typeface-source-sans-pro": "0.0.75",
|
||||||
"webpack": "^4.41.2",
|
"webpack": "^4.41.2",
|
||||||
"webpack-cli": "^3.3.10",
|
"webpack-cli": "^3.3.10",
|
||||||
"webpack-manifest-plugin": "^2.2.0"
|
"webpack-manifest-plugin": "^2.2.0",
|
||||||
|
"webpack-plugin-hash-output": "^3.2.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.7.6",
|
"@babel/runtime": "^7.7.6",
|
||||||
|
"@juggle/resize-observer": "^2.5.0",
|
||||||
"core-js": "^3.4.8",
|
"core-js": "^3.4.8",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"npm": "^6.13.3",
|
"npm": "^6.13.3",
|
||||||
|
|||||||
544
records.json
544
records.json
@ -1,544 +0,0 @@
|
|||||||
{
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/typeface-roboto/index.css": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/typeface-source-sans-pro/index.css": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/@fortawesome/fontawesome-pro/css/all.css": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/async-form/async-form.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/asidenav/asidenav.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/show-hide/show-hide.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/inputs/inputs.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/inputs/radio.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/form/form.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/alerts/alerts.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/tooltips/tooltips.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/course-teaser/course-teaser.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/modal/modal.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/navbar/navbar.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/async-table/async-table.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/async-table/async-table-filter.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/mass-input/mass-input.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/inputs/file-input.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!node_modules/sass-loader/dist/cjs.js!frontend/src/utils/inputs/checkbox.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/postcss-loader/src/index.js!frontend/src/utils/form/datepicker.css": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
},
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--5-1!node_modules/postcss-loader/src/index.js??ref--5-2!node_modules/typeface-source-sans-pro/index.css": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--5-1!node_modules/postcss-loader/src/index.js??ref--5-2!node_modules/typeface-roboto/index.css": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--5-1!node_modules/postcss-loader/src/index.js??ref--5-2!node_modules/@fortawesome/fontawesome-pro/css/all.css": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/asidenav/asidenav.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/async-form/async-form.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/show-hide/show-hide.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/form/form.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/inputs/inputs.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/inputs/radio.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/tooltips/tooltips.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/course-teaser/course-teaser.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/modal/modal.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/async-table/async-table.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/async-table/async-table-filter.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/mass-input/mass-input.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/alerts/alerts.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/navbar/navbar.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--5-1!node_modules/postcss-loader/src/index.js??ref--5-2!frontend/src/utils/form/datepicker.css": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/inputs/file-input.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/inputs/checkbox.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/app.scss": [
|
|
||||||
{
|
|
||||||
"modules": {
|
|
||||||
"byIdentifier": {},
|
|
||||||
"usedIds": {}
|
|
||||||
},
|
|
||||||
"chunks": {
|
|
||||||
"byName": {},
|
|
||||||
"bySource": {},
|
|
||||||
"usedIds": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -71,6 +71,7 @@ import Handler.Utils.SchoolLdap
|
|||||||
import Handler.Utils.ExamOffice.Exam
|
import Handler.Utils.ExamOffice.Exam
|
||||||
import Handler.Utils.ExamOffice.Course
|
import Handler.Utils.ExamOffice.Course
|
||||||
import Handler.Utils.Profile
|
import Handler.Utils.Profile
|
||||||
|
import Handler.Utils.Routes
|
||||||
import Utils.Form
|
import Utils.Form
|
||||||
import Utils.Sheet
|
import Utils.Sheet
|
||||||
import Utils.SystemMessage
|
import Utils.SystemMessage
|
||||||
@ -96,86 +97,12 @@ import qualified Ldap.Client as Ldap
|
|||||||
|
|
||||||
import UnliftIO.Pool
|
import UnliftIO.Pool
|
||||||
|
|
||||||
|
|
||||||
-- This is where we define all of the routes in our application. For a full
|
|
||||||
-- explanation of the syntax, please see:
|
|
||||||
-- http://www.yesodweb.com/book/routing-and-handlers
|
|
||||||
--
|
|
||||||
-- Note that this is really half the story; in Application.hs, mkYesodDispatch
|
|
||||||
-- generates the rest of the code. Please see the following documentation
|
|
||||||
-- for an explanation for this split:
|
|
||||||
-- http://www.yesodweb.com/book/scaffolding-and-the-site-template#scaffolding-and-the-site-template_foundation_and_application_modules
|
|
||||||
--
|
|
||||||
-- This function also generates the following type synonyms:
|
|
||||||
-- type Handler x = HandlerT UniWorX IO x
|
|
||||||
-- type Widget = WidgetT UniWorX IO ()
|
|
||||||
mkYesodData "UniWorX" uniworxRoutes
|
|
||||||
|
|
||||||
deriving instance Generic CourseR
|
|
||||||
deriving instance Generic SheetR
|
|
||||||
deriving instance Generic SubmissionR
|
|
||||||
deriving instance Generic MaterialR
|
|
||||||
deriving instance Generic TutorialR
|
|
||||||
deriving instance Generic ExamR
|
|
||||||
deriving instance Generic CourseApplicationR
|
|
||||||
deriving instance Generic AllocationR
|
|
||||||
deriving instance Generic SchoolR
|
|
||||||
deriving instance Generic ExamOfficeR
|
|
||||||
deriving instance Generic CourseNewsR
|
|
||||||
deriving instance Generic CourseEventR
|
|
||||||
deriving instance Generic (Route UniWorX)
|
|
||||||
|
|
||||||
data RouteChildren
|
|
||||||
type instance Children RouteChildren a = ChildrenRouteChildren a
|
|
||||||
type family ChildrenRouteChildren a where
|
|
||||||
ChildrenRouteChildren (Route EmbeddedStatic) = '[]
|
|
||||||
ChildrenRouteChildren (Route Auth) = '[]
|
|
||||||
ChildrenRouteChildren UUID = '[]
|
|
||||||
ChildrenRouteChildren (Key a) = '[]
|
|
||||||
ChildrenRouteChildren (CI a) = '[]
|
|
||||||
|
|
||||||
ChildrenRouteChildren a = Children ChGeneric a
|
|
||||||
|
|
||||||
-- | Convenient Type Synonyms:
|
-- | Convenient Type Synonyms:
|
||||||
type DB = YesodDB UniWorX
|
type DB = YesodDB UniWorX
|
||||||
type Form x = Html -> MForm (HandlerFor UniWorX) (FormResult x, Widget)
|
type Form x = Html -> MForm (HandlerFor UniWorX) (FormResult x, Widget)
|
||||||
type MsgRenderer = MsgRendererS UniWorX -- see Utils
|
type MsgRenderer = MsgRendererS UniWorX -- see Utils
|
||||||
type MailM a = MailT (HandlerFor UniWorX) a
|
type MailM a = MailT (HandlerFor UniWorX) a
|
||||||
|
|
||||||
-- Pattern Synonyms for convenience
|
|
||||||
pattern CSheetR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> SheetR -> Route UniWorX
|
|
||||||
pattern CSheetR tid ssh csh shn ptn
|
|
||||||
= CourseR tid ssh csh (SheetR shn ptn)
|
|
||||||
|
|
||||||
pattern CMaterialR :: TermId -> SchoolId -> CourseShorthand -> MaterialName -> MaterialR -> Route UniWorX
|
|
||||||
pattern CMaterialR tid ssh csh mnm ptn
|
|
||||||
= CourseR tid ssh csh (MaterialR mnm ptn)
|
|
||||||
|
|
||||||
pattern CTutorialR :: TermId -> SchoolId -> CourseShorthand -> TutorialName -> TutorialR -> Route UniWorX
|
|
||||||
pattern CTutorialR tid ssh csh tnm ptn
|
|
||||||
= CourseR tid ssh csh (TutorialR tnm ptn)
|
|
||||||
|
|
||||||
pattern CExamR :: TermId -> SchoolId -> CourseShorthand -> ExamName -> ExamR -> Route UniWorX
|
|
||||||
pattern CExamR tid ssh csh tnm ptn
|
|
||||||
= CourseR tid ssh csh (ExamR tnm ptn)
|
|
||||||
|
|
||||||
pattern CSubmissionR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> CryptoFileNameSubmission -> SubmissionR -> Route UniWorX
|
|
||||||
pattern CSubmissionR tid ssh csh shn cid ptn
|
|
||||||
= CSheetR tid ssh csh shn (SubmissionR cid ptn)
|
|
||||||
|
|
||||||
pattern CApplicationR :: TermId -> SchoolId -> CourseShorthand -> CryptoFileNameCourseApplication -> CourseApplicationR -> Route UniWorX
|
|
||||||
pattern CApplicationR tid ssh csh appId ptn
|
|
||||||
= CourseR tid ssh csh (CourseApplicationR appId ptn)
|
|
||||||
|
|
||||||
pattern CNewsR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDCourseNews -> CourseNewsR -> Route UniWorX
|
|
||||||
pattern CNewsR tid ssh csh nId ptn
|
|
||||||
= CourseR tid ssh csh (CourseNewsR nId ptn)
|
|
||||||
|
|
||||||
pattern CEventR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDCourseEvent -> CourseEventR -> Route UniWorX
|
|
||||||
pattern CEventR tid ssh csh nId ptn
|
|
||||||
= CourseR tid ssh csh (CourseEventR nId ptn)
|
|
||||||
|
|
||||||
|
|
||||||
-- Requires `rendeRoute`, thus cannot currently be moved to Foundation.I18n
|
-- Requires `rendeRoute`, thus cannot currently be moved to Foundation.I18n
|
||||||
instance RenderMessage UniWorX (UnsupportedAuthPredicate AuthTag (Route UniWorX)) where
|
instance RenderMessage UniWorX (UnsupportedAuthPredicate AuthTag (Route UniWorX)) where
|
||||||
renderMessage f ls (UnsupportedAuthPredicate tag route) = mr . MsgUnsupportedAuthPredicate (mr tag) $ Text.intercalate "/" pieces
|
renderMessage f ls (UnsupportedAuthPredicate tag route) = mr . MsgUnsupportedAuthPredicate (mr tag) $ Text.intercalate "/" pieces
|
||||||
@ -1486,6 +1413,7 @@ siteLayout' headingOverride widget = do
|
|||||||
primaryLanguage <- unsafeHead . Text.splitOn "-" <$> selectLanguage appLanguages
|
primaryLanguage <- unsafeHead . Text.splitOn "-" <$> selectLanguage appLanguages
|
||||||
|
|
||||||
mcurrentRoute <- getCurrentRoute
|
mcurrentRoute <- getCurrentRoute
|
||||||
|
let currentHandler = classifyHandler <$> mcurrentRoute
|
||||||
|
|
||||||
-- Get the breadcrumbs, as defined in the YesodBreadcrumbs instance.
|
-- Get the breadcrumbs, as defined in the YesodBreadcrumbs instance.
|
||||||
let
|
let
|
||||||
|
|||||||
@ -1,10 +1,84 @@
|
|||||||
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||||
|
|
||||||
module Foundation.Routes
|
module Foundation.Routes
|
||||||
( uniworxRoutes
|
( module Foundation.Routes.Definitions
|
||||||
|
, module Foundation.Routes
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import ClassyPrelude.Yesod
|
import Import.NoFoundation
|
||||||
import Yesod.Routes.TH.Types (ResourceTree)
|
import Foundation.Type
|
||||||
|
|
||||||
|
import Foundation.Routes.Definitions
|
||||||
|
|
||||||
uniworxRoutes :: [ResourceTree String]
|
-- This is where we define all of the routes in our application. For a full
|
||||||
uniworxRoutes = $(parseRoutesFile "routes")
|
-- explanation of the syntax, please see:
|
||||||
|
-- http://www.yesodweb.com/book/routing-and-handlers
|
||||||
|
--
|
||||||
|
-- Note that this is really half the story; in Application.hs, mkYesodDispatch
|
||||||
|
-- generates the rest of the code. Please see the following documentation
|
||||||
|
-- for an explanation for this split:
|
||||||
|
-- http://www.yesodweb.com/book/scaffolding-and-the-site-template#scaffolding-and-the-site-template_foundation_and_application_modules
|
||||||
|
--
|
||||||
|
-- This function also generates the following type synonyms:
|
||||||
|
-- type Handler x = HandlerT UniWorX IO x
|
||||||
|
-- type Widget = WidgetT UniWorX IO ()
|
||||||
|
mkYesodData "UniWorX" uniworxRoutes
|
||||||
|
|
||||||
|
deriving instance Generic CourseR
|
||||||
|
deriving instance Generic SheetR
|
||||||
|
deriving instance Generic SubmissionR
|
||||||
|
deriving instance Generic MaterialR
|
||||||
|
deriving instance Generic TutorialR
|
||||||
|
deriving instance Generic ExamR
|
||||||
|
deriving instance Generic CourseApplicationR
|
||||||
|
deriving instance Generic AllocationR
|
||||||
|
deriving instance Generic SchoolR
|
||||||
|
deriving instance Generic ExamOfficeR
|
||||||
|
deriving instance Generic CourseNewsR
|
||||||
|
deriving instance Generic CourseEventR
|
||||||
|
deriving instance Generic (Route UniWorX)
|
||||||
|
|
||||||
|
data RouteChildren
|
||||||
|
type instance Children RouteChildren a = ChildrenRouteChildren a
|
||||||
|
type family ChildrenRouteChildren a where
|
||||||
|
ChildrenRouteChildren (Route EmbeddedStatic) = '[]
|
||||||
|
ChildrenRouteChildren (Route Auth) = '[]
|
||||||
|
ChildrenRouteChildren UUID = '[]
|
||||||
|
ChildrenRouteChildren (Key a) = '[]
|
||||||
|
ChildrenRouteChildren (CI a) = '[]
|
||||||
|
|
||||||
|
ChildrenRouteChildren a = Children ChGeneric a
|
||||||
|
|
||||||
|
-- Pattern Synonyms for convenience
|
||||||
|
pattern CSheetR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> SheetR -> Route UniWorX
|
||||||
|
pattern CSheetR tid ssh csh shn ptn
|
||||||
|
= CourseR tid ssh csh (SheetR shn ptn)
|
||||||
|
|
||||||
|
pattern CMaterialR :: TermId -> SchoolId -> CourseShorthand -> MaterialName -> MaterialR -> Route UniWorX
|
||||||
|
pattern CMaterialR tid ssh csh mnm ptn
|
||||||
|
= CourseR tid ssh csh (MaterialR mnm ptn)
|
||||||
|
|
||||||
|
pattern CTutorialR :: TermId -> SchoolId -> CourseShorthand -> TutorialName -> TutorialR -> Route UniWorX
|
||||||
|
pattern CTutorialR tid ssh csh tnm ptn
|
||||||
|
= CourseR tid ssh csh (TutorialR tnm ptn)
|
||||||
|
|
||||||
|
pattern CExamR :: TermId -> SchoolId -> CourseShorthand -> ExamName -> ExamR -> Route UniWorX
|
||||||
|
pattern CExamR tid ssh csh tnm ptn
|
||||||
|
= CourseR tid ssh csh (ExamR tnm ptn)
|
||||||
|
|
||||||
|
pattern CSubmissionR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> CryptoFileNameSubmission -> SubmissionR -> Route UniWorX
|
||||||
|
pattern CSubmissionR tid ssh csh shn cid ptn
|
||||||
|
= CSheetR tid ssh csh shn (SubmissionR cid ptn)
|
||||||
|
|
||||||
|
pattern CApplicationR :: TermId -> SchoolId -> CourseShorthand -> CryptoFileNameCourseApplication -> CourseApplicationR -> Route UniWorX
|
||||||
|
pattern CApplicationR tid ssh csh appId ptn
|
||||||
|
= CourseR tid ssh csh (CourseApplicationR appId ptn)
|
||||||
|
|
||||||
|
pattern CNewsR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDCourseNews -> CourseNewsR -> Route UniWorX
|
||||||
|
pattern CNewsR tid ssh csh nId ptn
|
||||||
|
= CourseR tid ssh csh (CourseNewsR nId ptn)
|
||||||
|
|
||||||
|
pattern CEventR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDCourseEvent -> CourseEventR -> Route UniWorX
|
||||||
|
pattern CEventR tid ssh csh nId ptn
|
||||||
|
= CourseR tid ssh csh (CourseEventR nId ptn)
|
||||||
|
|||||||
10
src/Foundation/Routes/Definitions.hs
Normal file
10
src/Foundation/Routes/Definitions.hs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module Foundation.Routes.Definitions
|
||||||
|
( uniworxRoutes
|
||||||
|
) where
|
||||||
|
|
||||||
|
import ClassyPrelude.Yesod
|
||||||
|
import Yesod.Routes.TH.Types (ResourceTree)
|
||||||
|
|
||||||
|
|
||||||
|
uniworxRoutes :: [ResourceTree String]
|
||||||
|
uniworxRoutes = $(parseRoutesFile "routes")
|
||||||
@ -89,7 +89,7 @@ getAllocationListR = do
|
|||||||
|
|
||||||
dbtSorting = mconcat
|
dbtSorting = mconcat
|
||||||
[ sortTerm $ queryAllocation . to (E.^. AllocationTerm)
|
[ sortTerm $ queryAllocation . to (E.^. AllocationTerm)
|
||||||
, sortSchool $ queryAllocation . to (E.^. AllocationSchool)
|
, sortSchoolShort $ queryAllocation . to (E.^. AllocationSchool)
|
||||||
, sortAllocationName $ queryAllocation . to (E.^. AllocationName)
|
, sortAllocationName $ queryAllocation . to (E.^. AllocationName)
|
||||||
, singletonMap "available" . SortColumn $ view queryAvailable
|
, singletonMap "available" . SortColumn $ view queryAvailable
|
||||||
, if
|
, if
|
||||||
@ -124,7 +124,7 @@ getAllocationListR = do
|
|||||||
|
|
||||||
psValidator :: PSValidator _ _
|
psValidator :: PSValidator _ _
|
||||||
psValidator = def
|
psValidator = def
|
||||||
& defaultSorting [SortDescBy "term", SortAscBy "school", SortAscBy "allocation"]
|
& defaultSorting [SortDescBy "term", SortAscBy "school-short", SortAscBy "allocation"]
|
||||||
|
|
||||||
table <- runDB $ dbTableWidget' psValidator DBTable{..}
|
table <- runDB $ dbTableWidget' psValidator DBTable{..}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,9 @@ module Handler.Utils.Routes
|
|||||||
( classifyHandler
|
( classifyHandler
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Import
|
import Import.NoFoundation
|
||||||
|
import Foundation.Routes
|
||||||
|
import Foundation.Type
|
||||||
|
|
||||||
import Utils.TH.Routes
|
import Utils.TH.Routes
|
||||||
|
|
||||||
|
|||||||
@ -138,12 +138,33 @@ packages:
|
|||||||
original:
|
original:
|
||||||
hackage: HaXml-1.25.5
|
hackage: HaXml-1.25.5
|
||||||
- completed:
|
- completed:
|
||||||
hackage: esqueleto-3.0.0@sha256:efd84fd11ceaf0ae4e1b0c6236122b1f213c2c6f2f4f58e30f03eddc2ec3f423,5248
|
hackage: persistent-2.10.4@sha256:16c4c0823dd5e16bac4d607895ab0f4febd0626c020e5755ed1a52bf04068148,4738
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
size: 1127
|
size: 2094
|
||||||
sha256: 74e43834d5cc468acc3cb6e8a81567ebfbb5350a3e07ae01dd7f30d6255274a1
|
sha256: b40d1783b539ddbbceaa827bf286d0b3bfcf76ca19e604c9d510b2a64008714e
|
||||||
original:
|
original:
|
||||||
hackage: esqueleto-3.0.0
|
hackage: persistent-2.10.4
|
||||||
|
- completed:
|
||||||
|
hackage: persistent-postgresql-2.10.1@sha256:ea53a0f1f4223b4884b5e19511325367879560d2432a02a976aa4da57c5fb760,2871
|
||||||
|
pantry-tree:
|
||||||
|
size: 740
|
||||||
|
sha256: 3cdbc757b1cebb65542fb919369be238b3f120adc45f023084a8b64c214d9675
|
||||||
|
original:
|
||||||
|
hackage: persistent-postgresql-2.10.1
|
||||||
|
- completed:
|
||||||
|
hackage: persistent-template-2.7.3@sha256:ac3e5e8c48e968b927bbf4e97162c52e7e417d69b05efeb1c581d7c682e043d2,2703
|
||||||
|
pantry-tree:
|
||||||
|
size: 560
|
||||||
|
sha256: fdfb2a721eb9c9831d7381d36bc52de0808a008ed3d553b6490080f337249684
|
||||||
|
original:
|
||||||
|
hackage: persistent-template-2.7.3
|
||||||
|
- completed:
|
||||||
|
hackage: esqueleto-3.2.3@sha256:5e1e0a8600e2744127ef4bb5956fa84ae6bc1fc337c7b8726fabb7ca53e2d9b3,5466
|
||||||
|
pantry-tree:
|
||||||
|
size: 1461
|
||||||
|
sha256: f6215274a43addd339f8bc89f1ca0e8fdfb08180b13d779ae8f7e360acc4c473
|
||||||
|
original:
|
||||||
|
hackage: esqueleto-3.2.3
|
||||||
- completed:
|
- completed:
|
||||||
hackage: HaskellNet-SSL-0.3.4.1@sha256:3ca14dd69460a380cf69aed40654fb10c4c03e344632b6a9986568c87feda157,1843
|
hackage: HaskellNet-SSL-0.3.4.1@sha256:3ca14dd69460a380cf69aed40654fb10c4c03e344632b6a9986568c87feda157,1843
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
@ -242,6 +263,20 @@ packages:
|
|||||||
sha256: 9ed161eadfda5b1eb36cfcf077146f7b66db1da69f1041fc720aea287ec021b0
|
sha256: 9ed161eadfda5b1eb36cfcf077146f7b66db1da69f1041fc720aea287ec021b0
|
||||||
original:
|
original:
|
||||||
hackage: generic-lens-1.2.0.0
|
hackage: generic-lens-1.2.0.0
|
||||||
|
- completed:
|
||||||
|
hackage: prometheus-metrics-ghc-1.0.0@sha256:0f4ecbefa810bd847e66c498ab3387bf21e426525a7c9a94841973c582719ba3,1231
|
||||||
|
pantry-tree:
|
||||||
|
size: 293
|
||||||
|
sha256: 8a6d6ef3235ab980e867f64b712b5d38f1a84c3ac4920f5b4c3b3e63bcdf6ec9
|
||||||
|
original:
|
||||||
|
hackage: prometheus-metrics-ghc-1.0.0
|
||||||
|
- completed:
|
||||||
|
hackage: wai-middleware-prometheus-1.0.0@sha256:1625792914fb2139f005685be8ce519111451cfb854816e430fbf54af46238b4,1314
|
||||||
|
pantry-tree:
|
||||||
|
size: 307
|
||||||
|
sha256: 6d64803c639ed4c7204ea6fab0536b97d3ee16cdecb9b4a883cd8e56d3c61402
|
||||||
|
original:
|
||||||
|
hackage: wai-middleware-prometheus-1.0.0
|
||||||
snapshots:
|
snapshots:
|
||||||
- completed:
|
- completed:
|
||||||
size: 498180
|
size: 498180
|
||||||
|
|||||||
@ -4,4 +4,4 @@ $#
|
|||||||
$# participantTable : widget table
|
$# participantTable : widget table
|
||||||
|
|
||||||
^{participantTable}
|
^{participantTable}
|
||||||
_{MsgCourseMembersCountOf (fromIntegral numParticipants) (courseCapacity course)}.
|
_{MsgCourseMembersCountOf (fromIntegral numParticipants) (courseCapacity course)}.
|
||||||
|
|||||||
@ -11,7 +11,7 @@ $if not isModal
|
|||||||
|
|
||||||
<div .main>
|
<div .main>
|
||||||
|
|
||||||
<div .main__content uw-poc>
|
<div .main__content uw-poc :isJust currentHandler:uw-handler=#{fromMaybe "" currentHandler}>
|
||||||
|
|
||||||
$if not isModal
|
$if not isModal
|
||||||
<!-- breadcrumbs -->
|
<!-- breadcrumbs -->
|
||||||
|
|||||||
@ -329,8 +329,12 @@ input[type="button"].btn-info:hover,
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table:only-child {
|
.table:first-child {
|
||||||
margin: 0;
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table--striped {
|
.table--striped {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
$newline never
|
$newline never
|
||||||
<th .table__th *{attrs} :isSortable:.sortable :isSorted SortAsc:.sorted-asc :isSorted SortDesc:.sorted-desc>
|
<th .table__th *{attrs} :isSortable:.sortable :isSorted SortAsc:.sorted-asc :isSorted SortDesc:.sorted-desc uw-hide-column-header=#{maybe "" toPathPiece sortableKey}>
|
||||||
$maybe flag <- sortableKey
|
$maybe flag <- sortableKey
|
||||||
$case directions
|
$case directions
|
||||||
$of [SortAsc]
|
$of [SortAsc]
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
$newline never
|
$newline never
|
||||||
|
<div table-utils>
|
||||||
<div .scrolltable .scrolltable--bordered>
|
<div .scrolltable .scrolltable--bordered>
|
||||||
<table *{dbsAttrs'}>
|
<table *{dbsAttrs'}>
|
||||||
$maybe wHeaders' <- wHeaders
|
$maybe wHeaders' <- wHeaders
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
$newline never
|
$newline never
|
||||||
<div ##{wIdent "table-wrapper"} :not (null rows && (dbsEmptyStyle == DBESNoHeading)):uw-async-table data-async-table-db-header=#{toPathPiece HeaderDBTableShortcircuit}>
|
<div ##{wIdent "table-wrapper"} :not (null rows && (dbsEmptyStyle == DBESNoHeading)):uw-async-table uw-hide-columns=#{dbtIdent} data-async-table-db-header=#{toPathPiece HeaderDBTableShortcircuit}>
|
||||||
^{table}
|
^{table}
|
||||||
|
|||||||
@ -6,9 +6,10 @@ const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
|||||||
const CopyPlugin = require('copy-webpack-plugin');
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
const yaml = require('js-yaml');
|
const yaml = require('js-yaml');
|
||||||
|
const HashOutput = require('webpack-plugin-hash-output');
|
||||||
|
|
||||||
var webpackVersion = require('webpack/package.json').version.split('.').slice(0, 2).join('.');
|
const webpackVersion = require('webpack/package.json').version.split('.').slice(0, 2).join('.');
|
||||||
|
const packageVersion = require('./package.json').version;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
module: {
|
module: {
|
||||||
@ -71,15 +72,21 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
entry: {
|
entry: {
|
||||||
main: path.resolve(__dirname, 'frontend/src', 'main.js')
|
main: [ path.resolve(__dirname, 'frontend/src', 'polyfill.js'),
|
||||||
|
path.resolve(__dirname, 'frontend/src', 'main.js')
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
|
new HashOutput({
|
||||||
|
validateOutput: true,
|
||||||
|
validateOutputRegex: /static\/wp-[^\/]\//
|
||||||
|
}),
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
// Options similar to the same options in webpackOptions.output
|
// Options similar to the same options in webpackOptions.output
|
||||||
// all options are optional
|
// all options are optional
|
||||||
filename: '[contenthash].css',
|
filename: '[chunkhash].css',
|
||||||
chunkFilename: '[contenthash].css',
|
chunkFilename: '[chunkhash].css',
|
||||||
ignoreOrder: false, // Enable to remove warnings about conflicting order
|
ignoreOrder: false, // Enable to remove warnings about conflicting order
|
||||||
}),
|
}),
|
||||||
new webpack.NamedChunksPlugin((chunk) => {
|
new webpack.NamedChunksPlugin((chunk) => {
|
||||||
@ -101,12 +108,15 @@ module.exports = {
|
|||||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||||
new CopyPlugin([
|
new CopyPlugin([
|
||||||
{ from: 'assets/lmu/sigillum.svg', to: path.resolve(__dirname, 'static', 'img/lmu/sigillum.svg') },
|
{ from: 'assets/lmu/sigillum.svg', to: path.resolve(__dirname, 'static', 'img/lmu/sigillum.svg') },
|
||||||
])
|
]),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
VERSION: JSON.stringify(packageVersion)
|
||||||
|
})
|
||||||
],
|
],
|
||||||
|
|
||||||
output: {
|
output: {
|
||||||
chunkFilename: '[contenthash].js',
|
chunkFilename: '[chunkhash].js',
|
||||||
filename: '[contenthash].js',
|
filename: '[chunkhash].js',
|
||||||
path: path.resolve(__dirname, 'static', `wp-${webpackVersion}`),
|
path: path.resolve(__dirname, 'static', `wp-${webpackVersion}`),
|
||||||
publicPath: `/static/res/wp-${webpackVersion}/`,
|
publicPath: `/static/res/wp-${webpackVersion}/`,
|
||||||
hashFunction: 'shake256',
|
hashFunction: 'shake256',
|
||||||
|
|||||||
Reference in New Issue
Block a user