Compare commits

...
Author SHA1 Message Date
sriram veeraghanta 6b2edbd8ee fix: move folders 2026-02-17 00:09:24 +05:30
Aaryan KhandelwalandGitHub d497304de5 refactor: table drag preview using decorations (#8597)
* refactor: table drag preview using decorations

* fix: history meta for table drag state
2026-02-13 17:59:37 +05:30
Anmol Singh BhatiaandGitHub 8fa08b2506 [GIT-57 | WEB-5912] fix: app sidebar ux and responsiveness (#8560)
* fix: project extended sidebar accordion ux

* fix: app sidebar mobile responsiveness ux

* chore: code refactor
2026-02-13 17:43:07 +05:30
sriram veeraghanta efc600ad8c chore(deps): update axios dependency 2026-02-13 16:35:16 +05:30
stelmskandGitHub a3a1d141cb i18n(ru): expand Russian translation coverage (#8603)
Added missing translations for:
- Profile preferences (language, timezone settings)
- Account settings sections (preferences, notifications, security, api-tokens, activity)
- Workspace settings (billing, exports, webhooks headings/descriptions)
- Project settings (states, labels, estimates, automations headings/descriptions)
- Power-K command palette (contextual actions, navigation, creation, preferences, help)
- Sidebar elements (stickies, your_work, pin/unpin)
- Common actions (copy_markdown, overview)
- Navigation customization options
2026-02-13 16:30:33 +05:30
sriram veeraghantaandGitHub dfce8c6278 chore: admin folder structure (#8632)
* chore: admin folder structure

* fix: copy right check and formatting

* fix: types
2026-02-13 16:29:45 +05:30
Vamsi KrishnaandGitHub fab84eb058 [WEB-5899]fix: project sort order (#8530)
* fix: project sort order

* chore: updated queryset for sort_order
2026-02-13 15:52:22 +05:30
Vipin ChaudharyandGitHub 60734b25ba [WIKI-877] fix: order of this dropdown options in pages #8563 2026-02-13 15:49:10 +05:30
SangeethaandGitHub cd613e5f8f [SECUR-105] fix: csv injection vulnerability sanitization #8611 2026-02-13 15:37:13 +05:30
Anmol Singh BhatiaandGitHub a8d81656fc [WEB-6137] fix: work item peek view outside click #8610 2026-02-12 16:28:51 +05:30
Anmol Singh BhatiaandGitHub dbe059b7b5 fix: workitem description input inital load (#8617) 2026-02-12 14:03:01 +05:30
Anmol Singh BhatiaandGitHub c93f9fc865 [WEB-6038] fix: work item empty title flicker #8618 2026-02-12 13:35:08 +05:30
Akshat JainandGitHub bcc8fb4d1d style: update ASCII art in install script header (#8628) 2026-02-12 13:34:31 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b59e541b35 chore(deps): bump cryptography (#8625)
Bumps the pip group with 1 update in the /apps/api/requirements directory: [cryptography](https://github.com/pyca/cryptography).


Updates `cryptography` from 44.0.1 to 46.0.5
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/44.0.1...46.0.5)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 46.0.5
  dependency-type: direct:production
  dependency-group: pip
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-11 13:06:42 +05:30
sriram veeraghantaandGitHub 2b6e24d526 chore: merge helpers and layouts (#8624)
* fix: remove constants and services

* fix: formatting

* chore: merge helpers and layouts

* fix: workspace disbale flag handling
2026-02-10 22:04:07 +05:30
sriram veeraghantaandGitHub 7793febcf8 chore: merge constants and services (#8623)
* fix: remove constants and services

* fix: formatting

* fix: types check
2026-02-10 21:10:54 +05:30
sriram veeraghantaandGitHub 06e4a1624c fix: remove ee folder from web (#8622) 2026-02-10 20:01:42 +05:30
SangeethaandGitHub 57ce2a5429 [WEB-6149] migration: change estimate point key max value to 50 #8620 2026-02-10 17:07:14 +05:30
sriramveeraghanta 0887cbbda8 chore(deps): update lodash package 2026-02-09 19:26:39 +05:30
M. PalanikannanandGitHub e1227f0b58 fix: type fix for description payload (#8619)
* fix: type fix

* fix: duplicate type fix
2026-02-09 15:18:09 +05:30
sriramveeraghanta ea7b30bc9c chore(deps): update the node pacakges 2026-02-09 14:59:33 +05:30
Vipin ChaudharyandGitHub dfbd043e50 [WEB-6058] chore : add logic to handle save#8614 2026-02-09 14:53:35 +05:30
sriramveeraghanta 13a679437d chore(deps): upgrade django version 2026-02-09 14:51:53 +05:30
SangeethaandGitHub 78729277e8 [SECUR-104] fix: Arbitrary Modification of API Token Rate Limits#8612 2026-02-09 14:50:29 +05:30
SangeethaandGitHub d191615a5e [SECUR-113] fix: ssrf for work item links (#8607) 2026-02-05 15:03:43 +05:30
sriramveeraghanta 587cb3ecfe fix: file fomatting 2026-02-02 18:08:44 +05:30
Bavisetti NarayanandGitHub b8d3b3c5eb fix: module percentage calculation (#8595) 2026-01-29 14:48:43 +05:30
sriram veeraghantaandGitHub 02d0ee3e0f chore: add copyright (#8584)
* feat: adding new copyright info on all files

* chore: adding CI
2026-01-27 13:54:22 +05:30
Ship itandGitHub 66decf6617 fully translated into Ukrainian language (#8579) 2026-01-27 01:29:56 +05:30
Prateek ShouryaandGitHub 32a2584578 [GIT-66] improvement: prevent disabling last enabled authentication method (#8570) 2026-01-27 00:47:37 +05:30
SangeethaandGitHub f7d5200ed8 migration: back migrate all product tour fields to set true (#8575) 2026-01-27 00:47:02 +05:30
b31c0195bc fix: pdf export (#8564)
* feat: pdf export

* fix: tests

* fix: tests

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2026-01-26 22:08:10 +05:30
M. PalanikannanandGitHub 20e266c9bb fix: node view renders (#8559)
* fix node renders

* fix handlers

* fix: duplicate id
2026-01-23 13:47:49 +05:30
puntoandGitHub 57806f9bd5 [GIT-45] fix: allow markdown file attachments (#8524)
* fix: allow markdown file attachments

- Add text/markdown to ATTACHMENT_MIME_TYPES
- Fixes issue where .md files were rejected with 'Invalid file type' error

* added the support for frontend mime type too
2026-01-23 13:38:47 +05:30
Aaryan KhandelwalandGitHub db8b67102d [WEB-5860] [WEB-5861] [WEB-5862] style: improved settings interface (#8520)
* style: improved profile settings

* chore: minor improvements

* style: improved workspace settings

* style: workspace settings content

* style: improved project settings

* fix: project settings flat map

* chore: add back navigation from settings pages

* style: settings content

* style: estimates list

* refactor: remove old code

* refactor: removed unnecessary line breaks

* refactor: create a common component for page header

* chore: add fade-in animation to sidebar

* fix: formatting

* fix: project settings sidebar header

* fix: workspace settings sidebar header

* fix: settings content wrapper scroll

* chore: separate project settings features

* fix: formatting

* refactor: custom theme selector

* refactor: settings headings

* refactor: settings headings

* fix: project settings sidebar padding

* fix: sidebar header padding

* fix: sidebar item permissions

* fix: missing editable check

* refactor: remove unused files

* chore: remove unnecessary code

* chore: add missing translations

* fix: formatting
2026-01-23 13:34:20 +05:30
SangeethaandGitHub ba5ba5bf54 [GIT-61] chore: allow .md files to be uploaded (#8571)
* chore: allow .md files to be uploaded

* chore: allow .md files to be uploaded
2026-01-23 13:33:20 +05:30
yyandGitHub bb4f172e26 chore: fix typos in comments (#8553) 2026-01-23 13:32:04 +05:30
2a29ab8d4a [WEB-5845] chore: changing description field to description json (#8230)
* chore: migrating description to description json

* chore: replace description with description_json

* chore: updated migration file

* chore: updated the migration file

* chore: added description key in external endpoint

* chore: updated the migration file

* chore: updated the typo

---------

Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
2026-01-22 18:23:59 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6c8779c8d3 chore(deps): bump lodash-es in the npm_and_yarn group across 1 directory (#8573)
Bumps the npm_and_yarn group with 1 update in the / directory: [lodash-es](https://github.com/lodash/lodash).


Updates `lodash-es` from 4.17.21 to 4.17.23
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash-es
  dependency-version: 4.17.23
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-22 15:47:30 +05:30
Aaryan KhandelwalandGitHub f7debcde79 [WEB-5898] chore: update tailwind config #8516 2026-01-19 20:42:44 +05:30
Aaryan KhandelwalandGitHub 3de76206b5 [WIKI-735] fix: table insert handle z-index #8545 2026-01-19 20:41:50 +05:30
Vamsi KrishnaandGitHub f783447796 [WEB-5907] fix: magic code sign-in at Space app. #8552 2026-01-19 20:41:00 +05:30
SangeethaandGitHub fa1b4a102a [WEB-5890] migration: added getting_started_checklist, tips, explored_feature fields on the workspace member table (#8489)
* migration: added getting_started_checklist and tips field

* fix: remove defaults and added explored_features field

* fix: added user table migration
2026-01-15 14:25:31 +05:30
sriramveeraghanta 8399f64bee chore(deps): react router upgraded 2026-01-09 14:43:36 +05:30
Bavisetti NarayanandGitHub 5f3f9d2623 chore: updated migration file name (#8515) 2026-01-09 14:36:45 +05:30
Prateek ShouryaandGitHub 110dbd9acd [VPAT-50] chore(security): add X-Frame-Options header to nginx configuration to prevent clickjacking attacks (#8507)
* [VPAT-50] chore(security): add X-Frame-Options header to nginx configuration to prevent clickjacking attacks

* [SECURITY] chore: enhance nginx configuration with additional security headers
2026-01-09 04:03:41 +05:30
Vipin ChaudharyandGitHub b83d460938 [WIKI-826] chore: add unique id as key to logo selector (#8494) 2026-01-07 15:05:14 +05:30
ea1f92e0c6 [WEB-5537]refactor: rename IssueUserProperty to ProjectUserProperty and update related references (#8206)
* refactor: rename IssueUserProperty to ProjectUserProperty and update related references across the codebase

* migrate: move issue user properties to project user properties and update related fields and constraints

* refactor: rename IssueUserPropertySerializer and IssueUserDisplayPropertyEndpoint to ProjectUserPropertySerializer and ProjectUserDisplayPropertyEndpoint, updating all related references

* fix: enhance ProjectUserDisplayPropertyEndpoint to handle missing properties by creating new entries and improve response handling

* fix: correct formatting in migration for ProjectUserProperty model options

* migrate: add migration to update existing non-service API tokens to remove workspace association

* migrate: refine migration to update existing non-service API tokens by excluding bot users from workspace removal

* chore: changed the project sort order in project user property

* chore: remove allowed_rate_limit from APIToken

* chore: updated user-properties endpoint for frontend

* chore: removed the extra projectuserproperty

* chore: updated the migration file

* chore: code refactor

* fix: type error

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: sangeethailango <sangeethailango21@gmail.com>
Co-authored-by: vamsikrishnamathala <matalav55@gmail.com>
Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
2026-01-06 15:37:19 +05:30
Prateek ShouryaandGitHub 3d5e427894 chore: remove unused right sidebar component and clean up workspace member settings (#8477) 2025-12-31 00:08:40 +05:30
sriram veeraghantaandGitHub 6129e8e53b Revert "[WEB-4959]chore: refactor project member page #8464" (#8476)
This reverts commit c97e418515.
2025-12-30 21:46:32 +05:30
sriram veeraghantaandGitHub d61b157929 chore: remove posthog events (#8465)
* chore: remove posthog events

* chore: remove event tracking

* chore: lint errors

* chore: minor changes based on comments

* fix: type errors
2025-12-30 19:59:50 +05:30
NikhilandGitHub a37e5e2f6a [WEB-5598] refactor: streamline object creation in workspace seed task and improve error handling in workspace creation #8264 2025-12-30 17:28:33 +05:30
lifandGitHub a976bea5af fix: correct spelling error in database.ts log message (#8452)
Fix "convertion" -> "conversion" in error log message.

Signed-off-by: majiayu000 <1835304752@qq.com>
2025-12-30 17:23:42 +05:30
1072509642 migration: added webhook version, navigation related fields and allowed_rate_limit for APIToken (#8339)
* migration: added version field in webhook

* chore: add max_length

* chore: added product tour fields

* chore: updated the migration file

* chore: removed the duplicated migration file

* chore: added allowed_rate_limit for api_tokens

* chore: changed key feature tour to product tour

* chore: added is_subscribed_to_changelog field

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2025-12-30 16:18:58 +05:30
Jayash TripathyandGitHub dcd8d27eae [WEB-5472] refactor: project form #8472 2025-12-30 15:57:33 +05:30
b-saikrishnakanthandGitHub c97e418515 [WEB-4959]chore: refactor project member page #8464 2025-12-30 14:59:16 +05:30
Aaryan KhandelwalandGitHub 031baaa162 [WEB-857] regression: image uploader error state #8471 2025-12-30 14:35:52 +05:30
Jayash TripathyandGitHub 9141a9377f [WEB-5472] refactor: components of project creation flow (#8462) 2025-12-30 14:32:48 +05:30
Anmol Singh BhatiaandGitHub 866338289e chore: navigation preference enhancements (#8468) 2025-12-30 13:22:28 +05:30
Prateek ShouryaandGitHub 8d479ac24c [WEB-5822] fix: migrate ImagePickerPopover to Propel Tabs component and render only enabled tabs #8290
- Replace custom tab implementation with Propel Tabs
- Dynamically render only enabled tabs based on configuration
- Filter tabs by isEnabled property for cleaner conditional rendering
- Improve tab navigation and accessibility with Propel components
2025-12-29 19:54:32 +05:30
lifandGitHub 94d5779f3a chore: remove unused get_client_ip import (#8453)
Remove unused import `get_client_ip` from workspace/invite.py.
Identified by ruff linter (F401 error).

Signed-off-by: majiayu000 <1835304752@qq.com>
2025-12-29 18:39:54 +05:30
b-saikrishnakanthandGitHub 20492ffa75 [WEB-5790] feat: new email templates (#8423) 2025-12-29 16:17:29 +05:30
Anmol Singh BhatiaandGitHub 630d2b9600 [WEB-5179] chore: icon utils code refactor #8458 2025-12-29 13:31:27 +05:30
Shaikh NaasirandGitHub ec215bb3b1 Typo: database extension error message (#8461) 2025-12-29 13:26:09 +05:30
Bavisetti NarayanandGitHub 3241693d6d [WEB-5815] chore: removed the deleted states (#8457) 2025-12-26 20:28:33 +05:30
Aaryan KhandelwalandGitHub 6cc5c1946a regression: red and green color backgrounds (#8456) 2025-12-26 19:17:03 +05:30
Anmol Singh BhatiaandGitHub 2980c2d76b refactor: actions icon migration (#8219)
* chore: gitignore updated

* chore: check icon added to propel package

* feat: search icon migration

* chore: check icon migration

* chore: plus icon added to propel package

* chore: code refactor

* chore: plus icon migration and code refactor

* chore: trash icon added to propel package

* chore: code refactor

* chore: trash icon migration

* chore: edit icon added to propel package

* chore: new tab icon added to propel package

* chore: edit icon migration

* chore: newtab icon migration

* chore: lock icon added to propel package

* chore: lock icon migration

* chore: globe icon added to propel package

* chore: globe icon migration

* chore: copy icon added to propel package

* chore: copy icon migration

* chore: link icon added to propel package

* chore: link icon migration

* chore: link icon migration

* chore: info icon added to propel package

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor
2025-12-26 17:19:15 +05:30
Aaryan KhandelwalandGitHub 92ac28fcb8 [WEB-5809] refactor: tailwind config inline variables (#8437) 2025-12-26 16:36:29 +05:30
Vamsi KrishnaandGitHub e3ba7c2889 [WEB-5781]chore: removed info banner for preferences #8442 2025-12-26 13:40:15 +05:30
Vamsi KrishnaandGitHub 78eeb9ce0d [WEB-5785]fix: favorites icon size #8449 2025-12-26 13:38:46 +05:30
Prateek ShouryaandGitHub f04be48f61 [WEB-5804] refactor: decouple filter value types from filter configurations (#8441)
* [WEB-5804] refactor: decouple filter value types from filter configurations

Remove value type constraints from filter configurations to support
operator-specific value types. Different operators can accept different
value types for the same filter property, so value types should be
determined at the operator level rather than the filter level.

- Remove generic value type parameter from TFilterConfig
- Update TOperatorConfigMap to accept union of all value types
- Simplify filter config factory signatures across all filter types
- Add forceUpdate parameter to updateConditionValue method

* refactor: remove filter value type constraints from filter configurations

Eliminate the generic value type parameter from filter configurations to allow for operator-specific value types. This change enhances flexibility by enabling different operators to accept various value types for the same filter property.

- Updated TFilterConfig and related interfaces to remove value type constraints
- Adjusted filter configuration methods and types accordingly
- Refactored date operator support to align with the new structure
2025-12-24 21:03:22 +05:30
Vamsi KrishnaandGitHub 5499e49b72 [WEB-5574]chore: notification card refactor (#8234)
* chore: notification card refactor

* chore: moved base activity types to constants package
2025-12-24 20:32:50 +05:30
Prateek ShouryaandGitHub 3c8624b1ba [WEB-5324] refactor: add Unified OAuth Configuration and Missing Gitea Options (#8050)
* refactor: add Unified OAuth Configuration and Missing Gitea Options

- Replaced the AuthenticationModes component with a more streamlined implementation using AuthenticationMethodCard.
- Removed obsolete authentication modes files from the codebase.
- Enhanced the AuthRoot component to utilize the new OAuth configuration hook for better management of authentication options.
- Updated type definitions for instance authentication modes to reflect the new structure.

* refactor: update OAuth type imports and remove obsolete types

- Replaced local type imports with centralized imports from @plane/types in core, extended, and index OAuth hooks.
- Removed the now redundant types.ts file as its definitions have been migrated.
- Enhanced type definitions for OAuth options to improve consistency across the application.

* feat: add new Gitea logo and update OAuth icon imports to use standard HTML img tags

* chore: remove unused authentication logos and upgrade button component
2025-12-24 20:20:12 +05:30
Anmol Singh BhatiaandGitHub 55c49cc633 [WEB-5808] chore: sidebar project list enhancements (#8451)
* chore: sidebar project list enhancements

* chore: code refactor

* chore: code refactor
2025-12-24 19:53:00 +05:30
Vamsi KrishnaandGitHub 5b28327551 [WEB-5782]chore: migrated modals to @plane/ui (#8420)
* chore: migrated modal to @plane/ui

* chore: fixed spacings
2025-12-24 19:45:55 +05:30
39728d4cc4 [WEB-5779] fix: handle loading state while fetching project cover image (#8419)
* refactor: replace cover image handling with CoverImage component across profile and project forms

* fix: extend CoverImage component to accept additional img props

* Update apps/web/core/components/common/cover-image.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: handle undefined cover image URL in ProfileSidebar component

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-24 19:30:01 +05:30
Bavisetti NarayanandGitHub 59f26a80bb [WEB-5667] fix: estimate value display in analytics #8448 2025-12-24 17:34:58 +05:30
SangeethaandGitHub 1a7c537ee5 [WEB-5791] fix: broken favicon in links (#8396)
* fix: using base url of a redirect url

* chore: internal networks check for the final_url

* fix: none final_url

* fix: exception handling

* fix: exception handling

* chore: remove unused imports

* refactor: moved ip address check logic into separate function

* fix: ValueError logic
2025-12-24 17:33:46 +05:30
Prateek ShouryaandGitHub 27a7cdcfc3 [WEB-5581] fix: resolve logo spinner hydration and theme loading issues (#8450)
- Fix hydration mismatch by lazy loading components that depend on theme
- Ensure LogoSpinner renders with correct theme on initial load
2025-12-24 17:29:27 +05:30
Prateek ShouryaandGitHub 0c795e95ac [WEB-5798] refactor: web and admin auth related components and update admin designs (#8431)
* refactor: web and admin auth related components and update admin designs.

* fix: format
2025-12-24 16:31:52 +05:30
sriram veeraghantaandGitHub 777200db7b chore: workspace events (#8439)
* chore: adding invite and joined events

* chore: adding workspace create and update events
2025-12-23 19:47:00 +05:30
Anmol Singh BhatiaandGitHub d09c91b838 [WEB-5772] chore: theme switcher code refactor #8438 2025-12-23 19:42:01 +05:30
Anmol Singh BhatiaandGitHub 2bc7080d24 [WEB-5772] chore: theme switcher and editor colors enhancements (#8436) 2025-12-23 18:09:26 +05:30
Jayash TripathyandGitHub 6cd85a7095 [WEB-5784] fix: truncation of links in work items (#8430) 2025-12-23 15:14:16 +05:30
Anmol Singh BhatiaandGitHub 5590bf7198 [WEB-5772] fix: theme switch flicker (#8428) 2025-12-23 15:01:01 +05:30
Vamsi KrishnaandGitHub 70eea50db5 [WEB-5786] fix: updated font size for dates at Kanban card #8429 2025-12-23 14:32:01 +05:30
Aaryan KhandelwalandGitHub 373e640a25 [WIKI-740] refactor: editor table performance (#8411) 2025-12-23 14:24:05 +05:30
Aaryan KhandelwalandGitHub 21df1028f8 [WEB-5792] regression: editor font family #8427 2025-12-23 14:20:36 +05:30
Anmol Singh BhatiaandGitHub c0552f06f2 [WEB-5788] fix: board layout group by icon #8426 2025-12-23 14:20:04 +05:30
sriram veeraghantaandGitHub dbf84bf6bb docs: update readme with react router badge (#8424)
Updated feature list and modified the local development section.
2025-12-23 01:33:49 +05:30
Jayash TripathyandGitHub cb9b4f7887 [WEB-5784] fix: truncation issue in wi properties (#8422)
* fix: update background surface 2 variables in tailwind config

* fix: improve layout and truncation handling in issue link and list items
2025-12-22 20:31:51 +05:30
Aaryan KhandelwalandGitHub 7a3cebdb6b fix: alpha colors (#8418) 2025-12-22 16:52:09 +05:30
Jayash TripathyandGitHub 5a2e2e7e11 [WEB-5662][WEB-5770] fix: alignment of cycles in sidebar and layout selection dropdown button (#8414) 2025-12-22 16:40:51 +05:30
Aaryan KhandelwalandGitHub e2fcb5044d [WEB-5775] fix: mentions search on empty query #8417 2025-12-22 16:38:53 +05:30
Vamsi KrishnaandGitHub daf65f5ddd [WEB-5776]chore: updated design system for alert modal #8415 2025-12-22 16:38:20 +05:30
Aaryan KhandelwalandGitHub 8a0b710a62 [WEB-5774] fix: editor nodes background colors (#8416) 2025-12-22 16:37:44 +05:30
Aaryan KhandelwalandGitHub 72883e76a5 [WEB-5773] fix: editor image full screen modal (#8413) 2025-12-22 15:50:02 +05:30
Vamsi KrishnaandGitHub 57bbf3cf43 [WEB-5746]fix: workspace member modal z-index #8410 2025-12-22 13:50:50 +05:30
b-saikrishnakanthandGitHub 46453560f2 [WEB-5657] chore: synchronization configuration for multiple providers in authentication adapter #8409 2025-12-22 12:55:19 +05:30
c2ce21e56c [WEB-5657] feat: add synchronization configuration for multiple providers in authentication adapter (#8336)
* feat: add sync functionality for OAuth providers

- Implemented `check_sync_enabled` method to verify if sync is enabled for Google, GitHub, GitLab, and Gitea.
- Added `sync_user_data` method to update user details, including first name, last name, display name, and avatar.
- Updated configuration variables to include sync options for each provider.
- Integrated sync check into the login/signup process.

* feat: add sync toggle for OAuth providers in configuration forms

* fix: remove default value for sync options in OAuth configuration forms

* chore: delete old avatar and upload a new one

* chore: update class method

* chore: add email nullable

* refactor: streamline sync check for multiple providers and improve avatar deletion logic

* fix: ensure ENABLE_SYNC configurations default to "0" for Gitea, Github, Gitlab, and Google forms

* fix: simplify toggle switch value handling in ControllerSwitch component

---------

Co-authored-by: b-saikrishnakanth <bsaikrishnakanth97@gmail.com>
2025-12-22 12:23:39 +05:30
sriram veeraghanta 4908211fe6 chore: update storybook dependency 2025-12-21 23:00:48 +05:30
Anmol Singh BhatiaandGitHub 83496487d4 [WEB-5614] chore: package and layout enhancements #8403 2025-12-21 12:41:11 +05:30
Vamsi KrishnaandGitHub 409a3e84ab [WEB-5768]chore: updated comment UI #8402 2025-12-19 20:02:40 +05:30
Anmol Singh BhatiaandGitHub 313314ebd6 [WEB-5614] chore: sidebar enhancement #8401 2025-12-19 19:09:36 +05:30
Vamsi KrishnaandGitHub 1ad5772694 [WEB-5761]fix: intake spacing issue (#8399) 2025-12-19 19:08:02 +05:30
Jayash TripathyandGitHub 67c39dfc3d [WEB-5762] fix: workitem detail sidebar properties design consistency (#8400) 2025-12-19 18:15:56 +05:30
Aaryan KhandelwalandGitHub 3876bf054c [WEB-5675] chore: implement fontsource as the fonts library (#8398) 2025-12-19 16:21:04 +05:30
Anmol Singh BhatiaandGitHub df69886080 [WEB-5614] chore: work item detail comment and sidebar enhancements (#8397) 2025-12-19 16:09:46 +05:30
Jayash TripathyandGitHub f3d5e7def3 [WEB-5454] fix: optimize date validation logic in CycleCreateUpdateModal #8394 2025-12-19 15:41:21 +05:30
Aaryan KhandelwalandGitHub 1e3514575f fix: custom z-index classNames (#8395) 2025-12-19 15:35:45 +05:30
Aaryan KhandelwalandGitHub 465c99f742 fix: input fields bg (#8389) 2025-12-18 20:25:46 +05:30
Jayash TripathyandGitHub 81dbd5ab19 [WEB-5614] refactor: update styling and structure across various components (#8388) 2025-12-18 20:19:51 +05:30
Aaryan KhandelwalandGitHub cb56fbe3ca fix: image uploader bg in light mode (#8385) 2025-12-18 20:00:57 +05:30
Anmol Singh BhatiaandGitHub 80acecb77a chore: platform layout enhancements (#8386) 2025-12-18 20:00:35 +05:30
Aaryan KhandelwalandGitHub 5e0091e32e fix: font imports (#8387) 2025-12-18 19:59:46 +05:30
Vipin ChaudharyandGitHub 59f024dad3 [WIKI-849] feat: debounce for mention search (#8380) 2025-12-18 18:44:16 +05:30
Aaryan KhandelwalandGitHub ba7b2a3e27 fix: space app default background (#8384) 2025-12-18 18:43:37 +05:30
b-saikrishnakanthandGitHub eafa393524 [WEB-5726] fix: showing an empty state on deleted work item link #8381 2025-12-18 18:41:29 +05:30
Aaryan KhandelwalandGitHub 9747bffbe2 [WEB-5652] fix: kanban quick add UI #8382 2025-12-18 18:40:00 +05:30
Anmol Singh BhatiaandGitHub b165e2a3fe [WEB-5614] chore: platform header and breadcrumb enhancements (#8383) 2025-12-18 18:39:06 +05:30
Aaryan KhandelwalandGitHub 3df58397b5 fix: tooltip imports (#8379) 2025-12-18 17:19:10 +05:30
Anmol Singh BhatiaandGitHub e56417e056 [WEB-5614] chore: primitive token updated (#8378) 2025-12-18 17:18:49 +05:30
Aaryan KhandelwalandGitHub cae66ef853 [WEB-5649] [WEB-5675] fix: local font files #8377 2025-12-18 16:48:50 +05:30
Anmol Singh BhatiaandGitHub 67416935b4 [WEB-5614] chore: platform design token enhancements (#8373) 2025-12-18 16:45:04 +05:30
Aaryan KhandelwalandGitHub 78f4a7294b [WEB-5676] style: gantt column outline #8374 2025-12-18 16:34:21 +05:30
Aaryan KhandelwalandGitHub ad2291dfee [WEB-5660] [WEB-5737] fix: cycle and module sidebar #8375 2025-12-18 16:33:57 +05:30
Jayash TripathyandGitHub c56bb06957 chore: update component styles and class names for consistency across the application (#8376) 2025-12-18 16:33:32 +05:30
Aaryan KhandelwalandGitHub 88f4d8253d chore: replace old classNames (#8372) 2025-12-18 13:08:38 +05:30
Anmol Singh BhatiaandGitHub 9b90fbf5aa [WEB-5641] chore: sub work item quick menu padding #8370 2025-12-17 21:26:42 +05:30
Aaryan KhandelwalandGitHub d9ab60104e [WEB-5742] fix: input field background #8369 2025-12-17 21:23:28 +05:30
Aaryan KhandelwalandGitHub f0b98ceb8f [WEB-5708] style: space app kanban card UI (#8368) 2025-12-17 21:01:55 +05:30
Aaryan KhandelwalandGitHub 4cac953cd1 fix: nested context menu UI (#8367) 2025-12-17 20:46:10 +05:30
Anmol Singh BhatiaandGitHub 508e332a23 [WEB-5614] chore: lucide icon code refactor #8365 2025-12-17 20:30:33 +05:30
Aaryan KhandelwalandGitHub b906d42e1d fix: material icons font file (#8366) 2025-12-17 20:30:11 +05:30
Anmol Singh BhatiaandGitHub 8b0a8160a1 [WEB-5614] fix: sidebar and label dropdown #8364 2025-12-17 20:17:42 +05:30
Aaryan KhandelwalandGitHub b7621c62eb fix: work item property icon renderer (#8363) 2025-12-17 19:56:31 +05:30
Anmol Singh BhatiaandGitHub 617c7ab734 [WEB-5614] chore: logo and icon enhancements #8362 2025-12-17 19:14:43 +05:30
Jayash TripathyandGitHub b675c5c7a7 [WEB-5614] chore: update component styles and class names for consistency across projects #8360 2025-12-17 18:28:29 +05:30
Anmol Singh BhatiaandGitHub 2f80d59ba9 [WEB-5614] fix: empty state and padding token fixes (#8359) 2025-12-17 17:58:22 +05:30
Aaryan KhandelwalandGitHub 8bd23ac44b [WEB-5730] fix: user mention colors #8358 2025-12-17 17:53:22 +05:30
Aaryan KhandelwalandGitHub dbda7504b2 [WEB-5732] style: update work item detail properties UI (#8357) 2025-12-17 17:52:54 +05:30
Anmol Singh BhatiaandGitHub 61db42bcb7 [WEB-5614] chore: custom theme on colour improvement #8356 2025-12-17 16:54:12 +05:30
Aaryan KhandelwalandGitHub 1f06b67c66 regression: replace old css vars with the new design system tokens (#8354)
* chore: replace old css vars

* fix: replace shadow and primary colors

* chore: remove hardcoded colors
2025-12-17 16:28:22 +05:30
Anmol Singh BhatiaandGitHub 1338e9d2a4 [WEB-5614] chore: work item detail and list layout enhancements #8355 2025-12-17 16:18:14 +05:30
Aaryan KhandelwalandGitHub 7c6a6a3432 chore: sync changes (#8343) 2025-12-17 14:07:01 +05:30
Aaryan KhandelwalandGitHub d8930c5188 [WEB-5708] regression: remove material icon instances from the Space app (#8353) 2025-12-17 14:03:29 +05:30
88f5a063d6 [WEB-5614] fix: new design system consistency (#8351)
* chore: tooltip enhancements

* chore: project card enhancements

* chore: work item card enhancements

* chore: update component styles and class names for consistency across the application

---------

Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
2025-12-17 13:32:29 +05:30
SangeethaandGitHub df710e00dc [WEB-5666] chore: set project timezone same as workspace timezone in project (#8340) 2025-12-16 20:46:53 +05:30
b-saikrishnakanthandGitHub e8047b6f1c [WEB-5671] chore: settings workspace members enhancements #8346 2025-12-16 19:11:07 +05:30
Anmol Singh BhatiaandGitHub fa63964566 feat: custom theming enhancements (#8342) 2025-12-16 18:17:59 +05:30
Jayash TripathyandGitHub be1113b170 [WEB-5699] refactor: update styling and classnames of charts according to new design system (#8345)
* refactor: update styling and class names according to new design system in charts

* refactor: clean up
2025-12-16 16:30:29 +05:30
Henit ChobisaandGitHub e7974afd8b [SILO-783] feat: added porters and new serializer based exporter (#8335) 2025-12-16 15:20:53 +05:30
Prateek ShouryaandGitHub fc1c7fe235 [WEB-5668] fix: add fetchWorkspaceLevelProjectEntities method and update project-related fetch keys (#8347) 2025-12-16 13:25:44 +05:30
+1 22339b9786 [WEB-5602] feat: new design system (#8220)
* chore: init tailwind v4

* chore: update all configs

* chore: add source to parse monorepo packages

* chore: combine all css files

* feat: added extended colors

* chore: update typography

* chore: update extended color var names

* refactor: remove initial spacing variable and update dark mode selector

* chore: update css files

* chore: update animations

* chore: remove spacing tokens

* fix: external css files

* chore: update tailwind-merge version

* chore: update font family

* chore: added brief agents.md and story for new design system

* chore: enhance design system documentation with rare exceptions for visual separation

* chore: add fontsource package for typography

* chore: material symbols font added

* chore: update shadow default

* chore: add stroke and outline theme vars

* chore: update ring and fill colors

* chore: overwrite tailwind typography tokens

* chore: add high contrast mode tokens

* chore: update scrollbar colors

* chore: backward compatibility for buttons and placeholders

* chore: add priority colors

* chore: update urgent priority color

* chore: update plan colors

* chore: add missing utility class

* chore: update height and padding classes

* chore: update label colors

* chore: add missing utlity

* chore: add typography plugin to space app

* chore: replace existing classNames with new design system tokens #8244 (#8278)

* chore: update border colors

* chore: update all borders

* chore: update text colors

* chore: update css variables

* chore: update font sizes and weights

* chore: update bg colors

* chore: sync changes

* fix: uncomment spacing-1200 variable in variables.css

* chore: update primary colors

* refactor: updated border to border-subtle

* refactor: update various components and improve UI consistency across the application

* updated classnames

* updated classnames

* refactor: update color-related class names to use new design system variables for consistency

* chore: default automations

* chore: update text sizes

* chore: home and power k

* chore: home and power k

* chore: replace ui package button components

* chore: update text sizes

* chore: updated issue identifier (#8275)

* refactor: top navigation and sidebar design token (#8276)

* chore: update all button components (#8277)

* chore: new button component

* chore: update existing buttons

* chore: overwrite tailwind typography tokens

* fix: twMerge config + fixed cn instances

* refactor: toast design token updated (#8279)

* chore: update existing buttons

* chore: tooltip design token updatged (#8280)

* chore: moved cn utility to propel (#8281)

* chore: update space app UI (#8285)

* chore; update space app filters component

* fix: button whitespace wrap

* chore: space app votes

* chore: update dropdown components

* refactor: auth, onboarding, sidebar, and common component design token migration (#8291)

* chore: checkbox component design token updated

* chore: indicator and oauth component design token updated

* chore: sidebar design token updated

* chore: auth and onboarding design token updated

* chore: update divider color

* style: update background colors and hover effects across list components

* fix: tailwind merge

* refactor: toggle switch design token migration and header utility classname added (#8295)

* chore: toggle component design token updated

* chore: h-header utility class added

* chore: updated color tokens for work item detail page (#8296)

* chore: update react-day-picker UI

* refactor: update button sizes and styles in filters components

* refactor: breadcrumbs design token updated (#8297)

* chore: update priority icon colors

* refactor: updated layout variables

* chore: update plan card primary CTA

* Chore update editor design system (#8299)

* refactor: update styles for callout, color selector, logo selector, and image uploader

* refactor:fix image

* chore: update settings UI

* chore: updated notifications color and size tokens (#8302)

* chore: update sm button border radius

* fix: logo renderer

* chore: icon button component

* chore: remove deprecated classes

* chore: remove deprecated classes

* chore: update editor list spacing

* fix: icon button size

* chore: improvements (#8309)

* chore: update cycles and modules pages

* refactor: update background styles across various components to use new design system colors

* fix: button type errors

* chore: update modals design system (#8310)

* refactor: callout bg

* refactor: code  bg

* refactor: modal size and variant

---------

Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>

* chore: update next-themes

* design: update billing and plans component styles and remove unused utility functions (#8313)

* refactor: empty state design token migration and improvements (#8315)

* fix: profile page

* refactor: tabs design token updated (#8316)

* chore: updated buttons and tokens for work items (#8317)

* fix: adjust trial button spacing in checkout modal

* chore: update add button hover state

* fix: type error (#8318)

* fix: type error

* chore: code refactor

* refactor: update button sizes and background styles in rich filters components

* refactor: update editor bg

* refactor: enhance Gantt chart sidebar functionality and styling

- Removed unused  prop from .
- Updated  to include new props for better block management and scrolling behavior.
- Improved auto-scroll functionality for Gantt chart items.
- Adjusted styles in  component for consistent design.

* regression: gantt design

* chore: new badge component

* fix: favorite star

* chore: update backgroung, typography and button sizes across workspace settings general and members pages

* fix: header button sizes

* fix: emoji icon logo (#8323)

* more fixes

* chore: update settings sidebar

* refactor: avatar component

* chore: updated work item detail sidebar (#8327)

* refactor: update link preview

* fix: work item property dropdowns

* fix: dropdown buttons border radius

* chore: update power k translation

* chore: updated profile activity design (#8328)

* chore: update settings pages

* chore: update work item sidebar alignments (#8330)

* refactor: admin design system

* chore: update page header

---------

Co-authored-by: Jayash Tripathy <76092296+JayashTripathy@users.noreply.github.com>
Co-authored-by: VipinDevelops <vipinchaudhary1809@gmail.com>
Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com>
Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com>
Co-authored-by: gakshita <akshitagoyal1516@gmail.com>
Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: b-saikrishnakanth <bsaikrishnakanth97@gmail.com>
Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com>

* fix: formatting

* reexport types

* fix: lint error

---------

Co-authored-by: Jayash Tripathy <76092296+JayashTripathy@users.noreply.github.com>
Co-authored-by: VipinDevelops <vipinchaudhary1809@gmail.com>
Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com>
Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com>
Co-authored-by: gakshita <akshitagoyal1516@gmail.com>
Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: b-saikrishnakanth <bsaikrishnakanth97@gmail.com>
Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com>
2025-12-12 20:50:14 +05:30
b-saikrishnakanthandGitHub d86418aad8 [WEB-5650] feat: Enable Gitea OAuth configuration (#8325)
* feat: implement OAuth configuration helper and integrate into auth forms

* fix: ensure OAuth providers are disabled by default if not configured
2025-12-12 20:11:35 +05:30
sriramveeraghanta 0263f07977 chore: file formating 2025-12-12 19:41:20 +05:30
Anmol Singh BhatiaandGitHub 2ac5efe2f0 [WEB-5647] chore: list layout work item identifier enhancements (#8326) 2025-12-12 19:21:29 +05:30
Anmol Singh BhatiaandGitHub 1b427392c4 [WEB-5124] chore: intake work item toast enhancements (#8329) 2025-12-12 18:29:11 +05:30
Vamsi KrishnaandGitHub 192ed9ba8d [WEB-5654]fix: custom select selection and dropdown close #8324 2025-12-12 15:43:30 +05:30
sriramveeraghanta 119cf39b4d Merge branch 'canary' of github.com:makeplane/plane into preview 2025-12-12 13:42:48 +05:30
sriramveeraghanta 234b088c4a chore(deps): upgrade next themes package 2025-12-12 13:41:17 +05:30
sriram veeraghantaandGitHub bc37edbe9a release: v1.2.0 #8283 2025-12-11 20:18:14 +05:30
Akshat JainandGitHub 7451d5e125 [INFRA-288] fix: add missing reverse proxy rule for bucket in Caddyfile.ce (#8319)
* fix: add missing reverse proxy rule for bucket in Caddyfile.ce

* fix: correct typo in reverse proxy address in Caddyfile.aio.ce
2025-12-11 19:21:10 +05:30
Akshat JainandGitHub 58e48a995b [INFRA-287] Fix: AIO Docker Image build failure (#8311)
* refactor: update build scripts and supervisor configuration

* Changed Caddyfile source in build.sh to use Caddyfile.aio.ce
* Updated Dockerfile to copy web assets from the correct directory
* Modified supervisor.conf to remove web and admin program configurations, and updated space program command to use react-router-serve

* chore: remove obsolete Caddyfile.aio.ce configuration

* chore: clean up Dockerfile by removing redundant cache removal command

* fix: update live program command to use .mjs extension in supervisor configuration

* fix: update live program command in supervisor configuration to remove .mjs extension

* fix: update reverse proxy ports in Caddyfile.aio.ce configuration

* fix: correct reverse proxy addresses in Caddyfile.aio.ce configuration

* refactor: remove outdated reverse proxy address updates from build script
2025-12-11 18:06:27 +05:30
sriram veeraghanta 248acdc4c1 sync: canary to preview 2025-12-11 17:20:33 +05:30
b-saikrishnakanthandsriram veeraghanta 7b41ffa08e [WEB-5624] chore: added webhook translations #8312 2025-12-11 17:12:19 +05:30
b-saikrishnakanthandGitHub 906c1b51bb [WEB-5624] chore: added webhook translations #8312 2025-12-11 17:02:20 +05:30
8e518966c2 chore: add Plane sync label to github templates #8303
Co-authored-by: Pushya Mitra Thiruvooru <pushya@Pushyas-MacBook-Pro.local>
2025-12-11 14:40:24 +05:30
sriramveeraghanta f8f764622a fix: broken lock file 2025-12-11 14:14:07 +05:30
sriramveeraghanta f5fe8d0a7e chore: sync changes from canary to preview 2025-12-11 14:11:28 +05:30
e0c97c5471 refactor: sanitize HTML function (#8307)
* refactor: replace isomorphic-dompurify with sanitize-html

* dompurify fixes

* more fixes with fallback and title

* build

---------

Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
2025-12-11 13:30:31 +05:30
AaronandGitHub 0370a1bfdd chore: fix/check tooling improvements with turbo (#8304) 2025-12-11 13:03:37 +05:30
sriram veeraghantaandGitHub 5e621cf620 fix: editor sync changes (#8306)
* chore: upate function declarations

* chore: formatted files
2025-12-11 13:01:25 +05:30
sriram veeraghantaandGitHub f1761c65b5 chore: fix ruff checks (#8305) 2025-12-11 01:18:09 +05:30
sriram veeraghantaandGitHub 97e21ba21c chore: format files in API server (#8292) 2025-12-10 23:50:01 +05:30
Dheeraj Kumar KetireddyandGitHub 647813a6ab [WEB-4440] fix: duplicate sequence when creating multiple workitems in rapid succession (#8298)
- Replace advisory lock with transaction-level lock in Issue model save method
- Updated the save method in the Issue model to use a transaction-level advisory lock for better concurrency control.
- Simplified the locking mechanism by removing the explicit unlock step, as the lock is automatically released at the end of the transaction.
- Maintained existing functionality for sequence and sort order management while improving code clarity.
2025-12-10 23:20:41 +05:30
M. PalanikannanandGitHub 76ebf395e6 fix: isomorphic dompurify #8301 2025-12-10 19:51:11 +05:30
M. PalanikannanandGitHub 67dfe91890 fix missing package (#8300) 2025-12-10 19:29:36 +05:30
sriramveeraghanta b53016b449 chore: realtime updates fix 2025-12-10 19:13:36 +05:30
M. PalanikannanandGitHub e20f686398 [WIKI-844] fix: realtime sync post vite migration with title editor sync and indexed db access (#8294)
* fix: robust way to handle socket connection and read from indexeddb cache when reqd

* fix: realtime sync working with failure handling

* fix: title editor added

* merge preview into fix/realtime-sync

* check

* page renderer props

* lint errors

* lint errors

* lint errors

* sanitize html

* sanitize html

* format fix

* fix lint
2025-12-10 19:02:52 +05:30
AaronandGitHub ff544c98b7 chore: optimize turborepo (#8286) 2025-12-10 17:05:14 +05:30
sriram veeraghanta be722f708d chore: moving star us button to the top navigation (#8289) 2025-12-10 15:12:49 +05:30
b-saikrishnakanthandsriramveeraghanta 43b3a7730e [WEB-5608] chore: Hide "Pro" Features in Community Edition (#8288)
* chore: Hide "Pro" Features in Community Edition

* refactor: remove time tracking feature and simplify project features list
2025-12-10 15:12:49 +05:30
sriram veeraghantaandGitHub 20510bb2dd chore: moving star us button to the top navigation (#8289) 2025-12-10 15:11:45 +05:30
b-saikrishnakanthandGitHub 7124e8e7ce [WEB-5608] chore: Hide "Pro" Features in Community Edition (#8288)
* chore: Hide "Pro" Features in Community Edition

* refactor: remove time tracking feature and simplify project features list
2025-12-10 15:11:13 +05:30
Anmol Singh Bhatiaandsriramveeraghanta df1a512a80 [WEB-5609] fix: extended sidebar item pin/unpin #8287 2025-12-10 13:48:58 +05:30
Anmol Singh BhatiaandGitHub 639a2aab41 [WEB-5609] fix: extended sidebar item pin/unpin #8287 2025-12-10 13:48:03 +05:30
NikhilandGitHub 491b5bc6cc [WEB-5575]feat: enhance APITokenLogMiddleware to support logging to MongoDB (#8241)
* feat: enhance APITokenLogMiddleware to support logging to MongoDB

- Added functionality to log external API requests to MongoDB, with a fallback to PostgreSQL if MongoDB is unavailable.
- Implemented error handling for MongoDB connection and logging operations.
- Introduced additional fields for MongoDB logs, including timestamps and user identifiers.
- Refactored request logging logic to streamline the process and improve maintainability.

* fix: improve MongoDB availability checks in APITokenLogMiddleware

- Enhanced the logic for determining MongoDB availability by checking if the collection is not None.
- Added a check for MongoDB configuration before attempting to retrieve the collection.
- Updated error handling to ensure the middleware correctly reflects the state of MongoDB connectivity.

* feat: implement logging functionality in logger_task for API activity

- Added a new logger_task module to handle logging of API activity to MongoDB and PostgreSQL.
- Introduced functions for safely decoding request/response bodies and processing logs based on MongoDB availability.
- Refactored APITokenLogMiddleware to utilize the new logging functions, improving code organization and maintainability.

* refactor: simplify MongoDB logging in logger_task and middleware

- Removed direct dependency on MongoDB collection in log_to_mongo function, now retrieving it internally.
- Updated process_logs to check MongoDB configuration before logging, enhancing error handling.
- Cleaned up logger.py by removing unused imports related to MongoDB.

* feat: add Celery task decorator to process_logs function in logger_task

- Introduced the @shared_task decorator to the process_logs function, enabling asynchronous processing of log data.
- Updated function signature to include a return type of None for clarity.
2025-12-10 01:01:10 +05:30
Vamsi KrishnaandGitHub 368af223db [WEB-5585]chore: timeline chart refactor (#8246)
* chore: timeline chart refactor

* fix: format
2025-12-10 01:00:37 +05:30
Dheeraj Kumar KetireddyandGitHub 8bb7ebb725 [WEB-5285] feat: enhance ChangeTrackerMixin to capture changed fields on save (#8270)
- Added an override for the save method in ChangeTrackerMixin to store changed fields before resetting tracking.
- Implemented a new method, _reset_tracked_fields, to ensure subsequent saves detect changes relative to the last saved state.
- Updated IssueComment to utilize _changes_on_save for determining changed fields, improving accuracy in tracking modifications.
2025-12-10 00:59:57 +05:30
b-saikrishnakanthandGitHub 079a624006 feat: add timezone selection to workspace settings (#8248)
* feat: add timezone selection to workspace onboarding, creation and settings

* refactor: remove timezone selection from workspace creation and onboarding forms
2025-12-10 00:59:39 +05:30
Vipin ChaudharyandGitHub 0bfb74d4c0 [WIKI-830] fix: copy clipboard functionality in the editor (#8229)
* feat: enhance clipboard functionality for markdown and HTML content

* fix: improve error handling and state management in CustomImageNodeView component

* fix: correct asset retrieval query by removing workspace filter in DuplicateAssetEndpoint

* fix: update meta tag creation in PasteAssetPlugin for clipboard HTML content

* feat: implement copyMarkdownToClipboard utility for enhanced clipboard functionality

* refactor: replace copyMarkdownToClipboard utility with copyTextToClipboard for simplified clipboard operations

* refactor: streamline clipboard operations by replacing copyTextToClipboard with copyMarkdownToClipboard in editor components

* refactor: simplify PasteAssetPlugin by removing unnecessary meta tag handling and streamlining HTML processing

* feat: implement asset duplication processing on paste for enhanced clipboard functionality

* chore:remove async from copy markdown method

* chore: add paste html

* remove:prevent default

* refactor: remove hasChanges from processAssetDuplication return type for simplified asset processing

* fix: format options-dropdown.tsx
2025-12-10 00:24:36 +05:30
sriram veeraghanta 362d29c7b0 sync: canary to preview using merge commit 2025-12-09 23:38:41 +05:30
sriram veeraghanta a55253d242 chore: update package version 2025-12-09 23:35:52 +05:30
69b64680d1 [WIKI-829] fix: add option to only show placeholder on empty editor (#8232)
* feat: add placeholderOnEmpty functionality to editor components

* Update packages/editor/src/core/extensions/placeholder.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* refactor: rename placeholderOnEmpty to showPlaceholderOnEmpty across editor components

* chore : make optional

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-09 21:13:20 +05:30
Vamsi KrishnaandGitHub 2f45bfb7f6 [WEB-5256]chore: quick actions refactor (#8019)
* chore: quick actions refactor

* chore: lint fix

* chore: unified factory for actions

* chore: lint fix

* * chore: removed redundant files
* chore: updated imports

* chore: updated interfaces to types

* chore: updated undefined handling
2025-12-09 21:12:15 +05:30
NikhilandGitHub 4b59998e52 [WEB-5592] chore: add static files update settings for static files support (#8251)
* chore: add static files collection and update settings for static files support

* chore: add WhiteNoise middleware for static file handling

* chore(deps): upgrade WhiteNoise to version 6.11.0 and add static file reverse proxy in Caddyfile
2025-12-09 21:05:26 +05:30
b-saikrishnakanthandGitHub f70384bff7 [WEB-5603] feat: enhance workspace settings layout and members page (#8266)
* feat: enhance workspace settings layout and members page with new components

* refactor: update workspace settings layout and members page to use default exports

* refactor: settings layout import changes

* refactor: simplify workspaceSlug usage in settings layout
2025-12-09 21:04:33 +05:30
pratapalakshmiandGitHub 7caa1bb482 [PAI-963] feat: enhance CustomSelect component with context for dropdown management (#8202)
* feat: enhance CustomSelect component with context for dropdown management

* refactor: streamline CustomSelect component structure and improve dropdown options rendering
2025-12-09 20:57:15 +05:30
Anmol Singh BhatiaandGitHub af939fca41 [WEB-5604] Label layout title column and content wrapper styling #8273 2025-12-09 18:37:46 +05:30
sriram veeraghantaandGitHub a3e651854a fix: updated npmrc with hoist patterns (#8271) 2025-12-09 16:32:49 +05:30
NikhilandGitHub 2240ac0e74 [WEB-5583]feat: add avatar download and upload functionality in authentication adapter (#8247)
* feat: add avatar download and upload functionality in authentication adapter

- Implemented `download_and_upload_avatar` method to fetch and store user avatars from OAuth providers.
- Enhanced user data saving process to include avatar handling.
- Updated `S3Storage` class with a new `upload_file` method for direct file uploads to S3.

* feat: enhance avatar download functionality with size limit checks

- Added checks for content length before downloading avatar images to ensure they do not exceed the maximum allowed size.
- Implemented chunked downloading of avatar images to handle large files efficiently.
- Updated the upload process to return None if the upload fails, improving error handling.

* feat: improve avatar filename generation with content type handling

- Refactored avatar download logic to determine file extension based on the content type from the response headers.
- Removed redundant code for extension mapping, ensuring a cleaner implementation.
- Enhanced error handling by returning None for unsupported content types.

* fix: remove authorization header for avatar download

- Updated the avatar download logic to remove the Authorization header when token data is not present, ensuring compatibility with scenarios where authentication is not required.

* feat: add method for avatar download headers

- Introduced `get_avatar_download_headers` method to centralize header management for avatar downloads.
- Updated `download_and_upload_avatar` method to utilize the new header method, improving code clarity and maintainability.
2025-12-09 15:48:27 +05:30
AaronandGitHub 11e7bd115b docs: eslint (#8269) 2025-12-09 13:26:22 +05:30
0ab94ed6d6 chore: run fixes (#8257)
* chore: run fixes

* fix: type, just use hocuspocusservercontext

* fix: codemod

---------

Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
2025-12-08 22:26:50 +05:30
a9e9cb2983 fix: activity tracking description (#8268)
* feat: add no_activity flag to control issue activity tracking during partial updates

* refactor: rename no_activity flag to skip_activity for clarity in issue activity tracking

* enhance description input handling with migration update support

* feat: implement skip_activity flag to conditionally log issue updates during partial updates

* refactor: skip-activity

* feat: add migration description update check to conditionally log issue updates

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
2025-12-08 22:18:14 +05:30
Anmol Singh BhatiaandGitHub f0bc2bd3bd [WEB-5600] chore: project identifier char limit updated and table layout enhancements (#8263) 2025-12-08 20:00:54 +05:30
Prateek ShouryaandGitHub 7659997b53 [WEB-5594] feat: enhance authentication method handling in member columns and introduce new login labels (#8260) 2025-12-08 18:25:48 +05:30
Prateek ShouryaandGitHub 316856a555 [WEB-5599] refactor: enhance Kanban swimlane components with improved props and structure (#8262) 2025-12-08 18:17:29 +05:30
Anmol Singh BhatiaandGitHub f41e121e58 [WEB-5317] chore: enable multi-select for use case in onboarding flow (#8049)
* chore: update use_case type from string to array

* chore: convert use_case field to JSONField with array support

* feat: implement multi-select UI for use case in onboarding

* chore: code refactor

* chore: revert backend changes

* chore: code refactor

* chore: code refactor

* chore: code refactor
2025-12-08 15:48:15 +05:30
sriram veeraghantaandGitHub 85daa1572c fix: upgrade turbo version (#8259) 2025-12-08 15:46:23 +05:30
Aaryan KhandelwalandGitHub b359849606 [WIKI-840] chore: accept spaces while searching for mentions #8258 2025-12-08 15:16:22 +05:30
b-saikrishnakanthandGitHub 5ea74e1112 [WEB-5591] chore: update discount info component to use local SVG assets #8250 2025-12-08 13:47:26 +05:30
Anmol Singh BhatiaandGitHub d9ea119588 [WEB-5589] chore: intake icon updated #8249 2025-12-08 13:46:23 +05:30
NikhilandGitHub d34c7f9a0e [WEB-5254] chore: add bot user to create workspace seed data (#8225)
* chore: add bot user to workspace seed task

* refactor: use BotTypeEnum for bot type in workspace seed task

* refactor: update bot user display name and last name in workspace seed task

* fix: return empty dictionary for missing cycle and module seeds in workspace seed task
2025-12-05 18:03:58 +05:30
NikhilandGitHub f81e120328 chore(deps): upgrade psycopg packages to version 3.3.0 (#8222)
* chore(deps): upgrade psycopg packages to version 3.3.0

* chore: update Python version to 3.12.x in CI workflow

* refactor: clean up imports and improve code formatting across multiple files
2025-12-05 16:06:19 +05:30
85d90030cf fix: eslint (#8185)
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
2025-12-05 16:03:51 +05:30
M. PalanikannanandGitHub 82c970ac4b [WIKI-804] fix: refactor image uploader (#8210)
* fix: refactor uploader

* fix: props

* fix: sites fix
2025-12-05 13:20:15 +05:30
Anmol Singh BhatiaandGitHub 392c8cf2e1 [WEB-5582] chore: top nav and user menu improvement (#8245)
* chore: user menu code refactor

* chore: CustomizeNavigationDialog enhancements
2025-12-04 21:25:09 +05:30
sriram veeraghantaandGitHub c3906048ea fix(CI): upgrade prettier oxc plugin version (#8242)
* fix: upgrade prettier oxc plugin version

* fix: type errors
2025-12-04 18:27:50 +05:30
Anmol Singh BhatiaandGitHub 1090b3e938 [WEB-5573] refactor: app rail enhancements (#8239)
* chore: app rail context added

* chore: dock/undock app rail implementation

* chore: refactor

* chore: code refactor

* chore: code refactor
2025-12-04 18:14:59 +05:30
sriramveeraghanta fe867135c4 Merge branch 'preview' of github.com:makeplane/plane into canary 2025-12-04 16:35:46 +05:30
sriramveeraghanta 65166d19be fix: docker ngnix version 2025-12-04 16:35:19 +05:30
sriramveeraghanta 3bc85c8430 fix: docker pnpm installation setup 2025-12-04 16:30:42 +05:30
sriramveeraghanta cf84ade52c fix: turbo version upgrade in docker files 2025-12-04 16:11:59 +05:30
Anmol Singh BhatiaandGitHub 9699a01fb8 [WEB-5572] fix storybook 500 error #8231 2025-12-04 15:37:14 +05:30
Prateek ShouryaandGitHub bc96d34cb7 [WEB-5576] fix: remove optionsClassName from email and workspace forms (#8240) 2025-12-04 15:36:39 +05:30
Vipin ChaudharyandGitHub 739b1bfb75 fix: undo unique id (#8238) 2025-12-04 14:45:43 +05:30
NikhilandGitHub 22da26742c chore(deps): update Django version to 4.2.27 in base requirements (#8237) 2025-12-04 14:07:52 +05:30
Prateek ShouryaandGitHub 63f3a57022 chore: add permanent redirects for /spaces and /god-mode in Caddyfile (#8236) 2025-12-04 14:03:07 +05:30
36d42856e9 [WEB-5493] feat: implement static cover image handling and selection (#8184)
* feat: implement cover image handling and static image selection

- Added functionality to handle cover image uploads and selections in project and profile forms.
- Introduced a new helper for managing cover images, including static images and uploaded assets.
- Updated components to utilize the new cover image helper for displaying and processing cover images.
- Added a set of static cover images for selection in the image picker.
- Enhanced error handling for image uploads and processing.

This update improves the user experience by providing a more robust cover image management system.

* refactor: rename STATIC_COVER_IMAGES_ARRAY to STATIC_COVER_IMAGES for consistency

- Updated the cover image helper to export STATIC_COVER_IMAGES instead of STATIC_COVER_IMAGES_ARRAY.
- Adjusted the ImagePickerPopover component to utilize the renamed export for rendering static cover images.

* feat: enhance project creation and image handling

- Introduced default project form values with a random emoji for logo props.
- Updated cover image handling in various components, ensuring consistent usage of the new cover image helper.
- Refactored image picker to improve search functionality and loading states.
- Removed unused constants and streamlined cover image type checks for better clarity and performance.

This update enhances the user experience in project creation and image selection processes.

* refactor: simplify cover image type definition and clean up code

- Removed duplicate type from TCoverImageType, streamlining the definition.
- Cleaned up whitespace in the cover image helper for improved readability.

This update enhances code clarity and maintains consistency in cover image handling.

* refactor: update cover image type definitions and simplify logic

- Changed ICoverImageResult and ICoverImagePayload interfaces to type aliases for better clarity.
- Simplified the logic in getCoverImageDisplayURL function to enhance readability and maintainability.

This update improves the structure and clarity of the cover image helper code.

* refactor: remove unused project cover image endpoint and update cover image handling

- Removed the ProjectPublicCoverImagesEndpoint and its associated URL from the project.
- Updated the cover image handling in the cover-image helper to utilize imported assets instead of static paths.
- Cleaned up the ProjectFavoritesViewSet and FileService by removing the now obsolete getProjectCoverImages method.

This update streamlines the cover image management and eliminates unnecessary code, enhancing overall maintainability.

* refactor: update cover image imports to new asset structure

- Replaced static path imports for cover images with updated paths to the new asset structure.
- This change improves organization and maintainability of cover image assets in the project.

This update aligns with recent refactoring efforts to streamline cover image handling.

* feat: add additional cover images to the helper

- Imported new cover images (24 to 29) into the cover-image helper.
- This update expands the available cover image options for use in the project, enhancing visual variety.

* refactor: remove ProjectPublicCoverImagesEndpoint from project URLs and views

* refactor: update cover image imports to include URL query parameter

- Modified cover image imports in the cover-image helper to append a URL query parameter for better asset handling.
- This change enhances the way cover images are processed and utilized within the project.

* refactor: extract default project form values into a utility function

- Created a new utility function `getProjectFormValues` to encapsulate the default project form values.
- Updated the `CreateProjectForm` component to use this utility function for setting default form values, improving code organization and maintainability.

* feat: integrate project update functionality in CreateProjectForm

- Added `updateProject` method to the `CreateProjectForm` component for updating project cover images after creation.
- Enhanced cover image handling by ensuring the correct URL is set for both uploaded and existing cover images.
This update improves the project creation workflow and ensures cover images are accurately updated.

* fix: update documentation for cover image handling

- Corrected the comment regarding local static images to reflect that they are served from the assets folder instead of the public folder.
- This change ensures accurate documentation for the `getCoverImageType` and `getCoverImageDisplayURL` functions, improving clarity for future developers.

* feat: implement random cover image selection for project forms

- Replaced the default cover image URL with a new utility function `getRandomCoverImage` that selects a random cover image from the available options.
- Updated the `getProjectFormValues` function to utilize this new method, enhancing the project creation experience with varied cover images.

---------

Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
2025-12-03 16:20:38 +05:30
Anmol Singh BhatiaandGitHub 105ac5ece5 [WEB-5569] chore: top nav search enhancements (#8226)
* chore: top nav power k search menu enhancements

* chore: expandable search panel refactor
2025-12-03 16:09:12 +05:30
SangeethaandGitHub b8a41ad5a0 [WEB-5560] fix: restrict guest users to view all details of a workspace members (#8215)
* fix: separate retrieve method in WorkspaceMemberViewSet

* fix: non project members accessing member detail:

* chore: error handle

* fix: role based response

* fix: use Enum
2025-12-03 16:06:46 +05:30
7c74d0a403 [WEB-5290] feat: selfhosted check (#8227)
* feat: add in common py

* fix: update marketing consent screen based on is self managed flag

* improvement: enhance ImagePickerPopover with dynamic tab options based on Unsplash configuration

* refactor: product updates modal to include changelog

* [WEB-5290] feat: implement fallback for product updates changelog with loading state and error handling

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-12-03 15:53:42 +05:30
Gustavo de OliveiraandGitHub 5f7ffcb37a - Add SIGNED_URL_EXPIRATION environment variable (#8136)
- Update S3Storage to use configurable expiration time
  - Default remains 3600 seconds (1 hour) for backward compatibility
  - Add comprehensive unit tests with mocked S3 client
  - Update .env.example with documentation and examples
2025-12-03 10:52:19 +05:30
Samuel TorresandGitHub 584a1aa725 fix: PATCH endpoint project estimate validation (#7872) 2025-12-03 01:44:31 +05:30
Saurabh KumarandGitHub 1e1520b85f [SILO-699] chore: add check for feature enabled for module and cycle create (#8146)
* add check for feature enabled for module and cycle create

* add more checks
2025-12-03 01:26:52 +05:30
b-saikrishnakanthandGitHub 0bc45e3047 [WEB-5503] fix: enhance error handling for label creation and update operations (#8175)
* fix: enhance error handling for label creation and update operations

* fix: improve error handling for label creation and update operations

* refactor: change error codes from enum to object for label operations

* refactor: update error code references in label handling to use consistent naming

* fix: improve error handling for label creation and update
2025-12-03 00:05:44 +05:30
Prateek ShouryaandGitHub cacd1b489e [WEB-5559] improvement: chat support functionality and remove Intercom provider (#8217)
* [WEB-5559] improve: chat support functionality and remove Intercom provider

- Added ChatSupportModal component for chat support integration.
- Replaced IntercomProvider with ChatSupportModal in AppProvider.
- Introduced useChatSupport hook to manage chat support state and actions.
- Updated help commands to utilize chat support instead of Intercom.
- Removed obsolete Intercom-related components and hooks.

* refactor: lazy load ChatSupportModal in AppProvider
2025-12-03 00:03:56 +05:30
Anmol Singh BhatiaandGitHub e650b19933 [WEB-5556] chore: tab navigation and sidebar improvements (#8218) 2025-12-02 18:05:01 +05:30
sriram veeraghantaandGitHub ec478a8979 chore: standard replacement from dotenv to dotenvx (#8216) 2025-12-02 15:32:00 +05:30
sriramveeraghanta e07d91e857 fix: update express version in overrides 2025-12-02 15:09:25 +05:30
sriramveeraghanta 9ccb3e321a chore(deps): upgrade express version 2025-12-02 15:01:12 +05:30
sriramveeraghanta f2c046dff9 fix: update mdast-util-to-hast package in overrides 2025-12-02 14:58:06 +05:30
Anmol Singh BhatiaandGitHub 2a378b3bc1 [WEB-5556] chore: tab navigation project header enhancement (#8212) 2025-12-02 14:35:19 +05:30
SangeethaandGitHub 8b0a797906 fix: import error (#8213) 2025-12-02 13:55:54 +05:30
Saurabh KumarandGitHub cea6f7530b [SILO-671] feat: add sticky external apis (#8139)
* add sticky external apis

* add created_at sort by to list

* remove select related method from query set
2025-12-01 18:57:54 +05:30
NikhilandGitHub a7e2e596bf [WEB-5518]: update magic code token to send code as 6 digit numbers (#8188)
* refactor: simplify token generation in MagicCodeProvider by using a numeric approach

* fix: update placeholder text for unique code input across multiple languages

* refactor: replace token generation with a numeric approach for user email updates

* fix: update placeholder text for unique code input in multiple languages to a numeric format

* refactor: replace random token generation with secrets for enhanced security in user email updates and magic code provider
2025-12-01 18:56:14 +05:30
Anmol Singh BhatiaandGitHub 980428b204 [WEB-5536] feat: prevent search panels from reopening on programmatic focus restoration (#8207) 2025-12-01 18:37:06 +05:30
Anmol Singh BhatiaandGitHub f428c3bdaf [WEB-5542] chore: scrollIntoView implementation for sidebar active project (#8208) 2025-12-01 17:43:05 +05:30
Prateek ShouryaandGitHub a05cd88a53 [WEB-5491] refactor: onboarding tour components (#8167) 2025-12-01 17:14:28 +05:30
60220801ac [WEB-4338] fix: incorrect error code in project retrieve API (#7234)
* fix: project error message and status code

* fix: incorrect member role check

* fix: project error message and status code

* fix: improve project permission checks and error handling in ProjectViewSet

* feat: enhance project settings layout with better loading strategy and fix all flicker

* fix: prevent rendering during project loading in ProjectAuthWrapper

* refactor: adjust layout structure in ProjectDetailSettingsLayout and enhance access restriction logic in ProjectAccessRestriction

* refactor: replace ProjectAccessRestriction component with updated version and enhance error handling

- Deleted the old ProjectAccessRestriction component.
- Introduced a new ProjectAccessRestriction component with improved error handling and user prompts for joining projects.
- Updated translations for new error states in multiple languages.

* fix: enhance error handling in IssueDetailsPage and remove JoinProject component

---------

Co-authored-by: Dheeraj Kumar Ketireddy <dheeru0198@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
2025-12-01 17:14:01 +05:30
Vipin ChaudharyandGitHub 8db95d9ec0 [WIKI-823] fix: callout element emoji search (#8201)
* fix: emoji search input

* fix: enhance keyboard navigation in EmojiPicker component
2025-12-01 13:37:20 +05:30
b-saikrishnakanthandGitHub 27bf2575bd [WEB-5534] fix: intake state dropdown (#8199) 2025-12-01 12:50:01 +05:30
b-saikrishnakanthandGitHub 22bb3c5ecc [WEB-5490] fix: intake section filter persists incorrectly across projects (#8187) 2025-12-01 12:49:05 +05:30
Aaryan KhandelwalandGitHub 6b85d67f6c [WIKI-788] regression: copy markdown option #8200 2025-12-01 12:48:28 +05:30
Prateek ShouryaandGitHub 123f59e74b [WEB-5525] improvement: update WorkspaceMenuRoot to use variant prop for layout adjustments (#8196) 2025-11-28 19:00:25 +05:30
sriram veeraghantaandGitHub c7bf912cf2 fix: state group choices (#8198) 2025-11-28 18:06:00 +05:30
Aaryan KhandelwalandGitHub 2980836015 [WEB-5527] fix: extended sidebar (#8197) 2025-11-28 16:54:53 +05:30
78fbdde165 [WEB-5282] chore: triage state in intake (#8135)
* chore: traige state in intake

* chore: triage state changes

* feat: implement intake state dropdown component and integrate into issue properties

* chore: added the triage state validation

* chore: added triage state filter

* chore: added workspace filter

* fix: migration file

* chore: added triage group state check

* chore: updated the filters

* chore: updated the filters

* chore: added variables for intake state

* fix: import error

* refactor: improve project intake state retrieval logic and update TriageGroupIcon component

* chore: changed the intake validation logic

* refactor: update intake state types and clean up unused interfaces

* chore: changed the state color

* chore: changed the update serializer

* chore: updated with current instance

* chore: update TriageGroupIcon color to match new intake state group color

* chore: stringified value

* chore: added validation in serializer

* chore: added logger instead of print

* fix: correct component closing syntax in ActiveProjectItem

* chore: updated the migration file

* chore: added noop in migation

---------

Co-authored-by: b-saikrishnakanth <bsaikrishnakanth97@gmail.com>
2025-11-28 16:16:48 +05:30
b-saikrishnakanthandGitHub dbc5a6348d fix: disable timezone selection button for non-admin users (#8195) 2025-11-28 16:14:43 +05:30
Aaryan KhandelwalandGitHub c685042a47 regression: projects breadcrumb in accordion layout (#8194) 2025-11-28 14:05:38 +05:30
Vipin ChaudharyandGitHub a4de486cf7 [WIKI-811] fix: ensure only non-deleted project pages are retrieved in page queries (#8182)
* fix: ensure soft delete handling for pages in PageViewSet methods

* refactor: streamline query for project IDs in PageDuplicateEndpoint

* refactor: remove soft delete condition from ProjectPage queries in PageViewSet and PageDuplicateEndpoint

* refactor: simplify ProjectPage query in PageViewSet for improved readability

* refactor: replace filter with get for Page queries in PageViewSet and PageDuplicateEndpoint to enhance clarity

* refactor: replace filter with get for Page queries in PagesDescriptionViewSet to improve efficiency
2025-11-27 20:55:50 +05:30
Prateek ShouryaandGitHub 3c84e75350 [WEB-5510] fix: handle null values in context indicator for improved stability (#8190) 2025-11-27 20:53:42 +05:30
Aaryan KhandelwalandGitHub 39749106a2 regression: sidebar toggle button position (#8186)
* fix: sidebar toggle button position

* chore: remove border radius
2025-11-27 20:53:12 +05:30
Aaryan KhandelwalandGitHub 9bcb1fa469 [WEB-5515]: comments ordering (#8193)
* fix: comments ordering

* fix: comment timestamp:
2025-11-27 20:51:06 +05:30
SangeethaandGitHub c31a225775 [WEB-5506] fix: new navigation pre release bugs (#8181)
* chore: update navigation_project_limit and navigation_control_preference

* chore: set default true for user specific widgets

* chore: use serializer in ProjectMemberPreferenceEndpoint
chore: use serializer in WorkspaceUserPropertiesEndpoint
"

* fix: validate preferences

* fix: status code

* fix: remove saving from validate

* fix: simply validate_preferences

* chore: create WorkspaceUserProperties if it doesn't exist

* fix: create WorksapceUserProperties it not exist

* fix: copy the instance

* Revert "fix: copy the instance"

This reverts commit ddb0384b6d.

* chore: migrate WorkspaceUserPreference to set defaults

* fix: migration file name

* Revert "fix: migration file name"

This reverts commit 80a21dedf1.

* Revert "chore: migrate WorkspaceUserPreference to set defaults"

This reverts commit 25bc583a08.
2025-11-27 18:12:20 +05:30
sriramveeraghanta 73c317f283 chore(deps): adding pacakges to resolutions to resolve vulnerbailities 2025-11-27 16:37:36 +05:30
Aaryan KhandelwalandGitHub a0da806a79 [WEB-5520] fix: comments UI in space app #8189 2025-11-27 15:58:10 +05:30
Aaryan KhandelwalandGitHub eddf80aaed [WEB-5511] regression: revamped navigation UI bugs (#8183) 2025-11-26 18:51:03 +05:30
Aaryan KhandelwalandGitHub 05b1c147a9 [WEB-5506] regression: navigation revamp bugs (#8180) 2025-11-26 16:25:29 +05:30
Prateek ShouryaandGitHub ae7898aaee fix: move X-Frame-Options header to auth page for enhanced security (#8179) 2025-11-26 13:57:46 +05:30
Anmol Singh BhatiaandGitHub 4806bdf99c [WEB-5170] feat: navigation revamp (#8162) 2025-11-26 12:56:11 +05:30
Prateek ShouryaandGitHub 37c59ef0d1 [WEB-5507] fix: remove module checks in analytics sidebar link handlers (#8177) 2025-11-26 00:02:14 +05:30
Prateek ShouryaandGitHub 3f11183768 [WEB-5508] fix: improve error handling in AuthFormRoot by ensuring safe access to error_code (#8178) 2025-11-25 23:59:21 +05:30
Aaryan KhandelwalandGitHub d38147b875 fix: build error (#8171) 2025-11-25 19:15:35 +05:30
Prateek ShouryaandGitHub 3436c4f1f5 [WEB-5501] refactor: optimize component structures and improve hooks (#8174)
* [WEB-5501] refactor: optimize component structures and improve hooks

- Updated type definitions in AppProvider to use React.ReactNode for children.
- Enhanced HomePeekOverviewsRoot by using MobX observer and integrating issue detail hook.
- Optimized ContentOverflowWrapper to prevent unnecessary re-renders by adjusting useEffect dependencies.
- Updated DashboardQuickLinks to include necessary dependencies in useCallback.
- Refactored GlobalShortcutsProvider to utilize refs for context and handler management, improving performance.
- Changed useCurrentTime to update every minute instead of every second.
- Refactored outside click hooks to use useCallback for better performance.
- Improved IntercomProvider and PostHogProvider to prevent multiple initializations using refs.

* refactor: simplify conditional rendering in HomePeekOverviewsRoot component

* refactor: improve outside click detection in sidebar and peek overview hooks

* refactor: enhance IntercomProvider and PostHogProvider with hydration state management
2025-11-25 18:52:20 +05:30
sriramveeraghanta 31e8563725 chore(deps): upgrade sentry pacakges 2025-11-25 16:25:07 +05:30
b-saikrishnakanthandGitHub 5ddfd0e1a9 [WEB-5497] fix: update change email button label for localization consistency #8173 2025-11-25 15:47:14 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ef99f8bbca chore(deps): bump @sentry/node (#8169)
Bumps the npm_and_yarn group with 1 update in the / directory: [@sentry/node](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/node` from 10.24.0 to 10.27.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/10.24.0...10.27.0)

---
updated-dependencies:
- dependency-name: "@sentry/node"
  dependency-version: 10.27.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-25 13:35:01 +05:30
2804987fe8 [WEB-5358] fix: analytics enhancements (#8150)
* refactor: enhance analytics page tab functionality and UI

- Introduced state management for selected tabs using useState and useEffect hooks.
- Updated tab handling to improve user experience with onValueChange for Tabs component.
- Refactored AnalyticsFilterActions to remove unused duration selection.
- Added Tooltip to project name in ActiveProjectItem for better visibility.
- Minor styling adjustments in Tabs component for improved layout consistency.

* refactor: improve tab layout in analytics page by adding w-fit

* fix: update styling for ActiveProjectItem component

* refactor: simplify analytics page and introduce useAnalyticsTabs hook

- Removed unused imports and optimized the analytics page component.
- Replaced getAnalyticsTabs with a new custom hook useAnalyticsTabs for better modularity.
- Added useAnalyticsTabs hook to manage analytics tab logic.
- Created new files for useAnalyticsTabs and analytics tabs in the 'ce' and 'ee' directories.

* fix: ensure consistent export in use-analytics-tabs file

* style: format code for better readability in AnalyticsPage component

* fix: add missing newline at end of file in use-analytics-tabs

* chore: remove unused analytics tab components

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-11-24 21:29:34 +05:30
09f4e3d4ae [WIKI-774] refactor: space app mentions #8152
Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-11-24 21:26:48 +05:30
7c8cbc4ead [WEB-4428] fix: duplicate labels with case insensitive (#7388)
Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-11-24 21:22:17 +05:30
ce6299937f [WEB-5430] feat: allow users to change email (#8120)
* feat: change user email

* chore: optimised the logic

* feat: add email change functionality and related modals in profile form

* refactor: format checkEmail method for improved readability

* chore: added rate limit exceeded validation

* feat: implement change email modal with localization support

- Added translation support for the change email modal, including titles, descriptions, and error messages.
- Integrated the useTranslation hook for dynamic text rendering.
- Updated form validation messages to utilize localized strings.
- Enhanced user feedback with localized success and error toast messages.
- Updated button labels and placeholders to reflect localization changes.

* chore: added extra validation in cache key

* fix: format files

---------

Co-authored-by: b-saikrishnakanth <bsaikrishnakanth97@gmail.com>
Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-11-24 21:21:52 +05:30
Dheeraj Kumar KetireddyandGitHub d6fce114d6 [SILO-663] chore: enhance error handling in ComplexFilterBackend with DRFValidationError (#8090)
* [SILO-663] chore: enhance error handling in ComplexFilterBackend with DRFValidationError

* Log the exception and re-raise it
2025-11-24 21:14:14 +05:30
Prateek ShouryaandGitHub e6d584fde7 [WEB-5489] fix: prevent AppRail hook usage outside context in disabled workspace components (#8164) 2025-11-24 16:03:36 +05:30
sriram veeraghantaandGitHub 0240e46f04 fix: live server docker build changes (#8166) 2025-11-24 14:08:37 +05:30
e36f4f47f1 [MOB-1199] dev: update index constraints for notifications to optimize query performance (#7870)
* dev: updated the index constraints for notification

* dev: updated migration file with AddIndexConcurrently

* dev: handled indexing for file asset with asset

* bumped migration sequence number

---------

Co-authored-by: Dheeraj Kumar Ketireddy <dheeru0198@gmail.com>
2025-11-23 19:41:27 +05:30
ba4e711f9b [WEB-5350] migration: navigation (#8156)
* migration: navigation related fields in workspace_user_properties

* fix: field names

* fix: remove max_length

* fix: create new migration

* fix: typo

* Renamed horizontal preference to tabbed

---------

Co-authored-by: Dheeraj Kumar Ketireddy <dheeru0198@gmail.com>
2025-11-21 20:51:47 +05:30
AaronandGitHub 0f4309659a [WEB-5478] chore: fix types (#8155) 2025-11-21 20:22:37 +05:30
SangeethaandGitHub 5cfb9538df [WEB-5312] migration: work item comments (#8072)
* migration: description field on issue_comment
sync: issue_comment and description

* fix: update if description already exists for the IssueComment

* feat: management command to copy IssueComment to Description

* fix: description creation order

* chore: add while loop

* fix: move write outside loop

* chore: change sync logic
chore: test cases

* chore: removed deleted_at filter and added order_by in management command

* fix: description_id

* migration: added parent_id for IssueComment

* fix: update update_by_id

* fix: use ChangeTrackerMixin in save

* chore: add docstring
fix: remove self.pk check
chore: wrap the description creation logic in transaction.atomic()

* fix: tests

* fix: use super save method

* fix: mulitple if conditions

* fix: update updated_at
2025-11-21 16:15:07 +05:30
Vipin ChaudharyandGitHub 6a26ce3a2b [WIKI-792] feat: add 'status' attribute to content validator (#8151) 2025-11-21 14:00:29 +05:30
sriramveeraghanta ec9c868f85 chore: file format using prettier 2025-11-21 13:48:01 +05:30
matutinalandGitHub 03c581433a chore: french localization updates (#8031) 2025-11-21 13:47:07 +05:30
2e15e4f786 fix: source map warning during build (#8148)
* [WEB-5473] fix: source map errors

* [WEB-5473] chore: run codemod

* fix: build errors in editor

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-11-21 13:43:52 +05:30
Aaryan KhandelwalandGitHub 9611cd1e73 [WIKI-786] refactor: bubble menu #8138 2025-11-21 12:49:30 +05:30
sriram veeraghantaandGitHub 8307badae5 fix: eslint errors and warnings (#8149) 2025-11-20 19:31:22 +05:30
83fdebf64d [WEB-5459] feat(codemods): add function declaration transformer with tests (#8137)
- Add jscodeshift-based codemod to convert arrow function components to function declarations
- Support React.FC, observer-wrapped, and forwardRef components
- Include comprehensive test suite covering edge cases
- Add npm script to run transformer across codebase
- Target only .tsx files in source directories, excluding node_modules and declaration files

* [WEB-5459] chore: updates after running codemod

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-11-20 17:39:40 +05:30
90866fb925 chore: use oxc tooling to speed up prettier (#8117)
Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-11-20 17:30:27 +05:30
Aaryan KhandelwalandGitHub f3031a44f7 [WEB-5429] refactor: conditional hooks (#8114)
* fix: conditional hooks

* chore: remove unwanted file
2025-11-20 16:22:46 +05:30
Aaryan KhandelwalGitHubNarayanBavisettiBavisetti NarayanVipinDevelopscoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
83679806fd [WIKI-419] chore: new asset duplicate endpoint added (#7172)
* chore: new asset duplicate endpoint added

* chore: change the type in url

* chore: added rate limiting for image duplication endpoint

* chore: added rate limiting per asset id

* chore: added throttle class

* chore: added validations for entity

* chore: added extra validations

* chore: removed the comment

* chore: reverted the frontend code

* chore: added the response key

* feat: handle image duplication for web

* feat: custom image duplication update

* fix: remove paste logic for image

* fix : remove entity validation

* refactor: remove entity id for duplication

* feat: handle duplication in utils

* feat: add asset duplication registry

* chore: update the set attribute method

* fix: add ref for api check

* chore :remove logs

* chore : add entity types types

* refactor: rename duplication success status value

* chore: update attribute to enums

* chore: update variable name

* chore: set uploading state

* chore : update enum name

* chore : update replace command

* chore: fix retry UI

* chore: remove default logic

* refactor: optimize imports in custom image extension files and improve error handling in image duplication

* fix:type error

* Update packages/editor/src/core/extensions/custom-image/components/node-view.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: enhance asset duplication handler to ignore HTTP sources

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com>
Co-authored-by: VipinDevelops <vipinchaudhary1809@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-11-20 15:05:01 +05:30
Aaryan KhandelwalandGitHub d462546055 [WIKI-788] fix: editor markdown copy rules (#8140) 2025-11-20 14:46:12 +05:30
Dheeraj Kumar KetireddyandGitHub f510020daa [WEB-5285] feat: add ChangeTrackerMixin to track model field changes and original values #8145 2025-11-20 14:36:55 +05:30
Prateek ShouryaandGitHub 1eaa48c95c [WEB-5458] improvement: restructure layout components to enhance loading (#8141) 2025-11-19 19:11:17 +05:30
sriramveeraghanta d6503232de chore(deps): bump npm vulnerabilities 2025-11-19 14:56:29 +05:30
Jayash TripathyandGitHub 88a41479f4 [WEB-5000] feat: enhance exports settings with rich filters (#8124) 2025-11-19 14:31:48 +05:30
Vipin ChaudharyandGitHub b73cf4f0e2 [WEB-5449] fix: enhance content check for unique ID generation and update issue loader condition (#8132) 2025-11-18 15:27:01 +05:30
Aaryan KhandelwalandGitHub 2e6225a883 [WEB-5436] feat: work item preview (#8121) 2025-11-18 13:54:26 +05:30
Vipin ChaudharyandGitHub f34ca18a34 [WIKI-775] fix: remove fallback image from emojis #8130 2025-11-18 13:53:04 +05:30
Prateek ShouryaandGitHub 69323f4164 [WEB-5442] fix: favorites list on workspace switch (#8131) 2025-11-18 13:52:29 +05:30
Jayash TripathyandGitHub 06300d1a8e [WEB-5447] feat: add dataPreventOutsideClick prop to reafactor: ComboboxOptions for enhanced click handling #8112 2025-11-17 19:47:48 +05:30
Vipin ChaudharyandGitHub 6e32cb51b6 [WIKI-770] fix: update editor ref after editor ready #8115 2025-11-17 16:41:30 +05:30
Vipin ChaudharyandGitHub bd0361de92 [WIKI-480] feat: unique id extension (#8098) 2025-11-17 16:14:20 +05:30
Prateek ShouryaandGitHub ebab5e209f [WEB-5437] fix: remove temporary code for issue display filters to fix work item ordering. (#8125) 2025-11-17 14:28:02 +05:30
Aaryan KhandelwalandGitHub c65e2c4aab [WIKI-773] refactor: editor mention components and hooks (#8111) 2025-11-17 14:07:37 +05:30
Prateek ShouryaandGitHub c04ae51d20 [WEB-5423] revert: update imports for pragmatic-drag-and-drop to use cjs entry points and add TypeScript error handling (#8118) 2025-11-14 19:04:49 +05:30
Prateek ShouryaandGitHub 19a0ef490b [WEB-5423] fix: typescript errors and add types check step to pull request workflow (#8110) 2025-11-14 16:18:36 +05:30
sriram veeraghantaGitHubcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>Copilot
4e357c4ad0 [WEB-5404] chore: update next images with html default images (#8101)
* chore: update next images with html default images

* chore: sync related changes

* Update apps/admin/core/components/instance/failure.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update apps/space/app/not-found.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update apps/space/core/components/issues/issue-layouts/error.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update apps/space/core/components/ui/not-found.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* chore: replace classname styles in space

* fix: copoilot suggestions

* fix: copilot suggestions

* chore: format files

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-13 18:33:18 +05:30
Prateek ShouryaandGitHub 80670b2b3f [WEB-5128] refactor: remove local database dependencies and unused code (#8109) 2025-11-13 18:32:15 +05:30
64f90b828b [WEB-5419] chore: add dotenv dependency, hosts and configure environment variables in admin, space, and web apps (#8107)
* chore: add dotenv dependency and configure environment variables in admin, space, and web apps

* chore: allowed multiple hosts in dev mode

* chore: move dotenv to dev deps

* chore: update Vite configuration to set server host to 127.0.0.1 for admin, space, and web apps

---------

Co-authored-by: gakshita <akshitagoyal1516@gmail.com>
2025-11-13 17:57:47 +05:30
AaronandGitHub 95c742244e [WEB-5416] fix: Use proper import syntax (#8105) 2025-11-13 17:41:06 +05:30
sriramveeraghanta 34c0e81d8e chore: update tubro envs and removed plausible script 2025-11-13 15:24:17 +05:30
Vipin ChaudharyandGitHub 2a2c4921a7 [WIKI-768] fix: ordering and grouping of list-related toolbar and slash commands #8104 2025-11-13 15:20:07 +05:30
Vipin ChaudharyandGitHub f4c2d519fc [WIKI-718] feat: add full width options in table block menu (#8087) 2025-11-13 14:46:27 +05:30
Jayash TripathyandGitHub cbfdcd5638 [WEB-5390] refactor: gantt layout support in base layouts (#8089) 2025-11-13 14:33:24 +05:30
Prateek ShouryaandGitHub a04d3b5c29 [WEB-5414] fix: Use correct SWR cache key for fetching project details (#8103) 2025-11-12 23:31:26 +05:30
sriram veeraghantaandGitHub 30da349475 [WEB-5413] feat: adding sentry error handling in web, space and admin (#8099) 2025-11-12 19:03:47 +05:30
Anmol Singh BhatiaandGitHub 0b78e03055 [WEB-5394] fix: propel storybook circular dependencies (#8096)
* fix: resolve circular dependency crashes in storybook stories

* fix: toolbar circular dependency fix and code refactor
2025-11-11 19:29:16 +05:30
Prateek ShouryaandGitHub f8a9a1576d [WEB-5399] chore: update dependencies and improve PostHog integration (#8097)
- Updated PostHog React package to version 1.4.0 and posthog-js to version 1.255.1.
- Refactored PostHogProvider to include hydration state management for improved event tracking.
- Cleaned up version references in pnpm-lock.yaml for react-router and next-themes.
2025-11-11 19:27:49 +05:30
Krzysztof DąbrowskiandGitHub 6ba15545d9 fix: escape slashes in environment variable values during update (#8081) 2025-11-11 17:52:03 +05:30
sriramveeraghanta da22c2f670 chore(deps): bump turbo package and remove unused packages 2025-11-11 15:50:10 +05:30
sriramveeraghanta d47755b4c6 chore: remove unused files 2025-11-11 15:45:53 +05:30
sriramveeraghanta b51da2f997 chore: remove unused middleware files 2025-11-11 15:33:34 +05:30
Aaryan KhandelwalandGitHub 1447be12e6 [WEB-5370] fix: cycle and modules not being removed from the work item edit modal #8094 2025-11-11 14:55:27 +05:30
Prateek ShouryaandGitHub 433b5a4fe1 [WEB-5386] refactor: update all apps to use react-router for development and enable SSR for space app. (#8095) 2025-11-11 14:08:42 +05:30
sriramveeraghanta 4ae0763d0f chore: bump turbo version 2025-11-11 13:32:10 +05:30
Prateek ShouryaandGitHub a407105bb8 [WEB-5389] chore: enable progress bar for production builds and add logo spinner for hydration fallback (#8093)
* [WEB-5389] chore: enable progress bar for production builds

* feat: add LogoSpinner to HydrateFallback in admin, space, and web apps
2025-11-11 12:22:40 +05:30
M. PalanikannanandGitHub 5d7bf554dd [WEB-5381] fix: buffer issue with vite #8091 2025-11-10 20:33:41 +05:30
sriramveeraghanta 5ec09a7fe1 chore: update dockerfile pnpm install with ci=true 2025-11-10 18:28:51 +05:30
sriramveeraghanta 3f0721c59a chore: upgrade vite version using catalog 2025-11-10 13:13:22 +05:30
Prateek ShouryaandGitHub dfc49eea27 [WEB-5379] fix: optimize filter handling in work item components (#8079) 2025-11-07 20:28:10 +05:30
Akshita GoyalandGitHub 1e5f583a24 [WEB-5367] fix: removed integrations and imports from the routes #8073 2025-11-07 16:12:12 +05:30
Aaryan KhandelwalandGitHub d05c222d0a regression: fallback base url for redirection (#8075) 2025-11-07 16:10:52 +05:30
Vamsi KrishnaandGitHub 8853637e98 [WEB-5064] fix: peek overview reloading when parent is added (#7950) 2025-11-06 19:48:35 +05:30
Aaryan KhandelwalandGitHub 12a050e7d3 [WIKI-538] chore: common description component (#7785)
* chore: common description input component

* chore: replace existing description input components

* fix: await for update calls

* refactor: handle fallback values for description states and form data

* fix: import statements

* chore: add workspaceDetails check
2025-11-06 19:47:38 +05:30
Vipin ChaudharyandGitHub fd542a85fa [WIKI-728] fix: update emoji insertion method #7962 2025-11-06 19:46:32 +05:30
Jayash TripathyandGitHub 0a738d419e [WEB-5306] refactor: base layouts (#8051) 2025-11-06 19:45:56 +05:30
Vipin ChaudharyandGitHub c719d67f71 [WIKI-714] fix: close drag handle dropdown when any key is pressed #8040 2025-11-06 18:58:23 +05:30
Vamsi KrishnaandGitHub 7178627f6a [WEB-5339] chore: work item sidebar refactor #8058 2025-11-06 18:57:50 +05:30
Aaryan KhandelwalandGitHub 10590ffa13 [WEB-5360] fix: modules list board layout #8074 2025-11-06 18:57:22 +05:30
Akshita GoyalandGitHub 37aabe7d1d [PAI-923] fix: implemented fetch key functions for auth layouts #8067 2025-11-06 18:50:45 +05:30
Anmol Singh BhatiaandGitHub fd38b9b6d8 [WEB-4320] refactor: migrate emoji reactions to propel (#8039) 2025-11-06 18:25:43 +05:30
M. PalanikannanandGitHub d709465a89 [WIKI-564] fix: editor deps #8069 2025-11-06 17:33:51 +05:30
Anmol Singh BhatiaandGitHub 9b3bd11c03 [WEB-5305] refactor: migrate emoji and logo components to propel and remove duplication (#8038) 2025-11-06 17:33:06 +05:30
Aaryan KhandelwalandGitHub 4927ef8c71 [WEB-5352] fix: layout selection icon #8070 2025-11-06 16:58:38 +05:30
dependabot[bot]andGitHub 0dcea8db70 chore(deps): bump django (#8068) 2025-11-06 16:21:43 +05:30
Prateek ShouryaandGitHub 0941856f67 [WEB-5356] feat: update public environment keys in Vite config for improved configuration management #8071 2025-11-06 15:41:01 +05:30
Prateek ShouryaandGitHub 696fb96e87 [WEB-5043] feat: web vite migration (#7973) 2025-11-06 14:08:48 +05:30
Prateek ShouryaandGitHub 118ecc81ba [WEB-5042] feat: sites vite migration (#7965) 2025-11-06 13:58:24 +05:30
AaronandGitHub 315e1d5eb0 [WEB-5040] feat: admin react-router migration (#7922) 2025-11-06 13:39:35 +05:30
NikhilandGitHub 545bfa203e [WEB-5345] chore: update IssueActivity model to use DO_NOTHING #8065 2025-11-05 20:14:42 +05:30
Vamsi KrishnaandGitHub 78d4356cfa [WEB-5343]chore: updated view access for exports page at workspace level (#8062)
* chore: updated view access for exports page at workspace level

* fix: permissions
2025-11-04 16:25:07 +05:30
NikhilandGitHub 3c6f24de64 [WEB-5237] feat: add workspace invitation and project member management endpoints (#8059) 2025-11-04 14:56:21 +05:30
NikhilandGitHub 96bbbec588 [WEB-5342] fix: update Issue query to use the correct manager in state checks #8060 2025-11-04 14:47:05 +05:30
Dheeraj Kumar KetireddyandGitHub bfea2c8572 [SILO-598] fix: Fixed bugs identified while testing SDK (#8042) 2025-10-31 19:53:22 +05:30
sriram veeraghantaandGitHub 961cdf6c8f fix: storybook build error (#8041) 2025-10-30 19:21:32 +05:30
Surya PrashanthandGitHub e8bdc47d6a [SILO-640] fix: add missing title field in IssueLinkCreateSerializer #8036 2025-10-29 20:38:27 +05:30
Vamsi KrishnaandGitHub 710138f3a1 [WEB-5296]chore: grouping filter refactor (#8034) 2025-10-29 20:36:28 +05:30
Anmol Singh BhatiaandGitHub 0e8128594b [WEB-5191 | WEB-5197] chore: actions and arrows icon revamp (#7984) 2025-10-29 20:13:08 +05:30
350107d6c1 [WEB-5088] feat: Power K v2 (#7905)
* feat: add project shortcut in command palette

* feat: global project switcher shortcut

* refactor: generalize command palette entity handling

* feat: extend command palette navigation

* feat: add issue shortcut to command palette

* feat: add modular project selection for cycle navigation

* chore: add reusable command palette utilities

* fix: update key sequence handling to use window methods for timeout management

* fix: build errors

* chore: minor ux copy improvements

* feat: implement a new command registry and renderer for enhanced command palette functionality

* feat: introduce new command palette components and enhance search functionality

* feat: enhance command palette components with improved initialization and loading indicators

* feat: Implement new command palette architecture with multi-step commands, context-aware filtering, and reusable components. Add comprehensive documentation and integration guides. Enhance command execution with a dedicated executor and context provider. Introduce new command types and improve existing command definitions for better usability and maintainability.

* refactor: hook arguments

* refactor: folder structure

* refactor: update import paths

* fix: context prop drilling

* refactor: update search components

* refactor: create actions

* chore: add type to pages

* chore: init contextual actions

* refactor: context based actions code split

* chore: module context-based actions

* refactor: streamline command execution flow and enhance multi-step handling in command palette

* refactor: remove placeholder management from command execution and implement centralized placeholder mapping

* chore: cycle context based actions

* refactor: simplify command execution by consolidating selection steps and adding page change handling

* chore: added more options to work item contextual actions

* chore: page context actions

* refactor: update step type definitions and enhance page mapping for command execution

* feat: implement Command Palette V2 with global shortcuts and enhanced context handling

* refactor: power k v2

* refactor: creation commands

* feat: add navigation utility for Power K context handling

* feat: implement comprehensive navigation commands for Power K

* refactor: work item contextual actions

* fix: build errors

* refactor: remaining contextual actions

* refactor: remove old code

* chore: update placeholder

* refactor: enhance command registry with observable properties and context-aware shortcut handling

* refactor: improve command filtering logic in CommandPaletteModal

* chore: context indicator

* chore: misc actions

* style: shortcut badge

* feat: add open entity actions and enhance navigation commands for Power K

* refactor: rename and reorganize Power K components for improved clarity and structure

* refactor: update CommandPalette components and streamline global shortcuts handling

* refactor: adjust debounce timing in CommandPaletteModal for improved responsiveness

* feat: implement shortcuts modal and enhance command registry for better shortcut management

* fix: search implemented

* refactor: search results code split

* refactor: search results code split

* feat: introduce creation and navigation command modules for Power K, enhancing command organization and functionality

* chore: update menu logos

* refactor: remove unused PowerKOpenEntityActionsExtended component from command palette

* refactor: search menu

* fix: clear context on backspace and manual clear

* refactor: rename creation command keys for consistency and clarity in Power K

* chore: added intake in global search

* chore: preferences menu

* chore: removed the empty serach params

* revert: command palette changes

* cleanup

* refactor: update command IDs to use underscores for consistency across Power K components

* refactor: extended context based actions

* chore: modal command item status props

* refactor: replace CommandPalette with CommandPaletteProvider in settings and profile layouts

* refactor: update settings menu to use translated labels instead of i18n labels

* refactor: update command titles to use translation keys for creation actions

* refactor: update navigation command titles to use translation keys for consistency

* chore: minor cleanup

* chore: misc commands added

* chore: code split for no search results command

* chore: state menu items for work item context based commands

* chore: add more props to no search results command

* chore: add more props to no search results command

* refactor: remove shortcut key for create workspace command

* Refactor command palette to use PowerK store

- Replaced instances of `useCommandPalette` with `usePowerK` across various components, including `AppSearch`, `CommandModal`, and `CommandPalette`.
- Introduced `PowerKStore` to manage modal states and commands, enhancing the command palette functionality.
- Updated modal handling to toggle `PowerKModal` and `ShortcutsListModal` instead of the previous command palette modals.
- Refactored related components to ensure compatibility with the new store structure and maintain functionality.

* Refactor PowerK command handling to remove context dependency

- Updated `usePowerKCommands` and `usePowerKCreationCommands` to eliminate the need for a context parameter, simplifying their usage.
- Adjusted related command records to utilize the new structure, ensuring consistent access to command configurations.
- Enhanced permission checks in creation commands to utilize user project roles for better access control.

* chore: add context indicator

* chore: update type import

* chore: migrate toast implementation from @plane/ui to @plane/propel/toast across multiple command files

* refactor: power k modal wrapper and provider

* fix: type imports

* chore: update creation command shortcuts

* fix: page context commands

* chore: update navigation and open command shortcuts

* fix: work item standalone page modals

* fix: context indicator visibility

* fix: potential error points

* fix: build errors

* fix: lint errors

* fix: import order

---------

Co-authored-by: Vihar Kurama <vihar.kurama@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2025-10-29 15:38:30 +05:30
Jayash TripathyandGitHub 73e0e8d529 [WEB-4944] feat: add base layouts for kanban and list with drag-and-drop support (#8032) 2025-10-29 14:49:34 +05:30
Anmol Singh BhatiaandGitHub 5247fedd23 [WEB-5271] chore: your work empty state updated #8029 2025-10-29 13:34:04 +05:30
Anmol Singh BhatiaandGitHub 0560849f88 [WEB-5271] fix: empty state translation error and code refactoring (#8027)
* fix: workspace view empty state translation

* chore: search empty state translation fix

* chore: workspace view empty state translation
2025-10-28 20:14:07 +05:30
sriram veeraghantaandGitHub 79537cd1df chore: code splitting instance config variables (#8028)
* chore: code splitting instance config variables

* chore: typo fixes
2025-10-28 19:52:23 +05:30
1126ca30b0 [WEB-5262] feat: gitea sso (#8022)
* Feature/7137/gitea sso (#7940)

* added gitea auth to admin panel with configs , added api calls

* added gitea to oauth root (for signup and signin)

* removed log

* replace github oauth with gitea ouath error messages

* added gitea to auth root

* fix: update token expiration handling and remove unused variable in Gitea callback

* fix: include Gitea in OAuth enabled checks

* fix: improve error handling when fetching emails from Gitea

* chore : remove logs and add semicolons

* refactor: update Gitea authentication components and imports for consistency

* fix: enhance Gitea authentication form to auto-populate host value and improve OAuth checks

* refactor: enhance Gitea OAuth provider with improved error handling and URL validation

* fix: update authentication success messages to check for string value "1"

---------

Co-authored-by: Shivam Jain <shivam.clgstash@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
2025-10-28 18:53:54 +05:30
SangeethaandGitHub 69fe581fd8 [WEB-5228] chore: IssueLink.DoesNotExist on crawl_work_item_link_title (#8003)
* chore: wrap IssueLink logic in a try-except block

* chore: user logger.warning

* fix: add id and url on the error message
2025-10-28 18:18:36 +05:30
Bavisetti NarayanandGitHub e09d986497 [WEB-5263] chore: removed the tracking of sanitized HTML (#8023)
* chore: removed the html sanitized tracking

* chore: added the greater than sign
2025-10-28 16:57:07 +05:30
SangeethaandGitHub 044003e7ec [WEB-5211] fix: accessing NoneType current_instance for delete_module_issue_activity bgtask (#7993)
* fix: accessing NoneType current_instance

* fix: return module_name

* fix: add none check
2025-10-28 16:56:39 +05:30
SangeethaandGitHub a8b6930486 [WEB-5224] chore: raise warning instead of logging an exception for webhook_send_task (#7996) 2025-10-28 16:49:19 +05:30
Anmol Singh BhatiaandGitHub 685e3fa2f8 [WEB-5236] chore: attachment icon revamp (#8009)
* chore: iconwrapper component added

* chore: attachments icon added to propel

* chore: code refactor

* chore: code refactor
2025-10-28 16:47:43 +05:30
Anmol Singh BhatiaandGitHub ec6e682044 [WEB-5255] fix: calendar layout weekly view (#8018)
* fix: calendar layout weekly view

* chore: code refactor
2025-10-28 16:47:10 +05:30
Anmol Singh BhatiaandGitHub b7aa25f2c6 [WEB-5248] chore: translations and empty state refactor (#8021)
* chore: translations code refactoring

* chore: empty state translation refactor

* chore: code refactor
2025-10-28 13:31:06 +05:30
Anmol Singh BhatiaandGitHub cf7f891bcb [WEB-5248] chore: empty state code refactor and translation fix (#8017)
* chore: empty state code refactor and translation fix

* chore: code refactor
2025-10-27 19:55:46 +05:30
Anmol Singh BhatiaandGitHub 3faf768112 [WEB-3567] chore: empty state refactoring and translation fix (#8014)
* chore: empty state component improvement and code refactor

* chore: translation code refactor

* chore: empty state code refactor
2025-10-27 17:03:35 +05:30
Vamsi KrishnaandGitHub be0d1871f0 [WEB-5241] fix: project features toggle #8015 2025-10-27 17:02:22 +05:30
Surya PrashanthandGitHub c07f7b7c1e [WEB-5245] fix: add is_time_tracking_enabled field in project serializer (#8016) 2025-10-27 16:54:03 +05:30
Surya PrashanthandGitHub 1d4cde9ba0 [WEB-5245] feat: auto-populate logo_props in project creation #8013 2025-10-27 16:19:52 +05:30
NikhilandGitHub c4dd4bd02f [WEB-5155]refactor: simplify user filtering logic in SearchEndpoint #8012 2025-10-27 16:17:19 +05:30
Anmol Singh BhatiaandGitHub d71dfe8f86 [WEB-5188 | WEB-5190] chore: layout and properties icon revamp (#7980) 2025-10-24 19:53:36 +05:30
Anmol Singh BhatiaandGitHub 33b6405695 [WEB-5160] chore: propel banner and archived work item improvements (#7999) 2025-10-24 19:48:28 +05:30
Anmol Singh BhatiaandGitHub 68fd2463f4 [WEB-5230 | WEB-5231] chore: new empty state implementation (#7972) 2025-10-24 17:21:14 +05:30
Vihar KuramaandGitHub a60d74a3c0 [WEB-5235] Fix activity timeline ordering for edited comments #8002 2025-10-24 16:17:51 +05:30
Anmol Singh BhatiaandGitHub 76ffe52cd5 [WEB-5220] fix: intake actions btn ui #7995 2025-10-24 16:05:46 +05:30
b65b1b4828 fix: use issue store was not loading up properly (#8006)
* use issue store was not loading up properly , and hence created data issues for rendering filters options

* Added React.FC for better type suggestions

* chore: observer implementation change

---------

Co-authored-by: shivam-jainn <shivam.clgstash@gmail.com>
2025-10-23 21:45:25 +05:30
sriram veeraghantaandGitHub e5063cd280 fix: get work item activity external endpoint (#8005)
* fix: activity get endpoint fixes

* chore: missing pk added
2025-10-23 21:37:30 +05:30
sriramveeraghanta 9448f642f0 sync: master branch with preview 2025-10-23 20:29:18 +05:30
Akshat Jainandsriramveeraghanta 443f8b793d [INFRA-252] fix: Add missing Redis environment variables in live service Docker Compose 2025-10-23 20:28:39 +05:30
Akshat JainandGitHub 96fa9ab15b [INFRA-252] fix: Add missing Redis environment variables in live service Docker Compose 2025-10-23 19:26:27 +05:30
sriram veeraghantaandGitHub d5bad5aedc release: v1.1.0 #7991 2025-10-23 18:24:42 +05:30
e710f5b278 [WEB-5196] chore: switch from isomorphic-dompurify to dompurify (#7983)
* [WEB-5196] chore: switch from isomorphic-dompurify to dompurify

Replace isomorphic-dompurify with dompurify package in utils.
This change simplifies the dependency and uses the canonical
DOMPurify package directly.

* fix: removing dompurify from the space app as dependency

* chore: remove unused import

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-10-23 16:33:32 +05:30
sriramveeraghanta 80bd1b6dcd Merge branch 'canary' of github.com:makeplane/plane into preview 2025-10-23 15:43:16 +05:30
b34c9ca04f fix: live server secret key app version env variables (#7997)
* fix: live server secret key app version env variables

* fix: revert to stable version

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-23 15:42:43 +05:30
sriramveeraghanta ad16ea1440 Merge branch 'canary' of github.com:makeplane/plane into preview 2025-10-23 14:54:09 +05:30
sriramveeraghanta addf3c4104 chore: added live server secret key in community docker file 2025-10-23 14:53:41 +05:30
Anmol Singh BhatiaandGitHub 38cdf756a5 [WEB-5158] chore: calendar start of week alignment fix and code refactoring #7978 2025-10-23 13:59:29 +05:30
Bavisetti NarayanandGitHub f94da68597 [WEB-5153] chore: optimised the cycle transfer issues (#7969)
* chore: optimised the cycle transfer issues

* chore: added more vlaidation in transfer

* chore: improve the comments
2025-10-23 00:29:32 +05:30
Bavisetti NarayanandGitHub 68aa2fe0b8 [WIKI-553] chore: improved pages components tracking (#7966)
* chore: page components tracking

* chore: changed the transaction task

* chore: added logger for description html
2025-10-23 00:29:05 +05:30
Vamsi KrishnaandGitHub 5fa9943b66 [WEB-5141] chore: project features refactor (#7960)
* chore: project features refactor

* fix: enable/disable condition for toggle

* fix: dependencies

* chore: lint fix

* chore: lint fix

* chore: added fallback exports
2025-10-23 00:27:16 +05:30
Anmol Singh BhatiaandGitHub 772b5c5719 [WEB-5201] chore: work item and inbox modal refactor (#7990)
* chore: work item and inbbox modal refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor
2025-10-22 21:11:08 +05:30
NikhilandGitHub d6657d5168 [WEB-5207] fix: update date formatting in IssueExportSchema for cycle start and end dates #7992 2025-10-22 17:11:43 +05:30
sriramveeraghanta 1b43efbc2a chore: version bump 2025-10-21 19:14:43 +05:30
64781be7d2 regression: downgrade to tiptap v2 (#7982)
* chore: downgrade to tiptap v2

* fix: revert back to hocuspocus

* fix: collaboration events added

* fix: lock unlock issues

* fix: build errors

* fix: type errors

* fix: graceful shutdown

---------

Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
2025-10-21 18:28:16 +05:30
sriramveeraghanta 59022b6beb fix: vite package upgrade 2025-10-21 14:41:34 +05:30
NikhilandGitHub f266cd8414 [WEB-5162] fix: add exporter URL patterns for exporting issues (#7977)
* feat: add exporter URL patterns for exporting issues and update session cookie age handling

* fix: ensure ADMIN_SESSION_COOKIE_AGE is an integer for consistent session handling
2025-10-16 15:36:48 +05:30
sriramveeraghanta 98b81d7ebb chore(deps): happy dom version bump 2025-10-16 14:55:57 +05:30
AaronandGitHub 00807d2d62 [WEB-5157] chore: fix propel icon clip paths #7975 2025-10-16 14:03:26 +05:30
Vamsi KrishnaandGitHub 9d757ccc6a [WEB-5156] fix: activity filters bug #7971 2025-10-15 19:57:52 +05:30
Anmol Singh BhatiaandGitHub a8c253acfe [WEB-5154] chore: nav button text colour updated #7970 2025-10-15 19:27:10 +05:30
Vamsi KrishnaandGitHub 2b106cbd66 [WEB-5054]feat: added activity filters for state and assignee activities (#7918)
* feat: added activity filters for state and assignee

* chore: removed unused funtion

* chore: lint fix
2025-10-15 17:12:03 +05:30
sriramveeraghanta f9cca8e2cb fix: live server log 2025-10-15 16:18:42 +05:30
ee176efae3 [WEB-5095]chore: updated delete modal info content (#7967)
* chore: updated delete modal info content

* chore: added language support for modal content

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-10-15 16:05:55 +05:30
Akshita GoyalandGitHub 44a483f895 [WEB-5142] fix: Issue modal styling #7961 2025-10-15 15:19:40 +05:30
sriramveeraghanta 196baf099a chore: lint errors 2025-10-15 15:15:48 +05:30
NikhilandGitHub 1a9ebc8b68 [WEB-5071] chore: implement webhook logging to MongoDB and fallback to database (#7896)
* feat: implement webhook logging to MongoDB and fallback to database (#7887)

- Added a new function `save_webhook_log` to log webhook requests and responses to MongoDB, with a fallback to the database if the MongoDB save fails.
- Updated the `webhook_send_task` to utilize the new logging function.
- Modified the `get_webhook_logs_queryset` to order logs by creation date and adjusted the chunk size for iteration.

* refactor: clean up log data formatting in save_webhook_log function

* fix: update retry_count type in save_webhook_log function
2025-10-15 14:53:43 +05:30
Vamsi KrishnaandGitHub 97e662215a [WEB-5125] fix: suspended user view #7964 2025-10-14 17:35:39 +05:30
Bavisetti NarayanandGitHub 606e34ec81 [WIKI-730] chore: handle body too large error (#7963)
* chore: added middleware to handle body too large

* chore: added middleware to handle body too large

* chore: indentend the code

* chore: changed the response structure

* chore: changed the response structure

* chore: created a new file for middleware

* chore: added a standardized error key
2025-10-14 17:21:11 +05:30
Anmol Singh BhatiaandGitHub a3019ebd46 [WEB-5092] feat: app sidebar enhancements (#7946)
* chore: project sidebar and settings sidebar improvement

* chore: navigationitem ui improvement

* chore: workspace level new icon added

* chore: propel icon migration

* chore: code refactor

* chore: dashboard icon updated

* chore: code refactor

* chore: icons updated

* chore: code refactor

* chore: code refactor

* chore: scroll area component refactor

* chore: sidebar enhancements

* chore: add and archive icon updated

* chore: code refactor

* chore: code refactor

* chore: code refactor
2025-10-14 17:20:20 +05:30
Prateek ShouryaandGitHub 9cfde896b3 [WEB-5134] refactor: update web ESLint configuration and refactor imports to use type imports (#7957)
* [WEB-5134] refactor: update `web` ESLint configuration and refactor imports to use type imports

- Enhanced ESLint configuration by adding new rules for import consistency and type imports.
- Refactored multiple files to replace regular imports with type imports for better clarity and performance.
- Ensured consistent use of type imports across the application to align with TypeScript best practices.

* refactor: standardize type imports across components

- Updated multiple files to replace regular imports with type imports for improved clarity and consistency.
- Ensured adherence to TypeScript best practices in the rich filters and issue layouts components.
2025-10-14 16:45:07 +05:30
Dheeraj Kumar KetireddyandGitHub 4168127803 [WEB-4999] feat: implement flexible data export utility with CSV, JSON, and XLSX support (#7884)
* feat: implement flexible data export utility with CSV, JSON, and XLSX support

- Introduced Exporter class for handling various data formats.
- Added formatters for CSV, JSON, and XLSX exports.
- Created schemas for defining export fields and their transformations.
- Implemented IssueExportSchema for exporting issue data with nested attributes.
- Enhanced issue export task to utilize the new exporter system for better data handling.

* feat: enhance issue export functionality with new relations and context handling

- Updated issue export task to utilize new IssueRelation model for better relationship management.
- Refactored Exporter class to accept QuerySets directly, improving performance and flexibility.
- Enhanced IssueExportSchema to include parent issues and relations in the export.
- Improved documentation for exporting multiple projects and filtering fields during export.

* feat: enhance export functionality with field filtering and context support

- Updated Exporter class to merge fields into options for formatting.
- Modified formatters to filter fields based on specified options.
- Enhanced ExportSchema to support optional field selection during serialization.
- Improved documentation for the serialize method to clarify field filtering capabilities.

* fixed type
2025-10-14 15:46:55 +05:30
Anmol Singh BhatiaandGitHub 9dc14d8d67 [WEB-5139] fix: custom menu item link (#7959)
* fix: custom menu link action

* chore: code refactor

* chore: code refactor
2025-10-14 15:45:49 +05:30
Anmol Singh BhatiaandGitHub 56007e7d47 [WEB-5129] chore: icons revamp (#7958)
* chore: code refactor

* chore: code refactor
2025-10-14 12:47:53 +05:30
Prateek ShouryaandGitHub cfb4a8212c [WEB-5099] improvement: enhance rich filters with new components and configurations (#7916)
* feat: enhance rich filters with new components and configurations

- Added `AdditionalFilterValueInput` for unsupported filter types.
- Introduced `FilterItem` and related components for better filter item management.
- Updated filter configurations to include new properties and support for multiple values.
- Improved loading states and error handling in filter components.
- Refactored existing filter logic to streamline operations and enhance performance.

* Refactor rich filters component structure and enhance filter item functionality

- Moved AddFilterButton and AddFilterDropdown to a new directory structure for better organization.
- Updated FilterItemProperty to handle filter selection and condition updates more effectively.
- Enhanced the FilterInstance class with methods to update condition properties and operators, improving filter management.
- Added new functionality to handle invalid filter states and improve user feedback.

* [WEB-5111] feat: add 'created_at' and 'updated_at' filters to work item configuration

- Introduced new filter configurations for 'created_at' and 'updated_at' in the work item filters.
- Updated relevant components to utilize these new filters, enhancing filtering capabilities.
- Added corresponding filter configuration functions in the utils for better date handling.

* fix: build
2025-10-14 01:39:24 +05:30
9f41e92d21 [WEB-5135] refactor: update sites ESLint configuration and refactor imports to use type imports (#7956)
- Enhanced ESLint configuration by adding new rules for import consistency and type imports.
- Refactored multiple files to replace regular imports with type imports for better clarity and performance.
- Ensured consistent use of type imports across the application to align with TypeScript best practices.

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2025-10-14 00:40:30 +05:30
Vamsi KrishnaandGitHub ddf07dc993 [WEB-5029] chore: updates icons for title and description #7862 2025-10-14 00:09:50 +05:30
eee9744159 [WEB-5119] chore: remove any deprecated toast instances from the plane/ui page (#7943)
* [WEB-5119] chore: remove deprecated toast implementation from @plane/ui

* fix: build

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-10-14 00:08:00 +05:30
Prateek ShouryaandGitHub c80c76b882 [WEB-5136] refactor: update admin ESLint configuration and refactor imports to use type imports (#7955)
- Enhanced ESLint configuration by adding new rules for import consistency and type imports.
- Refactored multiple files to replace regular imports with type imports for better clarity and performance.
- Ensured consistent use of type imports across the application to align with TypeScript best practices.
2025-10-13 21:07:49 +05:30
Vamsi KrishnaandGitHub ffe38b592a [WEB-5080] fix: attachment and link count in spreadsheet columns #7942 2025-10-13 21:03:54 +05:30
Anmol Singh BhatiaandGitHub 87eb1949c6 [WEB-5129] feat: icons revamp (#7945) 2025-10-13 19:43:24 +05:30
Anmol Singh BhatiaandGitHub c736354739 [WEB-5130] chore: propel scroll area improvement #7952 2025-10-13 19:30:58 +05:30
sriramveeraghanta 3eecec552a chore(deps): lock file resolutions 2025-10-13 13:43:05 +05:30
sriramveeraghanta c561164d65 chore(deps): happy dom resolution added 2025-10-13 13:32:13 +05:30
c3dd790f7e [WEB-5058] feat: enhance workspace seeding with cycles, modules, and views creation (#7951)
* feat: enhance workspace seeding with cycles, modules, and views creation

- Added `create_cycles`, `create_modules`, and `create_views` functions to the workspace seeding process, enabling the creation of cycles, modules, and views based on new seed data.
- Updated `create_project_issues` to associate issues with cycles and modules.
- Introduced new seed files: `cycles.json`, `modules.json`, and `views.json` to provide initial data for cycles, modules, and views.
- Integrated these new functionalities into the `workspace_seed` task for comprehensive workspace initialization.

* feat: add project_id to page seed data for improved association

- Added `project_id` field to the page with `id` 2 in `pages.json` to establish a clear link between pages and their respective projects.
- This enhancement supports better organization and retrieval of page data within the project context.

* feat: enhance workspace seed task with improved display properties and layout options

- Updated the `create_project_and_member` function to include new display properties and layout configurations for better project visualization.
- Modified display filters to group by state and added calendar layout options.
- Enhanced the `create_modules` and `create_views` functions with improved formatting and structure for better readability and maintainability.

* Update apps/api/plane/bgtasks/workspace_seed_task.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: correct project_id mapping in cycle and view creation functions

- Updated the `create_cycles` function to use the correct `project_id` from the `project_map` for fetching the last cycle.
- Removed redundant `view_id` extraction in the `create_views` function to streamline view creation process.

* refactor: update create_cycles function to return project mapping

- Changed the return type of the `create_cycles` function from `None` to `Dict[int, uuid.UUID]` to provide a mapping of project IDs after cycle creation.
- This modification enhances the function's utility by allowing the caller to access the generated project mappings directly.

* refactor: remove unused view_map variable in create_views function

- Eliminated the `view_map` dictionary from the `create_views` function as it was not utilized, streamlining the code.
- This change enhances code clarity and maintainability by removing unnecessary elements.

* refactor: improve issue creation logic in create_project_issues function

- Added comments to clarify the creation of issue labels, cycle issues, and module issues within the `create_project_issues` function.
- Enhanced code readability and maintainability by structuring the issue creation process with clear conditional checks for cycles and modules.

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-11 20:55:48 +05:30
Akshita GoyalandGitHub 70be4a4ace [WEB-5127] chore: added readonly properties + fixed state dropdown + added additional props to issue modal (#7949)
* chore: added readonly properties + fixed state dropdown + added addditional props to issue modal

* fix: refactor

* fix: refactors

* fix: build
2025-10-11 16:18:32 +05:30
Anmol Singh BhatiaandGitHub 2c17f8ad72 [WEB-5103] fix: custom menu event propagation (#7932)
* fix: custommenu event propagation

* chore: code refactor
2025-10-11 15:02:34 +05:30
NikhilandGitHub 151674687c [WEB-5059] feat: add page creation functionality to workspace seeding process (#7948)
* feat: add page creation functionality to workspace seeding process

- Implemented `create_pages` function to create pages for each project in the workspace based on data from `pages.json`.
- Integrated page creation into the `workspace_seed` task, ensuring pages are created alongside project issues.
- Added a new `pages.json` seed file containing initial page data and descriptions.

* fix: update page creation logic and seed data

- Set `is_global` to `False` for pages created in the `create_pages` function.
- Adjusted the project type check to be case-insensitive in the page creation logic.
- Added `id`, `project_id`, and `description_stripped` fields to the `pages.json` seed data for improved page initialization.
2025-10-10 18:32:15 +05:30
sriram veeraghantaandGitHub 8cd29c5009 fix: adding comprehensive logs for live server (#7947)
* fix: adding comprehensive logs

* fix: document controller naming convention

* fix: axios interception logger
2025-10-10 18:28:52 +05:30
Jayash TripathyandGitHub 9ce6179421 [WEB-5120] refactor: add onSearchQueryKeyDown prop to ComboboxOptions for keyboard event handling #7941 2025-10-10 16:46:47 +05:30
sriram veeraghanta 71ec9fadb7 chore: package versions 2025-10-10 01:52:23 +05:30
sriram veeraghanta c88ed332e5 chore: package version upgrade 2025-10-10 01:36:32 +05:30
Aaryan KhandelwalandGitHub d8c1dff34f [WIKI-725] regression: editor floaters propagation #7938 2025-10-10 01:35:14 +05:30
Akshat JainandGitHub 5d161f671d [INFRA-246] Update Valkey image version to 7.2.11-alpine in docker-compose files #7935 2025-10-10 00:19:17 +05:30
sriram veeraghantaandGitHub ae215a542e [WIKI-724 ] feat: sentry setup for live express server #7934 2025-10-09 21:00:43 +05:30
Anmol Singh BhatiaandGitHub b7c14ac9f5 [WEB-5104] chore: combobox improvements (#7933)
* chore: base ui package upgrade

* feat: migrate combobox to use base-ui primitives

* chore: code refactor

* chore: code refactor
2025-10-09 19:13:17 +05:30
75cd2017a0 [WEB-5104] chore: base-ui package upgrade and combobox migration (#7931)
* chore: base ui package upgrade

* feat: migrate combobox to use base-ui primitives

* chore: code refactor

* fix: format fixes

---------

Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-10-09 18:47:09 +05:30
Rohan BirtiaandGitHub 1895cfe728 Update docker-compose.yml for valkey security patch (#7926)
Critical security patch for Redis.
https://github.com/valkey-io/valkey/security/advisories/GHSA-9rfg-jx7v-52p6
2025-10-09 17:15:49 +05:30
M. PalanikannanandGitHub 98e0089723 [WIKI-722] fix: hocuspocus provider update to v3 (#7930)
* fix: hocuspocus provider

* fix: hocuspocus updated url instead of queryparams
2025-10-09 15:38:15 +05:30
sriramveeraghanta 561ae82d9d chore: update gitignore and dockerignore 2025-10-09 15:35:11 +05:30
Vamsi KrishnaandGitHub afac9f72db [WEB-5101]fix: toast action items hover behaviour (#7929)
* fix: toast action items hover behaviour

* fix: refactored toast styles
2025-10-09 15:10:56 +05:30
M. PalanikannanandGitHub 0f038705ed fix: hocuspocus version upgrade (#7928) 2025-10-09 14:32:43 +05:30
Prateek ShouryaandGitHub 0b257c8693 [WEB-5093] improvement: adds content-based MIME type detection for uploads #7925
- Refactored file upload utilities to use async functions for better handling of file metadata.
- Introduced MIME type detection using the file-type library.
- Updated file service methods to await metadata retrieval.
- Added new dependencies for file-type and updated package.json accordingly.
- Removed deprecated file handling code from utils and adjusted imports across services.
2025-10-09 12:22:43 +05:30
Aaryan KhandelwalandGitHub f2539c5051 fix: editor dropdowns positioning (#7927)
* fix: editor dropdowns positioning

* fix: add cleanup to prevent memory leak

* chore: add editor fallback
2025-10-09 00:25:21 +05:30
sriram veeraghantaandGitHub 5d60d6d702 chore: adding types node package to pnpm catalog (#7923) 2025-10-08 18:42:53 +05:30
3cbb60417c [WEB-4976]chore: propel toast implementation (#7832)
* draft: implementation

* chore: migrated toast from @plane/ui to propel package

* chore: reverted unwanted changes

* fix: lint errors

* chore: added dismiss toast to propel toast

* fix: build and lint errors

* chore: code refactor

---------

Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
2025-10-07 23:37:10 +05:30
Anmol Singh BhatiaandGitHub d38f4811b6 [WEB-3567] dev: empty state v2 (#7910) 2025-10-07 20:53:49 +05:30
Anmol Singh BhatiaandGitHub 3e82984f92 [WEB-5068] fix: CustomMenu closeOnSelect behavior #7897 2025-10-07 20:52:30 +05:30
SangeethaandGitHub 89ddd1bf42 [WEB-5081] fix: update timezone in user settings #7915 2025-10-07 20:51:44 +05:30
NikhilandGitHub 0b15a32ec6 [WEB-5038] fix: cycle creation in external api endpoint (#7866)
* feat: set default owner for cycle creation if not provided

* Updated CycleListCreateAPIEndpoint to assign the current user as the owner when the 'owned_by' field is not included in the request data.
* Enhanced the CycleCreateSerializer initialization to ensure proper ownership assignment during cycle creation.

* feat: add comprehensive tests for Cycle API endpoints

* Introduced a new test suite for Cycle API endpoints, covering creation, retrieval, updating, and deletion of cycles.
* Implemented tests for various scenarios including successful operations, invalid data handling, and conflict resolution with external IDs.
* Enhanced test coverage for listing cycles with different view filters and verifying cycle metrics annotations.

* feat: enhance CycleCreateSerializer to include ownership assignment

* Added 'owned_by' field to CycleCreateSerializer to specify the user who owns the cycle.
* Updated CycleListCreateAPIEndpoint to remove redundant ownership assignment logic, relying on the serializer to handle default ownership.
* Ensured that if 'owned_by' is not provided, it defaults to the current user during cycle creation.

* fix: correct assertion syntax in CycleListCreateAPIEndpoint tests

* Updated the assertion in the test for successful cycle creation to use the correct syntax for checking the response status code.
* Ensured that the test accurately verifies the expected behavior of the API endpoint.
2025-10-07 18:50:09 +05:30
Anmol Singh BhatiaandGitHub f5eb13f66f [WEB-5086] fix: instance setup screen asset #7914 2025-10-07 18:47:36 +05:30
M. PalanikannanandGitHub c3e8ce8f28 [WIKI-710] [WIKI-717] fix: slash commands and mentions in comments (#7919)
* fix: fixing slash commands and mentions

* fix: tiptap types handled
2025-10-07 18:01:39 +05:30
Yaroslav HalchenkoandGitHub 2ca8620246 Add codespell support (config, workflow to detect/not fix) and make it fix some typos (#7864)
* Add github action to codespell preview on push and PRs

* Add rudimentary codespell config

* [DATALAD RUNCMD] chore: run codespell throughout fixing a few typos interactively

=== Do not change lines below ===
{
 "chain": [],
 "cmd": "codespell -w -i 3 -C 4 ./apps/web/core/components/workspace/delete-workspace-form.tsx ./deployments/cli/community/README.md",
 "exit": 0,
 "extra_inputs": [],
 "inputs": [],
 "outputs": [],
 "pwd": "."
}
^^^ Do not change lines above ^^^

* Adjust coespell regex to ignore all camelCased words

* [DATALAD RUNCMD] chore: run codespell throughout fixing a few  new typos automagically

=== Do not change lines below ===
{
 "chain": [],
 "cmd": "codespell -w",
 "exit": 0,
 "extra_inputs": [],
 "inputs": [],
 "outputs": [],
 "pwd": "."
}
^^^ Do not change lines above ^^^
2025-10-07 17:42:49 +05:30
sriram veeraghantaandGitHub 07ff457663 [PRIME-24] fix: update live server aio build path (#7911)
* fix: udpate live server aio build path

* chore: build path
2025-10-07 17:21:07 +05:30
Jayash TripathyandGitHub d3024f8a7f [WEB-4731] feat: add baseui input component to propel package (#7769)
*  feat: add input component to propel package

* 🚨 fix: lint

* 🚨 fix: lint

* fix: add aria-invalid attribute to Input component for better accessibility

* chore: fix formatting in package.json and reorder imports in popover-menu stories
2025-10-07 16:48:02 +05:30
M. PalanikannanandGitHub a740a9f69d [WIKI-713] fix: extra line created on enter key #7913 2025-10-07 14:22:09 +05:30
guru_sainathandGitHub 298acb1e91 [MOB-1221] dev: updated god mode ui #7879 2025-10-07 00:24:09 +05:30
Martin DagarinandGitHub f9e21c58c0 Deployment AIO fixed live entered FATAL state (#7893) 2025-10-06 23:42:21 +05:30
Dheeraj Kumar KetireddyandGitHub 5c842d592e Chore: Filter backend optimizations (#7900)
* refactor: enhance ComplexFilterBackend and BaseFilterSet for Q object filtering

- Introduced BaseFilterSet to support Q object construction for complex filtering.
- Updated ComplexFilterBackend to utilize Q objects for building querysets.
- Improved error handling and validation in filter methods.
- Refactored filter evaluation logic to streamline query construction.

* fix: improve filter processing in BaseFilterSet to handle empty cleaned_data and optimize filter evaluation

- Added handling for cases where cleaned_data is None or empty, returning an empty Q object.
- Optimized filter evaluation by only processing filters that are provided in the request data.

* update ComplexFilterBackend to pass queryset in filter evaluation
2025-10-06 23:36:16 +05:30
0589ac56d5 [WEB-5048] chore: implements esm exports for all packages (#7816)
* fix: esm module imports for live and editor

* fix: convert all pacakges to export esm and cjs build

* fix: auto export

* fix: translation files formatting

* fix: convert eslint files to cjs files for supporting current config

* fix: code uuid package upgrade

---------

Co-authored-by: Aaron Reisman <aaron.reisman@plane.so>
2025-10-06 21:01:32 +05:30
AaronandGitHub cbcb026e9a [WEB-5083] chore: add stories to propel #7888 2025-10-06 20:48:52 +05:30
Vipin ChaudharyandGitHub 0cca31ef2e [WIKI-707] [WIKI-708] fix: editor floating ui modal (#7909) 2025-10-06 16:30:47 +05:30
sriram veeraghanta 4c8f51568a chore: pmp lock upgrade 2025-10-06 01:23:09 +05:30
sriram veeraghanta 8d14a5a004 chore(deps): added vite to catalog 2025-10-06 00:56:52 +05:30
sriram veeraghanta 97381c5145 chore(deps): pnpm version upgrade 2025-10-06 00:38:32 +05:30
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4709a0fca4 chore(deps): bump django (#7892)
Bumps the pip group with 1 update in the /apps/api/requirements directory: [django](https://github.com/django/django).


Updates `django` from 4.2.24 to 4.2.25
- [Commits](https://github.com/django/django/compare/4.2.24...4.2.25)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 4.2.25
  dependency-type: direct:production
  dependency-group: pip
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-05 19:25:09 +05:30
Prateek ShouryaandGitHub b695f161ef [WEB-5074] fix: remove the filter conditions check from the defaultOpen property to prevent unwanted popups. (#7903) 2025-10-03 19:56:31 +05:30
M. PalanikannanandGitHub 431af01483 [WIKI-704] fix: disable exiting on unhandled rejection/exception #7902 2025-10-03 16:10:21 +05:30
Aaryan KhandelwalandGitHub 269fc0d9e0 [WIKI-705] regression: mentions dropdown selection across editors #7901 2025-10-03 15:53:35 +05:30
Anmol Singh BhatiaandGitHub 3c389e2e50 [WEB-5072] chore: propel combobox improvement #7899 2025-10-03 15:01:21 +05:30
M. PalanikannanandGitHub f6677f252f [WIKI-704] fix: hocuspocus error handling (#7898) 2025-10-03 14:01:38 +05:30
Vipin ChaudharyandGitHub 794271ac17 fix-config_types (#7886) 2025-10-01 16:24:58 +05:30
Aaryan KhandelwalandGitHub 931a48d7d7 [WIKI-569] regression: parser kit extensions mismatch #7885 2025-10-01 15:27:06 +05:30
Vipin ChaudharyandGitHub b37e453b32 [WIKI-699] refactor: editor config (#7850) 2025-10-01 15:00:10 +05:30
Aaryan KhandelwalandGitHub a69c6f1b9c regression: slash commands render logic #7882 2025-10-01 02:44:31 +05:30
M. PalanikannanandGitHub ee68895ad3 [WIKI-682] feat: hocuspocus v3 server migration (#7880)
* fix: hocuspocus v3

* fix: publishing messages across redis extension
2025-09-30 20:55:55 +05:30
e110ef55b8 [WIKI-569] chore: migrate to tiptap v3 (#7526)
* chore: upgrade to tiptap v3

* chore: update starter kit

* chore: tippy to floating-ui migration for mentions

* chore: update remaining floating menus

* chore: update setEditorValue function

* fix: potential bugs

* chore: extract out common floating ui positioning logic

* fix: storage api

* fix: bubble menu

* fix: type errors

* fix: type errors

* chore: upgrade tiptap-markdown package

* fix: mentions close callback

* chore: update bubbling sequence

* chore: update package.json

* chore: update tiptap catalogs

* fix: add error handling

* fix: file plugin types

* chore: update live package.json

* fix: broken lock file

---------

Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
2025-09-30 19:56:12 +05:30
sriram veeraghantaandGitHub 5951372555 [WIKI-679] refactor: live server (#7720) 2025-09-30 19:28:15 +05:30
Prateek ShouryaandGitHub 7ce21a6488 [WEB-4969] feat: add toggle button for work item filters row visibility (#7865)
* [WEB-4969] feat: add toggle button for work item filters row visibility

* fix: improve error handling in filter update and refine visibility condition check

* chore: correct visibility toggle parameter in filter store
2025-09-30 18:19:43 +05:30
sriram veeraghantaandGitHub 992457efd2 [WEB-5050] feat: updated work item urls with issues. (#7871)
* feat: updated work item urls with issues.

* chore: updated urls names

* chore: rename file

* chore: code refactor for url ordering

* fix: renamed issue-atachments to attachments
2025-09-30 17:47:05 +05:30
Vamsi KrishnaandGitHub c45151d5e6 [WEB-4882]feat: suspended users (#7844) 2025-09-30 15:31:56 +05:30
Anmol Singh BhatiaandGitHub 726529044e [WEB-4934] dev: propel button implementation (#7859)
* dev: button component code refactor

* dev: propel button component implementation
2025-09-30 15:31:00 +05:30
Vipin ChaudharyandGitHub 0ad439fa63 [WIKI-696] fix: peek view close on click block menu options (#7861)
* fix: block close

* chore : add toggle option in block menu

* fix: separate methods
2025-09-30 15:29:35 +05:30
Aaryan KhandelwalandGitHub 843faf85b7 [WIKI-668] fix: comments mention #7874 2025-09-30 15:27:41 +05:30
sriram veeraghantaandGitHub ded36785e7 feat: optimize project list external endpoint (#7869) 2025-09-29 20:40:33 +05:30
sriram veeraghantaandGitHub 9237f568dd [WEB-5044] fix: ruff lint and format errors (#7868)
* fix: lint errors

* fix: file formatting

* fix: code refactor
2025-09-29 19:15:32 +05:30
NikhilandGitHub 1fb22bd252 [WEB-4805] fix: upgraded psycopgy packages to fix linting and removed unused imports (#7735)
* chore: update psycopg dependencies to version 3.2.9 in base requirements

* refactor: clean up unused imports across multiple files

* chore: update lxml dependency to version 6.0.0 in base requirements

* style: improve code readability by breaking long lines into multiple lines across several files

* style: enhance readability by breaking long lines in ModuleSerializer docstring
2025-09-29 14:33:50 +05:30
Vihar KuramaandGitHub e891482a97 [WEB-4881] feat(utils): add URL display formatter #7770 2025-09-29 14:33:25 +05:30
sriram veeraghantaandGitHub bedc1fae1f [WEB-5027] fix: replace lodash package with lodash-es (#7856)
* fix: replace lodash pacakge with lodash-es

* fix: lint errors

* fix: import fixes
2025-09-26 17:59:31 +05:30
sriram veeraghantaandGitHub 63096ceb8c release: v1.0.0 #7711 2025-09-10 14:53:52 +05:30
sriram veeraghantaandGitHub 9b58b9259e fix: adding proxy command in compose file #7519 2025-07-30 21:03:03 +05:30
sriram veeraghantaandGitHub 1e235600b7 release: v0.28.0 #7488 2025-07-30 16:51:00 +05:30
sriram veeraghantaandGitHub 04b0d7ee9f release: v0.27.1 #7328 2025-07-04 13:31:00 +05:30
sriram veeraghantaandGitHub 5a3f709e72 release: v0.27.0 #7296 2025-07-01 17:40:56 +05:30
sriram veeraghantaandGitHub 5ef33ab798 release: v0.26.1 #7181 2025-06-06 17:55:03 +05:30
sriram veeraghantaandGitHub 27806fbe20 release: v0.26.1 #7179 2025-06-06 17:27:49 +05:30
5218 changed files with 192423 additions and 125240 deletions
+7
View File
@@ -0,0 +1,7 @@
[codespell]
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = .git*,*.svg,i18n,*-lock.yaml,*.css,.codespellrc,migrations,*.js,*.map,*.mjs
check-hidden = true
# ignore all CamelCase and camelCase
ignore-regex = \b[A-Za-z][a-z]+[A-Z][a-zA-Z]+\b
ignore-words-list = tread
+6
View File
@@ -61,3 +61,9 @@ temp/
# Misc
*.pem
*.key
# React Router - https://github.com/remix-run/react-router-templates/blob/dc79b1a065f59f3bfd840d4ef75cc27689b611e6/default/.dockerignore
.react-router/
build/
node_modules/
README.md
+1 -1
View File
@@ -1,7 +1,7 @@
name: Bug report
description: Create a bug report to help us improve Plane
title: "[bug]: "
labels: [🐛bug]
labels: [🐛bug, plane]
assignees: [vihar, pushya22]
body:
- type: markdown
@@ -1,7 +1,7 @@
name: Feature request
description: Suggest a feature to improve Plane
title: "[feature]: "
labels: [✨feature]
labels: [✨feature, plane]
assignees: [vihar, pushya22]
body:
- type: markdown
+48
View File
@@ -0,0 +1,48 @@
---
description: Guidelines for bash commands and tooling in the monorepo
applyTo: "**/*.sh"
---
# Bash & Tooling Instructions
This document outlines the standard tools and commands used in this monorepo.
## Package Manager
We use **pnpm** for package management.
- **Do not use `npm` or `yarn`.**
- Lockfile: `pnpm-lock.yaml`
- Workspace configuration: `pnpm-workspace.yaml`
### Common Commands
- Install dependencies: `pnpm install`
- Run a script in a specific package: `pnpm --filter <package_name> run <script>`
- Run a script in all packages: `pnpm -r run <script>`
## Monorepo Tooling
We use **Turbo** for build system orchestration.
- Configuration: `turbo.json`
## Project Structure
- `apps/`: Contains application services (admin, api, live, proxy, space, web).
- `packages/`: Contains shared packages and libraries.
- `deployments/`: Deployment configurations.
## Running Tests
- To run tests in a specific package (e.g., codemods):
```bash
cd packages/codemods
pnpm run test
```
- Or from root:
```bash
pnpm --filter @plane/codemods run test
```
## Docker
- Local development uses `docker-compose-local.yml`.
- Production/Staging uses `docker-compose.yml`.
@@ -0,0 +1,129 @@
---
description: Guidelines for using modern TypeScript features (v5.0-v5.8)
applyTo: "**/*.{ts,tsx,mts,cts}"
---
# TypeScript Coding Guidelines & Modern Features (v5.0 - v5.8)
When writing TypeScript code, prioritize using modern features and best practices introduced in recent versions (up to 5.8).
## Global Themes Across 5.x
1. **Standard decorators are here; legacy decorators are legacy.**
New TC39-compliant decorators landed in 5.0 and were extended in 5.2 (metadata). Old `experimentalDecorators`-style behavior is still supported but should be treated as legacy.
2. **Type system is more precise and less noisy.**
Major work went into narrowing, control flow analysis, error messages, and new helpers like `NoInfer`, inferred predicates, and better `undefined`/`never`/uninitialized checks.
3. **Module / runtime interop has been modernized.**
Options like `--moduleResolution bundler`, `--module nodenext`/`node18`, `--rewriteRelativeImportExtensions`, `--erasableSyntaxOnly`, and `--verbatimModuleSyntax` are about playing nicely with ESM, Node 18+/22+, direct TypeScript execution, and bundlers.
4. **The standard library keeps tracking modern JS.**
Support for new ES features (iterator helpers, `Object.groupBy`/`Map.groupBy`, new Set/ES2024 APIs) shows up as type declarations and sometimes extra checks (regex syntax checking, etc.).
When generating or refactoring code, prefer these newer idioms, and avoid patterns that conflict with updated checks.
## Modern Features to Utilize
### Type System & Inference
- **`const` Type Parameters (5.0)**: Use `const` type parameters for more precise literal inference.
```typescript
declare function names<const T extends string[]>(...names: T): void;
```
- **`@satisfies` Operator (5.0)**: Use `satisfies` to validate types without widening them.
- **Inferred Type Predicates (5.5)**: Allow TypeScript to infer type predicates for functions that filter arrays or check types, reducing the need for explicit `is` return types.
- **`NoInfer` Utility (5.4)**: Use `NoInfer<T>` to block inference for specific type arguments when you want them to be determined by other arguments.
- **Narrowing**:
- **Switch(true) (5.3)**: Utilize narrowing in `switch(true)` blocks.
- **Boolean Comparisons (5.3)**: Rely on narrowing from direct boolean comparisons.
- **Closures (5.4)**: Trust preserved narrowing in closures when variables aren't modified after the check.
- **Constant Indexed Access (5.5)**: Use constant indices to narrow object/array properties.
### Syntax & Control Flow
- **Decorators (5.0)**: Use standard ECMAScript decorators (Stage 3).
- **`using` Declarations (5.2)**: Use `using` for explicit resource management (Disposable pattern) instead of manual cleanup.
```typescript
using resource = new Resource();
```
- **Import Attributes (5.3/5.8)**: Use `with { type: "json" }` for import attributes. Avoid the deprecated `assert` syntax.
- **`switch` Exhaustiveness**: Rely on TypeScript's exhaustiveness checking in switch statements.
### Modules & Imports
- **`verbatimModuleSyntax` (5.0)**: Respect this flag by using `import type` explicitly when importing types to ensure they are erased during compilation.
- **Type-Only Imports with Extensions (5.2)**: You can use `.ts`, `.mts`, `.cts` extensions in `import type` statements.
- **`resolution-mode` (5.3)**: Use `import type { Type } from "mod" with { "resolution-mode": "import" }` if needed for specific module resolution contexts.
- **JSDoc `@import` (5.5)**: Use `@import` tags in JSDoc for cleaner type imports in JS files if working in a mixed codebase.
### Standard Library & Built-ins
- **Iterator Helpers (5.6)**: Use new iterator methods (map, filter, etc.) if targeting modern environments.
- **Set Methods (5.5)**: Utilize new `Set` methods like `union`, `intersection`, etc., when available.
- **`Object.groupBy` / `Map.groupBy` (5.4)**: Use these standard methods for grouping instead of external libraries like Lodash when appropriate.
- **`Promise.withResolvers` (5.7)**: Use `Promise.withResolvers()` for creating promises with exposed resolve/reject functions.
### Configuration & Tooling
- **`--moduleResolution bundler` (5.0)**: Assume this resolution strategy for modern web projects (Vite, Next.js, etc.).
- **`--erasableSyntaxOnly` (5.8)**: Be aware of this flag; avoid TypeScript-specific syntax that cannot be simply erased (like `enum`s or `namespaces`) if the project aims for maximum compatibility with tools like Node.js's `--strip-types`. Prefer `const` objects or unions over `enum`s if requested.
## Specific Coding Patterns
### Arrays & Collections
- Use **Copying Array Methods (5.2)** (`toSorted`, `toSpliced`, `with`) for immutable array operations.
- **TypedArrays (5.7)**: Be aware that TypedArrays are now generic over `ArrayBufferLike`.
### Classes
- **Parameter Decorators (5.0/5.2)**: Use modern standard decorators.
- **`super` Property Access (5.3)**: Avoid accessing instance fields via `super`.
### Error Handling
- **Checks for Never-Initialized Variables (5.7)**: Ensure variables are initialized before use to avoid new errors.
## Deprecations to Avoid
- Avoid `import ... assert` (use `with`).
- Avoid implicit `any` returns in `undefined`-returning functions (though 5.1 makes this easier, explicit is better).
- Avoid `enum`s if the project prefers erasable syntax (5.8).
## Version-Specific Highlights
### TypeScript 5.0
- **Decorators**: Use standard decorators unless `experimentalDecorators` is explicitly enabled.
- **`const` Type Parameters**: Use for literal inference.
- **Enums**: All enums are union enums.
- **Modules**: `--moduleResolution bundler` and `--verbatimModuleSyntax` are key for modern bundlers.
### TypeScript 5.1
- **Returns**: `undefined`-returning functions don't need explicit returns.
- **Getters/Setters**: Can have unrelated types with explicit annotations.
### TypeScript 5.2
- **Resource Management**: `using` declarations for `Symbol.dispose`.
- **Decorator Metadata**: Use `context.metadata` for design-time metadata.
### TypeScript 5.3
- **Import Attributes**: Use `with { type: "json" }`.
- **Switch(true)**: Narrowing works in `switch(true)`.
### TypeScript 5.4
- **Closures**: Narrowing preserved in closures if last assignment is before creation.
- **`NoInfer`**: Block inference for specific arguments.
- **Grouping**: `Object.groupBy` / `Map.groupBy`.
### TypeScript 5.5
- **Inferred Predicates**: Functions checking types often don't need explicit `is` return types.
- **Constant Index Access**: Better narrowing for constant keys.
- **Regex**: Syntax checking for regex literals.
### TypeScript 5.6
- **Truthiness Checks**: Errors on always-truthy/falsy conditions (e.g., `if (/regex/)`).
- **Iterator Helpers**: `.map`, `.filter` on iterators.
### TypeScript 5.7
- **Uninitialized Variables**: Stricter checks for never-initialized variables.
- **Relative Imports**: `--rewriteRelativeImportExtensions` for `.ts` imports in output.
- **ES2024**: Support for `Promise.withResolvers`, `Atomics.waitAsync`.
### TypeScript 5.8
- **Return Checks**: Granular checks for conditional returns.
- **Node Modules**: `--module node18` stable; `require()` of ESM allowed in `nodenext`.
- **Erasable Syntax**: `--erasableSyntaxOnly` forbids enums, namespaces, etc.
When generating code, always prefer the most modern, standard, and type-safe approach available in TypeScript 5.8.
+25
View File
@@ -0,0 +1,25 @@
# Codespell configuration is within .codespellrc
---
name: Codespell
on:
push:
branches: [preview]
pull_request:
branches: [preview]
permissions:
contents: read
jobs:
codespell:
name: Check for spelling errors
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Annotate locations with typos
uses: codespell-project/codespell-problem-matcher@v1
- name: Codespell
uses: codespell-project/actions-codespell@v2
+45
View File
@@ -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."
@@ -27,11 +27,13 @@ jobs:
github.event.pull_request.draft == false &&
github.event.pull_request.requested_reviewers != null
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: "3.x"
python-version: "3.12.x"
cache: 'pip'
cache-dependency-path: 'apps/api/requirements.txt'
- name: Install Pylint
run: python -m pip install ruff
- name: Install API Dependencies
@@ -17,10 +17,11 @@ concurrency:
cancel-in-progress: true
jobs:
build-and-lint:
name: Build and lint web apps
# Format check has no build dependencies - run immediately in parallel
check-format:
name: check:format
runs-on: ubuntu-latest
timeout-minutes: 25
timeout-minutes: 10
if: |
github.event.pull_request.draft == false &&
github.event.pull_request.requested_reviewers != null
@@ -29,25 +30,140 @@ jobs:
TURBO_SCM_HEAD: ${{ github.sha }}
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 50
filter: blob:none
- name: Set up Node.js
uses: actions/setup-node@v4
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: Lint Affected
run: pnpm turbo run check:lint --affected
- name: Check Affected format
- name: Check formatting
run: pnpm turbo run check:format --affected
- name: Build Affected
# Build packages - required for lint and type checks
build:
name: Build packages
runs-on: ubuntu-latest
timeout-minutes: 15
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 }}
NODE_OPTIONS: "--max-old-space-size=4096"
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: Restore Turbo cache
uses: actions/cache/restore@v4
with:
path: .turbo
key: turbo-${{ runner.os }}-${{ github.event.pull_request.base.sha }}-${{ github.sha }}
restore-keys: |
turbo-${{ runner.os }}-${{ github.event.pull_request.base.sha }}-
turbo-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build packages
run: pnpm turbo run build --affected
- name: Save Turbo cache
uses: actions/cache/save@v4
with:
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 }}
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 }}
NODE_OPTIONS: "--max-old-space-size=4096"
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: Restore Turbo cache
uses: actions/cache/restore@v4
with:
path: .turbo
key: turbo-${{ runner.os }}-${{ github.event.pull_request.base.sha }}-${{ github.sha }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run ${{ matrix.task }}
run: pnpm turbo run ${{ matrix.task }} --affected
+12 -1
View File
@@ -16,9 +16,10 @@ node_modules
/out/
# Production
/build
dist/
out/
build/
.react-router/
# Misc
.DS_Store
@@ -99,3 +100,13 @@ dev-editor
*.rdb.gz
storybook-static
CLAUDE.md
build/
.react-router/
build/
.react-router/
temp/
scripts/
+1
View File
@@ -0,0 +1 @@
pnpm lint-staged
+2
View File
@@ -0,0 +1,2 @@
[tools]
node = "22.18.0"
+45 -25
View File
@@ -1,34 +1,54 @@
# Enforce pnpm workspace behavior and allow Turbo's lifecycle hooks if scripts are disabled
# This repo uses pnpm with workspaces.
# ------------------------------
# Core Workspace Behavior
# ------------------------------
# Prefer linking local workspace packages when available
prefer-workspace-packages=true
link-workspace-packages=true
shared-workspace-lockfile=true
# Always prefer using local workspace packages when available
prefer-workspace-packages = true
# Make peer installs smoother across the monorepo
auto-install-peers=true
strict-peer-dependencies=false
# Symlink workspace packages instead of duplicating them
link-workspace-packages = true
# If scripts are disabled (e.g., CI with --ignore-scripts), allowlisted packages can still run their hooks
# Turbo occasionally performs postinstall tasks for optimal performance
# moved to pnpm-workspace.yaml: onlyBuiltDependencies (e.g., allow turbo)
# Use a single lockfile across the whole monorepo
shared-workspace-lockfile = true
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=prettier
public-hoist-pattern[]=typescript
# Ensure packages added from workspace save using workspace: protocol
save-workspace-protocol = true
# Reproducible installs across CI and dev
prefer-frozen-lockfile=true
# Prefer resolving to highest versions in monorepo to reduce duplication
resolution-mode=highest
# ------------------------------
# Dependency Resolution
# ------------------------------
# Speed up native module builds by caching side effects
side-effects-cache=true
# Choose the highest compatible version across the workspace
# → reduces fragmentation & node_modules bloat
resolution-mode = highest
# Speed up local dev by reusing local store when possible
prefer-offline=true
# Automatically install peer dependencies instead of forcing every package to declare them
auto-install-peers = true
# Ensure workspace protocol is used when adding internal deps
save-workspace-protocol=true
# Don't break the install if peers are missing
strict-peer-dependencies = false
# ------------------------------
# Performance Optimizations
# ------------------------------
# Use cached artifacts for native modules (sharp, esbuild, etc.)
side-effects-cache = true
# Prefer local cached packages rather than hitting network
prefer-offline = true
# In CI, refuse to modify lockfile (prevents drift)
prefer-frozen-lockfile = true
# Use isolated linker (best compatibility with Node ecosystem tools)
node-linker = isolated
# Hoist commonly used tools to the root to prevent duplicates and speed up resolution
public-hoist-pattern[] = typescript
public-hoist-pattern[] = eslint
public-hoist-pattern[] = *@plane/*
public-hoist-pattern[] = vite
public-hoist-pattern[] = turbo
+10
View File
@@ -0,0 +1,10 @@
.next/
.react-router/
.turbo/
.vite/
build/
dist/
node_modules/
out/
pnpm-lock.yaml
storybook-static/
+15
View File
@@ -0,0 +1,15 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"overrides": [
{
"files": ["packages/codemods/**/*"],
"options": {
"printWidth": 80
}
}
],
"plugins": ["@prettier/plugin-oxc"],
"printWidth": 120,
"tabWidth": 2,
"trailingComma": "es5"
}
+24
View File
@@ -0,0 +1,24 @@
# Agent Development Guide
## Commands
- `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:types` - TypeScript type checking
- `pnpm fix` - Auto-fix format and lint issues
- `pnpm turbo run <command> --filter=<package>` - Target specific package/app
- `pnpm --filter=@plane/ui storybook` - Start Storybook on port 6006
## Code Style
- **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
- **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
- **Testing**: All features require unit tests, use existing test framework per package
- **Components**: Build in `@plane/ui` with Storybook for isolated development
+1
View File
@@ -0,0 +1 @@
eslint.config.mjs @lifeiscontent
+10 -8
View File
@@ -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 use [Eslint default rule guide](https://eslint.org/docs/rules/), with minor changes. An automated formatter is available using prettier.
- 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`.
## Ways to contribute
@@ -187,18 +187,19 @@ Adding a new language involves several steps to ensure it integrates seamlessly
Add the new language to the TLanguage type in the language definitions file:
```ts
// packages/i18n/src/types/language.ts
export type TLanguage = "en" | "fr" | "your-lang";
// packages/i18n/src/types/language.ts
export type TLanguage = "en" | "fr" | "your-lang";
```
1. **Add language configuration**
Include the new language in the list of supported languages:
```ts
// packages/i18n/src/constants/language.ts
export const SUPPORTED_LANGUAGES: ILanguageOption[] = [
{ label: "English", value: "en" },
{ label: "Your Language", value: "your-lang" }
];
// packages/i18n/src/constants/language.ts
export const SUPPORTED_LANGUAGES: ILanguageOption[] = [
{ label: "English", value: "en" },
{ label: "Your Language", value: "your-lang" },
];
```
2. **Create translation files**
@@ -210,6 +211,7 @@ Adding a new language involves several steps to ensure it integrates seamlessly
3. **Update import logic**
Modify the language import logic to include your new language:
```ts
private importLanguageFile(language: TLanguage): Promise<any> {
switch (language) {
+3
View File
@@ -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.
+34
View File
@@ -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.
+2 -4
View File
@@ -54,7 +54,7 @@ Getting started with Plane is simple. Choose the setup that works best for you:
## 🌟 Features
- **Issues**
- **Work Items**
Efficiently create and manage tasks with a robust rich text editor that supports file uploads. Enhance organization and tracking by adding sub-properties and referencing related issues.
- **Cycles**
@@ -72,15 +72,13 @@ Getting started with Plane is simple. Choose the setup that works best for you:
- **Analytics**
Access real-time insights across all your Plane data. Visualize trends, remove blockers, and keep your projects moving forward.
- **Drive** (_coming soon_): The drive helps you share documents, images, videos, or any other files that make sense to you or your team and align on the problem/solution.
## 🛠️ Local development
See [CONTRIBUTING](./CONTRIBUTING.md)
## ⚙️ Built with
[![Next JS](https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white)](https://nextjs.org/)
[![React Router](https://img.shields.io/badge/-React%20Router-CA4245?logo=react-router&style=for-the-badge&logoColor=white)](https://reactrouter.com/)
[![Django](https://img.shields.io/badge/Django-092E20?style=for-the-badge&logo=django&logoColor=green)](https://www.djangoproject.com/)
[![Node JS](https://img.shields.io/badge/node.js-339933?style=for-the-badge&logo=Node.js&logoColor=white)](https://nodejs.org/en)
+5
View File
@@ -0,0 +1,5 @@
# React Router - https://github.com/remix-run/react-router-templates/blob/dc79b1a065f59f3bfd840d4ef75cc27689b611e6/default/.dockerignore
.react-router/
build/
node_modules/
README.md
+8 -8
View File
@@ -1,12 +1,12 @@
NEXT_PUBLIC_API_BASE_URL="http://localhost:8000"
VITE_API_BASE_URL="http://localhost:8000"
NEXT_PUBLIC_WEB_BASE_URL="http://localhost:3000"
VITE_WEB_BASE_URL="http://localhost:3000"
NEXT_PUBLIC_ADMIN_BASE_URL="http://localhost:3001"
NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
VITE_ADMIN_BASE_URL="http://localhost:3001"
VITE_ADMIN_BASE_PATH="/god-mode"
NEXT_PUBLIC_SPACE_BASE_URL="http://localhost:3002"
NEXT_PUBLIC_SPACE_BASE_PATH="/spaces"
VITE_SPACE_BASE_URL="http://localhost:3002"
VITE_SPACE_BASE_PATH="/spaces"
NEXT_PUBLIC_LIVE_BASE_URL="http://localhost:3100"
NEXT_PUBLIC_LIVE_BASE_PATH="/live"
VITE_LIVE_BASE_URL="http://localhost:3100"
VITE_LIVE_BASE_PATH="/live"
-12
View File
@@ -1,12 +0,0 @@
.next/*
out/*
public/*
dist/*
node_modules/*
.turbo/*
.env*
.env
.env.local
.env.development
.env.production
.env.test
-4
View File
@@ -1,4 +0,0 @@
module.exports = {
root: true,
extends: ["@plane/eslint-config/next.js"],
};
+9 -5
View File
@@ -1,6 +1,10 @@
.next
.vercel
.tubro
out/
dist/
.next/
.react-router/
.turbo/
.vite/
build/
dist/
node_modules/
out/
pnpm-lock.yaml
storybook-static/
-5
View File
@@ -1,5 +0,0 @@
{
"printWidth": 120,
"tabWidth": 2,
"trailingComma": "es5"
}
+59 -74
View File
@@ -1,103 +1,88 @@
# syntax=docker/dockerfile:1.7
FROM node:22-alpine AS base
# Setup pnpm package manager with corepack and configure global bin directory for caching
WORKDIR /app
ENV TURBO_TELEMETRY_DISABLED=1
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
ENV CI=1
RUN corepack enable pnpm
# =========================================================================== #
# *****************************************************************************
# STAGE 1: Build the project
# *****************************************************************************
FROM base AS builder
RUN apk add --no-cache libc6-compat
WORKDIR /app
ARG TURBO_VERSION=2.5.6
RUN corepack enable pnpm && pnpm add -g turbo@${TURBO_VERSION}
RUN pnpm add -g turbo@2.6.3
COPY . .
# Create a pruned workspace for just the admin app
RUN turbo prune --scope=admin --docker
# *****************************************************************************
# STAGE 2: Install dependencies & build the project
# *****************************************************************************
# =========================================================================== #
FROM base AS installer
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Build in production mode; we still install dev deps explicitly below
ENV NODE_ENV=production
# Public envs required at build time (pick up via process.env)
ARG VITE_API_BASE_URL=""
ENV VITE_API_BASE_URL=$VITE_API_BASE_URL
ARG VITE_API_BASE_PATH="/api"
ENV VITE_API_BASE_PATH=$VITE_API_BASE_PATH
ARG VITE_ADMIN_BASE_URL=""
ENV VITE_ADMIN_BASE_URL=$VITE_ADMIN_BASE_URL
ARG VITE_ADMIN_BASE_PATH="/god-mode"
ENV VITE_ADMIN_BASE_PATH=$VITE_ADMIN_BASE_PATH
ARG VITE_SPACE_BASE_URL=""
ENV VITE_SPACE_BASE_URL=$VITE_SPACE_BASE_URL
ARG VITE_SPACE_BASE_PATH="/spaces"
ENV VITE_SPACE_BASE_PATH=$VITE_SPACE_BASE_PATH
ARG VITE_LIVE_BASE_URL=""
ENV VITE_LIVE_BASE_URL=$VITE_LIVE_BASE_URL
ARG VITE_LIVE_BASE_PATH="/live"
ENV VITE_LIVE_BASE_PATH=$VITE_LIVE_BASE_PATH
ARG VITE_WEB_BASE_URL=""
ENV VITE_WEB_BASE_URL=$VITE_WEB_BASE_URL
ARG VITE_WEB_BASE_PATH=""
ENV VITE_WEB_BASE_PATH=$VITE_WEB_BASE_PATH
ARG VITE_WEBSITE_URL="https://plane.so"
ENV VITE_WEBSITE_URL=$VITE_WEBSITE_URL
ARG VITE_SUPPORT_EMAIL="support@plane.so"
ENV VITE_SUPPORT_EMAIL=$VITE_SUPPORT_EMAIL
COPY .gitignore .gitignore
COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN corepack enable pnpm
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm fetch --store-dir=/pnpm/store
# Copy full directory structure before fetch to ensure all package.json files are available
COPY --from=builder /app/out/full/ .
COPY turbo.json turbo.json
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install --offline --frozen-lockfile --store-dir=/pnpm/store
ARG NEXT_PUBLIC_API_BASE_URL=""
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
ARG NEXT_PUBLIC_ADMIN_BASE_URL=""
ENV NEXT_PUBLIC_ADMIN_BASE_URL=$NEXT_PUBLIC_ADMIN_BASE_URL
ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
ENV NEXT_PUBLIC_ADMIN_BASE_PATH=$NEXT_PUBLIC_ADMIN_BASE_PATH
ARG NEXT_PUBLIC_SPACE_BASE_URL=""
ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL
ARG NEXT_PUBLIC_SPACE_BASE_PATH="/spaces"
ENV NEXT_PUBLIC_SPACE_BASE_PATH=$NEXT_PUBLIC_SPACE_BASE_PATH
ARG NEXT_PUBLIC_WEB_BASE_URL=""
ENV NEXT_PUBLIC_WEB_BASE_URL=$NEXT_PUBLIC_WEB_BASE_URL
ENV NEXT_TELEMETRY_DISABLED=1
ENV TURBO_TELEMETRY_DISABLED=1
# Fetch dependencies to cache store, then install offline with dev deps
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm fetch --store-dir=/pnpm/store
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store CI=true pnpm install --offline --frozen-lockfile --store-dir=/pnpm/store --prod=false
# Build only the admin package
RUN pnpm turbo run build --filter=admin
# *****************************************************************************
# STAGE 3: Copy the project and start it
# *****************************************************************************
FROM base AS runner
WORKDIR /app
# =========================================================================== #
# Don't run production as root
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs
FROM nginx:1.29-alpine AS production
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=installer /app/apps/admin/.next/standalone ./
COPY --from=installer /app/apps/admin/.next/static ./apps/admin/.next/static
COPY --from=installer /app/apps/admin/public ./apps/admin/public
ARG NEXT_PUBLIC_API_BASE_URL=""
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
ARG NEXT_PUBLIC_ADMIN_BASE_URL=""
ENV NEXT_PUBLIC_ADMIN_BASE_URL=$NEXT_PUBLIC_ADMIN_BASE_URL
ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
ENV NEXT_PUBLIC_ADMIN_BASE_PATH=$NEXT_PUBLIC_ADMIN_BASE_PATH
ARG NEXT_PUBLIC_SPACE_BASE_URL=""
ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL
ARG NEXT_PUBLIC_SPACE_BASE_PATH="/spaces"
ENV NEXT_PUBLIC_SPACE_BASE_PATH=$NEXT_PUBLIC_SPACE_BASE_PATH
ARG NEXT_PUBLIC_WEB_BASE_URL=""
ENV NEXT_PUBLIC_WEB_BASE_URL=$NEXT_PUBLIC_WEB_BASE_URL
ENV NEXT_TELEMETRY_DISABLED=1
ENV TURBO_TELEMETRY_DISABLED=1
COPY apps/admin/nginx/nginx.conf /etc/nginx/nginx.conf
COPY --from=installer /app/apps/admin/build/client /usr/share/nginx/html/god-mode
EXPOSE 3000
CMD ["node", "apps/admin/server.js"]
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -fsS http://127.0.0.1:3000/ >/dev/null || exit 1
CMD ["nginx", "-g", "daemon off;"]
+1 -1
View File
@@ -8,7 +8,7 @@ COPY . .
RUN corepack enable pnpm && pnpm add -g turbo
RUN pnpm install
ENV NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
ENV VITE_ADMIN_BASE_PATH="/god-mode"
EXPOSE 3000
+22 -16
View File
@@ -1,11 +1,17 @@
"use client";
import { FC } 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 { useForm } from "react-hook-form";
import { Lightbulb } from "lucide-react";
import { IFormattedInstanceConfiguration, TInstanceAIConfigurationKeys } from "@plane/types";
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IFormattedInstanceConfiguration, TInstanceAIConfigurationKeys } from "@plane/types";
// components
import { ControllerInput, TControllerInputFormField } from "@/components/common/controller-input";
import type { TControllerInputFormField } from "@/components/common/controller-input";
import { ControllerInput } from "@/components/common/controller-input";
// hooks
import { useInstance } from "@/hooks/store";
@@ -15,7 +21,7 @@ type IInstanceAIForm = {
type AIFormValues = Record<TInstanceAIConfigurationKeys, string>;
export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
export function InstanceAIForm(props: IInstanceAIForm) {
const { config } = props;
// store
const { updateInstanceConfigurations } = useInstance();
@@ -42,7 +48,7 @@ export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
<a
href="https://platform.openai.com/docs/models/overview"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
Learn more
@@ -63,7 +69,7 @@ export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
<a
href="https://platform.openai.com/api-keys"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
here.
@@ -94,8 +100,8 @@ export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
<div className="space-y-8">
<div className="space-y-3">
<div>
<div className="pb-1 text-xl font-medium text-custom-text-100">OpenAI</div>
<div className="text-sm font-normal text-custom-text-300">If you use ChatGPT, this is for you.</div>
<div className="pb-1 text-18 font-medium text-primary">OpenAI</div>
<div className="text-13 font-regular text-tertiary">If you use ChatGPT, this is for you.</div>
</div>
<div className="grid-col grid w-full grid-cols-1 items-center justify-between gap-x-12 gap-y-8 lg:grid-cols-3">
{aiFormFields.map((field) => (
@@ -114,13 +120,13 @@ export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
</div>
</div>
<div className="space-y-4">
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
{isSubmitting ? "Saving..." : "Save changes"}
<div className="flex flex-col gap-4 items-start">
<Button variant="primary" size="lg" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
{isSubmitting ? "Saving" : "Save changes"}
</Button>
<div className="relative inline-flex items-center gap-2 rounded border border-custom-primary-100/20 bg-custom-primary-100/10 px-4 py-2 text-xs text-custom-primary-200">
<Lightbulb height="14" width="14" />
<div className="relative inline-flex items-center gap-1.5 rounded-sm border border-accent-subtle bg-accent-subtle px-4 py-2 text-caption-sm-regular text-accent-secondary ">
<Lightbulb className="size-4" />
<div>
If you have a preferred AI models vendor, please get in{" "}
<a className="underline font-medium" href="https://plane.so/contact">
@@ -131,4 +137,4 @@ export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
</div>
</div>
);
};
}
@@ -1,10 +0,0 @@
import { ReactNode } from "react";
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Artificial Intelligence Settings - God Mode",
};
export default function AILayout({ children }: { children: ReactNode }) {
return <>{children}</>;
}
+31 -26
View File
@@ -1,45 +1,50 @@
"use client";
/**
* 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";
// components
import { PageWrapper } from "@/components/common/page-wrapper";
// hooks
import { useInstance } from "@/hooks/store";
// components
// types
import type { Route } from "./+types/page";
// local
import { InstanceAIForm } from "./form";
const InstanceAIPage = observer(() => {
const InstanceAIPage = observer(function InstanceAIPage(_props: Route.ComponentProps) {
// store
const { fetchInstanceConfigurations, formattedConfig } = useInstance();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
return (
<>
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">AI features for all your workspaces</div>
<div className="text-sm font-normal text-custom-text-300">
Configure your AI API credentials so Plane AI features are turned on for all your workspaces.
<PageWrapper
header={{
title: "AI features for all your workspaces",
description: "Configure your AI API credentials so Plane AI features are turned on for all your workspaces.",
}}
>
{formattedConfig ? (
<InstanceAIForm config={formattedConfig} />
) : (
<Loader className="space-y-8">
<Loader.Item height="50px" width="40%" />
<div className="w-2/3 grid grid-cols-2 gap-x-8 gap-y-4">
<Loader.Item height="50px" />
<Loader.Item height="50px" />
</div>
</div>
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? (
<InstanceAIForm config={formattedConfig} />
) : (
<Loader className="space-y-8">
<Loader.Item height="50px" width="40%" />
<div className="w-2/3 grid grid-cols-2 gap-x-8 gap-y-4">
<Loader.Item height="50px" />
<Loader.Item height="50px" />
</div>
<Loader.Item height="50px" width="20%" />
</Loader>
)}
</div>
</div>
</>
<Loader.Item height="50px" width="20%" />
</Loader>
)}
</PageWrapper>
);
});
export const meta: Route.MetaFunction = () => [{ title: "Artificial Intelligence Settings - God Mode" }];
export default InstanceAIPage;
@@ -0,0 +1,225 @@
/**
* 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";
import { useForm } from "react-hook-form";
// plane internal packages
import { API_BASE_URL } from "@plane/constants";
import { Button, getButtonStyling } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IFormattedInstanceConfiguration, TInstanceGiteaAuthenticationConfigurationKeys } from "@plane/types";
// components
import { CodeBlock } from "@/components/common/code-block";
import { ConfirmDiscardModal } from "@/components/common/confirm-discard-modal";
import type { TControllerInputFormField } from "@/components/common/controller-input";
import { ControllerInput } from "@/components/common/controller-input";
import type { TControllerSwitchFormField } from "@/components/common/controller-switch";
import { ControllerSwitch } from "@/components/common/controller-switch";
import type { TCopyField } from "@/components/common/copy-field";
import { CopyField } from "@/components/common/copy-field";
// hooks
import { useInstance } from "@/hooks/store";
type Props = {
config: IFormattedInstanceConfiguration;
};
type GiteaConfigFormValues = Record<TInstanceGiteaAuthenticationConfigurationKeys, string>;
export function InstanceGiteaConfigForm(props: Props) {
const { config } = props;
// states
const [isDiscardChangesModalOpen, setIsDiscardChangesModalOpen] = useState(false);
// store hooks
const { updateInstanceConfigurations } = useInstance();
// form data
const {
handleSubmit,
control,
reset,
formState: { errors, isDirty, isSubmitting },
} = useForm<GiteaConfigFormValues>({
defaultValues: {
GITEA_HOST: config["GITEA_HOST"] || "https://gitea.com",
GITEA_CLIENT_ID: config["GITEA_CLIENT_ID"],
GITEA_CLIENT_SECRET: config["GITEA_CLIENT_SECRET"],
ENABLE_GITEA_SYNC: config["ENABLE_GITEA_SYNC"] || "0",
},
});
const originURL = !isEmpty(API_BASE_URL) ? API_BASE_URL : typeof window !== "undefined" ? window.location.origin : "";
const GITEA_FORM_FIELDS: TControllerInputFormField[] = [
{
key: "GITEA_HOST",
type: "text",
label: "Gitea Host",
description: (
<>Use the URL of your Gitea instance. For the official Gitea instance, use &quot;https://gitea.com&quot;.</>
),
placeholder: "https://gitea.com",
error: Boolean(errors.GITEA_HOST),
required: true,
},
{
key: "GITEA_CLIENT_ID",
type: "text",
label: "Client ID",
description: (
<>
You will get this from your{" "}
<a
tabIndex={-1}
href="https://gitea.com/user/settings/applications"
target="_blank"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
Gitea OAuth application settings.
</a>
</>
),
placeholder: "70a44354520df8bd9bcd",
error: Boolean(errors.GITEA_CLIENT_ID),
required: true,
},
{
key: "GITEA_CLIENT_SECRET",
type: "password",
label: "Client secret",
description: (
<>
Your client secret is also found in your{" "}
<a
tabIndex={-1}
href="https://gitea.com/user/settings/applications"
target="_blank"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
Gitea OAuth application settings.
</a>
</>
),
placeholder: "9b0050f94ec1b744e32ce79ea4ffacd40d4119cb",
error: Boolean(errors.GITEA_CLIENT_SECRET),
required: true,
},
];
const GITEA_FORM_SWITCH_FIELD: TControllerSwitchFormField<GiteaConfigFormValues> = {
name: "ENABLE_GITEA_SYNC",
label: "Gitea",
};
const GITEA_SERVICE_FIELD: TCopyField[] = [
{
key: "Callback_URI",
label: "Callback URI",
url: `${originURL}/auth/gitea/callback/`,
description: (
<>
We will auto-generate this. Paste this into your <CodeBlock darkerShade>Authorized Callback URI</CodeBlock>{" "}
field{" "}
<a
tabIndex={-1}
href={`${control._formValues.GITEA_HOST || "https://gitea.com"}/user/settings/applications`}
target="_blank"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
here.
</a>
</>
),
},
];
const onSubmit = async (formData: GiteaConfigFormValues) => {
const payload: Partial<GiteaConfigFormValues> = { ...formData };
try {
const response = await updateInstanceConfigurations(payload);
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Done!",
message: "Your Gitea authentication is configured. You should test it now.",
});
reset({
GITEA_HOST: response.find((item) => item.key === "GITEA_HOST")?.value,
GITEA_CLIENT_ID: response.find((item) => item.key === "GITEA_CLIENT_ID")?.value,
GITEA_CLIENT_SECRET: response.find((item) => item.key === "GITEA_CLIENT_SECRET")?.value,
ENABLE_GITEA_SYNC: response.find((item) => item.key === "ENABLE_GITEA_SYNC")?.value,
});
} catch (err) {
console.error(err);
}
};
const handleGoBack = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
if (isDirty) {
e.preventDefault();
setIsDiscardChangesModalOpen(true);
}
};
return (
<>
<ConfirmDiscardModal
isOpen={isDiscardChangesModalOpen}
onDiscardHref="/authentication"
handleClose={() => setIsDiscardChangesModalOpen(false)}
/>
<div className="flex flex-col gap-8">
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1 pt-1">
<div className="pt-2.5 text-18 font-medium">Gitea-provided details for Plane</div>
{GITEA_FORM_FIELDS.map((field) => (
<ControllerInput
key={field.key}
control={control}
type={field.type}
name={field.key}
label={field.label}
description={field.description}
placeholder={field.placeholder}
error={field.error}
required={field.required}
/>
))}
<ControllerSwitch control={control} field={GITEA_FORM_SWITCH_FIELD} />
<div className="flex flex-col gap-1 pt-4">
<div className="flex items-center gap-4">
<Button
variant="primary"
size="lg"
onClick={(e) => void handleSubmit(onSubmit)(e)}
loading={isSubmitting}
disabled={!isDirty}
>
{isSubmitting ? "Saving" : "Save changes"}
</Button>
<Link href="/authentication" className={getButtonStyling("secondary", "lg")} onClick={handleGoBack}>
Go back
</Link>
</div>
</div>
</div>
<div className="col-span-2 md:col-span-1">
<div className="flex flex-col gap-y-4 px-6 pt-1.5 pb-4 bg-layer-1 rounded-lg">
<div className="pt-2 text-18 font-medium">Plane-provided details for Gitea</div>
{GITEA_SERVICE_FIELD.map((field) => (
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
))}
</div>
</div>
</div>
</div>
</>
);
}
@@ -0,0 +1,105 @@
/**
* 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";
// plane internal packages
import { setPromiseToast } from "@plane/propel/toast";
import { Loader, ToggleSwitch } from "@plane/ui";
// assets
import giteaLogo from "@/app/assets/logos/gitea-logo.svg?url";
// components
import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card";
import { PageWrapper } from "@/components/common/page-wrapper";
// hooks
import { useInstance } from "@/hooks/store";
// types
import type { Route } from "./+types/page";
// local
import { InstanceGiteaConfigForm } from "./form";
const InstanceGiteaAuthenticationPage = observer(function InstanceGiteaAuthenticationPage() {
// store
const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance();
// state
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
// config
const enableGiteaConfig = formattedConfig?.IS_GITEA_ENABLED ?? "";
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
const updateConfig = async (key: "IS_GITEA_ENABLED", value: string) => {
setIsSubmitting(true);
const payload = {
[key]: value,
};
const updateConfigPromise = updateInstanceConfigurations(payload);
setPromiseToast(updateConfigPromise, {
loading: "Saving Configuration",
success: {
title: "Configuration saved",
message: () => `Gitea authentication is now ${value === "1" ? "active" : "disabled"}.`,
},
error: {
title: "Error",
message: () => "Failed to save configuration",
},
});
await updateConfigPromise
.then(() => {
setIsSubmitting(false);
})
.catch((err) => {
console.error(err);
setIsSubmitting(false);
});
};
const isGiteaEnabled = enableGiteaConfig === "1";
return (
<PageWrapper
customHeader={
<AuthenticationMethodCard
name="Gitea"
description="Allow members to login or sign up to plane with their Gitea accounts."
icon={<img src={giteaLogo} height={24} width={24} alt="Gitea Logo" />}
config={
<ToggleSwitch
value={isGiteaEnabled}
onChange={() => {
updateConfig("IS_GITEA_ENABLED", isGiteaEnabled ? "0" : "1");
}}
size="sm"
disabled={isSubmitting || !formattedConfig}
/>
}
disabled={isSubmitting || !formattedConfig}
withBorder={false}
/>
}
>
{formattedConfig ? (
<InstanceGiteaConfigForm config={formattedConfig} />
) : (
<Loader className="space-y-8">
<Loader.Item height="50px" width="25%" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" width="50%" />
</Loader>
)}
</PageWrapper>
);
});
export const meta: Route.MetaFunction = () => [{ title: "Gitea Authentication - God Mode" }];
export default InstanceGiteaAuthenticationPage;
@@ -1,19 +1,28 @@
"use client";
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { FC, useState } from "react";
import isEmpty from "lodash/isEmpty";
import { useState } from "react";
import { isEmpty } from "lodash-es";
import Link from "next/link";
import { useForm } from "react-hook-form";
import { Monitor } from "lucide-react";
// plane internal packages
import { API_BASE_URL } from "@plane/constants";
import { IFormattedInstanceConfiguration, TInstanceGithubAuthenticationConfigurationKeys } from "@plane/types";
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
import { cn } from "@plane/utils";
import { Button, getButtonStyling } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IFormattedInstanceConfiguration, TInstanceGithubAuthenticationConfigurationKeys } from "@plane/types";
// components
import { CodeBlock } from "@/components/common/code-block";
import { ConfirmDiscardModal } from "@/components/common/confirm-discard-modal";
import { ControllerInput, TControllerInputFormField } from "@/components/common/controller-input";
import { CopyField, TCopyField } from "@/components/common/copy-field";
import type { TControllerInputFormField } from "@/components/common/controller-input";
import type { TControllerSwitchFormField } from "@/components/common/controller-switch";
import { ControllerSwitch } from "@/components/common/controller-switch";
import { ControllerInput } from "@/components/common/controller-input";
import type { TCopyField } from "@/components/common/copy-field";
import { CopyField } from "@/components/common/copy-field";
// hooks
import { useInstance } from "@/hooks/store";
@@ -23,7 +32,7 @@ type Props = {
type GithubConfigFormValues = Record<TInstanceGithubAuthenticationConfigurationKeys, string>;
export const InstanceGithubConfigForm: FC<Props> = (props) => {
export function InstanceGithubConfigForm(props: Props) {
const { config } = props;
// states
const [isDiscardChangesModalOpen, setIsDiscardChangesModalOpen] = useState(false);
@@ -40,6 +49,7 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
GITHUB_CLIENT_ID: config["GITHUB_CLIENT_ID"],
GITHUB_CLIENT_SECRET: config["GITHUB_CLIENT_SECRET"],
GITHUB_ORGANIZATION_ID: config["GITHUB_ORGANIZATION_ID"],
ENABLE_GITHUB_SYNC: config["ENABLE_GITHUB_SYNC"] || "0",
},
});
@@ -57,7 +67,7 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
tabIndex={-1}
href="https://github.com/settings/applications/new"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
GitHub OAuth application settings.
@@ -79,7 +89,7 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
tabIndex={-1}
href="https://github.com/settings/applications/new"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
GitHub OAuth application settings.
@@ -101,7 +111,12 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
},
];
const GITHUB_SERVICE_FIELD: TCopyField[] = [
const GITHUB_FORM_SWITCH_FIELD: TControllerSwitchFormField<GithubConfigFormValues> = {
name: "ENABLE_GITHUB_SYNC",
label: "GitHub",
};
const GITHUB_COMMON_SERVICE_DETAILS: TCopyField[] = [
{
key: "Origin_URL",
label: "Origin URL",
@@ -113,7 +128,7 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
tabIndex={-1}
href="https://github.com/settings/applications/new"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
here.
@@ -121,6 +136,9 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
</>
),
},
];
const GITHUB_SERVICE_DETAILS: TCopyField[] = [
{
key: "Callback_URI",
label: "Callback URI",
@@ -133,7 +151,7 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
tabIndex={-1}
href="https://github.com/settings/applications/new"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
here.
@@ -146,20 +164,22 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
const onSubmit = async (formData: GithubConfigFormValues) => {
const payload: Partial<GithubConfigFormValues> = { ...formData };
await updateInstanceConfigurations(payload)
.then((response = []) => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Done!",
message: "Your GitHub authentication is configured. You should test it now.",
});
reset({
GITHUB_CLIENT_ID: response.find((item) => item.key === "GITHUB_CLIENT_ID")?.value,
GITHUB_CLIENT_SECRET: response.find((item) => item.key === "GITHUB_CLIENT_SECRET")?.value,
GITHUB_ORGANIZATION_ID: response.find((item) => item.key === "GITHUB_ORGANIZATION_ID")?.value,
});
})
.catch((err) => console.error(err));
try {
const response = await updateInstanceConfigurations(payload);
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Done!",
message: "Your GitHub authentication is configured. You should test it now.",
});
reset({
GITHUB_CLIENT_ID: response.find((item) => item.key === "GITHUB_CLIENT_ID")?.value,
GITHUB_CLIENT_SECRET: response.find((item) => item.key === "GITHUB_CLIENT_SECRET")?.value,
GITHUB_ORGANIZATION_ID: response.find((item) => item.key === "GITHUB_ORGANIZATION_ID")?.value,
ENABLE_GITHUB_SYNC: response.find((item) => item.key === "ENABLE_GITHUB_SYNC")?.value,
});
} catch (err) {
console.error(err);
}
};
const handleGoBack = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
@@ -179,7 +199,7 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
<div className="flex flex-col gap-8">
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1 pt-1">
<div className="pt-2.5 text-xl font-medium">GitHub-provided details for Plane</div>
<div className="pt-2.5 text-18 font-medium">GitHub-provided details for Plane</div>
{GITHUB_FORM_FIELDS.map((field) => (
<ControllerInput
key={field.key}
@@ -193,31 +213,51 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
required={field.required}
/>
))}
<ControllerSwitch control={control} field={GITHUB_FORM_SWITCH_FIELD} />
<div className="flex flex-col gap-1 pt-4">
<div className="flex items-center gap-4">
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting} disabled={!isDirty}>
{isSubmitting ? "Saving..." : "Save changes"}
</Button>
<Link
href="/authentication"
className={cn(getButtonStyling("neutral-primary", "md"), "font-medium")}
onClick={handleGoBack}
<Button
variant="primary"
size="lg"
onClick={(e) => void handleSubmit(onSubmit)(e)}
loading={isSubmitting}
disabled={!isDirty}
>
{isSubmitting ? "Saving" : "Save changes"}
</Button>
<Link href="/authentication" className={getButtonStyling("secondary", "lg")} onClick={handleGoBack}>
Go back
</Link>
</div>
</div>
</div>
<div className="col-span-2 md:col-span-1">
<div className="flex flex-col gap-y-4 px-6 pt-1.5 pb-4 bg-custom-background-80/60 rounded-lg">
<div className="pt-2 text-xl font-medium">Plane-provided details for GitHub</div>
{GITHUB_SERVICE_FIELD.map((field) => (
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
))}
<div className="col-span-2 md:col-span-1 flex flex-col gap-y-6">
<div className="pt-2 text-18 font-medium">Plane-provided details for GitHub</div>
<div className="flex flex-col gap-y-4">
{/* common service details */}
<div className="flex flex-col gap-y-4 px-6 py-4 bg-layer-1 rounded-lg">
{GITHUB_COMMON_SERVICE_DETAILS.map((field) => (
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
))}
</div>
{/* web service details */}
<div className="flex flex-col rounded-lg overflow-hidden">
<div className="px-6 py-3 bg-layer-3 font-medium text-11 uppercase flex items-center gap-x-3 text-secondary">
<Monitor className="w-3 h-3" />
Web
</div>
<div className="px-6 py-4 flex flex-col gap-y-4 bg-layer-1">
{GITHUB_SERVICE_DETAILS.map((field) => (
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
))}
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
};
}
@@ -1,10 +0,0 @@
import { ReactNode } from "react";
import { Metadata } from "next";
export const metadata: Metadata = {
title: "GitHub Authentication - God Mode",
};
export default function GitHubAuthenticationLayout({ children }: { children: ReactNode }) {
return <>{children}</>;
}
@@ -1,24 +1,33 @@
"use client";
/**
* 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 Image from "next/image";
import { useTheme } from "next-themes";
import useSWR from "swr";
// plane internal packages
import { Loader, ToggleSwitch, setPromiseToast } from "@plane/ui";
import { setPromiseToast } from "@plane/propel/toast";
import { Loader, ToggleSwitch } from "@plane/ui";
import { resolveGeneralTheme } from "@plane/utils";
// assets
import githubLightModeImage from "@/app/assets/logos/github-black.png?url";
import githubDarkModeImage from "@/app/assets/logos/github-white.png?url";
// components
import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card";
import { PageWrapper } from "@/components/common/page-wrapper";
// hooks
import { useInstance } from "@/hooks/store";
// icons
import githubLightModeImage from "@/public/logos/github-black.png";
import githubDarkModeImage from "@/public/logos/github-white.png";
// local components
// types
import type { Route } from "./+types/page";
// local
import { InstanceGithubConfigForm } from "./form";
const InstanceGithubAuthenticationPage = observer(() => {
const InstanceGithubAuthenticationPage = observer(function InstanceGithubAuthenticationPage(
_props: Route.ComponentProps
) {
// store
const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance();
// state
@@ -40,10 +49,10 @@ const InstanceGithubAuthenticationPage = observer(() => {
const updateConfigPromise = updateInstanceConfigurations(payload);
setPromiseToast(updateConfigPromise, {
loading: "Saving Configuration...",
loading: "Saving Configuration",
success: {
title: "Configuration saved",
message: () => `GitHub authentication is now ${value ? "active" : "disabled"}.`,
message: () => `GitHub authentication is now ${value === "1" ? "active" : "disabled"}.`,
},
error: {
title: "Error",
@@ -64,50 +73,49 @@ const InstanceGithubAuthenticationPage = observer(() => {
const isGithubEnabled = enableGithubConfig === "1";
return (
<>
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<AuthenticationMethodCard
name="GitHub"
description="Allow members to login or sign up to plane with their GitHub accounts."
icon={
<Image
src={resolveGeneralTheme(resolvedTheme) === "dark" ? githubDarkModeImage : githubLightModeImage}
height={24}
width={24}
alt="GitHub Logo"
/>
}
config={
<ToggleSwitch
value={isGithubEnabled}
onChange={() => {
updateConfig("IS_GITHUB_ENABLED", isGithubEnabled ? "0" : "1");
}}
size="sm"
disabled={isSubmitting || !formattedConfig}
/>
}
disabled={isSubmitting || !formattedConfig}
withBorder={false}
/>
</div>
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? (
<InstanceGithubConfigForm config={formattedConfig} />
) : (
<Loader className="space-y-8">
<Loader.Item height="50px" width="25%" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" width="50%" />
</Loader>
)}
</div>
</div>
</>
<PageWrapper
customHeader={
<AuthenticationMethodCard
name="GitHub"
description="Allow members to login or sign up to plane with their GitHub accounts."
icon={
<img
src={resolveGeneralTheme(resolvedTheme) === "dark" ? githubDarkModeImage : githubLightModeImage}
height={24}
width={24}
alt="GitHub Logo"
/>
}
config={
<ToggleSwitch
value={isGithubEnabled}
onChange={() => {
updateConfig("IS_GITHUB_ENABLED", isGithubEnabled ? "0" : "1");
}}
size="sm"
disabled={isSubmitting || !formattedConfig}
/>
}
disabled={isSubmitting || !formattedConfig}
withBorder={false}
/>
}
>
{formattedConfig ? (
<InstanceGithubConfigForm config={formattedConfig} />
) : (
<Loader className="space-y-8">
<Loader.Item height="50px" width="25%" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" width="50%" />
</Loader>
)}
</PageWrapper>
);
});
export const meta: Route.MetaFunction = () => [{ title: "GitHub Authentication - God Mode" }];
export default InstanceGithubAuthenticationPage;
@@ -1,17 +1,27 @@
import { FC, useState } from "react";
import isEmpty from "lodash/isEmpty";
/**
* 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";
import { useForm } from "react-hook-form";
// plane internal packages
import { API_BASE_URL } from "@plane/constants";
import { IFormattedInstanceConfiguration, TInstanceGitlabAuthenticationConfigurationKeys } from "@plane/types";
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
import { cn } from "@plane/utils";
import { Button, getButtonStyling } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IFormattedInstanceConfiguration, TInstanceGitlabAuthenticationConfigurationKeys } from "@plane/types";
// components
import { CodeBlock } from "@/components/common/code-block";
import { ConfirmDiscardModal } from "@/components/common/confirm-discard-modal";
import { ControllerInput, TControllerInputFormField } from "@/components/common/controller-input";
import { CopyField, TCopyField } from "@/components/common/copy-field";
import type { TControllerInputFormField } from "@/components/common/controller-input";
import type { TControllerSwitchFormField } from "@/components/common/controller-switch";
import { ControllerSwitch } from "@/components/common/controller-switch";
import { ControllerInput } from "@/components/common/controller-input";
import type { TCopyField } from "@/components/common/copy-field";
import { CopyField } from "@/components/common/copy-field";
// hooks
import { useInstance } from "@/hooks/store";
@@ -21,7 +31,7 @@ type Props = {
type GitlabConfigFormValues = Record<TInstanceGitlabAuthenticationConfigurationKeys, string>;
export const InstanceGitlabConfigForm: FC<Props> = (props) => {
export function InstanceGitlabConfigForm(props: Props) {
const { config } = props;
// states
const [isDiscardChangesModalOpen, setIsDiscardChangesModalOpen] = useState(false);
@@ -38,6 +48,7 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
GITLAB_HOST: config["GITLAB_HOST"],
GITLAB_CLIENT_ID: config["GITLAB_CLIENT_ID"],
GITLAB_CLIENT_SECRET: config["GITLAB_CLIENT_SECRET"],
ENABLE_GITLAB_SYNC: config["ENABLE_GITLAB_SYNC"] || "0",
},
});
@@ -68,7 +79,7 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
tabIndex={-1}
href="https://docs.gitlab.com/ee/integration/oauth_provider.html"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
GitLab OAuth application settings
@@ -91,7 +102,7 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
tabIndex={-1}
href="https://docs.gitlab.com/ee/integration/oauth_provider.html"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
GitLab OAuth application settings
@@ -105,6 +116,11 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
},
];
const GITLAB_FORM_SWITCH_FIELD: TControllerSwitchFormField<GitlabConfigFormValues> = {
name: "ENABLE_GITLAB_SYNC",
label: "GitLab",
};
const GITLAB_SERVICE_FIELD: TCopyField[] = [
{
key: "Callback_URL",
@@ -117,7 +133,7 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
tabIndex={-1}
href="https://docs.gitlab.com/ee/integration/oauth_provider.html"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
GitLab OAuth application
@@ -131,20 +147,22 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
const onSubmit = async (formData: GitlabConfigFormValues) => {
const payload: Partial<GitlabConfigFormValues> = { ...formData };
await updateInstanceConfigurations(payload)
.then((response = []) => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Done!",
message: "Your GitLab authentication is configured. You should test it now.",
});
reset({
GITLAB_HOST: response.find((item) => item.key === "GITLAB_HOST")?.value,
GITLAB_CLIENT_ID: response.find((item) => item.key === "GITLAB_CLIENT_ID")?.value,
GITLAB_CLIENT_SECRET: response.find((item) => item.key === "GITLAB_CLIENT_SECRET")?.value,
});
})
.catch((err) => console.error(err));
try {
const response = await updateInstanceConfigurations(payload);
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Done!",
message: "Your GitLab authentication is configured. You should test it now.",
});
reset({
GITLAB_HOST: response.find((item) => item.key === "GITLAB_HOST")?.value,
GITLAB_CLIENT_ID: response.find((item) => item.key === "GITLAB_CLIENT_ID")?.value,
GITLAB_CLIENT_SECRET: response.find((item) => item.key === "GITLAB_CLIENT_SECRET")?.value,
ENABLE_GITLAB_SYNC: response.find((item) => item.key === "ENABLE_GITLAB_SYNC")?.value,
});
} catch (err) {
console.error(err);
}
};
const handleGoBack = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
@@ -164,7 +182,7 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
<div className="flex flex-col gap-8">
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1 pt-1">
<div className="pt-2.5 text-xl font-medium">GitLab-provided details for Plane</div>
<div className="pt-2.5 text-18 font-medium">GitLab-provided details for Plane</div>
{GITLAB_FORM_FIELDS.map((field) => (
<ControllerInput
key={field.key}
@@ -178,24 +196,27 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
required={field.required}
/>
))}
<ControllerSwitch control={control} field={GITLAB_FORM_SWITCH_FIELD} />
<div className="flex flex-col gap-1 pt-4">
<div className="flex items-center gap-4">
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting} disabled={!isDirty}>
{isSubmitting ? "Saving..." : "Save changes"}
</Button>
<Link
href="/authentication"
className={cn(getButtonStyling("neutral-primary", "md"), "font-medium")}
onClick={handleGoBack}
<Button
variant="primary"
size="lg"
onClick={(e) => void handleSubmit(onSubmit)(e)}
loading={isSubmitting}
disabled={!isDirty}
>
{isSubmitting ? "Saving" : "Save changes"}
</Button>
<Link href="/authentication" className={getButtonStyling("secondary", "lg")} onClick={handleGoBack}>
Go back
</Link>
</div>
</div>
</div>
<div className="col-span-2 md:col-span-1">
<div className="flex flex-col gap-y-4 px-6 pt-1.5 pb-4 bg-custom-background-80/60 rounded-lg">
<div className="pt-2 text-xl font-medium">Plane-provided details for GitLab</div>
<div className="flex flex-col gap-y-4 px-6 pt-1.5 pb-4 bg-layer-3 rounded-lg">
<div className="pt-2 text-18 font-medium">Plane-provided details for GitLab</div>
{GITLAB_SERVICE_FIELD.map((field) => (
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
))}
@@ -205,4 +226,4 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
</div>
</>
);
};
}
@@ -1,10 +0,0 @@
import { ReactNode } from "react";
import { Metadata } from "next";
export const metadata: Metadata = {
title: "GitLab Authentication - God Mode",
};
export default function GitlabAuthenticationLayout({ children }: { children: ReactNode }) {
return <>{children}</>;
}
@@ -1,20 +1,29 @@
"use client";
/**
* 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 Image from "next/image";
import useSWR from "swr";
import { Loader, ToggleSwitch, setPromiseToast } from "@plane/ui";
import { setPromiseToast } from "@plane/propel/toast";
import { Loader, ToggleSwitch } from "@plane/ui";
// assets
import GitlabLogo from "@/app/assets/logos/gitlab-logo.svg?url";
// components
import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card";
import { PageWrapper } from "@/components/common/page-wrapper";
// hooks
import { useInstance } from "@/hooks/store";
// icons
import GitlabLogo from "@/public/logos/gitlab-logo.svg";
// local components
// types
import type { Route } from "./+types/page";
// local
import { InstanceGitlabConfigForm } from "./form";
const InstanceGitlabAuthenticationPage = observer(() => {
const InstanceGitlabAuthenticationPage = observer(function InstanceGitlabAuthenticationPage(
_props: Route.ComponentProps
) {
// store
const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance();
// state
@@ -34,10 +43,10 @@ const InstanceGitlabAuthenticationPage = observer(() => {
const updateConfigPromise = updateInstanceConfigurations(payload);
setPromiseToast(updateConfigPromise, {
loading: "Saving Configuration...",
loading: "Saving Configuration",
success: {
title: "Configuration saved",
message: () => `GitLab authentication is now ${value ? "active" : "disabled"}.`,
message: () => `GitLab authentication is now ${value === "1" ? "active" : "disabled"}.`,
},
error: {
title: "Error",
@@ -55,47 +64,46 @@ const InstanceGitlabAuthenticationPage = observer(() => {
});
};
return (
<>
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<AuthenticationMethodCard
name="GitLab"
description="Allow members to login or sign up to plane with their GitLab accounts."
icon={<Image src={GitlabLogo} height={24} width={24} alt="GitLab Logo" />}
config={
<ToggleSwitch
value={Boolean(parseInt(enableGitlabConfig))}
onChange={() => {
if (Boolean(parseInt(enableGitlabConfig)) === true) {
updateConfig("IS_GITLAB_ENABLED", "0");
} else {
updateConfig("IS_GITLAB_ENABLED", "1");
}
}}
size="sm"
disabled={isSubmitting || !formattedConfig}
/>
}
disabled={isSubmitting || !formattedConfig}
withBorder={false}
/>
</div>
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? (
<InstanceGitlabConfigForm config={formattedConfig} />
) : (
<Loader className="space-y-8">
<Loader.Item height="50px" width="25%" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" width="50%" />
</Loader>
)}
</div>
</div>
</>
<PageWrapper
customHeader={
<AuthenticationMethodCard
name="GitLab"
description="Allow members to login or sign up to plane with their GitLab accounts."
icon={<img src={GitlabLogo} height={24} width={24} alt="GitLab Logo" />}
config={
<ToggleSwitch
value={Boolean(parseInt(enableGitlabConfig))}
onChange={() => {
if (Boolean(parseInt(enableGitlabConfig)) === true) {
updateConfig("IS_GITLAB_ENABLED", "0");
} else {
updateConfig("IS_GITLAB_ENABLED", "1");
}
}}
size="sm"
disabled={isSubmitting || !formattedConfig}
/>
}
disabled={isSubmitting || !formattedConfig}
withBorder={false}
/>
}
>
{formattedConfig ? (
<InstanceGitlabConfigForm config={formattedConfig} />
) : (
<Loader className="space-y-8">
<Loader.Item height="50px" width="25%" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" width="50%" />
</Loader>
)}
</PageWrapper>
);
});
export const meta: Route.MetaFunction = () => [{ title: "GitLab Authentication - God Mode" }];
export default InstanceGitlabAuthenticationPage;
@@ -1,18 +1,28 @@
"use client";
import { FC, useState } from "react";
import isEmpty from "lodash/isEmpty";
/**
* 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";
import { useForm } from "react-hook-form";
import { Monitor } from "lucide-react";
// plane internal packages
import { API_BASE_URL } from "@plane/constants";
import { IFormattedInstanceConfiguration, TInstanceGoogleAuthenticationConfigurationKeys } from "@plane/types";
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
import { cn } from "@plane/utils";
import { Button, getButtonStyling } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IFormattedInstanceConfiguration, TInstanceGoogleAuthenticationConfigurationKeys } from "@plane/types";
// components
import { CodeBlock } from "@/components/common/code-block";
import { ConfirmDiscardModal } from "@/components/common/confirm-discard-modal";
import { ControllerInput, TControllerInputFormField } from "@/components/common/controller-input";
import { CopyField, TCopyField } from "@/components/common/copy-field";
import type { TControllerInputFormField } from "@/components/common/controller-input";
import type { TControllerSwitchFormField } from "@/components/common/controller-switch";
import { ControllerSwitch } from "@/components/common/controller-switch";
import { ControllerInput } from "@/components/common/controller-input";
import type { TCopyField } from "@/components/common/copy-field";
import { CopyField } from "@/components/common/copy-field";
// hooks
import { useInstance } from "@/hooks/store";
@@ -22,7 +32,7 @@ type Props = {
type GoogleConfigFormValues = Record<TInstanceGoogleAuthenticationConfigurationKeys, string>;
export const InstanceGoogleConfigForm: FC<Props> = (props) => {
export function InstanceGoogleConfigForm(props: Props) {
const { config } = props;
// states
const [isDiscardChangesModalOpen, setIsDiscardChangesModalOpen] = useState(false);
@@ -38,6 +48,7 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
defaultValues: {
GOOGLE_CLIENT_ID: config["GOOGLE_CLIENT_ID"],
GOOGLE_CLIENT_SECRET: config["GOOGLE_CLIENT_SECRET"],
ENABLE_GOOGLE_SYNC: config["ENABLE_GOOGLE_SYNC"] || "0",
},
});
@@ -55,7 +66,7 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
tabIndex={-1}
href="https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow#creatingcred"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
Learn more
@@ -77,7 +88,7 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
tabIndex={-1}
href="https://developers.google.com/identity/oauth2/web/guides/get-google-api-clientid"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
Learn more
@@ -90,7 +101,12 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
},
];
const GOOGLE_SERVICE_DETAILS: TCopyField[] = [
const GOOGLE_FORM_SWITCH_FIELD: TControllerSwitchFormField<GoogleConfigFormValues> = {
name: "ENABLE_GOOGLE_SYNC",
label: "Google",
};
const GOOGLE_COMMON_SERVICE_DETAILS: TCopyField[] = [
{
key: "Origin_URL",
label: "Origin URL",
@@ -102,7 +118,7 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
<a
href="https://console.cloud.google.com/apis/credentials/oauthclient"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
here.
@@ -110,6 +126,9 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
</p>
),
},
];
const GOOGLE_SERVICE_DETAILS: TCopyField[] = [
{
key: "Callback_URI",
label: "Callback URI",
@@ -121,7 +140,7 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
<a
href="https://console.cloud.google.com/apis/credentials/oauthclient"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
here.
@@ -134,19 +153,21 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
const onSubmit = async (formData: GoogleConfigFormValues) => {
const payload: Partial<GoogleConfigFormValues> = { ...formData };
await updateInstanceConfigurations(payload)
.then((response = []) => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Done!",
message: "Your Google authentication is configured. You should test it now.",
});
reset({
GOOGLE_CLIENT_ID: response.find((item) => item.key === "GOOGLE_CLIENT_ID")?.value,
GOOGLE_CLIENT_SECRET: response.find((item) => item.key === "GOOGLE_CLIENT_SECRET")?.value,
});
})
.catch((err) => console.error(err));
try {
const response = await updateInstanceConfigurations(payload);
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Done!",
message: "Your Google authentication is configured. You should test it now.",
});
reset({
GOOGLE_CLIENT_ID: response.find((item) => item.key === "GOOGLE_CLIENT_ID")?.value,
GOOGLE_CLIENT_SECRET: response.find((item) => item.key === "GOOGLE_CLIENT_SECRET")?.value,
ENABLE_GOOGLE_SYNC: response.find((item) => item.key === "ENABLE_GOOGLE_SYNC")?.value,
});
} catch (err) {
console.error(err);
}
};
const handleGoBack = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
@@ -166,7 +187,7 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
<div className="flex flex-col gap-8">
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1 pt-1">
<div className="pt-2.5 text-xl font-medium">Google-provided details for Plane</div>
<div className="pt-2.5 text-18 font-medium">Google-provided details for Plane</div>
{GOOGLE_FORM_FIELDS.map((field) => (
<ControllerInput
key={field.key}
@@ -180,31 +201,51 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
required={field.required}
/>
))}
<ControllerSwitch control={control} field={GOOGLE_FORM_SWITCH_FIELD} />
<div className="flex flex-col gap-1 pt-4">
<div className="flex items-center gap-4">
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting} disabled={!isDirty}>
{isSubmitting ? "Saving..." : "Save changes"}
</Button>
<Link
href="/authentication"
className={cn(getButtonStyling("neutral-primary", "md"), "font-medium")}
onClick={handleGoBack}
<Button
variant="primary"
size="lg"
onClick={(e) => void handleSubmit(onSubmit)(e)}
loading={isSubmitting}
disabled={!isDirty}
>
{isSubmitting ? "Saving" : "Save changes"}
</Button>
<Link href="/authentication" className={getButtonStyling("secondary", "lg")} onClick={handleGoBack}>
Go back
</Link>
</div>
</div>
</div>
<div className="col-span-2 md:col-span-1">
<div className="flex flex-col gap-y-4 px-6 pt-1.5 pb-4 bg-custom-background-80/60 rounded-lg">
<div className="pt-2 text-xl font-medium">Plane-provided details for Google</div>
{GOOGLE_SERVICE_DETAILS.map((field) => (
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
))}
<div className="col-span-2 md:col-span-1 flex flex-col gap-y-6">
<div className="pt-2 text-18 font-medium">Plane-provided details for Google</div>
<div className="flex flex-col gap-y-4">
{/* common service details */}
<div className="flex flex-col gap-y-4 px-6 py-4 bg-layer-1 rounded-lg">
{GOOGLE_COMMON_SERVICE_DETAILS.map((field) => (
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
))}
</div>
{/* web service details */}
<div className="flex flex-col rounded-lg overflow-hidden">
<div className="px-6 py-3 bg-layer-3 font-medium text-11 uppercase flex items-center gap-x-3 text-secondary">
<Monitor className="w-3 h-3" />
Web
</div>
<div className="px-6 py-4 flex flex-col gap-y-4 bg-layer-1">
{GOOGLE_SERVICE_DETAILS.map((field) => (
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
))}
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
};
}
@@ -1,10 +0,0 @@
import { ReactNode } from "react";
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Google Authentication - God Mode",
};
export default function GoogleAuthenticationLayout({ children }: { children: ReactNode }) {
return <>{children}</>;
}
@@ -1,20 +1,29 @@
"use client";
/**
* 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 Image from "next/image";
import useSWR from "swr";
import { Loader, ToggleSwitch, setPromiseToast } from "@plane/ui";
import { setPromiseToast } from "@plane/propel/toast";
import { Loader, ToggleSwitch } from "@plane/ui";
// assets
import GoogleLogo from "@/app/assets/logos/google-logo.svg?url";
// components
import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card";
import { PageWrapper } from "@/components/common/page-wrapper";
// hooks
import { useInstance } from "@/hooks/store";
// icons
import GoogleLogo from "@/public/logos/google-logo.svg";
// local components
// types
import type { Route } from "./+types/page";
// local
import { InstanceGoogleConfigForm } from "./form";
const InstanceGoogleAuthenticationPage = observer(() => {
const InstanceGoogleAuthenticationPage = observer(function InstanceGoogleAuthenticationPage(
_props: Route.ComponentProps
) {
// store
const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance();
// state
@@ -34,10 +43,10 @@ const InstanceGoogleAuthenticationPage = observer(() => {
const updateConfigPromise = updateInstanceConfigurations(payload);
setPromiseToast(updateConfigPromise, {
loading: "Saving Configuration...",
loading: "Saving Configuration",
success: {
title: "Configuration saved",
message: () => `Google authentication is now ${value ? "active" : "disabled"}.`,
message: () => `Google authentication is now ${value === "1" ? "active" : "disabled"}.`,
},
error: {
title: "Error",
@@ -55,48 +64,47 @@ const InstanceGoogleAuthenticationPage = observer(() => {
});
};
return (
<>
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<AuthenticationMethodCard
name="Google"
description="Allow members to login or sign up to plane with their Google
<PageWrapper
customHeader={
<AuthenticationMethodCard
name="Google"
description="Allow members to login or sign up to plane with their Google
accounts."
icon={<Image src={GoogleLogo} height={24} width={24} alt="Google Logo" />}
config={
<ToggleSwitch
value={Boolean(parseInt(enableGoogleConfig))}
onChange={() => {
if (Boolean(parseInt(enableGoogleConfig)) === true) {
updateConfig("IS_GOOGLE_ENABLED", "0");
} else {
updateConfig("IS_GOOGLE_ENABLED", "1");
}
}}
size="sm"
disabled={isSubmitting || !formattedConfig}
/>
}
disabled={isSubmitting || !formattedConfig}
withBorder={false}
/>
</div>
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? (
<InstanceGoogleConfigForm config={formattedConfig} />
) : (
<Loader className="space-y-8">
<Loader.Item height="50px" width="25%" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" width="50%" />
</Loader>
)}
</div>
</div>
</>
icon={<img src={GoogleLogo} height={24} width={24} alt="Google Logo" />}
config={
<ToggleSwitch
value={Boolean(parseInt(enableGoogleConfig))}
onChange={() => {
if (Boolean(parseInt(enableGoogleConfig)) === true) {
updateConfig("IS_GOOGLE_ENABLED", "0");
} else {
updateConfig("IS_GOOGLE_ENABLED", "1");
}
}}
size="sm"
disabled={isSubmitting || !formattedConfig}
/>
}
disabled={isSubmitting || !formattedConfig}
withBorder={false}
/>
}
>
{formattedConfig ? (
<InstanceGoogleConfigForm config={formattedConfig} />
) : (
<Loader className="space-y-8">
<Loader.Item height="50px" width="25%" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" width="50%" />
</Loader>
)}
</PageWrapper>
);
});
export const meta: Route.MetaFunction = () => [{ title: "Google Authentication - God Mode" }];
export default InstanceGoogleAuthenticationPage;
@@ -1,10 +0,0 @@
import { ReactNode } from "react";
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Authentication Settings - Plane Web",
};
export default function AuthenticationLayout({ children }: { children: ReactNode }) {
return <>{children}</>;
}
@@ -1,113 +1,174 @@
"use client";
/**
* 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 { useCallback, useRef, useState } from "react";
import { observer } from "mobx-react";
import { useTheme } from "next-themes";
import useSWR from "swr";
// plane internal packages
import { TInstanceConfigurationKeys } from "@plane/types";
import { Loader, ToggleSwitch, setPromiseToast } from "@plane/ui";
import { cn } from "@plane/utils";
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";
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";
// plane admin components
import { AuthenticationModes } from "@/plane-admin/components/authentication";
const InstanceAuthenticationPage = observer(() => {
// store
const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
// types
import type { Route } from "./+types/page";
const InstanceAuthenticationPage = observer(function InstanceAuthenticationPage(_props: Route.ComponentProps) {
// theme
const { resolvedTheme: resolvedThemeAdmin } = useTheme();
const resolvedTheme = resolveGeneralTheme(resolvedThemeAdmin);
// Ref to store authentication modes for validation (avoids circular dependency)
const authenticationModesRef = useRef<TInstanceAuthenticationModes[]>([]);
// state
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
// store hooks
const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance();
// derived values
const enableSignUpConfig = formattedConfig?.ENABLE_SIGNUP ?? "";
const updateConfig = async (key: TInstanceConfigurationKeys, value: string) => {
setIsSubmitting(true);
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
const payload = {
[key]: value,
};
// 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 updateConfigPromise = updateInstanceConfigurations(payload);
// Only validate if this is an authentication method key
if (isAuthMethodKey) {
const canDisable = canDisableAuthMethod(key, currentAuthModes, formattedConfig);
setPromiseToast(updateConfigPromise, {
loading: "Saving configuration",
success: {
title: "Success",
message: () => "Configuration saved successfully",
},
error: {
title: "Error",
message: () => "Failed to save configuration",
},
});
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;
}
}
}
await updateConfigPromise
.then(() => {
setIsSubmitting(false);
})
.catch((err) => {
console.error(err);
setIsSubmitting(false);
// Proceed with the update
setIsSubmitting(true);
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",
},
});
};
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 (
<>
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">Manage authentication modes for your instance</div>
<div className="text-sm font-normal text-custom-text-300">
Configure authentication modes for your team and restrict sign-ups to be invite only.
</div>
</div>
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? (
<div className="space-y-3">
<div className={cn("w-full flex items-center gap-14 rounded")}>
<div className="flex grow items-center gap-4">
<div className="grow">
<div className="text-lg font-medium pb-1">Allow anyone to sign up even without an invite</div>
<div className={cn("font-normal leading-5 text-custom-text-300 text-xs")}>
Toggling this off will only let users sign up when they are invited.
</div>
</div>
</div>
<div className={`shrink-0 pr-4 ${isSubmitting && "opacity-70"}`}>
<div className="flex items-center gap-4">
<ToggleSwitch
value={Boolean(parseInt(enableSignUpConfig))}
onChange={() => {
if (Boolean(parseInt(enableSignUpConfig)) === true) {
updateConfig("ENABLE_SIGNUP", "0");
} else {
updateConfig("ENABLE_SIGNUP", "1");
}
}}
size="sm"
disabled={isSubmitting}
/>
</div>
<PageWrapper
header={{
title: "Manage authentication modes for your instance",
description: "Configure authentication modes for your team and restrict sign-ups to be invite only.",
}}
>
{formattedConfig ? (
<div className="space-y-3">
<div className={cn("w-full flex items-center gap-14 rounded-sm")}>
<div className="flex grow items-center gap-4">
<div className="grow">
<div className="text-16 font-medium pb-1">Allow anyone to sign up even without an invite</div>
<div className={cn("font-regular leading-5 text-tertiary text-11")}>
Toggling this off will only let users sign up when they are invited.
</div>
</div>
<div className="text-lg font-medium pt-6">Available authentication modes</div>
<AuthenticationModes disabled={isSubmitting} updateConfig={updateConfig} />
</div>
) : (
<Loader className="space-y-10">
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="20%" />
</Loader>
)}
<div className={`shrink-0 pr-4 ${isSubmitting && "opacity-70"}`}>
<div className="flex items-center gap-4">
<ToggleSwitch
value={Boolean(parseInt(enableSignUpConfig))}
onChange={() => {
if (Boolean(parseInt(enableSignUpConfig)) === true) {
updateConfig("ENABLE_SIGNUP", "0");
} else {
updateConfig("ENABLE_SIGNUP", "1");
}
}}
size="sm"
disabled={isSubmitting}
/>
</div>
</div>
</div>
<div className="text-lg font-medium pt-6">Available authentication modes</div>
{authenticationModes.map((method) => (
<AuthenticationMethodCard
key={method.key}
name={method.name}
description={method.description}
icon={method.icon}
config={method.config}
disabled={isSubmitting}
unavailable={method.unavailable}
/>
))}
</div>
</div>
</>
) : (
<Loader className="space-y-10">
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="20%" />
</Loader>
)}
</PageWrapper>
);
});
export const meta: Route.MetaFunction = () => [{ title: "Authentication Settings - Plane Web" }];
export default InstanceAuthenticationPage;
@@ -1,13 +1,20 @@
"use client";
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import React, { FC, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { useForm } from "react-hook-form";
// types
import { IFormattedInstanceConfiguration, TInstanceEmailConfigurationKeys } from "@plane/types";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IFormattedInstanceConfiguration, TInstanceEmailConfigurationKeys } from "@plane/types";
// ui
import { Button, CustomSelect, TOAST_TYPE, setToast } from "@plane/ui";
import { CustomSelect } from "@plane/ui";
// components
import { ControllerInput, TControllerInputFormField } from "@/components/common/controller-input";
import type { TControllerInputFormField } from "@/components/common/controller-input";
import { ControllerInput } from "@/components/common/controller-input";
// hooks
import { useInstance } from "@/hooks/store";
// local components
@@ -27,7 +34,7 @@ const EMAIL_SECURITY_OPTIONS: { [key in TEmailSecurityKeys]: string } = {
NONE: "No email security",
};
export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
export function InstanceEmailForm(props: IInstanceEmailForm) {
const { config } = props;
// states
const [isSendTestEmailModalOpen, setIsSendTestEmailModalOpen] = useState(false);
@@ -156,13 +163,12 @@ export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
/>
))}
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-300">Email security</h4>
<h4 className="text-13 text-tertiary">Email security</h4>
<CustomSelect
value={emailSecurityKey}
label={EMAIL_SECURITY_OPTIONS[emailSecurityKey]}
onChange={handleEmailSecurityChange}
buttonClassName="rounded-md border-custom-border-200"
optionsClassName="w-full"
buttonClassName="rounded-md border-subtle"
input
>
{Object.entries(EMAIL_SECURITY_OPTIONS).map(([key, value]) => (
@@ -173,12 +179,12 @@ export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
</CustomSelect>
</div>
</div>
<div className="flex flex-col gap-6 my-6 pt-4 border-t border-custom-border-100">
<div className="flex flex-col gap-6 my-6 pt-4 border-t border-subtle">
<div className="flex w-full max-w-xl flex-col gap-y-10 px-1">
<div className="mr-8 flex items-center gap-10 pt-4">
<div className="grow">
<div className="text-sm font-medium text-custom-text-100">Authentication</div>
<div className="text-xs font-normal text-custom-text-300">
<div className="text-13 font-medium text-primary">Authentication</div>
<div className="text-11 font-regular text-tertiary">
This is optional, but we recommend setting up a username and a password for your SMTP server.
</div>
</div>
@@ -204,14 +210,16 @@ export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
<div className="flex max-w-4xl items-center py-1 gap-4">
<Button
variant="primary"
size="lg"
onClick={handleSubmit(onSubmit)}
loading={isSubmitting}
disabled={!isValid || !isDirty}
>
{isSubmitting ? "Saving..." : "Save changes"}
{isSubmitting ? "Saving" : "Save changes"}
</Button>
<Button
variant="outline-primary"
variant="secondary"
size="lg"
onClick={() => setIsSendTestEmailModalOpen(true)}
loading={isSubmitting}
disabled={!isValid}
@@ -221,4 +229,4 @@ export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
</div>
</div>
);
};
}
@@ -1,14 +0,0 @@
import { ReactNode } from "react";
import { Metadata } from "next";
interface EmailLayoutProps {
children: ReactNode;
}
export const metadata: Metadata = {
title: "Email Settings - God Mode",
};
export default function EmailLayout({ children }: EmailLayoutProps) {
return <>{children}</>;
}
+49 -39
View File
@@ -1,15 +1,24 @@
"use client";
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { useEffect, useState } from "react";
import { observer } from "mobx-react";
import useSWR from "swr";
import { Loader, setToast, TOAST_TYPE, ToggleSwitch } from "@plane/ui";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Loader, ToggleSwitch } from "@plane/ui";
// components
import { PageWrapper } from "@/components/common/page-wrapper";
// hooks
import { useInstance } from "@/hooks/store";
// components
// types
import type { Route } from "./+types/page";
// local
import { InstanceEmailForm } from "./email-config-form";
const InstanceEmailPage: React.FC = observer(() => {
const InstanceEmailPage = observer(function InstanceEmailPage(_props: Route.ComponentProps) {
// store
const { fetchInstanceConfigurations, formattedConfig, disableEmail } = useInstance();
@@ -49,45 +58,46 @@ const InstanceEmailPage: React.FC = observer(() => {
}, [formattedConfig]);
return (
<>
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="flex items-center justify-between gap-4 border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">Secure emails from your own instance</div>
<div className="text-sm font-normal text-custom-text-300">
Plane can send useful emails to you and your users from your own instance without talking to the Internet.
<div className="text-sm font-normal text-custom-text-300">
Set it up below and please test your settings before you save them.&nbsp;
<span className="text-red-400">Misconfigs can lead to email bounces and errors.</span>
</div>
<PageWrapper
header={{
title: "Secure emails from your own instance",
description: (
<>
Plane can send useful emails to you and your users from your own instance without talking to the Internet.
<div className="text-13 font-regular text-tertiary">
Set it up below and please test your settings before you save them.&nbsp;
<span className="text-danger-primary">Misconfigs can lead to email bounces and errors.</span>
</div>
</div>
{isLoading ? (
<Loader>
<Loader.Item width="24px" height="16px" className="rounded-full" />
</Loader>
</>
),
actions: isLoading ? (
<Loader>
<Loader.Item width="24px" height="16px" className="rounded-full" />
</Loader>
) : (
<ToggleSwitch value={isSMTPEnabled} onChange={handleToggle} size="sm" disabled={isSubmitting} />
),
}}
>
{isSMTPEnabled && !isLoading && (
<>
{formattedConfig ? (
<InstanceEmailForm config={formattedConfig} />
) : (
<ToggleSwitch value={isSMTPEnabled} onChange={handleToggle} size="sm" disabled={isSubmitting} />
<Loader className="space-y-10">
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="20%" />
</Loader>
)}
</div>
{isSMTPEnabled && !isLoading && (
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? (
<InstanceEmailForm config={formattedConfig} />
) : (
<Loader className="space-y-10">
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="75%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="40%" />
<Loader.Item height="50px" width="20%" />
</Loader>
)}
</div>
)}
</div>
</>
</>
)}
</PageWrapper>
);
});
export const meta: Route.MetaFunction = () => [{ title: "Email Settings - God Mode" }];
export default InstanceEmailPage;
@@ -1,9 +1,16 @@
import React, { FC, useEffect, 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 { useEffect, useState, Fragment } from "react";
import { Dialog, Transition } from "@headlessui/react";
// plane imports
import { Button } from "@plane/propel/button";
import { InstanceService } from "@plane/services";
// ui
import { Button, Input } from "@plane/ui";
import { Input } from "@plane/ui";
type Props = {
isOpen: boolean;
@@ -18,7 +25,7 @@ enum ESendEmailSteps {
const instanceService = new InstanceService();
export const SendTestEmailModal: FC<Props> = (props) => {
export function SendTestEmailModal(props: Props) {
const { isOpen, handleClose } = props;
// state
@@ -60,10 +67,10 @@ export const SendTestEmailModal: FC<Props> = (props) => {
};
return (
<Transition.Root show={isOpen} as={React.Fragment}>
<Transition.Root show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-20" onClose={handleClose}>
<Transition.Child
as={React.Fragment}
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
@@ -71,12 +78,12 @@ export const SendTestEmailModal: FC<Props> = (props) => {
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-custom-backdrop transition-opacity" />
<div className="fixed inset-0 bg-backdrop transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-20 overflow-y-auto">
<div className="my-10 flex justify-center p-4 text-center sm:p-0 md:my-20">
<Transition.Child
as={React.Fragment}
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
@@ -84,8 +91,8 @@ export const SendTestEmailModal: FC<Props> = (props) => {
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform rounded-lg bg-custom-background-100 p-5 px-4 text-left shadow-custom-shadow-md transition-all w-full sm:max-w-xl">
<h3 className="text-lg font-medium leading-6 text-custom-text-100">
<Dialog.Panel className="relative transform rounded-lg bg-surface-1 p-5 px-4 text-left shadow-raised-200 transition-all w-full sm:max-w-xl">
<h3 className="text-16 font-medium leading-6 text-primary">
{sendEmailStep === ESendEmailSteps.SEND_EMAIL
? "Send test email"
: sendEmailStep === ESendEmailSteps.SUCCESS
@@ -100,12 +107,12 @@ export const SendTestEmailModal: FC<Props> = (props) => {
value={receiverEmail}
onChange={(e) => setReceiverEmail(e.target.value)}
placeholder="Receiver email"
className="w-full resize-none text-lg"
className="w-full resize-none text-16"
tabIndex={1}
/>
)}
{sendEmailStep === ESendEmailSteps.SUCCESS && (
<div className="flex flex-col gap-y-4 text-sm">
<div className="flex flex-col gap-y-4 text-13">
<p>
We have sent the test email to {receiverEmail}. Please check your spam folder if you cannot find
it.
@@ -113,14 +120,14 @@ export const SendTestEmailModal: FC<Props> = (props) => {
<p>If you still cannot find it, recheck your SMTP configuration and trigger a new test email.</p>
</div>
)}
{sendEmailStep === ESendEmailSteps.FAILED && <div className="text-sm">{error}</div>}
{sendEmailStep === ESendEmailSteps.FAILED && <div className="text-13">{error}</div>}
<div className="flex items-center gap-2 justify-end mt-5">
<Button variant="neutral-primary" size="sm" onClick={handleClose} tabIndex={2}>
<Button variant="secondary" size="lg" onClick={handleClose} tabIndex={2}>
{sendEmailStep === ESendEmailSteps.SEND_EMAIL ? "Cancel" : "Close"}
</Button>
{sendEmailStep === ESendEmailSteps.SEND_EMAIL && (
<Button variant="primary" size="sm" loading={isLoading} onClick={handleSubmit} tabIndex={3}>
{isLoading ? "Sending email..." : "Send email"}
<Button variant="primary" size="lg" loading={isLoading} onClick={handleSubmit} tabIndex={3}>
{isLoading ? "Sending email" : "Send email"}
</Button>
)}
</div>
@@ -132,4 +139,4 @@ export const SendTestEmailModal: FC<Props> = (props) => {
</Dialog>
</Transition.Root>
);
};
}
@@ -1,24 +1,30 @@
"use client";
import { FC } 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 { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
import { Telescope } from "lucide-react";
// types
import { IInstance, IInstanceAdmin } from "@plane/types";
// ui
import { Button, Input, TOAST_TYPE, ToggleSwitch, setToast } from "@plane/ui";
// plane imports
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IInstance, IInstanceAdmin } from "@plane/types";
import { Input, ToggleSwitch } from "@plane/ui";
// components
import { ControllerInput } from "@/components/common/controller-input";
import { useInstance } from "@/hooks/store";
import { IntercomConfig } from "./intercom";
// hooks
import { useInstance } from "@/hooks/store";
// components
import { IntercomConfig } from "./intercom";
export interface IGeneralConfigurationForm {
instance: IInstance;
instanceAdmins: IInstanceAdmin[];
}
export const GeneralConfigurationForm: FC<IGeneralConfigurationForm> = observer((props) => {
export const GeneralConfigurationForm = observer(function GeneralConfigurationForm(props: IGeneralConfigurationForm) {
const { instance, instanceAdmins } = props;
// hooks
const { instanceConfigurations, updateInstanceInfo, updateInstanceConfigurations } = useInstance();
@@ -27,8 +33,8 @@ export const GeneralConfigurationForm: FC<IGeneralConfigurationForm> = observer(
const {
handleSubmit,
control,
watch,
formState: { errors, isSubmitting },
watch,
} = useForm<Partial<IInstance>>({
defaultValues: {
instance_name: instance?.instance_name,
@@ -63,8 +69,8 @@ export const GeneralConfigurationForm: FC<IGeneralConfigurationForm> = observer(
return (
<div className="space-y-8">
<div className="space-y-3">
<div className="text-lg font-medium">Instance details</div>
<div className="space-y-4">
<div className="text-16 font-medium text-primary">Instance details</div>
<div className="grid-col grid w-full grid-cols-1 items-center justify-between gap-8 md:grid-cols-2 lg:grid-cols-3">
<ControllerInput
key="instance_name"
@@ -78,54 +84,52 @@ export const GeneralConfigurationForm: FC<IGeneralConfigurationForm> = observer(
/>
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-300">Email</h4>
<h4 className="text-13 text-tertiary">Email</h4>
<Input
id="email"
name="email"
type="email"
value={instanceAdmins[0]?.user_detail?.email ?? ""}
placeholder="Admin email"
className="w-full cursor-not-allowed !text-custom-text-400"
className="w-full cursor-not-allowed !text-placeholder"
autoComplete="on"
disabled
/>
</div>
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-300">Instance ID</h4>
<h4 className="text-13 text-tertiary">Instance ID</h4>
<Input
id="instance_id"
name="instance_id"
type="text"
value={instance.instance_id}
className="w-full cursor-not-allowed rounded-md font-medium !text-custom-text-400"
className="w-full cursor-not-allowed rounded-md font-medium !text-placeholder"
disabled
/>
</div>
</div>
</div>
<div className="space-y-3">
<div className="text-lg font-medium">Chat + telemetry</div>
<div className="space-y-6">
<div className="text-16 font-medium text-primary pb-1.5 border-b border-subtle">Chat + telemetry</div>
<IntercomConfig isTelemetryEnabled={watch("is_telemetry_enabled") ?? false} />
<div className="flex items-center gap-14 px-4 py-3 border border-custom-border-200 rounded">
<div className="flex items-center gap-14">
<div className="grow flex items-center gap-4">
<div className="shrink-0">
<div className="flex items-center justify-center w-10 h-10 bg-custom-background-80 rounded-full">
<Telescope className="w-6 h-6 text-custom-text-300/80 p-0.5" />
<div className="flex items-center justify-center size-11 bg-layer-1 rounded-lg">
<Telescope className="size-5 text-tertiary" />
</div>
</div>
<div className="grow">
<div className="text-sm font-medium text-custom-text-100 leading-5">
Let Plane collect anonymous usage data
</div>
<div className="text-xs font-normal text-custom-text-300 leading-5">
<div className="text-13 font-medium text-primary leading-5">Let Plane collect anonymous usage data</div>
<div className="text-11 font-regular text-tertiary leading-5">
No PII is collected.This anonymized data is used to understand how you use Plane and build new features
in line with{" "}
<a
href="https://developers.plane.so/self-hosting/telemetry"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
our Telemetry Policy.
@@ -146,8 +150,15 @@ export const GeneralConfigurationForm: FC<IGeneralConfigurationForm> = observer(
</div>
<div>
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
{isSubmitting ? "Saving..." : "Save changes"}
<Button
variant="primary"
size="lg"
onClick={() => {
void handleSubmit(onSubmit)();
}}
loading={isSubmitting}
>
{isSubmitting ? "Saving" : "Save changes"}
</Button>
</div>
</div>
@@ -1,10 +1,14 @@
"use client";
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { FC, useState } from "react";
import { useState } from "react";
import { observer } from "mobx-react";
import useSWR from "swr";
import { MessageSquare } from "lucide-react";
import { IFormattedInstanceConfiguration } from "@plane/types";
import type { IFormattedInstanceConfiguration } from "@plane/types";
import { ToggleSwitch } from "@plane/ui";
// hooks
import { useInstance } from "@/hooks/store";
@@ -13,7 +17,7 @@ type TIntercomConfig = {
isTelemetryEnabled: boolean;
};
export const IntercomConfig: FC<TIntercomConfig> = observer((props) => {
export const IntercomConfig = observer(function IntercomConfig(props: TIntercomConfig) {
const { isTelemetryEnabled } = props;
// hooks
const { instanceConfigurations, updateInstanceConfigurations, fetchInstanceConfigurations } = useInstance();
@@ -46,22 +50,22 @@ export const IntercomConfig: FC<TIntercomConfig> = observer((props) => {
};
const enableIntercomConfig = () => {
submitInstanceConfigurations({ IS_INTERCOM_ENABLED: isIntercomEnabled ? "0" : "1" });
void submitInstanceConfigurations({ IS_INTERCOM_ENABLED: isIntercomEnabled ? "0" : "1" });
};
return (
<>
<div className="flex items-center gap-14 px-4 py-3 border border-custom-border-200 rounded">
<div className="flex items-center gap-14">
<div className="grow flex items-center gap-4">
<div className="shrink-0">
<div className="flex items-center justify-center w-10 h-10 bg-custom-background-80 rounded-full">
<MessageSquare className="w-6 h-6 text-custom-text-300/80 p-0.5" />
<div className="flex items-center justify-center size-11 bg-layer-1 rounded-lg">
<MessageSquare className="size-5 text-tertiary p-0.5" />
</div>
</div>
<div className="grow">
<div className="text-sm font-medium text-custom-text-100 leading-5">Chat with us</div>
<div className="text-xs font-normal text-custom-text-300 leading-5">
<div className="text-13 font-medium text-primary leading-5">Chat with us</div>
<div className="text-11 font-regular text-tertiary leading-5">
Let your users chat with us via Intercom or another service. Toggling Telemetry off turns this off
automatically.
</div>
@@ -1,10 +0,0 @@
import { ReactNode } from "react";
import { Metadata } from "next";
export const metadata: Metadata = {
title: "General Settings - God Mode",
};
export default function GeneralLayout({ children }: { children: ReactNode }) {
return <>{children}</>;
}
@@ -1,31 +1,35 @@
"use client";
/**
* 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";
// hooks
import { useInstance } from "@/hooks/store";
// components
// local imports
import { GeneralConfigurationForm } from "./form";
// types
import type { Route } from "./+types/page";
function GeneralPage() {
const { instance, instanceAdmins } = useInstance();
return (
<>
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">General settings</div>
<div className="text-sm font-normal text-custom-text-300">
Change the name of your instance and instance admin e-mail addresses. Enable or disable telemetry in your
instance.
</div>
</div>
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{instance && instanceAdmins && (
<GeneralConfigurationForm instance={instance} instanceAdmins={instanceAdmins} />
)}
</div>
</div>
</>
<PageWrapper
header={{
title: "General settings",
description:
"Change the name of your instance and instance admin e-mail addresses. Enable or disable telemetry in your instance.",
}}
>
{instance && instanceAdmins && <GeneralConfigurationForm instance={instance} instanceAdmins={instanceAdmins} />}
</PageWrapper>
);
}
export const meta: Route.MetaFunction = () => [{ title: "General Settings - God Mode" }];
export default observer(GeneralPage);
@@ -1,8 +1,13 @@
"use client";
import { FC } 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 { useForm } from "react-hook-form";
import { IFormattedInstanceConfiguration, TInstanceImageConfigurationKeys } from "@plane/types";
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IFormattedInstanceConfiguration, TInstanceImageConfigurationKeys } from "@plane/types";
// components
import { ControllerInput } from "@/components/common/controller-input";
// hooks
@@ -14,7 +19,7 @@ type IInstanceImageConfigForm = {
type ImageConfigFormValues = Record<TInstanceImageConfigurationKeys, string>;
export const InstanceImageConfigForm: FC<IInstanceImageConfigForm> = (props) => {
export function InstanceImageConfigForm(props: IInstanceImageConfigForm) {
const { config } = props;
// store hooks
const { updateInstanceConfigurations } = useInstance();
@@ -57,7 +62,7 @@ export const InstanceImageConfigForm: FC<IInstanceImageConfigForm> = (props) =>
<a
href="https://unsplash.com/documentation#creating-a-developer-account"
target="_blank"
className="text-custom-primary-100 hover:underline"
className="text-accent-primary hover:underline"
rel="noreferrer"
>
Learn more.
@@ -71,10 +76,10 @@ export const InstanceImageConfigForm: FC<IInstanceImageConfigForm> = (props) =>
</div>
<div>
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
{isSubmitting ? "Saving..." : "Save changes"}
<Button variant="primary" size="lg" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
{isSubmitting ? "Saving" : "Save changes"}
</Button>
</div>
</div>
);
};
}
@@ -1,14 +0,0 @@
import { ReactNode } from "react";
import { Metadata } from "next";
interface ImageLayoutProps {
children: ReactNode;
}
export const metadata: Metadata = {
title: "Images Settings - God Mode",
};
export default function ImageLayout({ children }: ImageLayoutProps) {
return <>{children}</>;
}
+27 -22
View File
@@ -1,41 +1,46 @@
"use client";
/**
* 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";
// components
import { PageWrapper } from "@/components/common/page-wrapper";
// hooks
import { useInstance } from "@/hooks/store";
// types
import type { Route } from "./+types/page";
// local
import { InstanceImageConfigForm } from "./form";
const InstanceImagePage = observer(() => {
const InstanceImagePage = observer(function InstanceImagePage(_props: Route.ComponentProps) {
// store
const { formattedConfig, fetchInstanceConfigurations } = useInstance();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
return (
<>
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">Third-party image libraries</div>
<div className="text-sm font-normal text-custom-text-300">
Let your users search and choose images from third-party libraries
</div>
</div>
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? (
<InstanceImageConfigForm config={formattedConfig} />
) : (
<Loader className="space-y-8">
<Loader.Item height="50px" width="50%" />
<Loader.Item height="50px" width="20%" />
</Loader>
)}
</div>
</div>
</>
<PageWrapper
header={{
title: "Third-party image libraries",
description: "Let your users search and choose images from third-party libraries",
}}
>
{formattedConfig ? (
<InstanceImageConfigForm config={formattedConfig} />
) : (
<Loader className="space-y-8">
<Loader.Item height="50px" width="50%" />
<Loader.Item height="50px" width="20%" />
</Loader>
)}
</PageWrapper>
);
});
export const meta: Route.MetaFunction = () => [{ title: "Images Settings - God Mode" }];
export default InstanceImagePage;
+19 -18
View File
@@ -1,33 +1,32 @@
"use client";
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { FC, ReactNode, useEffect } from "react";
import { useEffect } from "react";
import { observer } from "mobx-react";
import { useRouter } from "next/navigation";
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
import { AdminHeader } from "./header";
import type { Route } from "./+types/layout";
import { AdminSidebar } from "./sidebar";
type TAdminLayout = {
children: ReactNode;
};
const AdminLayout: FC<TAdminLayout> = (props) => {
const { children } = props;
function AdminLayout(_props: Route.ComponentProps) {
// router
const router = useRouter();
const { replace } = useRouter();
// store hooks
const { isUserLoggedIn } = useUser();
useEffect(() => {
if (isUserLoggedIn === false) {
router.push("/");
}
}, [router, isUserLoggedIn]);
if (isUserLoggedIn === false) replace("/");
}, [replace, isUserLoggedIn]);
if (isUserLoggedIn === undefined) {
return (
@@ -41,9 +40,11 @@ const AdminLayout: FC<TAdminLayout> = (props) => {
return (
<div className="relative flex h-screen w-screen overflow-hidden">
<AdminSidebar />
<main className="relative flex h-full w-full flex-col overflow-hidden bg-custom-background-100">
<main className="relative flex h-full w-full flex-col overflow-hidden bg-surface-1">
<AdminHeader />
<div className="h-full w-full overflow-hidden">{children}</div>
<div className="h-full w-full overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md">
<Outlet />
</div>
</main>
<NewUserPopup />
</div>
@@ -51,6 +52,6 @@ const AdminLayout: FC<TAdminLayout> = (props) => {
}
return <></>;
};
}
export default observer(AdminLayout);
@@ -1,4 +1,8 @@
"use client";
/**
* 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";
@@ -16,7 +20,7 @@ import { useTheme, useUser } from "@/hooks/store";
// service initialization
const authService = new AuthService();
export const AdminSidebarDropdown = observer(() => {
export const AdminSidebarDropdown = observer(function AdminSidebarDropdown() {
// store hooks
const { isSidebarCollapsed } = useTheme();
const { currentUser, signOut } = useUser();
@@ -35,20 +39,20 @@ export const AdminSidebarDropdown = observer(() => {
const getSidebarMenuItems = () => (
<Menu.Items
className={cn(
"absolute left-0 z-20 mt-1.5 flex w-52 flex-col divide-y divide-custom-sidebar-border-100 rounded-md border border-custom-sidebar-border-200 bg-custom-sidebar-background-100 px-1 py-2 text-xs shadow-lg outline-none",
"absolute left-0 z-20 mt-1.5 flex w-52 flex-col divide-y divide-subtle rounded-md border border-subtle bg-surface-1 px-1 py-2 text-11 shadow-lg outline-none",
{
"left-4": isSidebarCollapsed,
}
)}
>
<div className="flex flex-col gap-2.5 pb-2">
<span className="px-2 text-custom-sidebar-text-200 truncate">{currentUser?.email}</span>
<span className="px-2 text-secondary truncate">{currentUser?.email}</span>
</div>
<div className="py-2">
<Menu.Item
as="button"
type="button"
className="flex w-full items-center gap-2 rounded px-2 py-1 hover:bg-custom-sidebar-background-80"
className="flex w-full items-center gap-2 rounded-sm px-2 py-1 hover:bg-layer-1-hover"
onClick={handleThemeSwitch}
>
<Palette className="h-4 w-4 stroke-[1.5]" />
@@ -61,7 +65,7 @@ export const AdminSidebarDropdown = observer(() => {
<Menu.Item
as="button"
type="submit"
className="flex w-full items-center gap-2 rounded px-2 py-1 hover:bg-custom-sidebar-background-80"
className="flex w-full items-center gap-2 rounded-sm px-2 py-1 hover:bg-layer-1-hover"
>
<LogOut className="h-4 w-4 stroke-[1.5]" />
Sign out
@@ -73,14 +77,14 @@ export const AdminSidebarDropdown = observer(() => {
useEffect(() => {
if (csrfToken === undefined)
authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
void authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
}, [csrfToken]);
return (
<div className="flex max-h-header items-center gap-x-5 gap-y-2 border-b border-custom-sidebar-border-200 px-4 py-3.5">
<div className="flex max-h-header items-center gap-x-5 gap-y-2 border-b border-subtle px-4 py-2.5">
<div className="h-full w-full truncate">
<div
className={`flex flex-grow items-center gap-x-2 truncate rounded py-1 ${
className={`flex flex-grow items-center gap-x-2 truncate rounded-sm ${
isSidebarCollapsed ? "justify-center" : ""
}`}
>
@@ -90,8 +94,8 @@ export const AdminSidebarDropdown = observer(() => {
"cursor-default": !isSidebarCollapsed,
})}
>
<div className="flex h-7 w-7 flex-shrink-0 items-center justify-center rounded bg-custom-sidebar-background-80">
<UserCog2 className="h-5 w-5 text-custom-text-200" />
<div className="flex size-8 flex-shrink-0 items-center justify-center rounded-sm bg-layer-1">
<UserCog2 className="size-5 text-primary" />
</div>
</Menu.Button>
{isSidebarCollapsed && (
@@ -111,7 +115,7 @@ export const AdminSidebarDropdown = observer(() => {
{!isSidebarCollapsed && (
<div className="flex w-full gap-2">
<h4 className="grow truncate text-base font-medium text-custom-text-200">Instance admin</h4>
<h4 className="grow truncate text-body-md-medium text-primary">Instance admin</h4>
</div>
)}
</div>
@@ -125,7 +129,7 @@ export const AdminSidebarDropdown = observer(() => {
src={getFileURL(currentUser.avatar_url)}
size={24}
shape="square"
className="!text-base"
className="!text-body-sm-medium"
/>
</Menu.Button>
@@ -1,26 +1,28 @@
"use client";
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { FC, useState, useRef } from "react";
import { useState, useRef } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { ExternalLink, FileText, HelpCircle, MoveLeft } from "lucide-react";
import { HelpCircle, MoveLeft } from "lucide-react";
import { Transition } from "@headlessui/react";
// plane internal packages
import { WEB_BASE_URL } from "@plane/constants";
import { DiscordIcon, GithubIcon } from "@plane/propel/icons";
// plane internal packages
import { DiscordIcon, GithubIcon, NewTabIcon, PageIcon } from "@plane/propel/icons";
import { Tooltip } from "@plane/propel/tooltip";
import { cn } from "@plane/utils";
// hooks
import { useTheme } from "@/hooks/store";
import { useInstance, useTheme } from "@/hooks/store";
// assets
// eslint-disable-next-line import/order
import packageJson from "package.json";
const helpOptions = [
{
name: "Documentation",
href: "https://docs.plane.so/",
Icon: FileText,
Icon: PageIcon,
},
{
name: "Join our Discord",
@@ -34,10 +36,11 @@ const helpOptions = [
},
];
export const AdminSidebarHelpSection: FC = observer(() => {
export const AdminSidebarHelpSection = observer(function AdminSidebarHelpSection() {
// states
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
// store
const { instance } = useInstance();
const { isSidebarCollapsed, toggleSidebar } = useTheme();
// refs
const helpOptionsRef = useRef<HTMLDivElement | null>(null);
@@ -47,7 +50,7 @@ export const AdminSidebarHelpSection: FC = observer(() => {
return (
<div
className={cn(
"flex w-full items-center justify-between gap-1 self-baseline border-t border-custom-border-200 bg-custom-sidebar-background-100 px-4 h-14 flex-shrink-0",
"flex w-full items-center justify-between gap-1 self-baseline border-t border-subtle bg-surface-1 px-4 h-14 flex-shrink-0",
{
"flex-col h-auto py-1.5": isSidebarCollapsed,
}
@@ -57,32 +60,32 @@ export const AdminSidebarHelpSection: FC = observer(() => {
<Tooltip tooltipContent="Redirect to Plane" position="right" className="ml-4" disabled={!isSidebarCollapsed}>
<a
href={redirectionLink}
className={`relative px-2 py-1.5 flex items-center gap-2 font-medium rounded border border-custom-primary-100/20 bg-custom-primary-100/10 text-xs text-custom-primary-200 whitespace-nowrap`}
className={`relative px-2 py-1 flex items-center gap-1 rounded-sm bg-layer-1 text-body-xs-medium text-secondary whitespace-nowrap`}
>
<ExternalLink size={14} />
<NewTabIcon width={14} height={14} />
{!isSidebarCollapsed && "Redirect to Plane"}
</a>
</Tooltip>
<Tooltip tooltipContent="Help" position={isSidebarCollapsed ? "right" : "top"} className="ml-4">
<button
type="button"
className={`ml-auto grid place-items-center rounded-md p-1.5 text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 ${
className={`ml-auto grid place-items-center rounded-md p-1.5 text-secondary outline-none hover:bg-layer-1-hover hover:text-primary ${
isSidebarCollapsed ? "w-full" : ""
}`}
onClick={() => setIsNeedHelpOpen((prev) => !prev)}
>
<HelpCircle className="h-3.5 w-3.5" />
<HelpCircle className="size-4" />
</button>
</Tooltip>
<Tooltip tooltipContent="Toggle sidebar" position={isSidebarCollapsed ? "right" : "top"} className="ml-4">
<button
type="button"
className={`grid place-items-center rounded-md p-1.5 text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 ${
className={`grid place-items-center rounded-md p-1.5 text-secondary outline-none hover:bg-layer-1-hover hover:text-primary ${
isSidebarCollapsed ? "w-full" : ""
}`}
onClick={() => toggleSidebar(!isSidebarCollapsed)}
>
<MoveLeft className={`h-3.5 w-3.5 duration-300 ${isSidebarCollapsed ? "rotate-180" : ""}`} />
<MoveLeft className={`size-4 duration-300 ${isSidebarCollapsed ? "rotate-180" : ""}`} />
</button>
</Tooltip>
</div>
@@ -100,7 +103,7 @@ export const AdminSidebarHelpSection: FC = observer(() => {
<div
className={`absolute bottom-2 min-w-[10rem] z-[15] ${
isSidebarCollapsed ? "left-full" : "-left-[75px]"
} divide-y divide-custom-border-200 whitespace-nowrap rounded bg-custom-background-100 p-1 shadow-custom-shadow-xs`}
} divide-y divide-subtle-1 whitespace-nowrap rounded-sm bg-surface-1 p-1 shadow-raised-100`}
ref={helpOptionsRef}
>
<div className="space-y-1 pb-2">
@@ -108,11 +111,11 @@ export const AdminSidebarHelpSection: FC = observer(() => {
if (href)
return (
<Link href={href} key={name} target="_blank">
<div className="flex items-center gap-x-2 rounded px-2 py-1 text-xs hover:bg-custom-background-80">
<div className="flex items-center gap-x-2 rounded-sm px-2 py-1 text-11 hover:bg-layer-1-hover">
<div className="grid flex-shrink-0 place-items-center">
<Icon className="h-3.5 w-3.5 text-custom-text-200" size={14} />
<Icon className="h-3.5 w-3.5 text-secondary" />
</div>
<span className="text-xs">{name}</span>
<span className="text-11">{name}</span>
</div>
</Link>
);
@@ -121,17 +124,17 @@ export const AdminSidebarHelpSection: FC = observer(() => {
<button
key={name}
type="button"
className="flex w-full items-center gap-x-2 rounded px-2 py-1 text-xs hover:bg-custom-background-80"
className="flex w-full items-center gap-x-2 rounded-sm px-2 py-1 text-11 hover:bg-layer-1"
>
<div className="grid flex-shrink-0 place-items-center">
<Icon className="h-3.5 w-3.5 text-custom-text-200" />
<Icon className="h-3.5 w-3.5 text-secondary" />
</div>
<span className="text-xs">{name}</span>
<span className="text-11">{name}</span>
</button>
);
})}
</div>
<div className="px-2 pb-1 pt-2 text-[10px]">Version: v{packageJson.version}</div>
<div className="px-2 pb-1 pt-2 text-10">Version: v{instance?.current_version}</div>
</div>
</Transition>
</div>
@@ -1,60 +1,26 @@
"use client";
/**
* 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";
import { Image, BrainCog, Cog, Lock, Mail } from "lucide-react";
// plane internal packages
import { WorkspaceIcon } from "@plane/propel/icons";
import { Tooltip } from "@plane/propel/tooltip";
import { cn } from "@plane/utils";
// hooks
import { useTheme } from "@/hooks/store";
import { useSidebarMenu } from "@/hooks/use-sidebar-menu";
const INSTANCE_ADMIN_LINKS = [
{
Icon: Cog,
name: "General",
description: "Identify your instances and get key details.",
href: `/general/`,
},
{
Icon: WorkspaceIcon,
name: "Workspaces",
description: "Manage all workspaces on this instance.",
href: `/workspace/`,
},
{
Icon: Mail,
name: "Email",
description: "Configure your SMTP controls.",
href: `/email/`,
},
{
Icon: Lock,
name: "Authentication",
description: "Configure authentication modes.",
href: `/authentication/`,
},
{
Icon: BrainCog,
name: "Artificial intelligence",
description: "Configure your OpenAI creds.",
href: `/ai/`,
},
{
Icon: Image,
name: "Images in Plane",
description: "Allow third-party image libraries.",
href: `/image/`,
},
];
export const AdminSidebarMenu = observer(() => {
// store hooks
const { isSidebarCollapsed, toggleSidebar } = useTheme();
export const AdminSidebarMenu = observer(function AdminSidebarMenu() {
// router
const pathName = usePathname();
// store hooks
const { isSidebarCollapsed, toggleSidebar } = useTheme();
// derived values
const sidebarMenu = useSidebarMenu();
const handleItemClick = () => {
if (window.innerWidth < 768) {
@@ -64,40 +30,27 @@ export const AdminSidebarMenu = observer(() => {
return (
<div className="flex h-full w-full flex-col gap-2.5 overflow-y-scroll vertical-scrollbar scrollbar-sm px-4 py-4">
{INSTANCE_ADMIN_LINKS.map((item, index) => {
const isActive = item.href === pathName || pathName.includes(item.href);
{sidebarMenu.map((item, index) => {
const isActive = item.href === pathName || pathName?.includes(item.href);
return (
<Link key={index} href={item.href} onClick={handleItemClick}>
<div>
<Tooltip tooltipContent={item.name} position="right" className="ml-2" disabled={!isSidebarCollapsed}>
<div
className={cn(
`group flex w-full items-center gap-3 rounded-md px-3 py-2 outline-none transition-colors`,
isActive
? "bg-custom-primary-100/10 text-custom-primary-100"
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80",
"group flex w-full items-center gap-3 rounded-md px-3 py-2 outline-none transition-colors",
{
"text-primary !bg-layer-transparent-active": isActive,
"text-secondary hover:bg-layer-transparent-hover active:bg-layer-transparent-active": !isActive,
},
isSidebarCollapsed ? "justify-center" : "w-[260px]"
)}
>
{<item.Icon className="h-4 w-4 flex-shrink-0" />}
{!isSidebarCollapsed && (
<div className="w-full ">
<div
className={cn(
`text-sm font-medium transition-colors`,
isActive ? "text-custom-primary-100" : "text-custom-sidebar-text-200"
)}
>
{item.name}
</div>
<div
className={cn(
`text-[10px] transition-colors`,
isActive ? "text-custom-primary-90" : "text-custom-sidebar-text-400"
)}
>
{item.description}
</div>
<div className={cn(`text-body-xs-medium transition-colors`)}>{item.name}</div>
<div className={cn(`text-caption-sm-regular transition-colors`)}>{item.description}</div>
</div>
)}
</div>
+8 -4
View File
@@ -1,6 +1,10 @@
"use client";
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { FC, useEffect, useRef } from "react";
import { useEffect, useRef } from "react";
import { observer } from "mobx-react";
// plane helpers
import { useOutsideClickDetector } from "@plane/hooks";
@@ -11,7 +15,7 @@ import { AdminSidebarDropdown } from "./sidebar-dropdown";
import { AdminSidebarHelpSection } from "./sidebar-help-section";
import { AdminSidebarMenu } from "./sidebar-menu";
export const AdminSidebar: FC = observer(() => {
export const AdminSidebar = observer(function AdminSidebar() {
// store
const { isSidebarCollapsed, toggleSidebar } = useTheme();
@@ -40,7 +44,7 @@ export const AdminSidebar: FC = observer(() => {
return (
<div
className={`inset-y-0 z-20 flex h-full flex-shrink-0 flex-grow-0 flex-col border-r border-custom-sidebar-border-200 bg-custom-sidebar-background-100 duration-300
className={`inset-y-0 z-20 flex h-full flex-shrink-0 flex-grow-0 flex-col border-r border-subtle bg-surface-1 duration-300
fixed md:relative
${isSidebarCollapsed ? "-ml-[290px]" : ""}
sm:${isSidebarCollapsed ? "-ml-[290px]" : ""}
@@ -1,19 +1,27 @@
/**
* 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";
import { Controller, useForm } from "react-hook-form";
// plane imports
import { WEB_BASE_URL, ORGANIZATION_SIZE, RESTRICTED_URLS } from "@plane/constants";
import { Button, getButtonStyling } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { InstanceWorkspaceService } from "@plane/services";
import { IWorkspace } from "@plane/types";
import type { IWorkspace } from "@plane/types";
// components
import { Button, CustomSelect, getButtonStyling, Input, setToast, TOAST_TYPE } from "@plane/ui";
import { CustomSelect, Input } from "@plane/ui";
// hooks
import { useWorkspace } from "@/hooks/store";
const instanceWorkspaceService = new InstanceWorkspaceService();
export const WorkspaceCreateForm = () => {
export function WorkspaceCreateForm() {
// router
const router = useRouter();
// states
@@ -82,7 +90,7 @@ export const WorkspaceCreateForm = () => {
<div className="space-y-8">
<div className="grid-col grid w-full max-w-4xl grid-cols-1 items-start justify-between gap-x-10 gap-y-6 lg:grid-cols-2">
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-300">Name your workspace</h4>
<h4 className="text-13 text-tertiary">Name your workspace</h4>
<div className="flex flex-col gap-1">
<Controller
control={control}
@@ -116,13 +124,13 @@ export const WorkspaceCreateForm = () => {
/>
)}
/>
<span className="text-xs text-red-500">{errors?.name?.message}</span>
<span className="text-11 text-danger-primary">{errors?.name?.message}</span>
</div>
</div>
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-300">Set your workspace&apos;s URL</h4>
<div className="flex gap-0.5 w-full items-center rounded-md border-[0.5px] border-custom-border-200 px-3">
<span className="whitespace-nowrap text-sm text-custom-text-200">{workspaceBaseURL}</span>
<h4 className="text-13 text-tertiary">Set your workspace&apos;s URL</h4>
<div className="flex gap-0.5 w-full items-center rounded-md border-[0.5px] border-subtle px-3">
<span className="whitespace-nowrap text-13 text-secondary">{workspaceBaseURL}</span>
<Controller
control={control}
name="slug"
@@ -146,19 +154,19 @@ export const WorkspaceCreateForm = () => {
ref={ref}
hasError={Boolean(errors.slug)}
placeholder="workspace-name"
className="block w-full rounded-md border-none bg-transparent !px-0 py-2 text-sm"
className="block w-full rounded-md border-none bg-transparent !px-0 py-2 text-13"
/>
)}
/>
</div>
{slugError && <p className="text-sm text-red-500">This URL is taken. Try something else.</p>}
{slugError && <p className="text-13 text-danger-primary">This URL is taken. Try something else.</p>}
{invalidSlug && (
<p className="text-sm text-red-500">{`URLs can contain only ( - ), ( _ ) and alphanumeric characters.`}</p>
<p className="text-13 text-danger-primary">{`URLs can contain only ( - ), ( _ ) and alphanumeric characters.`}</p>
)}
{errors.slug && <span className="text-xs text-red-500">{errors.slug.message}</span>}
{errors.slug && <span className="text-11 text-danger-primary">{errors.slug.message}</span>}
</div>
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-300">How many people will use this workspace?</h4>
<h4 className="text-13 text-tertiary">How many people will use this workspace?</h4>
<div className="w-full">
<Controller
name="organization_size"
@@ -170,12 +178,11 @@ export const WorkspaceCreateForm = () => {
onChange={onChange}
label={
ORGANIZATION_SIZE.find((c) => c === value) ?? (
<span className="text-custom-text-400">Select a range</span>
<span className="text-placeholder">Select a range</span>
)
}
buttonClassName="!border-[0.5px] !border-custom-border-200 !shadow-none"
buttonClassName="!border-[0.5px] !border-subtle !shadow-none"
input
optionsClassName="w-full"
>
{ORGANIZATION_SIZE.map((item) => (
<CustomSelect.Option key={item} value={item}>
@@ -186,7 +193,7 @@ export const WorkspaceCreateForm = () => {
)}
/>
{errors.organization_size && (
<span className="text-sm text-red-500">{errors.organization_size.message}</span>
<span className="text-13 text-danger-primary">{errors.organization_size.message}</span>
)}
</div>
</div>
@@ -194,17 +201,17 @@ export const WorkspaceCreateForm = () => {
<div className="flex max-w-4xl items-center py-1 gap-4">
<Button
variant="primary"
size="sm"
size="lg"
onClick={handleSubmit(handleCreateWorkspace)}
disabled={!isValid}
loading={isSubmitting}
>
{isSubmitting ? "Creating workspace" : "Create workspace"}
</Button>
<Link className={getButtonStyling("neutral-primary", "sm")} href="/workspace">
<Link className={getButtonStyling("secondary", "lg")} href="/workspace">
Go back
</Link>
</div>
</div>
);
};
}
@@ -1,21 +1,30 @@
"use client";
/**
* 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";
// types
import type { Route } from "./+types/page";
// local
import { WorkspaceCreateForm } from "./form";
const WorkspaceCreatePage = observer(() => (
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">Create a new workspace on this instance.</div>
<div className="text-sm font-normal text-custom-text-300">
You will need to invite users from Workspace Settings after you create this workspace.
</div>
</div>
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
const WorkspaceCreatePage = observer(function WorkspaceCreatePage(_props: Route.ComponentProps) {
return (
<PageWrapper
header={{
title: "Create a new workspace on this instance.",
description: "You will need to invite users from Workspace Settings after you create this workspace.",
}}
>
<WorkspaceCreateForm />
</div>
</div>
));
</PageWrapper>
);
});
export const meta: Route.MetaFunction = () => [{ title: "Create Workspace - God Mode" }];
export default WorkspaceCreatePage;
@@ -1,10 +0,0 @@
import { ReactNode } from "react";
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Workspace Management - God Mode",
};
export default function WorkspaceManagementLayout({ children }: { children: ReactNode }) {
return <>{children}</>;
}
@@ -1,4 +1,8 @@
"use client";
/**
* 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";
@@ -6,15 +10,20 @@ import Link from "next/link";
import useSWR from "swr";
import { Loader as LoaderIcon } from "lucide-react";
// types
import { TInstanceConfigurationKeys } from "@plane/types";
import { Button, getButtonStyling, Loader, setPromiseToast, ToggleSwitch } from "@plane/ui";
import { Button, getButtonStyling } from "@plane/propel/button";
import { setPromiseToast } from "@plane/propel/toast";
import type { TInstanceConfigurationKeys } from "@plane/types";
import { Loader, ToggleSwitch } from "@plane/ui";
import { cn } from "@plane/utils";
// components
import { PageWrapper } from "@/components/common/page-wrapper";
import { WorkspaceListItem } from "@/components/workspace/list-item";
// hooks
import { useInstance, useWorkspace } from "@/hooks/store";
// types
import type { Route } from "./+types/page";
const WorkspaceManagementPage = observer(() => {
const WorkspaceManagementPage = observer(function WorkspaceManagementPage(_props: Route.ComponentProps) {
// states
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
// store
@@ -66,102 +75,98 @@ const WorkspaceManagementPage = observer(() => {
};
return (
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="flex items-center justify-between gap-4 border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="flex flex-col gap-1">
<div className="text-xl font-medium text-custom-text-100">Workspaces on this instance</div>
<div className="text-sm font-normal text-custom-text-300">
See all workspaces and control who can create them.
</div>
</div>
</div>
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
<div className="space-y-3">
{formattedConfig ? (
<div className={cn("w-full flex items-center gap-14 rounded")}>
<div className="flex grow items-center gap-4">
<div className="grow">
<div className="text-lg font-medium pb-1">Prevent anyone else from creating a workspace.</div>
<div className={cn("font-normal leading-5 text-custom-text-300 text-xs")}>
Toggling this on will let only you create workspaces. You will have to invite users to new
workspaces.
</div>
</div>
</div>
<div className={`shrink-0 pr-4 ${isSubmitting && "opacity-70"}`}>
<div className="flex items-center gap-4">
<ToggleSwitch
value={Boolean(parseInt(disableWorkspaceCreation))}
onChange={() => {
if (Boolean(parseInt(disableWorkspaceCreation)) === true) {
updateConfig("DISABLE_WORKSPACE_CREATION", "0");
} else {
updateConfig("DISABLE_WORKSPACE_CREATION", "1");
}
}}
size="sm"
disabled={isSubmitting}
/>
<PageWrapper
header={{
title: "Workspaces on this instance",
description: "See all workspaces and control who can create them.",
}}
>
<div className="space-y-3">
{formattedConfig ? (
<div className={cn("w-full flex items-center gap-14 rounded-sm")}>
<div className="flex grow items-center gap-4">
<div className="grow">
<div className="text-16 font-medium pb-1">Prevent anyone else from creating a workspace.</div>
<div className={cn("font-regular leading-5 text-tertiary text-11")}>
Toggling this on will let only you create workspaces. You will have to invite users to new workspaces.
</div>
</div>
</div>
) : (
<Loader>
<Loader.Item height="50px" width="100%" />
</Loader>
)}
{workspaceLoader !== "init-loader" ? (
<>
<div className="pt-6 flex items-center justify-between gap-2">
<div className="flex flex-col items-start gap-x-2">
<div className="flex items-center gap-2 text-lg font-medium">
All workspaces on this instance{" "}
<span className="text-custom-text-300"> {workspaceIds.length}</span>
{workspaceLoader && ["mutation", "pagination"].includes(workspaceLoader) && (
<LoaderIcon className="w-4 h-4 animate-spin" />
)}
</div>
<div className={cn("font-normal leading-5 text-custom-text-300 text-xs")}>
You can&apos;t yet delete workspaces and you can only go to the workspace if you are an Admin or a
Member.
</div>
<div className={`shrink-0 pr-4 ${isSubmitting && "opacity-70"}`}>
<div className="flex items-center gap-4">
<ToggleSwitch
value={Boolean(parseInt(disableWorkspaceCreation))}
onChange={() => {
if (Boolean(parseInt(disableWorkspaceCreation)) === true) {
updateConfig("DISABLE_WORKSPACE_CREATION", "0");
} else {
updateConfig("DISABLE_WORKSPACE_CREATION", "1");
}
}}
size="sm"
disabled={isSubmitting}
/>
</div>
</div>
</div>
) : (
<Loader>
<Loader.Item height="50px" width="100%" />
</Loader>
)}
{workspaceLoader !== "init-loader" ? (
<>
<div className="pt-6 flex items-center justify-between gap-2">
<div className="flex flex-col items-start gap-x-2">
<div className="flex items-center gap-2 text-16 font-medium">
All workspaces on this instance <span className="text-tertiary"> {workspaceIds.length}</span>
{workspaceLoader && ["mutation", "pagination"].includes(workspaceLoader) && (
<LoaderIcon className="w-4 h-4 animate-spin" />
)}
</div>
<div className="flex items-center gap-2">
<Link href="/workspace/create" className={getButtonStyling("primary", "sm")}>
Create workspace
</Link>
<div className={cn("font-regular leading-5 text-tertiary text-11")}>
You can&apos;t yet delete workspaces and you can only go to the workspace if you are an Admin or a
Member.
</div>
</div>
<div className="flex flex-col gap-4 py-2">
{workspaceIds.map((workspaceId) => (
<WorkspaceListItem key={workspaceId} workspaceId={workspaceId} />
))}
<div className="flex items-center gap-2">
<Link href="/workspace/create" className={getButtonStyling("primary", "base")}>
Create workspace
</Link>
</div>
{hasNextPage && (
<div className="flex justify-center">
<Button
variant="link-primary"
onClick={() => fetchNextWorkspaces()}
disabled={workspaceLoader === "pagination"}
>
Load more
{workspaceLoader === "pagination" && <LoaderIcon className="w-3 h-3 animate-spin" />}
</Button>
</div>
)}
</>
) : (
<Loader className="space-y-10 py-8">
<Loader.Item height="24px" width="20%" />
<Loader.Item height="92px" width="100%" />
<Loader.Item height="92px" width="100%" />
<Loader.Item height="92px" width="100%" />
</Loader>
)}
</div>
</div>
<div className="flex flex-col gap-4 py-2">
{workspaceIds.map((workspaceId) => (
<WorkspaceListItem key={workspaceId} workspaceId={workspaceId} />
))}
</div>
{hasNextPage && (
<div className="flex justify-center">
<Button
variant="link"
size="lg"
onClick={() => fetchNextWorkspaces()}
disabled={workspaceLoader === "pagination"}
>
Load more
{workspaceLoader === "pagination" && <LoaderIcon className="w-3 h-3 animate-spin" />}
</Button>
</div>
)}
</>
) : (
<Loader className="space-y-10 py-8">
<Loader.Item height="24px" width="20%" />
<Loader.Item height="92px" width="100%" />
<Loader.Item height="92px" width="100%" />
<Loader.Item height="92px" width="100%" />
</Loader>
)}
</div>
</div>
</PageWrapper>
);
});
export const meta: Route.MetaFunction = () => [{ title: "Workspace Management - God Mode" }];
export default WorkspaceManagementPage;
+17 -10
View File
@@ -1,29 +1,36 @@
import { FC } from "react";
import { Info, X } from "lucide-react";
/**
* 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 { TAdminAuthErrorInfo } from "@plane/constants";
import type { TAdminAuthErrorInfo } from "@plane/constants";
// icons
import { CloseIcon } from "@plane/propel/icons";
type TAuthBanner = {
bannerData: TAdminAuthErrorInfo | undefined;
handleBannerData?: (bannerData: TAdminAuthErrorInfo | undefined) => void;
};
export const AuthBanner: FC<TAuthBanner> = (props) => {
export function AuthBanner(props: TAuthBanner) {
const { bannerData, handleBannerData } = props;
if (!bannerData) return <></>;
return (
<div className="relative flex items-center p-2 rounded-md gap-2 border border-custom-primary-100/50 bg-custom-primary-100/10">
<div className="relative flex items-center p-2 rounded-md gap-2 border border-accent-strong/50 bg-accent-primary/10">
<div className="w-4 h-4 flex-shrink-0 relative flex justify-center items-center">
<Info size={16} className="text-custom-primary-100" />
<Info size={16} className="text-accent-primary" />
</div>
<div className="w-full text-sm font-medium text-custom-primary-100">{bannerData?.message}</div>
<div className="w-full text-13 font-medium text-accent-primary">{bannerData?.message}</div>
<div
className="relative ml-auto w-6 h-6 rounded-sm flex justify-center items-center transition-all cursor-pointer hover:bg-custom-primary-100/20 text-custom-primary-100/80"
className="relative ml-auto w-6 h-6 rounded-xs flex justify-center items-center transition-all cursor-pointer hover:bg-accent-primary/20 text-accent-primary"
onClick={() => handleBannerData && handleBannerData(undefined)}
>
<X className="w-4 h-4 flex-shrink-0" />
<CloseIcon className="w-4 h-4 flex-shrink-0" />
</div>
</div>
);
};
}
+14 -8
View File
@@ -1,12 +1,18 @@
"use client";
/**
* 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 const AuthHeader = () => (
<div className="flex items-center justify-between gap-6 w-full flex-shrink-0 sticky top-0">
<Link href="/">
<PlaneLockup height={20} width={95} className="text-custom-text-100" />
</Link>
</div>
);
export function AuthHeader() {
return (
<div className="flex items-center justify-between gap-6 w-full flex-shrink-0 sticky top-0">
<Link href="/">
<PlaneLockup height={20} width={95} className="text-primary" />
</Link>
</div>
);
}
+11 -73
View File
@@ -1,22 +1,13 @@
import { ReactNode } from "react";
import Image from "next/image";
/**
* 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 { SUPPORT_EMAIL, EAdminAuthErrorCodes, TAdminAuthErrorInfo } from "@plane/constants";
import { TGetBaseAuthenticationModeProps, TInstanceAuthenticationModes } from "@plane/types";
import { resolveGeneralTheme } from "@plane/utils";
// components
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
import githubLightModeImage from "@/public/logos/github-black.png";
import githubDarkModeImage from "@/public/logos/github-white.png";
import GitlabLogo from "@/public/logos/gitlab-logo.svg";
import GoogleLogo from "@/public/logos/google-logo.svg";
import type { TAdminAuthErrorInfo } from "@plane/constants";
import { SUPPORT_EMAIL, EAdminAuthErrorCodes } from "@plane/constants";
export enum EErrorAlertType {
BANNER_ALERT = "BANNER_ALERT",
@@ -27,7 +18,7 @@ export enum EErrorAlertType {
}
const errorCodeMessages: {
[key in EAdminAuthErrorCodes]: { title: string; message: (email?: string | undefined) => ReactNode };
[key in EAdminAuthErrorCodes]: { title: string; message: (email?: string) => React.ReactNode };
} = {
// admin
[EAdminAuthErrorCodes.ADMIN_ALREADY_EXIST]: {
@@ -80,14 +71,11 @@ const errorCodeMessages: {
},
[EAdminAuthErrorCodes.ADMIN_USER_DEACTIVATED]: {
title: `User account deactivated`,
message: () => `User account deactivated. Please contact ${!!SUPPORT_EMAIL ? SUPPORT_EMAIL : "administrator"}.`,
message: () => `User account deactivated. Please contact ${SUPPORT_EMAIL ? SUPPORT_EMAIL : "administrator"}.`,
},
};
export const authErrorHandler = (
errorCode: EAdminAuthErrorCodes,
email?: string | undefined
): TAdminAuthErrorInfo | undefined => {
export const authErrorHandler = (errorCode: EAdminAuthErrorCodes, email?: string): TAdminAuthErrorInfo | undefined => {
const bannerAlertErrorCodes = [
EAdminAuthErrorCodes.ADMIN_ALREADY_EXIST,
EAdminAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME,
@@ -110,53 +98,3 @@ export const authErrorHandler = (
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: <Mails className="h-6 w-6 p-0.5 text-custom-text-300/80" />,
config: <EmailCodesConfiguration disabled={disabled} updateConfig={updateConfig} />,
},
{
key: "passwords-login",
name: "Passwords",
description: "Allow members to create accounts with passwords and use it with their email addresses to sign in.",
icon: <KeyRound className="h-6 w-6 p-0.5 text-custom-text-300/80" />,
config: <PasswordLoginConfiguration disabled={disabled} updateConfig={updateConfig} />,
},
{
key: "google",
name: "Google",
description: "Allow members to log in or sign up for Plane with their Google accounts.",
icon: <Image src={GoogleLogo} height={20} width={20} alt="Google Logo" />,
config: <GoogleConfiguration disabled={disabled} updateConfig={updateConfig} />,
},
{
key: "github",
name: "GitHub",
description: "Allow members to log in or sign up for Plane with their GitHub accounts.",
icon: (
<Image
src={resolveGeneralTheme(resolvedTheme) === "dark" ? githubDarkModeImage : githubLightModeImage}
height={20}
width={20}
alt="GitHub Logo"
/>
),
config: <GithubConfiguration disabled={disabled} updateConfig={updateConfig} />,
},
{
key: "gitlab",
name: "GitLab",
description: "Allow members to log in or sign up to plane with their GitLab accounts.",
icon: <Image src={GitlabLogo} height={20} width={20} alt="GitLab Logo" />,
config: <GitlabConfiguration disabled={disabled} updateConfig={updateConfig} />,
},
];
+26 -4
View File
@@ -1,9 +1,31 @@
"use client";
/**
* 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";
import { Outlet } from "react-router";
// hooks
import { useUser } from "@/hooks/store/use-user";
function RootLayout() {
// router
const { replace } = useRouter();
// store hooks
const { isUserLoggedIn } = useUser();
useEffect(() => {
if (isUserLoggedIn === true) replace("/general");
}, [replace, isUserLoggedIn]);
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
{children}
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8 bg-surface-1">
<Outlet />
</div>
);
}
export default observer(RootLayout);
+13 -3
View File
@@ -1,4 +1,8 @@
"use client";
/**
* 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
@@ -8,9 +12,10 @@ import { InstanceSetupForm } from "@/components/instance/setup-form";
// hooks
import { useInstance } from "@/hooks/store";
// components
import type { Route } from "./+types/page";
import { InstanceSignInForm } from "./sign-in-form";
const HomePage = () => {
function HomePage() {
// store hooks
const { instance, error } = useInstance();
@@ -35,6 +40,11 @@ const HomePage = () => {
// if instance is fetched and setup is done, show sign in form
return <InstanceSignInForm />;
};
}
export default observer(HomePage);
export const meta: Route.MetaFunction = () => [
{ title: "Admin Instance Setup & Sign-In" },
{ name: "description", content: "Configure your Plane instance or sign in to the admin portal." },
];
+22 -16
View File
@@ -1,16 +1,22 @@
"use client";
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { FC, useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { useSearchParams } from "next/navigation";
import { Eye, EyeOff } from "lucide-react";
// plane internal packages
import { API_BASE_URL, EAdminAuthErrorCodes, TAdminAuthErrorInfo } from "@plane/constants";
import type { EAdminAuthErrorCodes, TAdminAuthErrorInfo } from "@plane/constants";
import { API_BASE_URL } from "@plane/constants";
import { Button } from "@plane/propel/button";
import { AuthService } from "@plane/services";
import { Button, Input, Spinner } from "@plane/ui";
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";
@@ -43,7 +49,7 @@ const defaultFromData: TFormData = {
password: "",
};
export const InstanceSignInForm: FC = () => {
export function InstanceSignInForm() {
// search params
const searchParams = useSearchParams();
const emailParam = searchParams.get("email") || undefined;
@@ -128,11 +134,11 @@ export const InstanceSignInForm: FC = () => {
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="email">
Email <span className="text-red-500">*</span>
<label className="text-13 text-tertiary font-medium" htmlFor="email">
Email <span className="text-danger-primary">*</span>
</label>
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
className="w-full border border-subtle !bg-surface-1 placeholder:text-placeholder"
id="email"
name="email"
type="email"
@@ -146,12 +152,12 @@ export const InstanceSignInForm: FC = () => {
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="password">
Password <span className="text-red-500">*</span>
<label className="text-13 text-tertiary font-medium" htmlFor="password">
Password <span className="text-danger-primary">*</span>
</label>
<div className="relative">
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
className="w-full border border-subtle !bg-surface-1 placeholder:text-placeholder"
id="password"
name="password"
type={showPassword ? "text" : "password"}
@@ -164,7 +170,7 @@ export const InstanceSignInForm: FC = () => {
{showPassword ? (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
className="absolute right-3 top-3.5 flex items-center justify-center text-placeholder"
onClick={() => setShowPassword(false)}
>
<EyeOff className="h-4 w-4" />
@@ -172,7 +178,7 @@ export const InstanceSignInForm: FC = () => {
) : (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
className="absolute right-3 top-3.5 flex items-center justify-center text-placeholder"
onClick={() => setShowPassword(true)}
>
<Eye className="h-4 w-4" />
@@ -181,7 +187,7 @@ export const InstanceSignInForm: FC = () => {
</div>
</div>
<div className="py-2">
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
<Button type="submit" size="xl" className="w-full" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Sign in"}
</Button>
</div>
@@ -190,4 +196,4 @@ export const InstanceSignInForm: FC = () => {
</div>
</>
);
};
}
-10
View File
@@ -1,10 +0,0 @@
"use client";
import { useTheme } from "next-themes";
import { Toast } from "@plane/ui";
import { resolveGeneralTheme } from "@plane/utils";
export const ToastWithTheme = () => {
const { resolvedTheme } = useTheme();
return <Toast theme={resolveGeneralTheme(resolvedTheme)} />;
};

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Before

Width:  |  Height:  |  Size: 466 B

After

Width:  |  Height:  |  Size: 466 B

Before

Width:  |  Height:  |  Size: 761 B

After

Width:  |  Height:  |  Size: 761 B

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Before

Width:  |  Height:  |  Size: 954 KiB

After

Width:  |  Height:  |  Size: 954 KiB

Before

Width:  |  Height:  |  Size: 418 KiB

After

Width:  |  Height:  |  Size: 418 KiB

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 640 640" width="32" height="32"><path d="m395.9 484.2-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5 21.2-17.9 33.8-11.8 17.2 8.3 27.1 13 27.1 13l-.1-109.2 16.7-.1.1 117.1s57.4 24.2 83.1 40.1c3.7 2.3 10.2 6.8 12.9 14.4 2.1 6.1 2 13.1-1 19.3l-61 126.9c-6.2 12.7-21.4 18.1-33.9 12" style="fill:#fff"/><path d="M622.7 149.8c-4.1-4.1-9.6-4-9.6-4s-117.2 6.6-177.9 8c-13.3.3-26.5.6-39.6.7v117.2c-5.5-2.6-11.1-5.3-16.6-7.9 0-36.4-.1-109.2-.1-109.2-29 .4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5c-9.8-.6-22.5-2.1-39 1.5-8.7 1.8-33.5 7.4-53.8 26.9C-4.9 212.4 6.6 276.2 8 285.8c1.7 11.7 6.9 44.2 31.7 72.5 45.8 56.1 144.4 54.8 144.4 54.8s12.1 28.9 30.6 55.5c25 33.1 50.7 58.9 75.7 62 63 0 188.9-.1 188.9-.1s12 .1 28.3-10.3c14-8.5 26.5-23.4 26.5-23.4S547 483 565 451.5c5.5-9.7 10.1-19.1 14.1-28 0 0 55.2-117.1 55.2-231.1-1.1-34.5-9.6-40.6-11.6-42.6M125.6 353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6 321.8 60 295.4c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5 38.5-30c13.8-3.7 31-3.1 31-3.1s7.1 59.4 15.7 94.2c7.2 29.2 24.8 77.7 24.8 77.7s-26.1-3.1-43-9.1m300.3 107.6s-6.1 14.5-19.6 15.4c-5.8.4-10.3-1.2-10.3-1.2s-.3-.1-5.3-2.1l-112.9-55s-10.9-5.7-12.8-15.6c-2.2-8.1 2.7-18.1 2.7-18.1L322 273s4.8-9.7 12.2-13c.6-.3 2.3-1 4.5-1.5 8.1-2.1 18 2.8 18 2.8L467.4 315s12.6 5.7 15.3 16.2c1.9 7.4-.5 14-1.8 17.2-6.3 15.4-55 113.1-55 113.1" style="fill:#609926"/><path d="M326.8 380.1c-8.2.1-15.4 5.8-17.3 13.8s2 16.3 9.1 20c7.7 4 17.5 1.8 22.7-5.4 5.1-7.1 4.3-16.9-1.8-23.1l24-49.1c1.5.1 3.7.2 6.2-.5 4.1-.9 7.1-3.6 7.1-3.6 4.2 1.8 8.6 3.8 13.2 6.1 4.8 2.4 9.3 4.9 13.4 7.3.9.5 1.8 1.1 2.8 1.9 1.6 1.3 3.4 3.1 4.7 5.5 1.9 5.5-1.9 14.9-1.9 14.9-2.3 7.6-18.4 40.6-18.4 40.6-8.1-.2-15.3 5-17.7 12.5-2.6 8.1 1.1 17.3 8.9 21.3s17.4 1.7 22.5-5.3c5-6.8 4.6-16.3-1.1-22.6 1.9-3.7 3.7-7.4 5.6-11.3 5-10.4 13.5-30.4 13.5-30.4.9-1.7 5.7-10.3 2.7-21.3-2.5-11.4-12.6-16.7-12.6-16.7-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3 4.7-9.7 9.4-19.3 14.1-29-4.1-2-8.1-4-12.2-6.1-4.8 9.8-9.7 19.7-14.5 29.5-6.7-.1-12.9 3.5-16.1 9.4-3.4 6.3-2.7 14.1 1.9 19.8z" style="fill:#609926"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 742 B

After

Width:  |  Height:  |  Size: 742 B

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

+41
View File
@@ -0,0 +1,41 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
/**
* Ensures that a URL has a trailing slash while preserving query parameters and fragments
* @param url - The URL to process
* @returns The URL with a trailing slash added to the pathname (if not already present)
*/
export function ensureTrailingSlash(url: string): string {
try {
const fallbackBaseUrl =
typeof window !== "undefined" && window.location.origin ? window.location.origin : "http://dummy.com";
// Handle relative URLs by creating a URL object with a fallback base URL
const urlObj = new URL(url, fallbackBaseUrl);
// Don't modify root path
if (urlObj.pathname === "/") {
return url;
}
// Add trailing slash if it doesn't exist
if (!urlObj.pathname.endsWith("/")) {
urlObj.pathname += "/";
}
// For relative URLs, return just the path + search + hash
if (url.startsWith("/")) {
return urlObj.pathname + urlObj.search + urlObj.hash;
}
// For absolute URLs, return the full URL
return urlObj.toString();
} catch (error) {
// If URL parsing fails, return the original URL
console.warn("Failed to parse URL for trailing slash enforcement:", url, error);
return url;
}
}
+20
View File
@@ -0,0 +1,20 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import React from "react";
// Minimal shim so code using next/image compiles under React Router + Vite
// without changing call sites. It just renders a native img.
type NextImageProps = React.ImgHTMLAttributes<HTMLImageElement> & {
src: string;
};
function Image({ src, alt = "", ...rest }: NextImageProps) {
return <img src={src} alt={alt} {...rest} />;
}
export default Image;
+23
View File
@@ -0,0 +1,23 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import React from "react";
import { Link as RRLink } from "react-router";
import { ensureTrailingSlash } from "./helper";
type NextLinkProps = React.ComponentProps<"a"> & {
href: string;
replace?: boolean;
prefetch?: boolean; // next.js prop, ignored
scroll?: boolean; // next.js prop, ignored
shallow?: boolean; // next.js prop, ignored
};
function Link({ href, replace, prefetch: _prefetch, scroll: _scroll, shallow: _shallow, ...rest }: NextLinkProps) {
return <RRLink to={ensureTrailingSlash(href)} replace={replace} {...rest} />;
}
export default Link;
+38
View File
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { useMemo } from "react";
import { useLocation, useNavigate, useSearchParams as useSearchParamsRR } from "react-router";
import { ensureTrailingSlash } from "./helper";
export function useRouter() {
const navigate = useNavigate();
return useMemo(
() => ({
push: (to: string) => navigate(ensureTrailingSlash(to)),
replace: (to: string) => navigate(ensureTrailingSlash(to), { replace: true }),
back: () => navigate(-1),
forward: () => navigate(1),
refresh: () => {
location.reload();
},
prefetch: async (_to: string) => {
// no-op in this shim
},
}),
[navigate]
);
}
export function usePathname(): string {
const { pathname } = useLocation();
return pathname;
}
export function useSearchParams(): URLSearchParams {
const [searchParams] = useSearchParamsRR();
return searchParams;
}
+42
View File
@@ -0,0 +1,42 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import React from "react";
import { Link } from "react-router";
// ui
import { Button } from "@plane/propel/button";
// images
import Image404 from "@/app/assets/images/404.svg?url";
function PageNotFound() {
return (
<div className={`h-screen w-full overflow-hidden bg-surface-1`}>
<div className="grid h-full place-items-center p-4">
<div className="space-y-8 text-center">
<div className="relative mx-auto h-60 w-60 lg:h-80 lg:w-80">
<img src={Image404} alt="404 - Page not found" className="h-full w-full object-contain" />
</div>
<div className="space-y-2">
<h3 className="text-16 font-semibold">Oops! Something went wrong.</h3>
<p className="text-13 text-secondary">
Sorry, the page you are looking for cannot be found. It may have been removed, had its name changed, or is
temporarily unavailable.
</p>
</div>
<Link to="/general/">
<span className="flex justify-center py-4">
<Button variant="secondary" size="lg">
Go to general settings
</Button>
</span>
</Link>
</div>
</div>
</div>
);
}
export default PageNotFound;
+39
View File
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as Sentry from "@sentry/react-router";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { HydratedRouter } from "react-router/dom";
Sentry.init({
dsn: process.env.VITE_SENTRY_DSN,
environment: process.env.VITE_SENTRY_ENVIRONMENT,
sendDefaultPii: process.env.VITE_SENTRY_SEND_DEFAULT_PII ? process.env.VITE_SENTRY_SEND_DEFAULT_PII === "1" : false,
release: process.env.VITE_APP_VERSION,
tracesSampleRate: process.env.VITE_SENTRY_TRACES_SAMPLE_RATE
? parseFloat(process.env.VITE_SENTRY_TRACES_SAMPLE_RATE)
: 0.1,
profilesSampleRate: process.env.VITE_SENTRY_PROFILES_SAMPLE_RATE
? parseFloat(process.env.VITE_SENTRY_PROFILES_SAMPLE_RATE)
: 0.1,
replaysSessionSampleRate: process.env.VITE_SENTRY_REPLAYS_SESSION_SAMPLE_RATE
? parseFloat(process.env.VITE_SENTRY_REPLAYS_SESSION_SAMPLE_RATE)
: 0.1,
replaysOnErrorSampleRate: process.env.VITE_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE
? parseFloat(process.env.VITE_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE)
: 1.0,
integrations: [],
});
startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<HydratedRouter />
</StrictMode>
);
});
-9
View File
@@ -1,9 +0,0 @@
"use client";
export default function RootErrorPage() {
return (
<div>
<p>Something went wrong.</p>
</div>
);
}
-39
View File
@@ -1,39 +0,0 @@
import { ReactNode } from "react";
import { Metadata } from "next";
// plane imports
import { ADMIN_BASE_PATH } from "@plane/constants";
// styles
import "@/styles/globals.css";
export const metadata: Metadata = {
title: "Plane | Simple, extensible, open-source project management tool.",
description:
"Open-source project management tool to manage work items, sprints, and product roadmaps with peace of mind.",
openGraph: {
title: "Plane | Simple, extensible, open-source project management tool.",
description:
"Open-source project management tool to manage work items, sprints, and product roadmaps with peace of mind.",
url: "https://plane.so/",
},
keywords:
"software development, customer feedback, software, accelerate, code management, release management, project management, work items tracking, agile, scrum, kanban, collaboration",
twitter: {
site: "@planepowers",
},
};
export default function RootLayout({ children }: { children: ReactNode }) {
const ASSET_PREFIX = ADMIN_BASE_PATH;
return (
<html lang="en">
<head>
<link rel="apple-touch-icon" sizes="180x180" href={`${ASSET_PREFIX}/favicon/apple-touch-icon.png`} />
<link rel="icon" type="image/png" sizes="32x32" href={`${ASSET_PREFIX}/favicon/favicon-32x32.png`} />
<link rel="icon" type="image/png" sizes="16x16" href={`${ASSET_PREFIX}/favicon/favicon-16x16.png`} />
<link rel="manifest" href={`${ASSET_PREFIX}/site.webmanifest.json`} />
<link rel="shortcut icon" href={`${ASSET_PREFIX}/favicon/favicon.ico`} />
</head>
<body className={`antialiased`}>{children}</body>
</html>
);
}
+102
View File
@@ -0,0 +1,102 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import type { ReactNode } from "react";
import { Links, Meta, Outlet, Scripts } from "react-router";
import type { LinksFunction } from "react-router";
import * as Sentry from "@sentry/react-router";
import appleTouchIcon from "@/app/assets/favicon/apple-touch-icon.png?url";
import favicon16 from "@/app/assets/favicon/favicon-16x16.png?url";
import favicon32 from "@/app/assets/favicon/favicon-32x32.png?url";
import faviconIco from "@/app/assets/favicon/favicon.ico?url";
import { LogoSpinner } from "@/components/common/logo-spinner";
import globalStyles from "@/styles/globals.css?url";
import { AppProviders } from "@/providers";
import type { Route } from "./+types/root";
// fonts
import "@fontsource-variable/inter";
import interVariableWoff2 from "@fontsource-variable/inter/files/inter-latin-wght-normal.woff2?url";
import "@fontsource/material-symbols-rounded";
import "@fontsource/ibm-plex-mono";
const APP_TITLE = "Plane | Simple, extensible, open-source project management tool.";
const APP_DESCRIPTION =
"Open-source project management tool to manage work items, sprints, and product roadmaps with peace of mind.";
export const links: LinksFunction = () => [
{ rel: "apple-touch-icon", sizes: "180x180", href: appleTouchIcon },
{ rel: "icon", type: "image/png", sizes: "32x32", href: favicon32 },
{ rel: "icon", type: "image/png", sizes: "16x16", href: favicon16 },
{ rel: "shortcut icon", href: faviconIco },
{ rel: "manifest", href: `/site.webmanifest.json` },
{ rel: "stylesheet", href: globalStyles },
{
rel: "preload",
href: interVariableWoff2,
as: "font",
type: "font/woff2",
crossOrigin: "anonymous",
},
];
export function Layout({ children }: { children: ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body className="antialiased" suppressHydrationWarning>
<AppProviders>{children}</AppProviders>
<Scripts />
</body>
</html>
);
}
export const meta: Route.MetaFunction = () => [
{ title: APP_TITLE },
{ name: "description", content: APP_DESCRIPTION },
{ property: "og:title", content: APP_TITLE },
{ property: "og:description", content: APP_DESCRIPTION },
{ property: "og:url", content: "https://plane.so/" },
{
name: "keywords",
content:
"software development, customer feedback, software, accelerate, code management, release management, project management, work items tracking, agile, scrum, kanban, collaboration",
},
{ name: "twitter:site", content: "@planepowers" },
];
export default function Root() {
return (
<div className="bg-canvas min-h-screen">
<Outlet />
</div>
);
}
export function HydrateFallback() {
return (
<div className="relative flex h-screen w-full items-center justify-center">
<LogoSpinner />
</div>
);
}
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
if (error) {
Sentry.captureException(error);
}
return (
<div>
<p>Something went wrong.</p>
</div>
);
}
+27
View File
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { index, layout, route } from "@react-router/dev/routes";
import type { RouteConfig } from "@react-router/dev/routes";
export default [
layout("./(all)/(home)/layout.tsx", [index("./(all)/(home)/page.tsx")]),
layout("./(all)/(dashboard)/layout.tsx", [
route("general", "./(all)/(dashboard)/general/page.tsx"),
route("workspace", "./(all)/(dashboard)/workspace/page.tsx"),
route("workspace/create", "./(all)/(dashboard)/workspace/create/page.tsx"),
route("email", "./(all)/(dashboard)/email/page.tsx"),
route("authentication", "./(all)/(dashboard)/authentication/page.tsx"),
route("authentication/github", "./(all)/(dashboard)/authentication/github/page.tsx"),
route("authentication/gitlab", "./(all)/(dashboard)/authentication/gitlab/page.tsx"),
route("authentication/google", "./(all)/(dashboard)/authentication/google/page.tsx"),
route("authentication/gitea", "./(all)/(dashboard)/authentication/gitea/page.tsx"),
route("ai", "./(all)/(dashboard)/ai/page.tsx"),
route("image", "./(all)/(dashboard)/image/page.tsx"),
]),
// Catch-all route for 404 handling - must be last
route("*", "./components/404.tsx"),
] satisfies RouteConfig;

Some files were not shown because too many files have changed in this diff Show More