Просмотр исходного кода

Merge branch 'master' into feat/ldap-group-sync

Futa Arai 2 лет назад
Родитель
Сommit
a032d242b1
100 измененных файлов с 315 добавлено и 302 удалено
  1. 1 1
      .github/release-drafter-dev-7.0.x.yml
  2. 2 0
      .github/workflows/ci-app-prod.yml
  3. 3 3
      .github/workflows/ci-app.yml
  4. 3 3
      .github/workflows/ci-slackbot-proxy.yml
  5. 1 1
      .github/workflows/release-slackbot-proxy.yml
  6. 2 2
      .github/workflows/release.yml
  7. 3 3
      .github/workflows/reusable-app-prod.yml
  8. 1 1
      .github/workflows/reusable-app-reg-suit.yml
  9. 37 1
      CHANGELOG.md
  10. 3 2
      apps/app/bin/download-cdn-resources.ts
  11. 1 1
      apps/app/bin/github-actions/update-readme.sh
  12. 2 0
      apps/app/config/logger/config.dev.js
  13. 2 1
      apps/app/config/next-i18next.config.js
  14. 6 5
      apps/app/docker/README.md
  15. 8 10
      apps/app/package.json
  16. 9 1
      apps/app/public/static/locales/en_US/admin.json
  17. 9 1
      apps/app/public/static/locales/ja_JP/admin.json
  18. 9 1
      apps/app/public/static/locales/zh_CN/admin.json
  19. 1 3
      apps/app/src/client/models/Linker.js
  20. 1 1
      apps/app/src/client/services/AdminAppContainer.js
  21. 1 1
      apps/app/src/client/services/AdminCustomizeContainer.js
  22. 1 1
      apps/app/src/client/services/AdminExternalAccountsContainer.js
  23. 11 1
      apps/app/src/client/services/AdminGeneralSecurityContainer.js
  24. 1 2
      apps/app/src/client/services/AdminGitHubSecurityContainer.js
  25. 1 2
      apps/app/src/client/services/AdminGoogleSecurityContainer.js
  26. 2 2
      apps/app/src/client/services/AdminHomeContainer.js
  27. 1 1
      apps/app/src/client/services/AdminImportContainer.js
  28. 1 1
      apps/app/src/client/services/AdminLdapSecurityContainer.js
  29. 1 1
      apps/app/src/client/services/AdminLocalSecurityContainer.js
  30. 1 1
      apps/app/src/client/services/AdminMarkDownContainer.js
  31. 1 1
      apps/app/src/client/services/AdminNotificationContainer.js
  32. 1 2
      apps/app/src/client/services/AdminOidcSecurityContainer.js
  33. 1 2
      apps/app/src/client/services/AdminSamlSecurityContainer.js
  34. 1 1
      apps/app/src/client/services/AdminSlackIntegrationLegacyContainer.js
  35. 1 1
      apps/app/src/client/services/AdminUsersContainer.js
  36. 2 1
      apps/app/src/client/services/layout.ts
  37. 2 2
      apps/app/src/client/services/renderer/renderer.tsx
  38. 1 1
      apps/app/src/client/services/user-ui-settings.ts
  39. 1 1
      apps/app/src/client/util/bookmark-utils.ts
  40. 1 2
      apps/app/src/client/util/toastr.ts
  41. 8 3
      apps/app/src/components/Admin/AuditLog/ActivityTable.tsx
  42. 2 2
      apps/app/src/components/Admin/Common/AdminNavigation.tsx
  43. 1 1
      apps/app/src/components/Admin/Customize/CustomizeThemeOptions.tsx
  44. 3 23
      apps/app/src/components/Admin/ExportArchiveData/SelectCollectionsModal.tsx
  45. 4 31
      apps/app/src/components/Admin/ExportArchiveDataPage.tsx
  46. 17 0
      apps/app/src/components/Admin/ForbiddenPage.tsx
  47. 0 1
      apps/app/src/components/Admin/ImportData/GrowiArchive/UploadForm.jsx
  48. 3 21
      apps/app/src/components/Admin/ImportData/GrowiArchiveSection.jsx
  49. 1 1
      apps/app/src/components/Admin/Security/GitHubSecuritySettingContents.jsx
  50. 1 1
      apps/app/src/components/Admin/Security/GoogleSecuritySettingContents.jsx
  51. 1 1
      apps/app/src/components/Admin/Security/OidcSecuritySettingContents.jsx
  52. 1 1
      apps/app/src/components/Admin/Security/SamlSecuritySettingContents.jsx
  53. 22 0
      apps/app/src/components/Admin/Security/SecuritySetting.jsx
  54. 1 1
      apps/app/src/components/Admin/UserGroup/UserGroupDeleteModal.tsx
  55. 1 2
      apps/app/src/components/Admin/UserGroup/UserGroupDropdown.tsx
  56. 1 2
      apps/app/src/components/Admin/UserGroup/UserGroupForm.tsx
  57. 1 3
      apps/app/src/components/Admin/UserGroup/UserGroupModal.tsx
  58. 1 1
      apps/app/src/components/Admin/UserGroup/UserGroupPage.tsx
  59. 4 2
      apps/app/src/components/Admin/UserGroupDetail/UserGroupDetailPage.tsx
  60. 1 1
      apps/app/src/components/Admin/UserGroupDetail/UserGroupPageList.tsx
  61. 1 1
      apps/app/src/components/Admin/UserGroupDetail/UserGroupUserFormByInput.jsx
  62. 1 1
      apps/app/src/components/Admin/UserGroupDetail/UserGroupUserModal.tsx
  63. 1 1
      apps/app/src/components/Admin/UserGroupDetail/UserGroupUserTable.tsx
  64. 1 1
      apps/app/src/components/Admin/Users/UserMenu.tsx
  65. 1 1
      apps/app/src/components/Admin/Users/UserTable.tsx
  66. 4 4
      apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx
  67. 4 4
      apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx
  68. 3 2
      apps/app/src/components/Bookmarks/BookmarkItem.tsx
  69. 16 24
      apps/app/src/components/Comments.tsx
  70. 3 3
      apps/app/src/components/Common/Dropdown/PageItemControl.tsx
  71. 1 1
      apps/app/src/components/ContentLinkButtons.tsx
  72. 1 1
      apps/app/src/components/CreateTemplateModal.jsx
  73. 1 1
      apps/app/src/components/CustomNavigation/CustomNav.tsx
  74. 5 5
      apps/app/src/components/DescendantsPageList.tsx
  75. 1 1
      apps/app/src/components/DuplicatedPathsTable.tsx
  76. 1 1
      apps/app/src/components/IdenticalPathPage.tsx
  77. 2 2
      apps/app/src/components/InAppNotification/InAppNotificationElm.tsx
  78. 1 1
      apps/app/src/components/InAppNotification/InAppNotificationList.tsx
  79. 2 2
      apps/app/src/components/InAppNotification/PageNotification/PageModelNotification.tsx
  80. 1 1
      apps/app/src/components/InAppNotification/PageNotification/UserModelNotification.tsx
  81. 1 1
      apps/app/src/components/LikeButtons.tsx
  82. 1 1
      apps/app/src/components/LoginForm.tsx
  83. 3 4
      apps/app/src/components/Navbar/AuthorInfo.tsx
  84. 1 1
      apps/app/src/components/Navbar/GlobalSearch.tsx
  85. 6 8
      apps/app/src/components/Navbar/GrowiContextualSubNavigation.tsx
  86. 3 2
      apps/app/src/components/Navbar/PersonalDropdown.jsx
  87. 6 3
      apps/app/src/components/Navbar/SubNavButtons.tsx
  88. 1 1
      apps/app/src/components/Page/CopyDropdown.jsx
  89. 9 12
      apps/app/src/components/Page/PageView.tsx
  90. 4 11
      apps/app/src/components/Page/RevisionLoader.tsx
  91. 1 1
      apps/app/src/components/PageAccessoriesModal/PageAttachment.tsx
  92. 1 2
      apps/app/src/components/PageAccessoriesModal/ShareLink/ShareLinkForm.tsx
  93. 2 1
      apps/app/src/components/PageAlert/FixPageGrantAlert.tsx
  94. 1 3
      apps/app/src/components/PageAlert/OldRevisionAlert.tsx
  95. 1 1
      apps/app/src/components/PageAlert/PageStaleAlert.tsx
  96. 1 1
      apps/app/src/components/PageAlert/TrashPageAlert.tsx
  97. 1 1
      apps/app/src/components/PageAttachment/DeleteAttachmentModal.tsx
  98. 2 2
      apps/app/src/components/PageAttachment/PageAttachmentList.tsx
  99. 7 21
      apps/app/src/components/PageComment.tsx
  100. 1 1
      apps/app/src/components/PageComment/Comment.tsx

+ 1 - 1
.github/release-drafter-dev-6.2.x.yml → .github/release-drafter-dev-7.0.x.yml

@@ -4,4 +4,4 @@ prerelease: true
 
 
 # Filter previous releases to consider only those with the tags starts with 'v6.2'
 # Filter previous releases to consider only those with the tags starts with 'v6.2'
 include-pre-releases: true
 include-pre-releases: true
-tag-prefix: v6.2
+tag-prefix: v7.0

+ 2 - 0
.github/workflows/ci-app-prod.yml

@@ -4,6 +4,7 @@ on:
   push:
   push:
     branches:
     branches:
       - master
       - master
+      - dev/7.*.x
       - dev/6.*.x
       - dev/6.*.x
     paths:
     paths:
       - .github/workflows/ci-app-prod.yml
       - .github/workflows/ci-app-prod.yml
@@ -19,6 +20,7 @@ on:
   pull_request:
   pull_request:
     branches:
     branches:
       - master
       - master
+      - dev/7.*.x
       - dev/6.*.x
       - dev/6.*.x
     types: [opened, reopened, synchronize]
     types: [opened, reopened, synchronize]
     paths:
     paths:

+ 3 - 3
.github/workflows/ci-app.yml

@@ -60,7 +60,7 @@ jobs:
 
 
       - name: Install dependencies
       - name: Install dependencies
         run: |
         run: |
-          yarn global add turbo@1.10.9
+          yarn global add turbo
           yarn --frozen-lockfile
           yarn --frozen-lockfile
 
 
       - name: Lint
       - name: Lint
@@ -131,7 +131,7 @@ jobs:
 
 
       - name: Install dependencies
       - name: Install dependencies
         run: |
         run: |
-          yarn global add turbo@1.10.9
+          yarn global add turbo
           yarn --frozen-lockfile
           yarn --frozen-lockfile
 
 
       - name: Test
       - name: Test
@@ -213,7 +213,7 @@ jobs:
 
 
       - name: Install dependencies
       - name: Install dependencies
         run: |
         run: |
-          yarn global add turbo@1.10.9
+          yarn global add turbo
           yarn --frozen-lockfile
           yarn --frozen-lockfile
 
 
       - name: turbo run dev:ci
       - name: turbo run dev:ci

+ 3 - 3
.github/workflows/ci-slackbot-proxy.yml

@@ -62,7 +62,7 @@ jobs:
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
         yarn --frozen-lockfile
         yarn --frozen-lockfile
 
 
     - name: Lint
     - name: Lint
@@ -136,7 +136,7 @@ jobs:
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
         yarn --frozen-lockfile
         yarn --frozen-lockfile
 
 
     - name: yarn dev:ci
     - name: yarn dev:ci
@@ -200,7 +200,7 @@ jobs:
 
 
     - name: Install turbo
     - name: Install turbo
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
 
 
     - name: Prune repositories
     - name: Prune repositories
       run: |
       run: |

+ 1 - 1
.github/workflows/release-slackbot-proxy.yml

@@ -108,7 +108,7 @@ jobs:
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
         yarn --frozen-lockfile
         yarn --frozen-lockfile
 
 
     - name: Bump versions for next RC
     - name: Bump versions for next RC

+ 2 - 2
.github/workflows/release.yml

@@ -30,7 +30,7 @@ jobs:
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
         yarn --frozen-lockfile
         yarn --frozen-lockfile
 
 
     - name: Bump versions
     - name: Bump versions
@@ -93,7 +93,7 @@ jobs:
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
         yarn --frozen-lockfile
         yarn --frozen-lockfile
 
 
     - name: Bump versions for next RC
     - name: Bump versions for next RC

+ 3 - 3
.github/workflows/reusable-app-prod.yml

@@ -36,7 +36,7 @@ jobs:
 
 
     - name: Install turbo
     - name: Install turbo
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
 
 
     - name: Prune repositories
     - name: Prune repositories
       run: |
       run: |
@@ -147,7 +147,7 @@ jobs:
 
 
     - name: Install turbo
     - name: Install turbo
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
 
 
     - name: Prune repositories
     - name: Prune repositories
       run: |
       run: |
@@ -238,7 +238,7 @@ jobs:
 
 
     - name: Install turbo
     - name: Install turbo
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
 
 
     - name: Prune repositories
     - name: Prune repositories
       run: |
       run: |

+ 1 - 1
.github/workflows/reusable-app-reg-suit.yml

@@ -62,7 +62,7 @@ jobs:
 
 
     - name: Install turbo
     - name: Install turbo
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
 
 
     - name: Prune repositories
     - name: Prune repositories
       run: |
       run: |

+ 37 - 1
CHANGELOG.md

@@ -1,9 +1,45 @@
 # Changelog
 # Changelog
 
 
-## [Unreleased](https://github.com/weseek/growi/compare/v6.1.8...HEAD)
+## [Unreleased](https://github.com/weseek/growi/compare/v6.1.11...HEAD)
 
 
 *Please do not manually update this file. We've automated the process.*
 *Please do not manually update this file. We've automated the process.*
 
 
+## [v6.1.11](https://github.com/weseek/growi/compare/v6.1.10...v6.1.11) - 2023-08-07
+
+### 🐛 Bug Fixes
+
+- fix: Admin page permission when the user transit with next-routing (#7908) @WNomunomu
+- fix: Transitioning to a non-existent page under "/me" results in a 500 error (#7946) @miya
+- fix: Auto-scroll search result content 2 (#7943) @yuki-takei
+
+## [v6.1.10](https://github.com/weseek/growi/compare/v6.1.9...v6.1.10) - 2023-08-01
+
+### 🐛 Bug Fixes
+
+- fix: Unsaved comments remain in the editor when transitioning to a new page  (#7912) @miya
+- fix: SWR settings for searching (#7940) @yuki-takei
+- fix: Auto-scroll search result content (#7935) @yuki-takei
+
+## [v6.1.9](https://github.com/weseek/growi/compare/v6.1.8...v6.1.9) - 2023-07-31
+
+### 💎 Features
+
+- feat: LightBox for enlargement of image (#7899) @WNomunomu
+
+### 🚀 Improvement
+
+- imprv: Do not use loadConfigs in skipSSR (#7929) @jam411
+- imprv: Improve default behavior of skipSSR (#7927) @jam411
+- imprv: Improved Design for Bookmarks Sidebar (#7886) @soumaeda
+
+### 🐛 Bug Fixes
+
+- fix: Page creation and update process (#7925) @yuki-takei
+- fix: Sidebar doesn't show the link to the administration panel when logged in as an Admin (#7914) @miya
+- fix: Improve page data mutation after renaming and deleting by GrowiContextualSubNavigation (#7926) @yuki-takei
+- fix: Revert dynamic import. (#7923) @TatsuyaIse
+- fix: Questionnaire wikiType (#7907) @TatsuyaIse
+
 ## [v6.1.8](https://github.com/weseek/growi/compare/v6.1.7...v6.1.8) - 2023-07-24
 ## [v6.1.8](https://github.com/weseek/growi/compare/v6.1.7...v6.1.8) - 2023-07-24
 
 
 ### 💎 Features
 ### 💎 Features

+ 3 - 2
apps/app/bin/download-cdn-resources.ts

@@ -3,11 +3,12 @@
  *
  *
  * @author Yuki Takei <yuki@weseek.co.jp>
  * @author Yuki Takei <yuki@weseek.co.jp>
  */
  */
-import { envUtils } from '@growi/core';
+import { envUtils } from '@growi/core/dist/utils';
 
 
-import CdnResourcesDownloader from './cdn/cdn-resources-downloader';
 import loggerFactory from '../src/utils/logger';
 import loggerFactory from '../src/utils/logger';
 
 
+import CdnResourcesDownloader from './cdn/cdn-resources-downloader';
+
 const logger = loggerFactory('growi:bin:download-cdn-resources');
 const logger = loggerFactory('growi:bin:download-cdn-resources');
 
 
 // check env var
 // check env var

+ 1 - 1
apps/app/bin/github-actions/update-readme.sh

@@ -2,4 +2,4 @@
 
 
 cd docker
 cd docker
 
 
-sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`6\.1\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/packages\/app\/docker\/Dockerfile.\+\)$/\1${RELEASED_VERSION}\2\3${RELEASED_VERSION}\4/" README.md
+sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`6\.2\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/packages\/app\/docker\/Dockerfile.\+\)$/\1${RELEASED_VERSION}\2\3${RELEASED_VERSION}\4/" README.md

+ 2 - 0
apps/app/config/logger/config.dev.js

@@ -29,6 +29,8 @@ module.exports = {
   'growi:service:search-delegator:elasticsearch': 'debug',
   'growi:service:search-delegator:elasticsearch': 'debug',
   'growi:service:g2g-transfer': 'debug',
   'growi:service:g2g-transfer': 'debug',
 
 
+  'growi:migration:add-installed-date-to-config': 'debug',
+
   /*
   /*
    * configure level for client
    * configure level for client
    */
    */

+ 2 - 1
apps/app/config/next-i18next.config.js

@@ -1,6 +1,7 @@
 const path = require('path');
 const path = require('path');
 
 
-const { isServer, AllLang, Lang } = require('@growi/core');
+const { AllLang, Lang } = require('@growi/core');
+const { isServer } = require('@growi/core/dist/utils');
 const I18nextChainedBackend = require('i18next-chained-backend').default;
 const I18nextChainedBackend = require('i18next-chained-backend').default;
 const I18NextHttpBackend = require('i18next-http-backend');
 const I18NextHttpBackend = require('i18next-http-backend');
 const I18NextLocalStorageBackend = require('i18next-localstorage-backend').default;
 const I18NextLocalStorageBackend = require('i18next-localstorage-backend').default;

+ 6 - 5
apps/app/docker/README.md

@@ -10,11 +10,12 @@ GROWI Official docker image
 Supported tags and respective Dockerfile links
 Supported tags and respective Dockerfile links
 ------------------------------------------------
 ------------------------------------------------
 
 
-* [`6.1.0`, `6.1`, `6`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v6.1.0/apps/app/docker/Dockerfile)
-* [`6.0.15`, `6.0`](https://github.com/weseek/growi/blob/v6.0.15/packages/app/docker/Dockerfile)
-* [`5.1.7`, `5.1`, `5`](https://github.com/weseek/growi/blob/v5.1.7/packages/app/docker/Dockerfile)
-* [`5.1.7-nocdn`, `5.1-nocdn`, `5-nocdn`](https://github.com/weseek/growi/blob/v5.1.7/packages/app/docker/Dockerfile)
-* [`4.5.23`, `4.5`, `4`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v4.5.23/packages/app/docker/Dockerfile)
+* [`6.2.0`, `6.2`, `6`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v6.2.0/apps/app/docker/Dockerfile)
+* [`6.1.0`, `6.1` (Dockerfile)](https://github.com/weseek/growi/blob/v6.1.8/apps/app/docker/Dockerfile)
+* [`6.0.15`, `6.0` (Dockerfile)](https://github.com/weseek/growi/blob/v6.0.15/packages/app/docker/Dockerfile)
+* [`5.1.7`, `5.1`, `5` (Dockerfile)](https://github.com/weseek/growi/blob/v5.1.7/packages/app/docker/Dockerfile)
+* [`5.1.7-nocdn`, `5.1-nocdn`, `5-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v5.1.7/packages/app/docker/Dockerfile)
+* [`4.5.23`, `4.5`, `4` (Dockerfile)](https://github.com/weseek/growi/blob/v4.5.23/packages/app/docker/Dockerfile)
 * [`4.5.23-nocdn`, `4.5-nocdn`, `4-nocdn`, `latest-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.5.23/packages/app/docker/Dockerfile)
 * [`4.5.23-nocdn`, `4.5-nocdn`, `4-nocdn`, `latest-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.5.23/packages/app/docker/Dockerfile)
 
 
 
 

+ 8 - 10
apps/app/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/app",
   "name": "@growi/app",
-  "version": "6.1.9-RC.0",
+  "version": "6.2.0-RC.0",
   "license": "MIT",
   "license": "MIT",
   "scripts": {
   "scripts": {
     "//// for production": "",
     "//// for production": "",
@@ -116,7 +116,7 @@
     "extensible-custom-error": "^0.0.7",
     "extensible-custom-error": "^0.0.7",
     "form-data": "^4.0.0",
     "form-data": "^4.0.0",
     "graceful-fs": "^4.1.11",
     "graceful-fs": "^4.1.11",
-    "hast-util-select": "^5.0.2",
+    "hast-util-select": "^5.0.5",
     "helmet": "^4.6.0",
     "helmet": "^4.6.0",
     "http-errors": "^2.0.0",
     "http-errors": "^2.0.0",
     "i18next": "^22.4.10",
     "i18next": "^22.4.10",
@@ -197,8 +197,7 @@
     "superjson": "^1.9.1",
     "superjson": "^1.9.1",
     "swagger-jsdoc": "^6.1.0",
     "swagger-jsdoc": "^6.1.0",
     "swr": "^2.0.3",
     "swr": "^2.0.3",
-    "throttle-debounce": "^3.0.1",
-    "toastr": "^2.1.2",
+    "throttle-debounce": "^5.0.0",
     "uglifycss": "^0.0.29",
     "uglifycss": "^0.0.29",
     "universal-bunyan": "^0.9.2",
     "universal-bunyan": "^0.9.2",
     "unstated": "^2.1.1",
     "unstated": "^2.1.1",
@@ -229,7 +228,6 @@
     "bootstrap": "^4.6.1",
     "bootstrap": "^4.6.1",
     "codemirror": "^5.64.0",
     "codemirror": "^5.64.0",
     "connect-browser-sync": "^2.1.0",
     "connect-browser-sync": "^2.1.0",
-    "core-js": "=2.6.9",
     "diff2html": "^3.4.35",
     "diff2html": "^3.4.35",
     "eazy-logger": "^3.1.0",
     "eazy-logger": "^3.1.0",
     "emoji-mart": "npm:panta82-emoji-mart@^3.0.1",
     "emoji-mart": "npm:panta82-emoji-mart@^3.0.1",
@@ -237,13 +235,13 @@
     "eslint-plugin-jest": "^26.5.3",
     "eslint-plugin-jest": "^26.5.3",
     "eslint-plugin-regex": "^1.8.0",
     "eslint-plugin-regex": "^1.8.0",
     "font-awesome": "^4.7.0",
     "font-awesome": "^4.7.0",
+    "fslightbox-react": "^1.7.6",
     "handsontable": "=6.2.2",
     "handsontable": "=6.2.2",
     "i18next-hmr": "^1.11.0",
     "i18next-hmr": "^1.11.0",
     "jest": "^29.5.0",
     "jest": "^29.5.0",
     "jest-date-mock": "^1.0.8",
     "jest-date-mock": "^1.0.8",
     "jest-localstorage-mock": "^2.4.14",
     "jest-localstorage-mock": "^2.4.14",
-    "jquery-slimscroll": "^1.3.8",
-    "jquery.cookie": "~1.4.1",
+    "jquery": "^3.7.0",
     "load-css-file": "^1.0.0",
     "load-css-file": "^1.0.0",
     "material-icons": "^1.11.3",
     "material-icons": "^1.11.3",
     "mongodb-memory-server": "^8.12.2",
     "mongodb-memory-server": "^8.12.2",
@@ -251,6 +249,7 @@
     "null-loader": "^4.0.1",
     "null-loader": "^4.0.1",
     "penpal": "^4.0.0",
     "penpal": "^4.0.0",
     "plantuml-encoder": "^1.2.5",
     "plantuml-encoder": "^1.2.5",
+    "popper.js": "^1.16.1",
     "prettier": "^1.19.1",
     "prettier": "^1.19.1",
     "react-codemirror2": "^6.0.0",
     "react-codemirror2": "^6.0.0",
     "react-copy-to-clipboard": "^5.0.1",
     "react-copy-to-clipboard": "^5.0.1",
@@ -264,8 +263,7 @@
     "simplebar-react": "^2.3.6",
     "simplebar-react": "^2.3.6",
     "socket.io-client": "^4.2.0",
     "socket.io-client": "^4.2.0",
     "source-map-loader": "^4.0.1",
     "source-map-loader": "^4.0.1",
-    "swagger2openapi": "^5.3.1",
-    "tsc-alias": "^1.2.9",
-    "fslightbox-react": "^1.7.6"
+    "swagger2openapi": "^7.0.8",
+    "tsc-alias": "^1.2.9"
   }
   }
 }
 }

+ 9 - 1
apps/app/public/static/locales/en_US/admin.json

@@ -3,7 +3,7 @@
     "display_name": "English"
     "display_name": "English"
   },
   },
   "last_login": "Last login",
   "last_login": "Last login",
-  "wiki_management_home_page": "Wiki Management Home Page",
+  "wiki_management_homepage": "Wiki Management Homepage",
   "public": "Public",
   "public": "Public",
   "anyone_with_the_link": "Anyone with the link",
   "anyone_with_the_link": "Anyone with the link",
   "specified_users": "Specified users",
   "specified_users": "Specified users",
@@ -46,6 +46,11 @@
     "admin_only": "Admin only",
     "admin_only": "Admin only",
     "admin_and_author": "Admin and author",
     "admin_and_author": "Admin and author",
     "anyone": "Anyone",
     "anyone": "Anyone",
+    "user_homepage_deletion": {
+      "user_homepage_deletion": "User homepage deletion",
+      "enable_user_homepage_deletion": "Enable user homepage deletion",
+      "when_deleting_a_user_the_user_homepage_is_also_deleted": "When deleting a user, the user homepage is also deleted."
+    },
     "session": "Session",
     "session": "Session",
     "max_age": "Max age (msec)",
     "max_age": "Max age (msec)",
     "max_age_desc": "Specifies the number (in milliseconds) to expire users session.<br>Default: 2592000000 (30days)",
     "max_age_desc": "Specifies the number (in milliseconds) to expire users session.<br>Default: 2592000000 (30days)",
@@ -1085,5 +1090,8 @@
     "activate_plugin_success": "Succeeded to activating {{pluginName}}",
     "activate_plugin_success": "Succeeded to activating {{pluginName}}",
     "deactivate_plugin_success": "Succeeded to deactivate {{pluginName}}",
     "deactivate_plugin_success": "Succeeded to deactivate {{pluginName}}",
     "remove_plugin_success": "Succeeded to removing {{pluginName}}"
     "remove_plugin_success": "Succeeded to removing {{pluginName}}"
+  },
+  "forbidden_page": {
+    "do_not_have_admin_permission": "Users without administrative rights cannot access the administration screen."
   }
   }
 }
 }

+ 9 - 1
apps/app/public/static/locales/ja_JP/admin.json

@@ -11,7 +11,7 @@
   "Edit": "編集",
   "Edit": "編集",
   "Description": "説明",
   "Description": "説明",
   "last_login": "最終ログイン",
   "last_login": "最終ログイン",
-  "wiki_management_home_page": "Wiki管理トップ",
+  "wiki_management_homepage": "Wiki管理トップ",
   "public": "公開",
   "public": "公開",
   "anyone_with_the_link": "リンクを知っている人のみ",
   "anyone_with_the_link": "リンクを知っている人のみ",
   "specified_users": "特定ユーザーのみ",
   "specified_users": "特定ユーザーのみ",
@@ -54,6 +54,11 @@
     "admin_only": "管理者のみ可能",
     "admin_only": "管理者のみ可能",
     "admin_and_author": "管理者とページ作者が可能",
     "admin_and_author": "管理者とページ作者が可能",
     "anyone": "誰でも可能",
     "anyone": "誰でも可能",
+    "user_homepage_deletion": {
+      "user_homepage_deletion": "ユーザーページの削除",
+      "enable_user_homepage_deletion": "ユーザーページの削除を有効化",
+      "when_deleting_a_user_the_user_homepage_is_also_deleted": "ユーザー削除時にユーザーページも削除します。"
+    },
     "session": "セッション",
     "session": "セッション",
     "max_age": "有効期間 (ミリ秒)",
     "max_age": "有効期間 (ミリ秒)",
     "max_age_desc": "ユーザーのセッション情報の有効期間をミリ秒で指定できます。<br>デフォルト値: 2592000000 (30日間)",
     "max_age_desc": "ユーザーのセッション情報の有効期間をミリ秒で指定できます。<br>デフォルト値: 2592000000 (30日間)",
@@ -1093,5 +1098,8 @@
     "activate_plugin_success": "{{pluginName}}を有効化しました",
     "activate_plugin_success": "{{pluginName}}を有効化しました",
     "deactivate_plugin_success": "{{pluginName}}を無効化しました",
     "deactivate_plugin_success": "{{pluginName}}を無効化しました",
     "remove_plugin_success": "{{pluginName}}を削除しました"
     "remove_plugin_success": "{{pluginName}}を削除しました"
+  },
+  "forbidden_page": {
+    "do_not_have_admin_permission": "管理者権限のないユーザーでは管理画面にはアクセスできません。"
   }
   }
 }
 }

+ 9 - 1
apps/app/public/static/locales/zh_CN/admin.json

@@ -11,7 +11,7 @@
   "Edit": "编辑",
   "Edit": "编辑",
   "Description": "描述",
   "Description": "描述",
   "last_login": "上次登录",
   "last_login": "上次登录",
-  "wiki_management_home_page": "Wiki管理首页",
+  "wiki_management_homepage": "Wiki管理首页",
   "public": "公共",
   "public": "公共",
   "anyone_with_the_link": "任何人",
   "anyone_with_the_link": "任何人",
   "specified_users": "仅指定用户",
   "specified_users": "仅指定用户",
@@ -54,6 +54,11 @@
 		"admin_only": "仅管理员",
 		"admin_only": "仅管理员",
 		"admin_and_author": "管理员|作者",
 		"admin_and_author": "管理员|作者",
 		"anyone": "任何人",
 		"anyone": "任何人",
+    "user_homepage_deletion": {
+      "user_homepage_deletion": "删除用户页面",
+      "enable_user_homepage_deletion": "启用删除用户页面",
+      "when_deleting_a_user_the_user_homepage_is_also_deleted": "当一个用户被删除时,用户页面也会被删除。"
+    },
     "session": "会议",
     "session": "会议",
     "max_age": "有效期间  (msec)",
     "max_age": "有效期间  (msec)",
     "max_age_desc": "指定使用户会话过期的数量(以毫秒为单位)。<br>默认值: 2592000000 (30天)",
     "max_age_desc": "指定使用户会话过期的数量(以毫秒为单位)。<br>默认值: 2592000000 (30天)",
@@ -1094,5 +1099,8 @@
     "activate_plugin_success": "Succeeded to activating {{pluginName}}",
     "activate_plugin_success": "Succeeded to activating {{pluginName}}",
     "deactivate_plugin_success": "Succeeded to deactivate {{pluginName}}",
     "deactivate_plugin_success": "Succeeded to deactivate {{pluginName}}",
     "remove_plugin_success": "Succeeded to removing {{pluginName}}"
     "remove_plugin_success": "Succeeded to removing {{pluginName}}"
+  },
+  "forbidden_page": {
+    "do_not_have_admin_permission": "没有管理权限的用户无法访问管理屏幕。"
   }
   }
 }
 }

+ 1 - 3
apps/app/src/client/models/Linker.js

@@ -1,7 +1,5 @@
 
 
-import { pagePathUtils } from '@growi/core';
-
-const { encodeSpaces } = pagePathUtils;
+import { encodeSpaces } from '@growi/core/dist/utils/page-path-utils';
 
 
 export default class Linker {
 export default class Linker {
 
 

+ 1 - 1
apps/app/src/client/services/AdminAppContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import { apiv3Get, apiv3Post, apiv3Put } from '../util/apiv3-client';
 import { apiv3Get, apiv3Post, apiv3Put } from '../util/apiv3-client';

+ 1 - 1
apps/app/src/client/services/AdminCustomizeContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';

+ 1 - 1
apps/app/src/client/services/AdminExternalAccountsContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';

+ 11 - 1
apps/app/src/client/services/AdminGeneralSecurityContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import {
 import {
@@ -38,6 +38,7 @@ export default class AdminGeneralSecurityContainer extends Container {
       expandOtherOptionsForCompleteDeletion: false,
       expandOtherOptionsForCompleteDeletion: false,
       isShowRestrictedByOwner: false,
       isShowRestrictedByOwner: false,
       isShowRestrictedByGroup: false,
       isShowRestrictedByGroup: false,
+      isUsersHomepageDeletionEnabled: false,
       isLocalEnabled: false,
       isLocalEnabled: false,
       isLdapEnabled: false,
       isLdapEnabled: false,
       isSamlEnabled: false,
       isSamlEnabled: false,
@@ -73,6 +74,7 @@ export default class AdminGeneralSecurityContainer extends Container {
       currentPageRecursiveCompleteDeletionAuthority: generalSetting.pageRecursiveCompleteDeletionAuthority,
       currentPageRecursiveCompleteDeletionAuthority: generalSetting.pageRecursiveCompleteDeletionAuthority,
       isShowRestrictedByOwner: !generalSetting.hideRestrictedByOwner,
       isShowRestrictedByOwner: !generalSetting.hideRestrictedByOwner,
       isShowRestrictedByGroup: !generalSetting.hideRestrictedByGroup,
       isShowRestrictedByGroup: !generalSetting.hideRestrictedByGroup,
+      isUsersHomepageDeletionEnabled: generalSetting.isUsersHomepageDeletionEnabled,
       sessionMaxAge: generalSetting.sessionMaxAge,
       sessionMaxAge: generalSetting.sessionMaxAge,
       wikiMode: generalSetting.wikiMode,
       wikiMode: generalSetting.wikiMode,
       disableLinkSharing: shareLinkSetting.disableLinkSharing,
       disableLinkSharing: shareLinkSetting.disableLinkSharing,
@@ -193,6 +195,13 @@ export default class AdminGeneralSecurityContainer extends Container {
     this.setState({ isShowRestrictedByGroup:  !this.state.isShowRestrictedByGroup });
     this.setState({ isShowRestrictedByGroup:  !this.state.isShowRestrictedByGroup });
   }
   }
 
 
+  /**
+   * Switch isUsersHomepageDeletionEnabled
+   */
+  switchIsUsersHomepageDeletionEnabled() {
+    this.setState({ isUsersHomepageDeletionEnabled: !this.state.isUsersHomepageDeletionEnabled });
+  }
+
   /**
   /**
    * Update restrictGuestMode
    * Update restrictGuestMode
    * @memberOf AdminGeneralSecuritySContainer
    * @memberOf AdminGeneralSecuritySContainer
@@ -209,6 +218,7 @@ export default class AdminGeneralSecurityContainer extends Container {
       pageRecursiveCompleteDeletionAuthority: this.state.currentPageRecursiveCompleteDeletionAuthority,
       pageRecursiveCompleteDeletionAuthority: this.state.currentPageRecursiveCompleteDeletionAuthority,
       hideRestrictedByGroup: !this.state.isShowRestrictedByGroup,
       hideRestrictedByGroup: !this.state.isShowRestrictedByGroup,
       hideRestrictedByOwner: !this.state.isShowRestrictedByOwner,
       hideRestrictedByOwner: !this.state.isShowRestrictedByOwner,
+      isUsersHomepageDeletionEnabled: this.state.isUsersHomepageDeletionEnabled,
     };
     };
 
 
     requestParams = await removeNullPropertyFromObject(requestParams);
     requestParams = await removeNullPropertyFromObject(requestParams);

+ 1 - 2
apps/app/src/client/services/AdminGitHubSecurityContainer.js

@@ -1,6 +1,5 @@
-import { isServer, pathUtils } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
-import urljoin from 'url-join';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 import { removeNullPropertyFromObject } from '~/utils/object-utils';
 import { removeNullPropertyFromObject } from '~/utils/object-utils';

+ 1 - 2
apps/app/src/client/services/AdminGoogleSecurityContainer.js

@@ -1,6 +1,5 @@
-import { isServer, pathUtils } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
-import urljoin from 'url-join';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 import { removeNullPropertyFromObject } from '~/utils/object-utils';
 import { removeNullPropertyFromObject } from '~/utils/object-utils';

+ 2 - 2
apps/app/src/client/services/AdminHomeContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -9,7 +9,7 @@ import { apiv3Get } from '../util/apiv3-client';
 const logger = loggerFactory('growi:services:AdminHomeContainer');
 const logger = loggerFactory('growi:services:AdminHomeContainer');
 
 
 /**
 /**
- * Service container for admin home page (AdminHome.jsx)
+ * Service container for admin homepage (AdminHome.jsx)
  * @extends {Container} unstated Container
  * @extends {Container} unstated Container
  */
  */
 export default class AdminHomeContainer extends Container {
 export default class AdminHomeContainer extends Container {

+ 1 - 1
apps/app/src/client/services/AdminImportContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';

+ 1 - 1
apps/app/src/client/services/AdminLdapSecurityContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';

+ 1 - 1
apps/app/src/client/services/AdminLocalSecurityContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';

+ 1 - 1
apps/app/src/client/services/AdminMarkDownContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import { apiv3Get, apiv3Put } from '../util/apiv3-client';
 import { apiv3Get, apiv3Put } from '../util/apiv3-client';

+ 1 - 1
apps/app/src/client/services/AdminNotificationContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import {
 import {

+ 1 - 2
apps/app/src/client/services/AdminOidcSecurityContainer.js

@@ -1,6 +1,5 @@
-import { isServer, pathUtils } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
-import urljoin from 'url-join';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 import { removeNullPropertyFromObject } from '~/utils/object-utils';
 import { removeNullPropertyFromObject } from '~/utils/object-utils';

+ 1 - 2
apps/app/src/client/services/AdminSamlSecurityContainer.js

@@ -1,6 +1,5 @@
-import { isServer, pathUtils } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
-import urljoin from 'url-join';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 import { removeNullPropertyFromObject } from '~/utils/object-utils';
 import { removeNullPropertyFromObject } from '~/utils/object-utils';

+ 1 - 1
apps/app/src/client/services/AdminSlackIntegrationLegacyContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 
 import { apiv3Get, apiv3Put } from '../util/apiv3-client';
 import { apiv3Get, apiv3Put } from '../util/apiv3-client';

+ 1 - 1
apps/app/src/client/services/AdminUsersContainer.js

@@ -1,4 +1,4 @@
-import { isServer } from '@growi/core';
+import { isServer } from '@growi/core/dist/utils';
 import { debounce } from 'throttle-debounce';
 import { debounce } from 'throttle-debounce';
 import { Container } from 'unstated';
 import { Container } from 'unstated';
 
 

+ 2 - 1
apps/app/src/client/services/layout.ts

@@ -1,4 +1,5 @@
-import type { IPage } from '~/interfaces/page';
+import type { IPage } from '@growi/core';
+
 import { useIsContainerFluid } from '~/stores/context';
 import { useIsContainerFluid } from '~/stores/context';
 import { useSWRxCurrentPage } from '~/stores/page';
 import { useSWRxCurrentPage } from '~/stores/page';
 import { useEditorMode } from '~/stores/ui';
 import { useEditorMode } from '~/stores/ui';

+ 2 - 2
apps/app/src/client/services/renderer/renderer.tsx

@@ -1,10 +1,10 @@
 import assert from 'assert';
 import assert from 'assert';
 
 
 import { isClient } from '@growi/core/dist/utils/browser-utils';
 import { isClient } from '@growi/core/dist/utils/browser-utils';
-import * as refsGrowiDirective from '@growi/remark-attachment-refs/dist/client/index.mjs';
+import * as refsGrowiDirective from '@growi/remark-attachment-refs/dist/client';
 import * as drawio from '@growi/remark-drawio';
 import * as drawio from '@growi/remark-drawio';
 // eslint-disable-next-line import/extensions
 // eslint-disable-next-line import/extensions
-import * as lsxGrowiDirective from '@growi/remark-lsx/dist/client/index.mjs';
+import * as lsxGrowiDirective from '@growi/remark-lsx/dist/client';
 import katex from 'rehype-katex';
 import katex from 'rehype-katex';
 import sanitize from 'rehype-sanitize';
 import sanitize from 'rehype-sanitize';
 import slug from 'rehype-slug';
 import slug from 'rehype-slug';

+ 1 - 1
apps/app/src/client/services/user-ui-settings.ts

@@ -15,7 +15,7 @@ const _putUserUISettingsInBulk = (): Promise<AxiosResponse<IUserUISettings>> =>
   return result;
   return result;
 };
 };
 
 
-const _putUserUISettingsInBulkDebounced = debounce(1500, false, _putUserUISettingsInBulk);
+const _putUserUISettingsInBulkDebounced = debounce(1500, _putUserUISettingsInBulk);
 
 
 type ScheduleToPutFunction = (settings: Partial<IUserUISettings>) => Promise<AxiosResponse<IUserUISettings>>;
 type ScheduleToPutFunction = (settings: Partial<IUserUISettings>) => Promise<AxiosResponse<IUserUISettings>>;
 const scheduleToPut: ScheduleToPutFunction = (settings: Partial<IUserUISettings>): Promise<AxiosResponse<IUserUISettings>> => {
 const scheduleToPut: ScheduleToPutFunction = (settings: Partial<IUserUISettings>): Promise<AxiosResponse<IUserUISettings>> => {

+ 1 - 1
apps/app/src/client/util/bookmark-utils.ts

@@ -1,4 +1,4 @@
-import { IRevision, Ref } from '@growi/core';
+import type { IRevision, Ref } from '@growi/core';
 
 
 import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
 import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
 
 

+ 1 - 2
apps/app/src/client/util/toastr.ts

@@ -1,5 +1,4 @@
 import { toast, ToastContent, ToastOptions } from 'react-toastify';
 import { toast, ToastContent, ToastOptions } from 'react-toastify';
-import * as toastrLegacy from 'toastr';
 
 
 import { toArrayIfNot } from '~/utils/array-utils';
 import { toArrayIfNot } from '~/utils/array-utils';
 
 
@@ -34,5 +33,5 @@ export const toastWarningOption: ToastOptions = {
   closeButton: true,
   closeButton: true,
 };
 };
 export const toastWarning = (content: ToastContent, option: ToastOptions = toastWarningOption): void => {
 export const toastWarning = (content: ToastContent, option: ToastOptions = toastWarningOption): void => {
-  toastrLegacy.warning(content, option);
+  toast.warning(content, option);
 };
 };

+ 8 - 3
apps/app/src/components/Admin/AuditLog/ActivityTable.tsx

@@ -1,7 +1,7 @@
 import React, { FC, useState, useCallback } from 'react';
 import React, { FC, useState, useCallback } from 'react';
 
 
-import { pagePathUtils } from '@growi/core';
-import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
+import { pagePathUtils } from '@growi/core/dist/utils';
+import { UserPicture } from '@growi/ui/dist/components';
 import { format } from 'date-fns';
 import { format } from 'date-fns';
 import { CopyToClipboard } from 'react-copy-to-clipboard';
 import { CopyToClipboard } from 'react-copy-to-clipboard';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
@@ -48,7 +48,12 @@ export const ActivityTable : FC<Props> = (props: Props) => {
                   { activity.user != null && (
                   { activity.user != null && (
                     <>
                     <>
                       <UserPicture user={activity.user} />
                       <UserPicture user={activity.user} />
-                      <a className="ml-2" href={pagePathUtils.userPageRoot(activity.user)}>{activity.snapshot?.username}</a>
+                      <a
+                        className="ml-2"
+                        href={pagePathUtils.userHomepagePath(activity.user)}
+                      >
+                        {activity.snapshot?.username}
+                      </a>
                     </>
                     </>
                   )}
                   )}
                 </td>
                 </td>

+ 2 - 2
apps/app/src/components/Admin/Common/AdminNavigation.tsx

@@ -1,6 +1,6 @@
 import React, { useCallback } from 'react';
 import React, { useCallback } from 'react';
 
 
-import { pathUtils } from '@growi/core';
+import { pathUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import Link from 'next/link';
 import Link from 'next/link';
 import urljoin from 'url-join';
 import urljoin from 'url-join';
@@ -29,7 +29,7 @@ const MenuLabel = ({ menu }: { menu: string }) => {
     case 'plugins':                  return <><i className="mr-1 icon-fw icon-puzzle"></i>{          t('plugins.plugins')}</>;
     case 'plugins':                  return <><i className="mr-1 icon-fw icon-puzzle"></i>{          t('plugins.plugins')}</>;
     case 'search':                   return <><i className="mr-1 icon-fw icon-magnifier"></i>{       t('full_text_search_management.full_text_search_management') }</>;
     case 'search':                   return <><i className="mr-1 icon-fw icon-magnifier"></i>{       t('full_text_search_management.full_text_search_management') }</>;
     case 'cloud':                    return <><i className="mr-1 icon-fw icon-share-alt"></i>{       t('cloud_setting_management.to_cloud_settings')} </>;
     case 'cloud':                    return <><i className="mr-1 icon-fw icon-share-alt"></i>{       t('cloud_setting_management.to_cloud_settings')} </>;
-    default:                         return <><i className="mr-1 icon-fw icon-home"></i>{            t('wiki_management_home_page') }</>;
+    default:                         return <><i className="mr-1 icon-fw icon-home"></i>{            t('wiki_management_homepage') }</>;
       /* eslint-enable no-multi-spaces, max-len */
       /* eslint-enable no-multi-spaces, max-len */
   }
   }
 };
 };

+ 1 - 1
apps/app/src/components/Admin/Customize/CustomizeThemeOptions.tsx

@@ -1,6 +1,6 @@
 import React, { useMemo } from 'react';
 import React, { useMemo } from 'react';
 
 
-import { GrowiThemeMetadata, GrowiThemeSchemeType } from '@growi/core';
+import { type GrowiThemeMetadata, GrowiThemeSchemeType } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
 import { ThemeColorBox } from './ThemeColorBox';
 import { ThemeColorBox } from './ThemeColorBox';

+ 3 - 23
apps/app/src/components/Admin/ExportArchiveData/SelectCollectionsModal.tsx

@@ -4,11 +4,9 @@ import { useTranslation } from 'next-i18next';
 import {
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 } from 'reactstrap';
-import * as toastr from 'toastr';
 
 
 import { apiPost } from '~/client/util/apiv1-client';
 import { apiPost } from '~/client/util/apiv1-client';
-
-// import { toastSuccess, toastError } from '~/client/util/toastr';
+import { toastError, toastSuccess } from '~/client/util/toastr';
 
 
 
 
 const GROUPS_PAGE = [
 const GROUPS_PAGE = [
@@ -72,37 +70,19 @@ const SelectCollectionsModal = (props: Props): JSX.Element => {
     try {
     try {
       // TODO: use apiv3Post
       // TODO: use apiv3Post
       const result = await apiPost<any>('/v3/export', { collections: Array.from(selectedCollections) });
       const result = await apiPost<any>('/v3/export', { collections: Array.from(selectedCollections) });
-      // TODO: toastSuccess, toastError
 
 
       if (!result.ok) {
       if (!result.ok) {
         throw new Error('Error occured.');
         throw new Error('Error occured.');
       }
       }
 
 
-      // TODO: toastSuccess, toastError
-      toastr.success(undefined, 'Export process has requested.', {
-        closeButton: true,
-        progressBar: true,
-        newestOnTop: false,
-        showDuration: '100',
-        hideDuration: '100',
-        timeOut: '1200',
-        extendedTimeOut: '150',
-      });
+      toastSuccess('Export process has requested.');
 
 
       onExportingRequested();
       onExportingRequested();
       onClose();
       onClose();
       uncheckAll();
       uncheckAll();
     }
     }
     catch (err) {
     catch (err) {
-      // TODO: toastSuccess, toastError
-      toastr.error(err, 'Error', {
-        closeButton: true,
-        progressBar: true,
-        newestOnTop: false,
-        showDuration: '100',
-        hideDuration: '100',
-        timeOut: '3000',
-      });
+      toastError(err);
     }
     }
   }, [onClose, onExportingRequested, selectedCollections, uncheckAll]);
   }, [onClose, onExportingRequested, selectedCollections, uncheckAll]);
 
 

+ 4 - 31
apps/app/src/components/Admin/ExportArchiveDataPage.tsx

@@ -1,11 +1,11 @@
 import React, { useCallback, useEffect, useState } from 'react';
 import React, { useCallback, useEffect, useState } from 'react';
 
 
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
-import * as toastr from 'toastr';
 
 
 
 
 import { apiDelete } from '~/client/util/apiv1-client';
 import { apiDelete } from '~/client/util/apiv1-client';
 import { apiv3Get } from '~/client/util/apiv3-client';
 import { apiv3Get } from '~/client/util/apiv3-client';
+import { toastError, toastSuccess } from '~/client/util/toastr';
 import { useAdminSocket } from '~/stores/socket-io';
 import { useAdminSocket } from '~/stores/socket-io';
 
 
 import LabeledProgressBar from './Common/LabeledProgressBar';
 import LabeledProgressBar from './Common/LabeledProgressBar';
@@ -34,7 +34,6 @@ const ExportArchiveDataPage = (): JSX.Element => {
       apiv3Get<{collections: any[]}>('/mongo/collections', {}),
       apiv3Get<{collections: any[]}>('/mongo/collections', {}),
       apiv3Get<{status: { zipFileStats: any[], isExporting: boolean, progressList: any[] }}>('/export/status', {}),
       apiv3Get<{status: { zipFileStats: any[], isExporting: boolean, progressList: any[] }}>('/export/status', {}),
     ]);
     ]);
-    // TODO: toastSuccess, toastError
 
 
     // filter only not ignored collection names
     // filter only not ignored collection names
     const filteredCollections = collectionsData.collections.filter((collectionName) => {
     const filteredCollections = collectionsData.collections.filter((collectionName) => {
@@ -69,16 +68,7 @@ const ExportArchiveDataPage = (): JSX.Element => {
         setExported(true);
         setExported(true);
         setZipFileStats(prev => prev.concat([addedZipFileStat]));
         setZipFileStats(prev => prev.concat([addedZipFileStat]));
 
 
-        // TODO: toastSuccess, toastError
-        toastr.success(undefined, `New Archive Data '${addedZipFileStat.fileName}' is added`, {
-          closeButton: true,
-          progressBar: true,
-          newestOnTop: false,
-          showDuration: '100',
-          hideDuration: '100',
-          timeOut: '1200',
-          extendedTimeOut: '150',
-        });
+        toastSuccess(`New Archive Data '${addedZipFileStat.fileName}' is added`);
       });
       });
     }
     }
   }, [socket]);
   }, [socket]);
@@ -89,27 +79,10 @@ const ExportArchiveDataPage = (): JSX.Element => {
 
 
       setZipFileStats(prev => prev.filter(stat => stat.fileName !== fileName));
       setZipFileStats(prev => prev.filter(stat => stat.fileName !== fileName));
 
 
-      // TODO: toastSuccess, toastError
-      toastr.success(undefined, `Deleted ${fileName}`, {
-        closeButton: true,
-        progressBar: true,
-        newestOnTop: false,
-        showDuration: '100',
-        hideDuration: '100',
-        timeOut: '1200',
-        extendedTimeOut: '150',
-      });
+      toastSuccess(`Deleted ${fileName}`);
     }
     }
     catch (err) {
     catch (err) {
-      // TODO: toastSuccess, toastError
-      toastr.error(err, 'Error', {
-        closeButton: true,
-        progressBar: true,
-        newestOnTop: false,
-        showDuration: '100',
-        hideDuration: '100',
-        timeOut: '3000',
-      });
+      toastError(err);
     }
     }
   }, []);
   }, []);
 
 

+ 17 - 0
apps/app/src/components/Admin/ForbiddenPage.tsx

@@ -0,0 +1,17 @@
+import React from 'react';
+
+import DefaultErrorPage from 'next/error';
+import { useTranslation } from 'react-i18next';
+
+
+export const ForbiddenPage = (): JSX.Element => {
+  const { t } = useTranslation('admin');
+
+  const errorMessage = t('forbidden_page.do_not_have_admin_permission');
+
+  return (
+    <>
+      <DefaultErrorPage statusCode={403} title={errorMessage} />
+    </>
+  );
+};

+ 0 - 1
apps/app/src/components/Admin/ImportData/GrowiArchive/UploadForm.jsx

@@ -32,7 +32,6 @@ class UploadForm extends React.Component {
 
 
     try {
     try {
       const { data } = await apiv3PostForm('/import/upload', formData);
       const { data } = await apiv3PostForm('/import/upload', formData);
-      // TODO: toastSuccess, toastError
       this.props.onUpload(data);
       this.props.onUpload(data);
     }
     }
     catch (err) {
     catch (err) {

+ 3 - 21
apps/app/src/components/Admin/ImportData/GrowiArchiveSection.jsx

@@ -2,11 +2,10 @@ import React, { Fragment } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
-import * as toastr from 'toastr';
 
 
 import { apiv3Delete, apiv3Get } from '~/client/util/apiv3-client';
 import { apiv3Delete, apiv3Get } from '~/client/util/apiv3-client';
+import { toastError, toastSuccess } from '~/client/util/toastr';
 
 
-// import { toastSuccess, toastError } from '~/client/util/toastr';
 
 
 import ImportForm from './GrowiArchive/ImportForm';
 import ImportForm from './GrowiArchive/ImportForm';
 import UploadForm from './GrowiArchive/UploadForm';
 import UploadForm from './GrowiArchive/UploadForm';
@@ -59,27 +58,10 @@ class GrowiArchiveSection extends React.Component {
       await apiv3Delete('/import/all');
       await apiv3Delete('/import/all');
       this.resetState();
       this.resetState();
 
 
-      // TODO: toastSuccess, toastError
-      toastr.success(undefined, `Deleted ${fileName}`, {
-        closeButton: true,
-        progressBar: true,
-        newestOnTop: false,
-        showDuration: '100',
-        hideDuration: '100',
-        timeOut: '1200',
-        extendedTimeOut: '150',
-      });
+      toastSuccess(`Deleted ${fileName}`);
     }
     }
     catch (err) {
     catch (err) {
-      // TODO: toastSuccess, toastError
-      toastr.error(err, 'Error', {
-        closeButton: true,
-        progressBar: true,
-        newestOnTop: false,
-        showDuration: '100',
-        hideDuration: '100',
-        timeOut: '3000',
-      });
+      toastError(err);
     }
     }
   }
   }
 
 

+ 1 - 1
apps/app/src/components/Admin/Security/GitHubSecuritySettingContents.jsx

@@ -1,7 +1,7 @@
 /* eslint-disable react/no-danger */
 /* eslint-disable react/no-danger */
 import React from 'react';
 import React from 'react';
 
 
-import { pathUtils } from '@growi/core';
+import { pathUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import urljoin from 'url-join';
 import urljoin from 'url-join';

+ 1 - 1
apps/app/src/components/Admin/Security/GoogleSecuritySettingContents.jsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 
 
-import { pathUtils } from '@growi/core';
+import { pathUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import urljoin from 'url-join';
 import urljoin from 'url-join';

+ 1 - 1
apps/app/src/components/Admin/Security/OidcSecuritySettingContents.jsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 
 
-import { pathUtils } from '@growi/core';
+import { pathUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import urljoin from 'url-join';
 import urljoin from 'url-join';

+ 1 - 1
apps/app/src/components/Admin/Security/SamlSecuritySettingContents.jsx

@@ -1,7 +1,7 @@
 /* eslint-disable react/no-danger */
 /* eslint-disable react/no-danger */
 import React from 'react';
 import React from 'react';
 
 
-import { pathUtils } from '@growi/core';
+import { pathUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { Collapse } from 'reactstrap';
 import { Collapse } from 'reactstrap';

+ 22 - 0
apps/app/src/components/Admin/Security/SecuritySetting.jsx

@@ -453,6 +453,28 @@ class SecuritySetting extends React.Component {
           ].map(arr => this.renderPageDeletePermission(arr[0], arr[1], arr[2], arr[3]))
           ].map(arr => this.renderPageDeletePermission(arr[0], arr[1], arr[2], arr[3]))
         }
         }
 
 
+        <h4>{t('security_settings.user_homepage_deletion.user_homepage_deletion')}</h4>
+        <div className="row mb-4">
+          <div className="col-6 offset-3">
+            <div className="custom-control custom-switch custom-checkbox-success">
+              <input
+                type="checkbox"
+                className="custom-control-input"
+                id="is-user-page-deletion-enabled"
+                checked={adminGeneralSecurityContainer.state.isUsersHomepageDeletionEnabled}
+                onChange={() => { adminGeneralSecurityContainer.switchIsUsersHomepageDeletionEnabled() }}
+              />
+              <label className="custom-control-label" htmlFor="is-user-page-deletion-enabled">
+                {t('security_settings.user_homepage_deletion.enable_user_homepage_deletion')}
+              </label>
+            </div>
+            <p
+              className="form-text text-muted small"
+              dangerouslySetInnerHTML={{ __html: t('security_settings.user_homepage_deletion.when_deleting_a_user_the_user_homepage_is_also_deleted') }}
+            />
+          </div>
+        </div>
+
         <h4>{t('security_settings.session')}</h4>
         <h4>{t('security_settings.session')}</h4>
         <div className="form-group row">
         <div className="form-group row">
           <label className="text-left text-md-right col-md-3 col-form-label">{t('security_settings.max_age')}</label>
           <label className="text-left text-md-right col-md-3 col-form-label">{t('security_settings.max_age')}</label>

+ 1 - 1
apps/app/src/components/Admin/UserGroup/UserGroupDeleteModal.tsx

@@ -2,12 +2,12 @@ import React, {
   FC, useCallback, useState, useMemo,
   FC, useCallback, useState, useMemo,
 } from 'react';
 } from 'react';
 
 
+import type { IUserGroupHasId } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import {
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 } from 'reactstrap';
 
 
-import { IUserGroupHasId } from '~/interfaces/user';
 
 
 /**
 /**
  * Delete User Group Select component
  * Delete User Group Select component

+ 1 - 2
apps/app/src/components/Admin/UserGroup/UserGroupDropdown.tsx

@@ -1,9 +1,8 @@
 import React, { FC, useCallback } from 'react';
 import React, { FC, useCallback } from 'react';
 
 
+import type { IUserGroupHasId } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
-import type { IUserGroupHasId } from '~/interfaces/user';
-
 type Props = {
 type Props = {
   selectableUserGroups?: IUserGroupHasId[]
   selectableUserGroups?: IUserGroupHasId[]
   onClickAddExistingUserGroupButton?(userGroup: IUserGroupHasId | null): void
   onClickAddExistingUserGroupButton?(userGroup: IUserGroupHasId | null): void

+ 1 - 2
apps/app/src/components/Admin/UserGroup/UserGroupForm.tsx

@@ -2,11 +2,10 @@ import React, {
   FC, useCallback, useEffect, useState,
   FC, useCallback, useEffect, useState,
 } from 'react';
 } from 'react';
 
 
+import type { IUserGroupHasId } from '@growi/core';
 import dateFnsFormat from 'date-fns/format';
 import dateFnsFormat from 'date-fns/format';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
-import { IUserGroupHasId } from '~/interfaces/user';
-
 type Props = {
 type Props = {
   userGroup: IUserGroupHasId,
   userGroup: IUserGroupHasId,
   parentUserGroup?: IUserGroupHasId,
   parentUserGroup?: IUserGroupHasId,

+ 1 - 3
apps/app/src/components/Admin/UserGroup/UserGroupModal.tsx

@@ -2,14 +2,12 @@ import React, {
   FC, useState, useEffect, useCallback,
   FC, useState, useEffect, useCallback,
 } from 'react';
 } from 'react';
 
 
-import { Ref } from '@growi/core';
+import type { Ref, IUserGroup, IUserGroupHasId } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import {
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 } from 'reactstrap';
 
 
-import { IUserGroup, IUserGroupHasId } from '~/interfaces/user';
-
 type Props = {
 type Props = {
   userGroup?: IUserGroupHasId,
   userGroup?: IUserGroupHasId,
   buttonLabel?: string,
   buttonLabel?: string,

+ 1 - 1
apps/app/src/components/Admin/UserGroup/UserGroupPage.tsx

@@ -1,12 +1,12 @@
 import React, { FC, useState, useCallback } from 'react';
 import React, { FC, useState, useCallback } from 'react';
 
 
+import type { IUserGroup, IUserGroupHasId } from '@growi/core';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 
 
 import { apiv3Delete, apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
 import { apiv3Delete, apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { ExternalGroupManagement } from '~/features/external-user-group/client/components/ExternalUserGroup/ExternalUserGroupManagement';
 import { ExternalGroupManagement } from '~/features/external-user-group/client/components/ExternalUserGroup/ExternalUserGroupManagement';
-import { IUserGroup, IUserGroupHasId } from '~/interfaces/user';
 import { useIsAclEnabled } from '~/stores/context';
 import { useIsAclEnabled } from '~/stores/context';
 import { useSWRxUserGroupList, useSWRxChildUserGroupList, useSWRxUserGroupRelationList } from '~/stores/user-group';
 import { useSWRxUserGroupList, useSWRxChildUserGroupList, useSWRxUserGroupRelationList } from '~/stores/user-group';
 
 

+ 4 - 2
apps/app/src/components/Admin/UserGroupDetail/UserGroupDetailPage.tsx

@@ -2,7 +2,10 @@ import React, {
   useState, useCallback, useEffect, useMemo,
   useState, useCallback, useEffect, useMemo,
 } from 'react';
 } from 'react';
 
 
-import { objectIdUtils } from '@growi/core';
+import type {
+  IUserGroup, IUserGroupHasId, IUserGroup, IUserGroupHasId,
+} from '@growi/core';
+import { objectIdUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 import Link from 'next/link';
 import Link from 'next/link';
@@ -17,7 +20,6 @@ import {
 } from '~/client/util/apiv3-client';
 } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { IExternalUserGroupHasId } from '~/features/external-user-group/interfaces/external-user-group';
 import { IExternalUserGroupHasId } from '~/features/external-user-group/interfaces/external-user-group';
-import { IUserGroup, IUserGroupHasId } from '~/interfaces/user';
 import { SearchTypes, SearchType } from '~/interfaces/user-group';
 import { SearchTypes, SearchType } from '~/interfaces/user-group';
 import Xss from '~/services/xss';
 import Xss from '~/services/xss';
 import { useIsAclEnabled } from '~/stores/context';
 import { useIsAclEnabled } from '~/stores/context';

+ 1 - 1
apps/app/src/components/Admin/UserGroupDetail/UserGroupPageList.tsx

@@ -1,10 +1,10 @@
 import React, { useEffect, useState, useCallback } from 'react';
 import React, { useEffect, useState, useCallback } from 'react';
 
 
+import type { IPageHasId } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
 import { apiv3Get } from '~/client/util/apiv3-client';
 import { apiv3Get } from '~/client/util/apiv3-client';
 import { toastError } from '~/client/util/toastr';
 import { toastError } from '~/client/util/toastr';
-import { IPageHasId } from '~/interfaces/page';
 
 
 import { PageListItemS } from '../../PageList/PageListItemS';
 import { PageListItemS } from '../../PageList/PageListItemS';
 import PaginationWrapper from '../../PaginationWrapper';
 import PaginationWrapper from '../../PaginationWrapper';

+ 1 - 1
apps/app/src/components/Admin/UserGroupDetail/UserGroupUserFormByInput.jsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 
 
-import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
+import { UserPicture } from '@growi/ui/dist/components';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { AsyncTypeahead } from 'react-bootstrap-typeahead';
 import { AsyncTypeahead } from 'react-bootstrap-typeahead';

+ 1 - 1
apps/app/src/components/Admin/UserGroupDetail/UserGroupUserModal.tsx

@@ -1,11 +1,11 @@
 import React from 'react';
 import React from 'react';
 
 
+import type { IUserGroupHasId } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import {
 import {
   Modal, ModalHeader, ModalBody,
   Modal, ModalHeader, ModalBody,
 } from 'reactstrap';
 } from 'reactstrap';
 
 
-import { IUserGroupHasId } from '~/interfaces/user';
 import { SearchTypes, SearchType } from '~/interfaces/user-group';
 import { SearchTypes, SearchType } from '~/interfaces/user-group';
 
 
 import CheckBoxForSerchUserOption from './CheckBoxForSerchUserOption';
 import CheckBoxForSerchUserOption from './CheckBoxForSerchUserOption';

+ 1 - 1
apps/app/src/components/Admin/UserGroupDetail/UserGroupUserTable.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 
 
-import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
+import { UserPicture } from '@growi/ui/dist/components';
 import dateFnsFormat from 'date-fns/format';
 import dateFnsFormat from 'date-fns/format';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 

+ 1 - 1
apps/app/src/components/Admin/Users/UserMenu.tsx

@@ -1,6 +1,6 @@
 import React, { useState, useCallback } from 'react';
 import React, { useState, useCallback } from 'react';
 
 
-import { IUserHasId, USER_STATUS } from '@growi/core';
+import { type IUserHasId, USER_STATUS } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import {
 import {
   UncontrolledDropdown, DropdownToggle, DropdownMenu,
   UncontrolledDropdown, DropdownToggle, DropdownMenu,

+ 1 - 1
apps/app/src/components/Admin/Users/UserTable.tsx

@@ -1,7 +1,7 @@
 import React, { useCallback } from 'react';
 import React, { useCallback } from 'react';
 
 
 import type { IUserHasId } from '@growi/core';
 import type { IUserHasId } from '@growi/core';
-import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
+import { UserPicture } from '@growi/ui/dist/components';
 import dateFnsFormat from 'date-fns/format';
 import dateFnsFormat from 'date-fns/format';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 

+ 4 - 4
apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx

@@ -2,6 +2,7 @@ import {
   FC, useCallback, useState,
   FC, useCallback, useState,
 } from 'react';
 } from 'react';
 
 
+import type { IPageToDeleteWithMeta } from '@growi/core';
 import { DropdownToggle } from 'reactstrap';
 import { DropdownToggle } from 'reactstrap';
 
 
 import {
 import {
@@ -13,7 +14,6 @@ import { TriangleIcon } from '~/components/Icons/TriangleIcon';
 import {
 import {
   BookmarkFolderItems, DragItemDataType, DragItemType, DRAG_ITEM_TYPE,
   BookmarkFolderItems, DragItemDataType, DragItemType, DRAG_ITEM_TYPE,
 } from '~/interfaces/bookmark-info';
 } from '~/interfaces/bookmark-info';
-import { IPageToDeleteWithMeta } from '~/interfaces/page';
 import { onDeletedBookmarkFolderFunction } from '~/interfaces/ui';
 import { onDeletedBookmarkFolderFunction } from '~/interfaces/ui';
 import { useBookmarkFolderDeleteModal } from '~/stores/modal';
 import { useBookmarkFolderDeleteModal } from '~/stores/modal';
 
 
@@ -29,7 +29,7 @@ type BookmarkFolderItemProps = {
   isOperable: boolean,
   isOperable: boolean,
   level: number
   level: number
   root: string
   root: string
-  isUserHomePage?: boolean
+  isUserHomepage?: boolean
   onClickDeleteMenuItemHandler: (pageToDelete: IPageToDeleteWithMeta) => void
   onClickDeleteMenuItemHandler: (pageToDelete: IPageToDeleteWithMeta) => void
   bookmarkFolderTreeMutation: () => void
   bookmarkFolderTreeMutation: () => void
 }
 }
@@ -38,7 +38,7 @@ export const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkF
   const BASE_FOLDER_PADDING = 15;
   const BASE_FOLDER_PADDING = 15;
   const acceptedTypes: DragItemType[] = [DRAG_ITEM_TYPE.FOLDER, DRAG_ITEM_TYPE.BOOKMARK];
   const acceptedTypes: DragItemType[] = [DRAG_ITEM_TYPE.FOLDER, DRAG_ITEM_TYPE.BOOKMARK];
   const {
   const {
-    isReadOnlyUser, bookmarkFolder, isOpen: _isOpen = false, isOperable, level, root, isUserHomePage,
+    isReadOnlyUser, bookmarkFolder, isOpen: _isOpen = false, isOperable, level, root, isUserHomepage,
     onClickDeleteMenuItemHandler, bookmarkFolderTreeMutation,
     onClickDeleteMenuItemHandler, bookmarkFolderTreeMutation,
   } = props;
   } = props;
 
 
@@ -155,7 +155,7 @@ export const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkF
             bookmarkFolder={childFolder}
             bookmarkFolder={childFolder}
             level={level + 1}
             level={level + 1}
             root={root}
             root={root}
-            isUserHomePage={isUserHomePage}
+            isUserHomepage={isUserHomepage}
             onClickDeleteMenuItemHandler={onClickDeleteMenuItemHandler}
             onClickDeleteMenuItemHandler={onClickDeleteMenuItemHandler}
             bookmarkFolderTreeMutation={bookmarkFolderTreeMutation}
             bookmarkFolderTreeMutation={bookmarkFolderTreeMutation}
           />
           />

+ 4 - 4
apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx

@@ -1,11 +1,11 @@
 
 
 import React, { useCallback } from 'react';
 import React, { useCallback } from 'react';
 
 
+import type { IPageToDeleteWithMeta } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
 
 
 import { toastSuccess } from '~/client/util/toastr';
 import { toastSuccess } from '~/client/util/toastr';
-import { IPageToDeleteWithMeta } from '~/interfaces/page';
 import { OnDeletedFunction } from '~/interfaces/ui';
 import { OnDeletedFunction } from '~/interfaces/ui';
 import {
 import {
   useSWRxUserBookmarks, useSWRMUTxCurrentUserBookmarks,
   useSWRxUserBookmarks, useSWRMUTxCurrentUserBookmarks,
@@ -27,13 +27,13 @@ import styles from './BookmarkFolderTree.module.scss';
 //  } & IPageHasId
 //  } & IPageHasId
 
 
 type Props = {
 type Props = {
-  isUserHomePage?: boolean,
+  isUserHomepage?: boolean,
   userId?: string,
   userId?: string,
   isOperable: boolean,
   isOperable: boolean,
 }
 }
 
 
 export const BookmarkFolderTree: React.FC<Props> = (props: Props) => {
 export const BookmarkFolderTree: React.FC<Props> = (props: Props) => {
-  const { isUserHomePage, userId } = props;
+  const { isUserHomepage, userId } = props;
 
 
   // const acceptedTypes: DragItemType[] = [DRAG_ITEM_TYPE.FOLDER, DRAG_ITEM_TYPE.BOOKMARK];
   // const acceptedTypes: DragItemType[] = [DRAG_ITEM_TYPE.FOLDER, DRAG_ITEM_TYPE.BOOKMARK];
   const { t } = useTranslation();
   const { t } = useTranslation();
@@ -114,7 +114,7 @@ export const BookmarkFolderTree: React.FC<Props> = (props: Props) => {
               isOpen={false}
               isOpen={false}
               level={0}
               level={0}
               root={bookmarkFolder._id}
               root={bookmarkFolder._id}
-              isUserHomePage={isUserHomePage}
+              isUserHomepage={isUserHomepage}
               onClickDeleteMenuItemHandler={onClickDeleteMenuItemHandler}
               onClickDeleteMenuItemHandler={onClickDeleteMenuItemHandler}
               bookmarkFolderTreeMutation={bookmarkFolderTreeMutation}
               bookmarkFolderTreeMutation={bookmarkFolderTreeMutation}
             />
             />

+ 3 - 2
apps/app/src/components/Bookmarks/BookmarkItem.tsx

@@ -2,7 +2,9 @@ import React, { useCallback, useState } from 'react';
 
 
 import nodePath from 'path';
 import nodePath from 'path';
 
 
-import { DevidedPagePath, pathUtils } from '@growi/core';
+import type { IPageHasId, IPageInfoAll, IPageToDeleteWithMeta } from '@growi/core';
+import { DevidedPagePath } from '@growi/core/dist/models';
+import { pathUtils } from '@growi/core/dist/utils';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { UncontrolledTooltip, DropdownToggle } from 'reactstrap';
 import { UncontrolledTooltip, DropdownToggle } from 'reactstrap';
@@ -13,7 +15,6 @@ import { addBookmarkToFolder, renamePage } from '~/client/util/bookmark-utils';
 import { ValidationTarget } from '~/client/util/input-validator';
 import { ValidationTarget } from '~/client/util/input-validator';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { BookmarkFolderItems, DragItemDataType, DRAG_ITEM_TYPE } from '~/interfaces/bookmark-info';
 import { BookmarkFolderItems, DragItemDataType, DRAG_ITEM_TYPE } from '~/interfaces/bookmark-info';
-import { IPageHasId, IPageInfoAll, IPageToDeleteWithMeta } from '~/interfaces/page';
 import { usePutBackPageModal } from '~/stores/modal';
 import { usePutBackPageModal } from '~/stores/modal';
 import { mutateAllPageInfo, useSWRMUTxCurrentPage, useSWRxPageInfo } from '~/stores/page';
 import { mutateAllPageInfo, useSWRMUTxCurrentPage, useSWRxPageInfo } from '~/stores/page';
 
 

+ 16 - 24
apps/app/src/components/Comments.tsx

@@ -1,9 +1,11 @@
-import React, { useEffect, useRef } from 'react';
+import React, { useEffect, useMemo, useRef } from 'react';
 
 
-import { type IRevisionHasId, pagePathUtils } from '@growi/core';
+import type { IRevisionHasId } from '@growi/core';
+import { pagePathUtils } from '@growi/core/dist/utils';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
+import { debounce } from 'throttle-debounce';
 
 
-import { ROOT_ELEM_ID as PageCommentRootElemId, type PageCommentProps } from '~/components/PageComment';
+import { type PageCommentProps } from '~/components/PageComment';
 import { useSWRxPageComment } from '~/stores/comment';
 import { useSWRxPageComment } from '~/stores/comment';
 import { useIsTrashPage, useSWRMUTxPageInfo } from '~/stores/page';
 import { useIsTrashPage, useSWRMUTxPageInfo } from '~/stores/page';
 
 
@@ -38,30 +40,21 @@ export const Comments = (props: CommentsProps): JSX.Element => {
 
 
   const pageCommentParentRef = useRef<HTMLDivElement>(null);
   const pageCommentParentRef = useRef<HTMLDivElement>(null);
 
 
+  const onLoadedDebounced = useMemo(() => debounce(500, () => onLoaded?.()), [onLoaded]);
+
   useEffect(() => {
   useEffect(() => {
     const parent = pageCommentParentRef.current;
     const parent = pageCommentParentRef.current;
     if (parent == null) return;
     if (parent == null) return;
 
 
-    const observerCallback = (mutationRecords: MutationRecord[]) => {
-      mutationRecords.forEach((record: MutationRecord) => {
-        const target = record.target as HTMLElement;
-
-        for (const child of Array.from(target.children)) {
-          const childId = (child as HTMLElement).id;
-          if (childId === PageCommentRootElemId) {
-            onLoaded?.();
-            break;
-          }
-        }
-
-      });
-    };
-
-    const observer = new MutationObserver(observerCallback);
-    observer.observe(parent, { childList: true });
-    return () => {
-      observer.disconnect();
-    };
+    const observer = new MutationObserver(() => {
+      onLoadedDebounced();
+    });
+    observer.observe(parent, { childList: true, subtree: true });
+
+    // no cleanup function -- 2023.07.31 Yuki Takei
+    // see: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/observe
+    // > You can call observe() multiple times on the same MutationObserver
+    // > to watch for changes to different parts of the DOM tree and/or different types of changes.
   }, [onLoaded]);
   }, [onLoaded]);
 
 
   const isTopPagePath = isTopPage(pagePath);
   const isTopPagePath = isTopPage(pagePath);
@@ -86,7 +79,6 @@ export const Comments = (props: CommentsProps): JSX.Element => {
             currentUser={currentUser}
             currentUser={currentUser}
             isReadOnly={false}
             isReadOnly={false}
             titleAlign="left"
             titleAlign="left"
-            hideIfEmpty={false}
           />
           />
         </div>
         </div>
         {!isDeleted && (
         {!isDeleted && (

+ 3 - 3
apps/app/src/components/Common/Dropdown/PageItemControl.tsx

@@ -2,6 +2,9 @@ import React, {
   useState, useCallback, useEffect,
   useState, useCallback, useEffect,
 } from 'react';
 } from 'react';
 
 
+import {
+  type IPageInfoAll, isIPageInfoForOperation,
+} from '@growi/core';
 import { getCustomModifiers } from '@growi/ui/dist/utils';
 import { getCustomModifiers } from '@growi/ui/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import {
 import {
@@ -9,9 +12,6 @@ import {
 } from 'reactstrap';
 } from 'reactstrap';
 
 
 import { NotAvailableForGuest } from '~/components/NotAvailableForGuest';
 import { NotAvailableForGuest } from '~/components/NotAvailableForGuest';
-import {
-  IPageInfoAll, isIPageInfoForOperation,
-} from '~/interfaces/page';
 import { IPageOperationProcessData } from '~/interfaces/page-operation';
 import { IPageOperationProcessData } from '~/interfaces/page-operation';
 import { useSWRxPageInfo } from '~/stores/page';
 import { useSWRxPageInfo } from '~/stores/page';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';

+ 1 - 1
apps/app/src/components/ContentLinkButtons.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 
 
-import { IUserHasId } from '@growi/core';
+import type { IUserHasId } from '@growi/core';
 import { Link as ScrollLink } from 'react-scroll';
 import { Link as ScrollLink } from 'react-scroll';
 
 
 import { RecentlyCreatedIcon } from '~/components/Icons/RecentlyCreatedIcon';
 import { RecentlyCreatedIcon } from '~/components/Icons/RecentlyCreatedIcon';

+ 1 - 1
apps/app/src/components/CreateTemplateModal.jsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 
 
-import { pathUtils } from '@growi/core';
+import { pathUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { Modal, ModalHeader, ModalBody } from 'reactstrap';
 import { Modal, ModalHeader, ModalBody } from 'reactstrap';

+ 1 - 1
apps/app/src/components/CustomNavigation/CustomNav.tsx

@@ -2,7 +2,7 @@ import React, {
   useEffect, useState, useRef, useMemo, useCallback,
   useEffect, useState, useRef, useMemo, useCallback,
 } from 'react';
 } from 'react';
 
 
-import { Breakpoint } from '@growi/ui/dist/interfaces/breakpoints';
+import { Breakpoint } from '@growi/ui/dist/interfaces';
 import {
 import {
   Nav, NavItem, NavLink,
   Nav, NavItem, NavLink,
 } from 'reactstrap';
 } from 'reactstrap';

+ 5 - 5
apps/app/src/components/DescendantsPageList.tsx

@@ -1,13 +1,13 @@
 import React, { useCallback, useState } from 'react';
 import React, { useCallback, useState } from 'react';
 
 
-import { useTranslation } from 'next-i18next';
-
-import { toastSuccess } from '~/client/util/toastr';
-import {
+import type {
   IDataWithMeta,
   IDataWithMeta,
   IPageHasId,
   IPageHasId,
   IPageInfoForOperation,
   IPageInfoForOperation,
-} from '~/interfaces/page';
+} from '@growi/core';
+import { useTranslation } from 'next-i18next';
+
+import { toastSuccess } from '~/client/util/toastr';
 import { IPagingResult } from '~/interfaces/paging-result';
 import { IPagingResult } from '~/interfaces/paging-result';
 import { OnDeletedFunction, OnPutBackedFunction } from '~/interfaces/ui';
 import { OnDeletedFunction, OnPutBackedFunction } from '~/interfaces/ui';
 import { useIsGuestUser, useIsReadOnlyUser, useIsSharedUser } from '~/stores/context';
 import { useIsGuestUser, useIsReadOnlyUser, useIsSharedUser } from '~/stores/context';

+ 1 - 1
apps/app/src/components/DuplicatedPathsTable.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 
 
-import { pagePathUtils } from '@growi/core';
+import { pagePathUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
 
 

+ 1 - 1
apps/app/src/components/IdenticalPathPage.tsx

@@ -1,6 +1,6 @@
 import React, { FC } from 'react';
 import React, { FC } from 'react';
 
 
-import { DevidedPagePath } from '@growi/core';
+import { DevidedPagePath } from '@growi/core/dist/models';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
 import { useCurrentPathname } from '~/stores/context';
 import { useCurrentPathname } from '~/stores/context';

+ 2 - 2
apps/app/src/components/InAppNotification/InAppNotificationElm.tsx

@@ -2,8 +2,8 @@ import React, {
   FC, useRef,
   FC, useRef,
 } from 'react';
 } from 'react';
 
 
-import { HasObjectId } from '@growi/core';
-import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
+import type { HasObjectId } from '@growi/core';
+import { UserPicture } from '@growi/ui/dist/components';
 import { DropdownItem } from 'reactstrap';
 import { DropdownItem } from 'reactstrap';
 
 
 import { IInAppNotificationOpenable } from '~/client/interfaces/in-app-notification-openable';
 import { IInAppNotificationOpenable } from '~/client/interfaces/in-app-notification-openable';

+ 1 - 1
apps/app/src/components/InAppNotification/InAppNotificationList.tsx

@@ -1,6 +1,6 @@
 import React, { FC } from 'react';
 import React, { FC } from 'react';
 
 
-import { HasObjectId } from '@growi/core';
+import type { HasObjectId } from '@growi/core';
 
 
 import type { IInAppNotification, PaginateResult } from '~/interfaces/in-app-notification';
 import type { IInAppNotification, PaginateResult } from '~/interfaces/in-app-notification';
 
 

+ 2 - 2
apps/app/src/components/InAppNotification/PageNotification/PageModelNotification.tsx

@@ -2,8 +2,8 @@ import React, {
   forwardRef, ForwardRefRenderFunction, useImperativeHandle,
   forwardRef, ForwardRefRenderFunction, useImperativeHandle,
 } from 'react';
 } from 'react';
 
 
-import { HasObjectId } from '@growi/core';
-import { PagePathLabel } from '@growi/ui/dist/components/PagePath/PagePathLabel';
+import type { HasObjectId } from '@growi/core';
+import { PagePathLabel } from '@growi/ui/dist/components/PagePath';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
 
 
 import type { IInAppNotificationOpenable } from '~/client/interfaces/in-app-notification-openable';
 import type { IInAppNotificationOpenable } from '~/client/interfaces/in-app-notification-openable';

+ 1 - 1
apps/app/src/components/InAppNotification/PageNotification/UserModelNotification.tsx

@@ -2,7 +2,7 @@ import React, {
   forwardRef, ForwardRefRenderFunction, useImperativeHandle,
   forwardRef, ForwardRefRenderFunction, useImperativeHandle,
 } from 'react';
 } from 'react';
 
 
-import { HasObjectId } from '@growi/core';
+import type { HasObjectId } from '@growi/core';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
 
 
 import type { IInAppNotificationOpenable } from '~/client/interfaces/in-app-notification-openable';
 import type { IInAppNotificationOpenable } from '~/client/interfaces/in-app-notification-openable';

+ 1 - 1
apps/app/src/components/LikeButtons.tsx

@@ -1,9 +1,9 @@
 import React, { FC, useState, useCallback } from 'react';
 import React, { FC, useState, useCallback } from 'react';
 
 
+import type { IUser } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import { UncontrolledTooltip, Popover, PopoverBody } from 'reactstrap';
 import { UncontrolledTooltip, Popover, PopoverBody } from 'reactstrap';
 
 
-import { IUser } from '../interfaces/user';
 
 
 import UserPictureList from './User/UserPictureList';
 import UserPictureList from './User/UserPictureList';
 
 

+ 1 - 1
apps/app/src/components/LoginForm.tsx

@@ -18,7 +18,6 @@ import { CompleteUserRegistration } from './CompleteUserRegistration';
 
 
 import styles from './LoginForm.module.scss';
 import styles from './LoginForm.module.scss';
 
 
-
 type LoginFormProps = {
 type LoginFormProps = {
   username?: string,
   username?: string,
   name?: string,
   name?: string,
@@ -335,6 +334,7 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
       resetRegisterErrors();
       resetRegisterErrors();
 
 
       const { redirectTo } = res.data;
       const { redirectTo } = res.data;
+
       if (redirectTo != null) {
       if (redirectTo != null) {
         router.push(redirectTo);
         router.push(redirectTo);
       }
       }

+ 3 - 4
apps/app/src/components/Navbar/AuthorInfo.tsx

@@ -1,8 +1,8 @@
 import React from 'react';
 import React from 'react';
 
 
-import { pagePathUtils } from '@growi/core';
 import type { IUser } from '@growi/core';
 import type { IUser } from '@growi/core';
-import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
+import { pagePathUtils } from '@growi/core/dist/utils';
+import { UserPicture } from '@growi/ui/dist/components';
 import { format } from 'date-fns';
 import { format } from 'date-fns';
 import Link from 'next/link';
 import Link from 'next/link';
 
 
@@ -18,7 +18,6 @@ export const AuthorInfo = (props: AuthorInfoProps): JSX.Element => {
     date, user, mode = 'create', locate = 'subnav',
     date, user, mode = 'create', locate = 'subnav',
   } = props;
   } = props;
 
 
-  const { userPageRoot } = pagePathUtils;
   const formatType = 'yyyy/MM/dd HH:mm';
   const formatType = 'yyyy/MM/dd HH:mm';
 
 
   const infoLabelForSubNav = mode === 'create'
   const infoLabelForSubNav = mode === 'create'
@@ -32,7 +31,7 @@ export const AuthorInfo = (props: AuthorInfoProps): JSX.Element => {
     : 'Last revision posted at';
     : 'Last revision posted at';
   const userLabel = user != null
   const userLabel = user != null
     ? (
     ? (
-      <Link href={userPageRoot(user)} prefetch={false}>
+      <Link href={pagePathUtils.userHomepagePath(user)} prefetch={false}>
         {user.name}
         {user.name}
       </Link>
       </Link>
     )
     )

+ 1 - 1
apps/app/src/components/Navbar/GlobalSearch.tsx

@@ -4,7 +4,7 @@ import React, {
 
 
 import assert from 'assert';
 import assert from 'assert';
 
 
-import { pathUtils } from '@growi/core';
+import { pathUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
 
 

+ 6 - 8
apps/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -1,8 +1,11 @@
 import React, { useState, useEffect, useCallback } from 'react';
 import React, { useState, useEffect, useCallback } from 'react';
 
 
-import {
-  isPopulated, IUser, pagePathUtils, IPagePopulatedToShowRevision,
+import { isPopulated } from '@growi/core';
+import type {
+  IUser, IPagePopulatedToShowRevision,
+  IPageToRenameWithMeta, IPageWithMeta, IPageInfoForEntity,
 } from '@growi/core';
 } from '@growi/core';
+import { pagePathUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
@@ -11,9 +14,6 @@ import { DropdownItem } from 'reactstrap';
 import { exportAsMarkdown, updateContentWidth, useUpdateStateAfterSave } from '~/client/services/page-operation';
 import { exportAsMarkdown, updateContentWidth, useUpdateStateAfterSave } from '~/client/services/page-operation';
 import { apiPost } from '~/client/util/apiv1-client';
 import { apiPost } from '~/client/util/apiv1-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
-import {
-  IPageToRenameWithMeta, IPageWithMeta, IPageInfoForEntity,
-} from '~/interfaces/page';
 import { OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction } from '~/interfaces/ui';
 import { OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction } from '~/interfaces/ui';
 import {
 import {
   useCurrentPathname,
   useCurrentPathname,
@@ -48,8 +48,6 @@ import type { SubNavButtonsProps } from './SubNavButtons';
 import AuthorInfoStyles from './AuthorInfo.module.scss';
 import AuthorInfoStyles from './AuthorInfo.module.scss';
 import PageEditorModeManagerStyles from './PageEditorModeManager.module.scss';
 import PageEditorModeManagerStyles from './PageEditorModeManager.module.scss';
 
 
-const { isUsersHomePage } = pagePathUtils;
-
 const AuthorInfoSkeleton = () => <Skeleton additionalClass={`${AuthorInfoStyles['grw-author-info-skeleton']} py-1`} />;
 const AuthorInfoSkeleton = () => <Skeleton additionalClass={`${AuthorInfoStyles['grw-author-info-skeleton']} py-1`} />;
 
 
 
 
@@ -397,7 +395,7 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
               />
               />
             )}
             )}
           </div>
           </div>
-          {(isAbleToShowPageAuthors && !isCompactMode && !isUsersHomePage(path ?? '')) && (
+          {(isAbleToShowPageAuthors && !isCompactMode && !pagePathUtils.isUsersHomepage(path ?? '')) && (
             <ul className={`${AuthorInfoStyles['grw-author-info']} text-nowrap border-left d-none d-lg-block d-edit-none py-2 pl-4 mb-0 ml-3`}>
             <ul className={`${AuthorInfoStyles['grw-author-info']} text-nowrap border-left d-none d-lg-block d-edit-none py-2 pl-4 mb-0 ml-3`}>
               <li className="pb-1">
               <li className="pb-1">
                 {currentPage != null
                 {currentPage != null

+ 3 - 2
apps/app/src/components/Navbar/PersonalDropdown.jsx

@@ -1,6 +1,7 @@
 import { useRef, useState } from 'react';
 import { useRef, useState } from 'react';
 
 
-import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
+import { pagePathUtils } from '@growi/core/dist/utils';
+import { UserPicture } from '@growi/ui/dist/components';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 import Link from 'next/link';
 import Link from 'next/link';
@@ -64,7 +65,7 @@ const PersonalDropdown = () => {
 
 
           <div className="btn-group btn-block mt-2" role="group">
           <div className="btn-group btn-block mt-2" role="group">
             <Link
             <Link
-              href={`/user/${currentUser.username}`}
+              href={pagePathUtils.userHomepagePath(currentUser)}
               className="btn btn-sm btn-outline-secondary col"
               className="btn btn-sm btn-outline-secondary col"
               data-testid="grw-personal-dropdown-menu-user-home"
               data-testid="grw-personal-dropdown-menu-user-home"
             >
             >

+ 6 - 3
apps/app/src/components/Navbar/SubNavButtons.tsx

@@ -1,5 +1,11 @@
 import React, { useCallback, useMemo } from 'react';
 import React, { useCallback, useMemo } from 'react';
 
 
+import type {
+  IPageInfoForOperation, IPageToDeleteWithMeta, IPageToRenameWithMeta,
+} from '@growi/core';
+import {
+  isIPageInfoForEntity, isIPageInfoForOperation,
+} from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import { DropdownItem } from 'reactstrap';
 import { DropdownItem } from 'reactstrap';
 
 
@@ -7,9 +13,6 @@ 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 {
-  IPageInfoForOperation, IPageToDeleteWithMeta, IPageToRenameWithMeta, isIPageInfoForEntity, isIPageInfoForOperation,
-} from '~/interfaces/page';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/stores/context';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/stores/context';
 import { IPageForPageDuplicateModal } from '~/stores/modal';
 import { IPageForPageDuplicateModal } from '~/stores/modal';
 
 

+ 1 - 1
apps/app/src/components/Page/CopyDropdown.jsx

@@ -2,7 +2,7 @@ import React, {
   useState, useMemo, useCallback,
   useState, useMemo, useCallback,
 } from 'react';
 } from 'react';
 
 
-import { pagePathUtils } from '@growi/core';
+import { pagePathUtils } from '@growi/core/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { CopyToClipboard } from 'react-copy-to-clipboard';
 import { CopyToClipboard } from 'react-copy-to-clipboard';

+ 9 - 12
apps/app/src/components/Page/PageView.tsx

@@ -2,8 +2,8 @@ import React, {
   useEffect, useMemo, useRef, useState,
   useEffect, useMemo, useRef, useState,
 } from 'react';
 } from 'react';
 
 
-
-import { type IPagePopulatedToShowRevision, pagePathUtils } from '@growi/core';
+import type { IPagePopulatedToShowRevision } from '@growi/core';
+import { isUsersHomepage } from '@growi/core/dist/utils/page-path-utils';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 
 
 import type { RendererConfig } from '~/interfaces/services/renderer';
 import type { RendererConfig } from '~/interfaces/services/renderer';
@@ -22,24 +22,21 @@ import { PageAlerts } from '../PageAlert/PageAlerts';
 import { PageContentFooter } from '../PageContentFooter';
 import { PageContentFooter } from '../PageContentFooter';
 import type { PageSideContentsProps } from '../PageSideContents';
 import type { PageSideContentsProps } from '../PageSideContents';
 import { UserInfo } from '../User/UserInfo';
 import { UserInfo } from '../User/UserInfo';
-import type { UsersHomePageFooterProps } from '../UsersHomePageFooter';
+import type { UsersHomepageFooterProps } from '../UsersHomepageFooter';
 
 
 import RevisionRenderer from './RevisionRenderer';
 import RevisionRenderer from './RevisionRenderer';
 
 
 import styles from './PageView.module.scss';
 import styles from './PageView.module.scss';
 
 
 
 
-const { isUsersHomePage } = pagePathUtils;
-
-
 const NotCreatablePage = dynamic(() => import('../NotCreatablePage').then(mod => mod.NotCreatablePage), { ssr: false });
 const NotCreatablePage = dynamic(() => import('../NotCreatablePage').then(mod => mod.NotCreatablePage), { ssr: false });
 const ForbiddenPage = dynamic(() => import('../ForbiddenPage'), { ssr: false });
 const ForbiddenPage = dynamic(() => import('../ForbiddenPage'), { ssr: false });
 const NotFoundPage = dynamic(() => import('../NotFoundPage'), { ssr: false });
 const NotFoundPage = dynamic(() => import('../NotFoundPage'), { ssr: false });
 const PageSideContents = dynamic<PageSideContentsProps>(() => import('../PageSideContents').then(mod => mod.PageSideContents), { ssr: false });
 const PageSideContents = dynamic<PageSideContentsProps>(() => import('../PageSideContents').then(mod => mod.PageSideContents), { ssr: false });
 const PageContentsUtilities = dynamic(() => import('./PageContentsUtilities').then(mod => mod.PageContentsUtilities), { ssr: false });
 const PageContentsUtilities = dynamic(() => import('./PageContentsUtilities').then(mod => mod.PageContentsUtilities), { ssr: false });
 const Comments = dynamic<CommentsProps>(() => import('../Comments').then(mod => mod.Comments), { ssr: false });
 const Comments = dynamic<CommentsProps>(() => import('../Comments').then(mod => mod.Comments), { ssr: false });
-const UsersHomePageFooter = dynamic<UsersHomePageFooterProps>(() => import('../UsersHomePageFooter')
-  .then(mod => mod.UsersHomePageFooter), { ssr: false });
+const UsersHomepageFooter = dynamic<UsersHomepageFooterProps>(() => import('../UsersHomepageFooter')
+  .then(mod => mod.UsersHomepageFooter), { ssr: false });
 const IdenticalPathPage = dynamic(() => import('../IdenticalPathPage').then(mod => mod.IdenticalPathPage), { ssr: false });
 const IdenticalPathPage = dynamic(() => import('../IdenticalPathPage').then(mod => mod.IdenticalPathPage), { ssr: false });
 
 
 
 
@@ -70,7 +67,7 @@ export const PageView = (props: Props): JSX.Element => {
 
 
   const page = pageBySWR ?? initialPage;
   const page = pageBySWR ?? initialPage;
   const isNotFound = isNotFoundMeta || page?.revision == null;
   const isNotFound = isNotFoundMeta || page?.revision == null;
-  const isUsersHomePagePath = isUsersHomePage(pagePath);
+  const isUsersHomepagePath = isUsersHomepage(pagePath);
 
 
 
 
   // ***************************  Auto Scroll  ***************************
   // ***************************  Auto Scroll  ***************************
@@ -118,8 +115,8 @@ export const PageView = (props: Props): JSX.Element => {
             onLoaded={() => setCommentsLoaded(true)}
             onLoaded={() => setCommentsLoaded(true)}
           />
           />
         </div>
         </div>
-        {(isUsersHomePagePath && page.creator != null) && (
-          <UsersHomePageFooter creatorId={page.creator._id} />
+        {(isUsersHomepagePath && page.creator != null) && (
+          <UsersHomepageFooter creatorId={page.creator._id} />
         )}
         )}
         <PageContentFooter page={page} />
         <PageContentFooter page={page} />
       </>
       </>
@@ -152,7 +149,7 @@ export const PageView = (props: Props): JSX.Element => {
       {specialContents}
       {specialContents}
       {specialContents == null && (
       {specialContents == null && (
         <>
         <>
-          {(isUsersHomePagePath && page?.creator != null) && <UserInfo author={page.creator} />}
+          {(isUsersHomepagePath && page?.creator != null) && <UserInfo author={page.creator} />}
           <div className={`mb-5 ${isMobile ? `page-mobile ${styles['page-mobile']}` : ''}`}>
           <div className={`mb-5 ${isMobile ? `page-mobile ${styles['page-mobile']}` : ''}`}>
             <Contents />
             <Contents />
           </div>
           </div>

+ 4 - 11
apps/app/src/components/Page/RevisionLoader.tsx

@@ -20,11 +20,6 @@ export type RevisionLoaderProps = {
 
 
 const logger = loggerFactory('growi:Page:RevisionLoader');
 const logger = loggerFactory('growi:Page:RevisionLoader');
 
 
-// Always render '#revision-loader' for MutationObserver of SearchResultContent
-const RevisionLoaderRoot = (props: React.HTMLAttributes<HTMLDivElement>): JSX.Element => (
-  <div id={ROOT_ELEM_ID} {...props}>{props.children}</div>
-);
-
 /**
 /**
  * Load data from server and render RevisionBody component
  * Load data from server and render RevisionBody component
  */
  */
@@ -76,11 +71,9 @@ export const RevisionLoader = (props: RevisionLoaderProps): JSX.Element => {
   }
   }
 
 
   return (
   return (
-    <RevisionLoaderRoot>
-      <RevisionRenderer
-        rendererOptions={rendererOptions}
-        markdown={markdown}
-      />
-    </RevisionLoaderRoot>
+    <RevisionRenderer
+      rendererOptions={rendererOptions}
+      markdown={markdown}
+    />
   );
   );
 };
 };

+ 1 - 1
apps/app/src/components/PageAccessoriesModal/PageAttachment.tsx

@@ -2,7 +2,7 @@ import React, {
   useCallback, useMemo, useState,
   useCallback, useMemo, useState,
 } from 'react';
 } from 'react';
 
 
-import { IAttachmentHasId } from '@growi/core';
+import type { IAttachmentHasId } from '@growi/core';
 
 
 import { useSWRxAttachments } from '~/stores/attachment';
 import { useSWRxAttachments } from '~/stores/attachment';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/stores/context';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/stores/context';

+ 1 - 2
apps/app/src/components/PageAccessoriesModal/ShareLink/ShareLinkForm.tsx

@@ -1,6 +1,5 @@
 import React, { FC, useState, useCallback } from 'react';
 import React, { FC, useState, useCallback } from 'react';
 
 
-import { isInteger } from 'core-js/fn/number';
 import {
 import {
   format, parse, addDays, set,
   format, parse, addDays, set,
 } from 'date-fns';
 } from 'date-fns';
@@ -63,7 +62,7 @@ export const ShareLinkForm: FC<Props> = (props: Props) => {
     }
     }
 
 
     if (expirationType === ExpirationType.NUMBER_OF_DAYS) {
     if (expirationType === ExpirationType.NUMBER_OF_DAYS) {
-      if (!isInteger(Number(numberOfDays))) {
+      if (!Number.isInteger(numberOfDays)) {
         throw new Error(t('share_links.Invalid_Number_of_Date'));
         throw new Error(t('share_links.Invalid_Number_of_Date'));
       }
       }
       return addDays(new Date(), numberOfDays);
       return addDays(new Date(), numberOfDays);

+ 2 - 1
apps/app/src/components/PageAlert/FixPageGrantAlert.tsx

@@ -1,5 +1,6 @@
 import React, { useEffect, useState, useCallback } from 'react';
 import React, { useEffect, useState, useCallback } from 'react';
 
 
+import { PageGrant } from '@growi/core';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import {
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
   Modal, ModalHeader, ModalBody, ModalFooter,
@@ -7,7 +8,7 @@ import {
 
 
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { toastError, toastSuccess } from '~/client/util/toastr';
-import { PageGrant, IPageGrantData } from '~/interfaces/page';
+import { IPageGrantData } from '~/interfaces/page';
 import { IRecordApplicableGrant, IResIsGrantNormalizedGrantData } from '~/interfaces/page-grant';
 import { IRecordApplicableGrant, IResIsGrantNormalizedGrantData } from '~/interfaces/page-grant';
 import { useCurrentUser } from '~/stores/context';
 import { useCurrentUser } from '~/stores/context';
 import { useSWRxApplicableGrant, useSWRxIsGrantNormalized, useSWRxCurrentPage } from '~/stores/page';
 import { useSWRxApplicableGrant, useSWRxIsGrantNormalized, useSWRxCurrentPage } from '~/stores/page';

+ 1 - 3
apps/app/src/components/PageAlert/OldRevisionAlert.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 
 
-import { pathUtils } from '@growi/core';
+import { returnPathForURL } from '@growi/core/dist/utils/path-utils';
 import Link from 'next/link';
 import Link from 'next/link';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 
 
@@ -12,8 +12,6 @@ export const OldRevisionAlert = (): JSX.Element => {
   const { data: isLatestRevision } = useIsLatestRevision();
   const { data: isLatestRevision } = useIsLatestRevision();
   const { data: page } = useSWRxCurrentPage();
   const { data: page } = useSWRxCurrentPage();
 
 
-  const { returnPathForURL } = pathUtils;
-
   if (page == null || isLatestRevision == null || isLatestRevision) {
   if (page == null || isLatestRevision == null || isLatestRevision) {
     return <></>;
     return <></>;
   }
   }

+ 1 - 1
apps/app/src/components/PageAlert/PageStaleAlert.tsx

@@ -1,6 +1,6 @@
+import { isIPageInfoForEntity } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
-import { isIPageInfoForEntity } from '~/interfaces/page';
 
 
 import { useIsEnabledStaleNotification } from '../../stores/context';
 import { useIsEnabledStaleNotification } from '../../stores/context';
 import { useSWRxCurrentPage, useSWRxPageInfo } from '../../stores/page';
 import { useSWRxCurrentPage, useSWRxPageInfo } from '../../stores/page';

+ 1 - 1
apps/app/src/components/PageAlert/TrashPageAlert.tsx

@@ -1,6 +1,6 @@
 import React, { useCallback } from 'react';
 import React, { useCallback } from 'react';
 
 
-import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
+import { UserPicture } from '@growi/ui/dist/components';
 import { format } from 'date-fns';
 import { format } from 'date-fns';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';

+ 1 - 1
apps/app/src/components/PageAttachment/DeleteAttachmentModal.tsx

@@ -3,7 +3,7 @@ import React, {
 } from 'react';
 } from 'react';
 
 
 import type { IUser } from '@growi/core';
 import type { IUser } from '@growi/core';
-import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
+import { UserPicture } from '@growi/ui/dist/components';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import {
 import {
   Button, Modal, ModalHeader, ModalBody, ModalFooter,
   Button, Modal, ModalHeader, ModalBody, ModalFooter,

+ 2 - 2
apps/app/src/components/PageAttachment/PageAttachmentList.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 
 
-import { IAttachmentHasId } from '@growi/core';
-import { Attachment } from '@growi/ui/dist/components/Attachment';
+import type { IAttachmentHasId } from '@growi/core';
+import { Attachment } from '@growi/ui/dist/components';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
 type Props = {
 type Props = {

+ 7 - 21
apps/app/src/components/PageComment.tsx

@@ -2,8 +2,7 @@ import React, {
   FC, useState, useMemo, memo, useCallback,
   FC, useState, useMemo, memo, useCallback,
 } from 'react';
 } from 'react';
 
 
-import { isPopulated, getIdForRef } from '@growi/core/dist/interfaces/common';
-import { type IRevisionHasId } from '@growi/core/dist/interfaces/revision';
+import { isPopulated, getIdForRef, type IRevisionHasId } from '@growi/core';
 import { Button } from 'reactstrap';
 import { Button } from 'reactstrap';
 
 
 import { apiPost } from '~/client/util/apiv1-client';
 import { apiPost } from '~/client/util/apiv1-client';
@@ -24,13 +23,6 @@ import { ReplyComments } from './PageComment/ReplyComments';
 
 
 import styles from './PageComment.module.scss';
 import styles from './PageComment.module.scss';
 
 
-export const ROOT_ELEM_ID = 'page-comments' as const;
-
-// Always render '#page-comments' for MutationObserver of SearchResultContent
-const PageCommentRoot = (props: React.HTMLAttributes<HTMLDivElement>): JSX.Element => (
-  <div id={ROOT_ELEM_ID} {...props}>{props.children}</div>
-);
-
 
 
 export type PageCommentProps = {
 export type PageCommentProps = {
   rendererOptions?: RendererOptions,
   rendererOptions?: RendererOptions,
@@ -40,14 +32,13 @@ export type PageCommentProps = {
   currentUser: any,
   currentUser: any,
   isReadOnly: boolean,
   isReadOnly: boolean,
   titleAlign?: 'center' | 'left' | 'right',
   titleAlign?: 'center' | 'left' | 'right',
-  hideIfEmpty?: boolean,
 }
 }
 
 
 export const PageComment: FC<PageCommentProps> = memo((props: PageCommentProps): JSX.Element => {
 export const PageComment: FC<PageCommentProps> = memo((props: PageCommentProps): JSX.Element => {
 
 
   const {
   const {
     rendererOptions: rendererOptionsByProps,
     rendererOptions: rendererOptionsByProps,
-    pageId, pagePath, revision, currentUser, isReadOnly, titleAlign, hideIfEmpty,
+    pageId, pagePath, revision, currentUser, isReadOnly, titleAlign,
   } = props;
   } = props;
 
 
   const { data: comments, mutate } = useSWRxPageComment(pageId);
   const { data: comments, mutate } = useSWRxPageComment(pageId);
@@ -117,8 +108,8 @@ export const PageComment: FC<PageCommentProps> = memo((props: PageCommentProps):
     mutatePageInfo();
     mutatePageInfo();
   }, [removeShowEditorId, mutate, mutatePageInfo]);
   }, [removeShowEditorId, mutate, mutatePageInfo]);
 
 
-  if (hideIfEmpty && comments?.length === 0) {
-    return <PageCommentRoot />;
+  if (comments?.length === 0) {
+    return <></>;
   }
   }
 
 
   let commentTitleClasses = 'border-bottom py-3 mb-3';
   let commentTitleClasses = 'border-bottom py-3 mb-3';
@@ -127,12 +118,7 @@ export const PageComment: FC<PageCommentProps> = memo((props: PageCommentProps):
   const rendererOptions = rendererOptionsByProps ?? rendererOptionsForCurrentPage;
   const rendererOptions = rendererOptionsByProps ?? rendererOptionsForCurrentPage;
 
 
   if (commentsFromOldest == null || commentsExceptReply == null || rendererOptions == null) {
   if (commentsFromOldest == null || commentsExceptReply == null || rendererOptions == null) {
-    if (hideIfEmpty) {
-      return <PageCommentRoot />;
-    }
-    return (
-      <></>
-    );
+    return <></>;
   }
   }
 
 
   const revisionId = getIdForRef(revision);
   const revisionId = getIdForRef(revision);
@@ -169,7 +155,7 @@ export const PageComment: FC<PageCommentProps> = memo((props: PageCommentProps):
   );
   );
 
 
   return (
   return (
-    <PageCommentRoot className={`${styles['page-comment-styles']} page-comments-row comment-list`}>
+    <div className={`${styles['page-comment-styles']} page-comments-row comment-list`}>
       <div className="container-lg">
       <div className="container-lg">
         <div className="page-comments">
         <div className="page-comments">
           <h2 className={commentTitleClasses}><i className="icon-fw icon-bubbles"></i>Comments</h2>
           <h2 className={commentTitleClasses}><i className="icon-fw icon-bubbles"></i>Comments</h2>
@@ -231,7 +217,7 @@ export const PageComment: FC<PageCommentProps> = memo((props: PageCommentProps):
           confirmToDelete={onDeleteComment}
           confirmToDelete={onDeleteComment}
         />
         />
       )}
       )}
-    </PageCommentRoot>
+    </div>
   );
   );
 });
 });
 
 

+ 1 - 1
apps/app/src/components/PageComment/Comment.tsx

@@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react';
 
 
 import type { IUser } from '@growi/core';
 import type { IUser } from '@growi/core';
 import * as pathUtils from '@growi/core/dist/utils/path-utils';
 import * as pathUtils from '@growi/core/dist/utils/path-utils';
-import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
+import { UserPicture } from '@growi/ui/dist/components';
 import { format, parseISO } from 'date-fns';
 import { format, parseISO } from 'date-fns';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import Link from 'next/link';
 import Link from 'next/link';

Некоторые файлы не были показаны из-за большого количества измененных файлов