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

Merge branch 'master' into feat/isolate-hackmd-from-app

yuken 3 лет назад
Родитель
Сommit
c222e391cb
100 измененных файлов с 571 добавлено и 384 удалено
  1. 33 11
      THIRD-PARTY-NOTICES.md
  2. BIN
      packages/app/public/static/fonts/Lato-Bold-latin-ext.woff2
  3. BIN
      packages/app/public/static/fonts/Lato-Bold-latin.woff2
  4. BIN
      packages/app/public/static/fonts/Lato-Regular-latin-ext.woff2
  5. BIN
      packages/app/public/static/fonts/Lato-Regular-latin.woff2
  6. BIN
      packages/app/public/static/fonts/PressStart2P-latin-ext.woff2
  7. BIN
      packages/app/public/static/fonts/PressStart2P-latin.woff2
  8. 0 2
      packages/app/public/static/locales/en_US/admin.json
  9. 6 0
      packages/app/public/static/locales/en_US/commons.json
  10. 0 2
      packages/app/public/static/locales/ja_JP/admin.json
  11. 6 0
      packages/app/public/static/locales/ja_JP/commons.json
  12. 0 2
      packages/app/public/static/locales/zh_CN/admin.json
  13. 6 0
      packages/app/public/static/locales/zh_CN/commons.json
  14. 0 1
      packages/app/public/static/locales/zh_CN/translation.json
  15. 2 2
      packages/app/resource/locales/en_US/welcome.md
  16. 2 2
      packages/app/resource/locales/ja_JP/welcome.md
  17. 2 2
      packages/app/resource/locales/zh_CN/welcome.md
  18. 2 2
      packages/app/src/components/Admin/App/AppSetting.jsx
  19. 1 1
      packages/app/src/components/Admin/App/AppSettingsPageContents.tsx
  20. 2 2
      packages/app/src/components/Admin/App/FileUploadSetting.tsx
  21. 2 2
      packages/app/src/components/Admin/App/MailSetting.tsx
  22. 1 1
      packages/app/src/components/Admin/App/PluginSetting.tsx
  23. 1 1
      packages/app/src/components/Admin/App/SiteUrlSetting.tsx
  24. 1 1
      packages/app/src/components/Admin/Common/AdminNavigation.jsx
  25. 1 1
      packages/app/src/components/Admin/Customize/CustomizeCssSetting.tsx
  26. 1 1
      packages/app/src/components/Admin/Customize/CustomizeFunctionSetting.tsx
  27. 1 1
      packages/app/src/components/Admin/Customize/CustomizeHeaderSetting.tsx
  28. 1 1
      packages/app/src/components/Admin/Customize/CustomizeLayoutSetting.tsx
  29. 3 3
      packages/app/src/components/Admin/Customize/CustomizeLogoSetting.tsx
  30. 1 1
      packages/app/src/components/Admin/Customize/CustomizeScriptSetting.tsx
  31. 3 2
      packages/app/src/components/Admin/Customize/CustomizeSidebarSetting.tsx
  32. 1 1
      packages/app/src/components/Admin/Customize/CustomizeThemeSetting.tsx
  33. 1 1
      packages/app/src/components/Admin/Customize/CustomizeTitle.tsx
  34. 44 43
      packages/app/src/components/Admin/ImportData/GrowiArchive/ImportForm.jsx
  35. 3 4
      packages/app/src/components/Admin/ImportData/GrowiArchiveSection.jsx
  36. 1 1
      packages/app/src/components/Admin/MarkdownSetting/IndentForm.tsx
  37. 1 1
      packages/app/src/components/Admin/MarkdownSetting/LineBreakForm.jsx
  38. 1 1
      packages/app/src/components/Admin/MarkdownSetting/PresentationForm.jsx
  39. 1 1
      packages/app/src/components/Admin/MarkdownSetting/XssForm.jsx
  40. 1 1
      packages/app/src/components/Admin/Notification/GlobalNotification.jsx
  41. 1 1
      packages/app/src/components/Admin/Security/GitHubSecuritySettingContents.jsx
  42. 1 1
      packages/app/src/components/Admin/Security/GoogleSecuritySettingContents.jsx
  43. 2 2
      packages/app/src/components/Admin/Security/OidcSecuritySettingContents.jsx
  44. 1 1
      packages/app/src/components/Admin/Security/SamlSecuritySettingContents.jsx
  45. 1 1
      packages/app/src/components/Admin/Security/SecuritySetting.jsx
  46. 1 1
      packages/app/src/components/Admin/Security/TwitterSecuritySettingContents.jsx
  47. 2 2
      packages/app/src/components/Admin/SlackIntegration/CustomBotWithProxySettings.jsx
  48. 1 1
      packages/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxySecretTokenSection.jsx
  49. 2 2
      packages/app/src/components/Admin/SlackIntegration/ManageCommandsProcess.jsx
  50. 2 2
      packages/app/src/components/Admin/SlackIntegration/ManageCommandsProcessWithoutProxy.jsx
  51. 1 1
      packages/app/src/components/Admin/SlackIntegration/OfficialBotSettings.jsx
  52. 1 1
      packages/app/src/components/Admin/SlackIntegration/WithProxyAccordions.jsx
  53. 2 2
      packages/app/src/components/Admin/UserGroup/UserGroupPage.tsx
  54. 5 5
      packages/app/src/components/Admin/UserGroupDetail/UserGroupDetailPage.tsx
  55. 38 0
      packages/app/src/components/AlertSiteUrlUndefined.tsx
  56. 33 12
      packages/app/src/components/Common/ImageCropModal.tsx
  57. 1 1
      packages/app/src/components/CustomNavigation/CustomNav.jsx
  58. 1 1
      packages/app/src/components/Hotkeys/HotkeysDetector.jsx
  59. 3 4
      packages/app/src/components/Hotkeys/HotkeysManager.jsx
  60. 2 0
      packages/app/src/components/Layout/BasicLayout.tsx
  61. 1 1
      packages/app/src/components/Me/ApiSettings.tsx
  62. 1 1
      packages/app/src/components/Me/BasicInfoSettings.tsx
  63. 1 1
      packages/app/src/components/Me/EditorSettings.tsx
  64. 1 1
      packages/app/src/components/Me/InAppNotificationSettings.tsx
  65. 2 2
      packages/app/src/components/Me/PasswordSettings.jsx
  66. 3 3
      packages/app/src/components/Me/ProfileImageSettings.tsx
  67. 1 1
      packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx
  68. 2 1
      packages/app/src/components/Page/CopyDropdown.jsx
  69. 1 1
      packages/app/src/components/Page/CopyDropdown.module.scss
  70. 22 14
      packages/app/src/components/PageEditor/CodeMirrorEditor.jsx
  71. 1 12
      packages/app/src/components/PageEditor/CodeMirrorEditor.module.scss
  72. 1 1
      packages/app/src/components/PageEditor/Editor.module.scss
  73. 13 13
      packages/app/src/components/PagePathHierarchicalLink.tsx
  74. 2 2
      packages/app/src/components/PasswordResetExecutionForm.tsx
  75. 22 0
      packages/app/src/components/RevisionComparer/RevisionComparer.module.scss
  76. 7 0
      packages/app/src/components/Sidebar/CustomSidebar.module.scss
  77. 4 2
      packages/app/src/components/Sidebar/CustomSidebar.tsx
  78. 1 1
      packages/app/src/components/Sidebar/RecentChanges.module.scss
  79. 9 7
      packages/app/src/components/Sidebar/Tag.tsx
  80. 0 143
      packages/app/src/components/StaffCredit/StaffCredit.jsx
  81. 5 4
      packages/app/src/components/StaffCredit/StaffCredit.module.scss
  82. 139 0
      packages/app/src/components/StaffCredit/StaffCredit.tsx
  83. 5 5
      packages/app/src/components/TableOfContents.tsx
  84. 7 0
      packages/app/src/pages/_document.page.tsx
  85. 4 4
      packages/app/src/pages/admin/[[...path]].page.tsx
  86. 1 1
      packages/app/src/pages/forgot-password.page.tsx
  87. 1 1
      packages/app/src/pages/me/[[...path]].page.tsx
  88. 1 1
      packages/app/src/pages/reset-password.page.tsx
  89. 1 1
      packages/app/src/pages/tags.page.tsx
  90. 11 1
      packages/app/src/pages/utils/commons.ts
  91. 3 1
      packages/app/src/server/routes/index.js
  92. 12 0
      packages/app/src/stores/staff.tsx
  93. 0 4
      packages/app/src/styles/_create-page.scss
  94. 1 2
      packages/app/src/styles/_editor.scss
  95. 49 0
      packages/app/src/styles/_fonts.scss
  96. 0 1
      packages/app/src/styles/_layout.scss
  97. 1 2
      packages/app/src/styles/_variables.scss
  98. 0 4
      packages/app/src/styles/bootstrap/_override.scss
  99. 2 2
      packages/app/src/styles/bootstrap/_variables.scss
  100. 0 4
      packages/app/src/styles/organisms/_wiki-custom-sidebar.scss

+ 33 - 11
THIRD-PARTY-NOTICES.md

@@ -13,10 +13,12 @@ https://github.com/weseek/growi.
 
 
 1. Apache License, Version 2.0 Derivative Works
-2. crowi/crowi (https://github.com/crowi/crowi)
-3. Microsoft/vscode (https://github.com/Microsoft/vscode)
-4. stephenhutchings/typicons.font (https://github.com/stephenhutchings/typicons.font)
-5. Kuromoji.js (https://github.com/takuyaa/kuromoji.js)
+1. crowi/crowi (https://github.com/crowi/crowi)
+1. Microsoft/vscode (https://github.com/Microsoft/vscode)
+1. Kuromoji.js (https://github.com/takuyaa/kuromoji.js)
+1. Lato (https://fonts.google.com/specimen/Lato)
+1. Press Start 2P (https://fonts.google.com/specimen/Press+Start+2P)
+1. stephenhutchings/typicons.font (https://github.com/stephenhutchings/typicons.font)
 
 
 License Notice for Apache License, Version 2.0 Derivative Works
@@ -90,21 +92,41 @@ SOFTWARE.
 ```
 
 
-License Notice for Typicons
+License Notice for Kuromoji.js
 ------------------------
 
-https://creativecommons.org/licenses/by-sa/3.0/
+https://github.com/takuyaa/kuromoji.js/blob/master/LICENSE-2.0.txt
 
 ```
-Copyright (c) 2018 Stephen Hutchings
+author: "Takuya Asano <takuya.a@gmail.com>"
 ```
 
 
-License Notice for Kuromoji.js
-------------------------
+License Notice for Lato
+---------------------
 
-https://github.com/takuyaa/kuromoji.js/blob/master/LICENSE-2.0.txt
+https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL
 
 ```
-author: "Takuya Asano <takuya.a@gmail.com>"
+Designed by Łukasz Dziedzic 
 ```
+
+
+License Notice for Press Start 2P
+------------------------------
+
+https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL
+
+```
+Designed by CodeMan38
+```
+
+
+License Notice for Typicons
+------------------------
+
+https://creativecommons.org/licenses/by-sa/3.0/
+
+```
+Copyright (c) 2018 Stephen Hutchings
+```

BIN
packages/app/public/static/fonts/Lato-Bold-latin-ext.woff2


BIN
packages/app/public/static/fonts/Lato-Bold-latin.woff2


BIN
packages/app/public/static/fonts/Lato-Regular-latin-ext.woff2


BIN
packages/app/public/static/fonts/Lato-Regular-latin.woff2


BIN
packages/app/public/static/fonts/PressStart2P-latin-ext.woff2


BIN
packages/app/public/static/fonts/PressStart2P-latin.woff2


+ 0 - 2
packages/app/public/static/locales/en_US/admin.json

@@ -3,7 +3,6 @@
     "display_name": "English"
   },
   "wiki_management_home_page": "Wiki Management Home Page",
-  "app_settings": "App Settings",
   "security_settings": {
     "security_settings": "Security Settings",
     "Guest Users Access": "Guest users access",
@@ -46,7 +45,6 @@
     "page_delete_rights_caution": "The \"Delete / Delete All\" permission (including descendant pages) is forced to be stronger than the \"Delete / Completely Delete\" permission. <br> <br> Admin only > Admin and autor > Anyone",
     "Authentication mechanism settings": "Authentication Mechanism Settings",
     "setup_is_not_yet_complete": "Setup is not yet complete",
-    "alert_siteUrl_is_not_set": "'Site URL' is NOT set. Set it from the {{link}}",
     "xss_prevent_setting": "Prevent XSS(Cross Site Scripting)",
     "xss_prevent_setting_link": "Go to Markdown Settings",
     "callback_URL": "Callback URL",

+ 6 - 0
packages/app/public/static/locales/en_US/commons.json

@@ -7,5 +7,11 @@
     "create_failed": "Failed to create {{target}}",
     "update_successed": "Succeeded to update {{target}}",
     "update_failed": "Failed to update {{target}}"
+  },
+  "alert": {
+    "siteUrl_is_not_set": "'Site URL' is NOT set. Set it from the {{link}}"
+  },
+  "headers": {
+    "app_settings": "App Settings"
   }
 }

+ 0 - 2
packages/app/public/static/locales/ja_JP/admin.json

@@ -12,7 +12,6 @@
   "Description": "説明",
   "last_login": "最終ログイン",
   "wiki_management_home_page": "Wiki管理トップ",
-  "app_settings": "アプリ設定",
   "public": "公開",
   "anyone_with_the_link": "リンクを知っている人のみ",
   "specified_users": "特定ユーザーのみ",
@@ -62,7 +61,6 @@
     "page_delete_rights_caution": "「(子孫ページを含む)ゴミ箱に入れる操作 / 完全に削除する」の権限は、「ゴミ箱に入れる操作 / 完全に削除する」よりも強い権限になるように強制されます。 <br><br> 管理者のみ可能 > 管理者とページ作者が可能 > 誰でも可能",
     "Authentication mechanism settings": "認証機構設定",
     "setup_is_not_yet_complete":"セットアップはまだ完了してません",
-    "alert_siteUrl_is_not_set": "'サイトURL' が設定されていません。{{link}} から設定してください。",
     "xss_prevent_setting": "XSS(Cross Site Scripting)対策設定",
     "xss_prevent_setting_link": "マークダウン設定ページに移動",
     "callback_URL": "コールバックURL",

+ 6 - 0
packages/app/public/static/locales/ja_JP/commons.json

@@ -7,5 +7,11 @@
     "create_failed": "{{target}}の作成に失敗しました",
     "update_successed": "{{target}}を更新しました",
     "update_failed": "{{target}}の更新に失敗しました"
+  },
+  "alert": {
+    "siteUrl_is_not_set": "'サイトURL' が設定されていません。{{link}} から設定してください。"
+  },
+  "headers": {
+    "app_settings": "アプリ設定"
   }
 }

+ 0 - 2
packages/app/public/static/locales/zh_CN/admin.json

@@ -10,7 +10,6 @@
   "Edit": "编辑",
   "Description": "描述",
   "wiki_management_home_page": "Wiki管理首页",
-  "app_settings": "系统设置",
   "public": "公共",
   "anyone_with_the_link": "任何人",
   "specified_users": "仅指定用户",
@@ -60,7 +59,6 @@
     "page_delete_rights_caution": "\"删除/全部删除\"权限(包括后代页面)被强制强于\"删除/完全删除\"权限。 <br> <br> 仅管理员 > 管理员|作者 > 何人",
 		"Authentication mechanism settings": "身份验证机制设置",
 		"setup_is_not_yet_complete": "安装尚未完成",
-		"alert_siteUrl_is_not_set": "主页URL未设置,通过 {{link}} 设置",
 		"xss_prevent_setting": "阻止XSS(跨站点脚本)",
 		"xss_prevent_setting_link": "转到Markdown设置",
 		"callback_URL": "回调URL",

+ 6 - 0
packages/app/public/static/locales/zh_CN/commons.json

@@ -7,5 +7,11 @@
     "create_failed": "Failed to create {{target}}",
     "update_successed": "Succeeded to update {{target}}",
     "update_failed": "Failed to update {{target}}"
+  },
+  "alert": {
+    "siteUrl_is_not_set": "主页URL未设置,通过 {{link}} 设置"
+  },
+  "headers": {
+    "app_settings": "系统设置"
   }
 }

+ 0 - 1
packages/app/public/static/locales/zh_CN/translation.json

@@ -583,7 +583,6 @@
     "popover_title": "Slack Notification",
     "popover_desc": "Input channel name. You can notify multiple channels by entering a comma-separated list."
   },
-  "security_settings": "安全设置",
   "share_links": {
     "Shere this page link to public": "Shere this page link to public",
     "share_link_list": "Share link list",

+ 2 - 2
packages/app/resource/locales/en_US/welcome.md

@@ -18,7 +18,7 @@ Let's increase the information exchange everyday.
 - We can create a bullet point by adding `-`  at the beginning of the line.
 - We can also copy and paste, drag and drop attachments such as images, PDF, Word/Excel/PowerPoint, etc.
 - Once we finished, press the "**Update**" button to publish the page.
-    - We can also save it by `Ctrl(⌘) +S`.
+    - We can also save it by `Ctrl(⌘) + S`.
 
 For more information: [Tutorial#Create New Page](https://docs.growi.org/en/guide/tutorial/create_page.html#create-new-page)
 
@@ -29,7 +29,7 @@ For more information: [Tutorial#Create New Page](https://docs.growi.org/en/guide
   <div class="card-body">
     <ul>
       <li>Ctrl(⌘) + "/" to show quick help.</li>
-      <li>We can write HTML with <a href="https://getbootstrap.com/docs/4.5/components/">Bootstrap 4</a>.</li>
+      <li>We can write HTML with <a href="https://getbootstrap.com/docs/4.6/components/">Bootstrap 4</a>.</li>
     </ul>
   </div>
 </div>

+ 2 - 2
packages/app/resource/locales/ja_JP/welcome.md

@@ -17,7 +17,7 @@ GROWI は個人・法人向けの Wiki | ナレッジベースツールです。
 - `- ` を行頭につけると、この文章のような箇条書きを書くことができます
 - 画像やPDF、Word/Excel/PowerPointなどの添付ファイルも、コピー&ペースト、ドラッグ&ドロップで貼ることができます
 - 書けたら "**更新**" ボタンを押してページを公開しましょう
-    - `Ctrl(⌘) +S` でも保存できます
+    - `Ctrl(⌘) + S` でも保存できます
 
 さらに詳しくはこちら: [チュートリアル#新規ページ作成](https://docs.growi.org/ja/guide/tutorial/create_page.html#新規ページ作成)
 
@@ -25,7 +25,7 @@ GROWI は個人・法人向けの Wiki | ナレッジベースツールです。
   <div class="card-header bg-primary text-light">Tips</div>
   <div class="card-body"><ul>
     <li>Ctrl(⌘) + "/" でショートカットヘルプを表示します</li>
-    <li>HTML/CSS の記述には、<a href="https://getbootstrap.com/docs/4.5/components/">Bootstrap 4</a> を利用できます</li>
+    <li>HTML/CSS の記述には、<a href="https://getbootstrap.com/docs/4.6/components/">Bootstrap 4</a> を利用できます</li>
   </ul></div>
 </div>
 

+ 2 - 2
packages/app/resource/locales/zh_CN/welcome.md

@@ -18,7 +18,7 @@ GROWI是一个针对个人和公司的Wiki - 一个知识库工具。
 - 我们可以通过在行首添加`-`来创建一个要点。
 - 我们还可以复制和粘贴,拖放附件,如图片、PDF、Word/Excel/PowerPoint等。
 - 一旦我们完成了,按 "**更新**"按钮来发布页面。
-    - 我们也可以通过`Ctrl(⌘) +S`来保存。
+    - 我们也可以通过`Ctrl(⌘) + S`来保存。
 
 了解更多信息: [Tutorial#Create New Page](https://docs.growi.org/en/guide/tutorial/create_page.html#create-new-page)
 
@@ -29,7 +29,7 @@ GROWI是一个针对个人和公司的Wiki - 一个知识库工具。
   <div class="card-body">
     <ul>
       <li>Ctrl(⌘) + "/" 显示快速帮助。</li>
-      <li>你可以用 <a href="https://getbootstrap.com/docs/4.5/components/">Bootstrap 4编写HTML</a>.</li>
+      <li>你可以用 <a href="https://getbootstrap.com/docs/4.6/components/">Bootstrap 4编写HTML</a>.</li>
     </ul>
   </div>
 </div>

+ 2 - 2
packages/app/src/components/Admin/App/AppSetting.jsx

@@ -18,12 +18,12 @@ const logger = loggerFactory('growi:appSettings');
 
 const AppSetting = (props) => {
   const { adminAppContainer } = props;
-  const { t } = useTranslation('admin');
+  const { t } = useTranslation(['admin', 'commons']);
 
   const submitHandler = useCallback(async() => {
     try {
       await adminAppContainer.updateAppSettingHandler();
-      toastSuccess(t('toaster.update_successed', { target: t('app_settings') }));
+      toastSuccess(t('toaster.update_successed', { target: t('commons:headers.app_settings') }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/App/AppSettingsPageContents.tsx

@@ -82,7 +82,7 @@ const AppSettingsPageContents = (props: Props) => {
 
       <div className="row">
         <div className="col-lg-12">
-          <h2 className="admin-setting-header">{t('app_settings')}</h2>
+          <h2 className="admin-setting-header">{t('commons:headers.app_settings')}</h2>
           <AppSetting />
         </div>
       </div>

+ 2 - 2
packages/app/src/components/Admin/App/FileUploadSetting.tsx

@@ -18,7 +18,7 @@ type Props = {
 
 
 const FileUploadSetting = (props: Props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation(['admin', 'commons']);
   const { adminAppContainer } = props;
   const { fileUploadType } = adminAppContainer.state;
   const fileUploadTypes = ['aws', 'gcs', 'gridfs', 'local'];
@@ -26,7 +26,7 @@ const FileUploadSetting = (props: Props) => {
   const submitHandler = useCallback(async() => {
     try {
       await adminAppContainer.updateFileUploadSettingHandler();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:app_setting.file_upload_settings') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:app_setting.file_upload_settings'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 2 - 2
packages/app/src/components/Admin/App/MailSetting.tsx

@@ -17,7 +17,7 @@ type Props = {
 
 
 const MailSetting = (props: Props) => {
-  const { t } = useTranslation();
+  const { t } = useTranslation(['admin', 'commons']);
   const { adminAppContainer } = props;
 
   const transmissionMethods = ['smtp', 'ses'];
@@ -25,7 +25,7 @@ const MailSetting = (props: Props) => {
   async function submitHandler() {
     try {
       await adminAppContainer.updateMailSettingHandler();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:app_setting.ses_settings') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:app_setting.ses_settings'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/App/PluginSetting.tsx

@@ -23,7 +23,7 @@ const PluginSetting = (props: Props) => {
   const submitHandler = useCallback(async() => {
     try {
       await adminAppContainer.updatePluginSettingHandler();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:app_setting.plugin_settings') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:app_setting.plugin_settings'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/App/SiteUrlSetting.tsx

@@ -24,7 +24,7 @@ const SiteUrlSetting = (props: Props) => {
   const submitHandler = useCallback(async() => {
     try {
       await adminAppContainer.updateSiteUrlSettingHandler();
-      toastSuccess(t('toaster.update_successed', { target: t('Site URL settings') }));
+      toastSuccess(t('toaster.update_successed', { target: t('Site URL settings'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/Common/AdminNavigation.jsx

@@ -23,7 +23,7 @@ const AdminNavigation = (props) => {
   const MenuLabel = ({ menu }) => {
     switch (menu) {
       /* eslint-disable no-multi-spaces, max-len */
-      case 'app':                      return <><i className="mr-1 icon-fw icon-settings"></i>{        t('app_settings') }</>;
+      case 'app':                      return <><i className="mr-1 icon-fw icon-settings"></i>{        t('commons:headers.app_settings') }</>;
       case 'security':                 return <><i className="mr-1 icon-fw icon-shield"></i>{          t('security_settings.security_settings') }</>;
       case 'markdown':                 return <><i className="mr-1 icon-fw icon-note"></i>{            t('markdown_settings.markdown_settings') }</>;
       case 'customize':                return <><i className="mr-1 icon-fw icon-wrench"></i>{          t('customize_settings.customize_settings') }</>;

+ 1 - 1
packages/app/src/components/Admin/Customize/CustomizeCssSetting.tsx

@@ -21,7 +21,7 @@ const CustomizeCssSetting = (props: Props): JSX.Element => {
   const onClickSubmit = useCallback(async() => {
     try {
       await adminCustomizeContainer.updateCustomizeCss();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.custom_css') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.custom_css'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/Customize/CustomizeFunctionSetting.tsx

@@ -25,7 +25,7 @@ const CustomizeFunctionSetting = (props: Props): JSX.Element => {
 
     try {
       await adminCustomizeContainer.updateCustomizeFunction();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.function') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.function'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/Customize/CustomizeHeaderSetting.tsx

@@ -21,7 +21,7 @@ const CustomizeHeaderSetting = (props: Props): JSX.Element => {
   const onClickSubmit = useCallback(async() => {
     try {
       await adminCustomizeContainer.updateCustomizeHeader();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.custom_header') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.custom_header'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/Customize/CustomizeLayoutSetting.tsx

@@ -19,7 +19,7 @@ const CustomizeLayoutSetting = (): JSX.Element => {
   const onClickSubmit = async() => {
     try {
       await apiv3Put('/customize-setting/layout', { isContainerFluid });
-      toastSuccess(t('toaster.update_successed', { target: t('customize_settings.layout') }));
+      toastSuccess(t('toaster.update_successed', { target: t('customize_settings.layout'), ns: 'commons' }));
       mutateLayoutSetting();
     }
     catch (err) {

+ 3 - 3
packages/app/src/components/Admin/Customize/CustomizeLogoSetting.tsx

@@ -59,7 +59,7 @@ const CustomizeLogoSetting = (): JSX.Element => {
       const { customizedParams } = response.data;
       setIsDefaultLogo(customizedParams.isDefaultLogo);
       setCustomizedLogoSrc(customizedParams.customizedLogoSrc);
-      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.custom_logo') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.custom_logo'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);
@@ -70,7 +70,7 @@ const CustomizeLogoSetting = (): JSX.Element => {
     try {
       await apiv3Delete('/customize-setting/delete-brand-logo');
       setCustomizedLogoSrc(null);
-      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.current_logo') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.current_logo'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);
@@ -86,7 +86,7 @@ const CustomizeLogoSetting = (): JSX.Element => {
       formData.append('file', croppedImage);
       const { data } = await apiv3PostForm('/customize-setting/upload-brand-logo', formData);
       setCustomizedLogoSrc(data.attachment.filePathProxied);
-      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.current_logo') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.current_logo'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/Customize/CustomizeScriptSetting.tsx

@@ -21,7 +21,7 @@ const CustomizeScriptSetting = (props: Props): JSX.Element => {
   const onClickSubmit = useCallback(async() => {
     try {
       await adminCustomizeContainer.updateCustomizeScript();
-      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.custom_script') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.custom_script'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 3 - 2
packages/app/src/components/Admin/Customize/CustomizeSidebarSetting.tsx

@@ -8,7 +8,8 @@ import { useSWRxSidebarConfig } from '~/stores/ui';
 import { useNextThemes } from '~/stores/use-next-themes';
 
 const CustomizeSidebarsetting = (): JSX.Element => {
-  const { t } = useTranslation('admin');
+  const { t } = useTranslation(['admin', 'commons']);
+
   const {
     update, isSidebarDrawerMode, isSidebarClosedAtDockMode, setIsSidebarDrawerMode, setIsSidebarClosedAtDockMode,
   } = useSWRxSidebarConfig();
@@ -20,7 +21,7 @@ const CustomizeSidebarsetting = (): JSX.Element => {
   const onClickSubmit = useCallback(async() => {
     try {
       await update();
-      toastSuccess(t('toaster.update_successed', { target: t('customize_settings.default_sidebar_mode.title') }));
+      toastSuccess(t('toaster.update_successed', { target: t('customize_settings.default_sidebar_mode.title'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/Customize/CustomizeThemeSetting.tsx

@@ -35,7 +35,7 @@ const CustomizeThemeSetting = (props: Props): JSX.Element => {
         });
       }
 
-      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.theme') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.theme'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/Customize/CustomizeTitle.tsx

@@ -22,7 +22,7 @@ export const CustomizeTitle: FC = () => {
       await apiv3Put('/customize-setting/customize-title', {
         customizeTitle: currentCustomizeTitle,
       });
-      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.custom_title') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:customize_settings.custom_title'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 44 - 43
packages/app/src/components/Admin/ImportData/GrowiArchive/ImportForm.jsx

@@ -3,14 +3,12 @@ import React from 'react';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 
-import AdminSocketIoContainer from '~/client/services/AdminSocketIoContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { apiv3Post } from '~/client/util/apiv3-client';
 import GrowiArchiveImportOption from '~/models/admin/growi-archive-import-option';
 import ImportOptionForPages from '~/models/admin/import-option-for-pages';
 import ImportOptionForRevisions from '~/models/admin/import-option-for-revisions';
-
-import { withUnstatedContainers } from '../../../UnstatedUtils';
+import { useAdminSocket } from '~/stores/socket-io';
 
 
 import ErrorViewer from './ErrorViewer';
@@ -103,50 +101,56 @@ class ImportForm extends React.Component {
   }
 
   setupWebsocketEventHandler() {
-    const socket = this.props.adminSocketIoContainer.getSocket();
-
-    // websocket event
-    // eslint-disable-next-line object-curly-newline
-    socket.on('admin:onProgressForImport', ({ collectionName, collectionProgress, appendedErrors }) => {
-      const { progressMap, errorsMap } = this.state;
-      progressMap[collectionName] = collectionProgress;
+    const { socket } = this.props;
+
+    if (socket != null) {
+      // websocket event
+      // eslint-disable-next-line object-curly-newline
+      socket.on('admin:onProgressForImport', ({ collectionName, collectionProgress, appendedErrors }) => {
+        const { progressMap, errorsMap } = this.state;
+        progressMap[collectionName] = collectionProgress;
+
+        const errors = errorsMap[collectionName] || [];
+        errorsMap[collectionName] = errors.concat(appendedErrors);
+
+        this.setState({
+          isImporting: true,
+          progressMap,
+          errorsMap,
+        });
+      });
 
-      const errors = errorsMap[collectionName] || [];
-      errorsMap[collectionName] = errors.concat(appendedErrors);
+      // websocket event
+      socket.on('admin:onTerminateForImport', () => {
+        this.setState({
+          isImporting: false,
+          isImported: true,
+        });
 
-      this.setState({
-        isImporting: true,
-        progressMap,
-        errorsMap,
+        toastSuccess(undefined, 'Import process has completed.');
       });
-    });
 
-    // websocket event
-    socket.on('admin:onTerminateForImport', () => {
-      this.setState({
-        isImporting: false,
-        isImported: true,
-      });
+      // websocket event
+      socket.on('admin:onErrorForImport', (err) => {
+        this.setState({
+          isImporting: false,
+          isImported: false,
+        });
 
-      toastSuccess(undefined, 'Import process has completed.');
-    });
-
-    // websocket event
-    socket.on('admin:onErrorForImport', (err) => {
-      this.setState({
-        isImporting: false,
-        isImported: false,
+        toastError(err, 'Import process has failed.');
       });
 
-      toastError(err, 'Import process has failed.');
-    });
+    }
+
   }
 
   teardownWebsocketEventHandler() {
-    const socket = this.props.adminSocketIoContainer.getSocket();
+    const { socket } = this.props;
 
-    socket.removeAllListeners('admin:onProgressForImport');
-    socket.removeAllListeners('admin:onTerminateForImport');
+    if (socket != null) {
+      socket.removeAllListeners('admin:onProgressForImport');
+      socket.removeAllListeners('admin:onTerminateForImport');
+    }
   }
 
   async toggleCheckbox(collectionName, bool) {
@@ -496,7 +500,7 @@ class ImportForm extends React.Component {
 
 ImportForm.propTypes = {
   t: PropTypes.func.isRequired, // i18next
-  adminSocketIoContainer: PropTypes.instanceOf(AdminSocketIoContainer).isRequired,
+  socket: PropTypes.object,
 
   fileName: PropTypes.string,
   innerFileStats: PropTypes.arrayOf(PropTypes.object).isRequired,
@@ -506,13 +510,10 @@ ImportForm.propTypes = {
 
 const ImportFormWrapperFc = (props) => {
   const { t } = useTranslation('admin');
+  const { data: socket } = useAdminSocket();
 
-  return <ImportForm t={t} {...props} />;
+  return <ImportForm t={t} socket={socket} {...props} />;
 };
 
-/**
- * Wrapper component for using unstated
- */
-const ImportFormWrapper = withUnstatedContainers(ImportFormWrapperFc, [AdminSocketIoContainer]);
 
-export default ImportFormWrapper;
+export default ImportFormWrapperFc;

+ 3 - 4
packages/app/src/components/Admin/ImportData/GrowiArchiveSection.jsx

@@ -8,7 +8,7 @@ import { apiv3Delete, apiv3Get } from '~/client/util/apiv3-client';
 
 // import { toastSuccess, toastError } from '~/client/util/apiNotification';
 
-// import ImportForm from './GrowiArchive/ImportForm';
+import ImportForm from './GrowiArchive/ImportForm';
 import UploadForm from './GrowiArchive/UploadForm';
 
 class GrowiArchiveSection extends React.Component {
@@ -129,12 +129,11 @@ class GrowiArchiveSection extends React.Component {
         {isTheSameVersion === false && this.renderDefferentVersionAlert()}
         {this.state.fileName != null && isTheSameVersion === true ? (
           <div className="px-4">
-            {/* show ImportForm by https://redmine.weseek.co.jp/issues/100061 */}
-            {/* <ImportForm
+            <ImportForm
               fileName={this.state.fileName}
               innerFileStats={this.state.innerFileStats}
               onDiscard={this.discardData}
-            /> */}
+            />
           </div>
         )
           : (

+ 1 - 1
packages/app/src/components/Admin/MarkdownSetting/IndentForm.tsx

@@ -26,7 +26,7 @@ const IndentForm = (props: Props) => {
   const onClickSubmit = useCallback(async(props) => {
     try {
       await props.adminMarkDownContainer.updateIndentSetting();
-      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.indent_header') }));
+      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.indent_header'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/MarkdownSetting/LineBreakForm.jsx

@@ -27,7 +27,7 @@ class LineBreakForm extends React.Component {
 
     try {
       await this.props.adminMarkDownContainer.updateLineBreakSetting();
-      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.lineBreak_header') }));
+      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.lineBreak_header'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/MarkdownSetting/PresentationForm.jsx

@@ -25,7 +25,7 @@ class PresentationForm extends React.Component {
 
     try {
       await this.props.adminMarkDownContainer.updatePresentationSetting();
-      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.presentation_header') }));
+      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.presentation_header'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/MarkdownSetting/XssForm.jsx

@@ -28,7 +28,7 @@ class XssForm extends React.Component {
 
     try {
       await this.props.adminMarkDownContainer.updateXssSetting();
-      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.xss_header') }));
+      toastSuccess(t('toaster.update_successed', { target: t('markdown_settings.xss_header'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/Notification/GlobalNotification.jsx

@@ -22,7 +22,7 @@ const GlobalNotification = (props) => {
   const onClickSubmit = useCallback(async() => {
     try {
       await adminNotificationContainer.updateGlobalNotificationForPages();
-      toastSuccess(t('toaster.update_successed', { target: t('external_notification.external_notification') }));
+      toastSuccess(t('toaster.update_successed', { target: t('external_notification.external_notification'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

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

@@ -90,7 +90,7 @@ class GitHubSecurityManagementContents extends React.Component {
                 <i
                   className="icon-exclamation"
                   // eslint-disable-next-line max-len
-                  dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('app_settings')}<i class="icon-login"></i></a>` }) }}
+                  dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('commons:headers.app_settings')}<i class="icon-login"></i></a>` }) }}
                 />
               </div>
             )}

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

@@ -88,7 +88,7 @@ class GoogleSecurityManagementContents extends React.Component {
                 <i
                   className="icon-exclamation"
                   // eslint-disable-next-line max-len
-                  dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('app_settings')}<i class="icon-login"></i></a>` }) }}
+                  dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('commons:headers.app_settings')}<i class="icon-login"></i></a>` }) }}
                 />
               </div>
             )}

+ 2 - 2
packages/app/src/components/Admin/Security/OidcSecuritySettingContents.jsx

@@ -82,7 +82,7 @@ class OidcSecurityManagementContents extends React.Component {
                 <i
                   className="icon-exclamation"
                   // eslint-disable-next-line max-len
-                  dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('app_settings')}<i class="icon-login"></i></a>` }) }}
+                  dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('commons:headers.app_settings')}<i class="icon-login"></i></a>` }) }}
                 />
               </div>
             )}
@@ -378,7 +378,7 @@ class OidcSecurityManagementContents extends React.Component {
                     <i
                       className="icon-exclamation"
                       // eslint-disable-next-line max-len
-                      dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('app_settings')}<i class="icon-login"></i></a>` }) }}
+                      dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('commons:headers.app_settings')}<i class="icon-login"></i></a>` }) }}
                     />
                   </div>
                 )}

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

@@ -99,7 +99,7 @@ class SamlSecurityManagementContents extends React.Component {
                 <i
                   className="icon-exclamation"
                   // eslint-disable-next-line max-len
-                  dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('app_settings')}<i class="icon-login"></i></a>` }) }}
+                  dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('commons:headers.app_settings')}<i class="icon-login"></i></a>` }) }}
                 />
               </div>
             )}

+ 1 - 1
packages/app/src/components/Admin/Security/SecuritySetting.jsx

@@ -453,7 +453,7 @@ class SecuritySetting extends React.Component {
           ].map(arr => this.renderPageDeletePermission(arr[0], arr[1], arr[2], arr[3]))
         }
 
-        <h4>{t('security_settings.session')}aa</h4>
+        <h4>{t('security_settings.session')}</h4>
         <div className="form-group row">
           <label className="text-left text-md-right col-md-3 col-form-label">{t('security_settings.max_age')}</label>
           <div className="col-md-6">

+ 1 - 1
packages/app/src/components/Admin/Security/TwitterSecuritySettingContents.jsx

@@ -90,7 +90,7 @@ class TwitterSecuritySettingContents extends React.Component {
                 <i
                   className="icon-exclamation"
                   // eslint-disable-next-line max-len
-                  dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('app_settings')}<i class="icon-login"></i></a>` }) }}
+                  dangerouslySetInnerHTML={{ __html: t('security_settings.alert_siteUrl_is_not_set', { link: `<a href="/admin/app">${t('commons:headers.app_settings')}<i class="icon-login"></i></a>` }) }}
                 />
               </div>
             )}

+ 2 - 2
packages/app/src/components/Admin/SlackIntegration/CustomBotWithProxySettings.jsx

@@ -50,7 +50,7 @@ const CustomBotWithProxySettings = (props) => {
       if (onPrimaryUpdated != null) {
         onPrimaryUpdated();
       }
-      toastSuccess(t('toaster.update_successed', { target: 'Primary' }));
+      toastSuccess(t('toaster.update_successed', { target: 'Primary', ns: 'commons' }));
     }
     catch (err) {
       toastError(err, 'Failed to change isPrimary');
@@ -77,7 +77,7 @@ const CustomBotWithProxySettings = (props) => {
       await apiv3Put('/slack-integration-settings/proxy-uri', {
         proxyUri: newProxyServerUri,
       });
-      toastSuccess(t('toaster.update_successed', { target: 'Proxy URL' }));
+      toastSuccess(t('toaster.update_successed', { target: 'Proxy URL', ns: 'commons' }));
     }
     catch (err) {
       toastError(err, 'Failed to update');

+ 1 - 1
packages/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxySecretTokenSection.jsx

@@ -38,7 +38,7 @@ const CustomBotWithoutProxySecretTokenSection = (props) => {
         onUpdatedSecretToken(inputSigningSecret, inputBotToken);
       }
 
-      toastSuccess(t('toaster.update_successed', { target: t('admin:slack_integration.custom_bot_without_proxy_settings') }));
+      toastSuccess(t('toaster.update_successed', { target: t('admin:slack_integration.custom_bot_without_proxy_settings'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 2 - 2
packages/app/src/components/Admin/SlackIntegration/ManageCommandsProcess.jsx

@@ -1,8 +1,8 @@
 import React, { useCallback, useState } from 'react';
 
 import { defaultSupportedCommandsNameForBroadcastUse, defaultSupportedCommandsNameForSingleUse, defaultSupportedSlackEventActions } from '@growi/slack';
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 
 import { apiv3Put } from '~/client/util/apiv3-client';
 import loggerFactory from '~/utils/logger';
@@ -265,7 +265,7 @@ const ManageCommandsProcess = ({
         permissionsForSingleUseCommands: permissionsForSingleUseCommandsState,
         permissionsForSlackEventActions: permissionsForEventsState,
       });
-      toastSuccess(t('toaster.update_successed', { target: 'Token' }));
+      toastSuccess(t('toaster.update_successed', { target: 'Token', ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 2 - 2
packages/app/src/components/Admin/SlackIntegration/ManageCommandsProcessWithoutProxy.jsx

@@ -1,8 +1,8 @@
 import React, { useCallback, useEffect, useState } from 'react';
 
 import { defaultSupportedCommandsNameForBroadcastUse, defaultSupportedCommandsNameForSingleUse, defaultSupportedSlackEventActions } from '@growi/slack';
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 
 import { apiv3Put } from '~/client/util/apiv3-client';
 import loggerFactory from '~/utils/logger';
@@ -207,7 +207,7 @@ const ManageCommandsProcessWithoutProxy = ({ commandPermission, eventActionsPerm
         commandPermission: editingCommandPermission,
         eventActionsPermission: editingEventActionsPermission,
       });
-      toastSuccess(t('toaster.update_successed', { target: 'the permission for commands' }));
+      toastSuccess(t('toaster.update_successed', { target: 'the permission for commands', ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Admin/SlackIntegration/OfficialBotSettings.jsx

@@ -46,7 +46,7 @@ const OfficialBotSettings = (props) => {
       if (onPrimaryUpdated != null) {
         onPrimaryUpdated();
       }
-      toastSuccess(t('toaster.update_successed', { target: 'Primary' }));
+      toastSuccess(t('toaster.update_successed', { target: 'Primary', ns: 'commons' }));
     }
     catch (err) {
       toastError(err, 'Failed to change isPrimary');

+ 1 - 1
packages/app/src/components/Admin/SlackIntegration/WithProxyAccordions.jsx

@@ -153,7 +153,7 @@ const GeneratingTokensAndRegisteringProxyServiceProcess = withUnstatedContainers
       if (props.onUpdateTokens != null) {
         props.onUpdateTokens();
       }
-      toastSuccess(t('toaster.update_successed', { target: 'Token' }));
+      toastSuccess(t('toaster.update_successed', { target: 'Token', ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 2 - 2
packages/app/src/components/Admin/UserGroup/UserGroupPage.tsx

@@ -93,7 +93,7 @@ export const UserGroupPage: FC = () => {
         description: userGroupData.description,
       });
 
-      toastSuccess(t('toaster.update_successed', { target: t('UserGroup') }));
+      toastSuccess(t('toaster.update_successed', { target: t('UserGroup'), ns: 'commons' }));
 
       // mutate
       await mutateUserGroups();
@@ -112,7 +112,7 @@ export const UserGroupPage: FC = () => {
         description: userGroupData.description,
       });
 
-      toastSuccess(t('toaster.update_successed', { target: t('UserGroup') }));
+      toastSuccess(t('toaster.update_successed', { target: t('UserGroup'), ns: 'commons' }));
 
       // mutate
       await mutateUserGroups();

+ 5 - 5
packages/app/src/components/Admin/UserGroupDetail/UserGroupDetailPage.tsx

@@ -124,10 +124,10 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
     async(targetGroup: IUserGroupHasId, userGroupData: Partial<IUserGroupHasId>, forceUpdateParents: boolean): Promise<void> => {
       try {
         await updateUserGroup(targetGroup, userGroupData, forceUpdateParents);
-        toastSuccess(t('toaster.update_successed', { target: t('UserGroup') }));
+        toastSuccess(t('toaster.update_successed', { target: t('UserGroup'), ns: 'commons' }));
       }
       catch {
-        toastError(t('toaster.update_failed', { target: t('UserGroup') }));
+        toastError(t('toaster.update_failed', { target: t('UserGroup'), ns: 'commons' }));
       }
     },
     [t, updateUserGroup],
@@ -205,7 +205,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
         parentId: userGroupData.parent,
       });
 
-      toastSuccess(t('toaster.update_successed', { target: t('UserGroup') }));
+      toastSuccess(t('toaster.update_successed', { target: t('UserGroup'), ns: 'commons' }));
 
       // mutate
       mutateChildUserGroups();
@@ -244,7 +244,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
         parentId: currentUserGroupId,
       });
 
-      toastSuccess(t('toaster.update_successed', { target: t('UserGroup') }));
+      toastSuccess(t('toaster.update_successed', { target: t('UserGroup'), ns: 'commons' }));
 
       // mutate
       mutateChildUserGroups();
@@ -296,7 +296,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
         parentId: null,
       });
 
-      toastSuccess(t('toaster.update_successed', { target: t('UserGroup') }));
+      toastSuccess(t('toaster.update_successed', { target: t('UserGroup'), ns: 'commons' }));
 
       // mutate
       mutateChildUserGroups();

+ 38 - 0
packages/app/src/components/AlertSiteUrlUndefined.tsx

@@ -0,0 +1,38 @@
+import { useTranslation } from 'next-i18next';
+
+import { useSiteUrl } from '~/stores/context';
+
+const isValidUrl = (str: string): boolean => {
+  try {
+    // eslint-disable-next-line no-new
+    new URL(str);
+    return true;
+  }
+  catch {
+    return false;
+  }
+};
+
+export const AlertSiteUrlUndefined = (): JSX.Element => {
+  const { t } = useTranslation();
+  const { data: siteUrl, error: errorSiteUrl } = useSiteUrl();
+  const isLoadingSiteUrl = siteUrl === undefined && errorSiteUrl === undefined;
+
+  if (isLoadingSiteUrl) {
+    return <></>;
+  }
+
+  if (typeof siteUrl === 'string' && isValidUrl(siteUrl)) {
+    return <></>;
+  }
+
+  return (
+    <div className="alert alert-danger rounded-0 d-edit-none mb-0 px-4 py-2">
+      <i className="icon-exclamation"></i>
+      {
+        t('common:alert.alert_siteUrl_is_not_set', { link: t('commons:headers.app_settings') })
+      } &gt;&gt; <a href="/admin/app">{t('commons:headers.app_settings')}<i className="icon-login"></i></a>
+    </div>
+  );
+};
+AlertSiteUrlUndefined.displayName = 'AlertSiteUrlUndefined';

+ 33 - 12
packages/app/src/components/Common/ImageCropModal.tsx

@@ -49,6 +49,14 @@ const ImageCropModal: FC<Props> = (props: Props) => {
   const { t } = useTranslation();
   const reset = useCallback(() => {
     if (imageRef) {
+      // Some SVG files may not have width and height properties, causing the render size to be 0x0
+      // Force imageRef to have width and height by create temporary image element then set the imageRef width with tempImage width
+      // Set imageRef width & height by natural width / height if image has no dimension
+      if (imageRef.width === 0 || imageRef.height === 0) {
+        imageRef.width = imageRef.naturalWidth;
+        imageRef.height = imageRef.naturalHeight;
+      }
+      // Get size of Image, min value of width and height
       const size = Math.min(imageRef.width, imageRef.height);
       setCropOtions({
         aspect: 1,
@@ -63,6 +71,7 @@ const ImageCropModal: FC<Props> = (props: Props) => {
 
   useEffect(() => {
     document.body.style.position = 'static';
+    setIsCropImage(true);
     reset();
   }, [reset]);
 
@@ -73,18 +82,22 @@ const ImageCropModal: FC<Props> = (props: Props) => {
   };
 
 
-  const onCropChange = (crop) => {
-    setCropOtions(crop);
-  };
+  const getCroppedImg = async(image: HTMLImageElement, crop: ICropOptions) => {
+    const {
+      naturalWidth: imageNaturalWidth, naturalHeight: imageNaturalHeight, width: imageWidth, height: imageHeight,
+    } = image;
+
+    const {
+      width: cropWidth, height: cropHeight, x, y,
+    } = crop;
 
-  const getCroppedImg = async(image, crop) => {
     const canvas = document.createElement('canvas');
-    const scaleX = image.naturalWidth / image.width;
-    const scaleY = image.naturalHeight / image.height;
-    canvas.width = crop.width;
-    canvas.height = crop.height;
+    const scaleX = imageNaturalWidth / imageWidth;
+    const scaleY = imageNaturalHeight / imageHeight;
+    canvas.width = cropWidth;
+    canvas.height = cropHeight;
     const ctx = canvas.getContext('2d');
-    ctx?.drawImage(image, crop.x * scaleX, crop.y * scaleY, crop.width * scaleX, crop.height * scaleY, 0, 0, crop.width, crop.height);
+    ctx?.drawImage(image, x * scaleX, y * scaleY, cropWidth * scaleX, cropHeight * scaleY, 0, 0, cropWidth, cropHeight);
     try {
       const blob = await canvasToBlob(canvas);
       return blob;
@@ -105,7 +118,6 @@ const ImageCropModal: FC<Props> = (props: Props) => {
   // Clear image and set isImageCrop true on modal close
   const onModalCloseHandler = async() => {
     setImageRef(null);
-    setIsCropImage(true);
     onModalClose();
   };
 
@@ -129,12 +141,21 @@ const ImageCropModal: FC<Props> = (props: Props) => {
       <ModalBody className="my-4">
         {
           isCropImage
-            ? (<ReactCrop src={src} crop={cropOptions} onImageLoaded={onImageLoaded} onChange={onCropChange} circularCrop={isCircular} />)
+            ? (
+              <ReactCrop
+                style={{ backgroundColor: 'transparent' }}
+                src={src}
+                crop={cropOptions}
+                onImageLoaded={onImageLoaded}
+                onChange={crop => setCropOtions(crop)}
+                circularCrop={isCircular}
+              />
+            )
             : (<img style={{ maxWidth: imageRef?.width }} src={imageRef?.src} />)
         }
       </ModalBody>
       <ModalFooter>
-        <button type="button" className="btn btn-outline-danger rounded-pill mr-auto" onClick={reset}>
+        <button type="button" className="btn btn-outline-danger rounded-pill mr-auto" disabled={!isCropImage} onClick={reset}>
           {t('crop_image_modal.reset')}
         </button>
         { !showCropOption && (

+ 1 - 1
packages/app/src/components/CustomNavigation/CustomNav.jsx

@@ -20,7 +20,7 @@ function getBreakpointOneLevelLarger(breakpoint) {
       return 'xl';
     case 'xl':
     default:
-      return '2xl';
+      return 'xxl';
   }
 }
 

+ 1 - 1
packages/app/src/components/Hotkeys/HotkeysDetector.jsx

@@ -1,6 +1,6 @@
 import React, { useMemo, useCallback } from 'react';
-import PropTypes from 'prop-types';
 
+import PropTypes from 'prop-types';
 import { GlobalHotKeys } from 'react-hotkeys';
 
 import HotkeyStroke from '~/client/models/HotkeyStroke';

+ 3 - 4
packages/app/src/components/Hotkeys/HotkeysManager.jsx

@@ -1,13 +1,12 @@
 import React, { useState } from 'react';
 
 import HotkeysDetector from './HotkeysDetector';
-
-import ShowStaffCredit from './Subscribers/ShowStaffCredit';
-import SwitchToMirrorMode from './Subscribers/SwitchToMirrorMode';
-import ShowShortcutsModal from './Subscribers/ShowShortcutsModal';
 import CreatePage from './Subscribers/CreatePage';
 import EditPage from './Subscribers/EditPage';
 import FocusToGlobalSearch from './Subscribers/FocusToGlobalSearch';
+import ShowShortcutsModal from './Subscribers/ShowShortcutsModal';
+import ShowStaffCredit from './Subscribers/ShowStaffCredit';
+import SwitchToMirrorMode from './Subscribers/SwitchToMirrorMode';
 
 // define supported components list
 const SUPPORTED_COMPONENTS = [

+ 2 - 0
packages/app/src/components/Layout/BasicLayout.tsx

@@ -9,6 +9,7 @@ import Sidebar from '../Sidebar';
 
 import { RawLayout } from './RawLayout';
 
+const AlertSiteUrlUndefined = dynamic(() => import('../AlertSiteUrlUndefined').then(mod => mod.AlertSiteUrlUndefined), { ssr: false });
 const HotkeysManager = dynamic(() => import('../Hotkeys/HotkeysManager'), { ssr: false });
 // const PageCreateModal = dynamic(() => import('../client/js/components/PageCreateModal'), { ssr: false });
 const GrowiNavbarBottom = dynamic(() => import('../Navbar/GrowiNavbarBottom').then(mod => mod.GrowiNavbarBottom), { ssr: false });
@@ -51,6 +52,7 @@ export const BasicLayout = ({
           </div>
 
           <div className="flex-fill mw-0" style={{ position: 'relative' }}>
+            <AlertSiteUrlUndefined />
             {children}
           </div>
         </div>

+ 1 - 1
packages/app/src/components/Me/ApiSettings.tsx

@@ -19,7 +19,7 @@ const ApiSettings = React.memo((): JSX.Element => {
       await apiv3Put('/personal-setting/api-token');
       mutateDatabaseData();
 
-      toastSuccess(t('toaster.update_successed', { target: t('page_me_apitoken.api_token') }));
+      toastSuccess(t('toaster.update_successed', { target: t('page_me_apitoken.api_token'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Me/BasicInfoSettings.tsx

@@ -22,7 +22,7 @@ export const BasicInfoSettings = (): JSX.Element => {
     try {
       await updateBasicInfo();
       sync();
-      toastSuccess(t('toaster.update_successed', { target: t('Basic Info') }));
+      toastSuccess(t('toaster.update_successed', { target: t('Basic Info'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Me/EditorSettings.tsx

@@ -234,7 +234,7 @@ export const EditorSettings = memo((): JSX.Element => {
   const updateRulesHandler = useCallback(async() => {
     try {
       await updateEditorSettings({ textlintSettings: { textlintRules } });
-      toastSuccess(t('toaster.update_successed', { target: 'Updated Textlint Settings' }));
+      toastSuccess(t('toaster.update_successed', { target: 'Updated Textlint Settings', ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Me/InAppNotificationSettings.tsx

@@ -54,7 +54,7 @@ const InAppNotificationSettings: FC = () => {
     try {
       const { data } = await apiv3Put('/personal-setting/in-app-notification-settings', { subscribeRules });
       setSubscribeRules(data.subscribeRules);
-      toastSuccess(t('toaster.update_successed', { target: 'InAppNotification Settings' }));
+      toastSuccess(t('toaster.update_successed', { target: 'InAppNotification Settings', ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 2 - 2
packages/app/src/components/Me/PasswordSettings.jsx

@@ -1,7 +1,7 @@
 import React, { useCallback } from 'react';
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
@@ -52,7 +52,7 @@ class PasswordSettings extends React.Component {
       if (onSubmit != null) {
         onSubmit();
       }
-      toastSuccess(t('toaster.update_successed', { target: t('Password') }));
+      toastSuccess(t('toaster.update_successed', { target: t('Password'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 3 - 3
packages/app/src/components/Me/ProfileImageSettings.tsx

@@ -48,7 +48,7 @@ const ProfileImageSettings = (): JSX.Element => {
       formData.append('file', croppedImage);
       const response = await apiPostForm('/attachments.uploadProfileImage', formData);
 
-      toastSuccess(t('toaster.update_successed', { target: t('Current Image') }));
+      toastSuccess(t('toaster.update_successed', { target: t('Current Image'), ns: 'commons' }));
 
       // eslint-disable-next-line @typescript-eslint/no-explicit-any
       setUploadedPictureSrc((response as any).attachment.filePathProxied);
@@ -64,7 +64,7 @@ const ProfileImageSettings = (): JSX.Element => {
       await apiPost('/attachments.removeProfileImage');
 
       setUploadedPictureSrc(undefined);
-      toastSuccess(t('toaster.update_successed', { target: t('Current Image') }));
+      toastSuccess(t('toaster.update_successed', { target: t('Current Image'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);
@@ -78,7 +78,7 @@ const ProfileImageSettings = (): JSX.Element => {
       const { userData } = response.data;
       setGravatarEnabled(userData.isGravatarEnabled);
 
-      toastSuccess(t('toaster.update_successed', { target: t('Set Profile Image') }));
+      toastSuccess(t('toaster.update_successed', { target: t('Set Profile Image'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 1 - 1
packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -330,7 +330,7 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
                     pageId={pageId}
                     revisionId={revisionId}
                     shareLinkId={shareLinkId}
-                    path={path}
+                    path={path ?? currentPathname} // If the page is empty, "path" is undefined
                     disableSeenUserInfoPopover={isSharedUser}
                     showPageControlDropdown={isAbleToShowPageManagement}
                     additionalMenuItemRenderer={additionalMenuItemsRenderer}

+ 2 - 1
packages/app/src/components/Page/CopyDropdown.jsx

@@ -11,6 +11,7 @@ import {
   Tooltip,
 } from 'reactstrap';
 
+import styles from './CopyDropdown.module.scss';
 
 const { encodeSpaces } = pagePathUtils;
 
@@ -109,7 +110,7 @@ const CopyDropdown = (props) => {
 
   return (
     <>
-      <Dropdown className="grw-copy-dropdown" isOpen={dropdownOpen} toggle={toggleDropdown}>
+      <Dropdown className={`${styles['grw-copy-dropdown']} grw-copy-dropdown`} isOpen={dropdownOpen} toggle={toggleDropdown}>
         <DropdownToggle
           caret
           className={dropdownToggleClassName}

+ 1 - 1
packages/app/src/styles/molecules/copy-dropdown.scss → packages/app/src/components/Page/CopyDropdown.module.scss

@@ -1,4 +1,4 @@
-.grw-copy-dropdown {
+.grw-copy-dropdown :global {
   .dropdown-menu {
     min-width: 310px;
 

+ 22 - 14
packages/app/src/components/PageEditor/CodeMirrorEditor.jsx

@@ -23,7 +23,8 @@ import EditorIcon from './EditorIcon';
 import EmojiPicker from './EmojiPicker';
 import EmojiPickerHelper from './EmojiPickerHelper';
 import GridEditModal from './GridEditModal';
-import geu from './GridEditorUtil';
+// TODO: re-impl with https://redmine.weseek.co.jp/issues/107248
+// import geu from './GridEditorUtil';
 // import HandsontableModal from './HandsontableModal';
 import LinkEditModal from './LinkEditModal';
 import mdu from './MarkdownDrawioUtil';
@@ -152,7 +153,8 @@ class CodeMirrorEditor extends AbstractEditor {
     this.renderCheatsheetModalButton = this.renderCheatsheetModalButton.bind(this);
 
     this.makeHeaderHandler = this.makeHeaderHandler.bind(this);
-    this.showGridEditorHandler = this.showGridEditorHandler.bind(this);
+    // TODO: re-impl with https://redmine.weseek.co.jp/issues/107248
+    // this.showGridEditorHandler = this.showGridEditorHandler.bind(this);
     this.showLinkEditHandler = this.showLinkEditHandler.bind(this);
     this.showHandsonTableHandler = this.showHandsonTableHandler.bind(this);
 
@@ -858,9 +860,10 @@ class CodeMirrorEditor extends AbstractEditor {
     cm.focus();
   }
 
-  showGridEditorHandler() {
-    this.gridEditModal.current.show(geu.getGridHtml(this.getCodeMirror()));
-  }
+  // TODO: re-impl with https://redmine.weseek.co.jp/issues/107248
+  // showGridEditorHandler() {
+  //   this.gridEditModal.current.show(geu.getGridHtml(this.getCodeMirror()));
+  // }
 
   showLinkEditHandler() {
     this.linkEditModal.current.show(markdownLinkUtil.getMarkdownLink(this.getCodeMirror()));
@@ -998,15 +1001,16 @@ class CodeMirrorEditor extends AbstractEditor {
       >
         <EditorIcon icon="Image" />
       </Button>,
-      <Button
-        key="nav-item-grid"
-        color={null}
-        size="sm"
-        title="Grid"
-        onClick={this.showGridEditorHandler}
-      >
-        <EditorIcon icon="Grid" />
-      </Button>,
+      // TODO: re-impl with https://redmine.weseek.co.jp/issues/107248
+      // <Button
+      //   key="nav-item-grid"
+      //   color={null}
+      //   size="sm"
+      //   title="Grid"
+      //   onClick={this.showGridEditorHandler}
+      // >
+      //   <EditorIcon icon="Grid" />
+      // </Button>,
       <Button
         key="nav-item-table"
         color={null}
@@ -1119,10 +1123,14 @@ class CodeMirrorEditor extends AbstractEditor {
         { this.renderCheatsheetOverlay() }
         { this.renderEmojiPicker() }
 
+        {/*
+        // TODO: re-impl with https://redmine.weseek.co.jp/issues/107248
         <GridEditModal
           ref={this.gridEditModal}
           onSave={(grid) => { return geu.replaceGridWithHtmlWithEditor(this.getCodeMirror(), grid) }}
         />
+         */}
+
         <LinkEditModal
           ref={this.linkEditModal}
           onSave={(linkText) => { return markdownLinkUtil.replaceFocusedMarkdownLinkWithEditor(this.getCodeMirror(), linkText) }}

+ 1 - 12
packages/app/src/components/PageEditor/CodeMirrorEditor.module.scss

@@ -1,4 +1,3 @@
-@use '~/styles/variables' as var;
 @use '~/styles/bootstrap/init' as bs;
 
 .grw-codemirror-editor :global {
@@ -17,7 +16,7 @@
     pre.CodeMirror-line.grw-cm-header-line {
       padding-top: 0.16em;
       padding-bottom: 0.08em;
-      font-family: bs.$font-family-monospace;
+      font-family: var(--font-family-monospace);
 
       // '#'
       .cm-formatting-header {
@@ -76,16 +75,6 @@
   .CodeMirror-hints {
     max-height: 30em !important;
 
-    .CodeMirror-hint.crowi-emoji-autocomplete {
-      font-family: var.$font-family-monospace-not-strictly;
-      line-height: 1.6em;
-
-      .img-container {
-        display: inline-block;
-        width: 30px;
-      }
-    }
-
     // active line
     .CodeMirror-hint-active.crowi-emoji-autocomplete {
       .img-container {

+ 1 - 1
packages/app/src/components/PageEditor/Editor.module.scss

@@ -158,7 +158,7 @@
 .modal-gfm-cheatsheet :global {
   .modal-body {
     .hljs {
-      font-family: bs.$font-family-monospace;
+      font-family: var(--font-family-monospace);
     }
   }
 }

+ 13 - 13
packages/app/src/components/PagePathHierarchicalLink.tsx

@@ -1,4 +1,4 @@
-import React, { memo } from 'react';
+import React, { memo, useCallback } from 'react';
 
 import Link from 'next/link';
 import urljoin from 'url-join';
@@ -19,9 +19,16 @@ type PagePathHierarchicalLinkProps = {
 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 const PagePathHierarchicalLink = memo((props: PagePathHierarchicalLinkProps): JSX.Element => {
   const {
-    linkedPagePath, linkedPagePathByHtml, basePath, isInTrash,
+    linkedPagePath, linkedPagePathByHtml, basePath, isInTrash, isInnerElem,
   } = props;
 
+  // eslint-disable-next-line react/prop-types
+  const RootElm = useCallback(({ children }) => {
+    return isInnerElem
+      ? <>{children}</>
+      : <span className="grw-page-path-hierarchical-link text-break">{children}</span>;
+  }, [isInnerElem]);
+
   // render root element
   if (linkedPagePath.isRoot) {
     if (basePath != null) {
@@ -30,17 +37,17 @@ const PagePathHierarchicalLink = memo((props: PagePathHierarchicalLinkProps): JS
 
     return isInTrash
       ? (
-        <>
+        <RootElm>
           <span className="path-segment">
             <Link href="/trash" prefetch={false}>
               <a ><i className="icon-trash"></i></a>
             </Link>
           </span>
           <span className="separator"><a href="/">/</a></span>
-        </>
+        </RootElm>
       )
       : (
-        <>
+        <RootElm>
           <span className="path-segment">
             <Link href="/" prefetch={false}>
               <a >
@@ -49,7 +56,7 @@ const PagePathHierarchicalLink = memo((props: PagePathHierarchicalLinkProps): JS
               </a>
             </Link>
           </span>
-        </>
+        </RootElm>
       );
   }
 
@@ -61,13 +68,6 @@ const PagePathHierarchicalLink = memo((props: PagePathHierarchicalLinkProps): JS
 
   const href = encodeURI(urljoin(basePath || '/', linkedPagePath.href));
 
-  // eslint-disable-next-line react/prop-types
-  const RootElm = ({ children }) => {
-    return props.isInnerElem
-      ? <>{children}</>
-      : <span className="grw-page-path-hierarchical-link text-break">{children}</span>;
-  };
-
   return (
     <RootElm>
       { isParentExists && (

+ 2 - 2
packages/app/src/components/PasswordResetExecutionForm.tsx

@@ -11,7 +11,7 @@ const logger = loggerFactory('growi:passwordReset');
 
 
 const PasswordResetExecutionForm: FC = () => {
-  const { t } = useTranslation();
+  const { t } = useTranslation(['translation', 'commons']);
 
   const [newPassword, setNewPassword] = useState('');
   const [newPasswordConfirm, setNewPasswordConfirm] = useState('');
@@ -41,7 +41,7 @@ const PasswordResetExecutionForm: FC = () => {
 
       setValidationErrorI18n('');
 
-      toastSuccess(t('toaster.update_successed', { target: t('Password') }));
+      toastSuccess(t('toaster.update_successed', { target: t('Password'), ns: 'commons' }));
     }
     catch (err) {
       toastError(err);

+ 22 - 0
packages/app/src/components/RevisionComparer/RevisionComparer.module.scss

@@ -11,4 +11,26 @@
   .d2h-file-header {
     display: none;
   }
+
+  .grw-copy-dropdown {
+    .dropdown-menu {
+      min-width: 310px;
+
+      .dropdown-header {
+        margin-bottom: 0.5em;
+        font-size: 1.1em;
+      }
+
+      // unset active styles
+      .dropdown-item:active {
+        color: unset;
+        background-color: unset;
+      }
+
+      .well {
+        font-size: 0.7em;
+        word-break: break-all;
+      }
+    }
+  }
 }

+ 7 - 0
packages/app/src/components/Sidebar/CustomSidebar.module.scss

@@ -0,0 +1,7 @@
+@use '~/styles/organisms/wiki-custom-sidebar.scss';
+
+.grw-custom-sidebar-content :global {
+  .wiki {
+    @extend %grw-custom-sidebar-content;
+  }
+}

+ 4 - 2
packages/app/src/components/Sidebar/CustomSidebar.tsx

@@ -10,6 +10,9 @@ import loggerFactory from '~/utils/logger';
 import RevisionRenderer from '../Page/RevisionRenderer';
 
 
+import styles from './CustomSidebar.module.scss';
+
+
 const logger = loggerFactory('growi:cli:CustomSidebar');
 
 
@@ -58,11 +61,10 @@ const CustomSidebar: FC = () => {
 
       {
         (!isLoading && markdown != null) && (
-          <div className="p-3">
+          <div className={`p-3 grw-custom-sidebar-content ${styles['grw-custom-sidebar-content']}`}>
             <RevisionRenderer
               rendererOptions={rendererOptions}
               markdown={markdown}
-              additionalClassName="grw-custom-sidebar-content"
             />
           </div>
         )

+ 1 - 1
packages/app/src/components/Sidebar/RecentChanges.module.scss

@@ -4,7 +4,7 @@
   transform: translateY(-2px);
 
   .custom-control-label::before {
-    padding-left: 16px;
+    padding-left: 19px;
     content: 'L';
   }
 

+ 9 - 7
packages/app/src/components/Sidebar/Tag.tsx

@@ -58,13 +58,15 @@ const Tag: FC = () => {
           </div>
         )
         : (
-          <TagList
-            tagData={tagData}
-            totalTags={totalCount}
-            activePage={activePage}
-            onChangePage={setOffsetByPageNumber}
-            pagingLimit={PAGING_LIMIT}
-          />
+          <div data-testid="grw-tags-list">
+            <TagList
+              tagData={tagData}
+              totalTags={totalCount}
+              activePage={activePage}
+              onChangePage={setOffsetByPageNumber}
+              pagingLimit={PAGING_LIMIT}
+            />
+          </div>
         )
       }
 

+ 0 - 143
packages/app/src/components/StaffCredit/StaffCredit.jsx

@@ -1,143 +0,0 @@
-import React from 'react';
-
-import PropTypes from 'prop-types';
-import {
-  Modal, ModalBody,
-} from 'reactstrap';
-
-import { apiv3Get } from '~/client/util/apiv3-client';
-import loggerFactory from '~/utils/logger';
-
-
-/**
- * Page staff credit component
- *
- * @export
- * @class StaffCredit
- * @extends {React.Component}
- */
-
-// eslint-disable-next-line no-unused-vars
-const logger = loggerFactory('growi:cli:StaffCredit');
-
-class StaffCredit extends React.Component {
-
-  constructor(props) {
-
-    super(props);
-    this.state = {
-      isShown: true,
-      contributors: null,
-    };
-    this.deleteCredit = this.deleteCredit.bind(this);
-  }
-
-  // to delete the staffCredit and to inform that to Hotkeys.jsx
-  deleteCredit() {
-    if (this.state.isShown) {
-      this.setState({ isShown: false });
-    }
-  }
-
-  renderMembers(memberGroup, keyPrefix) {
-    // construct members elements
-    const members = memberGroup.members.map((member) => {
-      return (
-        <div className={memberGroup.additionalClass} key={`${keyPrefix}-${member.name}-container`}>
-          <span className="dev-position" key={`${keyPrefix}-${member.name}-position`}>
-            {/* position or '&nbsp;' */}
-            { member.position || '\u00A0' }
-          </span>
-          <p className="dev-name" key={`${keyPrefix}-${member.name}`}>{member.name}</p>
-        </div>
-      );
-    });
-    return (
-      <React.Fragment key={`${keyPrefix}-fragment`}>
-        {members}
-      </React.Fragment>
-    );
-  }
-
-  renderContributors() {
-    if (this.state.isShown) {
-      const credit = this.state.contributors.map((contributor) => {
-        // construct members elements
-        const memberGroups = contributor.memberGroups.map((memberGroup, idx) => {
-          return this.renderMembers(memberGroup, `${contributor.sectionName}-group${idx}`);
-        });
-        return (
-          <React.Fragment key={`${contributor.sectionName}-fragment`}>
-            <div className={`row ${contributor.additionalClass}`} key={`${contributor.sectionName}-row`}>
-              <h2 className="col-md-12 dev-team staff-credit-mt-10rem staff-credit-mb-6rem" key={contributor.sectionName}>{contributor.sectionName}</h2>
-              {memberGroups}
-            </div>
-            <div className="clearfix"></div>
-          </React.Fragment>
-        );
-      });
-      return (
-        <div className="text-center staff-credit-content" onClick={this.deleteCredit}>
-          <h1 className="staff-credit-mb-6rem">GROWI Contributors</h1>
-          <div className="clearfix"></div>
-          {credit}
-        </div>
-      );
-    }
-    return null;
-  }
-
-  async componentDidMount() {
-    const res = await apiv3Get('/staffs');
-    const contributors = res.data.contributors;
-    this.setState({ contributors });
-
-    setTimeout(() => {
-      // px / sec
-      const scrollSpeed = 200;
-      const target = $('.credit-curtain');
-      const scrollTargetHeight = target.children().innerHeight();
-      const duration = scrollTargetHeight / scrollSpeed * 1000;
-      target.animate({ scrollTop: scrollTargetHeight }, duration, 'linear');
-      target.slimScroll({
-        height: target.innerHeight(),
-        // Able to scroll after automatic schooling is complete so set "bottom" to allow scrolling from the bottom.
-        start: 'bottom',
-        color: '#FFFFFF',
-      });
-    }, 10);
-  }
-
-  render() {
-    const { onClosed } = this.props;
-
-    if (this.state.contributors === null) {
-      return <></>;
-    }
-
-    return (
-      <Modal
-        isOpen={this.state.isShown}
-        onClosed={() => {
-          if (onClosed != null) {
-            onClosed();
-          }
-        }}
-        toggle={this.deleteCredit}
-        scrollable
-        className="staff-credit"
-      >
-        <ModalBody className="credit-curtain">
-          {this.renderContributors()}
-        </ModalBody>
-      </Modal>
-    );
-  }
-
-}
-
-StaffCredit.propTypes = {
-  onClosed: PropTypes.func,
-};
-
-export default StaffCredit;

+ 5 - 4
packages/app/src/styles/_staff_credit.scss → packages/app/src/components/StaffCredit/StaffCredit.module.scss

@@ -1,5 +1,5 @@
 // Staff Credit
-.staff-credit {
+.staff-credit :global {
   // attached !important for updating from .modal-dialog class style
   width: 80vw !important;
   max-width: unset !important;
@@ -17,13 +17,14 @@
     background-color: black;
     background-image: radial-gradient(rgba(50, 100, 100, 0.75), black 120%);
   }
-  &::after {
+
+  .background {
     position: absolute;
     top: 0;
     left: 0;
     width: 100%;
     height: 100%;
-    content: '';
+    pointer-events: none;
     background: repeating-linear-gradient(0deg, rgba(black, 0.15), rgba(black, 0.15) 2px, transparent 2px, transparent 4px);
   }
 
@@ -35,7 +36,7 @@
   h6,
   .dev-position,
   .dev-name {
-    font-family: 'Press Start 2P', $font-family-for-staff-credit;
+    font-family: 'Press Start 2P', Lato, -apple-system, BlinkMacSystemFont, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
     color: white;
   }
 

+ 139 - 0
packages/app/src/components/StaffCredit/StaffCredit.tsx

@@ -0,0 +1,139 @@
+import React, { useCallback, useState } from 'react';
+
+import { animateScroll } from 'react-scroll';
+import {
+  Modal, ModalBody,
+} from 'reactstrap';
+
+import { useSWRxStaffs } from '~/stores/staff';
+import loggerFactory from '~/utils/logger';
+
+
+import styles from './StaffCredit.module.scss';
+
+
+// eslint-disable-next-line no-unused-vars
+const logger = loggerFactory('growi:cli:StaffCredit');
+
+
+type Props = {
+  onClosed?: () => void,
+}
+
+const StaffCredit = (props: Props): JSX.Element => {
+
+  const { onClosed } = props;
+
+  const { data: contributors } = useSWRxStaffs();
+
+  const [isScrolling, setScrolling] = useState(false);
+
+
+  const closeHandler = useCallback(() => {
+    if (onClosed != null) {
+      onClosed();
+    }
+  }, [onClosed]);
+
+  const contentsClickedHandler = useCallback(() => {
+    if (isScrolling) {
+      setScrolling(false);
+    }
+    else {
+      closeHandler();
+    }
+  }, [closeHandler, isScrolling]);
+
+  const renderMembers = useCallback((memberGroup, keyPrefix) => {
+    // construct members elements
+    const members = memberGroup.members.map((member) => {
+      return (
+        <div className={memberGroup.additionalClass} key={`${keyPrefix}-${member.name}-container`}>
+          <span className="dev-position" key={`${keyPrefix}-${member.name}-position`}>
+            {/* position or '&nbsp;' */}
+            { member.position || '\u00A0' }
+          </span>
+          <p className="dev-name" key={`${keyPrefix}-${member.name}`}>{member.name}</p>
+        </div>
+      );
+    });
+    return (
+      <React.Fragment key={`${keyPrefix}-fragment`}>
+        {members}
+      </React.Fragment>
+    );
+  }, []);
+
+  const renderContributors = useCallback(() => {
+    if (contributors == null) {
+      return <></>;
+    }
+
+    const credit = contributors.map((contributor) => {
+      // construct members elements
+      const memberGroups = contributor.memberGroups.map((memberGroup, idx) => {
+        return renderMembers(memberGroup, `${contributor.sectionName}-group${idx}`);
+      });
+      return (
+        <React.Fragment key={`${contributor.sectionName}-fragment`}>
+          <div className={`row ${contributor.additionalClass}`} key={`${contributor.sectionName}-row`}>
+            <h2 className="col-md-12 dev-team staff-credit-mt-10rem staff-credit-mb-6rem" key={contributor.sectionName}>{contributor.sectionName}</h2>
+            {memberGroups}
+          </div>
+          <div className="clearfix"></div>
+        </React.Fragment>
+      );
+    });
+    return (
+      <div className="text-center staff-credit-content" onClick={contentsClickedHandler}>
+        <h1 className="staff-credit-mb-6rem">GROWI Contributors</h1>
+        <div className="clearfix"></div>
+        {credit}
+      </div>
+    );
+  }, [contentsClickedHandler, contributors, renderMembers]);
+
+
+  const openedHandler = useCallback(() => {
+    // init
+    animateScroll.scrollTo(0, { containerId: 'modalBody', duration: 0 });
+
+    setScrolling(true);
+
+    // start scrolling
+    animateScroll.scrollToBottom({
+      containerId: 'modalBody',
+      smooth: 'linear',
+      delay: 200,
+      duration: (scrollDistanceInPx: number) => {
+        const scrollSpeed = 200;
+        return scrollDistanceInPx / scrollSpeed * 1000;
+      },
+    });
+  }, []);
+
+
+  const isLoaded = contributors !== undefined;
+
+  if (contributors == null) {
+    return <></>;
+  }
+
+  return (
+    <Modal
+      isOpen={isLoaded}
+      toggle={closeHandler}
+      scrollable
+      className={`staff-credit ${styles['staff-credit']}`}
+      onOpened={openedHandler}
+    >
+      <ModalBody id="modalBody" className="credit-curtain">
+        {renderContributors()}
+      </ModalBody>
+      <div className="background"></div>
+    </Modal>
+  );
+
+};
+
+export default StaffCredit;

+ 5 - 5
packages/app/src/components/TableOfContents.tsx

@@ -6,13 +6,10 @@ import { useIsUserPage } from '~/stores/context';
 import { useTocOptions } from '~/stores/renderer';
 import loggerFactory from '~/utils/logger';
 
-
 import { StickyStretchableScroller } from './StickyStretchableScroller';
 
-
 import styles from './TableOfContents.module.scss';
 
-
 // eslint-disable-next-line no-unused-vars
 const logger = loggerFactory('growi:TableOfContents');
 
@@ -28,7 +25,10 @@ const TableOfContents = (): JSX.Element => {
     // calculate absolute top of '#revision-toc' element
     const parentElem = document.querySelector('.grw-side-contents-container');
     const containerElem = document.querySelector('#revision-toc');
-    if (parentElem == null || containerElem == null) {
+
+    // rendererOptions for redo calcViewHeight()
+    // see: https://github.com/weseek/growi/pull/6791
+    if (parentElem == null || containerElem == null || rendererOptions == null) {
       return 0;
     }
     const parentBottom = parentElem.getBoundingClientRect().bottom;
@@ -45,7 +45,7 @@ const TableOfContents = (): JSX.Element => {
     }
     // bottom - revisionToc top
     return bottom - (containerTop + containerPaddingTop);
-  }, [isUserPage]);
+  }, [isUserPage, rendererOptions]);
 
   return (
     <div id="revision-toc" className={`revision-toc ${styles['revision-toc']}`}>

+ 7 - 0
packages/app/src/pages/_document.page.tsx

@@ -1,3 +1,4 @@
+/* eslint-disable @next/next/google-font-display */
 import React from 'react';
 
 import Document, {
@@ -28,6 +29,12 @@ class GrowiDocument extends Document {
           {renderScriptTagsByGroup('basis')}
           {renderStyleTagsByGroup('basis')}
           */}
+          <link rel='preload' href="/static/fonts/PressStart2P-latin.woff2" as="font" type="font/woff2" />
+          <link rel='preload' href="/static/fonts/PressStart2P-latin-ext.woff2" as="font" type="font/woff2" />
+          <link rel='preload' href="/static/fonts/Lato-Regular-latin.woff2" as="font" type="font/woff2" />
+          <link rel='preload' href="/static/fonts/Lato-Regular-latin-ext.woff2" as="font" type="font/woff2" />
+          <link rel='preload' href="/static/fonts/Lato-Bold-latin.woff2" as="font" type="font/woff2" />
+          <link rel='preload' href="/static/fonts/Lato-Bold-latin-ext.woff2" as="font" type="font/woff2" />
         </Head>
         <body>
           <Main />

+ 4 - 4
packages/app/src/pages/admin/[[...path]].page.tsx

@@ -86,7 +86,7 @@ type Props = CommonProps & {
   siteUrl: string,
 };
 
-const AdminMarkdownSettingsPage: NextPage<Props> = (props: Props) => {
+const AdminPage: NextPage<Props> = (props: Props) => {
 
   const { t } = useTranslation('admin');
   const router = useRouter();
@@ -115,7 +115,7 @@ const AdminMarkdownSettingsPage: NextPage<Props> = (props: Props) => {
       />,
     },
     app: {
-      title: t('app_settings'),
+      title: t('commons:headers.app_settings'),
       component: <AppSettingsPageContents />,
     },
     security: {
@@ -335,11 +335,11 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
   }
 
   injectServerConfigurations(context, props);
-  await injectNextI18NextConfigurations(context, props, ['admin']);
+  await injectNextI18NextConfigurations(context, props, ['admin', 'commons']);
 
   return {
     props,
   };
 };
 
-export default AdminMarkdownSettingsPage;
+export default AdminPage;

+ 1 - 1
packages/app/src/pages/forgot-password.page.tsx

@@ -50,7 +50,7 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
 
   const props: CommonProps = result.props as CommonProps;
 
-  await injectNextI18NextConfigurations(context, props, ['translation']);
+  await injectNextI18NextConfigurations(context, props, ['translation', 'commons']);
 
   return {
     props,

+ 1 - 1
packages/app/src/pages/me/[[...path]].page.tsx

@@ -198,7 +198,7 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
 
   await injectUserUISettings(context, props);
   await injectServerConfigurations(context, props);
-  await injectNextI18NextConfigurations(context, props, ['translation', 'admin']);
+  await injectNextI18NextConfigurations(context, props, ['translation', 'admin', 'commons']);
 
   return {
     props,

+ 1 - 1
packages/app/src/pages/reset-password.page.tsx

@@ -62,7 +62,7 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
     props.email = email;
   }
 
-  await injectNextI18NextConfigurations(context, props, ['translation']);
+  await injectNextI18NextConfigurations(context, props, ['translation', 'commons']);
 
   return {
     props,

+ 1 - 1
packages/app/src/pages/tags.page.tsx

@@ -63,7 +63,7 @@ const TagPage: NextPage<CommonProps> = (props: Props) => {
       <Head>
       </Head>
       <BasicLayout title={useCustomTitle(props, 'GROWI')} className={classNames.join(' ')}>
-        <div className="grw-container-convertible mb-5 pb-5">
+        <div className="grw-container-convertible mb-5 pb-5" data-testid="tags-page">
           <h2 className="my-3">{`${t('Tags')}(${totalCount})`}</h2>
           <div className="px-3 mb-5 text-center">
             <TagCloudBox tags={tagData} minSize={20} />

+ 11 - 1
packages/app/src/pages/utils/commons.ts

@@ -76,7 +76,17 @@ export const getNextI18NextConfig = async(
     ?? configManager.getConfig('crowi', 'app:globalLang') as Lang
     ?? Lang.en_US;
 
-  return serverSideTranslations(locale, namespacesRequired ?? ['translation'], nextI18NextConfig, preloadAllLang ? AllLang : false);
+  // TODO: Consider to not include translation as default or other architecture idea
+  // see: https://redmine.weseek.co.jp/issues/107092
+  const namespaces = ['commons'];
+  if (namespacesRequired != null) {
+    namespaces.push(...namespacesRequired);
+  }
+  else {
+    namespaces.push('translation');
+  }
+
+  return serverSideTranslations(locale, namespaces, nextI18NextConfig, preloadAllLang ? AllLang : false);
 };
 
 /**

+ 3 - 1
packages/app/src/server/routes/index.js

@@ -84,6 +84,9 @@ module.exports = function(crowi, app) {
 
   app.get('/register'                 , applicationInstalled, login.preLogin, login.register);
 
+  // load before "/admin/*"
+  app.get('/admin/export/:fileName'             , loginRequiredStrictly , adminRequired ,admin.export.api.validators.export.download(), admin.export.download);
+
   app.get('/admin/*'                    , applicationInstalled, loginRequiredStrictly , adminRequired , next.delegateToNext);
   app.get('/admin'                    , applicationInstalled, loginRequiredStrictly , adminRequired , next.delegateToNext);
   // app.get('/admin/app'                , applicationInstalled, loginRequiredStrictly , adminRequired , admin.app.index);
@@ -152,7 +155,6 @@ module.exports = function(crowi, app) {
 
   // export management for admin
   // app.get('/admin/export'                       , loginRequiredStrictly , adminRequired ,admin.export.index);
-  // app.get('/admin/export/:fileName'             , loginRequiredStrictly , adminRequired ,admin.export.api.validators.export.download(), admin.export.download);
 
   // app.get('/admin/*'                            , loginRequiredStrictly ,adminRequired, admin.notFound.index);
 

+ 12 - 0
packages/app/src/stores/staff.tsx

@@ -0,0 +1,12 @@
+import useSWR, { SWRResponse } from 'swr';
+
+import { apiv3Get } from '~/client/util/apiv3-client';
+
+export const useSWRxStaffs = (): SWRResponse<any, Error> => {
+  return useSWR(
+    '/staffs',
+    endpoint => apiv3Get(endpoint).then((response) => {
+      return response.data.contributors;
+    }),
+  );
+};

+ 0 - 4
packages/app/src/styles/_create-page.scss

@@ -8,8 +8,4 @@
   .grw-btn-create-page {
     min-width: 90px;
   }
-
-  .create-page-under-tree-label code {
-    font-family: $font-family-monospace-not-strictly;
-  }
 }

+ 1 - 2
packages/app/src/styles/_editor.scss

@@ -1,7 +1,7 @@
 @import './bootstrap/init' ;
 @import './variables' ;
 @import './mixins' ;
-@import 'sidebar-wiki';
+@import './organisms/wiki-custom-sidebar';
 
 // global imported
 .layout-root.editing {
@@ -239,7 +239,6 @@
   &.editing-sidebar {
     .page-editor-preview-body {
       width: 320px;
-      padding-top: 0;
       margin-right: auto;
       margin-left: auto;
 

+ 49 - 0
packages/app/src/styles/_fonts.scss

@@ -0,0 +1,49 @@
+// Press Start 2P
+@font-face {
+  font-family: 'Press Start 2P';
+  font-style: normal;
+  font-weight: 400;
+  src: url('/static/fonts/PressStart2P-latin-ext.woff2') format('woff2');
+  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+@font-face {
+  font-family: 'Press Start 2P';
+  font-style: normal;
+  font-weight: 400;
+  src: url('/static/fonts/PressStart2P-latin.woff2') format('woff2');
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+// Lato
+@font-face {
+  font-family: 'Lato';
+  font-style: normal;
+  font-weight: 400;
+  src: url('/static/fonts/Lato-Regular-latin-ext.woff2') format('woff2');
+  font-display: swap;
+  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+@font-face {
+  font-family: 'Lato';
+  font-style: normal;
+  font-weight: 400;
+  src: url('/static/fonts/Lato-Regular-latin.woff2') format('woff2');
+  font-display: swap;
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+@font-face {
+  font-family: 'Lato';
+  font-style: normal;
+  font-weight: 700;
+  src: url('/static/fonts/Lato-Bold-latin-ext.woff2') format('woff2');
+  font-display: swap;
+  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+@font-face {
+  font-family: 'Lato';
+  font-style: normal;
+  font-weight: 700;
+  src: url('/static/fonts/Lato-Bold-latin.woff2') format('woff2');
+  font-display: swap;
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}

+ 0 - 1
packages/app/src/styles/_layout.scss

@@ -30,7 +30,6 @@ body.not-found-page .grw-container-convertible {
 }
 
 .grw-modal-head {
-  font-size: 1em;
   border-bottom: 1px solid transparent;
 }
 

+ 1 - 2
packages/app/src/styles/_variables.scss

@@ -9,14 +9,13 @@ $grw-marker-blue: #6cf;
 $grw-marker-cyan: #6ff;
 $grw-marker-green: #6f6;
 
-$font-family-for-staff-credit: Lato, -apple-system, BlinkMacSystemFont, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif !default;
 $font-family-monospace-not-strictly: Monaco, Menlo, Consolas, 'Courier New', MeiryoKe_Gothic, monospace;
 
 //== Layout
 $grw-navbar-height: 52px;
 $grw-navbar-border-width: 3.3333px;
 // slightly larger than $zindex-sticky
-// https://getbootstrap.jp/docs/4.5/layout/overview/#z-index
+// https://getbootstrap.jp/docs/4.6/layout/overview/#z-index
 $grw-navbar-z-index: 1025;
 
 $grw-subnav-min-height: 95px;

+ 0 - 4
packages/app/src/styles/bootstrap/_override.scss

@@ -1,7 +1,3 @@
-body {
-  font-family: $font-family-sans-serif;
-}
-
 * {
   outline: none !important;
 }

+ 2 - 2
packages/app/src/styles/bootstrap/_variables.scss

@@ -47,7 +47,7 @@ $grid-breakpoints: (
   md: 768px,
   lg: 992px,
   xl: 1200px,
-  2xl: 1480px,
+  xxl: 1480px,
 );
 
 // Grid containers
@@ -59,7 +59,7 @@ $container-max-widths: (
   md: 720px,
   lg: 960px,
   xl: 1140px,
-  2xl: 1320px,
+  xxl: 1320px,
 );
 
 //== Typography

+ 0 - 4
packages/app/src/styles/_sidebar-wiki.scss → packages/app/src/styles/organisms/_wiki-custom-sidebar.scss

@@ -44,7 +44,3 @@
     margin: 0;
   }
 }
-
-.grw-custom-sidebar-content.wiki {
-  @extend %grw-custom-sidebar-content;
-}

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