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 ( - {text} + {text} ); } } 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 ( { placeholder={placeholder} required={required} oninput={oninput} + hidden={hidden} + style={style} + value={defaultOption} > { - options.map((option: string) => ( - {option} + options.map((option: any) => ( + optionValue && optionText + ? {option[optionText]} + : {option} )) } 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 ( - {tabs && tabs.map((tab: Tabs) => + {tabs && tabs.map((tab: ITab) => // tslint:disable-next-line:jsx-key void; } diff --git a/src/app/constants/CreateLogTabs.tsx b/src/app/constants/CreateLogTabs.tsx index 1902b91..5993247 100644 --- a/src/app/constants/CreateLogTabs.tsx +++ b/src/app/constants/CreateLogTabs.tsx @@ -9,15 +9,15 @@ */ import * as m from 'mithril'; -import { Tabs } from '../interfaces/Tabs'; +import { ITab } from '../interfaces/Tab'; import MarkdownViewer from '../atoms/MarkdownViewer'; -import { Log } from '../interfaces/Log'; +import { ILog } from '../interfaces/Log'; import MarkdownEditor from '../atoms/MarkdownEditor'; /** * The tab information used by the TabHeader and TabContent of the Log detail page. */ -const CreateLogTabs: Tabs[] = [ +const CreateLogTabs: ITab[] = [ { name: 'Edit', id: 'description', @@ -29,8 +29,8 @@ const CreateLogTabs: Tabs[] = [ { name: 'Preview', id: 'preview', - content: (log: Log): JSX.Element => ( - + content: (log: ILog): JSX.Element => ( + ) } ]; diff --git a/src/app/constants/LinkLogToRunColumns.tsx b/src/app/constants/LinkLogToRunColumns.tsx index 7de2c07..871604a 100644 --- a/src/app/constants/LinkLogToRunColumns.tsx +++ b/src/app/constants/LinkLogToRunColumns.tsx @@ -7,8 +7,8 @@ */ import * as m from 'mithril'; -import { Log } from '../interfaces/Log'; -import { format } from 'date-fns'; +import { ILog } from '../interfaces/Log'; +import { formatDateField } from '../utility/DateUtil'; /** * The columns for the Log table in LinklogToRun. @@ -17,60 +17,60 @@ const LinkLogToRunColumns = ( runNumber: number, linkAction: (logId: number, runNumber: number) => void ): any[] => [ - { - header: 'Action', - accessor: null, - cell: (row: Log): JSX.Element => ( - linkAction(row.logId, runNumber)} - data-dismiss="modal" - > - Link - - ) - }, - { - header: 'Log id', - accessor: 'logId' - }, - { - header: 'Title', - accessor: 'title' - }, - { - header: 'Sub-type', - accessor: 'subtype', - cell: (row: Log): JSX.Element | string => ( - row.subtype === 'run' ? - ( - - {row.subtype} - - ) - : row.subtype - ) - }, - { - header: 'Origin', - accessor: 'origin', - cell: (row: Log): JSX.Element | string => ( - row.origin === 'human' ? - ( - - {row.origin} - - ) - : row.origin - ) - }, - { - header: 'Creation time', - accessor: 'creationTime', - cell: (row: Log): string => (row.creationTime ? format(row.creationTime, 'HH:mm:ss DD/MM/YYYY') : 'Unkown') - } -]; + { + header: 'Action', + accessor: null, + cell: (row: ILog): JSX.Element => ( + linkAction(row.logId, runNumber)} + data-dismiss="modal" + > + Link + + ) + }, + { + header: 'Log id', + accessor: 'logId' + }, + { + header: 'Title', + accessor: 'title' + }, + { + header: 'Sub-type', + accessor: 'subtype', + cell: (row: ILog): JSX.Element | string => ( + row.subtype === 'run' ? + ( + + {row.subtype} + + ) + : row.subtype + ) + }, + { + header: 'Origin', + accessor: 'origin', + cell: (row: ILog): JSX.Element | string => ( + row.origin === 'human' ? + ( + + {row.origin} + + ) + : row.origin + ) + }, + { + header: 'Creation time', + accessor: 'creationTime', + cell: (row: ILog): string => (row.creationTime ? formatDateField(row.creationTime) : 'Unkown') + } + ]; type LinkLogToRunColumns = typeof LinkLogToRunColumns; export default LinkLogToRunColumns; diff --git a/src/app/constants/LinkRunToLogColumns.tsx b/src/app/constants/LinkRunToLogColumns.tsx index ab41d86..4b7899b 100644 --- a/src/app/constants/LinkRunToLogColumns.tsx +++ b/src/app/constants/LinkRunToLogColumns.tsx @@ -7,8 +7,8 @@ */ import * as m from 'mithril'; -import { format } from 'date-fns'; -import { Run } from '../interfaces/Run'; +import { IRun } from '../interfaces/Run'; +import { formatDateField } from '../utility/DateUtil'; /** * The columns for the Log table in LinklogToRun. @@ -20,7 +20,7 @@ const LinkRunToLogColumns = ( { header: 'Action', accessor: null, - cell: (row: Run): JSX.Element => ( + cell: (row: IRun): JSX.Element => ( (row.timeO2Start ? format(row.timeO2Start, 'HH:mm:ss DD/MM/YYYY') : 'Unkown') + accessor: 'o2StartTime', + cell: (row: IRun): string => (row.O2StartTime ? formatDateField(row.O2StartTime) : 'Unkown') }, { header: 'Time O\xB2 end', - accessor: 'timeO2End', - cell: (row: Run): string => (row.timeO2End ? format(row.timeO2End, 'HH:mm:ss DD/MM/YYYY') : 'Unkown') + accessor: 'o2EndTime', + cell: (row: IRun): string => + (row.O2EndTime ? formatDateField(row.O2EndTime) : 'Run In Progress') }, { header: 'Time trg start', - accessor: 'timeTrgStart', - cell: (row: Run): string => (row.timeTrgStart ? format(row.timeTrgStart, 'HH:mm:ss DD/MM/YYYY') : 'Unkown') + accessor: 'trgStartTime', + cell: (row: IRun): string => (row.TrgStartTime ? formatDateField(row.TrgStartTime) : 'Unkown') }, { header: 'Time trg end', - accessor: 'timeTrgEnd', - cell: (row: Run): string => (row.timeTrgEnd ? format(row.timeTrgEnd, 'HH:mm:ss DD/MM/YYYY') : 'Unkown') + accessor: 'trgEndTime', + cell: (row: IRun): string => + (row.TrgEndTime ? formatDateField(row.TrgEndTime) : 'Run In Progress') } ]; diff --git a/src/app/constants/LogColumns.tsx b/src/app/constants/LogColumns.tsx index 1396bd3..a0d246d 100644 --- a/src/app/constants/LogColumns.tsx +++ b/src/app/constants/LogColumns.tsx @@ -7,8 +7,8 @@ */ import * as m from 'mithril'; -import { Log } from '../interfaces/Log'; -import { format } from 'date-fns'; +import { ILog } from '../interfaces/Log'; +import { formatDateField } from '../utility/DateUtil'; /** * The columns used by the Table that holds Log entities. @@ -21,7 +21,7 @@ const LogColumns: any[] = [ { header: 'Title', accessor: 'title', - cell: (row: Log): JSX.Element => ( + cell: (row: ILog): JSX.Element => ( {row.title} @@ -30,20 +30,24 @@ const LogColumns: any[] = [ { header: 'Sub-type', accessor: 'subtype', - cell: (row: Log): JSX.Element | string => ( + cell: (row: ILog): JSX.Element | string => ( row.subtype === 'run' ? ( {row.subtype} ) - : row.subtype + : ( + + {row.subtype} + + ) ) }, { header: 'Origin', accessor: 'origin', - cell: (row: Log): JSX.Element | string => ( + cell: (row: ILog): JSX.Element | string => ( row.origin === 'human' ? ( @@ -51,22 +55,22 @@ const LogColumns: any[] = [ ) : row.origin === 'process' ? - ( - - {row.origin} - - ) : row.origin + ( + + {row.origin} + + ) : row.origin ) }, { header: 'Creation time', accessor: 'creationTime', - cell: (row: Log): string => (row.creationTime ? format(row.creationTime, 'HH:mm:ss DD/MM/YYYY') : 'Unkown') + cell: (row: ILog): string => (row.creationTime ? formatDateField(row.creationTime) : 'Unkown') }, { header: 'Author', accessor: 'user', - cell: (row: Log): string => (row.user ? row.user.userId.toString() : 'Unknown') + cell: (row: ILog): string => (row.user ? row.user.name : 'Unknown') } ]; diff --git a/src/app/constants/LogDescription.tsx b/src/app/constants/LogDescription.tsx index 88fe154..abdee74 100644 --- a/src/app/constants/LogDescription.tsx +++ b/src/app/constants/LogDescription.tsx @@ -9,23 +9,23 @@ */ import * as m from 'mithril'; -import { Description } from '../interfaces/Description'; -import { format } from 'date-fns'; -import { Log } from '../interfaces/Log'; +import { IDescription } from '../interfaces/Description'; +import { ILog } from '../interfaces/Log'; +import { formatDateField } from '../utility/DateUtil'; /** * The tab information used by the TabHeader and TabContent of the Log detail page. */ -const LogDescription: Description[] = [ +const LogDescription: IDescription[] = [ { label: 'Log id', - value: (log: Log): number => { + value: (log: ILog): number => { return log.logId; } }, { label: 'Subtype', - value: (log: Log): JSX.Element => ( + value: (log: ILog): JSX.Element => ( ( + value: (log: ILog): JSX.Element => ( { - return format(log.creationTime, 'HH:mm:ss DD/MM/YYYY'); + value: (log: ILog): string => { + return formatDateField(log.creationTime); } }, { label: 'Author', - value: (log: Log): number => { + value: (log: ILog): string => { return log.user && - log.user.userId; + log.user.name; } } ]; diff --git a/src/app/constants/MarkdownHelpText.tsx b/src/app/constants/MarkdownHelpText.tsx index a77c911..8a4676e 100644 --- a/src/app/constants/MarkdownHelpText.tsx +++ b/src/app/constants/MarkdownHelpText.tsx @@ -9,9 +9,10 @@ * It explains the basics of markdown, it also gives a link that * redirects to a website with a more detailed explanation. */ +import { APPLICATION_NAME } from './constants'; const MarkdownHelpText: string = - `Jiskefet uses markdown for text formatting and editing. + `${APPLICATION_NAME} uses markdown for text formatting and editing. These are the basic techniques. For a more detailed explanation [go here](https://guides.github.com/features/mastering-markdown/). diff --git a/src/app/constants/RunColumns.tsx b/src/app/constants/RunColumns.tsx index 9198058..89caf70 100644 --- a/src/app/constants/RunColumns.tsx +++ b/src/app/constants/RunColumns.tsx @@ -7,8 +7,8 @@ */ import * as m from 'mithril'; -import { format } from 'date-fns'; -import { Run } from '../interfaces/Run'; +import { IRun } from '../interfaces/Run'; +import { formatDateField } from '../utility/DateUtil'; /** * The columns used by the Table that holds Run entities. @@ -17,7 +17,7 @@ const RunColumns = [ { header: 'Run number', accessor: 'runNumber', - cell: (row: Run): JSX.Element => ( + cell: (row: IRun): JSX.Element => ( {row.runNumber} @@ -25,23 +25,24 @@ const RunColumns = [ }, { header: 'Time O\xB2 start', - accessor: 'timeO2Start', - cell: (row: Run): string => (row.timeO2Start ? format(row.timeO2Start, 'HH:mm:ss DD/MM/YYYY') : 'Unkown') + accessor: 'o2StartTime', + cell: (row: IRun): string => (row.O2StartTime ? formatDateField(row.O2StartTime) : 'Unkown') }, { header: `Time O\xB2 end`, - accessor: 'timeO2End', - cell: (row: Run): string => (row.timeO2End ? format(row.timeO2End, 'HH:mm:ss DD/MM/YYYY') : 'Unkown') + accessor: 'o2EndTime', + cell: (row: IRun): string => (row.O2EndTime ? formatDateField(row.O2EndTime) : 'Run In Progress') }, { header: 'Time trg start', - accessor: 'timeTrgStart', - cell: (row: Run): string => (row.timeTrgStart ? format(row.timeTrgStart, 'HH:mm:ss DD/MM/YYYY') : 'Unkown') + accessor: 'trgStartTime', + cell: (row: IRun): string => (row.TrgStartTime ? formatDateField(row.TrgStartTime) : 'Unkown') }, { header: 'Time trg end', - accessor: 'timeTrgEnd', - cell: (row: Run): string => (row.timeTrgEnd ? format(row.timeTrgEnd, 'HH:mm:ss DD/MM/YYYY') : 'Unkown') + accessor: 'trgEndTime', + cell: (row: IRun): string => + (row.TrgEndTime ? formatDateField(row.TrgEndTime) : 'Run In Progress') }, { header: 'Activity id', diff --git a/src/app/constants/RunDescription.tsx b/src/app/constants/RunDescription.tsx index 79a4931..10db9bb 100644 --- a/src/app/constants/RunDescription.tsx +++ b/src/app/constants/RunDescription.tsx @@ -9,53 +9,51 @@ */ import * as m from 'mithril'; -import { Description } from '../interfaces/Description'; -import { Run } from '../interfaces/Run'; +import { IDescription } from '../interfaces/Description'; +import { IRun } from '../interfaces/Run'; import { formatDateField } from '../utility/DateUtil'; /** * The tab information used by the TabHeader and TabContent of the Log detail page. */ -const RunDescription: Description[] = [ +const RunDescription: IDescription[] = [ { label: 'Run number', - value: (run: Run): number => { + value: (run: IRun): number => { return run.runNumber; } }, { - label: 'Time O² start', - value: (run: Run): string | Date => { - return formatDateField(run.timeO2Start); + label: 'Time O\xB2 start', + value: (run: IRun): string | Date => { + return formatDateField(run.O2StartTime); } }, { - label: 'Time O² end', - value: (run: Run): string | Date => { - return formatDateField(run.timeO2Start); - } + label: 'Time O\xB2 end', + value: (run: IRun): string | Date => + (run.O2EndTime ? formatDateField(run.O2EndTime) : 'Run In Progress') }, { label: 'Time TRG start', - value: (run: Run): string | Date => { - return formatDateField(run.timeO2Start); + value: (run: IRun): string | Date => { + return formatDateField(run.TrgStartTime); } }, { label: 'Time TRG end', - value: (run: Run): string | Date => { - return formatDateField(run.timeO2Start); - } + value: (run: IRun): string | Date => + (run.TrgEndTime ? formatDateField(run.TrgEndTime) : 'Run In Progress') }, { label: 'Run type', - value: (run: Run): string[] => { + value: (run: IRun): string[] => { return run.runType; } }, { label: 'Run quality', - value: (run: Run): JSX.Element => ( + value: (run: IRun): JSX.Element => ( {run.runQuality} @@ -63,43 +61,43 @@ const RunDescription: Description[] = [ }, { label: 'Number of detectors', - value: (run: Run): number => { + value: (run: IRun): number => { return run.nDetectors; } }, { label: 'Number of FLP\'s', - value: (run: Run): number => { + value: (run: IRun): number => { return run.nFlps; } }, { label: 'Number of EPN\'s', - value: (run: Run): number => { + value: (run: IRun): number => { return run.nEpns; } }, { label: 'Number of timeframes', - value: (run: Run): number => { + value: (run: IRun): number => { return run.nTimeframes; } }, { label: 'Number of sub-timeframes', - value: (run: Run): number => { + value: (run: IRun): number => { return run.nSubtimeframes; } }, { label: 'Bytes read out', - value: (run: Run): number => { + value: (run: IRun): number => { return run.bytesReadOut; } }, { label: 'Bytes timeframe builder', - value: (run: Run): number => { + value: (run: IRun): number => { return run.bytesTimeframeBuilder; } }, diff --git a/src/app/constants/SubsystemOverviewColumns.tsx b/src/app/constants/SubsystemOverviewColumns.tsx index 4210560..b27c8bb 100644 --- a/src/app/constants/SubsystemOverviewColumns.tsx +++ b/src/app/constants/SubsystemOverviewColumns.tsx @@ -6,7 +6,7 @@ * copied verbatim in the file "LICENSE" */ -import { SubsystemOverview } from '../interfaces/SubsystemOverview'; +import { ISubsystemOverview } from '../interfaces/SubsystemOverview'; import * as m from 'mithril'; /** @@ -24,7 +24,7 @@ const SubsystemOverviewColumns: any[] = [ { header: 'Last Log Entry', accessor: 'lastLog', - cell: (row: SubsystemOverview): JSX.Element => ( + cell: (row: ISubsystemOverview): JSX.Element => ( {row.lastLog} diff --git a/src/app/constants/apiUrls.ts b/src/app/constants/apiUrls.ts index 670bfac..af6d384 100644 --- a/src/app/constants/apiUrls.ts +++ b/src/app/constants/apiUrls.ts @@ -6,40 +6,49 @@ * copied verbatim in the file "LICENSE" */ +import { BASE_URL } from './constants'; + /** * This file contains all the api calls. * Each call should be prefixed with an HTTP request method (e.g. get/post/put/patch/delete) */ -const baseUrl = process.env.API_URL; - // Subsystem -export const getSubsystems = (): string => `${baseUrl}subsystems`; -export const getSubsystem = (id: string | number): string => `${baseUrl}subsystems/${id}`; -export const getSubsystemOverviews = (query?: string): string => `${baseUrl}overview${query ? `?${query}` : ''}`; -export const getSubsystemPermissions = (userId: number): string => `${baseUrl}users/${userId}/tokens`; -export const postToken = (userId: number): string => (`${baseUrl}users/${userId}/tokens`); +export const getSubsystems = (): string => `${BASE_URL}subsystems`; +export const getSubsystem = (id: string | number): string => `${BASE_URL}subsystems/${id}`; +export const getSubsystemOverviews = (query?: string): string => `${BASE_URL}overview${query ? `?${query}` : ''}`; +export const getSubsystemPermissions = (userId: number): string => `${BASE_URL}users/${userId}/tokens`; +export const postToken = (userId: number): string => (`${BASE_URL}users/${userId}/tokens`); // Attachment -export const getAttachmentsByLog = (logId: number): string => `${baseUrl}attachments/${logId}/logs`; -export const postAttachment = (): string => `${baseUrl}attachments`; +export const getAttachmentsByLog = (logId: number): string => `${BASE_URL}attachments/${logId}/logs`; // Auth -export const getProfile = (): string => `${baseUrl}user/profile`; +export const getProfile = (): string => `${BASE_URL}user/profile`; export const getAuthorize = (authGrant: string): string => `${process.env.API_URL}auth?grant=${authGrant}`; // User -export const getUser = (id: string | number): string => `${baseUrl}users/${id}`; +export const getUser = (id: string | number): string => `${BASE_URL}users/${id}`; export const getLogsForUser = (id: string | number, query?: string): string => - `${baseUrl}users/${id}/logs${query ? `?${query}` : ''}`; + `${BASE_URL}users/${id}/logs${query ? `?${query}` : ''}`; // Run -export const getRuns = (query?: string): string => `${baseUrl}runs${query ? `?${query}` : ''}`; -export const getRun = (id: string | number): string => `${baseUrl}runs/${id}`; -export const linkLogToRunUrl = (runNumber: number): string => `${baseUrl}runs/${runNumber}/logs`; +export const getRuns = (query?: string): string => `${BASE_URL}runs${query ? `?${query}` : ''}`; +export const getRun = (id: string | number): string => `${BASE_URL}runs/${id}`; +export const linkLogToRunUrl = (runNumber: number): string => `${BASE_URL}runs/${runNumber}/logs`; // Log -export const getLogs = (query?: string): string => `${baseUrl}logs${query ? `?${query}` : ''}`; -export const getLog = (id: string | number): string => `${baseUrl}logs/${id}`; -export const linkRunToLogUrl = (runNumber: number): string => `${baseUrl}logs/${runNumber}/runs`; -export const postLog = (): string => `${baseUrl}logs`; +export const getLogs = (query?: string): string => `${BASE_URL}logs${query ? `?${query}` : ''}`; +export const getLog = (id: string | number): string => `${BASE_URL}logs/${id}`; +export const linkRunToLogUrl = (runNumber: number): string => `${BASE_URL}logs/${runNumber}/runs`; +export const postLog = (): string => `${BASE_URL}logs`; +export const postAttachment = (logId: string | number): string => `${BASE_URL}logs/${logId}/attachments`; + +// Tag +export const getTags = (): string => `${BASE_URL}tags`; +export const getTag = (id: string | number): string => `${BASE_URL}tags/${id}`; +export const getTagsForLog = (logId: string | number): string => `${BASE_URL}tags/${logId}/logs`; +export const getTagsForRun = (runId: string | number): string => `${BASE_URL}tags/${runId}/runs`; +export const postTag = (): string => `${BASE_URL}tags`; +export const updateTag = (id: string | number): string => `${BASE_URL}tags/${id}`; +export const deleteTage = (id: string | number): string => `${BASE_URL}tags/${id}`; diff --git a/src/app/interfaces/Attachment.ts b/src/app/interfaces/Attachment.ts index 6e7a5ba..d317f59 100644 --- a/src/app/interfaces/Attachment.ts +++ b/src/app/interfaces/Attachment.ts @@ -6,30 +6,27 @@ * copied verbatim in the file "LICENSE" */ -import { Log } from './Log'; +import { ILog } from './Log'; /** * Interface with the fields for fetching one or more Attachment entries. */ -export interface Attachment { +export interface IAttachment { fileId?: number; - log?: Log; - creationTime?: string; - title: string; + title?: string; + fileName: string; fileMime: string; fileData: string; - fileMD5?: string; + log?: ILog; } /** * Interface with the fields for creating a Attachment entry. */ -export interface AttachmentCreate { - fileId?: number; - log?: Log; - creationTime?: string; - title: string; +export interface IAttachmentCreate { + title?: string; + fileName: string; fileMime: string; fileData: string; - fileMD5?: string; + log?: ILog; } diff --git a/src/app/interfaces/Auth.ts b/src/app/interfaces/Auth.ts index 628e7c0..6bbb597 100644 --- a/src/app/interfaces/Auth.ts +++ b/src/app/interfaces/Auth.ts @@ -10,6 +10,6 @@ /** * Response object after calling api/auth endpoint. */ -export interface AuthorizeResponse { +export interface IAuthorizeResponse { token: string; } diff --git a/src/app/interfaces/CollapsableItem.ts b/src/app/interfaces/CollapsableItem.ts index 44cf82c..2883660 100644 --- a/src/app/interfaces/CollapsableItem.ts +++ b/src/app/interfaces/CollapsableItem.ts @@ -9,7 +9,7 @@ /** * An item that has a collapsable state. */ -export interface CollapsableItem { +export interface ICollapsableItem { /** * The unique id of the item. */ diff --git a/src/app/interfaces/Column.ts b/src/app/interfaces/Column.ts index 4f74adb..39d8375 100644 --- a/src/app/interfaces/Column.ts +++ b/src/app/interfaces/Column.ts @@ -9,7 +9,7 @@ /** * A column/table header for a table. */ -export interface Column { +export interface IColumn { /** * The column header name to be displayed in the table. */ diff --git a/src/app/interfaces/Description.ts b/src/app/interfaces/Description.ts index 3f277d2..0f7339f 100644 --- a/src/app/interfaces/Description.ts +++ b/src/app/interfaces/Description.ts @@ -9,7 +9,7 @@ /** * Interface with the fields for fetching one or more Attachment entries. */ -export interface Description { +export interface IDescription { label: string; value: any; } diff --git a/src/app/interfaces/Event.ts b/src/app/interfaces/Event.ts index acdae12..3148675 100644 --- a/src/app/interfaces/Event.ts +++ b/src/app/interfaces/Event.ts @@ -10,7 +10,7 @@ * Event that is given when calling functions on input elements. * e.g. event.target.value} /> */ -export interface Event { +export interface IEvent { target: HTMLInputElement; preventDefault: () => void; } diff --git a/src/app/interfaces/Filter.ts b/src/app/interfaces/Filter.ts index ccfb1a7..8a25bb3 100644 --- a/src/app/interfaces/Filter.ts +++ b/src/app/interfaces/Filter.ts @@ -12,7 +12,7 @@ import { OrderDirection } from '../enums/OrderDirection'; * Object containing (possibly) multiple filters by key - value pair. * Example: { searchterm: 'foo', pageNumber: 1 } */ -export interface FilterState { +export interface IFilterState { [key: string]: FilterValue; } diff --git a/src/app/interfaces/HttpError.ts b/src/app/interfaces/HttpError.ts index 9b3f58b..ae40461 100644 --- a/src/app/interfaces/HttpError.ts +++ b/src/app/interfaces/HttpError.ts @@ -1,3 +1,5 @@ +import { IResponseObject } from './ResponseObject'; + /* * Copyright (C) 2018 Amsterdam University of Applied Sciences (AUAS) * @@ -10,9 +12,17 @@ * An error received from, for example, a failed API call. * Returned in the catch portion of the Promise request(). */ -export interface HttpError { - error: string; - statusCode: number; - message: string; - stack?: string; +export interface IHttpError extends IResponseObject { + error: { + error: string; + code: number; + message: string; + details?: Array>; + innerError?: IInnerError; + }; +} + +export interface IInnerError { + code: string; + innerError: IInnerError; } diff --git a/src/app/interfaces/Log.ts b/src/app/interfaces/Log.ts index 1d10ba3..df1933d 100644 --- a/src/app/interfaces/Log.ts +++ b/src/app/interfaces/Log.ts @@ -6,35 +6,39 @@ * copied verbatim in the file "LICENSE" */ -import { Attachment } from './Attachment'; -import { User } from './User'; -import { Run } from './Run'; +import { IAttachmentCreate, IAttachment } from './Attachment'; +import { IUser } from './User'; +import { IRun } from './Run'; /** * Interface with the fields for fetching one or more Log entries. */ -export interface Log { +export interface ILog { logId: number; subtype: string; userId?: number; origin: string; creationTime: string; title: string; - text: string; - user: User; - runs?: Run[]; - attachments?: Attachment[]; + body: string; + user: IUser; + runs?: IRun[]; + attachments?: IAttachment[]; + comments?: ILog[]; + commentFkRootLogId?: number; + commentFkParentLogId?: number; } /** * Interface with the fields for creating a Log entry. */ -export interface LogCreate { - subtype: string; - origin: string; +export interface ILogCreate { + subtype?: string; + origin?: string; title: string; - text: string; + body: string; user: number; - runs: any[]; - attachments?: Attachment[]; + parentId?: number; + run?: number; + attachments?: IAttachmentCreate[]; } diff --git a/src/app/interfaces/ResponseObject.ts b/src/app/interfaces/ResponseObject.ts index 038ed8d..fd0b1de 100644 --- a/src/app/interfaces/ResponseObject.ts +++ b/src/app/interfaces/ResponseObject.ts @@ -6,12 +6,18 @@ * copied verbatim in the file "LICENSE" */ - /** - * Interface for the API Response Object with a single item - */ -export interface ResponseObject { +/** + * Interface of information for every API response + */ +export interface IResponseObject { apiVersion: string; - meta?: Meta; + meta?: IMeta; +} + +/** + * Interface to standardize the response of a API object containing a single item + */ +export interface ISuccessObject extends IResponseObject { data: { [key: string]: any; item: T; @@ -19,20 +25,17 @@ export interface ResponseObject { } /** - * Interface for the API Response Object with a collection of items + * Interface to standardize the response of a API object containing multiple items */ -export interface CollectionResponseObject { - apiVersion: string; - meta?: Meta; +export interface ICollectionSuccessObject extends IResponseObject { data: { [key: string]: any; items: T[]; }; } - /** - * Interface for key value pair + * Interface for Meta object */ -export interface Meta { +export interface IMeta { [key: string]: string; } diff --git a/src/app/interfaces/Run.ts b/src/app/interfaces/Run.ts index 025de50..aa5f03f 100644 --- a/src/app/interfaces/Run.ts +++ b/src/app/interfaces/Run.ts @@ -6,17 +6,17 @@ * copied verbatim in the file "LICENSE" */ -import { Log } from './Log'; +import { ILog } from './Log'; /** * Interface with the fields for fetching one or more Log entries. */ -export interface Run { +export interface IRun { runNumber: number; - timeO2Start: Date | string; - timeTrgStart: Date | string; - timeTrgEnd: Date | string; - timeO2End: Date | string; + O2StartTime: Date | string; + TrgStartTime: Date | string; + TrgEndTime: Date | string; + O2EndTime: Date | string; runType: string[]; runQuality: string[]; activityId: string; @@ -27,5 +27,5 @@ export interface Run { nSubtimeframes: number; bytesReadOut: number; bytesTimeframeBuilder: number; - logs: Log[]; + logs: ILog[]; } diff --git a/src/app/interfaces/Setting.ts b/src/app/interfaces/Setting.ts index cc8bde6..da34836 100644 --- a/src/app/interfaces/Setting.ts +++ b/src/app/interfaces/Setting.ts @@ -9,7 +9,7 @@ /** * Interface with setting fields in order to setup the front end authentication */ -export interface Setting { +export interface ISetting { USE_CERN_SSO: string; AUTH_URL: string; [index: string]: string | boolean | number; diff --git a/src/app/interfaces/SubSytem.ts b/src/app/interfaces/SubSytem.ts index 14b4975..81bfa00 100644 --- a/src/app/interfaces/SubSytem.ts +++ b/src/app/interfaces/SubSytem.ts @@ -6,7 +6,7 @@ * copied verbatim in the file "LICENSE" */ -export class Subsystem { +export class ISubsystem { subsystemId: number; subsystemName: string; } diff --git a/src/app/interfaces/SubsystemOverview.ts b/src/app/interfaces/SubsystemOverview.ts index 567ef95..579dbc8 100644 --- a/src/app/interfaces/SubsystemOverview.ts +++ b/src/app/interfaces/SubsystemOverview.ts @@ -9,7 +9,7 @@ /** * Interface with the fields for fetching a SubsystemOverview. */ -export interface SubsystemOverview { +export interface ISubsystemOverview { subsystemName: string; logs: number; userId: string; diff --git a/src/app/interfaces/SubsystemPermission.ts b/src/app/interfaces/SubsystemPermission.ts index 8a0a79b..a254594 100644 --- a/src/app/interfaces/SubsystemPermission.ts +++ b/src/app/interfaces/SubsystemPermission.ts @@ -9,7 +9,7 @@ /** * Interface with the fields for creating a Token entry. */ -export interface SubsystemPermissionCreate { +export interface ISubsystemPermissionCreate { user: number; // userId subsystem: number; // subsystemId subSystemTokenDescription: string; @@ -20,7 +20,7 @@ export interface SubsystemPermissionCreate { /** * Interface with the fields to display */ -export interface SubsystemPermission { +export interface ISubsystemPermission { subsystemPermissionId: number; subSystemTokenDescription: string; } @@ -28,6 +28,6 @@ export interface SubsystemPermission { /** * Response object when a token is successfully created. */ -export interface SubsystemToken extends SubsystemPermissionCreate { +export interface ISubsystemToken extends ISubsystemPermissionCreate { subSystemHash: string; } diff --git a/src/app/interfaces/Tabs.ts b/src/app/interfaces/Tabs.ts deleted file mode 100644 index 5e2d962..0000000 --- a/src/app/interfaces/Tabs.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2018 Amsterdam University of Applied Sciences (AUAS) - * - * This software is distributed under the terms of the - * GNU General Public Licence version 3 (GPL) version 3, - * copied verbatim in the file "LICENSE" - */ - - /** - * Interface for a tab. - */ -export interface Tabs { - /** - * The name of the tab - */ - name: string; - - /** - * The id of the tab - */ - id: string; - - /** - * If the tab is active i.e. selected or not. - */ - active?: boolean; - - /** - * Function to create the content of the tab body. - */ - content: (param?: object | string) => JSX.Element | string; -} diff --git a/src/app/interfaces/User.ts b/src/app/interfaces/User.ts index 84872c3..5bca1d1 100644 --- a/src/app/interfaces/User.ts +++ b/src/app/interfaces/User.ts @@ -9,9 +9,10 @@ /** * Interface with the fields for creating a Token entry. */ -export interface User { +export interface IUser { userId: number; externalUserId: number; samsId: number; token: string; + name: string; } diff --git a/src/app/molecules/Attachment.tsx b/src/app/molecules/Attachment.tsx index 41890c1..8a1029e 100644 --- a/src/app/molecules/Attachment.tsx +++ b/src/app/molecules/Attachment.tsx @@ -14,10 +14,11 @@ import { selectAttachmentToBeCreated } from '../redux/ducks/attachment/selectors import { saveAttachment, fetchAttachmentsByLog } from '../redux/ducks/attachment/operations'; import { selectCurrentLog, selectLogToBeCreated } from '../redux/ducks/log/selectors'; import { setLogToBeCreated } from '../redux/ducks/log/actions'; -import { LogCreate } from '../interfaces/Log'; -import { Event } from '../interfaces/Event'; +import { ILogCreate } from '../interfaces/Log'; +import { IEvent } from '../interfaces/Event'; import Label from '../atoms/Label'; import Input from '../atoms/Input'; +import { FILE_UPLOAD_LIMIT } from '../constants/constants'; interface Attrs { /** @@ -37,29 +38,29 @@ interface Attrs { type Vnode = m.Vnode; export default class AttachmentComponent extends MithrilTsxComponent { - private isExistingItem: boolean; + private isCreateLog: boolean; private hasChosenAttachment: boolean; private maxFileSize: number; constructor(vnode: Vnode) { super(); - this.isExistingItem = vnode.attrs.isExistingItem; + this.isCreateLog = vnode.attrs.isExistingItem; this.hasChosenAttachment = false; - this.maxFileSize = 5000000; // Equals to 5MB + this.maxFileSize = FILE_UPLOAD_LIMIT * 1024 * 1024; } /** * Gets called after User selects a file. * @param event The event of having selected a file. */ - getSelectedFiles = (event: Event) => { + getSelectedFiles = (event: IEvent) => { const files = (event.target as HTMLInputElement).files as FileList; const maxSizeLabel = document.getElementById('maximum-size-label') as HTMLElement; if (files[0].size > this.maxFileSize) { maxSizeLabel.hidden = false; } else { maxSizeLabel.hidden = true; - this.read(files[0], this.isExistingItem); + this.read(files[0], this.isCreateLog); } } @@ -90,10 +91,10 @@ export default class AttachmentComponent extends MithrilTsxComponent { /** * Saves the base64 encoded string into the state. * @param base64String The file base64 encoded string. - * @param name The name of the file. + * @param fileName The name of the file. * @param isExistingItem Whether the attachment is added to an existing Item. */ - saveAttachmentState = (base64String: string, name: string, isExistingItem: boolean) => { + saveAttachmentState = (base64String: string, fileName: string, isExistingItem: boolean) => { const fileMime = base64String.substring( 'data:'.length, base64String.indexOf(';base64,') ); @@ -105,7 +106,7 @@ export default class AttachmentComponent extends MithrilTsxComponent { if (isExistingItem) { log = currentLog; const attachmentToBeCreated = { - title: name, + fileName, fileMime, fileData, ...(log && { log }) @@ -115,10 +116,9 @@ export default class AttachmentComponent extends MithrilTsxComponent { // Check if attachment was not already added (needs to be adjusted for multiple file upload) if (logToBeCreated && (logToBeCreated.attachments === undefined || logToBeCreated.attachments.length > 0)) { - logToBeCreated.attachments = new Array(); - logToBeCreated.attachments.push({ title: name, fileMime, fileData }); - store.dispatch(setLogToBeCreated(logToBeCreated as LogCreate)); + logToBeCreated.attachments.push({ fileName, fileMime, fileData }); + store.dispatch(setLogToBeCreated(logToBeCreated as ILogCreate)); } } } @@ -131,7 +131,7 @@ export default class AttachmentComponent extends MithrilTsxComponent { const attachment = selectAttachmentToBeCreated(state); const currentLog = selectCurrentLog(state); if (attachment && this.hasChosenAttachment && currentLog) { - await store.dispatch(saveAttachment(attachment)) + await store.dispatch(saveAttachment(attachment, currentLog.logId)) .then(async () => { // Reset the input form const fileInput = document.getElementById('addAttachment') as HTMLFormElement; @@ -155,7 +155,7 @@ export default class AttachmentComponent extends MithrilTsxComponent { return ( - Maximum file size is 5MB! Please select a smaller file. + Filesize exceeds the maximum limit of {FILE_UPLOAD_LIMIT}MB! Please select a smaller file. { style="max-width:40%; padding:10px 10px 10px 0px;" hidden={hideImagePreview} /> - + ; @@ -70,7 +70,7 @@ export default class Filter extends MithrilTsxComponent { class="form-control form-control-sm" id={inputField.name} {...{ - [inputField.event]: (event: Event) => { + [inputField.event]: (event: IEvent) => { onEvent(inputField.name, event.target.value); } }} diff --git a/src/app/molecules/FormGroup.tsx b/src/app/molecules/FormGroup.tsx index 65bdfdf..2e7e524 100644 --- a/src/app/molecules/FormGroup.tsx +++ b/src/app/molecules/FormGroup.tsx @@ -13,6 +13,7 @@ interface Attrs { formGroupStyle?: string; label?: JSX.Element; field: JSX.Element; + hidden?: boolean; } type Vnode = m.Vnode; @@ -20,9 +21,9 @@ type Vnode = m.Vnode; export default class FormGroup extends MithrilTsxComponent { view(vnode: Vnode) { - const { formGroupStyle, label, field } = vnode.attrs; + const { formGroupStyle, label, field, hidden } = vnode.attrs; return ( - + {label} {field} diff --git a/src/app/molecules/NavBar.tsx b/src/app/molecules/NavBar.tsx index c4c9797..ff34f0d 100644 --- a/src/app/molecules/NavBar.tsx +++ b/src/app/molecules/NavBar.tsx @@ -14,6 +14,7 @@ import { store } from '../redux/configureStore'; import { toggleSidebar } from '../redux/ducks/ui/actions'; import { fetchProfile } from '../redux/ducks/auth/operations'; import { selectProfile } from '../redux/ducks/auth/selectors'; +import NavItem from '../atoms/NavItem'; export default class NavBar extends MithrilTsxComponent<{}> { toggleSidebar = () => { @@ -30,11 +31,15 @@ export default class NavBar extends MithrilTsxComponent<{}> { const profile = selectProfile(store.getState()); return ( - + @@ -46,7 +51,7 @@ export default class NavBar extends MithrilTsxComponent<{}> { class="d-inline-block align-top jf-logo" alt="" /> - Jiskefet + {process.env.APPLICATION_NAME} {Cookie.get('token') ? @@ -68,6 +73,18 @@ export default class NavBar extends MithrilTsxComponent<{}> { } + {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 { > { + + + JIRA Board + + + { constructor() { @@ -25,9 +26,9 @@ export default class SideBar extends MithrilTsxComponent<{}> { - + - + ); } diff --git a/src/app/molecules/TabContainer.tsx b/src/app/molecules/TabContainer.tsx index 747a10d..6ca6b2d 100644 --- a/src/app/molecules/TabContainer.tsx +++ b/src/app/molecules/TabContainer.tsx @@ -11,6 +11,7 @@ import { MithrilTsxComponent } from 'mithril-tsx-component'; interface Attrs { titles: string[]; + disableds: string[]; } type Vnode = m.Vnode; @@ -29,13 +30,15 @@ export default class TabContainer extends MithrilTsxComponent { view(vnode: Vnode) { const { titles } = vnode.attrs; + const { disableds } = vnode.attrs; const children = vnode.children as any[]; return ( {titles.length > 0 && titles.map((title: string) => ( - + disableds.length > 0 && disableds.map((disabled: string) => ( + this.handleOnTabClick(e)} @@ -44,6 +47,7 @@ export default class TabContainer extends MithrilTsxComponent { {title} + )) ))} diff --git a/src/app/molecules/Table.tsx b/src/app/molecules/Table.tsx index 650f235..1c67751 100644 --- a/src/app/molecules/Table.tsx +++ b/src/app/molecules/Table.tsx @@ -8,7 +8,7 @@ import * as m from 'mithril'; import { MithrilTsxComponent } from 'mithril-tsx-component'; -import { Column } from '../interfaces/Column'; +import { IColumn } from '../interfaces/Column'; import TableHeader from '../atoms/TableHeader'; import { OrderDirection } from '../enums/OrderDirection'; @@ -21,7 +21,7 @@ interface Attrs { /** * The columns (table headers) for the table. */ - columns: Column[]; + columns: IColumn[]; /** * The optional css class for the table. */ @@ -40,7 +40,7 @@ export default class Table extends MithrilTsxComponent { /** * Returns the order direction for a column, based on orderBy and orderDirection. */ - getOrder = (column: Column, orderBy: string, orderDirection: OrderDirection): OrderDirection | null => { + getOrder = (column: IColumn, orderBy: string, orderDirection: OrderDirection): OrderDirection | null => { if (orderBy === column.accessor) { return orderDirection; } else { @@ -55,7 +55,7 @@ export default class Table extends MithrilTsxComponent { - {columns && columns.map((column: Column) => + {columns && columns.map((column: IColumn) => // tslint:disable-next-line:jsx-key { {data && data.map((row: object) => // tslint:disable-next-line:jsx-key - {columns.map((column: Column) => ( + {columns.map((column: IColumn) => ( { (column.cell ? diff --git a/src/app/organisms/CreateLog.tsx b/src/app/organisms/CreateLog.tsx index 09d8afc..ac281fc 100644 --- a/src/app/organisms/CreateLog.tsx +++ b/src/app/organisms/CreateLog.tsx @@ -8,16 +8,16 @@ import * as m from 'mithril'; import { MithrilTsxComponent } from 'mithril-tsx-component'; -import { Event } from '../interfaces/Event'; +import { IEvent } from '../interfaces/Event'; import Modal from '../atoms/Modal'; import MarkdownViewer from '../atoms/MarkdownViewer'; import MarkdownHelpText from '../constants/MarkdownHelpText'; import AttachmentComponent from '../molecules/Attachment'; import { selectProfile } from '../redux/ducks/auth/selectors'; import { store } from '../redux/configureStore'; -import { LogCreate } from '../interfaces/Log'; -import { createLog } from '../redux/ducks/log/operations'; -import { selectLogToBeCreated } from '../redux/ducks/log/selectors'; +import { ILogCreate, ILog } from '../interfaces/Log'; +import { createLog, fetchLog } from '../redux/ducks/log/operations'; +import { selectLogToBeCreated, selectCurrentLog } from '../redux/ducks/log/selectors'; import { clearLogToBeCreated, setLogToBeCreated } from '../redux/ducks/log/actions'; import MarkdownEditor from '../atoms/MarkdownEditor'; import TabContainer from '../molecules/TabContainer'; @@ -27,9 +27,11 @@ import Select from '../atoms/Select'; import FormGroup from '../molecules/FormGroup'; import Button, { ButtonType, ButtonClass } from '../atoms/Button'; import HttpErrorAlert from '../atoms/HttpErrorAlert'; +import EmojiPicker from '../atoms/EmojiPicker'; interface Attrs { runNumber?: number | undefined; + logNumber?: number | undefined; } type Vnode = m.Vnode; @@ -41,28 +43,59 @@ export default class CreateLog extends MithrilTsxComponent { } setValueForLogToBeCreated = (key: string, value: any) => { - let logToBeCreated = selectLogToBeCreated(store.getState()) as LogCreate | {}; + let logToBeCreated = selectLogToBeCreated(store.getState()) as ILogCreate | {}; if (!logToBeCreated) { logToBeCreated = {}; } logToBeCreated = { ...logToBeCreated, [key]: value }; if (logToBeCreated) { - store.dispatch(setLogToBeCreated(logToBeCreated as LogCreate)); + store.dispatch(setLogToBeCreated(logToBeCreated as ILogCreate)); } } - addToCreateLog = (event: Event) => { + fetchParentLog = async (logNumber: number): Promise => { + await store.dispatch(fetchLog(logNumber)); + return selectCurrentLog(store.getState()); + } + addToCreateLog = (event: IEvent) => { this.setValueForLogToBeCreated(event.target.id, event.target.value); } + // TODO: Refactor this + appendTitle = (content: string) => { + const logToBeCreated = selectLogToBeCreated(store.getState()) as ILogCreate; + if (logToBeCreated !== null) { + this.setValueForLogToBeCreated('title', logToBeCreated.title + content); + } else { + this.setValueForLogToBeCreated('title', content); + } + } + + appendDescription = (content: string) => { + const logToBeCreated = selectLogToBeCreated(store.getState()) as ILogCreate; + if (logToBeCreated !== null) { + this.setValueForLogToBeCreated('body', logToBeCreated.body + content); + } else { + this.setValueForLogToBeCreated('body', content); + } + } + addDescription = (content: string) => { - this.setValueForLogToBeCreated('text', content); + this.setValueForLogToBeCreated('body', content); } - async saveLog(runNumber: number | undefined) { - if (runNumber) { - this.setValueForLogToBeCreated('runs', runNumber); + async saveLog(runNumber: number | undefined, logNumber: number | undefined) { + + if (logNumber) { + // Create commentLog + this.setValueForLogToBeCreated('parentId', logNumber); + this.setValueForLogToBeCreated('subtype', 'comment'); + } else { + // Create runLog + this.setValueForLogToBeCreated('run', runNumber); + this.setValueForLogToBeCreated('subtype', 'run'); } + const profile = selectProfile(store.getState()); if (profile) { this.setValueForLogToBeCreated('user', profile.userData.userId); @@ -81,9 +114,9 @@ export default class CreateLog extends MithrilTsxComponent { return ( { + onsubmit={(event: IEvent) => { event.preventDefault(); - this.saveLog(vnode.attrs.runNumber); + this.saveLog(vnode.attrs.runNumber, vnode.attrs.logNumber); }} > @@ -101,15 +134,26 @@ export default class CreateLog extends MithrilTsxComponent { )} field={( - + + + + + + + + )} /> { name="subtype" required={true} oninput={this.addToCreateLog} - options={['run']} + options={vnode.attrs.logNumber ? ['comment'] : ['run']} /> )} /> + )} field={( { - - this.addDescription(content)} - /> + + + this.addDescription(content)} + /> + + + + diff --git a/src/app/organisms/CreateToken.tsx b/src/app/organisms/CreateToken.tsx index fd83e74..85ded2c 100644 --- a/src/app/organisms/CreateToken.tsx +++ b/src/app/organisms/CreateToken.tsx @@ -8,8 +8,7 @@ import * as m from 'mithril'; import { MithrilTsxComponent } from 'mithril-tsx-component'; -import { Event } from '../interfaces/Event'; -import { Subsystem } from '../interfaces/SubSytem'; +import { IEvent } from '../interfaces/Event'; import Table from '../molecules/Table'; import SubsystemPermissionColumns from '../constants/SubsystemPermissionColumns'; import SuccessMessage from '../atoms/SuccessMessage'; @@ -26,13 +25,17 @@ import { selectFetchingSubsystemPermissions, selectFetchingSubsystems } from '../redux/ducks/subsystem/selectors'; -import { SubsystemPermissionCreate } from '../interfaces/SubsystemPermission'; +import { ISubsystemPermissionCreate } from '../interfaces/SubsystemPermission'; import Spinner from '../atoms/Spinner'; import { fetchProfile } from '../redux/ducks/auth/operations'; import { selectProfile } from '../redux/ducks/auth/selectors'; import { fetchUser } from '../redux/ducks/user/operations'; import { UserProfile } from '../interfaces/UserProfile'; import Input from '../atoms/Input'; +import FormGroup from '../molecules/FormGroup'; +import Label from '../atoms/Label'; +import Select from '../atoms/Select'; +import Button, { ButtonType, ButtonClass } from '../atoms/Button'; export default class CreateToken extends MithrilTsxComponent<{}> { @@ -60,7 +63,7 @@ export default class CreateToken extends MithrilTsxComponent<{}> { isMember: true, editEorReason: true }; - store.dispatch(createToken(tokenToCreate as SubsystemPermissionCreate)); + store.dispatch(createToken(tokenToCreate as ISubsystemPermissionCreate)); event.target.reset(); // Clear the form. } @@ -74,7 +77,7 @@ export default class CreateToken extends MithrilTsxComponent<{}> { Create a new token - For a machine to make use of the jiskefet API, + For a machine to make use of the API, it needs to use an access token for authentication and authorization. This token can be generated here and is linked to your account. @@ -84,16 +87,16 @@ export default class CreateToken extends MithrilTsxComponent<{}> { { + onsubmit={(event: IEvent) => { event.preventDefault(); this.handleSubmit(event); }} > - - - Token description - - + + )} + field={( { className="form-control" required={true} /> - What's the token for? - - - - - Select subsystem: - - - - + + )} + field={( + + - Please select a subsystem. - - { - subsystems && subsystems.map((subsystem: Subsystem) => ( - - {subsystem.subsystemName} - - )) - } - - 0} - >No subsystems found, - please add subsystems directly via SQL queries in the database. - - - - - - Generate Token - - + + {/* Below div could become an *alert* atom */} + 0} + >No subsystems found, + please add subsystems directly + via SQL queries in the database. + + + + )} + /> + + )} + /> { - {vnode.children} diff --git a/src/app/organisms/Log.tsx b/src/app/organisms/Log.tsx index 0a3f51f..c4fcab1 100644 --- a/src/app/organisms/Log.tsx +++ b/src/app/organisms/Log.tsx @@ -1,3 +1,4 @@ + /* * Copyright (C) 2018 Amsterdam University of Applied Sciences (AUAS) * @@ -5,7 +6,6 @@ * GNU General Public Licence version 3 (GPL) version 3, * copied verbatim in the file "LICENSE" */ - import * as m from 'mithril'; import Spinner from '../atoms/Spinner'; import HttpErrorAlert from '../atoms/HttpErrorAlert'; @@ -15,8 +15,11 @@ import LinkRunToLog from '../atoms/LinkRunToLog'; import SuccessMessage from '../atoms/SuccessMessage'; import { store } from '../redux/configureStore'; import { fetchAttachmentsByLog } from '../redux/ducks/attachment/operations'; -import { fetchLog } from '../redux/ducks/log/operations'; -import { selectCurrentLog, selectIsFetchingLog, selectIsPatchingLinkRunToLog } from '../redux/ducks/log/selectors'; +import { fetchLog, fetchThread } from '../redux/ducks/log/operations'; +import { + selectCurrentLog, selectIsFetchingLog, + selectIsPatchingLinkRunToLog, selectThread +} from '../redux/ducks/log/selectors'; import Card from '../atoms/Card'; import DescriptionList from '../atoms/DescriptionList'; import LogDescription from '../constants/LogDescription'; @@ -25,122 +28,256 @@ import MarkdownViewer from '../atoms/MarkdownViewer'; import Table from '../molecules/Table'; import RunColumns from '../constants/RunColumns'; import { selectAttachments } from '../redux/ducks/attachment/selectors'; -import { Attachment } from '../interfaces/Attachment'; +import { IAttachment } from '../interfaces/Attachment'; import { download } from '../utility/FileUtil'; import AttachmentComponent from '../molecules/Attachment'; +import { selectFetchingTags, selectTagsForLog, selectTags } from '../redux/ducks/tag/selectors'; +import { ITag, ITagCreate } from '../interfaces/Tag'; +import Input from '../atoms/Input'; +import FormGroup from '../molecules/FormGroup'; +import Label from '../atoms/Label'; +import Select from '../atoms/Select'; +import { createTag } from '../redux/ducks/tag/operations'; +import Button, { ButtonType, ButtonClass } from '../atoms/Button'; +import Comment from '../atoms/Comment'; +import { ILog } from '../interfaces/Log'; +import { setFilter } from '../redux/ducks/filter/actions'; +import { FilterName } from '../interfaces/Filter'; +import { selectQueryString } from '../redux/ducks/filter/selectors'; interface Attrs { - logId: number; + logId: number; } - type Vnode = m.Vnode; - export default class Log extends MithrilTsxComponent { - constructor(vnode: Vnode) { - super(); - store.dispatch(fetchLog(vnode.attrs.logId)); - store.dispatch(fetchAttachmentsByLog(vnode.attrs.logId)); - } + constructor(vnode: Vnode) { + super(); + store.dispatch(fetchLog(vnode.attrs.logId)); + store.dispatch(fetchAttachmentsByLog(vnode.attrs.logId)); + store.dispatch(setFilter(FilterName.Log, 'threadId', vnode.attrs.logId)); + const queryString = selectQueryString(store.getState())(FilterName.Log); + console.log('fetching thread...'); + store.dispatch(fetchThread(queryString)); + } - view(vnode: Vnode) { - const addExistingRunId = 'add-existing-run'; - const state = store.getState(); - const currentLog = selectCurrentLog(state); - const isFetchingLog = selectIsFetchingLog(state); - const isPatchingLinkRunToLog = selectIsPatchingLinkRunToLog(state); - const attachments = selectAttachments(store.getState()); - const ATTACHMENT_MODAL_ID = 'attachment-modal-id'; + async handleSubmit(event: any): Promise { + const log = await selectCurrentLog(store.getState()) as ILog | null; + const tagId = event.target.tag.value; + const newTag = event.target.tagText.value; + if (log && tagId) { + // add existing tag to Log + // store.dispatch() + event.target.reset(); // Clear the form. + } else if (log && newTag) { + // create new tag and add to Log + const tagToCreate = { + tagText: newTag + }; + store.dispatch(createTag(tagToCreate as ITagCreate)); + event.target.reset(); // Clear the form. + } + } - return ( - - - - - - - - - - } - footerContent={( - - { - currentLog && currentLog.text - ? - : 'This log has no text' - } - { - currentLog && currentLog.runs && currentLog.runs.length > 0 - ? ( - - ) - : 'This log has no runs' - } - { - - - {attachments && attachments.map((attachment: Attachment) => - - - {attachment.title} - - - )} - - - - - - - - - - - } - { - 'Not yet implemented' - } - - )} + view(vnode: Vnode) { + const addExistingRunId = 'add-existing-run'; + const state = store.getState(); + const currentLog = selectCurrentLog(state); + const isFetchingLog = selectIsFetchingLog(state); + const isPatchingLinkRunToLog = selectIsPatchingLinkRunToLog(state); + const attachments = selectAttachments(store.getState()); + const tagsForLog = selectTagsForLog(store.getState()); + const tags = selectTags(store.getState()); + const thread = selectThread(store.getState()); + return ( + + + + + + + + + + + + m.route.set( + `/logs/create/comments/ + ${currentLog && currentLog.logId}` + )} + text="Reply on this Log" + /> + + } + footerContent={( + 0 ? ('') : 'Runs']} + // Check wether the current log has a Run, if not pass "Runs" as tab to hide to TabContainer + > + { + currentLog && currentLog.body + ? + : 'This log has no text' + } + { + currentLog && currentLog.runs && currentLog.runs.length > 0 + ? ( + + ) + : 'This log has no runs' + } + { + + + {attachments && attachments.map((attachment: IAttachment) => + + - - + {attachment.fileName} + + + )} + + + + + + + + - - - - ); - } + } + { + + Currently added tags: + + {tagsForLog && tagsForLog.map((tag: ITag) => + + + {tag.tagText} + + + )} + + + Add a Tag to this log: + { + event.preventDefault(); + this.handleSubmit(event); + }} + > + + )} + field={( + + + + + + )} + /> + + )} + field={( + + )} + /> + + )} + /> + + + } + { + + {thread && thread.comments && thread.comments.map((log: ILog) => + + )} + + } + + )} + > + + + + + + + + ); + } } diff --git a/src/app/organisms/Login.tsx b/src/app/organisms/Login.tsx index 8db252a..c765791 100644 --- a/src/app/organisms/Login.tsx +++ b/src/app/organisms/Login.tsx @@ -13,6 +13,8 @@ import * as Cookie from 'js-cookie'; import { store } from '../redux/configureStore'; import { selectIsAuthorizing } from '../redux/ducks/auth/selectors'; import Spinner from '../atoms/Spinner'; +import Button, { ButtonType, ButtonClass } from '../atoms/Button'; +import { APPLICATION_NAME } from '../constants/constants'; /** * Landing page for unauthorized users. @@ -31,34 +33,37 @@ export default class Login extends MithrilTsxComponent<{}> { Please close the browser to end the user session. - : - - Welcome to Jiskefet - Please sign in to use the application. - - {localStorage.getItem('USE_CERN_SSO') === 'true' ? - : - - - Sign up + : + + Welcome to {APPLICATION_NAME} + Please sign in to use the application. + + {localStorage.getItem('USE_CERN_SSO') === 'true' ? + : + + + Sign up - - } - - { - - Sign in - + } - + + { + + + } + + - - } - + } + ); diff --git a/src/app/organisms/Logs.tsx b/src/app/organisms/Logs.tsx index 23a1166..7af5360 100644 --- a/src/app/organisms/Logs.tsx +++ b/src/app/organisms/Logs.tsx @@ -15,7 +15,7 @@ import LogColumns from '../constants/LogColumns'; import { MithrilTsxComponent } from 'mithril-tsx-component'; import Filter from '../molecules/Filter'; import Pagination from '../atoms/Pagination'; -import { Event } from '../interfaces/Event'; +import { IEvent } from '../interfaces/Event'; import PageCounter from '../atoms/PageCounter'; import { createDummyTable } from '../utility/DummyService'; import ContentBlock from '../molecules/ContentBlock'; @@ -30,6 +30,10 @@ import { selectQueryString, selectFilters } from '../redux/ducks/filter/selector import { setFilter, resetFilters } from '../redux/ducks/filter/actions'; import { fetchLogs } from '../redux/ducks/log/operations'; import { selectIsFetchingLogs, selectLogCount, selectLogs } from '../redux/ducks/log/selectors'; +import Label from '../atoms/Label'; +import Select from '../atoms/Select'; +import Button, { ButtonClass } from '../atoms/Button'; +import { PAGE_SIZES } from '../constants/constants'; const inputFields = [ { @@ -82,13 +86,24 @@ export default class Logs extends MithrilTsxComponent<{}> { } view() { - const pageSizes = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]; const collapsableFilterItem = selectCollapsableItem(store.getState(), 'filters'); const logFilters = selectFilters(store.getState())[FilterName.Log]; return ( + - + + + m.route.set( + '/logs/create' + )} + text="Create new log" + /> + + { - - Page size - + - { + oninput={(event: IEvent) => { store.dispatch( setFilter(FilterName.Log, 'pageSize', event.target.value) ); store.dispatch(setFilter(FilterName.Log, 'pageNumber', 1)); this.setQueryAndFetch(); }} - value={logFilters.pageSize} - > - {pageSizes.map((pageSize: number) => - // tslint:disable-next-line:jsx-key - {pageSize} - )} - + defaultOption={logFilters.pageSize} + options={PAGE_SIZES} + /> { } view(vnode: Vnode) { - const pageSizes = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]; const isCernProfile = localStorage.getItem('USE_CERN_SSO') === 'true'; const profile = selectProfile(store.getState()); const { userId } = vnode.attrs; @@ -77,7 +79,7 @@ export default class Profile extends MithrilTsxComponent { @@ -120,33 +122,28 @@ export default class Profile extends MithrilTsxComponent { - - Page size - + - { + oninput={(event: IEvent) => { store.dispatch( setFilter(FilterName.UserLog, 'pageSize', event.target.value) ); store.dispatch(setFilter(FilterName.UserLog, 'pageNumber', 1)); this.setQueryAndFetch(userId); }} - value={userLogFilters.pageSize} - > - {pageSizes.map((pageSize: number) => - // tslint:disable-next-line:jsx-key - {pageSize} - )} - + defaultOption={userLogFilters.pageSize} + options={PAGE_SIZES} + /> { ); } + async handleSubmit(event: any): Promise { + const log = await selectCurrentRun(store.getState()) as Run | null; + const tagId = event.target.tag.value; + const newTag = event.target.tagText.value; + if (log && tagId) { + // add existing tag to Log + // store.dispatch() + event.target.reset(); // Clear the form. + } else if (log && newTag) { + // create new tag and add to Log + const tagToCreate = { + tagText: newTag + }; + store.dispatch(createTag(tagToCreate as ITagCreate)); + event.target.reset(); // Clear the form. + } + } + view(vnode: Vnode) { const addExistingRunId = 'add-existing-run'; const state = store.getState(); const currentRun = selectCurrentRun(state); const isFetchingRun = selectIsFetchingRun(state); const isPatchingLinkLogToRun = selectIsPatchingLinkLogToRun(state); + const tagsForRun = selectTagsForRun(store.getState()); + const tags = selectTags(store.getState()); return ( { this.linkingButton(addExistingRunId, vnode.attrs.runNumber) } footerContent={( - + { currentRun && currentRun.logs.length > 0 - ? - : 'This run has no logs' - } - { - 'Not yet implemented' + ? + : 'This run has no logs' } { - 'Not yet implemented' + + Currently added tags: + + {tagsForRun && tagsForRun.map((tag: ITag) => + + + {tag.tagText} + + + )} + + + Add a Tag to this log: + { + event.preventDefault(); + this.handleSubmit(event); + }} + > + + )} + field={( + + + + + + )} + /> + + )} + field={( + + )} + /> + + )} + /> + + } )} diff --git a/src/app/organisms/Runs.tsx b/src/app/organisms/Runs.tsx index 5ea57bb..8e9fc45 100644 --- a/src/app/organisms/Runs.tsx +++ b/src/app/organisms/Runs.tsx @@ -16,7 +16,7 @@ import Filter from '../molecules/Filter'; import { createDummyTable } from '../utility/DummyService'; import PageCounter from '../atoms/PageCounter'; import Pagination from '../atoms/Pagination'; -import { Event } from '../interfaces/Event'; +import { IEvent } from '../interfaces/Event'; import ContentBlock from '../molecules/ContentBlock'; import SuccessMessage from '../atoms/SuccessMessage'; import Badges from '../atoms/Badges'; @@ -30,6 +30,9 @@ import { setFilter, resetFilters } from '../redux/ducks/filter/actions'; import { OrderDirection } from '../enums/OrderDirection'; import { fetchRuns } from '../redux/ducks/run/operations'; import { selectIsFetchingRuns, selectRuns, selectRunCount } from '../redux/ducks/run/selectors'; +import Label from '../atoms/Label'; +import Select from '../atoms/Select'; +import { PAGE_SIZES } from '../constants/constants'; const inputFields = [ { @@ -132,7 +135,6 @@ export default class Runs extends MithrilTsxComponent<{}> { } view() { - const pageSizes = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]; const collapsableFilterItem = selectCollapsableItem(store.getState(), 'filters'); const runFilters = selectFilters(store.getState())[FilterName.Run]; return ( @@ -211,32 +213,28 @@ export default class Runs extends MithrilTsxComponent<{}> { - - Page size - + - { + oninput={(event: IEvent) => { store.dispatch( setFilter(FilterName.Run, 'pageSize', event.target.value) ); store.dispatch(setFilter(FilterName.Run, 'pageNumber', 1)); this.setQueryAndFetch(); }} - value={runFilters.pageSize} - > - {pageSizes.map((pageSize: number) => - // tslint:disable-next-line:jsx-key - {pageSize} - )} - + defaultOption={runFilters.pageSize} + options={PAGE_SIZES} + /> { @@ -59,18 +61,16 @@ export default class SubsystemsOverview extends MithrilTsxComponent<{}> { - - - Time range in hours. - - + { + oninput={(event: IEvent) => { store.dispatch(setFilter( FilterName.Subsystem, 'timeRange', @@ -78,13 +78,9 @@ export default class SubsystemsOverview extends MithrilTsxComponent<{}> { )); this.setQueryAndFetch(); }} - value={subsystemFilters.timeRange} - > - {timeRanges.map((timeRange: number) => - {timeRange} - )} - - + defaultOption={subsystemFilters.timeRange} + options={timeRanges} + /> ; export default class CreateLogPage extends MithrilTsxComponent { view(vnode: Vnode) { - const { runNumber } = vnode.attrs; + const { runNumber, logNumber } = vnode.attrs; return ( - + ); } diff --git a/src/app/redux/configureStore.ts b/src/app/redux/configureStore.ts index 388860a..987b77c 100644 --- a/src/app/redux/configureStore.ts +++ b/src/app/redux/configureStore.ts @@ -10,7 +10,7 @@ import { createStore, applyMiddleware, combineReducers, AnyAction, Reducer } fro import thunk, { ThunkMiddleware } from 'redux-thunk'; import { composeWithDevTools } from 'redux-devtools-extension'; import { RootState, RootActions, Store } from './types'; -import { subsystem, success, ui, attachment, auth, filter, error, user, run, log } from './ducks'; +import { subsystem, success, ui, attachment, auth, filter, error, user, run, log, tag } from './ducks'; import { ActionTypes as AuthActionTypes } from './ducks/auth/types'; import { RootFilterState } from './ducks/filter/types'; @@ -31,7 +31,8 @@ const appReducer = combineReducers({ error, user, run, - log + log, + tag }); const rootReducer = (state: RootState | undefined, action: RootActions) => { diff --git a/src/app/redux/ducks/attachment/actions.ts b/src/app/redux/ducks/attachment/actions.ts index cbe6fc8..acfb00c 100644 --- a/src/app/redux/ducks/attachment/actions.ts +++ b/src/app/redux/ducks/attachment/actions.ts @@ -15,8 +15,8 @@ import { SetAttachmentToBeCreatedAction, ClearAttachmentToBeCreatedAction } from './types'; -import { Attachment } from '../../../interfaces/Attachment'; -import { CollectionResponseObject } from '../../../interfaces/ResponseObject'; +import { IAttachment, IAttachmentCreate } from '../../../interfaces/Attachment'; +import { ICollectionSuccessObject } from '../../../interfaces/ResponseObject'; // Action creators export const fetchAttachmentsByLogRequest = (): FetchAttachmentsByLogRequestAction => ({ @@ -24,7 +24,7 @@ export const fetchAttachmentsByLogRequest = (): FetchAttachmentsByLogRequestActi }); export const fetchAttachmentsByLogSuccess = ( - payload: CollectionResponseObject): FetchAttachmentsByLogSuccessAction => ({ + payload: ICollectionSuccessObject): FetchAttachmentsByLogSuccessAction => ({ type: ActionTypes.FETCH_ATTACHMENTS_BY_LOG_SUCCESS, payload }); @@ -37,9 +37,9 @@ export const createAttachmentSuccess = (): CreateAttachmentSuccessAction => ({ type: ActionTypes.CREATE_ATTACHMENT_SUCCESS }); -export const setAttachmentToBeCreated = (attachment: Attachment): SetAttachmentToBeCreatedAction => ({ +export const setAttachmentToBeCreated = (attachmentToBeCreated: IAttachmentCreate): SetAttachmentToBeCreatedAction => ({ type: ActionTypes.SET_ATTACHMENT_TO_BE_CREATED, - payload: attachment + payload: attachmentToBeCreated }); export const clearAttachmentToBeCreated = (): ClearAttachmentToBeCreatedAction => ({ diff --git a/src/app/redux/ducks/attachment/operations.ts b/src/app/redux/ducks/attachment/operations.ts index fdca7c1..28d1147 100644 --- a/src/app/redux/ducks/attachment/operations.ts +++ b/src/app/redux/ducks/attachment/operations.ts @@ -9,8 +9,8 @@ import { ThunkResult, AttachmentAction } from './types'; import { ThunkDispatch } from 'redux-thunk'; import { RootState } from '../../types'; -import { Attachment } from '../../../interfaces/Attachment'; -import { HttpError } from '../../../interfaces/HttpError'; +import { IAttachment, IAttachmentCreate } from '../../../interfaces/Attachment'; +import { IHttpError } from '../../../interfaces/HttpError'; import { request } from '../../../request'; import { fetchAttachmentsByLogRequest, @@ -21,7 +21,7 @@ import { import { getAttachmentsByLog, postAttachment } from '../../../constants/apiUrls'; import { addHttpError } from '../error/actions'; import { ErrorAction } from '../error/types'; -import { CollectionResponseObject } from '../../../interfaces/ResponseObject'; +import { ICollectionSuccessObject } from '../../../interfaces/ResponseObject'; // Thunks export const fetchAttachmentsByLog = (logId: number): ThunkResult> => @@ -30,24 +30,24 @@ export const fetchAttachmentsByLog = (logId: number): ThunkResult> return request({ method: 'GET', url: getAttachmentsByLog(logId) - }).then((result: CollectionResponseObject) => { + }).then((result: ICollectionSuccessObject) => { dispatch(fetchAttachmentsByLogSuccess(result)); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; -export const saveAttachment = (attachment: Attachment): ThunkResult> => +export const saveAttachment = (attachment: IAttachmentCreate, logId: number): ThunkResult> => async (dispatch: ThunkDispatch): Promise => { dispatch(createAttachmentRequest()); return request({ method: 'POST', data: attachment, - url: postAttachment() + url: postAttachment(logId) }).then((result: any) => { // SuccesModel.add('Successfully saved attachment.'); dispatch(createAttachmentSuccess()); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; diff --git a/src/app/redux/ducks/attachment/selectors.ts b/src/app/redux/ducks/attachment/selectors.ts index 8bd476f..33d8312 100644 --- a/src/app/redux/ducks/attachment/selectors.ts +++ b/src/app/redux/ducks/attachment/selectors.ts @@ -7,10 +7,10 @@ */ import { RootState } from '../../types'; -import { Attachment } from '../../../interfaces/Attachment'; +import { IAttachment, IAttachmentCreate } from '../../../interfaces/Attachment'; // Selectors -export const selectAttachments = (state: RootState): Attachment[] => state.attachment.attachments; -export const selectAttachmentToBeCreated = (state: RootState): Attachment | null => ( +export const selectAttachments = (state: RootState): IAttachment[] => state.attachment.attachments; +export const selectAttachmentToBeCreated = (state: RootState): IAttachmentCreate | null => ( state.attachment.attachmentToBeCreated ); diff --git a/src/app/redux/ducks/attachment/types.ts b/src/app/redux/ducks/attachment/types.ts index 6236e7a..ef27352 100644 --- a/src/app/redux/ducks/attachment/types.ts +++ b/src/app/redux/ducks/attachment/types.ts @@ -9,16 +9,16 @@ import { Action } from 'redux'; import { ThunkAction } from 'redux-thunk'; import { RootState } from '../../types'; -import { Attachment, AttachmentCreate } from '../../../interfaces/Attachment'; -import { CollectionResponseObject } from '../../../interfaces/ResponseObject'; +import { IAttachment, IAttachmentCreate } from '../../../interfaces/Attachment'; +import { ICollectionSuccessObject } from '../../../interfaces/ResponseObject'; // State interface export interface AttachmentState { isFetchingAttachments: boolean; isFetchingAttachment: boolean; isCreatingAttachment: boolean; - attachments: Attachment[]; - attachmentToBeCreated: AttachmentCreate | null; + attachments: IAttachment[]; + attachmentToBeCreated: IAttachmentCreate | null; } // Action types @@ -38,7 +38,7 @@ export interface FetchAttachmentsByLogRequestAction extends Action { export interface FetchAttachmentsByLogSuccessAction extends Action { type: ActionTypes.FETCH_ATTACHMENTS_BY_LOG_SUCCESS; - payload: CollectionResponseObject; + payload: ICollectionSuccessObject; } export interface CreateAttachmentRequestAction extends Action { @@ -51,7 +51,7 @@ export interface CreateAttachmentSuccessAction extends Action { export interface SetAttachmentToBeCreatedAction extends Action { type: ActionTypes.SET_ATTACHMENT_TO_BE_CREATED; - payload: Attachment; + payload: IAttachmentCreate; } export interface ClearAttachmentToBeCreatedAction extends Action { diff --git a/src/app/redux/ducks/auth/actions.ts b/src/app/redux/ducks/auth/actions.ts index 646460f..7d9a174 100644 --- a/src/app/redux/ducks/auth/actions.ts +++ b/src/app/redux/ducks/auth/actions.ts @@ -15,15 +15,15 @@ import { UserLogoutAction } from './types'; import { UserProfile } from '../../../interfaces/UserProfile'; -import { AuthorizeResponse } from '../../../interfaces/Auth'; -import { ResponseObject } from '../../../interfaces/ResponseObject'; +import { IAuthorizeResponse } from '../../../interfaces/Auth'; +import { ISuccessObject } from '../../../interfaces/ResponseObject'; // Action creators export const fetchProfileRequest = (): FetchProfileRequestAction => ({ type: ActionTypes.FETCH_PROFILE_REQUEST }); -export const fetchProfileSuccess = (payload: ResponseObject): FetchProfileSuccessAction => ({ +export const fetchProfileSuccess = (payload: ISuccessObject): FetchProfileSuccessAction => ({ type: ActionTypes.FETCH_PROFILE_SUCCESS, payload }); @@ -32,7 +32,7 @@ export const authorizeRequest = (): AuthorizeRequestAction => ({ type: ActionTypes.AUTHORIZE_REQUEST }); -export const authorizeSuccess = (payload: AuthorizeResponse): AuthorizeSuccessAction => ({ +export const authorizeSuccess = (payload: IAuthorizeResponse): AuthorizeSuccessAction => ({ type: ActionTypes.AUTHORIZE_SUCCESS, payload }); diff --git a/src/app/redux/ducks/auth/operations.ts b/src/app/redux/ducks/auth/operations.ts index b60c06a..2d1ad05 100644 --- a/src/app/redux/ducks/auth/operations.ts +++ b/src/app/redux/ducks/auth/operations.ts @@ -12,14 +12,14 @@ import { ThunkDispatch } from 'redux-thunk'; import { RootState } from '../../types'; import { request } from '../../../request'; import { fetchProfileSuccess, fetchProfileRequest, authorizeRequest, authorizeSuccess, userLogout } from './actions'; -import { HttpError } from '../../../interfaces/HttpError'; +import { IHttpError } from '../../../interfaces/HttpError'; import { UserProfile } from '../../../interfaces/UserProfile'; import { getAuthorize, getProfile } from '../../../constants/apiUrls'; -import { AuthorizeResponse } from '../../../interfaces/Auth'; +import { IAuthorizeResponse } from '../../../interfaces/Auth'; import { initialize } from '../../../app'; import { ErrorAction } from '../error/types'; import { addHttpError } from '../error/actions'; -import { ResponseObject } from '../../../interfaces/ResponseObject'; +import { ISuccessObject } from '../../../interfaces/ResponseObject'; // Thunks export const fetchProfile = (): ThunkResult> => @@ -28,9 +28,9 @@ export const fetchProfile = (): ThunkResult> => return request({ method: 'GET', url: getProfile() - }).then((result: ResponseObject) => { + }).then((result: ISuccessObject) => { dispatch(fetchProfileSuccess(result)); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; @@ -41,9 +41,9 @@ export const authorize = (authGrant: string): ThunkResult> => return request({ method: 'GET', url: getAuthorize(authGrant) - }).then((result: AuthorizeResponse) => { + }).then((result: IAuthorizeResponse) => { dispatch(authorizeSuccess(result)); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; diff --git a/src/app/redux/ducks/auth/types.ts b/src/app/redux/ducks/auth/types.ts index a90a217..56a429a 100644 --- a/src/app/redux/ducks/auth/types.ts +++ b/src/app/redux/ducks/auth/types.ts @@ -10,8 +10,8 @@ import { Action } from 'redux'; import { RootState } from '../../types'; import { ThunkAction } from 'redux-thunk'; import { UserProfile } from '../../../interfaces/UserProfile'; -import { AuthorizeResponse } from '../../../interfaces/Auth'; -import { ResponseObject } from '../../../interfaces/ResponseObject'; +import { IAuthorizeResponse } from '../../../interfaces/Auth'; +import { ISuccessObject } from '../../../interfaces/ResponseObject'; // State interface export interface AuthState { @@ -37,7 +37,7 @@ export interface FetchProfileRequestAction extends Action { export interface FetchProfileSuccessAction extends Action { type: ActionTypes.FETCH_PROFILE_SUCCESS; - payload: ResponseObject; + payload: ISuccessObject; } export interface AuthorizeRequestAction extends Action { @@ -46,7 +46,7 @@ export interface AuthorizeRequestAction extends Action { export interface AuthorizeSuccessAction extends Action { type: ActionTypes.AUTHORIZE_SUCCESS; - payload: AuthorizeResponse; + payload: IAuthorizeResponse; } export interface UserLogoutAction extends Action { diff --git a/src/app/redux/ducks/error/actions.ts b/src/app/redux/ducks/error/actions.ts index 094cf88..6cc8d2b 100644 --- a/src/app/redux/ducks/error/actions.ts +++ b/src/app/redux/ducks/error/actions.ts @@ -7,10 +7,10 @@ */ import { ActionTypes, AddHttpErrorAction, ClearHttpErrorsAction } from './types'; -import { HttpError } from '../../../interfaces/HttpError'; +import { IHttpError } from '../../../interfaces/HttpError'; // Action creators -export const addHttpError = (httpError: HttpError): AddHttpErrorAction => ({ +export const addHttpError = (httpError: IHttpError): AddHttpErrorAction => ({ type: ActionTypes.ADD_HTTP_ERROR, payload: httpError, }); diff --git a/src/app/redux/ducks/error/operations.ts b/src/app/redux/ducks/error/operations.ts index 6d35de7..1d670d7 100644 --- a/src/app/redux/ducks/error/operations.ts +++ b/src/app/redux/ducks/error/operations.ts @@ -11,15 +11,16 @@ import { ThunkDispatch } from 'redux-thunk'; import { RootState } from '../../types'; import { selectErrors } from './selectors'; import { clearHttpErrors } from './actions'; -import { HttpError } from '../../../interfaces/HttpError'; +import { IHttpError } from '../../../interfaces/HttpError'; // Thunks /** * Returns the errors currently in the error store and then clears the error store (excluding errorHistory). */ -export const extractErrors = (): ThunkResult> => - async (dispatch: ThunkDispatch, getState: () => RootState): Promise => { +export const extractErrors = (): ThunkResult>>> => + async (dispatch: ThunkDispatch, getState: () => + RootState): Promise>> => { const errors = await selectErrors(getState()); if (errors.length > 0) { await dispatch(clearHttpErrors()); diff --git a/src/app/redux/ducks/error/selectors.ts b/src/app/redux/ducks/error/selectors.ts index 26a2716..acd1667 100644 --- a/src/app/redux/ducks/error/selectors.ts +++ b/src/app/redux/ducks/error/selectors.ts @@ -7,7 +7,7 @@ */ import { RootState } from '../../types'; -import { HttpError } from '../../../interfaces/HttpError'; +import { IHttpError } from '../../../interfaces/HttpError'; // Selectors -export const selectErrors = (state: RootState): HttpError[] => state.error.httpErrors; +export const selectErrors = (state: RootState): Array> => state.error.httpErrors; diff --git a/src/app/redux/ducks/error/types.ts b/src/app/redux/ducks/error/types.ts index 887d010..1db8899 100644 --- a/src/app/redux/ducks/error/types.ts +++ b/src/app/redux/ducks/error/types.ts @@ -9,11 +9,11 @@ import { Action } from 'redux'; import { ThunkAction } from 'redux-thunk'; import { RootState } from '../../types'; -import { HttpError } from '../../../interfaces/HttpError'; +import { IHttpError } from '../../../interfaces/HttpError'; // State interface export interface ErrorState { - httpErrors: HttpError[]; + httpErrors: Array>; errorHistory: object[]; } @@ -26,7 +26,7 @@ export enum ActionTypes { // Action interfaces export interface AddHttpErrorAction extends Action { type: ActionTypes.ADD_HTTP_ERROR; - payload: HttpError; + payload: IHttpError; } export interface ClearHttpErrorsAction extends Action { diff --git a/src/app/redux/ducks/filter/actions.ts b/src/app/redux/ducks/filter/actions.ts index 1834e27..149f3c6 100644 --- a/src/app/redux/ducks/filter/actions.ts +++ b/src/app/redux/ducks/filter/actions.ts @@ -7,7 +7,7 @@ */ import { ActionTypes, SetFilterAction, ResetFiltersAction, SetFiltersAction } from './types'; -import { FilterState, FilterName } from '../../../interfaces/Filter'; +import { IFilterState, FilterName } from '../../../interfaces/Filter'; // Action creators @@ -35,7 +35,7 @@ export const setFilter = (name: FilterName, key: string, value: any): SetFilterA * @param name The name of the filter reducer. * @param filters The filters used to replace the filter values. */ -export const setFilters = (name: FilterName, filters: FilterState): SetFiltersAction => ({ +export const setFilters = (name: FilterName, filters: IFilterState): SetFiltersAction => ({ type: ActionTypes.SET_FILTERS, name, payload: filters diff --git a/src/app/redux/ducks/filter/reducers.ts b/src/app/redux/ducks/filter/reducers.ts index 88dc0a8..a018432 100644 --- a/src/app/redux/ducks/filter/reducers.ts +++ b/src/app/redux/ducks/filter/reducers.ts @@ -10,7 +10,7 @@ import { Reducer, combineReducers } from 'redux'; import { FilterAction, ActionTypes } from './types'; import { OrderDirection } from '../../../enums/OrderDirection'; import * as _ from 'lodash'; -import { FilterState, FilterValue, FilterName } from '../../../interfaces/Filter'; +import { IFilterState, FilterValue, FilterName } from '../../../interfaces/Filter'; import { createNamedWrapperReducer } from '../../utils/reducerWrappers'; // Initial state @@ -19,7 +19,7 @@ import { createNamedWrapperReducer } from '../../utils/reducerWrappers'; * Returns the initial state for the name given. * @param name The name identifier for the state. */ -const getInitialState = (name: FilterName): FilterState | {} => { +const getInitialState = (name: FilterName): IFilterState | {} => { switch (name) { case FilterName.Log: { return { @@ -28,9 +28,9 @@ const getInitialState = (name: FilterName): FilterState | {} => { creationTime: null, origin: null, subType: null, - orderBy: 'creationTime', + orderBy: 'logId', orderDirection: OrderDirection.Descending, - pageSize: 16, + pageSize: 20, pageNumber: 1, }; } @@ -50,7 +50,7 @@ const getInitialState = (name: FilterName): FilterState | {} => { endTimeTrgEnd: null, orderBy: null, orderDirection: null, - pageSize: 16, + pageSize: 20, pageNumber: 1, }; case FilterName.Subsystem: @@ -63,7 +63,7 @@ const getInitialState = (name: FilterName): FilterState | {} => { return { orderBy: null, orderDirection: null, - pageSize: 16, + pageSize: 20, pageNumber: 1, }; default: @@ -78,7 +78,7 @@ const getInitialState = (name: FilterName): FilterState | {} => { * @param source * @param mergeObj */ -const mergeOverlappingKeys = (source: FilterState, mergeObj: FilterState): FilterState => { +const mergeOverlappingKeys = (source: IFilterState, mergeObj: IFilterState): IFilterState => { const result = _.mapValues(source, (value: FilterValue, key: string) => { return mergeObj[key] || value; }); @@ -86,8 +86,8 @@ const mergeOverlappingKeys = (source: FilterState, mergeObj: FilterState): Filte }; // Reducer -const filterReducer: Reducer - = (state: FilterState, action: FilterAction): FilterState => { +const filterReducer: Reducer + = (state: IFilterState, action: FilterAction): IFilterState => { if (!state || _.isEmpty(state)) { state = getInitialState(action.name); } @@ -112,10 +112,10 @@ const filterReducer: Reducer }; const rootReducer = combineReducers({ - [FilterName.Log]: createNamedWrapperReducer(filterReducer, FilterName.Log), - [FilterName.Run]: createNamedWrapperReducer(filterReducer, FilterName.Run), - [FilterName.Subsystem]: createNamedWrapperReducer(filterReducer, FilterName.Subsystem), - [FilterName.UserLog]: createNamedWrapperReducer(filterReducer, FilterName.UserLog), + [FilterName.Log]: createNamedWrapperReducer(filterReducer, FilterName.Log), + [FilterName.Run]: createNamedWrapperReducer(filterReducer, FilterName.Run), + [FilterName.Subsystem]: createNamedWrapperReducer(filterReducer, FilterName.Subsystem), + [FilterName.UserLog]: createNamedWrapperReducer(filterReducer, FilterName.UserLog), }); export default rootReducer; diff --git a/src/app/redux/ducks/filter/selectors.ts b/src/app/redux/ducks/filter/selectors.ts index 14a2234..a0f8eb1 100644 --- a/src/app/redux/ducks/filter/selectors.ts +++ b/src/app/redux/ducks/filter/selectors.ts @@ -11,7 +11,7 @@ import { RootFilterState } from './types'; import { createSelector } from 'reselect'; import * as m from 'mithril'; import * as _ from 'lodash'; -import { FilterState, FilterName } from '../../../interfaces/Filter'; +import { IFilterState, FilterName } from '../../../interfaces/Filter'; // Selectors export const selectFilters = (state: RootState): RootFilterState => state.filter; @@ -24,7 +24,7 @@ export const selectQueryString = createSelector( selectFilters, (filters: RootFilterState) => _.memoize( (filterName: FilterName): string => { - const childFilters: FilterState = filters[filterName]; + const childFilters: IFilterState = filters[filterName]; return m.buildQueryString(_.pickBy(childFilters)); } ) diff --git a/src/app/redux/ducks/filter/types.ts b/src/app/redux/ducks/filter/types.ts index a64f319..950f956 100644 --- a/src/app/redux/ducks/filter/types.ts +++ b/src/app/redux/ducks/filter/types.ts @@ -9,7 +9,7 @@ import { RootState, NamedAction } from '../../types'; import { ThunkAction } from 'redux-thunk'; import { OrderDirection } from '../../../enums/OrderDirection'; -import { FilterState, FilterValue, FilterName } from '../../../interfaces/Filter'; +import { IFilterState, FilterValue, FilterName } from '../../../interfaces/Filter'; // State interface export interface RootFilterState { @@ -19,7 +19,7 @@ export interface RootFilterState { [FilterName.UserLog]: UserLogFilters; } -export interface LogFilters extends FilterState { +export interface LogFilters extends IFilterState { logId: string | null; searchterm: string | null; creationTime: string | null; @@ -31,7 +31,7 @@ export interface LogFilters extends FilterState { pageNumber: number; } -export interface RunFilters extends FilterState { +export interface RunFilters extends IFilterState { runNumber: string | null; activityId: string | null; runType: string | null; @@ -50,13 +50,13 @@ export interface RunFilters extends FilterState { pageNumber: number; } -export interface SubsystemFilters extends FilterState { +export interface SubsystemFilters extends IFilterState { orderBy: string | null; orderDirection: OrderDirection | null; timeRange: number | null; } -export interface UserLogFilters extends FilterState { +export interface UserLogFilters extends IFilterState { orderBy: string | null; orderDirection: OrderDirection | null; pageSize: number; @@ -85,7 +85,7 @@ export interface SetFilterAction extends NamedFilterAction { export interface SetFiltersAction extends NamedFilterAction { type: ActionTypes.SET_FILTERS; - payload: FilterState; + payload: IFilterState; } export interface ResetFiltersAction extends NamedFilterAction { diff --git a/src/app/redux/ducks/index.ts b/src/app/redux/ducks/index.ts index dbf53e0..b0fdda3 100644 --- a/src/app/redux/ducks/index.ts +++ b/src/app/redux/ducks/index.ts @@ -20,3 +20,4 @@ export { default as error } from './error'; export { default as user } from './user'; export { default as run } from './run'; export { default as log } from './log'; +export { default as tag } from './tag'; diff --git a/src/app/redux/ducks/log/actions.ts b/src/app/redux/ducks/log/actions.ts index 03d2b08..37426e1 100644 --- a/src/app/redux/ducks/log/actions.ts +++ b/src/app/redux/ducks/log/actions.ts @@ -18,16 +18,27 @@ import { CreateLogSuccessAction, SetLogToBeCreatedAction, ClearLogToBeCreatedAction, + FetchThreadRequestAction, + FetchThreadSuccessAction, } from './types'; -import { Log, LogCreate } from '../../../interfaces/Log'; -import { ResponseObject, CollectionResponseObject } from '../../../interfaces/ResponseObject'; +import { ILog, ILogCreate } from '../../../interfaces/Log'; +import { ISuccessObject, ICollectionSuccessObject } from '../../../interfaces/ResponseObject'; // Action creators +export const fetchThreadRequest = (): FetchThreadRequestAction => ({ + type: ActionTypes.FETCH_THREAD_REQUEST +}); + +export const fetchThreadSuccess = (payload: ISuccessObject): FetchThreadSuccessAction => ({ + type: ActionTypes.FETCH_THREAD_SUCCESS, + payload +}); + export const fetchLogsRequest = (): FetchLogsByLogRequestAction => ({ type: ActionTypes.FETCH_LOGS_REQUEST }); -export const fetchLogsSuccess = (payload: CollectionResponseObject): FetchLogsByLogSuccessAction => ({ +export const fetchLogsSuccess = (payload: ICollectionSuccessObject): FetchLogsByLogSuccessAction => ({ type: ActionTypes.FETCH_LOGS_SUCCESS, payload }); @@ -36,7 +47,7 @@ export const fetchLogRequest = (): FetchLogRequestAction => ({ type: ActionTypes.FETCH_LOG_REQUEST }); -export const fetchLogSuccess = (payload: ResponseObject): FetchLogSuccessAction => ({ +export const fetchLogSuccess = (payload: ISuccessObject): FetchLogSuccessAction => ({ type: ActionTypes.FETCH_LOG_SUCCESS, payload }); @@ -57,7 +68,7 @@ export const linkRunToLogSucces = (): LinkLogToLogSuccesAction => ({ type: ActionTypes.LINK_RUN_TO_LOG_SUCCESS }); -export const setLogToBeCreated = (logToBeCreated: LogCreate): SetLogToBeCreatedAction => ({ +export const setLogToBeCreated = (logToBeCreated: ILogCreate): SetLogToBeCreatedAction => ({ type: ActionTypes.SET_LOG_TO_BE_CREATED, payload: logToBeCreated }); diff --git a/src/app/redux/ducks/log/operations.ts b/src/app/redux/ducks/log/operations.ts index 7fcea11..648aac7 100644 --- a/src/app/redux/ducks/log/operations.ts +++ b/src/app/redux/ducks/log/operations.ts @@ -9,8 +9,8 @@ import { ThunkResult, LogAction } from './types'; import { ThunkDispatch } from 'redux-thunk'; import { RootState } from '../../types'; -import { Log, LogCreate } from '../../../interfaces/Log'; -import { HttpError } from '../../../interfaces/HttpError'; +import { ILog, ILogCreate } from '../../../interfaces/Log'; +import { IHttpError } from '../../../interfaces/HttpError'; import { request } from '../../../request'; import { fetchLogsRequest, @@ -20,23 +20,40 @@ import { createLogRequest, createLogSuccess, linkRunToLogRequest, - linkRunToLogSucces + linkRunToLogSucces, + fetchThreadRequest, + fetchThreadSuccess } from './actions'; import { getLogs, getLog, linkRunToLogUrl, postLog } from '../../../constants/apiUrls'; import { ErrorAction } from '../error/types'; import { addHttpError } from '../error/actions'; -import { ResponseObject, CollectionResponseObject } from '../../../interfaces/ResponseObject'; +import { ISuccessObject, ICollectionSuccessObject } from '../../../interfaces/ResponseObject'; +import { addSuccessMessage } from '../success/actions'; +import { SuccessAction } from '../success/types'; // Thunks +export const fetchThread = (query?: string): ThunkResult> => + async (dispatch: ThunkDispatch): Promise => { + dispatch(fetchThreadRequest()); + return request({ + method: 'GET', + url: getLogs(query) + }).then((result: ISuccessObject) => { + dispatch(fetchThreadSuccess(result)); + }).catch((error: IHttpError) => { + dispatch(addHttpError(error)); + }); + }; + export const fetchLogs = (query?: string): ThunkResult> => async (dispatch: ThunkDispatch): Promise => { dispatch(fetchLogsRequest()); return request({ method: 'GET', url: getLogs(query) - }).then((result: CollectionResponseObject) => { + }).then((result: ICollectionSuccessObject) => { dispatch(fetchLogsSuccess(result)); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; @@ -47,9 +64,9 @@ export const fetchLog = (id: number): ThunkResult> => return request({ method: 'GET', url: getLog(id) - }).then((result: ResponseObject) => { + }).then((result: ISuccessObject) => { dispatch(fetchLogSuccess(result)); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; @@ -63,13 +80,13 @@ export const linkRunToLog = (logId: number, runNumber: number): ThunkResult { dispatch(linkRunToLogSucces()); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; -export const createLog = (logToBeCreated: LogCreate): ThunkResult> => - async (dispatch: ThunkDispatch): Promise => { +export const createLog = (logToBeCreated: ILogCreate): ThunkResult> => + async (dispatch: ThunkDispatch): Promise => { dispatch(createLogRequest()); return request({ method: 'POST', @@ -80,7 +97,10 @@ export const createLog = (logToBeCreated: LogCreate): ThunkResult> dispatch(addHttpError(result.error)); } dispatch(createLogSuccess()); - }).catch((error: HttpError) => { + const message = + `Successfully created Log with id: ${result.data.item.logId}`; + dispatch(addSuccessMessage(message)); + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; diff --git a/src/app/redux/ducks/log/reducers.ts b/src/app/redux/ducks/log/reducers.ts index 9730887..18d1975 100644 --- a/src/app/redux/ducks/log/reducers.ts +++ b/src/app/redux/ducks/log/reducers.ts @@ -11,6 +11,7 @@ import { ActionTypes, LogState, LogAction } from './types'; // Initial state const initialState: LogState = { + isFetchingThread: false, isFetchingLogs: false, isFetchingLog: false, isPatchingLinkRunToLog: false, @@ -19,12 +20,24 @@ const initialState: LogState = { count: 0, current: null, logToBeCreated: null, + thread: null }; // Reducer const logReducer: Reducer = (state: LogState = initialState, action: LogAction): LogState => { switch (action.type) { + case ActionTypes.FETCH_THREAD_REQUEST: + return { + ...state, + isFetchingThread: true + }; + case ActionTypes.FETCH_THREAD_SUCCESS: + return { + ...state, + isFetchingThread: false, + thread: action.payload.data.item + }; case ActionTypes.FETCH_LOGS_REQUEST: return { ...state, diff --git a/src/app/redux/ducks/log/selectors.ts b/src/app/redux/ducks/log/selectors.ts index 3f1676f..933b043 100644 --- a/src/app/redux/ducks/log/selectors.ts +++ b/src/app/redux/ducks/log/selectors.ts @@ -7,17 +7,19 @@ */ import { RootState } from '../../types'; -import { Log, LogCreate } from '../../../interfaces/Log'; +import { ILog, ILogCreate } from '../../../interfaces/Log'; // Selectors export const selectIsFetchingLogs = (state: RootState): boolean => state.log.isFetchingLogs; +export const selectIsFetchingThread = (state: RootState): boolean => state.log.isFetchingThread; export const selectIsFetchingLog = (state: RootState): boolean => state.log.isFetchingLog; export const selectIsPatchingLinkRunToLog = (state: RootState): boolean => state.log.isPatchingLinkRunToLog; -export const selectLogs = (state: RootState): Log[] => state.log.logs; +export const selectLogs = (state: RootState): ILog[] => state.log.logs; export const selectLogCount = (state: RootState): number => state.log.count; -export const selectCurrentLog = (state: RootState): Log | null => ( +export const selectThread = (state: RootState): ILog | null => state.log.thread; +export const selectCurrentLog = (state: RootState): ILog | null => ( state.log.current ); -export const selectLogToBeCreated = (state: RootState): LogCreate | null => ( +export const selectLogToBeCreated = (state: RootState): ILogCreate | null => ( state.log.logToBeCreated ); diff --git a/src/app/redux/ducks/log/types.ts b/src/app/redux/ducks/log/types.ts index 69802f3..139caa1 100644 --- a/src/app/redux/ducks/log/types.ts +++ b/src/app/redux/ducks/log/types.ts @@ -9,23 +9,27 @@ import { Action } from 'redux'; import { ThunkAction } from 'redux-thunk'; import { RootState } from '../../types'; -import { Log, LogCreate } from '../../../interfaces/Log'; -import { ResponseObject, CollectionResponseObject } from '../../../interfaces/ResponseObject'; +import { ILog, ILogCreate } from '../../../interfaces/Log'; +import { ISuccessObject, ICollectionSuccessObject } from '../../../interfaces/ResponseObject'; // State interface export interface LogState { + isFetchingThread: boolean; isFetchingLogs: boolean; isFetchingLog: boolean; isPatchingLinkRunToLog: boolean; isCreatingLog: boolean; - logs: Log[]; + logs: ILog[]; count: number; - current: Log | null; - logToBeCreated: LogCreate | null; + current: ILog | null; + logToBeCreated: ILogCreate | null; + thread: ILog | null; } // Action types export enum ActionTypes { + FETCH_THREAD_REQUEST = 'jiskefet/log/FETCH_THREAD_REQUEST', + FETCH_THREAD_SUCCESS = 'jiskefet/log/FETCH_THREAD_SUCCESS', FETCH_LOGS_REQUEST = 'jiskefet/log/FETCH_LOGS_LOG_REQUEST', FETCH_LOGS_SUCCESS = 'jiskefet/log/FETCH_LOGS_LOG_SUCCESS', FETCH_LOG_REQUEST = 'jiskefet/log/FETCH_LOG_REQUEST', @@ -39,13 +43,22 @@ export enum ActionTypes { } // Action interfaces +export interface FetchThreadRequestAction extends Action { + type: ActionTypes.FETCH_THREAD_REQUEST; +} + +export interface FetchThreadSuccessAction extends Action { + type: ActionTypes.FETCH_THREAD_SUCCESS; + payload: ISuccessObject; +} + export interface FetchLogsByLogRequestAction extends Action { type: ActionTypes.FETCH_LOGS_REQUEST; } export interface FetchLogsByLogSuccessAction extends Action { type: ActionTypes.FETCH_LOGS_SUCCESS; - payload: CollectionResponseObject; + payload: ICollectionSuccessObject; } export interface FetchLogRequestAction extends Action { @@ -54,7 +67,7 @@ export interface FetchLogRequestAction extends Action { export interface FetchLogSuccessAction extends Action { type: ActionTypes.FETCH_LOG_SUCCESS; - payload: ResponseObject; + payload: ISuccessObject; } export interface LinkLogToLogRequestAction extends Action { @@ -75,7 +88,7 @@ export interface CreateLogSuccessAction extends Action { export interface SetLogToBeCreatedAction extends Action { type: ActionTypes.SET_LOG_TO_BE_CREATED; - payload: LogCreate; + payload: ILogCreate; } export interface ClearLogToBeCreatedAction extends Action { @@ -84,6 +97,8 @@ export interface ClearLogToBeCreatedAction extends Action { // Combine actions into single type export type LogAction = + | FetchThreadRequestAction + | FetchThreadSuccessAction | FetchLogsByLogRequestAction | FetchLogsByLogSuccessAction | FetchLogRequestAction diff --git a/src/app/redux/ducks/run/actions.ts b/src/app/redux/ducks/run/actions.ts index 0fe3c59..d07eac4 100644 --- a/src/app/redux/ducks/run/actions.ts +++ b/src/app/redux/ducks/run/actions.ts @@ -15,15 +15,15 @@ import { LinkLogToRunRequestAction, LinkLogToRunSuccesAction, } from './types'; -import { Run } from '../../../interfaces/Run'; -import { CollectionResponseObject, ResponseObject } from '../../../interfaces/ResponseObject'; +import { IRun } from '../../../interfaces/Run'; +import { ICollectionSuccessObject, ISuccessObject } from '../../../interfaces/ResponseObject'; // Action creators export const fetchRunsRequest = (): FetchRunsByLogRequestAction => ({ type: ActionTypes.FETCH_RUNS_REQUEST }); -export const fetchRunsSuccess = (payload: CollectionResponseObject): FetchRunsByLogSuccessAction => ({ +export const fetchRunsSuccess = (payload: ICollectionSuccessObject): FetchRunsByLogSuccessAction => ({ type: ActionTypes.FETCH_RUNS_SUCCESS, payload }); @@ -32,7 +32,7 @@ export const fetchRunRequest = (): FetchRunRequestAction => ({ type: ActionTypes.FETCH_RUN_REQUEST }); -export const fetchRunSuccess = (run: ResponseObject): FetchRunSuccessAction => ({ +export const fetchRunSuccess = (run: ISuccessObject): FetchRunSuccessAction => ({ type: ActionTypes.FETCH_RUN_SUCCESS, payload: run }); diff --git a/src/app/redux/ducks/run/operations.ts b/src/app/redux/ducks/run/operations.ts index 9299c16..bb16976 100644 --- a/src/app/redux/ducks/run/operations.ts +++ b/src/app/redux/ducks/run/operations.ts @@ -9,8 +9,8 @@ import { ThunkResult, RunAction } from './types'; import { ThunkDispatch } from 'redux-thunk'; import { RootState } from '../../types'; -import { Run } from '../../../interfaces/Run'; -import { HttpError } from '../../../interfaces/HttpError'; +import { IRun } from '../../../interfaces/Run'; +import { IHttpError } from '../../../interfaces/HttpError'; import { request } from '../../../request'; import { fetchRunsRequest, @@ -23,7 +23,7 @@ import { import { getRuns, getRun, linkLogToRunUrl } from '../../../constants/apiUrls'; import { ErrorAction } from '../error/types'; import { addHttpError } from '../error/actions'; -import { CollectionResponseObject, ResponseObject } from '../../../interfaces/ResponseObject'; +import { ICollectionSuccessObject, ISuccessObject } from '../../../interfaces/ResponseObject'; // Thunks export const fetchRuns = (query?: string): ThunkResult> => @@ -32,9 +32,9 @@ export const fetchRuns = (query?: string): ThunkResult> => return request({ method: 'GET', url: getRuns(query) - }).then((result: CollectionResponseObject) => { + }).then((result: ICollectionSuccessObject) => { dispatch(fetchRunsSuccess(result)); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; @@ -45,9 +45,9 @@ export const fetchRun = (id: number | string): ThunkResult> => return request({ method: 'GET', url: getRun(id) - }).then((result: ResponseObject) => { + }).then((result: ISuccessObject) => { dispatch(fetchRunSuccess(result)); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; @@ -61,7 +61,7 @@ export const linkLogToRun = (logId: number, runNumber: number): ThunkResult { dispatch(linkLogToRunSucces()); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; diff --git a/src/app/redux/ducks/run/selectors.ts b/src/app/redux/ducks/run/selectors.ts index 9c5185d..405cb05 100644 --- a/src/app/redux/ducks/run/selectors.ts +++ b/src/app/redux/ducks/run/selectors.ts @@ -7,14 +7,14 @@ */ import { RootState } from '../../types'; -import { Run } from '../../../interfaces/Run'; +import { IRun } from '../../../interfaces/Run'; // Selectors export const selectIsFetchingRuns = (state: RootState): boolean => state.run.isFetchingRuns; export const selectIsFetchingRun = (state: RootState): boolean => state.run.isFetchingRun; export const selectIsPatchingLinkLogToRun = (state: RootState): boolean => state.run.isPatchingLinkLogToRun; -export const selectRuns = (state: RootState): Run[] => state.run.runs; +export const selectRuns = (state: RootState): IRun[] => state.run.runs; export const selectRunCount = (state: RootState): number => state.run.count; -export const selectCurrentRun = (state: RootState): Run | null => ( +export const selectCurrentRun = (state: RootState): IRun | null => ( state.run.current ); diff --git a/src/app/redux/ducks/run/types.ts b/src/app/redux/ducks/run/types.ts index 9f032d1..48885e1 100644 --- a/src/app/redux/ducks/run/types.ts +++ b/src/app/redux/ducks/run/types.ts @@ -9,17 +9,17 @@ import { Action } from 'redux'; import { ThunkAction } from 'redux-thunk'; import { RootState } from '../../types'; -import { Run } from '../../../interfaces/Run'; -import { CollectionResponseObject, ResponseObject } from '../../../interfaces/ResponseObject'; +import { IRun } from '../../../interfaces/Run'; +import { ICollectionSuccessObject, ISuccessObject } from '../../../interfaces/ResponseObject'; // State interface export interface RunState { isFetchingRuns: boolean; isFetchingRun: boolean; isPatchingLinkLogToRun: boolean; - runs: Run[]; + runs: IRun[]; count: number; - current: Run | null; + current: IRun | null; } // Action types @@ -39,7 +39,7 @@ export interface FetchRunsByLogRequestAction extends Action { export interface FetchRunsByLogSuccessAction extends Action { type: ActionTypes.FETCH_RUNS_SUCCESS; - payload: CollectionResponseObject; + payload: ICollectionSuccessObject; } export interface FetchRunRequestAction extends Action { @@ -48,7 +48,7 @@ export interface FetchRunRequestAction extends Action { export interface FetchRunSuccessAction extends Action { type: ActionTypes.FETCH_RUN_SUCCESS; - payload: ResponseObject; + payload: ISuccessObject; } export interface LinkLogToRunRequestAction extends Action { diff --git a/src/app/redux/ducks/subsystem/actions.ts b/src/app/redux/ducks/subsystem/actions.ts index f120973..789854f 100644 --- a/src/app/redux/ducks/subsystem/actions.ts +++ b/src/app/redux/ducks/subsystem/actions.ts @@ -6,7 +6,7 @@ * copied verbatim in the file "LICENSE" */ -import { Subsystem } from '../../../interfaces/SubSytem'; +import { ISubsystem } from '../../../interfaces/SubSytem'; import { FetchSubsystemsRequestAction, ActionTypes, @@ -20,25 +20,26 @@ import { CreateTokenRequestAction, CreateTokenSuccessAction } from './types'; -import { SubsystemOverview } from '../../../interfaces/SubsystemOverview'; -import { SubsystemPermission } from '../../../interfaces/SubsystemPermission'; -import { CollectionResponseObject, ResponseObject } from '../../../interfaces/ResponseObject'; +import { ISubsystemOverview } from '../../../interfaces/SubsystemOverview'; +import { ISubsystemPermission } from '../../../interfaces/SubsystemPermission'; +import { ICollectionSuccessObject, ISuccessObject } from '../../../interfaces/ResponseObject'; // Action creators export const fetchSubsystemsRequest = (): FetchSubsystemsRequestAction => ({ type: ActionTypes.FETCH_SUBSYSTEMS_REQUEST }); -export const fetchSubsystemsSuccess = (payload: CollectionResponseObject): FetchSubsystemsSuccessAction => ({ - type: ActionTypes.FETCH_SUBSYSTEMS_SUCCESS, - payload -}); +export const fetchSubsystemsSuccess = + (payload: ICollectionSuccessObject): FetchSubsystemsSuccessAction => ({ + type: ActionTypes.FETCH_SUBSYSTEMS_SUCCESS, + payload + }); export const fetchSubsystemRequest = (): FetchSubsystemRequestAction => ({ type: ActionTypes.FETCH_SUBSYSTEM_REQUEST }); -export const fetchSubsystemSuccess = (payload: ResponseObject): FetchSubsystemSuccessAction => ({ +export const fetchSubsystemSuccess = (payload: ISuccessObject): FetchSubsystemSuccessAction => ({ type: ActionTypes.FETCH_SUBSYSTEM_SUCCESS, payload }); @@ -48,7 +49,7 @@ export const fetchSubsystemOverviewsRequest = (): FetchSubsystemOverviewsRequest }); export const fetchSubsystemOverviewsSuccess = ( - payload: CollectionResponseObject): FetchSubsystemOverviewsSuccessAction => ({ + payload: ICollectionSuccessObject): FetchSubsystemOverviewsSuccessAction => ({ type: ActionTypes.FETCH_SUBSYSTEM_OVERVIEWS_SUCCESS, payload }); @@ -58,7 +59,7 @@ export const fetchSubsystemPermissionsRequest = (): FetchSubsystemPermissionsReq }); export const fetchSubsystemPermissionsSuccess - = (payload: CollectionResponseObject): FetchSubsystemPermissionsSuccessAction => ({ + = (payload: ICollectionSuccessObject): FetchSubsystemPermissionsSuccessAction => ({ type: ActionTypes.FETCH_SUBSYSTEM_PERMISSIONS_SUCCESS, payload }); diff --git a/src/app/redux/ducks/subsystem/operations.ts b/src/app/redux/ducks/subsystem/operations.ts index 9ad4b5b..24023cd 100644 --- a/src/app/redux/ducks/subsystem/operations.ts +++ b/src/app/redux/ducks/subsystem/operations.ts @@ -19,8 +19,8 @@ import { getSubsystemPermissions, postToken } from '../../../constants/apiUrls'; -import { Subsystem } from '../../../interfaces/SubSytem'; -import { HttpError } from '../../../interfaces/HttpError'; +import { ISubsystem } from '../../../interfaces/SubSytem'; +import { IHttpError } from '../../../interfaces/HttpError'; import { request } from '../../../request'; import { fetchSubsystemsRequest, @@ -34,17 +34,17 @@ import { createTokenRequest, createTokenSuccess } from './actions'; -import { SubsystemOverview } from '../../../interfaces/SubsystemOverview'; +import { ISubsystemOverview } from '../../../interfaces/SubsystemOverview'; import { - SubsystemPermission, - SubsystemPermissionCreate, - SubsystemToken + ISubsystemPermission, + ISubsystemPermissionCreate, + ISubsystemToken } from '../../../interfaces/SubsystemPermission'; import { addHttpError } from '../error/actions'; import { ErrorAction } from '../error/types'; import { addSuccessMessage } from '../success/actions'; import { SuccessAction } from '../success/types'; -import { CollectionResponseObject, ResponseObject } from '../../../interfaces/ResponseObject'; +import { ICollectionSuccessObject, ISuccessObject } from '../../../interfaces/ResponseObject'; // Thunks export const fetchSubsystems = (): ThunkResult => @@ -53,9 +53,9 @@ export const fetchSubsystems = (): ThunkResult => request({ method: 'GET', url: getSubsystems() - }).then((result: CollectionResponseObject) => { + }).then((result: ICollectionSuccessObject) => { dispatch(fetchSubsystemsSuccess(result)); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; @@ -66,9 +66,9 @@ export const fetchSubsystem = (id: string | number): ThunkResult> return request({ method: 'GET', url: getSubsystem(id) - }).then((result: ResponseObject) => { + }).then((result: ISuccessObject) => { dispatch(fetchSubsystemSuccess(result)); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; @@ -79,9 +79,9 @@ export const fetchSubsystemOverviews = (query?: string): ThunkResult => request({ method: 'GET', url: getSubsystemOverviews(query) - }).then((result: CollectionResponseObject) => { + }).then((result: ICollectionSuccessObject) => { dispatch(fetchSubsystemOverviewsSuccess(result)); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; @@ -92,26 +92,26 @@ export const fetchSubsystemPermissions = (userId: number): ThunkResult => request({ method: 'GET', url: getSubsystemPermissions(userId) - }).then((result: CollectionResponseObject) => { + }).then((result: ICollectionSuccessObject) => { dispatch(fetchSubsystemPermissionsSuccess(result)); - }).catch((error: HttpError) => { + }).catch((error: IHttpError) => { dispatch(addHttpError(error)); }); }; -export const createToken = (payload: SubsystemPermissionCreate): ThunkResult> => +export const createToken = (payload: ISubsystemPermissionCreate): ThunkResult> => async (dispatch: ThunkDispatch): Promise => { dispatch(createTokenRequest()); return request({ method: 'POST', data: payload, url: postToken(payload.user) - }).then((result: ResponseObject) => { + }).then((result: ISuccessObject
{error.message}
{error.error.message}
For a machine to make use of the jiskefet API, +
For a machine to make use of the API, it needs to use an access token for authentication and authorization. This token can be generated here and is linked to your account. @@ -84,16 +87,16 @@ export default class CreateToken extends MithrilTsxComponent<{}> {
What's the token for?
Please sign in to use the application.