Преглед изворни кода

Merge pull request #8894 from weseek/master

Release v7.0.11
Yuki Takei пре 1 година
родитељ
комит
f94f4ac092
100 измењених фајлова са 534 додато и 82 уклоњено
  1. 15 7
      .github/workflows/reusable-app-prod.yml
  2. 1 0
      apps/app/next-env.d.ts
  3. 16 0
      apps/app/next.config.js
  4. 13 13
      apps/app/package.json
  5. 25 25
      apps/app/playwright.config.ts
  6. 30 0
      apps/app/playwright/20-basic-features/access-to-pagelist.spec.ts
  7. 50 0
      apps/app/playwright/20-basic-features/click-page-icons.spec.ts
  8. 49 0
      apps/app/playwright/20-basic-features/comments.spec.ts
  9. 47 0
      apps/app/playwright/20-basic-features/sticky-features.spec.ts
  10. 45 0
      apps/app/playwright/21-basic-features-for-guest/access-to-page.spec.ts
  11. 14 0
      apps/app/playwright/21-basic-features-for-guest/sticky-for-guest.spec.ts
  12. 37 0
      apps/app/playwright/22-sharelink/access-to-sharelink.spec.ts
  13. 122 0
      apps/app/playwright/60-home/home.spec.ts
  14. 5 20
      apps/app/playwright/auth.setup.ts
  15. 21 0
      apps/app/playwright/utils/CollapseSidebar.ts
  16. 24 0
      apps/app/playwright/utils/Login.ts
  17. 2 0
      apps/app/playwright/utils/index.ts
  18. 1 2
      apps/app/public/static/locales/en_US/translation.json
  19. 1 2
      apps/app/public/static/locales/fr_FR/translation.json
  20. 1 2
      apps/app/public/static/locales/ja_JP/translation.json
  21. 1 2
      apps/app/public/static/locales/zh_CN/translation.json
  22. 5 0
      apps/app/src/client/components/.eslintrc.js
  23. 0 0
      apps/app/src/client/components/Admin/AdminHome/AdminHome.jsx
  24. 0 0
      apps/app/src/client/components/Admin/AdminHome/EnvVarsTable.tsx
  25. 0 0
      apps/app/src/client/components/Admin/AdminHome/SystemInfomationTable.tsx
  26. 0 0
      apps/app/src/client/components/Admin/App/AppSetting.jsx
  27. 0 0
      apps/app/src/client/components/Admin/App/AppSettingsPageContents.tsx
  28. 0 0
      apps/app/src/client/components/Admin/App/AwsSetting.tsx
  29. 0 0
      apps/app/src/client/components/Admin/App/AzureSetting.tsx
  30. 0 0
      apps/app/src/client/components/Admin/App/ConfirmModal.tsx
  31. 0 0
      apps/app/src/client/components/Admin/App/FileUploadSetting.tsx
  32. 0 0
      apps/app/src/client/components/Admin/App/GcsSetting.tsx
  33. 0 0
      apps/app/src/client/components/Admin/App/MailSetting.tsx
  34. 0 0
      apps/app/src/client/components/Admin/App/MaintenanceMode.tsx
  35. 0 0
      apps/app/src/client/components/Admin/App/MaskedInput.module.scss
  36. 0 0
      apps/app/src/client/components/Admin/App/MaskedInput.tsx
  37. 0 0
      apps/app/src/client/components/Admin/App/QuestionnaireSettings.tsx
  38. 0 0
      apps/app/src/client/components/Admin/App/SesSetting.tsx
  39. 0 0
      apps/app/src/client/components/Admin/App/SiteUrlSetting.tsx
  40. 0 0
      apps/app/src/client/components/Admin/App/SmtpSetting.tsx
  41. 1 1
      apps/app/src/client/components/Admin/App/V5PageMigration.tsx
  42. 0 0
      apps/app/src/client/components/Admin/AuditLog/ActivityTable.tsx
  43. 0 0
      apps/app/src/client/components/Admin/AuditLog/AuditLogDisableMode.tsx
  44. 1 1
      apps/app/src/client/components/Admin/AuditLog/AuditLogSettings.tsx
  45. 0 0
      apps/app/src/client/components/Admin/AuditLog/DateRangePicker.tsx
  46. 0 0
      apps/app/src/client/components/Admin/AuditLog/SearchUsernameTypeahead.tsx
  47. 0 0
      apps/app/src/client/components/Admin/AuditLog/SelectActionDropdown.tsx
  48. 1 1
      apps/app/src/client/components/Admin/AuditLogManagement.tsx
  49. 0 0
      apps/app/src/client/components/Admin/Common/Accordion.jsx
  50. 0 0
      apps/app/src/client/components/Admin/Common/AdminInstallButtonRow.tsx
  51. 0 0
      apps/app/src/client/components/Admin/Common/AdminUpdateButtonRow.tsx
  52. 0 0
      apps/app/src/client/components/Admin/Common/LabeledProgressBar.tsx
  53. 0 0
      apps/app/src/client/components/Admin/Customize/Customize.jsx
  54. 0 0
      apps/app/src/client/components/Admin/Customize/CustomizeCssSetting.tsx
  55. 0 0
      apps/app/src/client/components/Admin/Customize/CustomizeFunctionOption.tsx
  56. 0 0
      apps/app/src/client/components/Admin/Customize/CustomizeFunctionSetting.tsx
  57. 1 1
      apps/app/src/client/components/Admin/Customize/CustomizeLayoutSetting.tsx
  58. 2 2
      apps/app/src/client/components/Admin/Customize/CustomizeLogoSetting.tsx
  59. 0 0
      apps/app/src/client/components/Admin/Customize/CustomizeNoscriptSetting.tsx
  60. 0 0
      apps/app/src/client/components/Admin/Customize/CustomizePresentationSetting.tsx
  61. 0 0
      apps/app/src/client/components/Admin/Customize/CustomizeScriptSetting.tsx
  62. 1 1
      apps/app/src/client/components/Admin/Customize/CustomizeSidebarSetting.tsx
  63. 0 0
      apps/app/src/client/components/Admin/Customize/CustomizeThemeOptions.tsx
  64. 0 0
      apps/app/src/client/components/Admin/Customize/CustomizeThemeSetting.tsx
  65. 1 1
      apps/app/src/client/components/Admin/Customize/CustomizeTitle.tsx
  66. 0 0
      apps/app/src/client/components/Admin/Customize/PagingSizeUncontrolledDropdown.jsx
  67. 0 0
      apps/app/src/client/components/Admin/Customize/ThemeColorBox.module.scss
  68. 0 0
      apps/app/src/client/components/Admin/Customize/ThemeColorBox.tsx
  69. 1 1
      apps/app/src/client/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx
  70. 0 0
      apps/app/src/client/components/Admin/ElasticsearchManagement/NormalizeIndicesControls.tsx
  71. 0 0
      apps/app/src/client/components/Admin/ElasticsearchManagement/RebuildIndexControls.jsx
  72. 0 0
      apps/app/src/client/components/Admin/ElasticsearchManagement/ReconnectControls.tsx
  73. 0 0
      apps/app/src/client/components/Admin/ElasticsearchManagement/StatusTable.jsx
  74. 0 0
      apps/app/src/client/components/Admin/ExportArchiveData/ArchiveFilesTable.tsx
  75. 0 0
      apps/app/src/client/components/Admin/ExportArchiveData/ArchiveFilesTableMenu.tsx
  76. 0 0
      apps/app/src/client/components/Admin/ExportArchiveData/SelectCollectionsModal.tsx
  77. 0 0
      apps/app/src/client/components/Admin/ExportArchiveDataPage.tsx
  78. 0 0
      apps/app/src/client/components/Admin/ForbiddenPage.tsx
  79. 0 0
      apps/app/src/client/components/Admin/FullTextSearchManagement.tsx
  80. 0 0
      apps/app/src/client/components/Admin/G2GDataTransfer.tsx
  81. 0 0
      apps/app/src/client/components/Admin/G2GDataTransferExportForm.tsx
  82. 0 0
      apps/app/src/client/components/Admin/G2GDataTransferStatusIcon.tsx
  83. 0 0
      apps/app/src/client/components/Admin/ImportData/GrowiArchive/ErrorViewer.tsx
  84. 0 0
      apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx
  85. 0 0
      apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionItem.jsx
  86. 0 0
      apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportForm.jsx
  87. 0 0
      apps/app/src/client/components/Admin/ImportData/GrowiArchive/UploadForm.jsx
  88. 0 0
      apps/app/src/client/components/Admin/ImportData/GrowiArchiveSection.jsx
  89. 0 0
      apps/app/src/client/components/Admin/ImportData/ImportDataPageContents.jsx
  90. 0 0
      apps/app/src/client/components/Admin/LegacySlackIntegration/LegacySlackIntegration.jsx
  91. 0 0
      apps/app/src/client/components/Admin/LegacySlackIntegration/SlackConfiguration.jsx
  92. 0 0
      apps/app/src/client/components/Admin/ManageExternalAccount.tsx
  93. 0 0
      apps/app/src/client/components/Admin/MarkdownSetting/IndentForm.tsx
  94. 0 0
      apps/app/src/client/components/Admin/MarkdownSetting/LineBreakForm.jsx
  95. 0 0
      apps/app/src/client/components/Admin/MarkdownSetting/MarkDownSettingContents.tsx
  96. 0 0
      apps/app/src/client/components/Admin/MarkdownSetting/WhitelistInput.tsx
  97. 0 0
      apps/app/src/client/components/Admin/MarkdownSetting/XssForm.jsx
  98. 0 0
      apps/app/src/client/components/Admin/NotFoundPage.tsx
  99. 0 0
      apps/app/src/client/components/Admin/Notification/GlobalNotification.jsx
  100. 0 0
      apps/app/src/client/components/Admin/Notification/GlobalNotificationList.jsx

+ 15 - 7
.github/workflows/reusable-app-prod.yml

@@ -213,7 +213,7 @@ jobs:
       fail-fast: false
       fail-fast: false
       matrix:
       matrix:
         # List string expressions that is comma separated ids of tests in "test/cypress/integration"
         # List string expressions that is comma separated ids of tests in "test/cypress/integration"
-        spec-group: ['20', '21', '22', '23', '30', '50', '60']
+        spec-group: ['20', '21', '23', '30', '50']
 
 
     services:
     services:
       mongodb:
       mongodb:
@@ -425,12 +425,6 @@ jobs:
       run: |
       run: |
         cat config/ci/.env.local.for-auto-install >> .env.production.local
         cat config/ci/.env.local.for-auto-install >> .env.production.local
 
 
-    # - name: Copy dotenv file for automatic installation with allowing guest mode
-    #   if: ${{ matrix.spec-group == '21' }}
-    #   working-directory: ./apps/app
-    #   run: |
-    #     cat config/ci/.env.local.for-auto-install-with-allowing-guest >> .env.production.local
-
     - name: Playwright Run
     - name: Playwright Run
       working-directory: ./apps/app
       working-directory: ./apps/app
       run: |
       run: |
@@ -440,6 +434,20 @@ jobs:
         MONGO_URI: mongodb://mongodb:27017/growi-playwright
         MONGO_URI: mongodb://mongodb:27017/growi-playwright
         ELASTICSEARCH_URI: http://localhost:${{ job.services.elasticsearch.ports['9200'] }}/growi
         ELASTICSEARCH_URI: http://localhost:${{ job.services.elasticsearch.ports['9200'] }}/growi
 
 
+    - name: Copy dotenv file for automatic installation with allowing guest mode
+      working-directory: ./apps/app
+      run: |
+        cat config/ci/.env.local.for-auto-install-with-allowing-guest >> .env.production.local
+
+    - name: Playwright Run (--project=${browser}/guest-mode)
+      working-directory: ./apps/app
+      run: |
+        yarn playwright test --project=${{ matrix.browser }}/guest-mode --shard=${{ matrix.shard }}
+      env:
+        HOME: /root # ref: https://github.com/microsoft/playwright/issues/6500
+        MONGO_URI: mongodb://mongodb:27017/growi-playwright-guest-mode
+        ELASTICSEARCH_URI: http://localhost:${{ job.services.elasticsearch.ports['9200'] }}/growi
+
     - name: Slack Notification
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master
       uses: weseek/ghaction-slack-notification@master
       if: failure()
       if: failure()

+ 1 - 0
apps/app/next-env.d.ts

@@ -1,5 +1,6 @@
 /// <reference types="next" />
 /// <reference types="next" />
 /// <reference types="next/image-types/global" />
 /// <reference types="next/image-types/global" />
+/// <reference types="next/navigation-types/compat/navigation" />
 
 
 // NOTE: This file should not be edited
 // NOTE: This file should not be edited
 // see https://nextjs.org/docs/basic-features/typescript for more information.
 // see https://nextjs.org/docs/basic-features/typescript for more information.

+ 16 - 0
apps/app/next.config.js

@@ -62,6 +62,19 @@ const getTranspilePackages = () => {
   return packages;
   return packages;
 };
 };
 
 
+const optimizePackageImports = [
+  '@growi/core',
+  '@growi/editor',
+  '@growi/pluginkit',
+  '@growi/presentation',
+  '@growi/preset-themes',
+  '@growi/remark-attachment-refs',
+  '@growi/remark-drawio',
+  '@growi/remark-growi-directive',
+  '@growi/remark-lsx',
+  '@growi/slack',
+  '@growi/ui',
+];
 
 
 module.exports = async(phase, { defaultConfig }) => {
 module.exports = async(phase, { defaultConfig }) => {
 
 
@@ -85,6 +98,9 @@ module.exports = async(phase, { defaultConfig }) => {
     transpilePackages: phase !== PHASE_PRODUCTION_SERVER
     transpilePackages: phase !== PHASE_PRODUCTION_SERVER
       ? getTranspilePackages()
       ? getTranspilePackages()
       : undefined,
       : undefined,
+    experimental: {
+      optimizePackageImports,
+    },
 
 
     /** @param config {import('next').NextConfig} */
     /** @param config {import('next').NextConfig} */
     webpack(config, options) {
     webpack(config, options) {

+ 13 - 13
apps/app/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/app",
   "name": "@growi/app",
-  "version": "7.0.10",
+  "version": "7.0.11-RC.0",
   "license": "MIT",
   "license": "MIT",
   "private": "true",
   "private": "true",
   "scripts": {
   "scripts": {
@@ -8,7 +8,7 @@
     "build": "run-p build:*",
     "build": "run-p build:*",
     "start": "yarn next start",
     "start": "yarn next start",
     "build:client": "yarn next build",
     "build:client": "yarn next build",
-    "build:server": "yarn cross-env NODE_ENV=production tsc -p tsconfig.build.server.json && tsc-alias -p tsconfig.build.server-tsc-alias.json",
+    "build:server": "yarn cross-env NODE_ENV=production tspc -p tsconfig.build.server.json",
     "postbuild:server": "shx echo \"Listing files under transpiled\" && shx ls transpiled && shx rm -rf dist && shx mv transpiled/src dist && shx rm -rf transpiled",
     "postbuild:server": "shx echo \"Listing files under transpiled\" && shx ls transpiled && shx rm -rf dist && shx mv transpiled/src dist && shx rm -rf transpiled",
     "clean": "shx rm -rf dist transpiled",
     "clean": "shx rm -rf dist transpiled",
     "server": "yarn cross-env NODE_ENV=production node -r dotenv-flow/config dist/server/app.js",
     "server": "yarn cross-env NODE_ENV=production node -r dotenv-flow/config dist/server/app.js",
@@ -28,16 +28,16 @@
     "cy:run": "cypress run --browser chromium",
     "cy:run": "cypress run --browser chromium",
     "//// for CI": "",
     "//// for CI": "",
     "dev:ci": "yarn cross-env NODE_ENV=development yarn ts-node src/server/app.ts --ci",
     "dev:ci": "yarn cross-env NODE_ENV=development yarn ts-node src/server/app.ts --ci",
-    "lint:typecheck": "npx -y tsc",
+    "lint:typecheck": "npx -y tspc",
     "lint:eslint": "yarn eslint --quiet \"**/*.{js,jsx,ts,tsx}\"",
     "lint:eslint": "yarn eslint --quiet \"**/*.{js,jsx,ts,tsx}\"",
     "lint:styles": "stylelint \"src/**/*.scss\"",
     "lint:styles": "stylelint \"src/**/*.scss\"",
     "lint:swagger2openapi": "node node_modules/.bin/oas-validate tmp/swagger.json",
     "lint:swagger2openapi": "node node_modules/.bin/oas-validate tmp/swagger.json",
     "lint": "run-p lint:*",
     "lint": "run-p lint:*",
     "prelint:swagger2openapi": "yarn openapi:v3",
     "prelint:swagger2openapi": "yarn openapi:v3",
     "test": "run-p test:*",
     "test": "run-p test:*",
-    "test:jest": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=4096\" jest --logHeapUsage",
+    "test:jest": "cross-env NODE_ENV=test TS_NODE_PROJECT=test/integration/tsconfig.json jest",
     "test:vitest": "run-p vitest:run vitest:run:integ vitest:run:components",
     "test:vitest": "run-p vitest:run vitest:run:integ vitest:run:components",
-    "jest:run": "cross-env NODE_ENV=test jest --passWithNoTests -- ",
+    "jest:run": "cross-env NODE_ENV=test TS_NODE_PROJECT=test/integration/tsconfig.json jest --passWithNoTests -- ",
     "reg:run": "reg-suit run",
     "reg:run": "reg-suit run",
     "vitest:run": "vitest run config src --coverage",
     "vitest:run": "vitest run config src --coverage",
     "vitest:run:integ": "vitest run -c vitest.config.integ.ts src --coverage",
     "vitest:run:integ": "vitest run -c vitest.config.integ.ts src --coverage",
@@ -72,6 +72,7 @@
     "@google-cloud/storage": "^5.8.5",
     "@google-cloud/storage": "^5.8.5",
     "@growi/core": "link:../../packages/core",
     "@growi/core": "link:../../packages/core",
     "@growi/pluginkit": "link:../../packages/pluginkit",
     "@growi/pluginkit": "link:../../packages/pluginkit",
+    "@growi/presentation": "link:../../packages/presentation",
     "@growi/preset-templates": "link:../../packages/preset-templates",
     "@growi/preset-templates": "link:../../packages/preset-templates",
     "@growi/preset-themes": "link:../../packages/preset-themes",
     "@growi/preset-themes": "link:../../packages/preset-themes",
     "@growi/remark-attachment-refs": "link:../../packages/remark-attachment-refs",
     "@growi/remark-attachment-refs": "link:../../packages/remark-attachment-refs",
@@ -133,7 +134,7 @@
     "md5": "^2.2.1",
     "md5": "^2.2.1",
     "mermaid": "^10.1.0",
     "mermaid": "^10.1.0",
     "method-override": "^3.0.0",
     "method-override": "^3.0.0",
-    "migrate-mongo": "^8.2.3",
+    "migrate-mongo": "^11.0.0",
     "mkdirp": "^1.0.3",
     "mkdirp": "^1.0.3",
     "mongoose": "^6.11.3",
     "mongoose": "^6.11.3",
     "mongoose-gridfs": "^1.2.42",
     "mongoose-gridfs": "^1.2.42",
@@ -143,6 +144,7 @@
     "multer-autoreap": "^1.0.3",
     "multer-autoreap": "^1.0.3",
     "mustache": "^4.2.0",
     "mustache": "^4.2.0",
     "next": "^14.1.3",
     "next": "^14.1.3",
+    "next-dynamic-loading-props": "^0.1.1",
     "next-i18next": "^15.2.0",
     "next-i18next": "^15.2.0",
     "next-superjson": "^0.0.4",
     "next-superjson": "^0.0.4",
     "next-themes": "^0.2.1",
     "next-themes": "^0.2.1",
@@ -165,8 +167,6 @@
     "react-card-flip": "^1.0.10",
     "react-card-flip": "^1.0.10",
     "react-datepicker": "^4.7.0",
     "react-datepicker": "^4.7.0",
     "react-disable": "^0.1.1",
     "react-disable": "^0.1.1",
-    "react-dnd": "^14.0.5",
-    "react-dnd-html5-backend": "^14.1.0",
     "react-dom": "^18.2.0",
     "react-dom": "^18.2.0",
     "react-error-boundary": "^3.1.4",
     "react-error-boundary": "^3.1.4",
     "react-i18next": "^14.1.0",
     "react-i18next": "^14.1.0",
@@ -176,7 +176,6 @@
     "react-scroll": "^1.8.7",
     "react-scroll": "^1.8.7",
     "react-stickynode": "^4.1.1",
     "react-stickynode": "^4.1.1",
     "react-syntax-highlighter": "^15.5.0",
     "react-syntax-highlighter": "^15.5.0",
-    "react-toastify": "^9.1.3",
     "react-use-ripple": "^1.5.2",
     "react-use-ripple": "^1.5.2",
     "reactstrap": "^9.2.2",
     "reactstrap": "^9.2.2",
     "reconnecting-websocket": "^4.4.0",
     "reconnecting-websocket": "^4.4.0",
@@ -208,7 +207,7 @@
     "url-join": "^4.0.0",
     "url-join": "^4.0.0",
     "usehooks-ts": "^2.6.0",
     "usehooks-ts": "^2.6.0",
     "validator": "^13.7.0",
     "validator": "^13.7.0",
-    "ws": "^8.3.0",
+    "ws": "^8.17.1",
     "xss": "^1.0.14",
     "xss": "^1.0.14",
     "y-mongodb-provider": "^0.1.10",
     "y-mongodb-provider": "^0.1.10",
     "y-socket.io": "^1.1.3",
     "y-socket.io": "^1.1.3",
@@ -223,7 +222,6 @@
     "@growi/core-styles": "link:../../packages/core-styles",
     "@growi/core-styles": "link:../../packages/core-styles",
     "@growi/custom-icons": "link:../../packages/custom-icons",
     "@growi/custom-icons": "link:../../packages/custom-icons",
     "@growi/editor": "link:../../packages/editor",
     "@growi/editor": "link:../../packages/editor",
-    "@growi/presentation": "link:../../packages/presentation",
     "@growi/ui": "link:../../packages/ui",
     "@growi/ui": "link:../../packages/ui",
     "@handsontable/react": "=2.1.0",
     "@handsontable/react": "=2.1.0",
     "@next/bundle-analyzer": "^14.1.3",
     "@next/bundle-analyzer": "^14.1.3",
@@ -272,9 +270,12 @@
     "pretty-bytes": "^6.1.1",
     "pretty-bytes": "^6.1.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",
+    "react-dnd": "^14.0.5",
+    "react-dnd-html5-backend": "^14.1.0",
     "react-dropzone": "^14.2.3",
     "react-dropzone": "^14.2.3",
     "react-hotkeys": "^2.0.0",
     "react-hotkeys": "^2.0.0",
     "react-input-autosize": "^3.0.0",
     "react-input-autosize": "^3.0.0",
+    "react-toastify": "^9.1.3",
     "rehype-rewrite": "^3.0.6",
     "rehype-rewrite": "^3.0.6",
     "replacestream": "^4.0.3",
     "replacestream": "^4.0.3",
     "sass": "^1.53.0",
     "sass": "^1.53.0",
@@ -282,7 +283,6 @@
     "simplebar-react": "^2.3.6",
     "simplebar-react": "^2.3.6",
     "socket.io-client": "^4.7.5",
     "socket.io-client": "^4.7.5",
     "source-map-loader": "^4.0.1",
     "source-map-loader": "^4.0.1",
-    "swagger2openapi": "^7.0.8",
-    "tsc-alias": "^1.2.9"
+    "swagger2openapi": "^7.0.8"
   }
   }
 }
 }

+ 25 - 25
apps/app/playwright.config.ts

@@ -1,10 +1,28 @@
 import fs from 'node:fs';
 import fs from 'node:fs';
 import path from 'node:path';
 import path from 'node:path';
 
 
-import { defineConfig, devices } from '@playwright/test';
+import { defineConfig, devices, type Project } from '@playwright/test';
 
 
 const authFile = path.resolve(__dirname, './playwright/.auth/admin.json');
 const authFile = path.resolve(__dirname, './playwright/.auth/admin.json');
 
 
+// Use prepared auth state.
+const storageState = fs.existsSync(authFile) ? authFile : undefined;
+
+const supportedBrowsers = ['chromium', 'firefox', 'webkit'] as const;
+
+const projects: Array<Project> = supportedBrowsers.map(browser => ({
+  name: browser,
+  use: { ...devices[`Desktop ${browser}`], storageState },
+  testIgnore: /(10-installer|21-basic-features-for-guest)\/.*\.spec\.ts/,
+  dependencies: ['setup', 'auth'],
+}));
+
+const projectsForGuestMode: Array<Project> = supportedBrowsers.map(browser => ({
+  name: `${browser}/guest-mode`,
+  use: { ...devices[`Desktop ${browser}`] }, // Do not use storageState
+  testMatch: /21-basic-features-for-guest\/.*\.spec\.ts/,
+}));
+
 /**
 /**
  * Read environment variables from file.
  * Read environment variables from file.
  * https://github.com/motdotla/dotenv
  * https://github.com/motdotla/dotenv
@@ -15,7 +33,9 @@ const authFile = path.resolve(__dirname, './playwright/.auth/admin.json');
  * See https://playwright.dev/docs/test-configuration.
  * See https://playwright.dev/docs/test-configuration.
  */
  */
 export default defineConfig({
 export default defineConfig({
-  timeout: 7 * 1000,
+  expect: {
+    timeout: 7 * 1000,
+  },
 
 
   testDir: './playwright',
   testDir: './playwright',
   outputDir: './playwright/output',
   outputDir: './playwright/output',
@@ -47,9 +67,6 @@ export default defineConfig({
     trace: 'on-first-retry',
     trace: 'on-first-retry',
 
 
     viewport: { width: 1400, height: 1024 },
     viewport: { width: 1400, height: 1024 },
-
-    // Use prepared auth state.
-    storageState: fs.existsSync(authFile) ? authFile : undefined,
   },
   },
 
 
   /* Configure projects for major browsers */
   /* Configure projects for major browsers */
@@ -60,31 +77,14 @@ export default defineConfig({
 
 
     {
     {
       name: 'chromium/installer',
       name: 'chromium/installer',
-      use: { ...devices['Desktop Chrome'] },
+      use: { ...devices['Desktop Chrome'], storageState },
       testMatch: /10-installer\/.*\.spec\.ts/,
       testMatch: /10-installer\/.*\.spec\.ts/,
       dependencies: ['setup'],
       dependencies: ['setup'],
     },
     },
 
 
-    {
-      name: 'chromium',
-      use: { ...devices['Desktop Chrome'] },
-      testIgnore: /10-installer\/.*\.spec\.ts/,
-      dependencies: ['setup', 'auth'],
-    },
-
-    {
-      name: 'firefox',
-      use: { ...devices['Desktop Firefox'] },
-      testIgnore: /10-installer\/.*\.spec\.ts/,
-      dependencies: ['setup', 'auth'],
-    },
+    ...projects,
 
 
-    {
-      name: 'webkit',
-      use: { ...devices['Desktop Safari'] },
-      testIgnore: /10-installer\/.*\.spec\.ts/,
-      dependencies: ['setup', 'auth'],
-    },
+    ...projectsForGuestMode,
 
 
     /* Test against mobile viewports. */
     /* Test against mobile viewports. */
     // {
     // {

+ 30 - 0
apps/app/playwright/20-basic-features/access-to-pagelist.spec.ts

@@ -0,0 +1,30 @@
+import { test, expect, type Page } from '@playwright/test';
+
+const openPageAccessoriesModal = async(page: Page): Promise<void> => {
+  await page.goto('/');
+  await page.getByTestId('pageListButton').click();
+  await expect(page.getByTestId('descendants-page-list-modal')).toBeVisible();
+};
+
+test('Page list modal is successfully opened', async({ page }) => {
+  await openPageAccessoriesModal(page);
+  await expect(page.getByTestId('page-list-item-L').first()).not.toContainText('You cannot see this page');
+});
+
+test('Successfully open PageItemControl', async({ page }) => {
+  await openPageAccessoriesModal(page);
+  await page.getByTestId('page-list-item-L').first().getByTestId('open-page-item-control-btn').click();
+  await expect(page.locator('.dropdown-menu.show')).toBeVisible();
+});
+
+test('Successfully close modal', async({ page }) => {
+  await openPageAccessoriesModal(page);
+  await page.locator('.btn-close').click();
+  await expect(page.getByTestId('descendants-page-list-modal')).not.toBeVisible();
+});
+
+test('Timeline list successfully openend', async({ page }) => {
+  await openPageAccessoriesModal(page);
+  await page.getByTestId('timeline-tab-button').click();
+  await expect(page.locator('.card-timeline').first()).toBeVisible();
+});

+ 50 - 0
apps/app/playwright/20-basic-features/click-page-icons.spec.ts

@@ -0,0 +1,50 @@
+import { test, expect } from '@playwright/test';
+
+test.describe('Click page icons', () => {
+  test.beforeEach(async({ page }) => {
+    await page.goto('/Sandbox');
+  });
+
+  test('Successfully Subscribe/Unsubscribe a page', async({ page }) => {
+    const subscribeButton = page.locator('.btn-subscribe');
+
+    // Subscribe
+    await subscribeButton.click();
+    await expect(subscribeButton).toHaveClass(/active/);
+
+    // Unsubscribe
+    await subscribeButton.click();
+    await expect(subscribeButton).not.toHaveClass(/active/);
+  });
+
+  test('Successfully Like/Unlike a page', async({ page }) => {
+    const likeButton = page.locator('.btn-like').first();
+
+    // Like
+    await likeButton.click();
+    await expect(likeButton).toHaveClass(/active/);
+
+    // Unlike
+    await likeButton.click();
+    await expect(likeButton).not.toHaveClass(/active/);
+  });
+
+  test('Successfully Bookmark / Unbookmark a page', async({ page }) => {
+    const bookmarkButton = page.locator('.btn-bookmark').first();
+
+    // Bookmark
+    await bookmarkButton.click();
+    await expect(bookmarkButton).toHaveClass(/active/);
+
+    // Unbookmark
+    await page.locator('.grw-bookmark-folder-menu-item').click();
+    await expect(bookmarkButton).not.toHaveClass(/active/);
+  });
+
+  test('Successfully display list of "seen by user"', async({ page }) => {
+    await page.locator('.btn-seen-user').click();
+
+    const imgCount = await page.locator('.user-list-content').locator('img').count();
+    expect(imgCount).toBe(1);
+  });
+});

+ 49 - 0
apps/app/playwright/20-basic-features/comments.spec.ts

@@ -0,0 +1,49 @@
+import { test, expect } from '@playwright/test';
+
+test('Create comment page', async({ page }) => {
+  await page.goto('/comment');
+  await page.getByTestId('editor-button').click();
+  await page.getByTestId('save-page-btn').click();
+  await expect(page.locator('.page-meta')).toBeVisible();
+});
+
+test('Successfully add comments', async({ page }) => {
+  const commentText = 'add comment';
+  await page.goto('/comment');
+
+  // Add comment
+  await page.getByTestId('page-comment-button').click();
+  await page.getByTestId('open-comment-editor-button').click();
+  await page.locator('.cm-content').fill(commentText);
+  await page.getByTestId('comment-submit-button').first().click();
+
+  await expect(page.locator('.page-comment-body')).toHaveText(commentText);
+  await expect(page.getByTestId('page-comment-button').locator('.grw-count-badge')).toHaveText('1');
+});
+
+test('Successfully reply comments', async({ page }) => {
+  const commentText = 'reply comment';
+  await page.goto('/comment');
+
+  // Reply comment
+  await page.getByTestId('page-comment-button').click();
+  await page.getByTestId('comment-reply-button').click();
+  await page.locator('.cm-content').fill(commentText);
+  await page.getByTestId('comment-submit-button').first().click();
+
+  await expect(page.locator('.page-comment-body').nth(1)).toHaveText(commentText);
+  await expect(page.getByTestId('page-comment-button').locator('.grw-count-badge')).toHaveText('2');
+});
+
+// test('Successfully delete comments', async({ page }) => {
+//   await page.goto('/comment');
+
+//   await page.getByTestId('page-comment-button').click();
+//   await page.getByTestId('comment-delete-button').first().click({ force: true });
+//   await expect(page.getByTestId('page-comment-delete-modal')).toBeVisible();
+//   await page.getByTestId('delete-comment-button').click();
+
+//   await expect(page.getByTestId('page-comment-button').locator('.grw-count-badge')).toHaveText('0');
+// });
+
+// TODO: https://redmine.weseek.co.jp/issues/139520

+ 47 - 0
apps/app/playwright/20-basic-features/sticky-features.spec.ts

@@ -0,0 +1,47 @@
+import { test, expect } from '@playwright/test';
+
+test.describe('Sticky features', () => {
+  test.beforeEach(async({ page }) => {
+    await page.goto('/');
+  });
+
+  test('Subnavigation displays changes on scroll down and up', async({ page }) => {
+    // Scroll down to trigger sticky effect
+    await page.evaluate(() => window.scrollTo(0, 250));
+    await expect(page.locator('.sticky-outer-wrapper').first()).toHaveClass(/active/);
+
+    // Scroll back to top
+    await page.evaluate(() => window.scrollTo(0, 0));
+    await expect(page.locator('.sticky-outer-wrapper').first()).not.toHaveClass(/active/);
+  });
+
+  test('Subnavigation is not displayed when move to other pages', async({ page }) => {
+    // Scroll down to trigger sticky effect
+    await page.evaluate(() => window.scrollTo(0, 250));
+    await expect(page.locator('.sticky-outer-wrapper').first()).toHaveClass(/active/);
+
+    // Move to /Sandbox page
+    await page.goto('/Sandbox');
+    await expect(page.locator('.sticky-outer-wrapper').first()).not.toHaveClass(/active/);
+  });
+
+  test('Able to click buttons on subnavigation switcher when sticky', async({ page }) => {
+    // Scroll down to trigger sticky effect
+    await page.evaluate(() => window.scrollTo(0, 250));
+    await expect(page.locator('.sticky-outer-wrapper').first()).toHaveClass(/active/);
+
+    // Click editor button
+    await page.getByTestId('editor-button').click();
+    await expect(page.locator('.layout-root')).toHaveClass(/editing/);
+  });
+
+  test('Subnavigation is sticky when on small window', async({ page }) => {
+    // Scroll down to trigger sticky effect
+    await page.evaluate(() => window.scrollTo(0, 500));
+    await expect(page.locator('.sticky-outer-wrapper').first()).toHaveClass(/active/);
+
+    // Set viewport to small size
+    await page.setViewportSize({ width: 600, height: 1024 });
+    await expect(page.getByTestId('grw-contextual-sub-nav').getByTestId('grw-page-editor-mode-manager')).toBeVisible();
+  });
+});

+ 45 - 0
apps/app/playwright/21-basic-features-for-guest/access-to-page.spec.ts

@@ -0,0 +1,45 @@
+import { test, expect } from '@playwright/test';
+
+import { collapseSidebar } from '../utils';
+
+test('/Sandbox is successfully loaded', async({ page }) => {
+
+  await page.goto('/Sandbox');
+
+  // Expect a title "to contain" a substring.
+  await expect(page).toHaveTitle(/Sandbox/);
+});
+
+test('/Sandbox/math is successfully loaded', async({ page }) => {
+
+  await page.goto('/Sandbox/Math');
+
+  // Check if the math elements are visible
+  await expect(page.locator('.math').first()).toBeVisible();
+});
+
+test('Access to /me page', async({ page }) => {
+  await page.goto('/me');
+
+  // Expect to be redirected to /login when accessing /me
+  await expect(page.getByTestId('login-form')).toBeVisible();
+});
+
+test('Access to /trash page', async({ page }) => {
+  await page.goto('/trash');
+
+  // Expect the trash page specific elements to be present when accessing /trash
+  await expect(page.getByTestId('trash-page-list')).toBeVisible();
+});
+
+// TODO: Improve collapseSidebar (https://redmine.weseek.co.jp/issues/148538)
+// test('Access to /tags page', async({ page }) => {
+//   await page.goto('/tags');
+
+//   await collapseSidebar(page, false);
+//   await page.getByTestId('grw-sidebar-nav-primary-tags').click();
+//   await expect(page.getByTestId('grw-sidebar-content-tags')).toBeVisible();
+//   await expect(page.getByTestId('grw-tags-list').first()).toBeVisible();
+//   await expect(page.getByTestId('grw-tags-list').first()).toContainText('You have no tag, You can set tags on pages');
+//   await expect(page.getByTestId('tags-page')).toBeVisible();
+// });

+ 14 - 0
apps/app/playwright/21-basic-features-for-guest/sticky-for-guest.spec.ts

@@ -0,0 +1,14 @@
+import { test, expect } from '@playwright/test';
+
+
+test('Sub navigation sticky changes when scrolling down and up', async({ page }) => {
+  await page.goto('/Sandbox');
+
+  // Sticky
+  await page.evaluate(() => window.scrollTo(0, 250));
+  await expect(page.locator('.sticky-outer-wrapper').first()).toHaveClass(/active/);
+
+  // Not sticky
+  await page.evaluate(() => window.scrollTo(0, 0));
+  await expect(page.locator('.sticky-outer-wrapper').first()).not.toHaveClass(/active/);
+});

+ 37 - 0
apps/app/playwright/22-sharelink/access-to-sharelink.spec.ts

@@ -0,0 +1,37 @@
+import { test, expect } from '@playwright/test';
+
+import { login } from '../utils/Login';
+
+test.describe.serial('Access to sharelink by guest', () => {
+  let createdSharelink: string | null;
+
+  test('Prepare sharelink', async({ page }) => {
+    await page.goto('/Sandbox/Bootstrap5');
+
+    // Create Sharelink
+    await page.getByTestId('open-page-item-control-btn').click();
+    await page.getByTestId('open-page-accessories-modal-btn-with-share-link-management-data-tab').click();
+    await page.getByTestId('btn-sharelink-toggleform').click();
+    await page.getByTestId('btn-sharelink-issue').click();
+
+    // Get ShareLink
+    createdSharelink = await page.getByTestId('share-link').textContent();
+    expect(createdSharelink).toHaveLength(24);
+  });
+
+  test('The sharelink page is successfully loaded', async({ page }) => {
+    await page.goto('/');
+
+    // Logout
+    await page.getByTestId('personal-dropdown-button').click();
+    await expect(page.getByTestId('logout-button')).toBeVisible();
+    await page.getByTestId('logout-button').click();
+    await page.waitForURL('http://localhost:3000/login');
+
+    // Access sharelink
+    await page.goto(`/share/${createdSharelink}`);
+    await expect(page.locator('.page-meta')).toBeVisible();
+
+    await login(page);
+  });
+});

+ 122 - 0
apps/app/playwright/60-home/home.spec.ts

@@ -0,0 +1,122 @@
+import { test, expect } from '@playwright/test';
+
+
+test('Visit User home', async({ page }) => {
+  await page.goto('dummy');
+
+  // Open PersonalDropdown
+  await page.getByTestId('personal-dropdown-button').click();
+  await expect(page.getByTestId('grw-personal-dropdown-menu-user-home')).toBeVisible();
+
+  // Click UserHomeMenu
+  await page.getByTestId('grw-personal-dropdown-menu-user-home').click();
+  await expect(page.getByTestId('grw-users-info')).toBeVisible();
+});
+
+test('Vist User settings', async({ page }) => {
+  await page.goto('dummy');
+
+  // Open PersonalDropdown
+  await page.getByTestId('personal-dropdown-button').click();
+  await expect(page.getByTestId('grw-personal-dropdown-menu-user-home')).toBeVisible();
+
+  // Click UserSettingsMenu
+  page.getByTestId('grw-personal-dropdown-menu-user-settings').click();
+  await expect(page.getByTestId('grw-user-settings')).toBeVisible();
+});
+
+test('Open questionnaire modal', async({ page }) => {
+  await page.goto('/dummy');
+
+  // Open PersonalDropdown
+  await page.getByTestId('personal-dropdown-button').click();
+  await expect(page.getByTestId('grw-personal-dropdown-menu-user-home')).toBeVisible();
+
+  // Expect the questionnaire modal to be displayed when the QuestionnaireModalToggleButton is clicked
+  await page.getByTestId('grw-proactive-questionnaire-modal-toggle-btn').click();
+  await expect(page.getByTestId('grw-proactive-questionnaire-modal')).toBeVisible();
+});
+
+test('Access User information', async({ page }) => {
+  await page.goto('/me');
+
+  // Click BasicInfoSettingUpdateButton
+  await expect(page.getByTestId('grw-user-settings')).toBeVisible();
+
+  // Expect a success toaster to be displayed when the BasicInfoSettingUpdateButton is pressed
+  await page.getByTestId('grw-besic-info-settings-update-button').click();
+  await expect(page.locator('.Toastify__toast')).toBeVisible();
+});
+
+test('Access External account', async({ page }) => {
+  await page.goto('/me');
+
+  // Click ExternalAccountsTabButton
+  await expect(page.getByTestId('grw-user-settings')).toBeVisible();
+  await page.getByTestId('external-accounts-tab-button').first().click();
+
+  // Expect an error toaster to be displayed when the AddExternalAccountsButton is pressed
+  await page.getByTestId('grw-external-account-add-button').click();
+  await expect(page.getByTestId('grw-associate-modal')).toBeVisible();
+  await page.getByTestId('add-external-account-button').click();
+  await expect(page.locator('.Toastify__toast')).toBeVisible();
+  await page.locator('.Toastify__close-button').click();
+  await expect(page.locator('.Toastify__toast')).not.toBeVisible();
+});
+
+test('Access Password setting', async({ page }) => {
+  await page.goto('/me');
+
+  // Click PasswordSettingTabButton
+  await expect(page.getByTestId('grw-user-settings')).toBeVisible();
+  await page.getByTestId('password-settings-tab-button').first().click();
+
+  // Expect three error toasters to be displayed when the PasswordUpdateButton is pressed
+  await page.getByTestId('grw-password-settings-update-button').click();
+  const toastElements = page.locator('.Toastify__toast');
+
+  const toastElementsCount = await toastElements.count();
+  for (let i = 0; i < toastElementsCount; i++) {
+    // eslint-disable-next-line no-await-in-loop
+    await toastElements.nth(i).click();
+  }
+
+  await expect(page.getByTestId('.Toastify__toast')).not.toBeVisible();
+});
+
+
+test('Access API setting', async({ page }) => {
+  await page.goto('/me');
+
+  // Click ApiSettingTabButton
+  await expect(page.getByTestId('grw-user-settings')).toBeVisible();
+  await page.getByTestId('api-settings-tab-button').first().click();
+
+  // Expect a success toaster to be displayed when the UpdateApiTokenButton is clicked
+  await page.getByTestId('grw-api-settings-update-button').click();
+  await expect(page.locator('.Toastify__toast')).toBeVisible();
+});
+
+test('Access In-App Notification setting', async({ page }) => {
+  await page.goto('/me');
+
+  // Click InAppNotificationSettingTabButton
+  await expect(page.getByTestId('grw-user-settings')).toBeVisible();
+  await page.getByTestId('in-app-notification-settings-tab-button').first().click();
+
+  // Expect a success toaster to be displayed when the InAppNotificationSettingsUpdateButton is clicked
+  await page.getByTestId('grw-in-app-notification-settings-update-button').click();
+  await expect(page.locator('.Toastify__toast')).toBeVisible();
+});
+
+test('Acccess Other setting', async({ page }) => {
+  await page.goto('/me');
+
+  // Click OtherSettingTabButton
+  await expect(page.getByTestId('grw-user-settings')).toBeVisible();
+  await page.getByTestId('other-settings-tab-button').first().click();
+
+  // Expect a success toaster to be displayed when the QuestionnaireSettingsUpdateButton is clicked
+  await page.getByTestId('grw-questionnaire-settings-update-btn').click();
+  await expect(page.locator('.Toastify__toast')).toBeVisible();
+});

+ 5 - 20
apps/app/playwright/auth.setup.ts

@@ -1,24 +1,9 @@
-import path from 'node:path';
+import { test as setup } from '@playwright/test';
 
 
-import { test as setup, expect } from '@playwright/test';
-
-const authFile = path.resolve(__dirname, './.auth/admin.json');
+import { login } from './utils/Login';
 
 
+// Commonised login process for use elsewhere
+// see: https://github.com/microsoft/playwright/issues/22114
 setup('Authenticate as the "admin" user', async({ page }) => {
 setup('Authenticate as the "admin" user', async({ page }) => {
-  // Perform authentication steps. Replace these actions with your own.
-  await page.goto('/admin');
-
-  const loginForm = await page.$('form#login-form');
-
-  if (loginForm != null) {
-    await page.getByLabel('Username or E-mail').fill('admin');
-    await page.getByLabel('Password').fill('adminadmin');
-    await page.locator('[type=submit]').filter({ hasText: 'Login' }).click();
-  }
-
-  await page.waitForURL('/admin');
-  await expect(page).toHaveTitle(/Wiki Management Homepage/);
-
-  // End of authentication steps.
-  await page.context().storageState({ path: authFile });
+  await login(page);
 });
 });

+ 21 - 0
apps/app/playwright/utils/CollapseSidebar.ts

@@ -0,0 +1,21 @@
+// TODO: https://redmine.weseek.co.jp/issues/148538
+import { expect, type Page } from '@playwright/test';
+
+export const collapseSidebar = async(page: Page, isCollapsed: boolean): Promise<void> => {
+  const isSidebarContentsHidden = !(await page.getByTestId('grw-sidebar-contents').isVisible());
+  if (isSidebarContentsHidden === isCollapsed) {
+    return;
+  }
+
+  const collapseSidebarToggle = page.getByTestId('btn-toggle-collapse');
+  await expect(collapseSidebarToggle).toBeVisible();
+
+  await collapseSidebarToggle.click();
+
+  if (isCollapsed) {
+    await expect(page.locator('.grw-sidebar-dock')).not.toBeVisible();
+  }
+  else {
+    await expect(page.locator('.grw-sidebar-dock')).toBeVisible();
+  }
+};

+ 24 - 0
apps/app/playwright/utils/Login.ts

@@ -0,0 +1,24 @@
+import path from 'node:path';
+
+import { expect, type Page } from '@playwright/test';
+
+const authFile = path.resolve(__dirname, '../.auth/admin.json');
+
+export const login = async(page: Page): Promise<void> => {
+  // Perform authentication steps. Replace these actions with your own.
+  await page.goto('/admin');
+
+  const loginForm = await page.$('form#login-form');
+
+  if (loginForm != null) {
+    await page.getByLabel('Username or E-mail').fill('admin');
+    await page.getByLabel('Password').fill('adminadmin');
+    await page.locator('[type=submit]').filter({ hasText: 'Login' }).click();
+  }
+
+  await page.waitForURL('/admin');
+  await expect(page).toHaveTitle(/Wiki Management Homepage/);
+
+  // End of authentication steps.
+  await page.context().storageState({ path: authFile });
+};

+ 2 - 0
apps/app/playwright/utils/index.ts

@@ -0,0 +1,2 @@
+export * from './CollapseSidebar';
+export * from './Login';

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

@@ -671,10 +671,9 @@
     "Email format is invalid": "Email format is invalid.",
     "Email format is invalid": "Email format is invalid.",
     "Email field is required": "Email field is required.",
     "Email field is required": "Email field is required.",
     "Password has invalid character": "Password has invalid character.",
     "Password has invalid character": "Password has invalid character.",
-    "Password minimum character should be more than 8 characters": "Password minimum character should be more than 8 characters.",
+    "Password minimum character should be more than n characters": "Password minimum character should be more than {{number}} characters.",
     "Password field is required": "Password field is required.",
     "Password field is required": "Password field is required.",
     "Username or E-mail has invalid characters": "Username or E-mail has invalid characters.",
     "Username or E-mail has invalid characters": "Username or E-mail has invalid characters.",
-    "Password minimum character should be more than 6 characters": "Password minimum character should be more than 6 characters.",
     "user_not_found": "User not found.",
     "user_not_found": "User not found.",
     "provider_duplicated_username_exception": "<p><strong><span class='material-symbols-outlined me-1'>cancel</span>DuplicatedUsernameException occured</strong></p><p class='mb-0'> Your {{ failedProviderForDuplicatedUsernameException }} authentication was succeeded, but a new user could not be created. See the issue <a href='https://github.com/weseek/growi/issues/193'>#193</a>.</p>"
     "provider_duplicated_username_exception": "<p><strong><span class='material-symbols-outlined me-1'>cancel</span>DuplicatedUsernameException occured</strong></p><p class='mb-0'> Your {{ failedProviderForDuplicatedUsernameException }} authentication was succeeded, but a new user could not be created. See the issue <a href='https://github.com/weseek/growi/issues/193'>#193</a>.</p>"
   },
   },

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

@@ -665,10 +665,9 @@
     "Email format is invalid": "Format d'adresse courriel invalide.",
     "Email format is invalid": "Format d'adresse courriel invalide.",
     "Email field is required": "Adresse courriel requise.",
     "Email field is required": "Adresse courriel requise.",
     "Password has invalid character": "Le mot de passe contient des caractères invalides",
     "Password has invalid character": "Le mot de passe contient des caractères invalides",
-    "Password minimum character should be more than 8 characters": "Le mot de passe doit contenir plus de 8 caractères.",
+    "Password minimum character should be more than n characters": "Le mot de passe doit contenir plus de {{number}} caractères.",
     "Password field is required": "Mot de passe requis.",
     "Password field is required": "Mot de passe requis.",
     "Username or E-mail has invalid characters": "Le nom d'utilisateur ou l'adresse courriel contient des caractères invalides",
     "Username or E-mail has invalid characters": "Le nom d'utilisateur ou l'adresse courriel contient des caractères invalides",
-    "Password minimum character should be more than 6 characters": "Le mot de passe doit contenir au moins 6 caractères.",
     "user_not_found": "Utilisateur introuvable.",
     "user_not_found": "Utilisateur introuvable.",
     "provider_duplicated_username_exception": "<p><strong><span class='material-symbols-outlined me-1'>cancel</span>DuplicatedUsernameException</strong></p><p class='mb-0'> L'authentification est réussie pour {{ failedProviderForDuplicatedUsernameException }} , mais la création d'un utilisateur a échouée. Voir <a href='https://github.com/weseek/growi/issues/193'>#193</a>.</p>"
     "provider_duplicated_username_exception": "<p><strong><span class='material-symbols-outlined me-1'>cancel</span>DuplicatedUsernameException</strong></p><p class='mb-0'> L'authentification est réussie pour {{ failedProviderForDuplicatedUsernameException }} , mais la création d'un utilisateur a échouée. Voir <a href='https://github.com/weseek/growi/issues/193'>#193</a>.</p>"
   },
   },

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

@@ -704,10 +704,9 @@
     "Email format is invalid": "メールアドレスのフォーマットが無効です",
     "Email format is invalid": "メールアドレスのフォーマットが無効です",
     "Email field is required": "メールアドレスは必須項目です",
     "Email field is required": "メールアドレスは必須項目です",
     "Password has invalid character": "パスワードに無効な文字があります",
     "Password has invalid character": "パスワードに無効な文字があります",
-    "Password minimum character should be more than 8 characters": "パスワードの最小文字数は8文字以上です",
+    "Password minimum character should be more than n characters": "パスワードの最小文字数は{{number}}文字以上です",
     "Password field is required": "パスワードの欄は必ず入力してください",
     "Password field is required": "パスワードの欄は必ず入力してください",
     "Username or E-mail has invalid characters": "ユーザー名または、メールアドレスに無効な文字があります",
     "Username or E-mail has invalid characters": "ユーザー名または、メールアドレスに無効な文字があります",
-    "Password minimum character should be more than 6 characters": "パスワードの最小文字数は6文字以上です",
     "user_not_found": "ユーザーが見つかりません",
     "user_not_found": "ユーザーが見つかりません",
     "provider_duplicated_username_exception": "<p><strong><span class='material-symbols-outlined me-1'>cancel</span>エラー: DuplicatedUsernameException</strong></p><p class='mb-0'> {{ failedProviderForDuplicatedUsernameException }} 認証は成功しましたが、新しいユーザーを作成できませんでした。詳しくは<a href='https://github.com/weseek/growi/issues/193'>こちら: #193</a>.</p>"
     "provider_duplicated_username_exception": "<p><strong><span class='material-symbols-outlined me-1'>cancel</span>エラー: DuplicatedUsernameException</strong></p><p class='mb-0'> {{ failedProviderForDuplicatedUsernameException }} 認証は成功しましたが、新しいユーザーを作成できませんでした。詳しくは<a href='https://github.com/weseek/growi/issues/193'>こちら: #193</a>.</p>"
   },
   },

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

@@ -674,10 +674,9 @@
     "Email format is invalid": "电子邮件的格式是无效的",
     "Email format is invalid": "电子邮件的格式是无效的",
     "Email field is required": "电子邮件字段是必需的",
     "Email field is required": "电子邮件字段是必需的",
     "Password has invalid character": "密码有无效字符",
     "Password has invalid character": "密码有无效字符",
-    "Password minimum character should be more than 8 characters": "密码最小字符应超过8个字符",
+    "Password minimum character should be more than n characters": "密码最小字符应超过{{number}}个字符",
     "Password field is required": "密码字段是必需的",
     "Password field is required": "密码字段是必需的",
     "Username or E-mail has invalid characters": "用户名或电子邮件有无效的字符",
     "Username or E-mail has invalid characters": "用户名或电子邮件有无效的字符",
-    "Password minimum character should be more than 6 characters": "密码最小字符应超过6个字符",
     "user_not_found": "未找到用户",
     "user_not_found": "未找到用户",
     "provider_duplicated_username_exception": "<p><strong><span class='material-symbols-outlined me-1'>cancel</span>发生了重复用户名异常</strong></p><p class='mb-0'> 你的 {{ failedProviderForDuplicatedUsernameException }} 认证成功了,但不能创建新的用户。参见问题<a href='https://github.com/weseek/growi/issues/193'>#193</a>.</p>"
     "provider_duplicated_username_exception": "<p><strong><span class='material-symbols-outlined me-1'>cancel</span>发生了重复用户名异常</strong></p><p class='mb-0'> 你的 {{ failedProviderForDuplicatedUsernameException }} 认证成功了,但不能创建新的用户。参见问题<a href='https://github.com/weseek/growi/issues/193'>#193</a>.</p>"
   },
   },

+ 5 - 0
apps/app/src/client/components/.eslintrc.js

@@ -0,0 +1,5 @@
+module.exports = {
+  extends: '../../../.eslintrc.js',
+  rules: {
+  },
+};

+ 0 - 0
apps/app/src/components/Admin/AdminHome/AdminHome.jsx → apps/app/src/client/components/Admin/AdminHome/AdminHome.jsx


+ 0 - 0
apps/app/src/components/Admin/AdminHome/EnvVarsTable.tsx → apps/app/src/client/components/Admin/AdminHome/EnvVarsTable.tsx


+ 0 - 0
apps/app/src/components/Admin/AdminHome/SystemInfomationTable.tsx → apps/app/src/client/components/Admin/AdminHome/SystemInfomationTable.tsx


+ 0 - 0
apps/app/src/components/Admin/App/AppSetting.jsx → apps/app/src/client/components/Admin/App/AppSetting.jsx


+ 0 - 0
apps/app/src/components/Admin/App/AppSettingsPageContents.tsx → apps/app/src/client/components/Admin/App/AppSettingsPageContents.tsx


+ 0 - 0
apps/app/src/components/Admin/App/AwsSetting.tsx → apps/app/src/client/components/Admin/App/AwsSetting.tsx


+ 0 - 0
apps/app/src/components/Admin/App/AzureSetting.tsx → apps/app/src/client/components/Admin/App/AzureSetting.tsx


+ 0 - 0
apps/app/src/components/Admin/App/ConfirmModal.tsx → apps/app/src/client/components/Admin/App/ConfirmModal.tsx


+ 0 - 0
apps/app/src/components/Admin/App/FileUploadSetting.tsx → apps/app/src/client/components/Admin/App/FileUploadSetting.tsx


+ 0 - 0
apps/app/src/components/Admin/App/GcsSetting.tsx → apps/app/src/client/components/Admin/App/GcsSetting.tsx


+ 0 - 0
apps/app/src/components/Admin/App/MailSetting.tsx → apps/app/src/client/components/Admin/App/MailSetting.tsx


+ 0 - 0
apps/app/src/components/Admin/App/MaintenanceMode.tsx → apps/app/src/client/components/Admin/App/MaintenanceMode.tsx


+ 0 - 0
apps/app/src/components/Admin/App/MaskedInput.module.scss → apps/app/src/client/components/Admin/App/MaskedInput.module.scss


+ 0 - 0
apps/app/src/components/Admin/App/MaskedInput.tsx → apps/app/src/client/components/Admin/App/MaskedInput.tsx


+ 0 - 0
apps/app/src/components/Admin/App/QuestionnaireSettings.tsx → apps/app/src/client/components/Admin/App/QuestionnaireSettings.tsx


+ 0 - 0
apps/app/src/components/Admin/App/SesSetting.tsx → apps/app/src/client/components/Admin/App/SesSetting.tsx


+ 0 - 0
apps/app/src/components/Admin/App/SiteUrlSetting.tsx → apps/app/src/client/components/Admin/App/SiteUrlSetting.tsx


+ 0 - 0
apps/app/src/components/Admin/App/SmtpSetting.tsx → apps/app/src/client/components/Admin/App/SmtpSetting.tsx


+ 1 - 1
apps/app/src/components/Admin/App/V5PageMigration.tsx → apps/app/src/client/components/Admin/App/V5PageMigration.tsx

@@ -10,7 +10,7 @@ import {
 } from '~/interfaces/websocket';
 } from '~/interfaces/websocket';
 import { useGlobalAdminSocket } from '~/stores/websocket';
 import { useGlobalAdminSocket } from '~/stores/websocket';
 
 
-import AdminAppContainer from '../../../client/services/AdminAppContainer';
+import AdminAppContainer from '../../../services/AdminAppContainer';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import LabeledProgressBar from '../Common/LabeledProgressBar';
 import LabeledProgressBar from '../Common/LabeledProgressBar';
 
 

+ 0 - 0
apps/app/src/components/Admin/AuditLog/ActivityTable.tsx → apps/app/src/client/components/Admin/AuditLog/ActivityTable.tsx


+ 0 - 0
apps/app/src/components/Admin/AuditLog/AuditLogDisableMode.tsx → apps/app/src/client/components/Admin/AuditLog/AuditLogDisableMode.tsx


+ 1 - 1
apps/app/src/components/Admin/AuditLog/AuditLogSettings.tsx → apps/app/src/client/components/Admin/AuditLog/AuditLogSettings.tsx

@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
 import { Collapse } from 'reactstrap';
 import { Collapse } from 'reactstrap';
 
 
 import { AllSupportedActions } from '~/interfaces/activity';
 import { AllSupportedActions } from '~/interfaces/activity';
-import { useActivityExpirationSeconds, useAuditLogAvailableActions } from '~/stores/context';
+import { useActivityExpirationSeconds, useAuditLogAvailableActions } from '~/stores-universal/context';
 
 
 export const AuditLogSettings: FC = () => {
 export const AuditLogSettings: FC = () => {
   const { t } = useTranslation();
   const { t } = useTranslation();

+ 0 - 0
apps/app/src/components/Admin/AuditLog/DateRangePicker.tsx → apps/app/src/client/components/Admin/AuditLog/DateRangePicker.tsx


+ 0 - 0
apps/app/src/components/Admin/AuditLog/SearchUsernameTypeahead.tsx → apps/app/src/client/components/Admin/AuditLog/SearchUsernameTypeahead.tsx


+ 0 - 0
apps/app/src/components/Admin/AuditLog/SelectActionDropdown.tsx → apps/app/src/client/components/Admin/AuditLog/SelectActionDropdown.tsx


+ 1 - 1
apps/app/src/components/Admin/AuditLogManagement.tsx → apps/app/src/client/components/Admin/AuditLogManagement.tsx

@@ -8,8 +8,8 @@ import { useTranslation } from 'react-i18next';
 import type { IClearable } from '~/client/interfaces/clearable';
 import type { IClearable } from '~/client/interfaces/clearable';
 import { toastError } from '~/client/util/toastr';
 import { toastError } from '~/client/util/toastr';
 import type { SupportedActionType } from '~/interfaces/activity';
 import type { SupportedActionType } from '~/interfaces/activity';
+import { useAuditLogEnabled, useAuditLogAvailableActions } from '~/stores-universal/context';
 import { useSWRxActivity } from '~/stores/activity';
 import { useSWRxActivity } from '~/stores/activity';
-import { useAuditLogEnabled, useAuditLogAvailableActions } from '~/stores/context';
 
 
 import PaginationWrapper from '../PaginationWrapper';
 import PaginationWrapper from '../PaginationWrapper';
 
 

+ 0 - 0
apps/app/src/components/Admin/Common/Accordion.jsx → apps/app/src/client/components/Admin/Common/Accordion.jsx


+ 0 - 0
apps/app/src/components/Admin/Common/AdminInstallButtonRow.tsx → apps/app/src/client/components/Admin/Common/AdminInstallButtonRow.tsx


+ 0 - 0
apps/app/src/components/Admin/Common/AdminUpdateButtonRow.tsx → apps/app/src/client/components/Admin/Common/AdminUpdateButtonRow.tsx


+ 0 - 0
apps/app/src/components/Admin/Common/LabeledProgressBar.tsx → apps/app/src/client/components/Admin/Common/LabeledProgressBar.tsx


+ 0 - 0
apps/app/src/components/Admin/Customize/Customize.jsx → apps/app/src/client/components/Admin/Customize/Customize.jsx


+ 0 - 0
apps/app/src/components/Admin/Customize/CustomizeCssSetting.tsx → apps/app/src/client/components/Admin/Customize/CustomizeCssSetting.tsx


+ 0 - 0
apps/app/src/components/Admin/Customize/CustomizeFunctionOption.tsx → apps/app/src/client/components/Admin/Customize/CustomizeFunctionOption.tsx


+ 0 - 0
apps/app/src/components/Admin/Customize/CustomizeFunctionSetting.tsx → apps/app/src/client/components/Admin/Customize/CustomizeFunctionSetting.tsx


+ 1 - 1
apps/app/src/components/Admin/Customize/CustomizeLayoutSetting.tsx → apps/app/src/client/components/Admin/Customize/CustomizeLayoutSetting.tsx

@@ -6,8 +6,8 @@ import { LoadingSpinner } from '@growi/ui/dist/components';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
+import { useNextThemes } from '~/stores-universal/use-next-themes';
 import { useSWRxLayoutSetting } from '~/stores/admin/customize';
 import { useSWRxLayoutSetting } from '~/stores/admin/customize';
-import { useNextThemes } from '~/stores/use-next-themes';
 
 
 const useIsContainerFluid = () => {
 const useIsContainerFluid = () => {
   const { data: layoutSetting, update: updateLayoutSetting } = useSWRxLayoutSetting();
   const { data: layoutSetting, update: updateLayoutSetting } = useSWRxLayoutSetting();

+ 2 - 2
apps/app/src/components/Admin/Customize/CustomizeLogoSetting.tsx → apps/app/src/client/components/Admin/Customize/CustomizeLogoSetting.tsx

@@ -2,12 +2,12 @@ import React, { useCallback, useState } from 'react';
 
 
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 
 
+import ImageCropModal from '~/client/components/Common/ImageCropModal';
 import {
 import {
   apiv3Delete, apiv3PostForm, apiv3Put,
   apiv3Delete, apiv3PostForm, apiv3Put,
 } from '~/client/util/apiv3-client';
 } from '~/client/util/apiv3-client';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { toastError, toastSuccess } from '~/client/util/toastr';
-import ImageCropModal from '~/components/Common/ImageCropModal';
-import { useIsDefaultLogo, useIsCustomizedLogoUploaded } from '~/stores/context';
+import { useIsDefaultLogo, useIsCustomizedLogoUploaded } from '~/stores-universal/context';
 
 
 import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
 import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
 
 

+ 0 - 0
apps/app/src/components/Admin/Customize/CustomizeNoscriptSetting.tsx → apps/app/src/client/components/Admin/Customize/CustomizeNoscriptSetting.tsx


+ 0 - 0
apps/app/src/components/Admin/Customize/CustomizePresentationSetting.tsx → apps/app/src/client/components/Admin/Customize/CustomizePresentationSetting.tsx


+ 0 - 0
apps/app/src/components/Admin/Customize/CustomizeScriptSetting.tsx → apps/app/src/client/components/Admin/Customize/CustomizeScriptSetting.tsx


+ 1 - 1
apps/app/src/components/Admin/Customize/CustomizeSidebarSetting.tsx → apps/app/src/client/components/Admin/Customize/CustomizeSidebarSetting.tsx

@@ -5,8 +5,8 @@ import { useTranslation } from 'next-i18next';
 import { Card, CardBody } from 'reactstrap';
 import { Card, CardBody } from 'reactstrap';
 
 
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
+import { useNextThemes } from '~/stores-universal/use-next-themes';
 import { useSWRxSidebarConfig } from '~/stores/admin/sidebar-config';
 import { useSWRxSidebarConfig } from '~/stores/admin/sidebar-config';
-import { useNextThemes } from '~/stores/use-next-themes';
 
 
 const CustomizeSidebarsetting = (): JSX.Element => {
 const CustomizeSidebarsetting = (): JSX.Element => {
   const { t } = useTranslation(['admin', 'commons']);
   const { t } = useTranslation(['admin', 'commons']);

+ 0 - 0
apps/app/src/components/Admin/Customize/CustomizeThemeOptions.tsx → apps/app/src/client/components/Admin/Customize/CustomizeThemeOptions.tsx


+ 0 - 0
apps/app/src/components/Admin/Customize/CustomizeThemeSetting.tsx → apps/app/src/client/components/Admin/Customize/CustomizeThemeSetting.tsx


+ 1 - 1
apps/app/src/components/Admin/Customize/CustomizeTitle.tsx → apps/app/src/client/components/Admin/Customize/CustomizeTitle.tsx

@@ -6,7 +6,7 @@ import { Card, CardBody } from 'reactstrap';
 
 
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
-import { useCustomizeTitle } from '~/stores/context';
+import { useCustomizeTitle } from '~/stores-universal/context';
 
 
 import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
 import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
 
 

+ 0 - 0
apps/app/src/components/Admin/Customize/PagingSizeUncontrolledDropdown.jsx → apps/app/src/client/components/Admin/Customize/PagingSizeUncontrolledDropdown.jsx


+ 0 - 0
apps/app/src/components/Admin/Customize/ThemeColorBox.module.scss → apps/app/src/client/components/Admin/Customize/ThemeColorBox.module.scss


+ 0 - 0
apps/app/src/components/Admin/Customize/ThemeColorBox.tsx → apps/app/src/client/components/Admin/Customize/ThemeColorBox.tsx


+ 1 - 1
apps/app/src/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx → apps/app/src/client/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx

@@ -6,7 +6,7 @@ import { useTranslation } from 'next-i18next';
 import { apiv3Get, apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
 import { apiv3Get, apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { SocketEventName } from '~/interfaces/websocket';
 import { SocketEventName } from '~/interfaces/websocket';
-import { useIsSearchServiceReachable } from '~/stores/context';
+import { useIsSearchServiceReachable } from '~/stores-universal/context';
 import { useAdminSocket } from '~/stores/socket-io';
 import { useAdminSocket } from '~/stores/socket-io';
 
 
 import NormalizeIndicesControls from './NormalizeIndicesControls';
 import NormalizeIndicesControls from './NormalizeIndicesControls';

+ 0 - 0
apps/app/src/components/Admin/ElasticsearchManagement/NormalizeIndicesControls.tsx → apps/app/src/client/components/Admin/ElasticsearchManagement/NormalizeIndicesControls.tsx


+ 0 - 0
apps/app/src/components/Admin/ElasticsearchManagement/RebuildIndexControls.jsx → apps/app/src/client/components/Admin/ElasticsearchManagement/RebuildIndexControls.jsx


+ 0 - 0
apps/app/src/components/Admin/ElasticsearchManagement/ReconnectControls.tsx → apps/app/src/client/components/Admin/ElasticsearchManagement/ReconnectControls.tsx


+ 0 - 0
apps/app/src/components/Admin/ElasticsearchManagement/StatusTable.jsx → apps/app/src/client/components/Admin/ElasticsearchManagement/StatusTable.jsx


+ 0 - 0
apps/app/src/components/Admin/ExportArchiveData/ArchiveFilesTable.tsx → apps/app/src/client/components/Admin/ExportArchiveData/ArchiveFilesTable.tsx


+ 0 - 0
apps/app/src/components/Admin/ExportArchiveData/ArchiveFilesTableMenu.tsx → apps/app/src/client/components/Admin/ExportArchiveData/ArchiveFilesTableMenu.tsx


+ 0 - 0
apps/app/src/components/Admin/ExportArchiveData/SelectCollectionsModal.tsx → apps/app/src/client/components/Admin/ExportArchiveData/SelectCollectionsModal.tsx


+ 0 - 0
apps/app/src/components/Admin/ExportArchiveDataPage.tsx → apps/app/src/client/components/Admin/ExportArchiveDataPage.tsx


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


+ 0 - 0
apps/app/src/components/Admin/FullTextSearchManagement.tsx → apps/app/src/client/components/Admin/FullTextSearchManagement.tsx


+ 0 - 0
apps/app/src/components/Admin/G2GDataTransfer.tsx → apps/app/src/client/components/Admin/G2GDataTransfer.tsx


+ 0 - 0
apps/app/src/components/Admin/G2GDataTransferExportForm.tsx → apps/app/src/client/components/Admin/G2GDataTransferExportForm.tsx


+ 0 - 0
apps/app/src/components/Admin/G2GDataTransferStatusIcon.tsx → apps/app/src/client/components/Admin/G2GDataTransferStatusIcon.tsx


+ 0 - 0
apps/app/src/components/Admin/ImportData/GrowiArchive/ErrorViewer.tsx → apps/app/src/client/components/Admin/ImportData/GrowiArchive/ErrorViewer.tsx


+ 0 - 0
apps/app/src/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx → apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx


+ 0 - 0
apps/app/src/components/Admin/ImportData/GrowiArchive/ImportCollectionItem.jsx → apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionItem.jsx


+ 0 - 0
apps/app/src/components/Admin/ImportData/GrowiArchive/ImportForm.jsx → apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportForm.jsx


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


+ 0 - 0
apps/app/src/components/Admin/ImportData/GrowiArchiveSection.jsx → apps/app/src/client/components/Admin/ImportData/GrowiArchiveSection.jsx


+ 0 - 0
apps/app/src/components/Admin/ImportData/ImportDataPageContents.jsx → apps/app/src/client/components/Admin/ImportData/ImportDataPageContents.jsx


+ 0 - 0
apps/app/src/components/Admin/LegacySlackIntegration/LegacySlackIntegration.jsx → apps/app/src/client/components/Admin/LegacySlackIntegration/LegacySlackIntegration.jsx


+ 0 - 0
apps/app/src/components/Admin/LegacySlackIntegration/SlackConfiguration.jsx → apps/app/src/client/components/Admin/LegacySlackIntegration/SlackConfiguration.jsx


+ 0 - 0
apps/app/src/components/Admin/ManageExternalAccount.tsx → apps/app/src/client/components/Admin/ManageExternalAccount.tsx


+ 0 - 0
apps/app/src/components/Admin/MarkdownSetting/IndentForm.tsx → apps/app/src/client/components/Admin/MarkdownSetting/IndentForm.tsx


+ 0 - 0
apps/app/src/components/Admin/MarkdownSetting/LineBreakForm.jsx → apps/app/src/client/components/Admin/MarkdownSetting/LineBreakForm.jsx


+ 0 - 0
apps/app/src/components/Admin/MarkdownSetting/MarkDownSettingContents.tsx → apps/app/src/client/components/Admin/MarkdownSetting/MarkDownSettingContents.tsx


+ 0 - 0
apps/app/src/components/Admin/MarkdownSetting/WhitelistInput.tsx → apps/app/src/client/components/Admin/MarkdownSetting/WhitelistInput.tsx


+ 0 - 0
apps/app/src/components/Admin/MarkdownSetting/XssForm.jsx → apps/app/src/client/components/Admin/MarkdownSetting/XssForm.jsx


+ 0 - 0
apps/app/src/components/Admin/NotFoundPage.tsx → apps/app/src/client/components/Admin/NotFoundPage.tsx


+ 0 - 0
apps/app/src/components/Admin/Notification/GlobalNotification.jsx → apps/app/src/client/components/Admin/Notification/GlobalNotification.jsx


+ 0 - 0
apps/app/src/components/Admin/Notification/GlobalNotificationList.jsx → apps/app/src/client/components/Admin/Notification/GlobalNotificationList.jsx


Неке датотеке нису приказане због велике количине промена