Răsfoiți Sursa

Merge pull request #8828 from weseek/feat/146155-design-coding-of-search-result-page

feat: Design coding of search result page
Yuki Takei 1 an în urmă
părinte
comite
0e876d2702

+ 2 - 0
apps/app/public/static/locales/en_US/translation.json

@@ -561,6 +561,8 @@
     "delete_completely": "Delete completely",
     "delete_completely": "Delete completely",
     "include_certain_path": "Include {{pathToInclude}} path ",
     "include_certain_path": "Include {{pathToInclude}} path ",
     "delete_all_selected_page": "Delete All",
     "delete_all_selected_page": "Delete All",
+    "select_all": "Select all",
+    "delete_selected_pages": "Delete selected pages",
     "currently_not_implemented": "This is not currently implemented",
     "currently_not_implemented": "This is not currently implemented",
     "search_again": "Search again",
     "search_again": "Search again",
     "number_of_list_to_display": "Display",
     "number_of_list_to_display": "Display",

+ 2 - 0
apps/app/public/static/locales/fr_FR/translation.json

@@ -561,6 +561,8 @@
     "delete_completely": "Supprimer définitivement",
     "delete_completely": "Supprimer définitivement",
     "include_certain_path": "Inclure le chemin {{pathToInclude}} ",
     "include_certain_path": "Inclure le chemin {{pathToInclude}} ",
     "delete_all_selected_page": "Tout supprimer",
     "delete_all_selected_page": "Tout supprimer",
+    "select_all": "Tout sélectionner",
+    "delete_selected_pages": "Supprimer les pages sélectionnées",
     "currently_not_implemented": "Non implémenté",
     "currently_not_implemented": "Non implémenté",
     "search_again": "Rechercher",
     "search_again": "Rechercher",
     "number_of_list_to_display": "Afficher",
     "number_of_list_to_display": "Afficher",

+ 2 - 0
apps/app/public/static/locales/ja_JP/translation.json

@@ -594,6 +594,8 @@
     "delete_completely": "完全に削除する",
     "delete_completely": "完全に削除する",
     "include_certain_path": "{{pathToInclude}}下を含む ",
     "include_certain_path": "{{pathToInclude}}下を含む ",
     "delete_all_selected_page": "一括削除",
     "delete_all_selected_page": "一括削除",
+    "select_all": "全件選択",
+    "delete_selected_pages": "選択したページを削除",
     "currently_not_implemented": "現在未実装の機能です",
     "currently_not_implemented": "現在未実装の機能です",
     "search_again": "再検索",
     "search_again": "再検索",
     "number_of_list_to_display": "表示件数",
     "number_of_list_to_display": "表示件数",

+ 2 - 0
apps/app/public/static/locales/zh_CN/translation.json

@@ -564,6 +564,8 @@
     "delete_completely": "完全删除",
     "delete_completely": "完全删除",
     "include_certain_path": "包含 {{pathToInclude}} 路径 ",
     "include_certain_path": "包含 {{pathToInclude}} 路径 ",
     "delete_all_selected_page": "删除所有",
     "delete_all_selected_page": "删除所有",
+    "select_all": "选择所有",
+    "delete_selected_pages": "删除选定页面",
     "currently_not_implemented": "这是当前未实现的功能",
     "currently_not_implemented": "这是当前未实现的功能",
     "search_again": "再次搜索",
     "search_again": "再次搜索",
     "number_of_list_to_display": "显示器的数量",
     "number_of_list_to_display": "显示器的数量",

+ 4 - 4
apps/app/src/components/PageControls/PageControls.tsx

@@ -10,13 +10,12 @@ import {
 } from '@growi/core';
 } from '@growi/core';
 import { useRect } from '@growi/ui/dist/utils';
 import { useRect } from '@growi/ui/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
-import { DropdownItem } from 'reactstrap';
 
 
 import {
 import {
   toggleLike, toggleSubscribe,
   toggleLike, toggleSubscribe,
 } from '~/client/services/page-operation';
 } from '~/client/services/page-operation';
 import { toastError } from '~/client/util/toastr';
 import { toastError } from '~/client/util/toastr';
-import { useIsGuestUser, useIsReadOnlyUser } from '~/stores/context';
+import { useIsGuestUser, useIsReadOnlyUser, useIsSearchPage } from '~/stores/context';
 import { useTagEditModal, type IPageForPageDuplicateModal } from '~/stores/modal';
 import { useTagEditModal, type IPageForPageDuplicateModal } from '~/stores/modal';
 import {
 import {
   EditorMode, useEditorMode, useIsDeviceLargerThanMd, usePageControlsX,
   EditorMode, useEditorMode, useIsDeviceLargerThanMd, usePageControlsX,
@@ -136,6 +135,7 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element =
   const { data: isReadOnlyUser } = useIsReadOnlyUser();
   const { data: isReadOnlyUser } = useIsReadOnlyUser();
   const { data: editorMode } = useEditorMode();
   const { data: editorMode } = useEditorMode();
   const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd();
   const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd();
+  const { data: isSearchPage } = useIsSearchPage();
 
 
   const { mutate: mutatePageInfo } = useSWRxPageInfo(pageId, shareLinkId);
   const { mutate: mutatePageInfo } = useSWRxPageInfo(pageId, shareLinkId);
 
 
@@ -279,7 +279,7 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element =
 
 
   return (
   return (
     <div className={`${styles['grw-page-controls']} hstack gap-2`} ref={pageControlsRef}>
     <div className={`${styles['grw-page-controls']} hstack gap-2`} ref={pageControlsRef}>
-      { isViewMode && isDeviceLargerThanMd && (
+      { isViewMode && isDeviceLargerThanMd && !isSearchPage && !isSearchPage && (
         <SearchButton />
         <SearchButton />
       )}
       )}
 
 
@@ -312,7 +312,7 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element =
               bookmarkCount={pageInfo.bookmarkCount}
               bookmarkCount={pageInfo.bookmarkCount}
             />
             />
           )}
           )}
-          {revisionId != null && (
+          {revisionId != null && !isSearchPage && (
             <SeenUserInfo
             <SeenUserInfo
               seenUsers={seenUsers}
               seenUsers={seenUsers}
               sumOfSeenUsers={sumOfSeenUsers}
               sumOfSeenUsers={sumOfSeenUsers}

+ 6 - 6
apps/app/src/components/PrivateLegacyPages.tsx

@@ -348,13 +348,14 @@ const PrivateLegacyPages = (): JSX.Element => {
     );
     );
   }, [t, openConvertModalHandler]);
   }, [t, openConvertModalHandler]);
 
 
-  const searchControlAllAction = useMemo(() => {
+  const extraControls = useMemo(() => {
     const isCheckboxDisabled = hitsCount === 0;
     const isCheckboxDisabled = hitsCount === 0;
 
 
     return (
     return (
-      <div className="search-control d-flex align-items-center">
-        <div className="d-flex ps-md-2">
+      <div className="d-flex align-items-center">
+        <div className="d-flex">
           <OperateAllControl
           <OperateAllControl
+            inputClassName="me-2"
             ref={selectAllControlRef}
             ref={selectAllControlRef}
             isCheckboxDisabled={isCheckboxDisabled}
             isCheckboxDisabled={isCheckboxDisabled}
             onCheckboxChanged={selectAllCheckboxChangedHandler}
             onCheckboxChanged={selectAllCheckboxChangedHandler}
@@ -387,15 +388,14 @@ const PrivateLegacyPages = (): JSX.Element => {
   const searchControl = useMemo(() => {
   const searchControl = useMemo(() => {
     return (
     return (
       <SearchControl
       <SearchControl
-        isSearchServiceReachable
         isEnableSort={false}
         isEnableSort={false}
         isEnableFilter={false}
         isEnableFilter={false}
         initialSearchConditions={{ keyword: initQ, limit: INITIAL_PAGING_SIZE }}
         initialSearchConditions={{ keyword: initQ, limit: INITIAL_PAGING_SIZE }}
         onSearchInvoked={searchInvokedHandler}
         onSearchInvoked={searchInvokedHandler}
-        allControl={searchControlAllAction}
+        extraControls={extraControls}
       />
       />
     );
     );
-  }, [searchInvokedHandler, searchControlAllAction]);
+  }, [searchInvokedHandler, extraControls]);
 
 
   const searchResultListHead = useMemo(() => {
   const searchResultListHead = useMemo(() => {
     if (data == null) {
     if (data == null) {

+ 10 - 0
apps/app/src/components/SearchPage.module.scss

@@ -0,0 +1,10 @@
+@use '@growi/core-styles/scss/bootstrap/init' as bs;
+@use '@growi/ui/scss/atoms/btn-muted';
+
+.search-page :global {
+  .btn.btn-muted-danger {
+    @include btn-muted.colorize(bs.$danger, bs.$secondary);
+
+    --bs-btn-active-bg: rgba(var(--bs-danger-rgb), 0.3);
+  }
+}

+ 60 - 22
apps/app/src/components/SearchPage.tsx

@@ -7,7 +7,7 @@ import { useTranslation } from 'next-i18next';
 import type { ISelectableAll, ISelectableAndIndeterminatable } from '~/client/interfaces/selectable-all';
 import type { ISelectableAll, ISelectableAndIndeterminatable } from '~/client/interfaces/selectable-all';
 import { useKeywordManager } from '~/client/services/search-operation';
 import { useKeywordManager } from '~/client/services/search-operation';
 import type { IFormattedSearchResult } from '~/interfaces/search';
 import type { IFormattedSearchResult } from '~/interfaces/search';
-import { useIsSearchServiceReachable, useShowPageLimitationL } from '~/stores/context';
+import { useShowPageLimitationL } from '~/stores/context';
 import { type ISearchConditions, type ISearchConfigurations, useSWRxSearch } from '~/stores/search';
 import { type ISearchConditions, type ISearchConfigurations, useSWRxSearch } from '~/stores/search';
 
 
 import { NotAvailableForGuest } from './NotAvailableForGuest';
 import { NotAvailableForGuest } from './NotAvailableForGuest';
@@ -17,6 +17,7 @@ import { OperateAllControl } from './SearchPage/OperateAllControl';
 import SearchControl from './SearchPage/SearchControl';
 import SearchControl from './SearchPage/SearchControl';
 import { type IReturnSelectedPageIds, SearchPageBase, usePageDeleteModalForBulkDeletion } from './SearchPage/SearchPageBase';
 import { type IReturnSelectedPageIds, SearchPageBase, usePageDeleteModalForBulkDeletion } from './SearchPage/SearchPageBase';
 
 
+import styles from './SearchPage.module.scss';
 
 
 // TODO: replace with "customize:showPageLimitationS"
 // TODO: replace with "customize:showPageLimitationS"
 const INITIAL_PAGIONG_SIZE = 20;
 const INITIAL_PAGIONG_SIZE = 20;
@@ -90,11 +91,12 @@ export const SearchPage = (): JSX.Element => {
   const [offset, setOffset] = useState<number>(0);
   const [offset, setOffset] = useState<number>(0);
   const [limit, setLimit] = useState<number>(showPageLimitationL ?? INITIAL_PAGIONG_SIZE);
   const [limit, setLimit] = useState<number>(showPageLimitationL ?? INITIAL_PAGIONG_SIZE);
   const [configurationsByControl, setConfigurationsByControl] = useState<Partial<ISearchConfigurations>>({});
   const [configurationsByControl, setConfigurationsByControl] = useState<Partial<ISearchConfigurations>>({});
+  const [isCollapsed, setIsCollapsed] = useState<boolean>(false);
+  const [selectedCount, setSelectedCount] = useState(0);
+
   const selectAllControlRef = useRef<ISelectableAndIndeterminatable|null>(null);
   const selectAllControlRef = useRef<ISelectableAndIndeterminatable|null>(null);
   const searchPageBaseRef = useRef<ISelectableAll & IReturnSelectedPageIds|null>(null);
   const searchPageBaseRef = useRef<ISelectableAll & IReturnSelectedPageIds|null>(null);
 
 
-  const { data: isSearchServiceReachable } = useIsSearchServiceReachable();
-
   const { data, conditions, mutate } = useSWRxSearch(keyword ?? '', null, {
   const { data, conditions, mutate } = useSWRxSearch(keyword ?? '', null, {
     ...configurationsByControl,
     ...configurationsByControl,
     offset,
     offset,
@@ -108,7 +110,7 @@ export const SearchPage = (): JSX.Element => {
     pushState(newKeyword);
     pushState(newKeyword);
 
 
     mutate();
     mutate();
-  }, [keyword, mutate, pushState]);
+  }, [mutate, pushState]);
 
 
   const selectAllCheckboxChangedHandler = useCallback((isChecked: boolean) => {
   const selectAllCheckboxChangedHandler = useCallback((isChecked: boolean) => {
     const instance = searchPageBaseRef.current;
     const instance = searchPageBaseRef.current;
@@ -123,6 +125,9 @@ export const SearchPage = (): JSX.Element => {
     else {
     else {
       instance.deselectAll();
       instance.deselectAll();
     }
     }
+
+    // update selected count
+    setSelectedCount(instance.getSelectedPageIds?.().size ?? 0);
   }, []);
   }, []);
 
 
   const selectedPagesByCheckboxesChangedHandler = useCallback((selectedCount: number, totalCount: number) => {
   const selectedPagesByCheckboxesChangedHandler = useCallback((selectedCount: number, totalCount: number) => {
@@ -139,8 +144,12 @@ export const SearchPage = (): JSX.Element => {
       instance.select();
       instance.select();
     }
     }
     else {
     else {
+      setIsCollapsed(true);
       instance.setIndeterminate();
       instance.setIndeterminate();
     }
     }
+
+    // update selected count
+    setSelectedCount(selectedCount);
   }, []);
   }, []);
 
 
   const pagingSizeChangedHandler = useCallback((pagingSize: number) => {
   const pagingSizeChangedHandler = useCallback((pagingSize: number) => {
@@ -166,46 +175,74 @@ export const SearchPage = (): JSX.Element => {
 
 
   const hitsCount = data?.meta.hitsCount;
   const hitsCount = data?.meta.hitsCount;
 
 
-  const allControl = useMemo(() => {
-    const isDisabled = hitsCount === 0;
-
+  const extraControls = useMemo(() => {
     return (
     return (
       <NotAvailableForGuest>
       <NotAvailableForGuest>
         <NotAvailableForReadOnlyUser>
         <NotAvailableForReadOnlyUser>
-          <OperateAllControl
-            ref={selectAllControlRef}
-            isCheckboxDisabled={isDisabled}
-            onCheckboxChanged={selectAllCheckboxChangedHandler}
+          <button
+            type="button"
+            className={`${isCollapsed ? 'active' : ''} btn btn-muted-danger d-flex align-items-center ms-2`}
+            aria-expanded="false"
+            onClick={() => { setIsCollapsed(!isCollapsed) }}
           >
           >
+            <span className="material-symbols-outlined fs-5">delete</span>
+            <span className={`material-symbols-outlined me-1 ${isCollapsed ? 'rotate-180' : ''}`}>keyboard_arrow_down</span>
+          </button>
+        </NotAvailableForReadOnlyUser>
+      </NotAvailableForGuest>
+    );
+  }, [isCollapsed]);
+
+  const collapseContents = useMemo(() => {
+    return (
+      <NotAvailableForGuest>
+        <NotAvailableForReadOnlyUser>
+          <div className="d-flex align-items-center py-2">
+            <div className="ms-4">
+              <OperateAllControl
+                inputId="cb-select-all"
+                inputClassName="form-check-input"
+                ref={selectAllControlRef}
+                isCheckboxDisabled={hitsCount === 0}
+                onCheckboxChanged={selectAllCheckboxChangedHandler}
+              >
+                <label
+                  className="form-check-label ms-2"
+                  htmlFor="cb-select-all"
+                >
+                  {t('search_result.select_all')}
+                </label>
+              </OperateAllControl>
+            </div>
+
             <button
             <button
               type="button"
               type="button"
-              className="btn border-0 text-danger"
-              disabled={isDisabled}
+              className="ms-3 open-delete-modal-button btn btn-outline-danger d-flex align-items-center"
+              disabled={selectedCount === 0}
               onClick={deleteAllButtonClickedHandler}
               onClick={deleteAllButtonClickedHandler}
             >
             >
-              {t('search_result.delete_all_selected_page')}
+              <span className="material-symbols-outlined fs-5">delete</span>{t('search_result.delete_selected_pages')}
             </button>
             </button>
-          </OperateAllControl>
+          </div>
         </NotAvailableForReadOnlyUser>
         </NotAvailableForReadOnlyUser>
       </NotAvailableForGuest>
       </NotAvailableForGuest>
     );
     );
-  }, [deleteAllButtonClickedHandler, hitsCount, selectAllCheckboxChangedHandler, t]);
+  }, [deleteAllButtonClickedHandler, hitsCount, selectAllCheckboxChangedHandler, selectedCount, t]);
+
 
 
   const searchControl = useMemo(() => {
   const searchControl = useMemo(() => {
-    if (!isSearchServiceReachable) {
-      return <></>;
-    }
     return (
     return (
       <SearchControl
       <SearchControl
-        isSearchServiceReachable={isSearchServiceReachable}
         isEnableSort
         isEnableSort
         isEnableFilter
         isEnableFilter
         initialSearchConditions={initialSearchConditions}
         initialSearchConditions={initialSearchConditions}
         onSearchInvoked={searchInvokedHandler}
         onSearchInvoked={searchInvokedHandler}
-        allControl={allControl}
+        extraControls={extraControls}
+        collapseContents={collapseContents}
+        isCollapsed={isCollapsed}
       />
       />
     );
     );
-  }, [allControl, initialSearchConditions, isSearchServiceReachable, searchInvokedHandler]);
+  }, [extraControls, collapseContents, initialSearchConditions, isCollapsed, searchInvokedHandler]);
 
 
   const searchResultListHead = useMemo(() => {
   const searchResultListHead = useMemo(() => {
     if (data == null) {
     if (data == null) {
@@ -241,6 +278,7 @@ export const SearchPage = (): JSX.Element => {
 
 
   return (
   return (
     <SearchPageBase
     <SearchPageBase
+      className={styles['search-page']}
       ref={searchPageBaseRef}
       ref={searchPageBaseRef}
       pages={data?.data}
       pages={data?.data}
       searchingKeyword={keyword}
       searchingKeyword={keyword}

+ 6 - 2
apps/app/src/components/SearchPage/OperateAllControl.tsx

@@ -7,6 +7,8 @@ import type { ISelectableAndIndeterminatable } from '~/client/interfaces/selecta
 import type { IndeterminateInputElement } from '~/interfaces/indeterminate-input-elm';
 import type { IndeterminateInputElement } from '~/interfaces/indeterminate-input-elm';
 
 
 type Props = {
 type Props = {
+  inputId?: string,
+  inputClassName?: string,
   isCheckboxDisabled?: boolean,
   isCheckboxDisabled?: boolean,
   onCheckboxChanged?: (isChecked: boolean) => void,
   onCheckboxChanged?: (isChecked: boolean) => void,
   children?: React.ReactNode,
   children?: React.ReactNode,
@@ -14,6 +16,8 @@ type Props = {
 
 
 const OperateAllControlSubstance: ForwardRefRenderFunction<ISelectableAndIndeterminatable, Props> = (props: Props, ref): JSX.Element => {
 const OperateAllControlSubstance: ForwardRefRenderFunction<ISelectableAndIndeterminatable, Props> = (props: Props, ref): JSX.Element => {
   const {
   const {
+    inputId,
+    inputClassName = '',
     isCheckboxDisabled,
     isCheckboxDisabled,
     onCheckboxChanged,
     onCheckboxChanged,
     children,
     children,
@@ -54,10 +58,10 @@ const OperateAllControlSubstance: ForwardRefRenderFunction<ISelectableAndIndeter
   return (
   return (
     <div className="d-flex align-items-center">
     <div className="d-flex align-items-center">
       <Input
       <Input
+        id={inputId}
         type="checkbox"
         type="checkbox"
-        id="cb-check-all"
         data-testid="cb-select-all"
         data-testid="cb-select-all"
-        className="ms-2"
+        className={inputClassName}
         innerRef={selectAllCheckboxElm}
         innerRef={selectAllCheckboxElm}
         disabled={isCheckboxDisabled}
         disabled={isCheckboxDisabled}
         onChange={checkboxChangedHandler}
         onChange={checkboxChangedHandler}

+ 16 - 24
apps/app/src/components/SearchPage/SearchControl.tsx

@@ -3,6 +3,7 @@ import React, {
 } from 'react';
 } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import { Collapse } from 'reactstrap';
 
 
 import { SORT_AXIS, SORT_ORDER } from '~/interfaces/search';
 import { SORT_AXIS, SORT_ORDER } from '~/interfaces/search';
 import type { ISearchConditions, ISearchConfigurations } from '~/stores/search';
 import type { ISearchConditions, ISearchConfigurations } from '~/stores/search';
@@ -14,25 +15,28 @@ import SortControl from './SortControl';
 import styles from './SearchControl.module.scss';
 import styles from './SearchControl.module.scss';
 
 
 type Props = {
 type Props = {
-  isSearchServiceReachable: boolean,
   isEnableSort: boolean,
   isEnableSort: boolean,
   isEnableFilter: boolean,
   isEnableFilter: boolean,
   initialSearchConditions: Partial<ISearchConditions>,
   initialSearchConditions: Partial<ISearchConditions>,
 
 
   onSearchInvoked?: (keyword: string, configurations: Partial<ISearchConfigurations>) => void,
   onSearchInvoked?: (keyword: string, configurations: Partial<ISearchConfigurations>) => void,
 
 
-  allControl: React.ReactNode,
+  extraControls: React.ReactNode,
+
+  collapseContents?: React.ReactNode,
+  isCollapsed?: boolean,
 }
 }
 
 
 const SearchControl = React.memo((props: Props): JSX.Element => {
 const SearchControl = React.memo((props: Props): JSX.Element => {
 
 
   const {
   const {
-    isSearchServiceReachable,
     isEnableSort,
     isEnableSort,
     isEnableFilter,
     isEnableFilter,
     initialSearchConditions,
     initialSearchConditions,
     onSearchInvoked,
     onSearchInvoked,
-    allControl,
+    extraControls,
+    collapseContents,
+    isCollapsed,
   } = props;
   } = props;
 
 
   const keywordOnInit = initialSearchConditions.keyword ?? '';
   const keywordOnInit = initialSearchConditions.keyword ?? '';
@@ -46,14 +50,6 @@ const SearchControl = React.memo((props: Props): JSX.Element => {
 
 
   const { t } = useTranslation('');
   const { t } = useTranslation('');
 
 
-  const searchFormSubmittedHandler = useCallback((input: string) => {
-    setKeyword(input);
-
-    onSearchInvoked?.(input, {
-      sort, order, includeUserPages, includeTrashPages,
-    });
-  }, [includeTrashPages, includeUserPages, onSearchInvoked, order, sort]);
-
   const changeSortHandler = useCallback((nextSort: SORT_AXIS, nextOrder: SORT_ORDER) => {
   const changeSortHandler = useCallback((nextSort: SORT_AXIS, nextOrder: SORT_ORDER) => {
     setSort(nextSort);
     setSort(nextSort);
     setOrder(nextOrder);
     setOrder(nextOrder);
@@ -156,20 +152,16 @@ const SearchControl = React.memo((props: Props): JSX.Element => {
             </div>
             </div>
           </>
           </>
         )}
         )}
-        <div className="d-flex">
-          <div className="btn-group">
-            {/* TODO: imprv to delete all result UI */}
-            {/* <button className={`btn btn-sm rounded ${styles['btn-delete']}`} type="button" data-bs-toggle="dropdown" aria-expanded="false">
-              <span className="material-symbols-outlined ">delete</span>
-              <span className="material-symbols-outlined ">expand_more</span>
-            </button> */}
-            {/* <ul className="dropdown-menu"> */}
-            {allControl}
-            {/* </ul> */}
-          </div>
-        </div>
+
+        {extraControls}
       </div>
       </div>
 
 
+      { collapseContents != null && (
+        <Collapse isOpen={isCollapsed}>
+          {collapseContents}
+        </Collapse>
+      ) }
+
       <SearchOptionModal
       <SearchOptionModal
         isOpen={isFileterOptionModalShown || false}
         isOpen={isFileterOptionModalShown || false}
         onClose={() => setIsFileterOptionModalShown(false)}
         onClose={() => setIsFileterOptionModalShown(false)}

+ 13 - 7
apps/app/src/components/SearchPage/SearchModalTriggerinput.tsx

@@ -18,14 +18,20 @@ export const SearchModalTriggerinput: React.FC<Props> = (props: Props) => {
   }, [openSearchModal, keywordOnInit]);
   }, [openSearchModal, keywordOnInit]);
 
 
   return (
   return (
-    <div>
-      <input
-        className="form-control"
-        type="input"
-        value={keywordOnInit}
+    <div className="d-flex align-items-center">
+      <span className="text-secondary material-symbols-outlined fs-4 me-2">search</span>
+      <form
+        className="w-100 position-relative"
         onClick={inputClickHandler}
         onClick={inputClickHandler}
-        readOnly
-      />
+      >
+        <input
+          className="form-control"
+          type="input"
+          value={keywordOnInit}
+          onClick={inputClickHandler}
+          readOnly
+        />
+      </form>
     </div>
     </div>
   );
   );
 };
 };

+ 3 - 1
apps/app/src/components/SearchPage/SearchPageBase.tsx

@@ -35,6 +35,7 @@ export interface IReturnSelectedPageIds {
 
 
 
 
 type Props = {
 type Props = {
+  className?: string,
   pages?: IPageWithSearchMeta[],
   pages?: IPageWithSearchMeta[],
   searchingKeyword?: string,
   searchingKeyword?: string,
 
 
@@ -54,6 +55,7 @@ const SearchResultContent = dynamic(() => import('./SearchResultContent').then(m
 const SearchPageBaseSubstance: ForwardRefRenderFunction<ISelectableAll & IReturnSelectedPageIds, Props> = (props:Props, ref) => {
 const SearchPageBaseSubstance: ForwardRefRenderFunction<ISelectableAll & IReturnSelectedPageIds, Props> = (props:Props, ref) => {
 
 
   const {
   const {
+    className,
     pages,
     pages,
     searchingKeyword,
     searchingKeyword,
     forceHideMenuItems,
     forceHideMenuItems,
@@ -171,7 +173,7 @@ const SearchPageBaseSubstance: ForwardRefRenderFunction<ISelectableAll & IReturn
     : undefined;
     : undefined;
 
 
   return (
   return (
-    <div className="search-result-base flex-grow-1 d-flex flex-expand-vh-100" data-testid="search-result-base">
+    <div className={`${className ?? ''} search-result-base flex-grow-1 d-flex flex-expand-vh-100`} data-testid="search-result-base">
 
 
       <div className="flex-expand-vert border boder-gray search-result-list" id="search-result-list">
       <div className="flex-expand-vert border boder-gray search-result-list" id="search-result-list">
 
 

+ 1 - 1
apps/app/src/components/SearchPage/SearchResultContent.tsx

@@ -188,7 +188,7 @@ export const SearchResultContent: FC<Props> = (props: Props) => {
       : undefined;
       : undefined;
 
 
     return (
     return (
-      <div className="d-flex flex-column align-items-end justify-content-center px-2 py-1">
+      <div className="d-flex flex-column flex-row-reverse flex px-2 py-1">
         <PageControls
         <PageControls
           pageId={page._id}
           pageId={page._id}
           revisionId={revisionId}
           revisionId={revisionId}