diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml index 29c2678316..301080b759 100644 --- a/.github/ISSUE_TEMPLATE/config.yaml +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -1,6 +1,6 @@ contact_links: - name: Help and support - about: Reach out to us on our Discord server or GitHub discussions. + about: Reach out to us on our Forum or GitHub discussions. - name: Dedicated support url: mailto:support@plane.so about: Write to us if you'd like dedicated support using Plane diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..3ef625faef --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,55 @@ +version: 2 +updates: + # JavaScript/TypeScript dependencies (pnpm monorepo root) + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + labels: + - "dependencies" + - "javascript" + groups: + minor-and-patch: + update-types: + - "minor" + - "patch" + + # Python dependencies + - package-ecosystem: "pip" + directory: "/apps/api" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + labels: + - "dependencies" + - "python" + groups: + minor-and-patch: + update-types: + - "minor" + - "patch" + + # GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + labels: + - "dependencies" + - "github-actions" + groups: + actions: + patterns: + - "*" + + # Docker - API + - package-ecosystem: "docker" + directory: "/apps/api" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + labels: + - "dependencies" + - "docker" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e3aba5cf14..6d63f54c4f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -20,43 +20,20 @@ jobs: fail-fast: false matrix: language: ["python", "javascript"] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Use only 'java' to analyze code written in Java, Kotlin or both - # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository uses: actions/checkout@v4 - # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). - # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh + uses: github/codeql-action/autobuild@v4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/copyright-check.yml b/.github/workflows/copyright-check.yml new file mode 100644 index 0000000000..1e20ed2eeb --- /dev/null +++ b/.github/workflows/copyright-check.yml @@ -0,0 +1,45 @@ +name: Copy Right Check + +on: + workflow_dispatch: + pull_request: + branches: + - "preview" + types: + - "opened" + - "synchronize" + - "ready_for_review" + - "review_requested" + - "reopened" + +jobs: + license-check: + name: Copy Right Check + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.22" + + - name: Install addlicense + run: | + go install github.com/google/addlicense@latest + echo "$(go env GOPATH)/bin" >> $GITHUB_PATH + + - name: Check Copyright For Python Files + run: | + set -e + echo "Running copyright check..." + addlicense -check -f COPYRIGHT.txt -ignore "**/migrations/**" $(git ls-files '*.py') + echo "Copyright check passed." + + - name: Check Copyright For TypeScript Files + run: | + set -e + echo "Running copyright check..." + addlicense -check -f COPYRIGHT.txt -ignore "**/*.config.ts" -ignore "**/*.d.ts" $(git ls-files '*.ts' '*.tsx') + echo "Copyright check passed." diff --git a/.github/workflows/pull-request-build-lint-web-apps.yml b/.github/workflows/pull-request-build-lint-web-apps.yml index 2a463852c0..6d67699231 100644 --- a/.github/workflows/pull-request-build-lint-web-apps.yml +++ b/.github/workflows/pull-request-build-lint-web-apps.yml @@ -117,16 +117,54 @@ jobs: path: .turbo key: turbo-${{ runner.os }}-${{ github.event.pull_request.base.sha }}-${{ github.sha }} - # Lint and type checks depend on build artifacts - check: - name: ${{ matrix.task }} + # Lint check - no build dependency, OxLint is a standalone Rust binary + check-lint: + name: check:lint + runs-on: ubuntu-latest + timeout-minutes: 10 + if: | + github.event.pull_request.draft == false && + github.event.pull_request.requested_reviewers != null + env: + TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha }} + TURBO_SCM_HEAD: ${{ github.sha }} + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + fetch-depth: 50 + filter: blob:none + + - name: Set up Node.js + uses: actions/setup-node@v6 + + - name: Enable Corepack and pnpm + run: corepack enable pnpm + + - name: Get pnpm store directory + shell: bash + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Cache pnpm store + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: pnpm-store-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + pnpm-store-${{ runner.os }}- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run check:lint + run: pnpm turbo run check:lint --affected + + # Type check depends on build artifacts + check-types: + name: check:types runs-on: ubuntu-latest needs: build timeout-minutes: 15 - strategy: - fail-fast: false - matrix: - task: [check:lint, check:types] env: TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha }} TURBO_SCM_HEAD: ${{ github.sha }} @@ -165,5 +203,5 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Run ${{ matrix.task }} - run: pnpm turbo run ${{ matrix.task }} --affected + - name: Run check:types + run: pnpm turbo run check:types --affected diff --git a/.prettierrc b/.oxfmtrc.json similarity index 52% rename from .prettierrc rename to .oxfmtrc.json index ef155d4a13..cf1c0efa79 100644 --- a/.prettierrc +++ b/.oxfmtrc.json @@ -1,5 +1,11 @@ { - "$schema": "https://json.schemastore.org/prettierrc", + "printWidth": 120, + "tabWidth": 2, + "trailingComma": "es5", + "sortTailwindcss": { + "stylesheet": "packages/tailwind-config/index.css", + "functions": ["cn", "clsx", "cva"] + }, "overrides": [ { "files": ["packages/codemods/**/*"], @@ -7,9 +13,5 @@ "printWidth": 80 } } - ], - "plugins": ["@prettier/plugin-oxc"], - "printWidth": 120, - "tabWidth": 2, - "trailingComma": "es5" + ] } diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000000..83ab91d8e3 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,53 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["react", "typescript", "jsx-a11y", "import", "promise", "unicorn", "oxc"], + "categories": { + "correctness": "warn", + "suspicious": "warn", + "perf": "warn" + }, + "env": { + "browser": true, + "node": true, + "es2024": true + }, + "settings": { + "react": { + "version": "18.3" + }, + "jsx-a11y": { + "polymorphicPropName": "as" + } + }, + "ignorePatterns": [ + ".cache/**", + ".next/**", + ".react-router/**", + ".storybook/**", + ".turbo/**", + ".vite/**", + "*.config.{js,mjs,cjs,ts}", + "build/**", + "coverage/**", + "dist/**", + "**/public/**", + "storybook-static/**" + ], + "rules": { + "react/react-in-jsx-scope": "off", + "react/prop-types": "off", + "unicorn/filename-case": "off", + "unicorn/no-null": "off", + "unicorn/prevent-abbreviations": "off", + "no-unused-vars": [ + "warn", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^_", + "destructuredArrayIgnorePattern": "^_", + "ignoreRestSiblings": true + } + ] + } +} diff --git a/AGENTS.md b/AGENTS.md index dc76d8ce59..3de0b80379 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ - `pnpm dev` - Start all dev servers (web:3000, admin:3001) - `pnpm build` - Build all packages and apps - `pnpm check` - Run all checks (format, lint, types) -- `pnpm check:lint` - ESLint across all packages +- `pnpm check:lint` - OxLint across all packages - `pnpm check:types` - TypeScript type checking - `pnpm fix` - Auto-fix format and lint issues - `pnpm turbo run --filter=` - Target specific package/app @@ -15,8 +15,8 @@ - **Imports**: Use `workspace:*` for internal packages, `catalog:` for external deps - **TypeScript**: Strict mode enabled, all files must be typed -- **Formatting**: Prettier with Tailwind plugin, run `pnpm fix:format` -- **Linting**: ESLint with shared config, max warnings vary by package +- **Formatting**: oxfmt, run `pnpm fix:format` +- **Linting**: OxLint with shared `.oxlintrc.json` config - **Naming**: camelCase for variables/functions, PascalCase for components/types - **Error Handling**: Use try-catch with proper error types, log errors appropriately - **State Management**: MobX stores in `packages/shared-state`, reactive patterns diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49c61547db..d0f3d75d26 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,7 +91,7 @@ If you would like to _implement_ it, an issue with your proposal must be submitt To ensure consistency throughout the source code, please keep these rules in mind as you are working: - All features or bug fixes must be tested by one or more specs (unit-tests). -- We lint with [ESLint 9](https://eslint.org/docs/latest/) using the shared `eslint.config.mjs` (type-aware via `typescript-eslint`) and format with [Prettier](https://prettier.io/) using `prettier.config.cjs`. +- We lint with [OxLint](https://oxc.rs/docs/guide/usage/linter) using the shared `.oxlintrc.json` and format with [oxfmt](https://oxc.rs/docs/guide/usage/formatter) using `.oxfmtrc.json`. ## Ways to contribute @@ -244,4 +244,4 @@ Happy translating! 🌍✨ ## Need help? Questions and suggestions -Questions, suggestions, and thoughts are most welcome. We can also be reached in our [Discord Server](https://discord.com/invite/A92xrEGCge). +Questions, suggestions, and thoughts are most welcome. We can also be reached in our [Forum](https://forum.plane.so). diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt new file mode 100644 index 0000000000..2a6fd91ff3 --- /dev/null +++ b/COPYRIGHT.txt @@ -0,0 +1,3 @@ +Copyright (c) 2023-present Plane Software, Inc. and contributors +SPDX-License-Identifier: AGPL-3.0-only +See the LICENSE file for details. \ No newline at end of file diff --git a/COPYRIGHT_CHECK.md b/COPYRIGHT_CHECK.md new file mode 100644 index 0000000000..a72b11a240 --- /dev/null +++ b/COPYRIGHT_CHECK.md @@ -0,0 +1,34 @@ +## Copyright check + +To verify that all tracked Python files contain the correct copyright header for **Plane Software Inc.** for the year **2023**, run this command from the repository root: + +```bash +addlicense --check -f COPYRIGHT.txt -ignore "**/migrations/**" $(git ls-files '*.py') +``` + +#### To Apply Changes + +python files + +```bash +addlicense -v -f COPYRIGHT.txt -ignore "**/migrations/**" $(git ls-files '*.py') +``` + +ts and tsx files in a specific app + +```bash +addlicense -v -f COPYRIGHT.txt \ + -ignore "**/*.config.ts" \ + -ignore "**/*.d.ts" \ + $(git ls-files 'packages/*.ts') +``` + +Note: Please make sure ts command is running on specific folder, running it for the whole mono repo is crashing os processes. + +#### Other Options + +- **`addlicense -check`**: runs in check-only mode and fails if any file is missing or has an incorrect header. +- **`-c "Plane Software Inc."`**: sets the copyright holder. +- **`-f LICENSE.txt`**: uses the contents and format defined in `LICENSE.txt` as the header template. +- **`-y 2023`**: sets the year in the header. +- **`$(git ls-files '*.py')`**: restricts the check to Python files tracked in git. diff --git a/README.md b/README.md index 5a466594f8..d2117149da 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,9 @@

Modern project management for all teams

-

- -Discord online members - -Commit activity per month -

-

Website • - Releases • + ForumTwitterDocumentation

@@ -33,7 +26,7 @@ Meet [Plane](https://plane.so/), an open-source project management tool to track issues, run ~sprints~ cycles, and manage product roadmaps without the chaos of managing the tool itself. 🧘‍♀️ -> Plane is evolving every day. Your suggestions, ideas, and reported bugs help us immensely. Do not hesitate to join in the conversation on [Discord](https://discord.com/invite/A92xrEGCge) or raise a GitHub issue. We read everything and respond to most. +> Plane is evolving every day. Your suggestions, ideas, and reported bugs help us immensely. Do not hesitate to join in the conversation on [Forum](https://forum.plane.so) or raise a GitHub issue. We read everything and respond to most. ## 🚀 Installation @@ -136,7 +129,7 @@ Explore Plane's [product documentation](https://docs.plane.so/) and [developer d ## ❤️ Community -Join the Plane community on [GitHub Discussions](https://github.com/orgs/makeplane/discussions) and our [Discord server](https://discord.com/invite/A92xrEGCge). We follow a [Code of conduct](https://github.com/makeplane/plane/blob/master/CODE_OF_CONDUCT.md) in all our community channels. +Join the Plane community on [GitHub Discussions](https://github.com/orgs/makeplane/discussions) and our [Forum](https://forum.plane.so). We follow a [Code of conduct](https://github.com/makeplane/plane/blob/master/CODE_OF_CONDUCT.md) in all our community channels. Feel free to ask questions, report bugs, participate in discussions, share ideas, request features, or showcase your projects. We’d love to hear from you! @@ -152,7 +145,7 @@ There are many ways you can contribute to Plane: - Report [bugs](https://github.com/makeplane/plane/issues/new?assignees=srinivaspendem%2Cpushya22&labels=%F0%9F%90%9Bbug&projects=&template=--bug-report.yaml&title=%5Bbug%5D%3A+) or submit [feature requests](https://github.com/makeplane/plane/issues/new?assignees=srinivaspendem%2Cpushya22&labels=%E2%9C%A8feature&projects=&template=--feature-request.yaml&title=%5Bfeature%5D%3A+). - Review the [documentation](https://docs.plane.so/) and submit [pull requests](https://github.com/makeplane/docs) to improve it—whether it's fixing typos or adding new content. -- Talk or write about Plane or any other ecosystem integration and [let us know](https://discord.com/invite/A92xrEGCge)! +- Talk or write about Plane or any other ecosystem integration and [let us know](https://forum.plane.so)! - Show your support by upvoting [popular feature requests](https://github.com/makeplane/plane/issues). Please read [CONTRIBUTING.md](https://github.com/makeplane/plane/blob/master/CONTRIBUTING.md) for details on the process for submitting pull requests to us. diff --git a/apps/admin/Dockerfile.admin b/apps/admin/Dockerfile.admin index 458d00a2e3..63b30a1c31 100644 --- a/apps/admin/Dockerfile.admin +++ b/apps/admin/Dockerfile.admin @@ -13,7 +13,7 @@ RUN corepack enable pnpm FROM base AS builder -RUN pnpm add -g turbo@2.6.3 +RUN pnpm add -g turbo@2.8.12 COPY . . diff --git a/apps/admin/app/(all)/(dashboard)/ai/form.tsx b/apps/admin/app/(all)/(dashboard)/ai/form.tsx index 69456d8b61..affbda4808 100644 --- a/apps/admin/app/(all)/(dashboard)/ai/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/ai/form.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useForm } from "react-hook-form"; import { Lightbulb } from "lucide-react"; import { Button } from "@plane/propel/button"; @@ -114,16 +120,16 @@ export function InstanceAIForm(props: IInstanceAIForm) { -
+
-
+
If you have a preferred AI models vendor, please get in{" "} - + touch with us.
diff --git a/apps/admin/app/(all)/(dashboard)/ai/page.tsx b/apps/admin/app/(all)/(dashboard)/ai/page.tsx index bf290ef361..dec3200982 100644 --- a/apps/admin/app/(all)/(dashboard)/ai/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/ai/page.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { observer } from "mobx-react"; import useSWR from "swr"; import { Loader } from "@plane/ui"; @@ -28,7 +34,7 @@ const InstanceAIPage = observer(function InstanceAIPage(_props: Route.ComponentP ) : ( -
+
diff --git a/apps/admin/app/(all)/(dashboard)/authentication/gitea/form.tsx b/apps/admin/app/(all)/(dashboard)/authentication/gitea/form.tsx index c2e637e279..f05464a41a 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/gitea/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/gitea/form.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useState } from "react"; import { isEmpty } from "lodash-es"; import Link from "next/link"; @@ -170,8 +176,8 @@ export function InstanceGiteaConfigForm(props: Props) { handleClose={() => setIsDiscardChangesModalOpen(false)} />
-
-
+
+
Gitea-provided details for Plane
{GITEA_FORM_FIELDS.map((field) => (
-
+
Plane-provided details for Gitea
{GITEA_SERVICE_FIELD.map((field) => ( diff --git a/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx b/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx index f0f7e23338..fe8eae4c65 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useState } from "react"; import { observer } from "mobx-react"; import useSWR from "swr"; diff --git a/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx b/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx index c1aef0f6cc..2425216b3a 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useState } from "react"; import { isEmpty } from "lodash-es"; import Link from "next/link"; @@ -191,8 +197,8 @@ export function InstanceGithubConfigForm(props: Props) { handleClose={() => setIsDiscardChangesModalOpen(false)} />
-
-
+
+
GitHub-provided details for Plane
{GITHUB_FORM_FIELDS.map((field) => (
-
+
Plane-provided details for GitHub
{/* common service details */} -
+
{GITHUB_COMMON_SERVICE_DETAILS.map((field) => ( ))}
{/* web service details */} -
-
- +
+
+ Web
-
+
{GITHUB_SERVICE_DETAILS.map((field) => ( ))} diff --git a/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx b/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx index 1186332d37..a7a29cf9d8 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useState } from "react"; import { observer } from "mobx-react"; import { useTheme } from "next-themes"; diff --git a/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx b/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx index 4511a70c91..7df6faf17a 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useState } from "react"; import { isEmpty } from "lodash-es"; import Link from "next/link"; @@ -174,8 +180,8 @@ export function InstanceGitlabConfigForm(props: Props) { handleClose={() => setIsDiscardChangesModalOpen(false)} />
-
-
+
+
GitLab-provided details for Plane
{GITLAB_FORM_FIELDS.map((field) => (
-
+
Plane-provided details for GitLab
{GITLAB_SERVICE_FIELD.map((field) => ( diff --git a/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx b/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx index 0f3fd76a65..5bcaef7268 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useState } from "react"; import { observer } from "mobx-react"; import useSWR from "swr"; diff --git a/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx b/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx index e068a1d07b..698ff34e9c 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useState } from "react"; import { isEmpty } from "lodash-es"; import Link from "next/link"; @@ -179,8 +185,8 @@ export function InstanceGoogleConfigForm(props: Props) { handleClose={() => setIsDiscardChangesModalOpen(false)} />
-
-
+
+
Google-provided details for Plane
{GOOGLE_FORM_FIELDS.map((field) => (
-
+
Plane-provided details for Google
{/* common service details */} -
+
{GOOGLE_COMMON_SERVICE_DETAILS.map((field) => ( ))}
{/* web service details */} -
-
- +
+
+ Web
-
+
{GOOGLE_SERVICE_DETAILS.map((field) => ( ))} diff --git a/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx b/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx index bf2a18d5a8..93a61497d2 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useState } from "react"; import { observer } from "mobx-react"; import useSWR from "swr"; diff --git a/apps/admin/app/(all)/(dashboard)/authentication/page.tsx b/apps/admin/app/(all)/(dashboard)/authentication/page.tsx index 10adc31297..26e5fc56f9 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/page.tsx @@ -1,16 +1,24 @@ -import { useState } from "react"; +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import { useCallback, useRef, useState } from "react"; import { observer } from "mobx-react"; import { useTheme } from "next-themes"; import useSWR from "swr"; // plane internal packages -import { setPromiseToast } from "@plane/propel/toast"; -import type { TInstanceConfigurationKeys } from "@plane/types"; +import { setPromiseToast, setToast, TOAST_TYPE } from "@plane/propel/toast"; +import type { TInstanceConfigurationKeys, TInstanceAuthenticationModes } from "@plane/types"; import { Loader, ToggleSwitch } from "@plane/ui"; import { cn, resolveGeneralTheme } from "@plane/utils"; // components import { PageWrapper } from "@/components/common/page-wrapper"; -// hooks import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card"; +// helpers +import { canDisableAuthMethod } from "@/helpers/authentication"; +// hooks import { useAuthenticationModes } from "@/hooks/oauth"; import { useInstance } from "@/hooks/store"; // types @@ -19,48 +27,87 @@ import type { Route } from "./+types/page"; const InstanceAuthenticationPage = observer(function InstanceAuthenticationPage(_props: Route.ComponentProps) { // theme const { resolvedTheme: resolvedThemeAdmin } = useTheme(); - // store - const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance(); + const resolvedTheme = resolveGeneralTheme(resolvedThemeAdmin); + // Ref to store authentication modes for validation (avoids circular dependency) + const authenticationModesRef = useRef([]); // state const [isSubmitting, setIsSubmitting] = useState(false); + // store hooks + const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance(); // derived values const enableSignUpConfig = formattedConfig?.ENABLE_SIGNUP ?? ""; - const resolvedTheme = resolveGeneralTheme(resolvedThemeAdmin); useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations()); - const updateConfig = async (key: TInstanceConfigurationKeys, value: string) => { - setIsSubmitting(true); + // Create updateConfig with validation - uses authenticationModesRef for current modes + const updateConfig = useCallback( + (key: TInstanceConfigurationKeys, value: string): void => { + // Check if trying to disable (value === "0") + if (value === "0") { + // Check if this key is an authentication method key + const currentAuthModes = authenticationModesRef.current; + const isAuthMethodKey = currentAuthModes.some((method) => method.enabledConfigKey === key); - const payload = { - [key]: value, - }; + // Only validate if this is an authentication method key + if (isAuthMethodKey) { + const canDisable = canDisableAuthMethod(key, currentAuthModes, formattedConfig); - const updateConfigPromise = updateInstanceConfigurations(payload); + if (!canDisable) { + setToast({ + type: TOAST_TYPE.ERROR, + title: "Cannot disable authentication", + message: + "At least one authentication method must remain enabled. Please enable another method before disabling this one.", + }); + return; + } + } + } - setPromiseToast(updateConfigPromise, { - loading: "Saving configuration", - success: { - title: "Success", - message: () => "Configuration saved successfully", - }, - error: { - title: "Error", - message: () => "Failed to save configuration", - }, - }); + // Proceed with the update + setIsSubmitting(true); - await updateConfigPromise - .then(() => { - setIsSubmitting(false); - }) - .catch((err) => { - console.error(err); - setIsSubmitting(false); + const payload = { + [key]: value, + }; + + const updateConfigPromise = updateInstanceConfigurations(payload); + + setPromiseToast(updateConfigPromise, { + loading: "Saving configuration", + success: { + title: "Success", + message: () => "Configuration saved successfully", + }, + error: { + title: "Error", + message: () => "Failed to save configuration", + }, }); - }; - const authenticationModes = useAuthenticationModes({ disabled: isSubmitting, updateConfig, resolvedTheme }); + void updateConfigPromise + .then(() => { + setIsSubmitting(false); + return undefined; + }) + .catch((err) => { + console.error(err); + setIsSubmitting(false); + }); + }, + [formattedConfig, updateInstanceConfigurations] + ); + + // Get authentication modes - this will use updateConfig which includes validation + const authenticationModes = useAuthenticationModes({ + disabled: isSubmitting, + updateConfig, + resolvedTheme, + }); + + // Update ref with latest authentication modes + authenticationModesRef.current = authenticationModes; + return ( {formattedConfig ? (
-
+
-
Allow anyone to sign up even without an invite
-
+
Allow anyone to sign up even without an invite
+
Toggling this off will only let users sign up when they are invited.
@@ -96,7 +143,7 @@ const InstanceAuthenticationPage = observer(function InstanceAuthenticationPage(
-
Available authentication modes
+
Available authentication modes
{authenticationModes.map((method) => (
-
+
@@ -201,7 +207,7 @@ export function InstanceEmailForm(props: IInstanceEmailForm) {
-
+
)} {sendEmailStep === ESendEmailSteps.FAILED &&
{error}
} -
+
diff --git a/apps/admin/app/(all)/(dashboard)/general/form.tsx b/apps/admin/app/(all)/(dashboard)/general/form.tsx index ff7b00e0dc..0b402b76c7 100644 --- a/apps/admin/app/(all)/(dashboard)/general/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/general/form.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { observer } from "mobx-react"; import { Controller, useForm } from "react-hook-form"; import { Telescope } from "lucide-react"; @@ -106,18 +112,18 @@ export const GeneralConfigurationForm = observer(function GeneralConfigurationFo
-
Chat + telemetry
+
Chat + telemetry
-
+
-
+
-
Let Plane collect anonymous usage data
-
+
Let Plane collect anonymous usage data
+
No PII is collected.This anonymized data is used to understand how you use Plane and build new features in line with{" "}
-
+
-
- +
+
-
Chat with us
-
+
Chat with us
+
Let your users chat with us via Intercom or another service. Toggling Telemetry off turns this off automatically.
diff --git a/apps/admin/app/(all)/(dashboard)/general/page.tsx b/apps/admin/app/(all)/(dashboard)/general/page.tsx index 6e0a86d52a..cb0a8c662e 100644 --- a/apps/admin/app/(all)/(dashboard)/general/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/general/page.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { observer } from "mobx-react"; // components import { PageWrapper } from "@/components/common/page-wrapper"; diff --git a/apps/admin/app/(all)/(dashboard)/image/form.tsx b/apps/admin/app/(all)/(dashboard)/image/form.tsx index 154e74bf12..72ab513398 100644 --- a/apps/admin/app/(all)/(dashboard)/image/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/image/form.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useForm } from "react-hook-form"; import { Button } from "@plane/propel/button"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; diff --git a/apps/admin/app/(all)/(dashboard)/image/page.tsx b/apps/admin/app/(all)/(dashboard)/image/page.tsx index 7b89393f27..e410e87eb8 100644 --- a/apps/admin/app/(all)/(dashboard)/image/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/image/page.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { observer } from "mobx-react"; import useSWR from "swr"; import { Loader } from "@plane/ui"; diff --git a/apps/admin/app/(all)/(dashboard)/layout.tsx b/apps/admin/app/(all)/(dashboard)/layout.tsx index ba5564bf52..e56acf7f9c 100644 --- a/apps/admin/app/(all)/(dashboard)/layout.tsx +++ b/apps/admin/app/(all)/(dashboard)/layout.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useEffect } from "react"; import { observer } from "mobx-react"; import { useRouter } from "next/navigation"; @@ -5,7 +11,7 @@ import { Outlet } from "react-router"; // components import { AdminHeader } from "@/components/common/header"; import { LogoSpinner } from "@/components/common/logo-spinner"; -import { NewUserPopup } from "@/components/new-user-popup"; +import { NewUserPopup } from "@/components/common/new-user-popup"; // hooks import { useUser } from "@/hooks/store"; // local components @@ -36,7 +42,7 @@ function AdminLayout(_props: Route.ComponentProps) {
-
+
diff --git a/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx b/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx index ed068d32ee..d1ddb1f5a3 100644 --- a/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx +++ b/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { Fragment, useEffect, useState } from "react"; import { observer } from "mobx-react"; import { useTheme as useNextTheme } from "next-themes"; @@ -33,14 +39,14 @@ export const AdminSidebarDropdown = observer(function AdminSidebarDropdown() { const getSidebarMenuItems = () => (
- {currentUser?.email} + {currentUser?.email}
diff --git a/apps/admin/app/(all)/(dashboard)/sidebar-menu.tsx b/apps/admin/app/(all)/(dashboard)/sidebar-menu.tsx index 3a81db3cc2..319cf0173d 100644 --- a/apps/admin/app/(all)/(dashboard)/sidebar-menu.tsx +++ b/apps/admin/app/(all)/(dashboard)/sidebar-menu.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { observer } from "mobx-react"; import Link from "next/link"; import { usePathname } from "next/navigation"; @@ -23,7 +29,7 @@ export const AdminSidebarMenu = observer(function AdminSidebarMenu() { }; return ( -
+
{sidebarMenu.map((item, index) => { const isActive = item.href === pathName || pathName?.includes(item.href); return ( @@ -32,9 +38,9 @@ export const AdminSidebarMenu = observer(function AdminSidebarMenu() {
{} {!isSidebarCollapsed && ( -
+
{item.name}
{item.description}
diff --git a/apps/admin/app/(all)/(dashboard)/sidebar.tsx b/apps/admin/app/(all)/(dashboard)/sidebar.tsx index ec340247f5..6d9c970c2a 100644 --- a/apps/admin/app/(all)/(dashboard)/sidebar.tsx +++ b/apps/admin/app/(all)/(dashboard)/sidebar.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useEffect, useRef } from "react"; import { observer } from "mobx-react"; // plane helpers @@ -38,13 +44,7 @@ export const AdminSidebar = observer(function AdminSidebar() { return (
diff --git a/apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx b/apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx index 270a81fee5..d250b76305 100644 --- a/apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useState, useEffect } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; @@ -8,6 +14,7 @@ import { Button, getButtonStyling } from "@plane/propel/button"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import { InstanceWorkspaceService } from "@plane/services"; import type { IWorkspace } from "@plane/types"; +import { validateSlug, validateWorkspaceName } from "@plane/utils"; // components import { CustomSelect, Input } from "@plane/ui"; // hooks @@ -90,14 +97,7 @@ export function WorkspaceCreateForm() { control={control} name="name" rules={{ - required: "This is a required field.", - validate: (value) => - /^[\w\s-]*$/.test(value) || - `Workspaces names can contain only (" "), ( - ), ( _ ) and alphanumeric characters.`, - maxLength: { - value: 80, - message: "Limit your name to 80 characters.", - }, + validate: (value) => validateWorkspaceName(value, true), }} render={({ field: { value, ref, onChange } }) => (

Set your workspace's URL

-
- {workspaceBaseURL} +
+ {workspaceBaseURL} validateSlug(value), }} render={({ field: { onChange, value, ref } }) => (
-
+
)} diff --git a/apps/admin/app/(all)/(home)/auth-banner.tsx b/apps/admin/app/(all)/(home)/auth-banner.tsx index a2db1ad656..43df781bbb 100644 --- a/apps/admin/app/(all)/(home)/auth-banner.tsx +++ b/apps/admin/app/(all)/(home)/auth-banner.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { Info } from "lucide-react"; // plane constants import type { TAdminAuthErrorInfo } from "@plane/constants"; @@ -14,16 +20,16 @@ export function AuthBanner(props: TAuthBanner) { if (!bannerData) return <>; return ( -
-
+
+
{bannerData?.message}
handleBannerData && handleBannerData(undefined)} > - +
); diff --git a/apps/admin/app/(all)/(home)/auth-header.tsx b/apps/admin/app/(all)/(home)/auth-header.tsx index 1238be6c5a..ae94821af9 100644 --- a/apps/admin/app/(all)/(home)/auth-header.tsx +++ b/apps/admin/app/(all)/(home)/auth-header.tsx @@ -1,9 +1,15 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import Link from "next/link"; import { PlaneLockup } from "@plane/propel/icons"; export function AuthHeader() { return ( -
+
diff --git a/apps/admin/app/(all)/(home)/auth-helpers.tsx b/apps/admin/app/(all)/(home)/auth-helpers.tsx index 18f793cd13..ea18dc9959 100644 --- a/apps/admin/app/(all)/(home)/auth-helpers.tsx +++ b/apps/admin/app/(all)/(home)/auth-helpers.tsx @@ -1,21 +1,13 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import Link from "next/link"; -import { KeyRound, Mails } from "lucide-react"; // plane packages import type { TAdminAuthErrorInfo } from "@plane/constants"; import { SUPPORT_EMAIL, EAdminAuthErrorCodes } from "@plane/constants"; -import type { TGetBaseAuthenticationModeProps, TInstanceAuthenticationModes } from "@plane/types"; -import { resolveGeneralTheme } from "@plane/utils"; -// components -import githubLightModeImage from "@/app/assets/logos/github-black.png?url"; -import githubDarkModeImage from "@/app/assets/logos/github-white.png?url"; -import GitlabLogo from "@/app/assets/logos/gitlab-logo.svg?url"; -import GoogleLogo from "@/app/assets/logos/google-logo.svg?url"; -import { EmailCodesConfiguration } from "@/components/authentication/email-config-switch"; -import { GithubConfiguration } from "@/components/authentication/github-config"; -import { GitlabConfiguration } from "@/components/authentication/gitlab-config"; -import { GoogleConfiguration } from "@/components/authentication/google-config"; -import { PasswordLoginConfiguration } from "@/components/authentication/password-config-switch"; -// images export enum EErrorAlertType { BANNER_ALERT = "BANNER_ALERT", @@ -58,7 +50,7 @@ const errorCodeMessages: { message: () => (
Admin user already exists.  - + Sign In  now. @@ -70,7 +62,7 @@ const errorCodeMessages: { message: () => (
Admin user does not exist.  - + Sign In  now. @@ -106,53 +98,3 @@ export const authErrorHandler = (errorCode: EAdminAuthErrorCodes, email?: string return undefined; }; - -export const getBaseAuthenticationModes: (props: TGetBaseAuthenticationModeProps) => TInstanceAuthenticationModes[] = ({ - disabled, - updateConfig, - resolvedTheme, -}) => [ - { - key: "unique-codes", - name: "Unique codes", - description: - "Log in or sign up for Plane using codes sent via email. You need to have set up SMTP to use this method.", - icon: , - config: , - }, - { - key: "passwords-login", - name: "Passwords", - description: "Allow members to create accounts with passwords and use it with their email addresses to sign in.", - icon: , - config: , - }, - { - key: "google", - name: "Google", - description: "Allow members to log in or sign up for Plane with their Google accounts.", - icon: Google Logo, - config: , - }, - { - key: "github", - name: "GitHub", - description: "Allow members to log in or sign up for Plane with their GitHub accounts.", - icon: ( - GitHub Logo - ), - config: , - }, - { - key: "gitlab", - name: "GitLab", - description: "Allow members to log in or sign up to plane with their GitLab accounts.", - icon: GitLab Logo, - config: , - }, -]; diff --git a/apps/admin/app/(all)/(home)/layout.tsx b/apps/admin/app/(all)/(home)/layout.tsx index 26c0e2b26e..a334536840 100644 --- a/apps/admin/app/(all)/(home)/layout.tsx +++ b/apps/admin/app/(all)/(home)/layout.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useEffect } from "react"; import { observer } from "mobx-react"; import { useRouter } from "next/navigation"; @@ -16,7 +22,7 @@ function RootLayout() { }, [replace, isUserLoggedIn]); return ( -
+
); diff --git a/apps/admin/app/(all)/(home)/page.tsx b/apps/admin/app/(all)/(home)/page.tsx index 12f701c462..7947adcdcc 100644 --- a/apps/admin/app/(all)/(home)/page.tsx +++ b/apps/admin/app/(all)/(home)/page.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { observer } from "mobx-react"; // components import { LogoSpinner } from "@/components/common/logo-spinner"; @@ -16,7 +22,7 @@ function HomePage() { // if instance is not fetched, show loading if (!instance && !error) { return ( -
+
); diff --git a/apps/admin/app/(all)/(home)/sign-in-form.tsx b/apps/admin/app/(all)/(home)/sign-in-form.tsx index cd40a4c77e..4e0afb8ea1 100644 --- a/apps/admin/app/(all)/(home)/sign-in-form.tsx +++ b/apps/admin/app/(all)/(home)/sign-in-form.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useEffect, useMemo, useState } from "react"; import { useSearchParams } from "next/navigation"; import { Eye, EyeOff } from "lucide-react"; @@ -10,7 +16,7 @@ import { Input, Spinner } from "@plane/ui"; // components import { Banner } from "@/components/common/banner"; // local components -import { FormHeader } from "../../../core/components/instance/form-header"; +import { FormHeader } from "@/components/instance/form-header"; import { AuthBanner } from "./auth-banner"; import { AuthHeader } from "./auth-header"; import { authErrorHandler } from "./auth-helpers"; @@ -105,8 +111,8 @@ export function InstanceSignInForm() { return ( <> -
-
+
+
-
-
@@ -159,12 +165,12 @@ export function InstanceSignInForm() { placeholder="Enter your password" value={formData.password} onChange={(e) => handleFormChange("password", e.target.value)} - autoComplete="on" + autoComplete="off" /> {showPassword ? ( diff --git a/apps/admin/core/components/common/controller-input.tsx b/apps/admin/components/common/controller-input.tsx similarity index 89% rename from apps/admin/core/components/common/controller-input.tsx rename to apps/admin/components/common/controller-input.tsx index 9dc38d851a..3d95b4484e 100644 --- a/apps/admin/core/components/common/controller-input.tsx +++ b/apps/admin/components/common/controller-input.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import React, { useState } from "react"; import type { Control } from "react-hook-form"; import { Controller } from "react-hook-form"; @@ -61,7 +67,7 @@ export function ControllerInput(props: Props) { (showPassword ? ( ); }); diff --git a/apps/admin/core/components/common/logo-spinner.tsx b/apps/admin/components/common/logo-spinner.tsx similarity index 76% rename from apps/admin/core/components/common/logo-spinner.tsx rename to apps/admin/components/common/logo-spinner.tsx index 4d06d28224..b74c38d707 100644 --- a/apps/admin/core/components/common/logo-spinner.tsx +++ b/apps/admin/components/common/logo-spinner.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useTheme } from "next-themes"; import LogoSpinnerDark from "@/app/assets/images/logo-spinner-dark.gif?url"; import LogoSpinnerLight from "@/app/assets/images/logo-spinner-light.gif?url"; diff --git a/apps/admin/core/components/new-user-popup.tsx b/apps/admin/components/common/new-user-popup.tsx similarity index 83% rename from apps/admin/core/components/new-user-popup.tsx rename to apps/admin/components/common/new-user-popup.tsx index a20cb147fc..30dda8d2dc 100644 --- a/apps/admin/core/components/new-user-popup.tsx +++ b/apps/admin/components/common/new-user-popup.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { observer } from "mobx-react"; import Link from "next/link"; import { useTheme as useNextTheme } from "next-themes"; @@ -18,7 +24,7 @@ export const NewUserPopup = observer(function NewUserPopup() { if (!isNewUserPopup) return <>; return ( -
+
Create workspace
@@ -35,7 +41,7 @@ export const NewUserPopup = observer(function NewUserPopup() {
-
+
{ return (
{customHeader ? ( -
{customHeader}
+
{customHeader}
) : ( header && ( -
+
-
{header.title}
-
{header.description}
+
{header.title}
+
{header.description}
{header.actions &&
{header.actions}
}
) )} -
+
{children}
diff --git a/apps/admin/core/components/instance/failure.tsx b/apps/admin/components/instance/failure.tsx similarity index 65% rename from apps/admin/core/components/instance/failure.tsx rename to apps/admin/components/instance/failure.tsx index 30d9f09dfb..1f1610fbe1 100644 --- a/apps/admin/core/components/instance/failure.tsx +++ b/apps/admin/components/instance/failure.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { observer } from "mobx-react"; import { useTheme } from "next-themes"; import { Button } from "@plane/propel/button"; @@ -18,12 +24,12 @@ export const InstanceFailureView = observer(function InstanceFailureView() { return ( <> -
-
-
+
+
+
Instance failure illustration -

Unable to fetch instance details.

-

+

Unable to fetch instance details.

+

We were unable to fetch the details of the instance. Fret not, it might just be a connectivity issue.

diff --git a/apps/admin/components/instance/form-header.tsx b/apps/admin/components/instance/form-header.tsx new file mode 100644 index 0000000000..652b7d3047 --- /dev/null +++ b/apps/admin/components/instance/form-header.tsx @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +export function FormHeader({ heading, subHeading }: { heading: string; subHeading: string }) { + return ( +
+ {heading} + {subHeading} +
+ ); +} diff --git a/apps/admin/core/components/instance/instance-not-ready.tsx b/apps/admin/components/instance/instance-not-ready.tsx similarity index 52% rename from apps/admin/core/components/instance/instance-not-ready.tsx rename to apps/admin/components/instance/instance-not-ready.tsx index 7092ae048c..4fa5e06705 100644 --- a/apps/admin/core/components/instance/instance-not-ready.tsx +++ b/apps/admin/components/instance/instance-not-ready.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import Link from "next/link"; import { Button } from "@plane/propel/button"; // assets @@ -5,12 +11,12 @@ import PlaneTakeOffImage from "@/app/assets/images/plane-takeoff.png?url"; export function InstanceNotReady() { return ( -
-
-
-

Welcome aboard Plane!

+
+
+
+

Welcome aboard Plane!

Plane Logo -

Get started by setting up your instance and workspace

+

Get started by setting up your instance and workspace

diff --git a/apps/admin/core/components/instance/loading.tsx b/apps/admin/components/instance/loading.tsx similarity index 76% rename from apps/admin/core/components/instance/loading.tsx rename to apps/admin/components/instance/loading.tsx index 2b45deff28..293b44bdcc 100644 --- a/apps/admin/core/components/instance/loading.tsx +++ b/apps/admin/components/instance/loading.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useTheme } from "next-themes"; // assets import LogoSpinnerDark from "@/app/assets/images/logo-spinner-dark.gif?url"; diff --git a/apps/admin/core/components/instance/setup-form.tsx b/apps/admin/components/instance/setup-form.tsx similarity index 82% rename from apps/admin/core/components/instance/setup-form.tsx rename to apps/admin/components/instance/setup-form.tsx index f463dfc593..74e80db45b 100644 --- a/apps/admin/core/components/instance/setup-form.tsx +++ b/apps/admin/components/instance/setup-form.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import { useEffect, useMemo, useState } from "react"; import { useSearchParams } from "next/navigation"; // icons @@ -7,11 +13,11 @@ import { API_BASE_URL, E_PASSWORD_STRENGTH } from "@plane/constants"; import { Button } from "@plane/propel/button"; import { AuthService } from "@plane/services"; import { Checkbox, Input, PasswordStrengthIndicator, Spinner } from "@plane/ui"; -import { getPasswordStrength } from "@plane/utils"; +import { getPasswordStrength, validatePersonName, validateCompanyName } from "@plane/utils"; // components import { AuthHeader } from "@/app/(all)/(home)/auth-header"; -import { Banner } from "@/components/common/banner"; -import { FormHeader } from "@/components/instance/form-header"; +import { Banner } from "../common/banner"; +import { FormHeader } from "./form-header"; // service initialization const authService = new AuthService(); @@ -133,8 +139,8 @@ export function InstanceSetupForm() { return ( <> -
-
+
+
-
+
-
-
-
-
-
-
@@ -288,12 +312,13 @@ export function InstanceSetupForm() { className="w-full border border-subtle !bg-surface-1 pr-12 placeholder:text-placeholder" onFocus={() => setIsRetryPasswordInputFocused(true)} onBlur={() => setIsRetryPasswordInputFocused(false)} + autoComplete="new-password" /> {showPassword.retypePassword ? ( )}
{emailError?.email && !isFocused && ( -

+

{emailError.email}

diff --git a/apps/space/components/account/auth-forms/index.ts b/apps/space/components/account/auth-forms/index.ts new file mode 100644 index 0000000000..125f6699c4 --- /dev/null +++ b/apps/space/components/account/auth-forms/index.ts @@ -0,0 +1,7 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +export * from "./auth-root"; diff --git a/apps/space/core/components/account/auth-forms/password.tsx b/apps/space/components/account/auth-forms/password.tsx similarity index 92% rename from apps/space/core/components/account/auth-forms/password.tsx rename to apps/space/components/account/auth-forms/password.tsx index 45426d4d95..7bf8971fa4 100644 --- a/apps/space/core/components/account/auth-forms/password.tsx +++ b/apps/space/components/account/auth-forms/password.tsx @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + import React, { useEffect, useMemo, useRef, useState } from "react"; import { observer } from "mobx-react"; import { Eye, EyeOff, XCircle } from "lucide-react"; @@ -119,7 +125,7 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props) -
+
handleFormChange("email", e.target.value)} placeholder="name@company.com" - className={`disable-autofill-style h-10 w-full placeholder:text-placeholder border-0`} + className={`h-10 w-full border-0 disable-autofill-style placeholder:text-placeholder`} disabled /> {passwordFormData.email.length > 0 && ( @@ -140,7 +146,7 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
-