| borgbutler-webapp/src/components/views/archives/ArchiveFileListView.jsx | ●●●●● patch | view | raw | blame | history | |
| borgbutler-webapp/src/components/views/archives/ArchiveView.jsx | ●●●●● patch | view | raw | blame | history | |
| borgbutler-webapp/src/components/views/archives/FileListEntry.jsx | ●●●●● patch | view | raw | blame | history | |
| borgbutler-webapp/src/components/views/archives/FileListFilters.jsx | ●●●●● patch | view | raw | blame | history | |
| borgbutler-webapp/src/components/views/archives/FileListTable.jsx | ●●●●● patch | view | raw | blame | history |
borgbutler-webapp/src/components/views/archives/ArchiveFileListView.jsx
New file @@ -0,0 +1,91 @@ import React from 'react' import {getRestServiceUrl, humanFileSize} from '../../../utilities/global'; import ErrorAlert from '../../general/ErrorAlert'; import {IconRefresh} from "../../general/IconComponents"; class ArchiveView extends React.Component { state = { isFetching: false, activeTab: '1', }; componentDidMount = () => { this.fetchArchiveFileList(); }; fetchArchiveFileList = (force) => { let forceReload = false; if (force && confirm('Are you sure you want to reload the archive file list? This may take a long time...')) { forceReload = true; } this.setState({ isFetching: true, failed: false }); fetch(getRestServiceUrl('repos/archive', { repo: this.state.repoId, archive: this.state.archiveId, force: forceReload }), { method: 'GET', headers: { 'Accept': 'application/json' } }) .then(response => response.json()) .then(json => { this.setState({ isFetching: false, archive: json }) }) .catch(() => this.setState({isFetching: false, failed: true})); }; render = () => { let content = undefined; let archive = this.state.archive; let pageHeader = ''; if (this.state.isFetching) { content = <i>Loading...</i>; } else if (this.state.failed) { content = <ErrorAlert title={'Cannot load Repositories'} description={'Something went wrong during contacting the rest api.'} action={{ handleClick: this.fetchArchive, title: 'Try again' }} />; } else if (this.state.archive) { pageHeader = <React.Fragment> {archive.repoDisplayName} <div className={'btn btn-outline-primary refresh-button-right'} onClick={this.fetchArchive.bind(this, true)} > <IconRefresh/> </div> </React.Fragment>; content = <React.Fragment> </React.Fragment>; } return <React.Fragment> <PageHeader> {pageHeader} </PageHeader> {content} </React.Fragment>; }; constructor(props) { super(props); this.fetchArchive = this.fetchArchive.bind(this); } } export default ArchiveView; borgbutler-webapp/src/components/views/archives/ArchiveView.jsx
@@ -5,6 +5,7 @@ import ErrorAlert from '../../general/ErrorAlert'; import {IconRefresh} from "../../general/IconComponents"; import classNames from "classnames"; import FileListTable from "./FileListTable"; class ArchiveView extends React.Component { @@ -21,6 +22,10 @@ fetchArchive = (force) => { let forceReload = false; if (force && window.confirm('Are you sure you want to reload the archive file list? This may take a long time...')) { forceReload = true; } this.setState({ isFetching: true, failed: false @@ -28,7 +33,7 @@ fetch(getRestServiceUrl('archives', { repo: this.state.repoId, archive: this.state.archiveId, force: force force: forceReload }), { method: 'GET', headers: { @@ -170,7 +175,9 @@ </Table> </TabPane> <TabPane tabId={'2'}> Hurzel <FileListTable entries={this.props.entries} /> </TabPane> </TabContent> </React.Fragment>; borgbutler-webapp/src/components/views/archives/FileListEntry.jsx
New file @@ -0,0 +1,19 @@ import React from 'react'; import PropTypes from 'prop-types'; import Highlight from 'react-highlighter'; function FileListEntry({entry, search}) { return ( <tr> <td className={'tt'}>{entry.mode}</td> <td className={'tt'}><Highlight search={search}>{entry.path}</Highlight></td> </tr> ); } FileListEntry.propTypes = { entry: PropTypes.shape({}).isRequired, search: PropTypes.string, }; export default FileListEntry; borgbutler-webapp/src/components/views/archives/FileListFilters.jsx
New file @@ -0,0 +1,91 @@ import React from 'react'; import PropTypes from 'prop-types'; import {FormButton, FormInput, FormLabel, FormSelect, FormOption} from '../../general/forms/FormComponents'; import {IconRefresh} from '../../general/IconComponents'; import I18n from '../../general/translation/I18n'; function FileListFilters({loadLog, changeFilter, filters}) { return ( <form onSubmit={loadLog} className={'form-inline'} > <FormLabel length={1}> Filter: </FormLabel> <FormSelect value={filters.threshold} name={'threshold'} onChange={changeFilter} hint={<I18n name={'logviewer.filter.level.hint'}/>} > <FormOption value={'error'}/> <FormOption value={'warn'}/> <FormOption value={'info'}/> <FormOption value={'debug'}/> <FormOption value={'trace'}/> </FormSelect> <FormInput value={filters.search} name={'search'} onChange={changeFilter} fieldLength={5} /> <FormSelect value={filters.locationFormat} name={'locationFormat'} onChange={changeFilter} hint={<I18n name={'logviewer.filter.location.hint'}/>} > <FormOption value={'none'} i18nKey={'common.none'}/> <FormOption value={'short'} i18nKey={'logviewer.filter.location.option.short'}/> <FormOption value={'normal'} i18nKey={'logviewer.filter.location.option.normal'}/> </FormSelect> <FormSelect value={filters.showStackTrace} name={'showStackTrace'} onChange={changeFilter} hint={<I18n name={'logviewer.filter.stacktraces.showHide.hint'}/>} > <FormOption value={'false'} i18nKey={'common.none'}/> <FormOption value={'true'} i18nKey={'logviewer.filter.stacktraces'}/> </FormSelect> <FormSelect value={filters.maxSize} name={'maxSize'} onChange={changeFilter} hint={<I18n name={'common.limitsResultSize'} />} > <FormOption value={'50'} /> <FormOption value={'100'} /> <FormOption value={'500'} /> <FormOption value={'1000'} /> <FormOption value={'10000'} /> </FormSelect> <FormButton type={'submit'} bsStyle={'primary'}> <IconRefresh/> </FormButton> </form> ); } FileListFilters.propTypes = { changeFilter: PropTypes.func.isRequired, filters: PropTypes.shape({ threshold: PropTypes.oneOf(['error', 'warn', 'info', 'debug', 'trace']), search: PropTypes.string, locationFormat: PropTypes.oneOf(['none', 'short', 'normal']), showStackTrace: PropTypes.oneOf(['true', 'false']), maxSize: PropTypes.oneOf(['50', '100', '500', '1000', '10000']), ascendingOrder: PropTypes.oneOf(['true', 'false']) }).isRequired, loadLog: PropTypes.func.isRequired }; export default FileListFilters; borgbutler-webapp/src/components/views/archives/FileListTable.jsx
New file @@ -0,0 +1,44 @@ import React from 'react'; import PropTypes from 'prop-types'; import {Table} from 'reactstrap'; import FileListEntry from './FileListEntry'; function FileListTable({entries, search}) { const lowercaseSearch = search.toLowerCase(); return ( <Table striped bordered hover size={'sm'} responsive> <thead> <tr> <th>Mode</th> <th style={{whiteSpace: 'nowrap'}}> Date </th> <th>Path</th> </tr> </thead> <tbody> {entries .filter(entry => [entry.message] .join('|#|').toLowerCase() .indexOf(lowercaseSearch) !== -1) .map((entry, index) => <FileListEntry entry={entry} search={lowercaseSearch} key={index} />)} </tbody> </Table> ); } FileListTable.propTypes = { entries: PropTypes.array, search: PropTypes.string }; FileListTable.defaultProps = { entries: [], search: '' }; export default FileListTable;