diff --git a/package-lock.json b/package-lock.json index b49ff6e..068f41b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3798,9 +3798,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA==" }, "lodash.camelcase": { "version": "4.3.0", @@ -4124,9 +4124,9 @@ "dev": true }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", diff --git a/package.json b/package.json index 6269867..8cc0e23 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "highlightjs": "^9.10.0", "jquery": "^3.4.0", "js-cookie": "^2.2.0", - "lodash": "^4.17.11", + "lodash": "^4.17.13", "marked": "^0.6.2", "mithril": "^1.1.6", "popper.js": "^1.14.4", diff --git a/src/app/app.tsx b/src/app/app.tsx index 61316e5..f7792d8 100755 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -10,9 +10,9 @@ import * as m from 'mithril'; import '../scss/main.scss'; import 'bootstrap'; import * as Cookie from 'js-cookie'; -import { Setting } from './interfaces/Setting'; +import { ISetting } from './interfaces/Setting'; import { CronJob } from 'cron'; -import { ResponseObject } from './interfaces/ResponseObject'; +import { ISuccessObject } from './interfaces/ResponseObject'; import LogPage from './pages/LogPage'; import LogsPage from './pages/LogsPage'; import RunsPage from './pages/RunsPage'; @@ -23,8 +23,11 @@ import SubsystemsOverviewPage from './pages/SubsystemsOverviewPage'; import ProfilePage from './pages/ProfilePage'; import LoginPage from './pages/LoginPage'; import AuthorizingPage from './pages/AuthorizingPage'; +import { APPLICATION_NAME } from './constants/constants'; +import TagsOverviewPage from './pages/TagsOverviewPage'; m.route.prefix(''); +document.title = APPLICATION_NAME; /** * Routes enabled when user is authenticated. */ @@ -49,6 +52,11 @@ const authenticatedRoutes = { ), }, + '/logs/create/comments/:id': { + view: (vnode: m.Vnode<{ id: number }>) => ( + + ), + }, '/logs/:id': { view: (vnode: m.Vnode<{ id: number }>) => ( @@ -78,6 +86,11 @@ const authenticatedRoutes = { view: (vnode: m.Vnode<{ userId: number }>) => ( ), + }, + '/tags': { + view: () => ( + + ), } }; @@ -102,6 +115,16 @@ const lockedOutRoutes = { * (logged in is in essence: does the user have a cookie with a JWT) */ export const initialize = () => { + const allowAnonymous = process.env.ALLOW_ANONYMOUS; + + if (typeof(allowAnonymous) !== 'undefined' && allowAnonymous.toLowerCase() === 'true') { + if (!Cookie.get('token')) { + Cookie.set('token', 'TEST'); + } + } else if (Cookie.get('token') && Cookie.get('token') === 'TEST') { + Cookie.remove('token'); + } + const token = Cookie.get('token'); if (token) { m.route(document.body, '/', authenticatedRoutes); @@ -111,16 +134,17 @@ export const initialize = () => { }; /** - * Creates a request to the /setting endpoint in order to retrieve settings for the authentication. + * Creates a request to the /setting endpoint in order to retrieve settings for the authentication and others. */ -export const getAuthSettings = () => { +export const getSettings = () => { return m.request({ method: 'GET', url: `${process.env.API_URL}setting` - }).then((result: ResponseObject) => { - // setting['date'] = new Date().valueOf(); - localStorage.setItem('USE_CERN_SSO', result.data.item.USE_CERN_SSO); - localStorage.setItem('AUTH_URL', result.data.item.AUTH_URL); + }).then((result: ISuccessObject) => { + const settingsArray = Object.entries(result.data.item); + settingsArray.forEach((setting: [string, string | number | boolean]) => { + localStorage.setItem(setting[0], setting[1].toString()); + }); }); }; @@ -128,8 +152,8 @@ export const getAuthSettings = () => { * Schedule a daily cronjob to check if the settings are up to date. */ new CronJob('0 2 * * *', () => { - getAuthSettings(); + getSettings(); }).start(); -getAuthSettings(); +getSettings(); initialize(); diff --git a/src/app/atoms/Badges.tsx b/src/app/atoms/Badges.tsx index 7558cec..911d765 100644 --- a/src/app/atoms/Badges.tsx +++ b/src/app/atoms/Badges.tsx @@ -9,13 +9,13 @@ import * as m from 'mithril'; import { MithrilTsxComponent } from 'mithril-tsx-component'; import * as _ from 'lodash'; -import { FilterState, FilterValue } from '../interfaces/Filter'; +import { IFilterState, FilterValue } from '../interfaces/Filter'; interface Attrs { /** * The values of the filters. */ - filters: FilterState; + filters: IFilterState; /** * Function being called when the event happens on a button click. @@ -37,11 +37,11 @@ type Vnode = m.Vnode; export default class Badges extends MithrilTsxComponent { - filteredFilters(filters: FilterState, ignoredFilters: string[]) { + filteredFilters(filters: IFilterState, ignoredFilters: string[]) { return _.omit(filters, ignoredFilters); } - assertActiveFilters(filters: FilterState) { + assertActiveFilters(filters: IFilterState) { let assertActiveFilters: boolean = false; Object.keys(filters).map((key: string) => { if (filters[key] !== null) { diff --git a/src/app/atoms/Button.tsx b/src/app/atoms/Button.tsx index 54c1076..789d8ce 100644 --- a/src/app/atoms/Button.tsx +++ b/src/app/atoms/Button.tsx @@ -8,7 +8,7 @@ import * as m from 'mithril'; import { MithrilTsxComponent } from 'mithril-tsx-component'; -import { Event } from '../interfaces/Event'; +import { IEvent } from '../interfaces/Event'; /** * Css class that defines the size (bootstrap) @@ -27,7 +27,8 @@ export enum ButtonClass { SUCCESS = 'btn btn-success', NAV = 'dropdown-item jf-dropdown-item', CLOSE = 'close', - INFO = 'btn btn-info' + INFO = 'btn btn-info', + SMALL = 'btn btn-sm btn-secondary' } /** @@ -47,7 +48,7 @@ interface Attrs { id?: string | number; margin?: string; href?: string; - onClick?: (event?: Event) => void; + onClick?: (event?: IEvent) => void; name?: string; value?: string | number; dataToggle?: string; diff --git a/src/app/atoms/Collapse.tsx b/src/app/atoms/Collapse.tsx index 298dd11..4000d05 100644 --- a/src/app/atoms/Collapse.tsx +++ b/src/app/atoms/Collapse.tsx @@ -8,7 +8,7 @@ import * as m from 'mithril'; import { MithrilTsxComponent } from 'mithril-tsx-component'; -import { Event } from '../interfaces/Event'; +import { IEvent } from '../interfaces/Event'; import { store } from '../redux/configureStore'; import { toggleCollapse, addCollapse } from '../redux/ducks/ui/actions'; import { selectCollapsableItem } from '../redux/ducks/ui/selectors'; @@ -31,6 +31,7 @@ interface Attrs { * Whether the component is initially collapsed. */ isInitiallyCollapsed?: boolean; + style?: object; } type Vnode = m.Vnode; @@ -49,13 +50,13 @@ export default class Collapse extends MithrilTsxComponent { * Toggles the collapsed state of the component. * @param event */ - toggleCollapse(event: Event) { + toggleCollapse(event: IEvent) { const id = event.target.id; store.dispatch(toggleCollapse(id)); } view(vnode: Vnode) { - const { icon, title, id } = vnode.attrs; + const { icon, title, id, style } = vnode.attrs; const collapsableItem = selectCollapsableItem(store.getState(), id); return (
@@ -67,9 +68,10 @@ export default class Collapse extends MithrilTsxComponent { aria-expanded={collapsableItem && collapsableItem.isCollapsed ? 'false' : 'true'} data-fa-transform="grow-10" onclick={this.toggleCollapse} + style={style} > {icon} -  {title} + {title}
{vnode.children} diff --git a/src/app/atoms/DescriptionList.tsx b/src/app/atoms/DescriptionList.tsx index a40e6fe..9292b3f 100644 --- a/src/app/atoms/DescriptionList.tsx +++ b/src/app/atoms/DescriptionList.tsx @@ -8,9 +8,9 @@ import * as m from 'mithril'; import { MithrilTsxComponent } from 'mithril-tsx-component'; -import { Description } from '../interfaces/Description'; -import { Log } from '../interfaces/Log'; -import { Run } from '../interfaces/Run'; +import { IDescription } from '../interfaces/Description'; +import { ILog } from '../interfaces/Log'; +import { IRun } from '../interfaces/Run'; interface Attrs { /** @@ -22,7 +22,7 @@ interface Attrs { /** * List of descriptions (label, value) */ - descriptions: Description[]; + descriptions: IDescription[]; /** * Optional number that determine the length of the first list. @@ -32,7 +32,7 @@ interface Attrs { /** * The entity to display the details of. */ - entity: Log | Run | null; + entity: ILog | IRun | null; } type Vnode = m.Vnode; @@ -51,7 +51,7 @@ export default class DescriptionList extends MithrilTsxComponent {
{ - descriptions.map((description: Description) => ( + descriptions.map((description: IDescription) => (
{description.label}
{description.value(entity)}
@@ -66,7 +66,7 @@ export default class DescriptionList extends MithrilTsxComponent {
{ listLength && - descriptions.slice(0, listLength).map((description: Description) => ( + descriptions.slice(0, listLength).map((description: IDescription) => (
{description.label}
{description.value(entity)}
@@ -77,7 +77,7 @@ export default class DescriptionList extends MithrilTsxComponent {
{ listLength && - descriptions.slice(listLength).map((description: Description) => ( + descriptions.slice(listLength).map((description: IDescription) => (
{description.label}
{description.value(entity)}
diff --git a/src/app/atoms/HttpErrorAlert.tsx b/src/app/atoms/HttpErrorAlert.tsx index cc82ec0..07b5a2b 100644 --- a/src/app/atoms/HttpErrorAlert.tsx +++ b/src/app/atoms/HttpErrorAlert.tsx @@ -7,7 +7,7 @@ */ import * as m from 'mithril'; -import { HttpError } from '../interfaces/HttpError'; +import { IHttpError } from '../interfaces/HttpError'; import { MithrilTsxComponent } from 'mithril-tsx-component'; import { extractErrors } from '../redux/ducks/error/operations'; import { store } from '../redux/configureStore'; @@ -23,7 +23,7 @@ interface Attrs { type Vnode = m.Vnode; export default class HttpErrorAlert extends MithrilTsxComponent { - errors: HttpError[] = []; + errors: Array> = []; async oninit() { const fetchedErrors = await store.dispatch(extractErrors()); @@ -46,12 +46,12 @@ export default class HttpErrorAlert extends MithrilTsxComponent {
- {errors.map((error: HttpError) => { + {errors.map((error: IHttpError) => { return ( // tslint:disable-next-line:jsx-key
-

{error.statusCode} {error.error}

-

{error.message}

+

{error.error.code} {error.error.error}

+

{error.error.message}

); })} diff --git a/src/app/atoms/Input.tsx b/src/app/atoms/Input.tsx index 45f87d5..c3e31b2 100644 --- a/src/app/atoms/Input.tsx +++ b/src/app/atoms/Input.tsx @@ -8,7 +8,7 @@ import * as m from 'mithril'; import { MithrilTsxComponent } from 'mithril-tsx-component'; -import { Event } from '../interfaces/Event'; +import { IEvent } from '../interfaces/Event'; export enum InputSize { SMALL = 'col-md-2', @@ -27,7 +27,8 @@ interface Attrs { required?: boolean; dataShowCaption?: string; value?: string | number; - oninput?: (event: Event) => void; + oninput?: (event: IEvent) => void; + children?: JSX.Element; } type Vnode = m.Vnode; @@ -45,7 +46,8 @@ export default class Input extends MithrilTsxComponent { required, dataShowCaption, value, - oninput + oninput, + children } = vnode.attrs; return ( { value={value} data-show-caption={dataShowCaption} oninput={oninput} - /> + > + {children} + ); } } diff --git a/src/app/atoms/Label.tsx b/src/app/atoms/Label.tsx index c63d1fc..4d16db5 100644 --- a/src/app/atoms/Label.tsx +++ b/src/app/atoms/Label.tsx @@ -13,15 +13,16 @@ interface Attrs { id: string; text: string; className?: string; + autofocus?: string; } type Vnode = m.Vnode; export default class Label extends MithrilTsxComponent { view(vnode: Vnode) { - const { id, text, className } = vnode.attrs; + const { id, text, className, autofocus } = vnode.attrs; return ( - + ); } } diff --git a/src/app/atoms/MarkdownEditor.tsx b/src/app/atoms/MarkdownEditor.tsx index 0d10558..1ae2bcb 100644 --- a/src/app/atoms/MarkdownEditor.tsx +++ b/src/app/atoms/MarkdownEditor.tsx @@ -8,10 +8,11 @@ import * as m from 'mithril'; import { MithrilTsxComponent } from 'mithril-tsx-component'; -import { Event } from '../interfaces/Event'; +import { IEvent } from '../interfaces/Event'; interface Attrs { postContent: (content: string) => void; + value?: string | number; } type Vnode = m.Vnode; @@ -21,15 +22,17 @@ type Vnode = m.Vnode; */ export default class MarkdownEditor extends MithrilTsxComponent { content: string; - /** * Bind event.target.value to this.content; */ - handleInput = (event: Event): void => { + handleInput = (event: IEvent): void => { this.content = event.target.value; } view(vnode: Vnode) { + const { + value + } = vnode.attrs; return (
@@ -37,7 +40,8 @@ export default class MarkdownEditor extends MithrilTsxComponent { id="markdown" class="rounded" placeholder="Type your description here" - oninput={(event: Event) => { + value={value} + oninput={(event: IEvent) => { this.handleInput(event); vnode.attrs.postContent(this.content); }} diff --git a/src/app/atoms/Pagination.tsx b/src/app/atoms/Pagination.tsx index 4a11214..d5c6f3e 100644 --- a/src/app/atoms/Pagination.tsx +++ b/src/app/atoms/Pagination.tsx @@ -9,7 +9,7 @@ import * as m from 'mithril'; import * as _ from 'lodash'; import { MithrilTsxComponent } from 'mithril-tsx-component'; -import { Event } from '../interfaces/Event'; +import { IEvent } from '../interfaces/Event'; import * as $ from 'jquery'; interface Attrs { @@ -63,7 +63,7 @@ export default class Pagination extends MithrilTsxComponent { {...{ type: this.inputIsActive ? 'text' : 'hidden' }} class="page-selector form-control form-control-sm" id="page-selector-id" - onchange={(event: Event) => { + onchange={(event: IEvent) => { let newPage = +event.target.value; newPage = newPage > numberOfPages ? numberOfPages : newPage; newPage = newPage < 1 ? 1 : newPage; diff --git a/src/app/atoms/Select.tsx b/src/app/atoms/Select.tsx index 1b771e5..80345b4 100644 --- a/src/app/atoms/Select.tsx +++ b/src/app/atoms/Select.tsx @@ -8,7 +8,7 @@ import * as m from 'mithril'; import { MithrilTsxComponent } from 'mithril-tsx-component'; -import { Event } from '../interfaces/Event'; +import { IEvent } from '../interfaces/Event'; export enum InputSize { SMALL = 'col-md-2', @@ -19,19 +19,31 @@ export enum InputSize { interface Attrs { id: string; className: string; - inputSize: InputSize; + style?: string; + inputSize?: InputSize; name?: string; placeholder?: string; required?: boolean; - oninput?: (event: Event) => void; - options: string[]; + oninput?: (event: IEvent) => void; + optionValue?: string; + optionText?: string; + options: any[]; + hidden?: boolean; + liveSearch?: boolean; + defaultOption?: string | number | null; } type Vnode = m.Vnode; export default class Select extends MithrilTsxComponent { view(vnode: Vnode) { - const { id, name, className, inputSize, placeholder, required, oninput, options } = vnode.attrs; + const { id, name, + className, inputSize, + placeholder, required, + oninput, options, + optionValue, optionText, + defaultOption, hidden, + style } = vnode.attrs; return ( diff --git a/src/app/atoms/TabHeader.tsx b/src/app/atoms/TabHeader.tsx index c4934ba..0d528d2 100644 --- a/src/app/atoms/TabHeader.tsx +++ b/src/app/atoms/TabHeader.tsx @@ -8,14 +8,14 @@ import * as m from 'mithril'; import { MithrilTsxComponent } from 'mithril-tsx-component'; -import { Tabs } from '../interfaces/Tabs'; +import { ITab } from '../interfaces/Tab'; interface Attrs { /** * The information of a tab. Each object in the array represents a tab. * It is used to set the id and name of the tab header. */ - tabs: Tabs[]; + tabs: ITab[]; } type Vnode = m.Vnode; @@ -30,7 +30,7 @@ export default class TabHeader extends MithrilTsxComponent { return (
+ {Cookie.get('token') + ? + + : + null + } ); } diff --git a/src/app/molecules/ProfileNavItem.tsx b/src/app/molecules/ProfileNavItem.tsx index 20e3342..82c24ea 100644 --- a/src/app/molecules/ProfileNavItem.tsx +++ b/src/app/molecules/ProfileNavItem.tsx @@ -39,7 +39,7 @@ export default class ProfileNavItem extends MithrilTsxComponent { > {`@${profile.profileData.name}`} {