<commit_before>import PropTypes from 'prop-types'
import React, {Suspense, useEffect, useMemo, useReducer, useRef, useState} from 'react'
import {searchFormTypePropTypes, selectionStylePropType} from 'tocco-entity-list/src/main'
import {Icon, LoadMask, scrollBehaviourPropType} from 'tocco-ui'
import {viewPersistor} from 'tocco-util'

import isRootLocation from '../../utils/isRootLocation'
import Action from '../Action'
import FileInput from '../FileInput'
import {StyledContentWrapper, StyledIconWrapper} from './StyledComponents'

export const getParent = params => {
  if (params && params.model) {
    const model = params.model.charAt(0).toUpperCase() + params.model.slice(1)
    const key = params.key
    const fullKey = `${model}/${key}`
    return {
      model: 'Docs_list_item',
      key: fullKey // e.g. "Domain/118"
    }
  }
  return null
}

export const getTql = domainTypes =>
  Array.isArray(domainTypes) && domainTypes.length > 0
    ? `exists(relDomain_type where IN(unique_id, ${domainTypes.map(type => `"${type}"`).join(',')}))`
    : null

export const getFormName = (parent, keys) =>
  parent ? 'Docs_list_item' : keys ? 'Root_docs_list_item_specific' : 'Root_docs_list_item'

export const getDefaultLocation = (model, key) => {
  switch (model) {
    case 'Domain':
      return `/docs/domain/${key}/list`
    case 'Folder':
      return `/docs/folder/${key}/list`
    case 'Resource':
      return `/docs/doc/${key}/detail`
    default:
      throw new Error(`Unexpected model: ${model}`)
  }
}

const LazyListApp = React.lazy(() => import('./LazyListApp'))

const DocsView = props => {
  const {
    domainTypes,
    rootNodes,
    navigationStrategy,
    changeListParent,
    onSearchChange,
    emitAction,
    openFileDialog,
    limit,
    searchFormType,
    selectionStyle,
    getCustomLocation,
    disableViewPersistor,
    getListFormName,
    domainDetailFormName,
    folderDetailFormName,
    showActions,
    scrollBehaviour,
    sortable,
    searchMode,
    openResource,
    searchFormCollapsed,
    locale,
    changeSelection,
    changeSearchFormCollapsed,
    params,
    navigate,
    path
  } = props
  // eslint-disable-next-line no-unused-vars
  const [entityListNumber, forceEntityListUpdate] = useReducer(x => x + 1, 0)

  const entityListKey = `entity-list-${entityListNumber}`

  const [selection, setSelection] = useState([])
  const parent = useMemo(() => getParent(params), [params])

  const keys = !parent && rootNodes ? rootNodes.map(node => `${node.entityName}/${node.key}`) : null
  const formName = useMemo(
    () => (getListFormName ? getListFormName(parent, keys) : getFormName(parent, keys)),
    [parent, keys, getListFormName]
  )

  const mounted = useRef(false)
  const searchModeRef = useRef(searchMode)

  searchModeRef.current = searchMode

  useEffect(() => {
    mounted.current = true
    return () => (mounted.current = false)
  })

  useEffect(() => {
    changeListParent(parent)
    setSelection([])
  }, [parent, changeListParent])

  const onSelectChange = selection => {
    setSelection(selection)
    changeSelection(selection)
  }

  const handleRowClick = ({id}) => {
    onSelectChange([id])

    if (searchModeRef.current && isRootLocation(path)) {
      if (!viewPersistor.viewInfoSelector('search').store) {
        // persist search store to allow a "go back to search"
        const searchStore = viewPersistor.viewInfoSelector(entityListKey).store
        viewPersistor.persistViewInfo('search', {store: searchStore})
      }
      forceEntityListUpdate()
    }

    const [model, key] = id.split('/')
    const location = getCustomLocation ? getCustomLocation(model, key) : getDefaultLocation(model, key)

    if (location) {
      if (openResource && model === 'Resource') {
        openResource(location)
      } else {
        navigate(location)
      }
    }
  }

  const tql = !parent && !keys ? getTql(domainTypes) : null

  const handleUploadDocument = function* (definition, selection, parent, params, config, onSuccess, onError) {
    const directory = false
    openFileDialog(directory, onSuccess, onError)
  }

  const handleUploadDirectory = function* (definition, selection, parent, params, config, onSuccess, onError) {
    const directory = true
    openFileDialog(directory, onSuccess, onError)
  }

  const handleSearch = e => {
    if (mounted.current) {
      onSearchChange(e)
    }
  }

  const store = disableViewPersistor
    ? undefined
    : isRootLocation(path)
    ? viewPersistor.viewInfoSelector('search').store
    : null

  const handleStoreCreation = store => {
    if (!disableViewPersistor) {
      viewPersistor.persistViewInfo(entityListKey, {store})
    }
  }

  return (
    <>
      <Suspense fallback={<LoadMask />}>
        <LazyListApp
          key={entityListKey}
          id="documents"
          entityName="Docs_list_item"
          formName={formName}
          limit={limit || 25}
          onRowClick={handleRowClick}
          searchFormPosition="left"
          searchFormType={searchFormType || 'admin'}
          parent={parent}
          onSearchChange={handleSearch}
          store={store}
          onStoreCreate={handleStoreCreation}
          cellRenderers={{
            'dms-label-with-icon': (rowData, column, cellRenderer) => (
              <StyledContentWrapper>
                <StyledIconWrapper>
                  <Icon icon={rowData.icon} />
                </StyledIconWrapper>
                <span>{cellRenderer(column.children[0])}</span>
              </StyledContentWrapper>
            ),
            'dms-actions': (rowData, column, cellRenderer) => (
              <StyledContentWrapper>
                {column.children
                  .filter(c => !c.dmsEntityModel || c.dmsEntityModel === rowData.type)
                  .map(c => cellRenderer(c))}<commit_msg>feat(docs-browser): use `Search_docs_list_item` form in search mode<commit_after>import PropTypes from 'prop-types'
import React, {Suspense, useEffect, useMemo, useReducer, useRef, useState} from 'react'
import {searchFormTypePropTypes, selectionStylePropType} from 'tocco-entity-list/src/main'
import {Icon, LoadMask, scrollBehaviourPropType} from 'tocco-ui'
import {viewPersistor} from 'tocco-util'

import isRootLocation from '../../utils/isRootLocation'
import Action from '../Action'
import FileInput from '../FileInput'
import {StyledContentWrapper, StyledIconWrapper} from './StyledComponents'

export const getParent = params => {
  if (params && params.model) {
    const model = params.model.charAt(0).toUpperCase() + params.model.slice(1)
    const key = params.key
    const fullKey = `${model}/${key}`
    return {
      model: 'Docs_list_item',
      key: fullKey // e.g. "Domain/118"
    }
  }
  return null
}

export const getTql = domainTypes =>
  Array.isArray(domainTypes) && domainTypes.length > 0
    ? `exists(relDomain_type where IN(unique_id, ${domainTypes.map(type => `"${type}"`).join(',')}))`
    : null

export const getFormName = (parent, keys) =>
  parent ? 'Docs_list_item' : keys ? 'Root_docs_list_item_specific' : 'Root_docs_list_item'

export const getDefaultLocation = (model, key) => {
  switch (model) {
    case 'Domain':
      return `/docs/domain/${key}/list`
    case 'Folder':
      return `/docs/folder/${key}/list`
    case 'Resource':
      return `/docs/doc/${key}/detail`
    default:
      throw new Error(`Unexpected model: ${model}`)
  }
}

const LazyListApp = React.lazy(() => import('./LazyListApp'))

const DocsView = props => {
  const {
    domainTypes,
    rootNodes,
    navigationStrategy,
    changeListParent,
    onSearchChange,
    emitAction,
    openFileDialog,
    limit,
    searchFormType,
    selectionStyle,
    getCustomLocation,
    disableViewPersistor,
    getListFormName,
    domainDetailFormName,
    folderDetailFormName,
    showActions,
    scrollBehaviour,
    sortable,
    searchMode,
    openResource,
    searchFormCollapsed,
    locale,
    changeSelection,
    changeSearchFormCollapsed,
    params,
    navigate,
    path
  } = props
  // eslint-disable-next-line no-unused-vars
  const [entityListNumber, forceEntityListUpdate] = useReducer(x => x + 1, 0)

  const entityListKey = `entity-list-${entityListNumber}`

  const [selection, setSelection] = useState([])
  const parent = useMemo(() => getParent(params), [params])

  const keys = !parent && rootNodes ? rootNodes.map(node => `${node.entityName}/${node.key}`) : null
  const formName = useMemo(
    () => (getListFormName ? getListFormName(parent, keys) : getFormName(parent, keys)),
    [parent, keys, getListFormName]
  )

  const mounted = useRef(false)
  const searchModeRef = useRef(searchMode)

  searchModeRef.current = searchMode

  useEffect(() => {
    mounted.current = true
    return () => (mounted.current = false)
  })

  useEffect(() => {
    changeListParent(parent)
    setSelection([])
  }, [parent, changeListParent])

  const onSelectChange = selection => {
    setSelection(selection)
    changeSelection(selection)
  }

  const handleRowClick = ({id}) => {
    onSelectChange([id])

    if (searchModeRef.current && isRootLocation(path)) {
      if (!viewPersistor.viewInfoSelector('search').store) {
        // persist search store to allow a "go back to search"
        const searchStore = viewPersistor.viewInfoSelector(entityListKey).store
        viewPersistor.persistViewInfo('search', {store: searchStore})
      }
      forceEntityListUpdate()
    }

    const [model, key] = id.split('/')
    const location = getCustomLocation ? getCustomLocation(model, key) : getDefaultLocation(model, key)

    if (location) {
      if (openResource && model === 'Resource') {
        openResource(location)
      } else {
        navigate(location)
      }
    }
  }

  const tql = !parent && !keys ? getTql(domainTypes) : null

  const handleUploadDocument = function* (definition, selection, parent, params, config, onSuccess, onError) {
    const directory = false
    openFileDialog(directory, onSuccess, onError)
  }

  const handleUploadDirectory = function* (definition, selection, parent, params, config, onSuccess, onError) {
    const directory = true
    openFileDialog(directory, onSuccess, onError)
  }

  const handleSearch = e => {
    if (mounted.current) {
      onSearchChange(e)
    }
  }

  const store = disableViewPersistor
    ? undefined
    : isRootLocation(path)
    ? viewPersistor.viewInfoSelector('search').store
    : null

  const handleStoreCreation = store => {
    if (!disableViewPersistor) {
      viewPersistor.persistViewInfo(entityListKey, {store})
    }
  }

  return (
    <>
      <Suspense fallback={<LoadMask />}>
        <LazyListApp
          key={entityListKey}
          id="documents"
          entityName="Docs_list_item"
          formName={formName}
          searchListFormName="Search_docs_list_item"
          limit={limit || 25}
          onRowClick={handleRowClick}
          searchFormPosition="left"
          searchFormType={searchFormType || 'admin'}
          parent={parent}
          onSearchChange={handleSearch}
          store={store}
          onStoreCreate={handleStoreCreation}
          cellRenderers={{
            'dms-label-with-icon': (rowData, column, cellRenderer) => (
              <StyledContentWrapper>
                <StyledIconWrapper>
                  <Icon icon={rowData.icon} />
                </StyledIconWrapper>
                <span>{cellRenderer(column.children[0])}</span>
              </StyledContentWrapper>
            ),
            'dms-actions': (rowData, column, cellRenderer) => (
              <StyledContentWrapper>
                {column.children
                  .filter(c => !c.dmsEntityModel || c.dmsEntityModel === rowData.type)
                  .map(c => cellRenderer(c))}
{'fraction_non_alphanumeric': 0.12462933152588325, 'fraction_numerical': 0.001863932898415657, 'mean_word_length': 3.068941744226129, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 71, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1889996', 'n_tokens_mistral': 3589, 'n_tokens_neox': 3491, 'n_words': 736}