|
@@ -1,356 +0,0 @@
|
|
|
-import React from 'react';
|
|
|
|
|
-import PropTypes from 'prop-types';
|
|
|
|
|
-import * as toastr from 'toastr';
|
|
|
|
|
-
|
|
|
|
|
-import { withTranslation } from 'react-i18next';
|
|
|
|
|
-
|
|
|
|
|
-import Page from '../PageList/Page';
|
|
|
|
|
-import SearchResultList from './SearchResultList';
|
|
|
|
|
-import DeletePageListModal from './DeletePageListModal';
|
|
|
|
|
-import AppContainer from '~/client/services/AppContainer';
|
|
|
|
|
-import { withUnstatedContainers } from '../UnstatedUtils';
|
|
|
|
|
-
|
|
|
|
|
-// NOTE : this file will be deleted in the future. Merge conflict happend in this file, so temporaly kept this left here.
|
|
|
|
|
-// Task 77833 deleted this file ;
|
|
|
|
|
-class SearchResult extends React.Component {
|
|
|
|
|
-
|
|
|
|
|
- constructor(props) {
|
|
|
|
|
- super(props);
|
|
|
|
|
- this.state = {
|
|
|
|
|
- deletionMode: false,
|
|
|
|
|
- selectedPages: new Set(),
|
|
|
|
|
- isDeleteCompletely: undefined,
|
|
|
|
|
- isDeleteConfirmModalShown: false,
|
|
|
|
|
- errorMessageForDeleting: undefined,
|
|
|
|
|
- };
|
|
|
|
|
- this.toggleDeleteCompletely = this.toggleDeleteCompletely.bind(this);
|
|
|
|
|
- this.deleteSelectedPages = this.deleteSelectedPages.bind(this);
|
|
|
|
|
- this.closeDeleteConfirmModal = this.closeDeleteConfirmModal.bind(this);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- isNotSearchedYet() {
|
|
|
|
|
- return !this.props.searchResultMeta.took;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- isNotFound() {
|
|
|
|
|
- return this.props.searchingKeyword !== '' && this.props.pages.length === 0;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- isError() {
|
|
|
|
|
- if (this.props.searchError !== null) {
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * move the page
|
|
|
|
|
- */
|
|
|
|
|
- visitPageButtonHandler(e) {
|
|
|
|
|
- window.location.href = e.currentTarget.value;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * toggle checkbox and add (or delete from) selected pages list
|
|
|
|
|
- *
|
|
|
|
|
- * @param {any} page
|
|
|
|
|
- * @memberof SearchResult
|
|
|
|
|
- */
|
|
|
|
|
- toggleCheckbox(page) {
|
|
|
|
|
- if (this.state.selectedPages.has(page)) {
|
|
|
|
|
- this.state.selectedPages.delete(page);
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- this.state.selectedPages.add(page);
|
|
|
|
|
- }
|
|
|
|
|
- this.setState({ isDeleteConfirmModalShown: false });
|
|
|
|
|
- this.setState({ selectedPages: this.state.selectedPages });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * check and return is all pages selected for delete?
|
|
|
|
|
- *
|
|
|
|
|
- * @returns all pages selected (or not)
|
|
|
|
|
- * @memberof SearchResult
|
|
|
|
|
- */
|
|
|
|
|
- isAllSelected() {
|
|
|
|
|
- return this.state.selectedPages.size === this.props.pages.length;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * handle checkbox clicking that all pages select for delete
|
|
|
|
|
- *
|
|
|
|
|
- * @memberof SearchResult
|
|
|
|
|
- */
|
|
|
|
|
- handleAllSelect() {
|
|
|
|
|
- if (this.isAllSelected()) {
|
|
|
|
|
- this.state.selectedPages.clear();
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- this.state.selectedPages.clear();
|
|
|
|
|
- this.props.pages.map((page) => {
|
|
|
|
|
- this.state.selectedPages.add(page);
|
|
|
|
|
- return;
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- this.setState({ selectedPages: this.state.selectedPages });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * change deletion mode
|
|
|
|
|
- *
|
|
|
|
|
- * @memberof SearchResult
|
|
|
|
|
- */
|
|
|
|
|
- handleDeletionModeChange() {
|
|
|
|
|
- this.state.selectedPages.clear();
|
|
|
|
|
- this.setState({ deletionMode: !this.state.deletionMode });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * toggle check delete completely
|
|
|
|
|
- *
|
|
|
|
|
- * @memberof SearchResult
|
|
|
|
|
- */
|
|
|
|
|
- toggleDeleteCompletely() {
|
|
|
|
|
- // request で completely が undefined でないと指定アリと見なされるため
|
|
|
|
|
- this.setState({ isDeleteCompletely: this.state.isDeleteCompletely ? undefined : true });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * delete selected pages
|
|
|
|
|
- *
|
|
|
|
|
- * @memberof SearchResult
|
|
|
|
|
- */
|
|
|
|
|
- deleteSelectedPages() {
|
|
|
|
|
- const deleteCompletely = this.state.isDeleteCompletely;
|
|
|
|
|
- Promise.all(Array.from(this.state.selectedPages).map((page) => {
|
|
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
|
|
- const pageId = page._id;
|
|
|
|
|
- const revisionId = page.revision._id;
|
|
|
|
|
-
|
|
|
|
|
- this.props.appContainer.apiPost('/pages.remove', { page_id: pageId, revision_id: revisionId, completely: deleteCompletely })
|
|
|
|
|
- .then((res) => {
|
|
|
|
|
- if (res.ok) {
|
|
|
|
|
- this.state.selectedPages.delete(page);
|
|
|
|
|
- return resolve();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return reject();
|
|
|
|
|
-
|
|
|
|
|
- })
|
|
|
|
|
- .catch((err) => {
|
|
|
|
|
- console.log(err.message); // eslint-disable-line no-console
|
|
|
|
|
- this.setState({ errorMessageForDeleting: err.message });
|
|
|
|
|
- return reject();
|
|
|
|
|
- });
|
|
|
|
|
- });
|
|
|
|
|
- }))
|
|
|
|
|
- .then(() => {
|
|
|
|
|
- window.location.reload();
|
|
|
|
|
- })
|
|
|
|
|
- .catch((err) => {
|
|
|
|
|
- toastr.error(err, 'Error occured', {
|
|
|
|
|
- closeButton: true,
|
|
|
|
|
- progressBar: true,
|
|
|
|
|
- newestOnTop: false,
|
|
|
|
|
- showDuration: '100',
|
|
|
|
|
- hideDuration: '100',
|
|
|
|
|
- timeOut: '3000',
|
|
|
|
|
- });
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * open confirm modal for page selection delete
|
|
|
|
|
- *
|
|
|
|
|
- * @memberof SearchResult
|
|
|
|
|
- */
|
|
|
|
|
- showDeleteConfirmModal() {
|
|
|
|
|
- this.setState({ isDeleteConfirmModalShown: true });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * close confirm modal for page selection delete
|
|
|
|
|
- *
|
|
|
|
|
- * @memberof SearchResult
|
|
|
|
|
- */
|
|
|
|
|
- closeDeleteConfirmModal() {
|
|
|
|
|
- this.setState({
|
|
|
|
|
- isDeleteConfirmModalShown: false,
|
|
|
|
|
- errorMessageForDeleting: undefined,
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- renderListView(pages) {
|
|
|
|
|
- return pages.map((page) => {
|
|
|
|
|
- // Add prefix 'id_' in pageId, because scrollspy of bootstrap doesn't work when the first letter of id attr of target component is numeral.
|
|
|
|
|
- const pageId = `#id_${page._id}`;
|
|
|
|
|
- return (
|
|
|
|
|
- <li key={page._id} className="nav-item page-list-li w-100 m-0 border-bottom">
|
|
|
|
|
- <a className="nav-link page-list-link d-flex align-items-baseline" href={pageId}>
|
|
|
|
|
- <div className="form-check my-auto">
|
|
|
|
|
- <input className="form-check-input my-auto" type="checkbox" value="" id="flexCheckDefault" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <Page page={page} noLink />
|
|
|
|
|
- <div className="ml-auto d-flex">
|
|
|
|
|
- { this.state.deletionMode
|
|
|
|
|
- && (
|
|
|
|
|
- <div className="custom-control custom-checkbox custom-checkbox-danger">
|
|
|
|
|
- <input
|
|
|
|
|
- type="checkbox"
|
|
|
|
|
- id={`page-delete-check-${page._id}`}
|
|
|
|
|
- className="custom-control-input search-result-list-delete-checkbox"
|
|
|
|
|
- value={pageId}
|
|
|
|
|
- checked={this.state.selectedPages.has(page)}
|
|
|
|
|
- onChange={() => { return this.toggleCheckbox(page) }}
|
|
|
|
|
- />
|
|
|
|
|
- <label className="custom-control-label" htmlFor={`page-delete-check-${page._id}`}></label>
|
|
|
|
|
- </div>
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- <div className="page-list-option">
|
|
|
|
|
- <button type="button" className="btn btn-link p-0" value={page.path} onClick={this.visitPageButtonHandler}><i className="icon-login" /></button>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </a>
|
|
|
|
|
- <div>{page.highlight['body.en']?.map(text => <p dangerouslySetInnerHTML={{ __html: text }} />)}</div>
|
|
|
|
|
- </li>
|
|
|
|
|
- );
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- render() {
|
|
|
|
|
- const { t } = this.props;
|
|
|
|
|
-
|
|
|
|
|
- if (this.isError()) {
|
|
|
|
|
- return (
|
|
|
|
|
- <div className="content-main">
|
|
|
|
|
- <i className="searcing fa fa-warning"></i> Error on searching.
|
|
|
|
|
- </div>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (this.isNotSearchedYet()) {
|
|
|
|
|
- return <div />;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (this.isNotFound()) {
|
|
|
|
|
- let under = '';
|
|
|
|
|
- if (this.props.tree != null) {
|
|
|
|
|
- under = ` under "${this.props.tree}"`;
|
|
|
|
|
- }
|
|
|
|
|
- return (
|
|
|
|
|
- <div className="content-main">
|
|
|
|
|
- <i className="icon-fw icon-info" /> No page found with "{this.props.searchingKeyword}"{under}
|
|
|
|
|
- </div>
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let deletionModeButtons = '';
|
|
|
|
|
- let allSelectCheck = '';
|
|
|
|
|
-
|
|
|
|
|
- if (this.state.deletionMode) {
|
|
|
|
|
- deletionModeButtons = (
|
|
|
|
|
- <div className="btn-group">
|
|
|
|
|
- <button type="button" className="btn btn-outline-secondary btn-sm rounded-pill-weak" onClick={() => { return this.handleDeletionModeChange() }}>
|
|
|
|
|
- <i className="icon-ban" /> {t('search_result.cancel')}
|
|
|
|
|
- </button>
|
|
|
|
|
- <button
|
|
|
|
|
- type="button"
|
|
|
|
|
- className="btn btn-danger btn-sm rounded-pill-weak"
|
|
|
|
|
- onClick={() => { return this.showDeleteConfirmModal() }}
|
|
|
|
|
- disabled={this.state.selectedPages.size === 0}
|
|
|
|
|
- >
|
|
|
|
|
- <i className="icon-trash" /> {t('search_result.delete')}
|
|
|
|
|
- </button>
|
|
|
|
|
- </div>
|
|
|
|
|
- );
|
|
|
|
|
- allSelectCheck = (
|
|
|
|
|
- <div className="custom-control custom-checkbox custom-checkbox-danger">
|
|
|
|
|
- <input
|
|
|
|
|
- id="all-select-check"
|
|
|
|
|
- className="custom-control-input"
|
|
|
|
|
- type="checkbox"
|
|
|
|
|
- onChange={() => { return this.handleAllSelect() }}
|
|
|
|
|
- checked={this.isAllSelected()}
|
|
|
|
|
- />
|
|
|
|
|
- <label className="custom-control-label" htmlFor="all-select-check"> {t('search_result.check_all')}</label>
|
|
|
|
|
- </div>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- deletionModeButtons = (
|
|
|
|
|
- <div className="btn-group">
|
|
|
|
|
- <button type="button" className="btn btn-outline-secondary rounded-pill btn-sm" onClick={() => { return this.handleDeletionModeChange() }}>
|
|
|
|
|
- <i className="ti-check-box" /> {t('search_result.deletion_mode_btn_lavel')}
|
|
|
|
|
- </button>
|
|
|
|
|
- </div>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const listView = this.renderListView(this.props.pages);
|
|
|
|
|
-
|
|
|
|
|
- /*
|
|
|
|
|
- UI あとで考える
|
|
|
|
|
- <span className="search-result-meta">Found: {this.props.searchResultMeta.total} pages with "{this.props.searchingKeyword}"</span>
|
|
|
|
|
- */
|
|
|
|
|
- return (
|
|
|
|
|
- <div className="content-main">
|
|
|
|
|
- <div className="search-result row" id="search-result">
|
|
|
|
|
- <div className="col-lg-6 d-none d-lg-block page-list search-result-list pr-0" id="search-result-list">
|
|
|
|
|
- <nav>
|
|
|
|
|
- <div className="d-flex align-items-start justify-content-between mt-1">
|
|
|
|
|
- <div className="search-result-meta">
|
|
|
|
|
- <i className="icon-magnifier" /> Found {this.props.searchResultMeta.total} pages with "{this.props.searchingKeyword}"
|
|
|
|
|
- </div>
|
|
|
|
|
- <div className="text-nowrap">
|
|
|
|
|
- {deletionModeButtons}
|
|
|
|
|
- {allSelectCheck}
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div className="page-list">
|
|
|
|
|
- <ul className="page-list-ul page-list-ul-flat nav nav-pills">{listView}</ul>
|
|
|
|
|
- </div>
|
|
|
|
|
- </nav>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div className="col-lg-6 search-result-content" id="search-result-content">
|
|
|
|
|
- <SearchResultList pages={this.props.pages} searchingKeyword={this.props.searchingKeyword} />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <DeletePageListModal
|
|
|
|
|
- isShown={this.state.isDeleteConfirmModalShown}
|
|
|
|
|
- pages={Array.from(this.state.selectedPages)}
|
|
|
|
|
- errorMessage={this.state.errorMessageForDeleting}
|
|
|
|
|
- cancel={this.closeDeleteConfirmModal}
|
|
|
|
|
- confirmedToDelete={this.deleteSelectedPages}
|
|
|
|
|
- isDeleteCompletely={this.state.isDeleteCompletely}
|
|
|
|
|
- toggleDeleteCompletely={this.toggleDeleteCompletely}
|
|
|
|
|
- />
|
|
|
|
|
- </div> // content-main
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Wrapper component for using unstated
|
|
|
|
|
- */
|
|
|
|
|
-const SearchResultWrapper = withUnstatedContainers(SearchResult, [AppContainer]);
|
|
|
|
|
-
|
|
|
|
|
-SearchResult.propTypes = {
|
|
|
|
|
- appContainer: PropTypes.instanceOf(AppContainer).isRequired,
|
|
|
|
|
- t: PropTypes.func.isRequired, // i18next
|
|
|
|
|
-
|
|
|
|
|
- pages: PropTypes.array.isRequired,
|
|
|
|
|
- searchingKeyword: PropTypes.string.isRequired,
|
|
|
|
|
- searchResultMeta: PropTypes.object.isRequired,
|
|
|
|
|
- searchError: PropTypes.object,
|
|
|
|
|
- tree: PropTypes.string,
|
|
|
|
|
-};
|
|
|
|
|
-SearchResult.defaultProps = {
|
|
|
|
|
- searchError: null,
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-export default withTranslation()(SearchResultWrapper);
|
|
|