Compare commits

...

515 Commits

Author SHA1 Message Date
0ca66a1d94
fix: misc changes 2024-07-02 13:34:31 -05:00
34d35a8da0
fix: deploy on fly, migrate to overmind 2024-07-02 13:33:53 -05:00
37c51cfca5
fix: better layering in docker image 2024-07-02 13:33:53 -05:00
585d9a1d1b
feat: monoimage 2024-07-02 13:33:52 -05:00
f3d05fda62
fix: gitignore 2024-07-02 13:33:35 -05:00
sriram veeraghanta
c76af7d7d6
Merge pull request #4688 from makeplane/preview
release: v0.21-dev
2024-06-03 18:54:06 +05:30
sriram veeraghanta
97eea75cb7 chore: update package version 2024-06-03 18:41:01 +05:30
Prateek Shourya
ddfd953408
fix: inbox issue store update logic. (#4683) 2024-06-03 14:32:18 +05:30
guru_sainath
a428bc16c4
[WEB-1492] fix: resolved issue creation error in layouts while group_by and sub_group_by filters applied in quick add (#4682)
* fix: resolved issue creation error in layouts while group_by and sub_group_by filters applied in quick add

* fix: updated braces in conditions
2024-06-03 14:27:39 +05:30
Nikhil
5322c0e57b
fix: instance register script (#4681)
* fix: instance register script

* dev: remove api key and add latest version and current version in types
2024-06-03 12:44:40 +05:30
sriram veeraghanta
81dfc15d1f fix: instance serializer 2024-06-01 01:04:19 +05:30
Nikhil
6a00fcc253
[WEB - 1505] chore: alter instance id field (#4676)
* chore: instance id

* dev: update to max length
2024-06-01 00:09:27 +05:30
Nikhil
f96e76dbbc
[WEB - 1500] chore: add extra fields on instance and create changelog table to store release change logs (#4673)
* chore: add extra fields on instance and create changelog table to store release change logs

* dev: rename new_version to latest_version
2024-05-31 23:39:13 +05:30
Aaryan Khandelwal
de7dad59f0
fix: ai buttons overlapping issue (#4621) 2024-05-31 20:28:28 +05:30
Anmol Singh Bhatia
1c901446ab
fix: checkbox ui component (#4675) 2024-05-31 20:21:00 +05:30
Prateek Shourya
e7d6e7d575
[WEB-1440] chore: update cycle empty state to use project level access. (#4672) 2024-05-31 18:30:57 +05:30
Prateek Shourya
a2cdbd52dc
[WEB-1436] chore: pages improvement. (#4657)
* add empty state if no pages are available.
* set access to private in create page modal when the modal is open form private tab.
2024-05-31 18:30:38 +05:30
Aaryan Khandelwal
608e193c36
chore: added primary variant to the alert modal (#4664) 2024-05-31 17:40:21 +05:30
Aaryan Khandelwal
830f0861c1
chore: created a new constant for archivable state groups (#4668) 2024-05-31 17:39:23 +05:30
Aaryan Khandelwal
98ebe88c86
[WEB-1501] dev: multiple select core components (#4667)
* dev: multiple select core components

* chore: added export statement
2024-05-31 17:37:24 +05:30
Aaryan Khandelwal
c8c86a33f8
chore: added a prop to render default state conditionally (#4669) 2024-05-31 17:36:12 +05:30
Aaryan Khandelwal
ba4798deb9
chore: created new constants for marketing website page links (#4670) 2024-05-31 17:30:50 +05:30
Aaryan Khandelwal
463d0732e9
chore: added buttonClassName prop to label dropdown (#4671) 2024-05-31 17:30:06 +05:30
Aaryan Khandelwal
a8184c366a
chore: priority dropdown accepts undefined (#4666) 2024-05-31 15:14:13 +05:30
Prateek Shourya
0a105a1c21
[WEB-1325] chore: refactor inbox issue store to avoid data loss. (#4640)
* [WEB-1325] chore: refactor inbox issue store to avoid data loss.

* chore: inbox store improvement.
2024-05-31 15:10:38 +05:30
Aaryan Khandelwal
bf4f97d7f6
refactor: checkbox ui component (#4665) 2024-05-31 15:05:28 +05:30
Aaryan Khandelwal
a9d9cbcb72
refactor: drag handle component (#4663) 2024-05-31 14:59:49 +05:30
sriram veeraghanta
092e65b43d
[WEB-1424] chore: page and view logo implementation, and emoji/icon (#4662)
* [WEB-1424] chore: page and view logo implementation, and emoji/icon picker improvement (#4583)

* chore: added logo_props

* chore: logo props in cycles, views and modules

* chore: emoji icon picker types updated

* chore: info icon added to plane ui package

* chore: icon color adjust helper function added

* style: icon picker ui improvement and default color options updated

* chore: update page logo action added in store

* chore: emoji code to unicode helper function added

* chore: common logo renderer component added

* chore: app header project logo updated

* chore: project logo updated across platform

* chore: page logo picker added

* chore: control link component improvement

* chore: list item improvement

* chore: emoji picker component updated

* chore: space app and package logo prop type updated

* chore: migration

* chore: logo added to project view

* chore: page logo picker added in create modal and breadcrumbs

* chore: view logo picker added in create modal and updated breadcrumbs

* fix: build error

* chore: AIO docker images for preview deployments (#4605)

* fix: adding single docker base file

* action added

* fix action

* dockerfile.base modified

* action fix

* dockerfile

* fix: base aio dockerfile

* fix: dockerfile.base

* fix: dockerfile base

* fix: modified folder structure

* fix: action

* fix: dockerfile

* fix: dockerfile.base

* fix: supervisor file name changed

* fix: base dockerfile updated

* fix dockerfile base

* fix: base dockerfile

* fix: docker files

* fix: base dockerfile

* update base image

* modified docker aio base

* aio base modified to debian-12-slim

* fixes

* finalize the dockerfiles with volume exposure

* modified the aio build and dockerfile

* fix: codacy suggestions implemented

* fix: codacy fix

* update aio build action

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>

* fix: merge conflict

* chore: lucide react added to planu ui package

* chore: new emoji picker component added with lucid icon and code refactor

* chore: logo component updated

* chore: emoji picker updated for pages and views

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: Manish Gupta <59428681+mguptahub@users.noreply.github.com>
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>

* fix: build error

---------

Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: Manish Gupta <59428681+mguptahub@users.noreply.github.com>
Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
2024-05-31 14:27:52 +05:30
Anmol Singh Bhatia
fc4ba5a170
[WEB-1235] chore: module and cycle sidebar graph improvement (#4650)
* chore: module and cycle sidebar graph improvement

* chore: code refactor
2024-05-31 13:57:46 +05:30
sriram veeraghanta
9143e5abc8 fix: build errors 2024-05-31 13:32:21 +05:30
Prateek Shourya
1cb26fa863
[WEB-1216] chore: increase module empty state for consistency. (#4658) 2024-05-31 12:42:36 +05:30
Prateek Shourya
9ff3c22089
[WEB-1419] chore: enable module creation with dates older than today. (#4659) 2024-05-31 12:38:45 +05:30
Anmol Singh Bhatia
653b1a7b30
fix: project state setting state name remove camel case logic (#4652) 2024-05-31 12:27:25 +05:30
Anmol Singh Bhatia
d27590cd49
[WEB-1493] chore: product tour asset and app sidebar quick action hover (#4655)
* chore: product tour asset updated

* fix: app sidebar quick action hover
2024-05-31 12:24:16 +05:30
Anmol Singh Bhatia
3cbc1dcf10
fix: email notification preferences (#4656) 2024-05-31 12:18:57 +05:30
M. Palanikannan
4d9cd0c318
fix: negate check while trying to discard (#4653) 2024-05-30 17:53:49 +05:30
Nikhil
87de913c76
[WEB - 1482] fix: uploads when using block storages other than s3 and minio (#4647)
* fix: minio storage and redirection

* dev: disconnect web url and app base url configuration.
2024-05-30 16:22:47 +05:30
Prateek Shourya
b016e1d1b5
[WEB-1467] chore: run the API's required to bootstrap the application in parallel. (#4642) 2024-05-30 16:20:58 +05:30
Aaryan Khandelwal
67bd14ceb5
chore: remove enter key extension (#4648) 2024-05-30 15:37:25 +05:30
Anmol Singh Bhatia
4091e61953
fix: notification mark all as read (#4643) 2024-05-30 12:00:55 +05:30
M. Palanikannan
ade6eded69
[WEB-1244] fix: add better image insertion and replacement logic in the editor (#4508)
* fix: add better image insertion and replacement logic

* refactor: image handling in editor

* chore: remove passing uploadKey around

* refactor: remove unused code

* fix: redundant files removed

* fix: add is editor ready to discard api to control behvaiours from our app

* fix: focus issues and image insertion position when not using slash command

* fix: import order fixed
2024-05-29 18:25:03 +05:30
Prateek Shourya
061a447734
[WEB-1445] fix: issue creation on sub groups when cycle/ module grouping is applied. (#4636) 2024-05-29 18:22:08 +05:30
Prateek Shourya
10ef4e657f
[WEB-1465] fix: theme fluctuation on initial load. (#4638) 2024-05-29 18:21:33 +05:30
Aaryan Khandelwal
8a30c2c484
[WEB-1480] fix: preserve page access when making a copy (#4568) 2024-05-29 18:19:50 +05:30
rahulramesha
6636a64817
[WEB-1374] fix: clear changes made on modal close (#4555) 2024-05-29 18:18:47 +05:30
Nikhil
571a3d1239
fix: remove issue duplicated when adding multiple modules (#4637) 2024-05-29 13:31:32 +05:30
Manish Gupta
49e65fbcb3
modified the actions to build images correctly (#4635) 2024-05-29 12:40:08 +05:30
Prateek Shourya
83c8338c64
[WEB-1476] style: fix padding on project icon in workspace sidebar. (#4631) 2024-05-28 20:53:19 +05:30
Nikhil
26ba4a409b
dev: activate user command (#4628) 2024-05-28 17:30:38 +05:30
Manish Gupta
a8c03281c6 chore: AIO docker images for preview deployments (#4605)
* fix: adding single docker base file

* action added

* fix action

* dockerfile.base modified

* action fix

* dockerfile

* fix: base aio dockerfile

* fix: dockerfile.base

* fix: dockerfile base

* fix: modified folder structure

* fix: action

* fix: dockerfile

* fix: dockerfile.base

* fix: supervisor file name changed

* fix: base dockerfile updated

* fix dockerfile base

* fix: base dockerfile

* fix: docker files

* fix: base dockerfile

* update base image

* modified docker aio base

* aio base modified to debian-12-slim

* fixes

* finalize the dockerfiles with volume exposure

* modified the aio build and dockerfile

* fix: codacy suggestions implemented

* fix: codacy fix

* update aio build action

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2024-05-28 17:28:02 +05:30
Anmol Singh Bhatia
5efa8264d8
fix: app sidebar project list dnd disabled when sidebar is collapsed (#4623) 2024-05-28 16:49:39 +05:30
sriram veeraghanta
c87749cbda fix: build errors 2024-05-28 15:11:54 +05:30
Nikhil
36b82a7776
[WEB - 1438] dev: oauth exception handling (#4602)
* dev: oauth exception handling

* dev: reset password on deactivation
2024-05-28 13:39:27 +05:30
Aaryan Khandelwal
ff03c0b718 [WEB-1322] dev: conflict free pages collaboration (#4463)
* chore: pages realtime

* chore: empty binary response

* chore: added a ypy package

* feat: pages collaboration

* chore: update fetching logic

* chore: degrade ypy version

* chore: replace useEffect fetch logic with useSWR

* chore: move all the update logic to the page store

* refactor: remove react-hook-form

* chore: save description_html as well

* chore: migrate old data logic

* fix: added description_binary as field name

* fix: code cleanup

* refactor: create separate hook to handle page description

* fix: build errors

* chore: combine updates instead of using the whole document

* chore: removed ypy package

* chore: added conflict resolving logic to the client side

* chore: add a save changes button

* chore: add read-only validation

* chore: remove saving state information

* chore: added permission class

* chore: removed the migration file

* chore: corrected the model field

* chore: rename pageStore to page

* chore: update collaboration provider

* chore: add try catch to handle error

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-05-28 13:10:03 +05:30
sriram veeraghanta
a04ce5abfc fix: posthog proxy config using rewrites 2024-05-28 13:08:37 +05:30
Anmol Singh Bhatia
e47ab573a7
chore: analytics tab enhancement (#4615) 2024-05-28 11:53:57 +05:30
Anmol Singh Bhatia
3f18e2fabc
fix: profile issue kanban group collapse and expand toggle (#4612) 2024-05-28 11:53:24 +05:30
Anmol Singh Bhatia
bfd6fb00a8
chore: user activity title update message updated (#4616) 2024-05-28 11:50:57 +05:30
Prateek Shourya
1d7b3efb80
[WEB-1148] chore: icons updates for consistency across platform. (#4571)
* [WEB-1148] chore: icons updates for consistency across platform.

* chore: update logic for rendering custom lead icon.

* chore: update Icon prop name.

* chore: update `Icon` prop to `icon`.
2024-05-28 11:50:04 +05:30
Prateek Shourya
c9586bfdcf
style: fix all empty states size of inconsistency. (#4613) 2024-05-28 11:49:14 +05:30
Anmol Singh Bhatia
05807fe123
[WEB-1067] chore: enter key entension added to editor package and issue modal description improvement (#4617)
* chore: enter key extension added to RichTextEditorWithRef editor package

* chore: enter to submit functionality added to issue and inbox issue modal description
2024-05-28 11:48:20 +05:30
Anmol Singh Bhatia
a8fcaf1f48
fix: project empty state flicker (#4609) 2024-05-27 19:25:31 +05:30
Anmol Singh Bhatia
6825f8a386
[WEB-1395] chore: project active cycle ui enhancements (#4608)
* chore: list item component improvement

* chore: project cycle ui enhancement
2024-05-27 19:24:11 +05:30
rahulramesha
b93fa4a340
[WEB-1416] chore: Refactor project sidebar dnd and remove @hello-pangea dnd (#4581)
* upgrade cmdk version to 1.0 to fix a critical issue

* project side bar dnd

* add some comments

* slight logic change for highlighting on drop
2024-05-27 19:20:26 +05:30
Anmol Singh Bhatia
44f743d52c
chore: cycle and module sidebar state filter implementation (#4522) 2024-05-27 17:51:13 +05:30
sriram veeraghanta
8730049c00 fix: updated deploy docker compose file with restart no with quotes 2024-05-27 16:51:05 +05:30
Anmol Singh Bhatia
2e6ad61f49
fix: sibling issue redirection and fetching issue resolved (#4603) 2024-05-27 16:04:05 +05:30
sriram veeraghanta
aaf3484ee5 fix: sentry config update for space app 2024-05-27 15:21:41 +05:30
sriram veeraghanta
87ef4eecb1 fix: sentry config update for web app 2024-05-27 15:02:03 +05:30
Prateek Shourya
2b2f667868
[WEB-1432] fix: redirection to parent issue detail page when it is from another project. (#4601) 2024-05-27 13:50:51 +05:30
Prateek Shourya
dbba991dd3
chore: send test email error message update and scrollbar improvement. (#4589) 2024-05-24 21:15:24 +05:30
Anmol Singh Bhatia
f76ca5643e
fix: issue store sub issue retrieve project id (#4588) 2024-05-24 21:14:34 +05:30
Aaryan Khandelwal
99e3097122
fix: update theme post sign-in (#4586) 2024-05-24 20:12:40 +05:30
Prateek Shourya
55148ab3f7
fix: redirection to /god-mode. (#4587) 2024-05-24 20:04:00 +05:30
rahulramesha
f0ece1c6b7
optionally chain sub issue count (#4585) 2024-05-24 19:39:28 +05:30
Nikhil
c7996544b4
fix: server error on email password disabled (#4584) 2024-05-24 18:43:37 +05:30
Prateek Shourya
724f227842
[WEB-1404] chore: admin app improvements (#4580)
* [WEB-1404] fix: redirection to web app and issue with telemetry checkbox in setup form.

* chore: add scrollbar in admin app.

* chore: fix `workspaces_exist` logic in instance api.
2024-05-24 16:59:49 +05:30
rahulramesha
7a47ce9d1d
[WEB-1398] fix: quick add issue flicker for cycle and module grouping (#4579)
* utilize optimistic updates to fix quick add issue flicker

* add comments
2024-05-24 15:59:41 +05:30
Aaryan Khandelwal
9f573d4299
[WEB-1415] fix: issue attachment count mutation (#4567)
* fix: attachment count mutation

* fix: attachment count update logic
2024-05-24 14:33:30 +05:30
Prateek Shourya
571d35cd8b
chore: fix breadcrumbs inconsistency. (#4574) 2024-05-24 14:20:56 +05:30
Prateek Shourya
4bb4609833
[WEB-1413] chore: update label creation toast error message. (#4572) 2024-05-24 13:56:20 +05:30
Prateek Shourya
f5d95ba3a1
chore: minor improvement in admin service provider field. (#4577) 2024-05-24 13:52:37 +05:30
Prateek Shourya
3ef67acb92
[WEB-1417] chore: fix size updating issue on create workspace form when multiple errors are shown. (#4575) 2024-05-24 13:52:22 +05:30
sriram veeraghanta
1dcea9bcc8
Merge pull request #4569 from makeplane/preview
release: v0.20-dev
2024-05-23 19:55:06 +05:30
Nikhil
b57432818d
[WEB - 1408] dev: add logo prop and accounts migration (#4558)
* dev: add logo prop and accounts migration

* dev: add default values for id_token

* dev: update is_active as read only field

* dev: delete all sessions when deactivating account

* dev: add issue description binary

* dev: add logo props for team
2024-05-23 18:08:35 +05:30
Prateek Shourya
6a3c4eb512
[WEB-1216] chore: update module empty state size for consistency. (#4565) 2024-05-23 16:47:34 +05:30
Prateek Shourya
ddc28d37d3
[WEB-1404] chore: auth and onboarding improvements (#4564)
* chore: `switch account` modal revamp.

* chore: workspace invitation page message display logic update.

* chore: update `showDeleteAccountModal` state to `showSwitchAccountModal`.
2024-05-23 16:45:23 +05:30
Anmol Singh Bhatia
073d453752
chore: plane logo asset updated (#4562) 2024-05-23 14:45:40 +05:30
Prateek Shourya
7089474c11
[WEB-1240] chore: toast messages updates (#4561)
* [WEB-1240] fix: toast messages inconsistency in sub-issues.

* [WEB-1241] chore: update draft issue creation toast message.

* chore: minor logic improvement.
2024-05-23 14:28:11 +05:30
Prateek Shourya
780caf59a0
[WEB-1404] chore: space app infinite loader and scroll fix. (#4559)
* [WEB-1404] chore: space app infinite loader and scroll fix.

* chore: revert back `isLoading` initail state to `true` in user store.
2024-05-23 14:27:16 +05:30
guru_sainath
c9e6ead3af
chore: handled suppert email validation in the admin app (#4560) 2024-05-23 14:04:53 +05:30
sriram veeraghanta
d17492319b fix: updating docker urls and base paths 2024-05-23 13:38:20 +05:30
sriram veeraghanta
c26c8cfe19 fix: nginx conf file updated by removing rewrites for space location 2024-05-22 20:52:02 +05:30
Prateek Shourya
2b27f39727
[WEB-1380] chore: fix sidebar expanding issue on sign out. (#4557)
* [WEB-1380] chore: fix sidebar expanding issue on sign out.

* chore: update sign-out redirect url.

* dev: update signout view and fix the entrypoint on docker-compose-local

* chore: remove localStorage reset logic for `app_sidebar_collapsed` on sign-out.

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
2024-05-22 20:39:31 +05:30
Anmol Singh Bhatia
577996b34a
chore: space app logo redirection updated (#4556) 2024-05-22 20:24:47 +05:30
rahulramesha
2a83ac7376
upgrade cmdk version to 1.0 to fix a critical issue (#4554) 2024-05-22 18:53:40 +05:30
Anmol Singh Bhatia
5381d0bc35
[WEB-1404] chore: auth improvements (#4553)
* chore: auth page logo and tab head title updated

* chore: auth page logo and tab head title updated

* chore: code refactor

* chore: space app existing user auth validation
2024-05-22 18:49:58 +05:30
Prateek Shourya
63a6be2143
[WEB-1404] chore: auth ui / ux fixes (#4552)
* chore: update deactivated account alert message to show support email if available in env.

* chore: clear error_info on email check.

* chore: fix log-in / sign-up forms alignment and minor ux copy fix.

* fix: auth redirection to `/sign-in` issue.

* chore: update `back to sign in` url in forgot password screen.
2024-05-22 17:31:56 +05:30
rahulramesha
ca73a11868
improve error overlay while dragging over a group (#4551) 2024-05-22 17:15:37 +05:30
Bavisetti Narayan
794183b640
[WEB-1343] chore: add and remove modules in kanban view (#4549)
* chore: removing and adding an issue to module

* chore: removed empty module validation

* modules single API call

* chore: removed the script

---------

Co-authored-by: rahulramesha <rahulramesham@gmail.com>
2024-05-22 17:07:08 +05:30
Satish Gandham
e99a7accec
WEB-1344 chore: Add storybook (#4490)
* Add storybbok integration for UI package

* Exclude stories from tailwind config

* Update gitignore

* chore: disable lint stage

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2024-05-22 16:36:31 +05:30
rahulramesha
c0961586a3
change color of quick add button (#4541) 2024-05-22 16:00:19 +05:30
Satish Gandham
6236adf4bc
[WEB-1390] chore: Add pre commit hook to check for lint and formatting errors (#4537)
* Add pre commit hook

* Add comments
2024-05-22 15:50:15 +05:30
Prateek Shourya
b084844565
[WEB-1401] chore: toast refactor in space app. (#4546)
* [WEB-1401] chore: toast refactor in space app.

* fix: build errors in space app.
2024-05-22 15:39:28 +05:30
Prateek Shourya
1912f6948c
[WEB-1386] chore: fix update view button to right even if no filters are applied. (#4548) 2024-05-22 15:37:19 +05:30
rahulramesha
fa332a9ba7
fix infinite loop for sub issues (#4550) 2024-05-22 15:36:11 +05:30
sriram veeraghanta
9013497a5a
fix: authentication views updated with new workflow (#4547)
* dev: update email check endpoint

* fix: auth magic login check

* chore: updated the error code handler and handled authentication workflow

* dev: add magic link login

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: guru_sainath <gurusainath007@gmail.com>
2024-05-22 14:49:06 +05:30
sriram veeraghanta
509d5fe554 fix: docker entrypoint 2024-05-22 13:00:45 +05:30
sriram veeraghanta
9591ccccaa fix: create sync pr workflow 2024-05-22 12:48:47 +05:30
sriram veeraghanta
a644d38218 chore: docker entry file name changes 2024-05-22 12:46:41 +05:30
Aaryan Khandelwal
639d24bd5a
[WEB-1262] refactor: custom hook for common dropdown logic (#4420)
* refactor: custom hook for common dropdown logic

* chore: clear query for label dropdowns
2024-05-22 12:45:51 +05:30
Prateek Shourya
f13c190676
[WEB-1396] chore: remove/ disable all actionable items from deploy url in case url is embedded using Iframe. (#4544)
* fix: is in iframe validation check

* chore: remove/ disable all actionable items from deploy url in case url is embeded using Iframe.

* chore: remove copy issue link option if clipboard write access is not granted.

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2024-05-22 12:39:34 +05:30
Prateek Shourya
0c80cf3d54
fix: enable toast in admin app. (#4545) 2024-05-22 12:34:57 +05:30
rahulramesha
6eca4a1f1f
fix unassigned issues count in analytics page (#4542) 2024-05-22 12:33:36 +05:30
sriram veeraghanta
1c377163d2 fix: converting takeoff scripts to docker entry points 2024-05-22 12:25:27 +05:30
rahulramesha
e6d626fbc5
fix list view visual alignments (#4543) 2024-05-22 11:34:15 +05:30
Prateek Shourya
709cd9dd6c
[WEB-1177] fix: sub-issues count mutation. (#4516)
* [WEB-1177] fix: sub-issues count mutation.

* chore: refactor sub_issues_count mutation logic.

* fix: build errors.
2024-05-21 17:14:41 +05:30
Prateek Shourya
8a960e269f
[WEB-1063] style: fix activity/ comment overflow issue. (#4540) 2024-05-21 16:36:33 +05:30
Anmol Singh Bhatia
846991332a
[WEB-1385] style: oauth button enhancement (#4539)
* style: oauth button enhancement

* style: space app applied issue filter section styling updated

* style: space app sidebar icon consistency

* chore: issue title input improvement

* fix: create workspace and invite workspace theme issue

* fix: member invite modal improvement
2024-05-21 16:29:30 +05:30
rahulramesha
afc2ca65cf
[WEB-1138] feat: List lssue Layout Drag and Drop (#4536)
* List Dnd Complete feature

* fix minor bugs in list dnd

* remove double overlay in kanban post refactor

* add missing dependencies to useEffects

* make provision to add to the last issue of the group

* show current child issues to also be disabled if the parent issue is being dragged

* fix last issue border

* fix code static analysis suggestions

* prevent context menu on drag handle
2024-05-21 16:25:57 +05:30
Nikhil
0f5294c5e2
[WEB - 1387]dev: custom csrf failure view (#4531)
* dev: custom csrf view

* dev: update template to use only css for styling
2024-05-21 15:04:10 +05:30
Nikhil
410f04c327
dev: rest framework throttling (#4534) 2024-05-21 15:03:01 +05:30
guru_sainath
4feec35773
[WEB-1301] chore: handled issues count in project, module, and cycle issues (#4538)
* chore: handled issues count in project, module, and cycle issues

* chore: changed the typo from getIssuesCount to issuesCount
2024-05-21 14:55:29 +05:30
sriram veeraghanta
e2ac60e259 fix: adding redirect rules to old signup page 2024-05-21 13:43:54 +05:30
Anmol Singh Bhatia
f77761b4f9
[WEB-1385] chore: auth screen and space app improvement (#4529)
* chore: instance sign in page alginment

* chore: strength indicator color updated

* chore: confirm password input improvement

* chore: space issue sidebar comment section validation added
2024-05-20 19:03:23 +05:30
sriram veeraghanta
c58e241159 chore: update package version 2024-05-20 16:18:11 +05:30
guru_sainath
4c5d66d6d1
fix: onboarded steps validation in web app (#4527) 2024-05-20 16:12:56 +05:30
guru_sainath
1355873e32
fix: next_path enabled for oauth auth (#4526) 2024-05-20 16:00:32 +05:30
Anmol Singh Bhatia
836452f074
[WEB-1319] chore: user default cover image (#4525)
* chore: user default cover image updated

* chore: space app issue sidebar coment section heading updated
2024-05-20 15:43:48 +05:30
rahulramesha
3bfa8f5f88
revert the commented out code, fixing the subIssue mutation (#4524) 2024-05-20 15:42:55 +05:30
Anmol Singh Bhatia
f8a443d6a6
chore: reset password error handling (#4523) 2024-05-20 14:50:11 +05:30
Nikhil
35f3716cb5
fix: magic code 500 error on wrong code or expired code (#4521) 2024-05-20 12:56:29 +05:30
sriram veeraghanta
193076636a fix: instance god-mode redirection 2024-05-20 12:53:43 +05:30
Nikhil
efe62bccf3
chore: delete workspace invitation on user deactivation (#4514) 2024-05-20 12:29:49 +05:30
Nikhil
e951cc3cf4
dev: add pg port to configuration (#4517) 2024-05-20 12:29:03 +05:30
guru_sainath
2138257da0
[WEB-1319] fix: handled magic sign_in and sign_up error codes in authentication (#4518)
* dev: differentiate error codes for magic code

* fix: handled auth error_codes for magic_sign_in and magic_sign_up

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
2024-05-20 12:23:48 +05:30
guru_sainath
603ebeb123
chore: handled the auto reload issue in auth-wrapper and instance wrapper (#4520) 2024-05-20 12:10:54 +05:30
Anmol Singh Bhatia
b4b111e297
[WEB-1319] chore: auth fixes and enhancement (#4519)
* chore: onboarding invite member button overflow fix

* chore: change password enhancement
2024-05-20 12:05:19 +05:30
rahulramesha
edf8109735
[WEB-1249] chore: New Kanban column design changes (#4509)
* new Kanban column design changes

* fix minor build error
2024-05-20 11:38:28 +05:30
Prateek Shourya
fe4dea1474
[WEB-1361] fix: comments access specifier mutation issue. (#4515) 2024-05-20 11:32:00 +05:30
Aaryan Khandelwal
915ea8a81c
fix: page title not displaying (#4513) 2024-05-19 21:40:50 +05:30
sriram veeraghanta
87610399c4 Merge branch 'fix/instance-layouts' of github.com:makeplane/plane into preview 2024-05-18 16:25:51 +05:30
sriram veeraghanta
31ca9e447d fix: instance config errors 2024-05-18 16:22:53 +05:30
Anmol Singh Bhatia
895fbcd5a7 chore: auth mobile screen responsiveness 2024-05-17 20:43:54 +05:30
sriram veeraghanta
69ce0031d0 fix: instance admin layout 2024-05-17 20:32:40 +05:30
Aaryan Khandelwal
1178c3b14d
fix: show untitle for blank page titles (#4505) 2024-05-17 19:40:12 +05:30
Nikhil
cbca2c78ee
[WEB - 1370] dev: remove session save and add callback for providers (#4506)
* dev: remove session save and add callback for providers

* dev: make session save every request as env variable
2024-05-17 19:39:40 +05:30
guru_sainath
6f05ec7e74
chore: after signout intead of redirecting to login page we are navigating to the same page where he logged out (#4507) 2024-05-17 19:38:48 +05:30
Bavisetti Narayan
e3e7c99e11
chore: user favorite sequence (#4501) 2024-05-17 18:51:47 +05:30
Bavisetti Narayan
fc31186aec
chore: snoozed inbox issue (#4487) 2024-05-17 18:19:17 +05:30
guru_sainath
564625ee22
fix: handled workspaceSlug and projectId in issue voting component (#4503) 2024-05-17 17:51:14 +05:30
Nikhil
a150a9d268
fix: cache invalidation on set password (#4504) 2024-05-17 17:49:35 +05:30
guru_sainath
1bf80847f5
fix: resolved build errors and implemented signout button (#4502) 2024-05-17 16:54:58 +05:30
Nikhil
85b54d2490
dev: migrations (#4489)
* dev: estimates and pages migrations

* dev: favorite and user migrations

* chore: workspace base model

* chore: workspace user properties

* chore: removed unused variables

* chore: favorite view set changes

* chore: default sequence id

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-05-17 15:59:55 +05:30
Anmol Singh Bhatia
38f5ecbdf2
[WEB-1330] chore: show password toggle improvement (#4471)
* chore: show password toggle improvement

* fix: merge conflict
2024-05-17 15:51:34 +05:30
Nikhil
8d860396bd
dev: auth callback for runing user post authentication workflows (#4498) 2024-05-17 15:27:34 +05:30
Samuel Torres
61e83ed808
Fix state patch api search by external id (#4495) 2024-05-17 15:26:10 +05:30
Manish Gupta
f7ccf01426
fix: selfhost back to tar.gz (#4500)
* fix: selfhost back to tar.gz

* fix
2024-05-17 15:24:21 +05:30
rahulramesha
90b50a4162
New Minor UX changes to Kanban (#4499) 2024-05-17 14:33:21 +05:30
guru_sainath
2988d5e429
[WEB-1319] chore: handled redirection when user is not logged in (#4497)
* chore: handled redirection when user is not logged in

* dev: handle url redirection in space app

* dev: remove user from redis on successful code matching
2024-05-17 14:27:49 +05:30
Aaryan Khandelwal
c2e293cf3b
[WEB-1310] chore: page title can be blank (#4486)
* chore: page title can be blank

* chore: handle undefined page name in the helper function
2024-05-17 12:56:44 +05:30
Aaryan Khandelwal
4c16ed8b23
[WEB-1336] fix: issue dates conflict in the calendar layout (#4480)
* fix: calendar dnd for due dates before issue start date

* chore: start date in calender view

* fix: add existing issues to calendar layout

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-05-17 12:45:28 +05:30
Anmol Singh Bhatia
f9de1e790c
chore: nested issue highlighting improvement (#4488) 2024-05-17 12:17:43 +05:30
Aaryan Khandelwal
c054b18ad5
chore: prevent route change on canceling confirm dialog (#4493) 2024-05-17 11:36:50 +05:30
Aaryan Khandelwal
8fae076fd6
chore: disccard changes made after creating issue (#4484) 2024-05-17 11:35:37 +05:30
sriram veeraghanta
8522db630e fix: build errors 2024-05-17 00:32:23 +05:30
Aaryan Khandelwal
f547692fe6
chore: hide bot accounts from the collabortors list (#4482) 2024-05-16 23:59:28 +05:30
Anmol Singh Bhatia
0c04c3dc14
chore: unwanted loader (#4485) 2024-05-16 23:57:13 +05:30
Aaryan Khandelwal
26188f208b
[WEB-1204] fix: Kanban and calendar drag and drop in mobile (#4408)
* chore: don't show context menu on mobile devices

* fix: drag and drop in mobile

* chore: default show more options in mobile

* fix: dnd in calendar layout
2024-05-16 23:42:54 +05:30
Anmol Singh Bhatia
33079c826d
[WEB-1319] chore: instance not ready pages improvement (#4492)
* chore: instance not ready pages improvement

* fix: magic sign up

* chore: issue mutation spinner and command k spinner updated

* chore: forgot password email input disabled

* chore: forgot password email input disabled revert

* chore: unused asset removed
2024-05-16 23:41:30 +05:30
Anmol Singh Bhatia
9bf1863f33
[WEB-1345] chore: auth and loader responsiveness (#4494)
* chore: logo spinner improvement

* chore: auth header action improvement
2024-05-16 23:35:53 +05:30
Anmol Singh Bhatia
fd4aa38dc2
fix: email preferences form reset (#4464) 2024-05-16 19:42:18 +05:30
sriram veeraghanta
e6142d8247 fix: adding new apple touch icon 2024-05-16 19:37:37 +05:30
Prateek Shourya
a1667f9a0f
chore: minor auth related improvements (#4483)
* chore: show `(optional)` in label of non-required fields.

* chore: fix github auth button text color.

* chore: minor ui/ ux improvement in oauth options.
2024-05-16 19:36:09 +05:30
guru_sainath
9b92fd4a16
chore: handled ui and preloading in init page (#4491) 2024-05-16 19:28:37 +05:30
sriram veeraghanta
7569c03cec fix: update site manifest with new logo 2024-05-16 18:45:11 +05:30
sriram veeraghanta
669faf7c72 fix: update package info 2024-05-16 17:51:10 +05:30
rahulramesha
1ad7011aac
[WEB-1249] feat: Kanban multi dragndrop (#4479)
* Kanban multi dnd

* complete Kanban multi dnd

* add proper brackets to if conditions
2024-05-16 17:29:01 +05:30
guru_sainath
bab52a2672
[WEB-1319] chore: New authentication workflow (#4481)
* chore: New authentication workflow

* chore: resolved build erros and updated imports in auth

* chore: code optimisation for query param util

* chore: added client for auth forms
2024-05-16 17:17:04 +05:30
Nikhil
37cc8d7b77
[WEB - 1333]fix: session age for admin and user (#4477)
* dev: fix session token save on admin and remove session save every request

* dev: update session cookie age to environment variable

* fix: adding save every request django session

* dev:  nginx configuration

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2024-05-16 16:17:05 +05:30
sriram veeraghanta
e9d80efbc9 fix: build errors 2024-05-16 16:00:47 +05:30
Aaryan Khandelwal
de27cd589a
chore: fethc csrf token every time before changing password (#4474)
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2024-05-16 13:21:01 +05:30
guru_sainath
507d7da717
fix: issue peekoverview handled state and query parameters handlers (#4475) 2024-05-16 13:14:36 +05:30
guru_sainath
2bf2e98b00
[WEB-1319] fix: handled issue filters mutation and updated the useParams with useSearchParams (#4473)
* chore: updated issue filters in space

* chore: persisting the query params even when we switch layouts

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2024-05-16 13:07:47 +05:30
Aaryan Khandelwal
8ecc461fb1
style: update space and admin app colors (#4465) 2024-05-16 11:35:31 +05:30
Aaryan Khandelwal
e044a8c2ac
chore: encode email before passing it as a query param (#4468) 2024-05-16 11:32:48 +05:30
Anmol Singh Bhatia
92c5ccef3d
fix: my activity user image overflow (#4469) 2024-05-16 05:09:33 +05:30
sriram veeraghanta
1fac702096 fix: build errors 2024-05-16 05:01:14 +05:30
Anmol Singh Bhatia
3bfe0950eb
chore: custom theme input placeholder improvement (#4472) 2024-05-16 04:39:26 +05:30
Anmol Singh Bhatia
68faced79d
chore: no issues found dark mode asset updated (#4466) 2024-05-16 04:35:35 +05:30
Aaryan Khandelwal
12cd22bba0
chore: sign out after deactivating account (#4476) 2024-05-16 04:24:50 +05:30
sriram veeraghanta
a195f1bf7e fix: space user validation check 2024-05-16 04:03:43 +05:30
Bavisetti Narayan
b14d44049c
[WEB-1328] chore: magic sign-in redirection (#4470)
* chore: magic signin redirection

* chore: expired magic code error message
2024-05-15 22:10:47 +05:30
sriram veeraghanta
0b84142dce Merge branch 'preview' of github.com:makeplane/plane into preview 2024-05-15 22:09:48 +05:30
sriram veeraghanta
7714825bab fix: adding new spinner 2024-05-15 22:09:16 +05:30
Nikhil
89f2e37b14
[WEB - 1315] fix: user sign up and sign in on a deactivated account (#4460)
* dev: remove email host user and email host password

* dev: fix user account deactivation error

* dev: fix caching issue of last workspace

* dev: add exclude for instances endpoint

* dev: update url redirection for auth
2024-05-15 22:08:54 +05:30
sriram veeraghanta
b78a064305 refactor: admin and added new spinner 2024-05-15 21:26:57 +05:30
Anmol Singh Bhatia
5ccb4f7d19
[WEB-1324] chore: change password page improvement (#4462)
* chore: change password page improvement

* chore: confirm password input improvement
2024-05-15 19:11:31 +05:30
Anmol Singh Bhatia
061d52727e
chore: project analytics improvement (#4457) 2024-05-15 18:43:07 +05:30
rahulramesha
69c9ae212b
fix profile issues filter (#4461) 2024-05-15 18:42:43 +05:30
sriram veeraghanta
0587c50ced fix: github setup workflow 2024-05-15 17:42:30 +05:30
guru_sainath
e1197f2b8f
chore: handled multiple children rendering in the space layout (#4459) 2024-05-15 16:28:38 +05:30
Ramesh Kumar Chandra
751a4a3b21
[WEB-1311] fix: Issue link copy shortcut macOS (#4455)
* chore: issue link copy shortcut in macos

* chore: dynamic shortcut key render in shortcut pop up

* chore: changing button depending on the os
2024-05-15 15:55:44 +05:30
sriram veeraghanta
a2fbd6132b refactor: publish boards 2024-05-15 02:25:38 +05:30
sriram veeraghanta
2b196ba1f1 fix: window workflow build error 2024-05-14 22:51:07 +05:30
sriram veeraghanta
8f6d9b8aca fix: build errors 2024-05-14 22:09:29 +05:30
sriram veeraghanta
bcc4524f7f fix: admin auth related fixes 2024-05-14 20:55:07 +05:30
Anmol Singh Bhatia
9b7b23f5a2
[WEB-1309] fix: auth fixes (#4456)
* dev: magic link login and email password disable

* dev: user account deactivation

* dev: change nginx conf routes

* feat: changemod space

* fix: space app dir fixes

* dev: invalidate cache for instances when creating workspace

* dev: update email templates for test email

* dev: fix build errors

* fix: auth fixes and improvement (#4452)

* chore: change password api updated and missing password error code added

* chore: auth helper updated

* chore: disable send code input suggestion

* chore: change password function updated

* fix: application error on sign in page

* chore: change password validation added and enhancement

* dev: space base path in web

* dev: admin user deactivated

* dev: user and instance admin session endpoint

* fix: last_workspace_id endpoint updated

* fix: magic sign in and email password check added

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
Co-authored-by: guru_sainath <gurusainath007@gmail.com>
2024-05-14 20:53:51 +05:30
Anmol Singh Bhatia
ab6f1ef780
[WEB-1298] chore: project cycle revamp (#4454)
* chore: cycle endpoint changes

* chore: completed cycle icon updated

* chore: project cycle list revamp and code refactor

* chore: cycle page improvement

* chore: added created by in retrieve endopoint

* fix: build error

* chore: cycle list page disclosure button improvement

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-05-14 19:22:08 +05:30
sriram veeraghanta
febf19ccc0
feat: converting space app to use nextjs app dir (#4451)
* feat: changemod space

* fix: space app dir fixes

* fix: build errors
2024-05-14 14:26:54 +05:30
sriram veeraghanta
087d54a261 fix: worflow update 2024-05-14 14:26:23 +05:30
Bavisetti Narayan
c2ce3ada14
chore: update issue link (#4450) 2024-05-14 13:49:14 +05:30
Nikhil
dbc0d7019b
[WEB - 1302]dev: nginx headers and auth error codes. (#4449)
* dev: add nginx headers

* fix: handled error messages in admin

---------

Co-authored-by: guru_sainath <gurusainath007@gmail.com>
2024-05-14 13:46:05 +05:30
Manish Gupta
2593dc8afc
added optional env FORCE_CPU and updated README (#4446) 2024-05-14 13:45:04 +05:30
Satish Gandham
18ba4009e0
- Stop the default behavior on the custom menu button. (#4440)
- Refactor menu click handler function
2024-05-13 13:05:10 +05:30
Aaryan Khandelwal
198a2a63f2
[WEB-1271] fix: show only joined projects in the filters list (#4417) 2024-05-13 12:06:34 +05:30
sriram veeraghanta
3723ece8d5 fix: postcss upgrade to latest version 2024-05-11 18:55:47 +05:30
sriram veeraghanta
91a66a757a fix: console warnings 2024-05-11 17:47:00 +05:30
Anmol Singh Bhatia
4aed6e7aed
fix: issue layout application error (#4437) 2024-05-11 16:29:53 +05:30
sriram veeraghanta
16d8dfc86e fix: build errors 2024-05-11 15:14:59 +05:30
Anmol Singh Bhatia
3355be9c9c
[WEB-1254] chore: list layout indentation enhancement and cycle list page ui improvement (#4435)
* chore: list layout indentation improvement

* chore: cycle list layout spacing and date ui updated

* chore: platform ui improvement
2024-05-11 14:47:56 +05:30
sriram veeraghanta
2ef3c06da0 fix: redirection issues and instance validation changes 2024-05-10 19:34:40 +05:30
M. Palanikannan
0ad8bf7664
[WEB-1118] fix: table selections using drag handle fixed (#4429)
* fix: table selections in using drag handle fixed

* fix: not show drag handles for empty p tags
2024-05-10 17:32:23 +05:30
Nikhil
88ebda42ff
fix: authentication redirection and UI (#4432)
* dev: update python version

* dev: handle magic code attempt exhausted

* dev: update app, space and god mode redirection paths

* fix: handled signup and signin workflow

* chore: auth input error indication and autofill styling improvement

* dev: add app redirection urls

* dev: update redirections

* chore: onboarding improvement

* chore: onboarding improvement

* chore: redirection issue in space resolved

* chore: instance empty state added

* dev: fix app, space, admin redirection in docker setitngs

---------

Co-authored-by: guru_sainath <gurusainath007@gmail.com>
Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
2024-05-10 17:30:38 +05:30
M. Palanikannan
2d1201cc92
fix: button click propagation stopped (#4430) 2024-05-10 16:41:13 +05:30
rahulramesha
b725c69882
list and spreadsheet sub issues mutation issue (#4415) 2024-05-10 16:14:15 +05:30
Manish Gupta
74eb50aa1a
selfhosting fixes for custom branch and platform (#4431) 2024-05-10 16:08:04 +05:30
Anmol Singh Bhatia
dc77e4afdb
chore: project publish modal improvement (#4422) 2024-05-10 15:25:16 +05:30
sriram veeraghanta
27315a8aa2 Merge branch 'preview' of github.com:makeplane/plane into preview 2024-05-10 15:24:32 +05:30
Anmol Singh Bhatia
da78933c61
[WEB-1274] chore: issue spreadsheet enhancement (#4423)
* chore: border and background remove from cycle and module select

* choe: indentation improvement
2024-05-10 15:24:18 +05:30
sriram veeraghanta
0ab2a98777 Merge branch 'preview' of github.com:makeplane/plane into preview 2024-05-10 15:24:13 +05:30
sriram veeraghanta
40560109b5 fix: admin app redirections 2024-05-10 15:23:51 +05:30
Anmol Singh Bhatia
0037377c8f
chore: project card enhancement (#4424) 2024-05-10 15:22:01 +05:30
Anmol Singh Bhatia
0af55e7bbb
[WEB-1250] chore: module list enhancement (#4425)
* chore: move module sub-header to app header

* chore: gantt header improvement, remove title

* chore: progress indicator size reduced

* chore: replace members with lead and updated start and end date ui
2024-05-10 15:21:05 +05:30
Anmol Singh Bhatia
57eda34082
chore: notification action item enhancement (#4426) 2024-05-10 15:19:59 +05:30
Anmol Singh Bhatia
e396424db7
[WEB-1251] chore: view list enhancement (#4427)
* chore: moved search query to mobx store

* chore: moved view sub-header to app header

* chore: created by avatar added in view item list
2024-05-10 15:19:05 +05:30
sriram veeraghanta
243680132e fix: space re-directions 2024-05-10 03:46:45 +05:30
sriram veeraghanta
547a76ae55
fix: admin and space redirections (#4419)
* dev: add admin and space base url

* fix: formatting

* dev: add app,space and admin base url to the api env

* fix: updated app base urls redirection

* dev: add change password endpoint

* dev: add none as default for base url

* dev: space password management endpoints

* fix: docker env update

* fix: docker and env settings

* fix: docker changes

* fix: next config update

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: guru_sainath <gurusainath007@gmail.com>
2024-05-10 02:32:42 +05:30
sriram veeraghanta
2320b33189 Merge branch 'preview' of github.com:makeplane/plane into preview 2024-05-09 21:06:08 +05:30
sriram veeraghanta
45bb1153ee fix: removing deploy with nginx env 2024-05-09 21:05:51 +05:30
Anmol Singh Bhatia
b8768d7274
fix: spreadsheet layout sticky column (#4416)
* fix: spreadsheet layout sticky column

* fix: spreadsheet layout sticky column
2024-05-09 19:06:39 +05:30
guru_sainath
58bf056ddb
fix: auth redirection issues in the web, space and admin apps (#4414)
* fix: login redirection

* dev: log the user out when deactivating the account

* dev: update redirect uris for google and github

* fix: redirection url and invitation api and add redirection to god mode in nginx

* dev: add reset password redirection

* dev: update nginx headers

* dev: fix setup sh and env example and put validation for use minio when fetching project covers

* dev: stabilize dev setup

* fix: handled redirection error in web, space, and admin apps

* fix: resovled build errors

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
2024-05-09 17:46:31 +05:30
rahulramesha
692f570258
[WEB-1134] fix: module link mutation issue (#4413)
* fix module link mutation issue

* reference moduleDetails?.link_module inside a prop of Disclosure instead of in a div
2024-05-09 16:49:24 +05:30
Anmol Singh Bhatia
11cd9f57d7
chore: applied filters update view button alignment improvement (#4407) 2024-05-09 15:52:56 +05:30
Aaryan Khandelwal
3a66986785
[WEB-1256] fix: accept image as a valid comment (#4409)
* fix: accept image as a valid comment

* fix: update space app helper function
2024-05-09 15:52:17 +05:30
rahulramesha
d8ab3e0087
[WEB-1139] chore: Calendar pragmatic dnd (#4410)
* replace Pragmatic DND for calendar

* remove unnecessary check
2024-05-09 15:51:49 +05:30
sriram veeraghanta
9d2d9c59ca fix: workflow updates 2024-05-09 02:52:00 +05:30
sriram veeraghanta
cfdcda4445 fix: build test workflow update 2024-05-09 01:41:48 +05:30
sriram veeraghanta
230fd961c7 feat: adding admin service to workflow 2024-05-09 00:56:18 +05:30
sriram veeraghanta
59335618b4
feat: session auth implementation (#4411)
* feat: session authentication and god-mode implementation (#4302)

* dev: move authentication to base class for credentials

* chore: new account creation

* dev: return error as query parameter

* dev: accounts and profile endpoints for user

* fix: user store updates

* fix: store fixes

* fix: type fixes

* dev: set is_password_autoset and is_email_verifier for auth providers

* dev: move all auth configuration to different apps

* dev: fix circular imports

* dev: remove unused imports

* dev: fix imports for authentication

* dev: update endpoints to use rest framework api viewa

* fix: onboarding fixes

* dev: session model changes

* fix: session model and add check for last name first name and avatar

* dev: fix referer redirect

* dev: remove auth imports

* dev: fix imports

* dev: update migrations

* fix: instance admin login

* comflict: conflicts resolved

* dev: fix import errors and email check endpoint

* fix: error messages and redirects after login

* dev: configs api

* fix: is github enabled boolean

* dev: merge config and instance api

* conflict: merge conflict resolved

* dev: instance admin sign up endpoint

* dev: enable magic link login

* dev: configure instance variables for github and google enabled

* chore: typo fixes

* fix: god mode docker file changes

* build-error: resolved build errors

* fix: docker compose changes

* dev: add email credential check endpoint

* fix: minor package changes

* fix: docker related changes

* dev: add nginx rules in the nginx template

* dev: refactor the url patterns

* fix: docker changes

* fix: docker files for god-mode

* fix: static export

* fix: nginx conf

* dev: smtp sender refused exception

* fix: godmode fixes

* chore: god mode revamp.

* dev: add csrf secured flag

* fix: oauth redirect uri and session settings

* chore: god mode app changes.  (#3982)

* chore: send test email functionality.

* style: authentication methods page UI revamp.

* chore: create workspace popup.

* fix: user me endpoint

* dev: fix redirection after authentication

* dev: handle god mode redirection

* fix: redirections

* fix: auth related hooks

* fix: store related fixes

* dev: fix session authentication for rest apis

* fix: linting errors

* fix: removing references of useStore=

* dev: fix redirection and password validation

* dev: add useUser hook

* fix: build fixes and lint issues

* fix: removing useApplication hook

* fix: build errors

* fix: delete unused files

* fix: auth build fixes

* fix: bugfixes

* dev: alter avatar to support more than 255 chars

* dev: fix profile endpoint and increase session expiry time and update session on every request

* chore: resolved the migration

* chore: resolved merge conflicts

* dev: error codes and error messages for the auth flow

* dev: instance admin sign up and sign in endpoint

* dev: use zxcvbn to validate password strength

* dev: add extra parameters when error handling on instance god mode

* chore: auth init

* chore: signin/ signup form ui updates and password strength meter.

* chore: update password fields.

* chore: validations and error handling.

* chore: updated sign-up form

* chore: updated workflow and updated the code structure

* chore: instance empty state for god-mode.

* chore: instance and auth wrappers update

* fix: renaming godmode

* fix: docker changes

* chore: updated authentication wrappers

* chore: updated the authentication workflow and rendered all pages

* fix: build errors

* fix: docker related fixes

* fix: tailing slash added to space and admin for valid nginx locations

* chore: seperate pages for signup and login

* git-action modified for admin file changes

* feature build action updated for admin app

* self host modified

* chore: resolved build errors and handled signin and signup in a seperate route

* chore: sign-in and sign-up revamp.

* fix: migration conflicts

* dev: migrations

* chore: handled redirection

* dev: admin url

* dev: create seperate endpoint for instance admin me

* dev: instance admin endpoint

* git action fixed

* chore: handled auth wrappers

* dev: add serializer and remove print logs

* fix: build errors

* dev: fix migrations

* dev: instance folder structuring

* fix: linting errors

* chore: resolved build errors

* chore: updated store and auth workflow and updates api service types

* chore: Replaced Next Link with Anchoer tag for god-mode redirection

* add 3333 port to allowed origins

* make password login working again

* dev: fix redirection, add admin signout endpoint and fix email credential check endpoint

* fix unique code sign in

* fix small build error

* enable sign out

* dev: add google client secret variable to configure instance

* dev: add referer for redirection

* fix origin urls for oauths

* admin setup and login separation

* dev: fix user redirection and tour completed endpoint

* fix build errors

* dev: add set password endpoint

* dev: remove user creation logic for redirection

* fix unique code page

* fix forgot password

* chore: onboarding revamp.

* dev: fix workspace slug redirection in login

* chore: invited user onboarding flow update.

* chore: fix switch or delete account modal.

* fix members exception

* refactor auth flows and add invitations to auth flow

* fix sig in sign up url

* fix action url

* fix build errors

* dev: fix user set password when logging in

* dev: reset password endpoint

* chore: confirm password validation for signup and onboarding.

* enable reset password

* fix build error

* chore: minor UI updates.

* chore: forgot and reset password UI revamp.

* fix authentication re directions

* dev: auth redirections

* change url paths for signup and signin

* dev: make the user logged in when changing passwords

* dev: next path redirection for web and space app

* dev: next path for magic sign in endpoint

* dev: github space endpoint

* chore: minor ui updates and fixes in web app.

* set password screen

* fix multiple unique code generation

* dev: next path base redirection

* dev: remove print logs

* dev: auth space endpoints

* fix build errors

* dev: invalidate cache on configuration update, god mode exception errors and authentication failed code

* dev: fix space endpoints and add extra endpoints

* chore: space auth revamp.

* dev: add sign up for space app

* fix: build errors.

* fix: auth redirection logic.

* chore: space app onboarding revamp.

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: gurusainath <gurusainath007@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: Manish Gupta <59428681+mguptahub@users.noreply.github.com>
Co-authored-by: Manish Gupta <manish@mgupta.me>
Co-authored-by: = <=>
Co-authored-by: rahulramesha <rahulramesham@gmail.com>

* chore: updated file structure for admin

* chore: updated admin-sidebar

* chore: auth error handling

* chore: onboarding UI updates and dark mode fixes.

* chore: add `user personalization` step to onboarding profile setup screen.

* chore: fix minor UI bugs

* chore: authentication workflow changes

* chore: handled signin workflow

* style: switch or delete account workflow

* chore: god mode redirection URL

* feat(dashboard): improve label readability (#4321)

change none label for all time in dashbard filters

* chore: god-mode redirection

* chore: onboarding ui updates and accept invitation workflow updates.

* chore: rename unique code auth form.

* style: space auth ux copy.

* chore: updated intance and auth wrapper logic

* chore: update default layout style.

* chore: update confirm password.

* chore: backend redirection

* style: update banner ui

* chore: minor ui updates and validation fix.

* chore: removed old auth hook

* chore: handled auth wrapper

* chore: handled store loaders in the user

* chore: handled logs

* chore: add loading spinners for all auth and onboarding form buttons.

* chore: add background pattern in admin auth forms and minor ui fixes.

* chore: UI changes and revamp components for authentication

* chore: auth UI consistency in web, space and admin.

* chore: resolved build errors

* chore: removed old auth hooks

* chore: handled lint errors in use accounts

* chore: updated authentication wrapper logic in web app

* [WEB -1149] dev: update dependencies (#4333)

* dev: upgrade dependencies remove unwanted dependency and add ruff as local dependency

* dev: add comments

* chore: authentication wrapper fetch user

* chore: updated store loader

* chore: removed old auth wrapper and replaced the imports with new auth wrapper

* chore: join workspace invitation workflow updates

* chore: build error resolved in deploy

* chore: handled onboarding step error in web app

* chore: SMTP Name and Password validation removed

* chore: handled seo and signout logic and new user popup

* chore: added redirection to plane in the sidebar

* chore: resolved build errors

* dev: admin session cookie update

* chore: updated cookie session time for admin

* dev: add start date and end date to projects (#4355)

* chore: add email security dropdown and remove SMTP username and password validation.

* chore: add tooltip to admin sidebar help-section.

* chore: add dropdown to collapsed admin sidebar.

* chore: profile themning

* chore: updated page error messages and theme in command palette

* dev: add email validation in email check apis

* dev: remove start date and end date from project

* chore: updated space folder structure and updated the store hooks

* dev: error codes for authentication

* chore: handled authentication in space and web apps

* chore: banner redirect handling the email

* dev: god mode error codes

* chore: updated error codes

* chore: updated onboarding images

* dev: signout endpoints and saving login domain while creating sessions

* feat: Self Host Data Backup (#4383)

* feat: implemented backup , support for docker-compose tool, readme updated

* minor fix in shell script

* codacy fixes

* chore: handled build errors in web

* chore: updated react, react-dom, and next versions

* chore: updated password autioset in the signin

* dev: add logo prop to views and pages

* chore: updated api service and handled the set password in store

* chore: handled build errors and code cleanup

* dev: return 401 when the session is not valid

* dev: users/me exception for api

* chore: installed lodash in space app

* dev: add auth route in nginx

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: gurusainath <gurusainath007@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: Manish Gupta <59428681+mguptahub@users.noreply.github.com>
Co-authored-by: Manish Gupta <manish@mgupta.me>
Co-authored-by: rahulramesha <rahulramesham@gmail.com>
Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
Co-authored-by: Daniel Alba <56451942+redrum15@users.noreply.github.com>
Co-authored-by: Nikhil <118773738+pablohashescobar@users.noreply.github.com>
2024-05-08 23:01:20 +05:30
sriram veeraghanta
ae43d05714 fix: upgrading posthog 2024-05-08 19:27:48 +05:30
Anmol Singh Bhatia
e604cfeec9
[WEB-1040] fix: list layout indentation fix (#4406) 2024-05-08 17:28:28 +05:30
Aaryan Khandelwal
32e601a8e6
chore: remove description trim logic (#4396) 2024-05-08 15:42:52 +05:30
Bavisetti Narayan
50e3174ea8
chore: email templates (#4392) 2024-05-08 15:41:41 +05:30
Aaryan Khandelwal
f2749f693f
[WEB-1211] fix: inbox issue description revalidate (#4388) 2024-05-08 14:47:18 +05:30
Aaryan Khandelwal
3b17dc51ba
[WEB-1208] fix: todo list item word break (#4405) 2024-05-08 14:44:18 +05:30
Anmol Singh Bhatia
2aaf0a1637
[WEB-1040] chore: list layout sub issue nesting implementation (#4404) 2024-05-08 14:39:27 +05:30
sriram veeraghanta
f09dd3d782 Merge branch 'preview' of github.com:makeplane/plane into preview 2024-05-08 14:24:48 +05:30
sriram veeraghanta
2cbb2db02e fix: community edition label 2024-05-08 14:24:19 +05:30
Aaryan Khandelwal
f7babd682e
dev: upgrade lucide-react version to the latest one (#4397) 2024-05-08 14:01:05 +05:30
Anmol Singh Bhatia
a46c507ca1
[WEB-1040] chore: spreadsheet indentation improvement (#4391)
* chore: spreadsheet indentation improvement

* chore: spreadsheet layout sub issu nesting improvement

* chore: sub issue spacing improvement

* chore: spreadsheet layout sub issue toggle button improvement
2024-05-08 13:59:51 +05:30
Prateek Shourya
10efd8d1d9
[WEB-1236] chore: add Create page button to public/ private page empty state. (#4401) 2024-05-08 13:43:08 +05:30
Prateek Shourya
6efa135e9e
[WEB-1217] style: fix inconsistency in height of project type and lead dropdown in create project modal. (#4400) 2024-05-08 13:42:40 +05:30
Aaryan Khandelwal
6793b9e6fa
[WEB-] chore: show code block copy button only on hover (#4361) 2024-05-08 13:42:11 +05:30
Prateek Shourya
760063ec75
[WEB-1182] style: fix inbox state filter title. (#4399) 2024-05-08 13:41:58 +05:30
Prateek Shourya
1cbbddb1be
[WEB-1215] chore: display favorite module filter in applied filter section. (#4402)
* [WEB-1215] chore: display `favorite module` filter in applied filter section.

* fix: build errors.
2024-05-08 13:40:58 +05:30
Aaryan Khandelwal
cc4bb385fe
[WEB-] chore: trigger command k from input fields and editors (#4362) 2024-05-08 13:40:25 +05:30
rahulramesha
13e6a67321
[WEB-1140] chore: Gantt pragmatic dnd (#4390)
* Gantt Drag and drop migration and enable Dnd in Modules and Cycles Gantt

* fix minor UI and code issues
2024-05-08 13:38:58 +05:30
Bavisetti Narayan
b8f1734738
fix: draft archive issues (#4393) 2024-05-07 20:31:44 +05:30
Bavisetti Narayan
d946c51ef1
chore: created by key changed (#4389) 2024-05-07 19:22:00 +05:30
Nikhil
a40517015b
[WEB - 1213] fix: module webhook (#4385)
* dev: fix module webhook

* dev: correct the comment

* dev: handle does not exist exception
2024-05-07 15:51:08 +05:30
Aaryan Khandelwal
967ad77078
fix: set focus on editor conditionally after image upload (#4387) 2024-05-07 15:49:11 +05:30
Prateek Shourya
f5f683b8b8
[WEB-1086] chore: update toast messages for consistency. (#4384) 2024-05-07 15:03:24 +05:30
Anmol Singh Bhatia
2aef40b7c5
[WEB-1176] chore: filter dropdown indicator and code refactor (#4379)
* chore: filter dropdown indicator and code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: refactor calculateTotalFilters function with typescript generics
2024-05-07 14:56:19 +05:30
Anmol Singh Bhatia
1eba6c24cd
[WEB-859] chore: inbox sidebar improvement (#4381)
* chore: added created by field in inbox issue

* chore: inbox sidebar list item created by avatar added

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-05-07 14:10:27 +05:30
Prateek Shourya
a46eccf300
[WEB-1117] chore: add tool tip for Snooze under Notification Panel. (#4377) 2024-05-07 14:08:02 +05:30
Aaryan Khandelwal
780b239ecb
[WEB-1142] chore: optimistically add issue to cycle/modules (#4334)
* chore: optimistically add issue to cycle and module

* chore: update toast alerts

* refactor: module issue store

* chore: added addCycleToIssueFunction
2024-05-07 14:05:56 +05:30
Anmol Singh Bhatia
a85517de99
[WEB-1098] chore: dropdowns enhancement (#4375) 2024-05-07 14:02:48 +05:30
P B
fb68b77068
Changed tagline, description, and disclaimer (#4382)
- Minor grammatical and language corrections
- Another update coming soon
2024-05-07 13:56:03 +05:30
Aaryan Khandelwal
20e7dc68e6
[WEB-1127] style: create and delete modals' consistency (#4345)
* style: update modals typography, alignment

* style: made the modal separator full width

* style: delete modals consistency

* style: update the remaining delete modals

* chore: delete modal secondary button text

* style: update the remaining create modals

* chore: update cancel button text

* chore: created modal core

* style: modals responsiveness
2024-05-07 12:44:36 +05:30
Anmol Singh Bhatia
5ef51edad7
[WEB-1134] fix: module link create and delete mutation (#4373)
* fix: module link create and delete mutation

* chore: module link mutation store updates

* chore: code refactor

---------

Co-authored-by: gurusainath <gurusainath007@gmail.com>
2024-05-06 17:56:35 +05:30
Bavisetti Narayan
06a664f6b9
[WEB-1151] fix: email notification for mentions (#4360)
* chore: mention notification

* chore: mention filters
2024-05-06 16:07:56 +05:30
Anmol Singh Bhatia
cdab12e4b6
[WEB-1157] chore: label select improvement (#4370) 2024-05-06 15:30:58 +05:30
Anmol Singh Bhatia
59f1cc1962
[WEB-1114] chore: recent activity message updated (#4371) 2024-05-06 15:28:33 +05:30
Anmol Singh Bhatia
562e50815d
[WEB-1007] chore: invalid issue error empty state added (#4372) 2024-05-06 15:27:56 +05:30
rahulramesha
463f4781ae
[WEB-1137] fix: Firefox distorted vertical text (#4376) 2024-05-06 15:22:45 +05:30
Bavisetti Narayan
653005bb3b
[WEB-1206] chore: bulk delete api logs (#4374)
* chore: bulk delete api logs

* chore: deletion time change
2024-05-06 15:19:03 +05:30
Nikhil
f1fda4ae4a
[WEB - 1122] fix: webhook for issues, issue comments, projects, cycles and modules. (#4330)
* dev: update webhook logic for issues

* dev: update issue webhooks for cycle and module

* dev: webhook for comment

* dev: issue attachment webhooks

* dev: add logging

* dev: add inbox issue webhooks

* dev: update the webhook send task

* dev: project webhooks for api

* dev: webhooks update for projects, cycles and modules

* dev: fix webhook on cycle and module create from external apis
2024-05-06 14:13:49 +05:30
Aaryan Khandelwal
fb74875cde
[WEB-1181] chore: added a loader for page description (#4358)
* chore: add loader for page description

* chore: added skeleton loader

* fix: title loader margin

* chore: increased laoder width
2024-05-03 23:12:44 +05:30
sriram veeraghanta
da957e06b6
Merge pull request #4349 from makeplane/preview
release: v0.19-dev
2024-05-03 20:36:07 +05:30
Anmol Singh Bhatia
c96225c812
[WEB-1183] fix: updated global issues filter while updating global view (#4357)
* chore: Updated global issues filter while updating global view

* fix: globale view modal clear all

---------

Co-authored-by: gurusainath <gurusainath007@gmail.com>
2024-05-03 20:29:11 +05:30
Aaryan Khandelwal
527ecd7d22
style: make edito height according to the content height (#4347) 2024-05-03 19:20:13 +05:30
rahulramesha
acd8f8d2d6
[WEB-1173] fix: order by for last updated when issue is updated (#4353)
* update the issue's updated at date when issue is updated

* sort issue's updated and created at regardless of the date format.

* move the logic to date time helpers

* revert back the third variable in update issue
2024-05-03 19:08:20 +05:30
Anmol Singh Bhatia
f4cc103238
[WEB-1175] fix: modal context (#4352)
* fix: modal context

* chore: code refactor
2024-05-03 17:58:15 +05:30
Aaryan Khandelwal
89f2f87b97
fix: decrease gantt full size z-index (#4354) 2024-05-03 17:38:21 +05:30
sriram veeraghanta
eed047bfe3 chore: update version 2024-05-03 17:23:12 +05:30
Aaryan Khandelwal
8cf4260219
fix: dropdowns should close on tab key (#4351) 2024-05-03 15:39:14 +05:30
rahulramesha
1b55411919
[WEB-1136] chore: Kanban drag and drop improvements (#4350)
* Kanban DnD improvement

* minor fixes for kanban dnd improvement

* change scroll duration

* fix feedback on the UX

* add highlight before drop

* add toast message explain drag and drop is currently disabled

* Change warning dnd message

* add comments

* fix minor build error
2024-05-03 15:12:06 +05:30
Nikhil
dc5edca34d
chore: update the default name that is added to instance (#4348) 2024-05-03 14:35:36 +05:30
Anmol Singh Bhatia
2884b0e22e
fix: inbox issue header quick action validation (#4344) 2024-05-03 14:17:21 +05:30
Aaryan Khandelwal
091fdb89ac
chore: hide display properties in the calendar layout (#4346) 2024-05-03 14:16:27 +05:30
Anmol Singh Bhatia
a678844073
[WEB-859] [WEB-1162] chore: issue detail improvement and assignees filter fix (#4342)
* chore: created by option added to inbox issue detail section

* chore: issue detail page improvement

* fix: inbox assignee filter key
2024-05-02 20:39:43 +05:30
Bavisetti Narayan
4cb5c4d9df
chore: workspace module list endpoint (#4343) 2024-05-02 19:18:41 +05:30
Aaryan Khandelwal
45c9dfb3cf
chore: update useCallback dependencies (#4341) 2024-05-02 19:18:08 +05:30
M. Palanikannan
4c78cd7c7e
fix: inbox create issue comment fixed (#4340) 2024-05-02 18:22:00 +05:30
Aaryan Khandelwal
c4229c9d55
fix: quick actions dropdown disabled state (#4335) 2024-05-02 16:13:58 +05:30
Aaryan Khandelwal
42c4c46939
[WEB-1154] fix: delete attachment modal logic (#4338)
* fix: delete attachment modal logic

* chore: remove console log

* chore: update delete attachment button type
2024-05-02 16:13:04 +05:30
Anmol Singh Bhatia
6918393b63
fix: existing issue list modal loading flicker (#4337) 2024-05-02 16:11:38 +05:30
Aaryan Khandelwal
6196c750f1
fix: issue description persistence (#4331) 2024-05-01 19:55:37 +05:30
Anmol Singh Bhatia
ed6dd37043
fix: module sub-header empty state validation (#4329) 2024-05-01 18:36:11 +05:30
Bavisetti Narayan
efa3eda85e
[WEB-1145] chore: updated sub issue count in cycles, modules and project (#4328)
* chore: total issue count

* chore: removed the migration file

* fix: issue count

---------

Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
2024-05-01 18:24:54 +05:30
Aaryan Khandelwal
eb0877a3c8
[WEB-1135] chore: store page full width information in local storage (#4327)
* chore: store page full width information in local storage

* chore: update page types
2024-05-01 18:10:39 +05:30
Aaryan Khandelwal
73fd6e641c
chore: show display name character in avatar (#4326) 2024-05-01 18:05:24 +05:30
Aaryan Khandelwal
34dd19cb00
chore: default pages sorting order (#4325) 2024-05-01 18:04:41 +05:30
Anmol Singh Bhatia
ecc277c571
[WEB-1101] chore: workspace view quick action enhancement (#4324)
* chore: workspace view quick action enhancement

* fix: issue quick action height
2024-05-01 18:03:13 +05:30
Bavisetti Narayan
d69f025b9a
[WEB-1132] fix: display datetime fields in user time zone format (#4323)
* fix: user timezone response

* chore: removed unused variables
2024-05-01 18:01:53 +05:30
Aaryan Khandelwal
ed4a0518fc
[WEB-1119] style: editor typography, borders and alignment throughout the platform (#4322)
* chore: new font sizes

* chore: update space app editor border

* chore: issue detials page x-padding

* chore: editor width
2024-05-01 18:01:30 +05:30
rahulramesha
2e2747c1f9
fix kanban collapsed vertical writing mode in firefox (#4320) 2024-05-01 12:21:26 +05:30
Daniel Alba
aa09ec7cd4 feat(dashboard): improve label readability (#4321)
change none label for all time in dashbard filters
2024-05-01 00:10:52 +05:30
Anmol Singh Bhatia
037ddd8bdb
fix: project view application error (#4319) 2024-04-30 20:14:35 +05:30
Anmol Singh Bhatia
d1978be778
[WEB-1100] fix: bug fixes and enhancement (#4318)
* fix: inbox issue description

* chore: outline heading removed from page toc

* chore: label setting page ui improvement

* fix: update issue modal description resetting

* chore: project page head title improvement
2024-04-30 19:39:50 +05:30
Aaryan Khandelwal
d2717a221c
[WEB-1110] dev: custom context menu for issues, cycles, modules, views, pages and projects (#4267)
* dev: context menu

* chore: handle menu position on close

* chore: project quick actions

* chore: add more options to the project context menu

* chore: cycle item context menu

* refactor: context menu folder structure

* chore: module custom context menu

* chore: view custom context menu

* chore: issues custom context menu

* chore: reorder options

* chore: issues custom context menu

* chore: render the context menu in a portal
2024-04-30 18:59:07 +05:30
sriram veeraghanta
cb6ecc86cc fix: sync action variable names 2024-04-30 17:53:35 +05:30
sriram veeraghanta
6972a520ce chore: rename workflows 2024-04-30 17:30:35 +05:30
Aaryan Khandelwal
4f4f1d92e8
fix: issue description placeholder (#4312) 2024-04-30 17:21:52 +05:30
Anmol Singh Bhatia
87a606446f
[WEB-1093] chore: padding and borders consistency (#4315)
* chore: global list layout and list item component added

* chore: project view list layout consistency

* chore: project view sub header consistency

* chore: pages list layout consistency

* chore: project view sub header improvement

* chore: list layout item component improvement

* chore: module list layout consistency

* chore: cycle list layout consistency

* chore: issue list layout consistency

* chore: header height consistency

* chore: sub header consistency

* chore: list layout improvement

* chore: inbox sidebar improvement

* fix: cycle quick action

* chore: inbox selected issue improvement

* chore: label option removed from pages filter

* chore: inbox create issue modal improvement
2024-04-30 17:21:24 +05:30
Aaryan Khandelwal
1b79517f07
[WEB-1111] chore: added a helper function to check if issue is peeked (#4305)
* chore: added a helper function to check if issue is peeked

* chore: make the kanban block observer

* chore: rename isIssuePeekd helper function
2024-04-30 17:20:02 +05:30
Anmol Singh Bhatia
e5681534d7
[WEB-1065] chore: workspace view and empty filter improvement (#4308)
* chore: workspace view layout improvement

* fix: empty applied filters

* chore: code refactor

* chore: code refactor

* fix: build errors

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2024-04-29 19:45:06 +05:30
Anmol Singh Bhatia
03065d2c1d
[WEB-1094] chore: inbox sidebar mobile responsiveness (#4309)
* chore: inbox sidebar mobile responsiveness

* chore: code refactor
2024-04-29 19:38:46 +05:30
Anmol Singh Bhatia
5d3c64752c
fix: switch toggle ui (#4311) 2024-04-29 18:59:05 +05:30
Anmol Singh Bhatia
4104f87d41
fix: view, api token and estimate modal description height (#4310) 2024-04-29 18:58:31 +05:30
sriram veeraghanta
32d14d7000 fix: auto merge workflow fixes 2024-04-29 18:50:44 +05:30
sriram veeraghanta
1c4ca42603 fix: auto merge workflow fixes 2024-04-29 18:46:50 +05:30
sriram veeraghanta
3077cc410e Merge branch 'preview' of github.com:makeplane/plane into preview 2024-04-29 18:33:29 +05:30
sriram veeraghanta
392075a9eb fix: workflow fixes 2024-04-29 18:33:09 +05:30
Anmol Singh Bhatia
c252650c9a
chore: project pages order by option improvement (#4307) 2024-04-29 18:24:58 +05:30
sriram veeraghanta
16b43c7b02 fix: pr check validation in workflow 2024-04-29 16:58:54 +05:30
sriram veeraghanta
354c3d95ce Merge branch 'preview' of github.com:makeplane/plane into preview 2024-04-29 16:48:04 +05:30
sriram veeraghanta
9739aa574d chore: remove comments from workflows 2024-04-29 16:47:49 +05:30
Bhavya Gogri
a6b0a7fa8e
make redis version same as the one in selfhosted docker-compose (#4183) 2024-04-29 16:42:25 +05:30
sriram veeraghanta
7fdaa64b90 fix: workflow fixes 2024-04-29 16:17:22 +05:30
sriram veeraghanta
6072c8b550 fix: revert the auto merge workflow changes 2024-04-29 16:13:52 +05:30
Aaryan Khandelwal
84fd1dca4b
[WEB-436] chore: added h4 to h6 heading options (#4304)
* chore: added h4 to h6 heading options

* fix: build errors
2024-04-29 16:04:37 +05:30
sriram veeraghanta
49a6c9582c fix: autommerge fixes 2024-04-29 14:09:36 +05:30
sriram veeraghanta
245a0e92ee fix: automerge workflow 2024-04-29 13:43:16 +05:30
Ramesh Kumar Chandra
709d3a115b
[WEB-711] style: profile and its settings pages responsiveness (#4022)
* [WEB-711] style: profile and its settings pages responsiveness

* chore: linting issues fix

* fix: mobile-view padding

---------

Co-authored-by: LAKHAN BAHETI <lakhanbaheti9@gmail.com>
2024-04-29 12:59:49 +05:30
Ramesh Kumar Chandra
9c8b4afc20
[WEB-808] style: workspace settings mobile responsiveness (#4047)
* [WEB-808] style: workspace settings mobile responsiveness

* fix: scroll on mobile-view

* responsiveness fixes

---------

Co-authored-by: LAKHAN BAHETI <lakhanbaheti9@gmail.com>
2024-04-29 12:59:04 +05:30
Lakhan Baheti
0c880bbbc8
[WEB-704] fix: inbox responsiveness (#4275)
* chore: inbox responsiveness

* fix: sidebar in full view

* style: border theme

* condition update
2024-04-29 00:54:35 +05:30
Ramesh Kumar Chandra
ea436c925a
projects list responsiveness (#4279) 2024-04-29 00:54:02 +05:30
Lakhan Baheti
4bccbc9804
pages responsiveness (#4287) 2024-04-29 00:52:43 +05:30
Aaryan Khandelwal
ac4bb1c1b4
refactor: remove unused icon files (#4297)
* refactor: remove unused icon files

* refactor: attachment icons folder structure
2024-04-29 00:51:31 +05:30
Aaryan Khandelwal
0e3d5cc4eb
fix: incomplete cycle issues auth (#4299) 2024-04-29 00:50:15 +05:30
rahulramesha
6ac3cb9b31
[WEB-1073] fix: Kanban dnd to work as tested on Chrome, Safari and Firefox (#4301)
* fix Kanban dnd to work as tested on Chrome Safari and Firefox

* fix edge cases

* revert back unintentional change
2024-04-29 00:48:50 +05:30
Aaryan Khandelwal
e75947a5f4
fix: double slashes in the global issues layout routes (#4298) 2024-04-26 18:32:20 +05:30
Anmol Singh Bhatia
e4777157a2
fix: project active cycle progress (#4296) 2024-04-26 18:30:38 +05:30
Anmol Singh Bhatia
0605b5f60c
chore: show sub-issue option added in profile display filter (#4295) 2024-04-26 18:30:02 +05:30
Aaryan Khandelwal
ad27184a91
[WEB-1072] fix: pages UI improvements (#4294)
* fix: outline alignment

* fix: textarea auto-resize logic
2024-04-26 18:29:18 +05:30
Aaryan Khandelwal
f87bb95236
chore: clear search term on escape key (#4289) 2024-04-26 18:27:32 +05:30
Anmol Singh Bhatia
f2fa6452c9
fix: cycle and module quick action z-index (#4293) 2024-04-26 14:56:51 +05:30
Anmol Singh Bhatia
80461e6484
chore: filter member option sorting improvement (#4285) 2024-04-26 13:21:08 +05:30
Anmol Singh Bhatia
88165a8fdb
chore: module and cycle sidebar stats item filter implementation (#4286) 2024-04-26 12:58:27 +05:30
Anmol Singh Bhatia
42cceb5e65
fix: filter state option order (#4284) 2024-04-26 12:57:36 +05:30
Anmol Singh Bhatia
15c7deb2db
fix: existing and parent issue modal empty state flicker (#4281) 2024-04-24 20:48:44 +05:30
Anmol Singh Bhatia
e60ef36bfe
fix: module and cycle event propagation (#4280) 2024-04-24 20:20:41 +05:30
sriram veeraghanta
bc2c97b9c3 Merge branch 'develop' of github.com:makeplane/plane into develop 2024-04-24 17:43:02 +05:30
Michael Ermer
7f99b9a554
feat(api/issuesBySequenceId): add api to retrieve issue based on its sequence identitifier (#4170) 2024-04-24 17:42:12 +05:30
Aaryan Khandelwal
b74a0ea4d3
fix: list layout block border color (#4278) 2024-04-24 17:33:12 +05:30
sriram veeraghanta
d9f11733ad Merge branches 'preview' and 'develop' of github.com:makeplane/plane into preview 2024-04-24 17:24:07 +05:30
Anmol Singh Bhatia
87aab74579
chore: delete label modal content updated (#4276) 2024-04-24 17:21:52 +05:30
Bavisetti Narayan
d5dd971fb4
chore: state triage filter (#4277) 2024-04-24 16:28:35 +05:30
Anmol Singh Bhatia
1789b8ddeb
fix: observer added to empty state component (#4274) 2024-04-24 16:27:01 +05:30
sriram veeraghanta
1caa109c16 fix: update actions ubuntu version 2024-04-24 15:32:56 +05:30
Lakhan Baheti
b711fedb65
[WEB-1046] fix: user activity overflow & repsonsiveness (#4262)
* fix: activity responsiveness

* fix: activity icon placement

* fix: build
2024-04-24 15:20:32 +05:30
Anmol Singh Bhatia
deaa63488b
chore: project date filter updated (#4273) 2024-04-24 15:18:13 +05:30
Anmol Singh Bhatia
87737dbfbe
chore: input character limit error message improvement (#4271) 2024-04-24 15:17:50 +05:30
Anmol Singh Bhatia
fc1cffd524
chore: active cycle stats improvement (#4268) 2024-04-24 15:17:04 +05:30
Anmol Singh Bhatia
6e574515e0
fix: module delete modal outside click event propagation (#4265) 2024-04-24 15:16:47 +05:30
Anmol Singh Bhatia
d87edede79
chore: created by added in issue sidebar and peek overview (#4264) 2024-04-24 15:16:30 +05:30
Anmol Singh Bhatia
196724214b
chore: applied filter responsiveness (#4263) 2024-04-24 15:16:00 +05:30
Anmol Singh Bhatia
695892d66e
chore: issue filter member options improvement (#4261) 2024-04-24 15:15:10 +05:30
Anmol Singh Bhatia
a4e5138d1c
[WEB-1047] chore: create page modal improvement (#4266)
* chore: create page modal improvement

* chore: create page modal improvement
2024-04-23 19:59:34 +05:30
Nikhil
f174a96ef2
dev: update python runtime (#4259) 2024-04-23 13:42:48 +05:30
sriram veeraghanta
5e53279734 Merge branch 'develop' of github.com:makeplane/plane into preview 2024-04-23 13:27:49 +05:30
Anmol Singh Bhatia
bf852739cd
fix: preserve initial value on create more issues (#4258) 2024-04-23 13:25:27 +05:30
Anmol Singh Bhatia
f17e4c73a2
[WEB-1015] fix: kanban layout cycle and module quick add (#4252)
* fix: kanban layout cycle and module quick add

* fix: kanban layout cycle and module quick add
2024-04-23 13:07:20 +05:30
Bavisetti Narayan
aee48f6fa4
[WEB-1042] fix: dashboard collaborators active issue count (#4256)
* chore: recent collaborators based on workspace

* chore: removed the duplicate issue
2024-04-23 13:04:14 +05:30
Anmol Singh Bhatia
f7d6219bd1
[WEB-1038] fix: kanban layout drag permission validation (#4255)
* fix: kanban layout drag permission validation

* chore: code refactor
2024-04-23 13:03:30 +05:30
Nikhil
75d14ce1ef
fix: workspace redirection when logging in (#4254) 2024-04-23 13:02:48 +05:30
Anmol Singh Bhatia
9659da5b31
[WEB-1034] fix: inbox issue user activity (#4251)
* fix: inbox issue user activity

* chore: code refactor

* fix: sentry issue
2024-04-23 13:01:26 +05:30
Anmol Singh Bhatia
51ade1295c
fix: comment text editor user mention validation addded (#4250) 2024-04-23 13:00:58 +05:30
Anmol Singh Bhatia
ac3def2929
fix: calendar header action alignment (#4249) 2024-04-23 12:59:48 +05:30
dependabot[bot]
098a1950c7
chore(deps): bump gunicorn in /apiserver/requirements (#4247)
Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 21.2.0 to 22.0.0.
- [Release notes](https://github.com/benoitc/gunicorn/releases)
- [Commits](https://github.com/benoitc/gunicorn/compare/21.2.0...22.0.0)

---
updated-dependencies:
- dependency-name: gunicorn
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-23 12:58:28 +05:30
Anmol Singh Bhatia
03fd5feda6
[WEB-1035] fix: peek module auto closing (#4246)
* fix: peek module auto closing

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: archived at in module

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-04-23 12:58:01 +05:30
Anmol Singh Bhatia
270f7c4503
fix: sign in redirection flicker (#4245) 2024-04-23 12:57:10 +05:30
Anmol Singh Bhatia
beff8536c9
fix: filter custom date select toggle (#4244) 2024-04-23 12:56:21 +05:30
Anmol Singh Bhatia
5d8c5b22e8
chore: kanban layout sub-group ui revamp & sub-group icon fix (#4243) 2024-04-23 12:55:42 +05:30
Anmol Singh Bhatia
60663821df
chore: list layout issue block improvement (#4241) 2024-04-23 12:55:19 +05:30
Prateek Shourya
302da646a8
[WEB-857] chore: update issue priority text color for custom theme in spreadsheet layout. (#4235) 2024-04-23 12:54:19 +05:30
Anmol Singh Bhatia
e0e8ce633b
[WEB-1027] fix: overflow & alignment fixes (#4234)
* fix: list layout issue title overflow

* fix: project feature toggle modal project name overflow

* fix: app sidebar project section alignment

* fix: issue title textarea

* fix: create issue modal project select overflow

* fix: module and cycle applied filters overflow fix
2024-04-23 12:53:52 +05:30
Prateek Shourya
f77d2d8c0a
[WEB-643] chore: update issue activity tabs. (#4232)
* remove `updates` tab.
* make `comments` as primary tab.
2024-04-23 12:52:31 +05:30
Prateek Shourya
c50a0602f7
[WEB-871] chore: update leave project modal message in members settings page. (#4230)
* [WEB-871] chore: update leave project modal message in members settings page.

* fix: build errors.
2024-04-23 12:51:20 +05:30
Prateek Shourya
38daf72361
[WEB-872] chore: add tooltip to peek overview header icons. (#4229) 2024-04-23 12:49:29 +05:30
sriram veeraghanta
a0b9596cb4
Merge pull request #4239 from makeplane/preview
chore:version update
2024-04-19 12:01:15 +05:30
sriram veeraghanta
c1e5c0d4eb chore:version update 2024-04-19 11:59:30 +05:30
sriram veeraghanta
f71e8a3a0f
Merge pull request #4238 from makeplane/preview
release: v0.18-dev
2024-04-19 11:56:03 +05:30
Prateek Shourya
086d14609c
[WEB-1028] fix: enable comments and issue reactions for guest/ viewer. (#4231) 2024-04-18 20:36:24 +05:30
Nikhil
cb9fda8557
fix: caching login when member joins a workspace from the email (#4237) 2024-04-18 20:34:59 +05:30
sriram veeraghanta
7da2cb143f Merge branch 'preview' of github.com:makeplane/plane into develop 2024-04-17 20:33:18 +05:30
Aaryan Khandelwal
0d70960e4e
[WEB-1026] chore: clear search query on project change (#4225)
* chore: clear search query on project change

* fix: project query search
2024-04-17 20:32:25 +05:30
Anmol Singh Bhatia
0c0bdd6969
[WEB-999] fix: inbox sentry error and accept modal fix (#4226)
* fix: inbox issue parent id sentry error

* fix: inbox issue accept parent id fix
2024-04-17 20:31:47 +05:30
Anmol Singh Bhatia
cdc73cedab
chore: sign in page improvement (#4224) 2024-04-17 19:46:35 +05:30
Bavisetti Narayan
bf9049a7a2
[WEB-987] fix: inbox issue activity (#4208)
* chore: inbox issue activity

* chore: dummy data script

* chore: inbox issue activity implementation

---------

Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
2024-04-17 19:43:22 +05:30
Aaryan Khandelwal
f0cb48006f
[WEB-1024] fix: textarea component auto-resize (#4221)
* chore: updated resize hook logic

* fix: page title overflow issue

* chore: add length validation to page title
2024-04-17 19:41:34 +05:30
rahulramesha
10ed12e589
[WEB-1005] chore: pragmatic drag n drop implementation for labels (#4223)
* pragmatic drag n drop implementation for labels

* minor code quality improvements
2024-04-17 18:20:02 +05:30
Bavisetti Narayan
68c870b791
[WEB-1023] chore: page mention transactions (#4222)
* chore: null validation for old value in pages

* chore: page transaction activity

* chore: serialized description_html
2024-04-17 18:18:14 +05:30
Anmol Singh Bhatia
8e764004f0
fix: draft issue parent select query params (#4218) 2024-04-17 14:53:24 +05:30
Aaryan Khandelwal
1880eb7704
[WEB-1019] chore: error state for unauthorized pages (#4219)
* chore: private page access

* chore: add error state for private pages

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-04-17 14:53:01 +05:30
M. Palanikannan
1080094b01
fix: tab key behaviour fixed for tabIndex containing editors with lists' and code blocks' tab key (#4216) 2024-04-16 21:48:30 +05:30
Anmol Singh Bhatia
d307c727ea
fix: profile activity application error (#4214) 2024-04-16 20:35:37 +05:30
M. Palanikannan
ec5667194f
fix: unnecessary focus in editor on swr sync (#4215) 2024-04-16 20:35:10 +05:30
Anmol Singh Bhatia
1549f3a808
chore: calendar layout quick add menu placement improvement (#4213) 2024-04-16 20:24:13 +05:30
sriram veeraghanta
0705d2559b fix: issue template version from text area to input 2024-04-16 18:58:05 +05:30
sriram veeraghanta
a36836abd7 fix: issue template update 2024-04-16 18:55:51 +05:30
M. Palanikannan
27db8699c8
[WEB-480] fix: code block paste from VSCode and indentation (#4198)
* fix: stroing the transactions in page

* fix: page details changes

* chore: page response change

* chore: removed duplicated endpoints

* chore: optimised the urls

* chore: removed archived and favorite pages

* chore: revamping pages store and components

* mentions loading state part done

* fixed mentions not showing in modals

* removed comments and cleaned up types

* removed unused types

* reset: head

* chore: pages store and component updates

* style: pages list item UI

* fix: improved colors and drag handle width

* fix: slash commands are no more shown in the code blocks

* fix: cleanup/hide drag handles post drop

* fix: hide/cleanup drag handles post drag start

* fix: aligning the drag handles better with the node post css changes of the length

* fix: juggling back and forth of drag handles in ordered and unordered lists

* chore: fix imports, ts errors and other things

* fix: clearing nodes to default node i.e paragraph before converting it to other types of nodes

For more reference on what this does, please refer https://tiptap.dev/docs/editor/api/commands/clear-nodes

* chore: clearNodes after delete in case of selections being present

* fix: hiding link selector in the bubble menu if inline code block is selected

* chore: filtering, ordering and searching implemented

* chore: updated pages store and updated UI

* chore: new core editor just for document editor created

* chore: removed setIsSubmitting prop in doc editor

* fix: fixed submitting state for image uploads

* refactor: setShouldShowAlert removed

* refactor: rerenderOnPropsChange prop removed

* chore: type inference magic in ref to expose an api for controlling editor menu items from outside

* fix: naming imports

* chore: change names of the exposed functions and removing old types

* refactor: remove debouncedUpdatesEnabled prop;

* refactor: editor heading markings now parsed using html

* chore: removed unrelated components from the document editor

* refactor: page details granular components

* fix: remove onActionCompleteHandler

* refactor: removed rerenderOnProps change prop

* feat: added getMarkDown function

* chore: update dropdown option actions

* fix: sidebar markings update logic

* chore: add image and to-do list actions to the toolbar

* fix: handling refs and populating them via callbacks

* feat: scroll to node api exposed

* cleaning up editor refs when the editor is destroyed

* feat: scrolling added to read only instance of the editor

* fix: markings logic

* fix: build errors with types

* fix: build erros

* fix: subscribing to transactions of editor via ref

* chore: remove debug statements

* fix: type errors

* fix: temporary different slash commands for document editor

* chore: inline code extension style

* chore: remove border from readOnly editor

* fix: editor bottom padding

* chore: pages improvements

* chore: handle Enter key on the page title

* feat: added loading indicator logic in mentions

* fix: mentions and slash commands now work well with multiple editors in one place

* refactor: page store structure, filtering logic

* feat: added better seperation in inline code blocks

* feat: list autojoining added

* fix: pages folder structure

* fix: image refocus from external parts

* working lists somewhat

* chore: implement page reactions

* fix: build errors

* fix: build errors

* fixed drag handles stuff

* task list item fixed

* working

* fix: working on multiple nested lists

* chore: remove debug statements

* fix: Tab key on first list item handled to not go out of editor focus

* feat: threshold auto scroll support added and multi nested list selection fixed

* fix: caret color bug with improved inline code blocks

* fix: node range error when bulk deleting with list

* fix: removed slash commands from working in code blocks

* chore: update typography margins

* chore: new field added in page model

* fix: better type inference in slash commands

* chore: code block UI

* feat: image insertion at correct position using ref added

* feat: added improved mentions support for space

* fix: type errors in mentions for comments in web app

* sync: core with document-core

* fix: build errors

* fix: fallback for appendTo not being able to find active container instantly

* fix: page store

* fix: page description

* fix: css quality issues

* chore: code cleanup

* chore: removed placeholder text in codeblocks

* chore: archived pages response change

* chore: archived pages response change

* fix: initial pages list fetch

* fix: pages list filters and ordering

* chore: add access change option in the quick actions dropdown

* fix: inline code block caret fixed

* regression: removing extra text

* chore: caret color removed

* feat: copy code button added in code blocks

* fix: initial load of page details

* fix: initial load of page details

* fix: image resizing weird behavior on click/expanding it too much fixed now

* chore: copy page response

* fix: todo list spacing

* chore: description html in the copy page

* chore: handle latest description on refetch

* fix: saner scroll behaviours

* fix: block menu positioning

* fix: updated empty string description

* feat: tab change sync support added

* fix: infinite rerendering with markings

* fix: block menu finally

* fix: intial load on reload bug fixed

* fix: nested lists alignment

* fix: editor padding

* fix: first level list items copyable

* chore: list spacing

* fix: title change

* fix: pages list block items interaction

* fix: saving chip position

* fix: delete action from block menu to focus properly

* fix: margin-bottom as 0 to avoid weird spacing when a paragraph node follows a list node

* style: table, chore: lite text editor toolbar

* fix: page description tab sync

* fix: lists spacing and alignment

* refactor: document editor props

* feat: rich text editor wrapper created and migrated core

* feat: created wrapper around lite text editor and merged core

* chore: add lite text editor toolbar

* fix: build errors

* fix: type errors and addead live updation of toolbar

* chore: pages migration

* fix: inbox issue

* refactor: remove redundant package

* refactor: unused files

* fix: add dompurify to space app

* fix: inline code margin

* fix: editor className props

* fix: build errors

* fix: traversing up the tree before assuming the parent is not a list item

* fix: drag handle positions for list items fixed

* fix: removed focus at end logic after deleting block

* fix: image wrapper overflow scroll fix with block menu's position

* fix: selection and deletion logic for nested lists fixed!!

* fix: hiding the block menu while scrolling in the document/app

* fix: merge conflicts resolved from develop

* fix: inbox issue description

* chore: move page title to the web app

* fix: handling edge cases for table selection

* chore: lint issues

* refactor: list item functions moved to same file

* refactor: use mention hook

* fix: added try catch blocks for mention suggestions

* chore: remove unused code

* fix: remove console logs

* fix: remove console logs

* fix: code block paste handler

* fix: tracking image uploading status to prevent sync rerenders

* chore: remove unnecessary props

* fix: code block pasting logic from vs code handled properly

* feat: additional checks for doc bounds

* fix: type of cancel button changed to button instead of default submit

* feat: editor focus on saved position while syncing via swr

* fix: type errors

* fix: changing names of plugins and removing packages

* fix: readonly editor synced

* removed console logs

* fix: stringifying instead of dom injection

* fix: use-editor try catch error handling

* fix: editor container click in try catch

* fix: some more error handling

* chore: removed commented out code

* fix: backspace error handling

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
Co-authored-by: gurusainath <gurusainath007@gmail.com>
Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
2024-04-16 18:50:45 +05:30
Anmol Singh Bhatia
59772be014
fix: issue comment validation (#4211) 2024-04-16 18:50:26 +05:30
Anmol Singh Bhatia
2ab4d99c15
chore: issue kanban cursor improvement (#4212) 2024-04-16 18:49:46 +05:30
Bavisetti Narayan
50825b574b
chore: instance admin script (#4210) 2024-04-16 18:06:46 +05:30
rahulramesha
e952d88905
fix spreadsheet labels dropdown keyboard navigation (#4209) 2024-04-16 18:06:02 +05:30
sriram veeraghanta
247720b0d4 Merge branch 'develop' of github.com:makeplane/plane into develop 2024-04-16 16:41:29 +05:30
sriram veeraghanta
dce3f4bce8 fix: upgrading turbo 2024-04-16 16:41:15 +05:30
M. Palanikannan
c18265c7cf
[WEB-480] fix: image uploading with swr sync fixed for pages (#4187)
* fix: stroing the transactions in page

* fix: page details changes

* chore: page response change

* chore: removed duplicated endpoints

* chore: optimised the urls

* chore: removed archived and favorite pages

* chore: revamping pages store and components

* mentions loading state part done

* fixed mentions not showing in modals

* removed comments and cleaned up types

* removed unused types

* reset: head

* chore: pages store and component updates

* style: pages list item UI

* fix: improved colors and drag handle width

* fix: slash commands are no more shown in the code blocks

* fix: cleanup/hide drag handles post drop

* fix: hide/cleanup drag handles post drag start

* fix: aligning the drag handles better with the node post css changes of the length

* fix: juggling back and forth of drag handles in ordered and unordered lists

* chore: fix imports, ts errors and other things

* fix: clearing nodes to default node i.e paragraph before converting it to other types of nodes

For more reference on what this does, please refer https://tiptap.dev/docs/editor/api/commands/clear-nodes

* chore: clearNodes after delete in case of selections being present

* fix: hiding link selector in the bubble menu if inline code block is selected

* chore: filtering, ordering and searching implemented

* chore: updated pages store and updated UI

* chore: new core editor just for document editor created

* chore: removed setIsSubmitting prop in doc editor

* fix: fixed submitting state for image uploads

* refactor: setShouldShowAlert removed

* refactor: rerenderOnPropsChange prop removed

* chore: type inference magic in ref to expose an api for controlling editor menu items from outside

* fix: naming imports

* chore: change names of the exposed functions and removing old types

* refactor: remove debouncedUpdatesEnabled prop;

* refactor: editor heading markings now parsed using html

* chore: removed unrelated components from the document editor

* refactor: page details granular components

* fix: remove onActionCompleteHandler

* refactor: removed rerenderOnProps change prop

* feat: added getMarkDown function

* chore: update dropdown option actions

* fix: sidebar markings update logic

* chore: add image and to-do list actions to the toolbar

* fix: handling refs and populating them via callbacks

* feat: scroll to node api exposed

* cleaning up editor refs when the editor is destroyed

* feat: scrolling added to read only instance of the editor

* fix: markings logic

* fix: build errors with types

* fix: build erros

* fix: subscribing to transactions of editor via ref

* chore: remove debug statements

* fix: type errors

* fix: temporary different slash commands for document editor

* chore: inline code extension style

* chore: remove border from readOnly editor

* fix: editor bottom padding

* chore: pages improvements

* chore: handle Enter key on the page title

* feat: added loading indicator logic in mentions

* fix: mentions and slash commands now work well with multiple editors in one place

* refactor: page store structure, filtering logic

* feat: added better seperation in inline code blocks

* feat: list autojoining added

* fix: pages folder structure

* fix: image refocus from external parts

* working lists somewhat

* chore: implement page reactions

* fix: build errors

* fix: build errors

* fixed drag handles stuff

* task list item fixed

* working

* fix: working on multiple nested lists

* chore: remove debug statements

* fix: Tab key on first list item handled to not go out of editor focus

* feat: threshold auto scroll support added and multi nested list selection fixed

* fix: caret color bug with improved inline code blocks

* fix: node range error when bulk deleting with list

* fix: removed slash commands from working in code blocks

* chore: update typography margins

* chore: new field added in page model

* fix: better type inference in slash commands

* chore: code block UI

* feat: image insertion at correct position using ref added

* feat: added improved mentions support for space

* fix: type errors in mentions for comments in web app

* sync: core with document-core

* fix: build errors

* fix: fallback for appendTo not being able to find active container instantly

* fix: page store

* fix: page description

* fix: css quality issues

* chore: code cleanup

* chore: removed placeholder text in codeblocks

* chore: archived pages response change

* chore: archived pages response change

* fix: initial pages list fetch

* fix: pages list filters and ordering

* chore: add access change option in the quick actions dropdown

* fix: inline code block caret fixed

* regression: removing extra text

* chore: caret color removed

* feat: copy code button added in code blocks

* fix: initial load of page details

* fix: initial load of page details

* fix: image resizing weird behavior on click/expanding it too much fixed now

* chore: copy page response

* fix: todo list spacing

* chore: description html in the copy page

* chore: handle latest description on refetch

* fix: saner scroll behaviours

* fix: block menu positioning

* fix: updated empty string description

* feat: tab change sync support added

* fix: infinite rerendering with markings

* fix: block menu finally

* fix: intial load on reload bug fixed

* fix: nested lists alignment

* fix: editor padding

* fix: first level list items copyable

* chore: list spacing

* fix: title change

* fix: pages list block items interaction

* fix: saving chip position

* fix: delete action from block menu to focus properly

* fix: margin-bottom as 0 to avoid weird spacing when a paragraph node follows a list node

* style: table, chore: lite text editor toolbar

* fix: page description tab sync

* fix: lists spacing and alignment

* refactor: document editor props

* feat: rich text editor wrapper created and migrated core

* feat: created wrapper around lite text editor and merged core

* chore: add lite text editor toolbar

* fix: build errors

* fix: type errors and addead live updation of toolbar

* chore: pages migration

* fix: inbox issue

* refactor: remove redundant package

* refactor: unused files

* fix: add dompurify to space app

* fix: inline code margin

* fix: editor className props

* fix: build errors

* fix: traversing up the tree before assuming the parent is not a list item

* fix: drag handle positions for list items fixed

* fix: removed focus at end logic after deleting block

* fix: image wrapper overflow scroll fix with block menu's position

* fix: selection and deletion logic for nested lists fixed!!

* fix: hiding the block menu while scrolling in the document/app

* fix: merge conflicts resolved from develop

* fix: inbox issue description

* chore: move page title to the web app

* fix: handling edge cases for table selection

* chore: lint issues

* refactor: list item functions moved to same file

* refactor: use mention hook

* fix: added try catch blocks for mention suggestions

* chore: remove unused code

* fix: remove console logs

* fix: remove console logs

* fix: tracking image uploading status to prevent sync rerenders

* chore: remove unnecessary props

* fix: type of cancel button changed to button instead of default submit

* feat: editor focus on saved position while syncing via swr

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
Co-authored-by: gurusainath <gurusainath007@gmail.com>
Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
2024-04-16 15:52:31 +05:30
M. Palanikannan
480aa906de
[WEB - 480] fix: horizontal rule drag and drop (#4200)
* fix: horizontal rule parsing (with backwards compatibility) and ensuring correct selections

* fix: table drag handle position

* fix: code block drag handle positioning fixes

* fix: blockquote duplication, selection and deletion fixed

* fix: possible range errors and type errors
2024-04-16 15:51:05 +05:30
rahulramesha
b9314889d7
fix empty blocks due to virtualization in rare cases (#4207) 2024-04-16 15:50:06 +05:30
sriram veeraghanta
01f7328714 Merge branch 'preview' of github.com:makeplane/plane into develop 2024-04-16 15:49:22 +05:30
rahulramesha
2311bd4f78
add a small min height for dropping issues incase tger are zero issues in the entire row of columns (#4206) 2024-04-16 15:48:32 +05:30
CoolCu
f729e46bb0
chore: fix some typos in comments (#4205)
Signed-off-by: CoolCu <coolcui@qq.com>
2024-04-16 15:48:11 +05:30
sriram veeraghanta
41d8005f0b Merge branch 'preview' of github.com:makeplane/plane into develop 2024-04-16 13:28:03 +05:30
17347013442
320cb94e35
Update issue_details.ts (#4199)
You should implement the interface here, not yourself
2024-04-16 13:24:54 +05:30
Aaryan Khandelwal
6eed2c202b
chore: created a global mark as favorite component (#4203) 2024-04-16 13:23:50 +05:30
Anmol Singh Bhatia
0a6d7713fb
chore: issue comment empty state added (#4202) 2024-04-16 13:22:27 +05:30
Anmol Singh Bhatia
18b573531f
chore: member invite modal improvement (#4201) 2024-04-16 13:21:56 +05:30
sriram veeraghanta
963d078aba fix: codeql workflow update 2024-04-15 20:02:57 +05:30
Aaryan Khandelwal
abce0ed946
[WEB-735] chore: added a custom placeholder prop to all the editors (#4194)
* chore: added a custom placeholder prop to all the editors

* chore: inbox issue create modal placeholder
2024-04-15 20:00:02 +05:30
Anmol Singh Bhatia
77b323f562
chore: cycle list page improvement (#4195) 2024-04-15 19:49:16 +05:30
Anmol Singh Bhatia
f4c528f535
chore: inbox issue attachment added (#4190) 2024-04-15 19:48:50 +05:30
Nikhil
c670a31836
fix: remove caching when joining workspace (#4192) 2024-04-15 19:46:44 +05:30
Anmol Singh Bhatia
54196aafc0
[WEB-989] chore: archived module and cycle sidebar details (#4191)
* chore: module and cycle archive

* fix: archived cycle sidebar details fetch

* fix: archived module sidebar details fetch

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-04-15 19:46:14 +05:30
Anmol Singh Bhatia
7a4ee509da
[WEB-999] chore: inbox issue bug fixes and improvement (#4188)
* chore: duplicate inbox issue header improvement

* chore: snooze inbox issue header action improvement

* fix: project inbox exception error

* chore: inbox issue modal improvement

* chore: code refactor

* chore: Updated the inbox issue mutation

---------

Co-authored-by: guru_sainath <gurusainath007@gmail.com>
2024-04-15 19:45:47 +05:30
Aaryan Khandelwal
8454e4f1e0
[WEB-986] fix: editor slash command positioning (#4186)
* fix: table selected cell border

* chore: add syncing message when revalidating page data

* fix: slash command positioning

* fix: mentions list dropdown positioning
2024-04-15 19:45:03 +05:30
rahulramesha
7a21855ab6
[WEB-1004] feat: Pragmatic dnd implementation for Kanban (#4189)
* Pragmatic drag and drop implmentation of Kanban

* refactor pragmatic dnd implementation and fix bugs

* fix dnd for modules, cycles, draft and project views
2024-04-15 17:02:53 +05:30
sriram veeraghanta
384624a21b Merge branch 'develop' of github.com:makeplane/plane into preview 2024-04-15 12:54:06 +05:30
Aaryan Khandelwal
7507cb0a0f
[WEB-994] fix: pages list mutation between projects (#4179)
* fix: pages list mutation between projects

* fix: page tab logic

* chore: remove pageType from the project pages store

* chore: rename computed helper functions
2024-04-15 12:50:22 +05:30
Bavisetti Narayan
d58bc03db6
fix: pages and inbox issue in dummy data (#4177) 2024-04-15 12:49:58 +05:30
guru_sainath
20b0edeaa6
[WEB-999] chore: updated UI improvements and workflow updates in the project inbox (#4180)
* chore: snoozed filter in the issue inbox filter

* chore: navigating to the next or previous issue when we accept, decline, or duplicate the issue in inbox

* chore: Implemented state, label, assignee and target_date in the inbox issue description and Implemented issue edit confirmation once we click accept the inbox issue

* chore: removed logs

* chore: inbox issue create response

* chore: update inbox issue response

* chore: updated inbox issue accept workflow and added issue properties in inbox issue create modal

* chore: resolved build errors and upgraded lucide react

* chore: updated inbox issue store hook

* chore: code cleanup and removed validation for inbox description

* fix: renamed the variable isLoading to loader in project-inbox store

* fix: updated set function for issue property update

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-04-15 12:49:14 +05:30
guru_sainath
a44a032683
[WEB-984] feat: integrated state filter in inbox issues filter (#4182)
* chore: added state filtering in the inbox filters

* chore: Clearing the issues when we apply filters
2024-04-15 12:48:43 +05:30
guru_sainath
251a432d8a
[WEB-1000] fix: handled edge case for color on custom analytics state_group (#4185)
* chore: handled edge case for color on custom analytics state_group

* fix: error handler
2024-04-13 22:18:34 +05:30
Aaryan Khandelwal
3e2355e223
[WEB-460] refactor: editors, chore: pages list improvement (#4090)
* fix: stroing the transactions in page

* fix: page details changes

* chore: page response change

* chore: removed duplicated endpoints

* chore: optimised the urls

* chore: removed archived and favorite pages

* chore: revamping pages store and components

* mentions loading state part done

* fixed mentions not showing in modals

* removed comments and cleaned up types

* removed unused types

* reset: head

* chore: pages store and component updates

* style: pages list item UI

* fix: improved colors and drag handle width

* fix: slash commands are no more shown in the code blocks

* fix: cleanup/hide drag handles post drop

* fix: hide/cleanup drag handles post drag start

* fix: aligning the drag handles better with the node post css changes of the length

* fix: juggling back and forth of drag handles in ordered and unordered lists

* chore: fix imports, ts errors and other things

* fix: clearing nodes to default node i.e paragraph before converting it to other types of nodes

For more reference on what this does, please refer https://tiptap.dev/docs/editor/api/commands/clear-nodes

* chore: clearNodes after delete in case of selections being present

* fix: hiding link selector in the bubble menu if inline code block is selected

* chore: filtering, ordering and searching implemented

* chore: updated pages store and updated UI

* chore: new core editor just for document editor created

* chore: removed setIsSubmitting prop in doc editor

* fix: fixed submitting state for image uploads

* refactor: setShouldShowAlert removed

* refactor: rerenderOnPropsChange prop removed

* chore: type inference magic in ref to expose an api for controlling editor menu items from outside

* fix: naming imports

* chore: change names of the exposed functions and removing old types

* refactor: remove debouncedUpdatesEnabled prop;

* refactor: editor heading markings now parsed using html

* chore: removed unrelated components from the document editor

* refactor: page details granular components

* fix: remove onActionCompleteHandler

* refactor: removed rerenderOnProps change prop

* feat: added getMarkDown function

* chore: update dropdown option actions

* fix: sidebar markings update logic

* chore: add image and to-do list actions to the toolbar

* fix: handling refs and populating them via callbacks

* feat: scroll to node api exposed

* cleaning up editor refs when the editor is destroyed

* feat: scrolling added to read only instance of the editor

* fix: markings logic

* fix: build errors with types

* fix: build erros

* fix: subscribing to transactions of editor via ref

* chore: remove debug statements

* fix: type errors

* fix: temporary different slash commands for document editor

* chore: inline code extension style

* chore: remove border from readOnly editor

* fix: editor bottom padding

* chore: pages improvements

* chore: handle Enter key on the page title

* feat: added loading indicator logic in mentions

* fix: mentions and slash commands now work well with multiple editors in one place

* refactor: page store structure, filtering logic

* feat: added better seperation in inline code blocks

* feat: list autojoining added

* fix: pages folder structure

* fix: image refocus from external parts

* working lists somewhat

* chore: implement page reactions

* fix: build errors

* fix: build errors

* fixed drag handles stuff

* task list item fixed

* working

* fix: working on multiple nested lists

* chore: remove debug statements

* fix: Tab key on first list item handled to not go out of editor focus

* feat: threshold auto scroll support added and multi nested list selection fixed

* fix: caret color bug with improved inline code blocks

* fix: node range error when bulk deleting with list

* fix: removed slash commands from working in code blocks

* chore: update typography margins

* chore: new field added in page model

* fix: better type inference in slash commands

* chore: code block UI

* feat: image insertion at correct position using ref added

* feat: added improved mentions support for space

* fix: type errors in mentions for comments in web app

* sync: core with document-core

* fix: build errors

* fix: fallback for appendTo not being able to find active container instantly

* fix: page store

* fix: page description

* fix: css quality issues

* chore: code cleanup

* chore: removed placeholder text in codeblocks

* chore: archived pages response change

* chore: archived pages response change

* fix: initial pages list fetch

* fix: pages list filters and ordering

* chore: add access change option in the quick actions dropdown

* fix: inline code block caret fixed

* regression: removing extra text

* chore: caret color removed

* feat: copy code button added in code blocks

* fix: initial load of page details

* fix: initial load of page details

* fix: image resizing weird behavior on click/expanding it too much fixed now

* chore: copy page response

* fix: todo list spacing

* chore: description html in the copy page

* chore: handle latest description on refetch

* fix: saner scroll behaviours

* fix: block menu positioning

* fix: updated empty string description

* feat: tab change sync support added

* fix: infinite rerendering with markings

* fix: block menu finally

* fix: intial load on reload bug fixed

* fix: nested lists alignment

* fix: editor padding

* fix: first level list items copyable

* chore: list spacing

* fix: title change

* fix: pages list block items interaction

* fix: saving chip position

* fix: delete action from block menu to focus properly

* fix: margin-bottom as 0 to avoid weird spacing when a paragraph node follows a list node

* style: table, chore: lite text editor toolbar

* fix: page description tab sync

* fix: lists spacing and alignment

* refactor: document editor props

* feat: rich text editor wrapper created and migrated core

* feat: created wrapper around lite text editor and merged core

* chore: add lite text editor toolbar

* fix: build errors

* fix: type errors and addead live updation of toolbar

* chore: pages migration

* fix: inbox issue

* refactor: remove redundant package

* refactor: unused files

* fix: add dompurify to space app

* fix: inline code margin

* fix: editor className props

* fix: build errors

* fix: traversing up the tree before assuming the parent is not a list item

* fix: drag handle positions for list items fixed

* fix: removed focus at end logic after deleting block

* fix: image wrapper overflow scroll fix with block menu's position

* fix: selection and deletion logic for nested lists fixed!!

* fix: hiding the block menu while scrolling in the document/app

* fix: merge conflicts resolved from develop

* fix: inbox issue description

* chore: move page title to the web app

* fix: handling edge cases for table selection

* chore: lint issues

* refactor: list item functions moved to same file

* refactor: use mention hook

* fix: added try catch blocks for mention suggestions

* chore: remove unused code

* fix: remove console logs

* fix: remove console logs

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
Co-authored-by: gurusainath <gurusainath007@gmail.com>
Co-authored-by: Palanikannan1437 <73993394+Palanikannan1437@users.noreply.github.com>
2024-04-11 21:28:59 +05:30
sriram veeraghanta
8b6035d315 Merge branch 'preview' of github.com:makeplane/plane into develop 2024-04-11 20:27:44 +05:30
Bavisetti Narayan
b90ca97461
[WEB-983] chore: dummy data script (#4173)
* chore: dummy data script

* fix: lint issues

* chore: removed print statment
2024-04-11 20:27:05 +05:30
Anmol Singh Bhatia
b0ab17021f
[WEB-943] chore: inbox cancelled issue header action improvement (#4172)
* chore: inbox cancelled issue header action improvement

* chore: code refactor
2024-04-11 20:26:23 +05:30
rahulramesha
cd395fa3d2
[WEB-905] fix: make entire Kanban issue block clickable (#4175)
* make entire block clickable

* left align title tooltip on kanban block

* fix minor issue with kanban issue title

* disable click inside the quick actions
2024-04-11 20:21:03 +05:30
guru_sainath
9e3fedd0df
chore: Handled inbox issue sorting locally in inbox store (#4169) 2024-04-10 21:29:21 +05:30
Anmol Singh Bhatia
5250c64be5
[WEB-937] fix: inbox issue hook render issue and code refactor (#4168)
* fix: inbox issue hook render issue and code refactor

* fix: inbox issue hook render issue and code refactor
2024-04-10 21:28:48 +05:30
Aaryan Khandelwal
5c6170507e
fix: drag handle position in the peek overview (#4167) 2024-04-10 21:28:06 +05:30
Anmol Singh Bhatia
4f15c03f91
fix: api token expiry time (#4165) 2024-04-10 21:27:22 +05:30
Nico Franke
43534dcaed
fix: email ssl setting not getting saved (#4161) 2024-04-10 20:01:22 +05:30
guru_sainath
cfbc1a91af
chore: optimised issue loaders and added enum for issue status and current tab (#4164) 2024-04-10 19:55:16 +05:30
Anmol Singh Bhatia
7aa1d750ea
chore: issue comment mention user improvement (#4163) 2024-04-10 19:52:10 +05:30
Anmol Singh Bhatia
91ed27837e
chore: kanban card padding improvement (#4162) 2024-04-10 19:51:13 +05:30
guru_sainath
f45c2d12fd
[WEB-927, WEB-928] fix: inbox issue bug fixes and improvement (#4160)
* chore: inbox duplicate issue modal improvement

* chore: handled tab navigation in inbox issues and handled cross project inbox issues

* chore: fetch inbox issue activity once the issue is updated in inbox issue

* chore: disable duplicate inbox issue actions

* chore: duplicate issue mutation in the inbox issue

* chore: inbox create modal sidebar tab change updated

* chore: multiple date selection in the inbox issue filters

* chore: code refactor

* chore: removed project dependancy on the inbox store structure

---------

Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
2024-04-10 16:08:31 +05:30
guru_sainath
d0cb00f28a
[WEB-926] fix: issue description component update handled in peek overview, issue detail, and inbox issues (#4159)
* fix: issue description mutation

* fix: implemented same issue description logic for issue detail and inbox issue description

* fix: fixed parent issue title dissapearing while loading the issue detail page

* chore: code cleanup

* chore: handled exception when issue in not available in issue detail in issue store

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
2024-04-10 14:58:46 +05:30
sriram veeraghanta
c80638090f Merge branch 'preview' of github.com:makeplane/plane into preview 2024-04-10 14:04:38 +05:30
Anmol Singh Bhatia
549790ee8a
[WEB-905] chore: kanban card icon color improvement (#4156)
* chore: kanban card icon color improvement

* chore: kanban card clickable area improvement
2024-04-10 14:03:22 +05:30
guru_sainath
1dac70ecbe
[WEB-406] chore: project inbox improvement (#4151)
* chore: inbox issue status pill improvement

* chore: loader inconsistancy resolved

* chore: accepted and decline inbox issue validation

* chore: removed clear all button in applied filters

* chore: inbox issue create label improvement

* chore: updated label filter

* chore: updated fetching activites and comments

* chore: inbox filters date

* chore: removed the print statement

* chore: inbox date filter updated

* chore: handled custom date filter in inbox issue query params

* chore: handled custom date filter in inbox issue single select

* chore: inbox custom date filter updated

* chore: inbox sidebar filter improvement

* chore: inbox sidebar filter improvement

* chore: duplicate issue detail

* chore: duplicate inbox issue improvement

* chore: lint issue resolved

---------

Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-04-10 13:52:57 +05:30
Prateek Shourya
3c2b2e3ed6
[WEB-924] chore: remove Add Issues button from completed cycles header. (#4153)
* [WEB-924] chore: remove `Add Issues` button from completed cycles header.

* fix: analytics button.
2024-04-10 13:02:56 +05:30
Anmol Singh Bhatia
f805acbcef
chore: workspace invite loader improvement (#4152) 2024-04-09 19:18:43 +05:30
sriram veeraghanta
b349de556a
chore: security policy updated 2024-04-09 17:53:09 +05:30
Anmol Singh Bhatia
699017014e
fix: workspace invite empty state flicker (#4150) 2024-04-09 15:04:16 +05:30
Prateek Shourya
2d4547601d
[WEB-903] fix: issue subscription button mutation. (#4149) 2024-04-09 15:03:49 +05:30
Prateek Shourya
9b918b727a
[WEB-918] style: update size of state dropdown button for consistency. (#4144) 2024-04-09 13:39:01 +05:30
Anmol Singh Bhatia
74a88fc028
[WEB-904] chore: feature validation empty state (#4145)
* chore: disable feature empty state added

* chore: disable feature empty state updated
2024-04-09 13:38:07 +05:30
Prateek Shourya
e86397b649
[WEB-917] style: update urgent priority icon design. (#4146) 2024-04-09 13:37:18 +05:30
Nikhil
7e0520d1cf
[WEB - 922]dev: fix workspace member caching (#4147)
* dev: fix workspace member caching

* fix: caching on debug
2024-04-09 13:36:08 +05:30
rahulramesha
95580d0c62
[WEB-881] fix: Sentry errors from previous build (#4142)
* Sentry Fix for "Non-Error promise rejection captured with value: Route change to url was aborted"

* Sentry fix for "undefined is not an object (evaluating 'n.response')"

* Possible Sentry Fix for "TypeError Function.entries(<anonymous>)"

* Possible Sentry fix for "null is not an object (evaluating 'e.type')"
2024-04-09 13:12:14 +05:30
guru_sainath
03df410b52
fix: updated issue description rendering when we switch between two issues via sub-issue (#4143) 2024-04-08 20:23:16 +05:30
sriram veeraghanta
c843a1757f fix: chat with us key handling 2024-04-08 19:39:43 +05:30
/dev/paul
2ea6d70fac
Changed the crips-wrapper to only use crisp when env is set (#4139) 2024-04-08 19:37:06 +05:30
/dev/paul
7bec244a67
Added fonts loaded from Google Fonts locally (#4140)
* Added all fonts locally

* Included LICENSEs

* Changed code to match linter-rules
2024-04-08 19:29:05 +05:30
guru_sainath
ddb07dbe5f
[WEB-406] chore: project inbox revamp (#4141)
* chore: removed inbox id

* fix: inbox changes

* chore: resolved merge conflicts

* chore: inbox issue response changes

* chore: inbox issue filters

* fix: inbox implementation revamp

* fix: type fixes

* fix: pagination implementation

* fix: inbox fixes

* fix: pagination fixes

* fix: inbox Issues pagination fixes

* chore: triage state change

* fix: inbox fixes

* chore: filtering using boolean

* chore: total results in the pagination

* fix: inbox main content changes

* fix: develop pull fixes

* chore: resolved build erros in inbox issues

* dev: fix migrations

* chore: module, labels and assignee in inbox

* chore: inbox issue order by

* chore: inbox filters

* chore: inbox ui revamp

* chore: inbox type updated

* chore: updated filters

* chore: updated filter menmbers and date types in inbox issue filter

* chore: inbox issue filter updated

* chore: updated date filter in the inbox issue filter

* chore: moved the current tab state from local state to store

* chore: updated the filter and fetch request in the inbox issues

* chore: updated tab change handler

* chore: handled isEmpty in the issue filters query params

* chore: inbox sidebar updated

* chore: enabled create inbox issue in mobx

* chore: replaced the key inbox_status to status

* chore: inbox sidebar pagination

* chore: updated inbox issue services

* chore: inbox sidebar total count indicator

* chore: create inbox issue updated

* chore: updated inbox issue sidebar layout

* chore: rendering issue detail in inbox issue

* chore: inbox issue content updated

* chore: create inbox issue modal description improvement

* fix: updated delete functionality in inbox store

* chore: updated multiple inbox issue creation

* chore: handled loading, empty states and inbox user access permissions

* chore: updated rendering issues in the sidebar

* chore: inbox sidebar label improvement

* chore: handled empty states

* chore: disabled inbox empty state added

* chore: module, labels and assignee in list endpoint

* chore: labels in list endpoint

* chore: inboc issue serializer

* chore: representation in serializer

* chore: super function

* chore: inbox empty state updated

* chore: implemented applied filters

* chore: inbox empty state updated

* chore: update date formats in applied filters

* chore: inbox skeleton updated

* chore: ui changes in the siebar list item

* chore: removed the module and cycle ids

* chore: inbox sidebar tab

* chore: inbox actions

* chore: updated inbox issue header actions

* chore: updated inbox issue code cleanup

* chore: loader improvement

* chore: inbox sidebar improvement

* chore: inbox sidebar empty state flicker

* fix: inbox issue delete operation

* chore: inbox issue title and description update indicator added

* fix: resolved issue property rendering in initial load

* chore: inbox sidebar and detail header improvement

* fix: handling selected filter in the issue filters and applied filters

* chore: inbox issue detail improvement

* chore: inbox issue label updated

* chore: inbox issue sidebar improvement

* fix: handling issue description update when we move between the issues in inbox

* chore: removed inbox issue helpers file

* chore: boolean checked

* chore: resolved file change requests

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
2024-04-08 19:11:47 +05:30
Nikhil
9b0949148f
[WEB - 883] fix: external apis (#3975)
* fix: external apis

* dev: remove descrypt in email password

* dev: add email as field in user serializer

* dev: fix linting errors

* dev: push commit to enable build triggers

* fix: formatting errors

* dev: remove instance value extraction
2024-04-08 18:46:05 +05:30
Prateek Shourya
8d009187ab
[WEB-916] fix: description editor missing when using create more button in create issue modal. (#4137)
* [WEB-916] fix: description editor missing when using create more button in create issue modal.

* chore: handle edge cases.

* chore: handle edge cases.
2024-04-08 18:44:04 +05:30
Prateek Shourya
39b5a58ce8
chore: remove placeholder from estimate property if no estimates are selected. (#4125) 2024-04-08 18:38:39 +05:30
Anmol Singh Bhatia
986f81e3ae
[WEB-905] chore: issue peek overview and kanban layout improvement (#4135)
* chore: peek overview and kanban card improvement

* chore: peek overview improvement
2024-04-08 18:38:05 +05:30
Prateek Shourya
fd2cacb0cd
[WEB-854] style: update height of sidebar help section and quick add across all layout for consitency. (#4138) 2024-04-08 18:35:35 +05:30
Nikhil
4f138ac3f9
[WEB - 908] chore: remove user dependency in dockerfile (#4124)
* chore: remove user dependency from api container

* dev: remove user assignment from dockerfile

* dev: add logging configuration
2024-04-08 15:01:17 +05:30
Nikhil
2c8c139c62
[WEB - 898] fix: external webhooks (#4119)
* fix: project webhooks

* fix: issue and issue comment webhooks

* dev: remove module and cycle issue webhooks

* fix: linting errors
2024-04-08 14:59:50 +05:30
sriram veeraghanta
57f2445bb8 fix: ingest events 2024-04-06 17:13:24 +05:30
rahulramesha
90609b306f
[WEB-914]: fix: Exception due to cycles and modules for undefined fields (#4127)
* fix cycle types

* fix module types
2024-04-05 20:05:55 +05:30
Bavisetti Narayan
62dac421dc
chore: archived cycles and mpdules (#4126) 2024-04-05 19:24:24 +05:30
guru_sainath
3742ea91bf
fix: issue description was not rendering in the issue create/edit modal (#4122) 2024-04-04 17:24:16 +05:30
Anmol Singh Bhatia
b4cc58d5dd
[WEB-756] chore: spreadsheet layout cycle and module feature toggle validation (#4121)
* chore: spreadsheet layout cycle and module feature toggle validation added

* chore: project analytics cycle and module feature toggle validation added
2024-04-04 15:49:25 +05:30
Crsi
71b73000d2
Dropped version info from docker-compose files (#4096)
According to the docker issue 11628
(https://github.com/docker/compose/issues/11628)
the 'version' field in docker-compose files is outdated.
It shows a warning like the following on hosts with a newer
Docker version:

```
WARN[0000] /srv/plane/docker-compose.yaml: `version` is obsolete
```

Also, the specs itself state the version was only informative:
https://github.com/compose-spec/compose-spec/blob/master/spec.md#version-and-name-top-level-elements

> The top-level `version` property is defined by the Compose
> Specification for backward compatibility. It is only informative.
> Compose doesn't use version to select an exact schema to
> validate the Compose file, but prefers the most recent schema
> when it's implemented.
2024-04-04 14:37:50 +05:30
Prateek Shourya
4cba7ff2f5
[WEB-848] fix: issue with parent child relation not being removed parent select dropwdown. (#4120) 2024-04-04 14:24:01 +05:30
Nikhil
840bc51537
fix: errors that were catched in sentry (#4114) 2024-04-03 21:05:33 +05:30
Prateek Shourya
0ab03c963c
[WEB-869] chore: fix assignee and parent ui inconsistency in create issue modal. (#4113) 2024-04-03 20:57:44 +05:30
Prateek Shourya
1fb8791941
chore: increase data dropdown close icon size for consitency. (#4115) 2024-04-03 20:51:22 +05:30
Anmol Singh Bhatia
e9518ced89
[WEB-756] chore: module and cycle feature toggle validation (#4112)
* chore: cycle and module feature issue block validation

* chore: cycle and module feature display properties validation

* chore: cycle and module feature display filters validation

* chore: cycle and module feature project view validation
2024-04-03 20:49:02 +05:30
Anmol Singh Bhatia
bc0752f7e8
[WEB-850] chore: calendar layout add existing issue (#4094)
* chore: calendar layout add existing issue added in project issue

* chore: code refactor
2024-04-03 20:46:19 +05:30
Prateek Shourya
4c97098218
chore: archival of modules, cycles and issues enhancement. (#4100) 2024-04-03 18:19:34 +05:30
Anmol Singh Bhatia
91d85ffed0
chore: issue block improvement (#4093) 2024-04-03 18:07:28 +05:30
Prateek Shourya
d485446ee2
[WEB-652] fix: issue activities alignment in dashboard. (#4106) 2024-04-03 18:06:46 +05:30
Anmol Singh Bhatia
92fd7b6977
chore: peek overview remove parent improvement (#4059) 2024-04-03 18:04:35 +05:30
Anmol Singh Bhatia
e7fc942514
[WEB-830] chore: project filter dropdown custom date select option improvement (#4069)
* chore: project filter dropdown custom date select option improvement

* fix: project filter custom date
2024-04-03 18:03:16 +05:30
Prateek Shourya
68ebcfd04e
[842] chore: disable comments and reactions in archived issues. (#4101) 2024-04-03 18:02:07 +05:30
Prateek Shourya
fed5916907
[WEB-848] fix: issue with parent child relation not being removed from update issue modal. (#4103) 2024-04-03 18:01:11 +05:30
1891 changed files with 73943 additions and 49444 deletions

View File

@ -2,8 +2,6 @@
*.pyc *.pyc
.env .env
venv venv
node_modules/
**/node_modules/
npm-debug.log npm-debug.log
.next/ .next/
**/.next/ **/.next/
@ -14,4 +12,4 @@ build/
out/ out/
**/out/ **/out/
dist/ dist/
**/dist/ **/dist/

59
.eslintrc-staged.js Normal file
View File

@ -0,0 +1,59 @@
/**
* Adds three new lint plugins over the existing configuration:
* This is used to lint staged files only.
* We should remove this file once the entire codebase follows these rules.
*/
module.exports = {
root: true,
extends: [
"custom",
],
parser: "@typescript-eslint/parser",
settings: {
"import/resolver": {
typescript: {},
node: {
moduleDirectory: ["node_modules", "."],
},
},
},
rules: {
"import/order": [
"error",
{
groups: ["builtin", "external", "internal", "parent", "sibling"],
pathGroups: [
{
pattern: "react",
group: "external",
position: "before",
},
{
pattern: "lucide-react",
group: "external",
position: "after",
},
{
pattern: "@headlessui/**",
group: "external",
position: "after",
},
{
pattern: "@plane/**",
group: "external",
position: "after",
},
{
pattern: "@/**",
group: "internal",
},
],
pathGroupsExcludedImportTypes: ["builtin", "internal", "react"],
alphabetize: {
order: "asc",
caseInsensitive: true,
},
},
],
},
};

View File

@ -4,7 +4,7 @@ module.exports = {
extends: ["custom"], extends: ["custom"],
settings: { settings: {
next: { next: {
rootDir: ["web/", "space/"], rootDir: ["web/", "space/", "admin/"],
}, },
}, },
}; };

View File

@ -55,12 +55,19 @@ body:
- Safari - Safari
- Other - Other
- type: dropdown - type: dropdown
id: version id: variant
attributes: attributes:
label: Version label: Variant
options: options:
- Cloud - Cloud
- Self-hosted - Self-hosted
- Local - Local
validations:
required: true
- type: input
id: version
attributes:
label: Version
placeholder: v0.17.0-dev
validations: validations:
required: true required: true

View File

@ -1,84 +0,0 @@
name: Auto Merge or Create PR on Push
on:
workflow_dispatch:
push:
branches:
- "sync/**"
env:
CURRENT_BRANCH: ${{ github.ref_name }}
SOURCE_BRANCH: ${{ secrets.SYNC_SOURCE_BRANCH_NAME }} # The sync branch such as "sync/ce"
TARGET_BRANCH: ${{ secrets.SYNC_TARGET_BRANCH_NAME }} # The target branch that you would like to merge changes like develop
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} # Personal access token required to modify contents and workflows
REVIEWER: ${{ secrets.SYNC_PR_REVIEWER }}
jobs:
Check_Branch:
runs-on: ubuntu-latest
outputs:
BRANCH_MATCH: ${{ steps.check-branch.outputs.MATCH }}
steps:
- name: Check if current branch matches the secret
id: check-branch
run: |
if [ "$CURRENT_BRANCH" = "$SOURCE_BRANCH" ]; then
echo "MATCH=true" >> $GITHUB_OUTPUT
else
echo "MATCH=false" >> $GITHUB_OUTPUT
fi
Auto_Merge:
if: ${{ needs.Check_Branch.outputs.BRANCH_MATCH == 'true' }}
needs: [Check_Branch]
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4.1.1
with:
fetch-depth: 0 # Fetch all history for all branches and tags
- name: Setup Git
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
- name: Setup GH CLI and Git Config
run: |
type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt install gh -y
- name: Check for merge conflicts
id: conflicts
run: |
git fetch origin $TARGET_BRANCH
git checkout $TARGET_BRANCH
# Attempt to merge the main branch into the current branch
if $(git merge --no-commit --no-ff $SOURCE_BRANCH); then
echo "No merge conflicts detected."
echo "HAS_CONFLICTS=false" >> $GITHUB_ENV
else
echo "Merge conflicts detected."
echo "HAS_CONFLICTS=true" >> $GITHUB_ENV
git merge --abort
fi
- name: Merge Change to Target Branch
if: env.HAS_CONFLICTS == 'false'
run: |
git commit -m "Merge branch '$SOURCE_BRANCH' into $TARGET_BRANCH"
git push origin $TARGET_BRANCH
- name: Create PR to Target Branch
if: env.HAS_CONFLICTS == 'true'
run: |
# Replace 'username' with the actual GitHub username of the reviewer.
PR_URL=$(gh pr create --base $TARGET_BRANCH --head $SOURCE_BRANCH --title "sync: merge conflicts need to be resolved" --body "" --reviewer $REVIEWER)
echo "Pull Request created: $PR_URL"

91
.github/workflows/build-aio-base.yml vendored Normal file
View File

@ -0,0 +1,91 @@
name: Build AIO Base Image
on:
workflow_dispatch:
env:
TARGET_BRANCH: ${{ github.ref_name }}
jobs:
base_build_setup:
name: Build Preparation
runs-on: ubuntu-latest
outputs:
gh_branch_name: ${{ steps.set_env_variables.outputs.TARGET_BRANCH }}
gh_buildx_driver: ${{ steps.set_env_variables.outputs.BUILDX_DRIVER }}
gh_buildx_version: ${{ steps.set_env_variables.outputs.BUILDX_VERSION }}
gh_buildx_platforms: ${{ steps.set_env_variables.outputs.BUILDX_PLATFORMS }}
gh_buildx_endpoint: ${{ steps.set_env_variables.outputs.BUILDX_ENDPOINT }}
build_base: ${{ steps.changed_files.outputs.base_any_changed }}
steps:
- id: set_env_variables
name: Set Environment Variables
run: |
echo "BUILDX_DRIVER=cloud" >> $GITHUB_OUTPUT
echo "BUILDX_VERSION=lab:latest" >> $GITHUB_OUTPUT
echo "BUILDX_PLATFORMS=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT
echo "BUILDX_ENDPOINT=makeplane/plane-dev" >> $GITHUB_OUTPUT
echo "TARGET_BRANCH=${{ env.TARGET_BRANCH }}" >> $GITHUB_OUTPUT
- id: checkout_files
name: Checkout Files
uses: actions/checkout@v4
- name: Get changed files
id: changed_files
uses: tj-actions/changed-files@v42
with:
files_yaml: |
base:
- aio/Dockerfile.base
base_build_push:
if: ${{ needs.base_build_setup.outputs.build_base == 'true' || github.event_name == 'workflow_dispatch' || needs.base_build_setup.outputs.gh_branch_name == 'master' }}
runs-on: ubuntu-latest
needs: [base_build_setup]
env:
BASE_IMG_TAG: makeplane/plane-aio-base:${{ needs.base_build_setup.outputs.gh_branch_name }}
TARGET_BRANCH: ${{ needs.base_build_setup.outputs.gh_branch_name }}
BUILDX_DRIVER: ${{ needs.base_build_setup.outputs.gh_buildx_driver }}
BUILDX_VERSION: ${{ needs.base_build_setup.outputs.gh_buildx_version }}
BUILDX_PLATFORMS: ${{ needs.base_build_setup.outputs.gh_buildx_platforms }}
BUILDX_ENDPOINT: ${{ needs.base_build_setup.outputs.gh_buildx_endpoint }}
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Set Docker Tag
run: |
if [ "${{ env.TARGET_BRANCH }}" == "master" ]; then
TAG=makeplane/plane-aio-base:latest
else
TAG=${{ env.BASE_IMG_TAG }}
fi
echo "BASE_IMG_TAG=${TAG}" >> $GITHUB_ENV
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: ${{ env.BUILDX_DRIVER }}
version: ${{ env.BUILDX_VERSION }}
endpoint: ${{ env.BUILDX_ENDPOINT }}
- name: Build and Push to Docker Hub
uses: docker/build-push-action@v5.1.0
with:
context: ./aio
file: ./aio/Dockerfile.base
platforms: ${{ env.BUILDX_PLATFORMS }}
tags: ${{ env.BASE_IMG_TAG }}
push: true
env:
DOCKER_BUILDKIT: 1
DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}

View File

@ -14,7 +14,7 @@ env:
jobs: jobs:
branch_build_setup: branch_build_setup:
name: Build-Push Web/Space/API/Proxy Docker Image name: Build Setup
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
gh_branch_name: ${{ steps.set_env_variables.outputs.TARGET_BRANCH }} gh_branch_name: ${{ steps.set_env_variables.outputs.TARGET_BRANCH }}
@ -22,10 +22,11 @@ jobs:
gh_buildx_version: ${{ steps.set_env_variables.outputs.BUILDX_VERSION }} gh_buildx_version: ${{ steps.set_env_variables.outputs.BUILDX_VERSION }}
gh_buildx_platforms: ${{ steps.set_env_variables.outputs.BUILDX_PLATFORMS }} gh_buildx_platforms: ${{ steps.set_env_variables.outputs.BUILDX_PLATFORMS }}
gh_buildx_endpoint: ${{ steps.set_env_variables.outputs.BUILDX_ENDPOINT }} gh_buildx_endpoint: ${{ steps.set_env_variables.outputs.BUILDX_ENDPOINT }}
build_frontend: ${{ steps.changed_files.outputs.frontend_any_changed }}
build_space: ${{ steps.changed_files.outputs.space_any_changed }}
build_backend: ${{ steps.changed_files.outputs.backend_any_changed }}
build_proxy: ${{ steps.changed_files.outputs.proxy_any_changed }} build_proxy: ${{ steps.changed_files.outputs.proxy_any_changed }}
build_apiserver: ${{ steps.changed_files.outputs.apiserver_any_changed }}
build_admin: ${{ steps.changed_files.outputs.admin_any_changed }}
build_space: ${{ steps.changed_files.outputs.space_any_changed }}
build_web: ${{ steps.changed_files.outputs.web_any_changed }}
steps: steps:
- id: set_env_variables - id: set_env_variables
@ -53,8 +54,12 @@ jobs:
uses: tj-actions/changed-files@v42 uses: tj-actions/changed-files@v42
with: with:
files_yaml: | files_yaml: |
frontend: apiserver:
- web/** - apiserver/**
proxy:
- nginx/**
admin:
- admin/**
- packages/** - packages/**
- 'package.json' - 'package.json'
- 'yarn.lock' - 'yarn.lock'
@ -67,17 +72,20 @@ jobs:
- 'yarn.lock' - 'yarn.lock'
- 'tsconfig.json' - 'tsconfig.json'
- 'turbo.json' - 'turbo.json'
backend: web:
- apiserver/** - web/**
proxy: - packages/**
- nginx/** - 'package.json'
- 'yarn.lock'
- 'tsconfig.json'
- 'turbo.json'
branch_build_push_frontend: branch_build_push_web:
if: ${{ needs.branch_build_setup.outputs.build_frontend == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }} if: ${{ needs.branch_build_setup.outputs.build_web == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
needs: [branch_build_setup] needs: [branch_build_setup]
env: env:
FRONTEND_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:${{ needs.branch_build_setup.outputs.gh_branch_name }} FRONTEND_TAG: makeplane/plane-frontend:${{ needs.branch_build_setup.outputs.gh_branch_name }}
TARGET_BRANCH: ${{ needs.branch_build_setup.outputs.gh_branch_name }} TARGET_BRANCH: ${{ needs.branch_build_setup.outputs.gh_branch_name }}
BUILDX_DRIVER: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }} BUILDX_DRIVER: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }}
BUILDX_VERSION: ${{ needs.branch_build_setup.outputs.gh_buildx_version }} BUILDX_VERSION: ${{ needs.branch_build_setup.outputs.gh_buildx_version }}
@ -87,9 +95,9 @@ jobs:
- name: Set Frontend Docker Tag - name: Set Frontend Docker Tag
run: | run: |
if [ "${{ github.event_name }}" == "release" ]; then if [ "${{ github.event_name }}" == "release" ]; then
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:stable,${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:${{ github.event.release.tag_name }} TAG=makeplane/plane-frontend:stable,makeplane/plane-frontend:${{ github.event.release.tag_name }}
elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:latest TAG=makeplane/plane-frontend:latest
else else
TAG=${{ env.FRONTEND_TAG }} TAG=${{ env.FRONTEND_TAG }}
fi fi
@ -124,12 +132,64 @@ jobs:
DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }} DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
branch_build_push_admin:
if: ${{ needs.branch_build_setup.outputs.build_admin== 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
runs-on: ubuntu-20.04
needs: [branch_build_setup]
env:
ADMIN_TAG: makeplane/plane-admin:${{ needs.branch_build_setup.outputs.gh_branch_name }}
TARGET_BRANCH: ${{ needs.branch_build_setup.outputs.gh_branch_name }}
BUILDX_DRIVER: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }}
BUILDX_VERSION: ${{ needs.branch_build_setup.outputs.gh_buildx_version }}
BUILDX_PLATFORMS: ${{ needs.branch_build_setup.outputs.gh_buildx_platforms }}
BUILDX_ENDPOINT: ${{ needs.branch_build_setup.outputs.gh_buildx_endpoint }}
steps:
- name: Set Admin Docker Tag
run: |
if [ "${{ github.event_name }}" == "release" ]; then
TAG=makeplane/plane-admin:stable,makeplane/plane-admin:${{ github.event.release.tag_name }}
elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then
TAG=makeplane/plane-admin:latest
else
TAG=${{ env.ADMIN_TAG }}
fi
echo "ADMIN_TAG=${TAG}" >> $GITHUB_ENV
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: ${{ env.BUILDX_DRIVER }}
version: ${{ env.BUILDX_VERSION }}
endpoint: ${{ env.BUILDX_ENDPOINT }}
- name: Check out the repo
uses: actions/checkout@v4
- name: Build and Push Frontend to Docker Container Registry
uses: docker/build-push-action@v5.1.0
with:
context: .
file: ./admin/Dockerfile.admin
platforms: ${{ env.BUILDX_PLATFORMS }}
tags: ${{ env.ADMIN_TAG }}
push: true
env:
DOCKER_BUILDKIT: 1
DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
branch_build_push_space: branch_build_push_space:
if: ${{ needs.branch_build_setup.outputs.build_space == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }} if: ${{ needs.branch_build_setup.outputs.build_space == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
needs: [branch_build_setup] needs: [branch_build_setup]
env: env:
SPACE_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-space:${{ needs.branch_build_setup.outputs.gh_branch_name }} SPACE_TAG: makeplane/plane-space:${{ needs.branch_build_setup.outputs.gh_branch_name }}
TARGET_BRANCH: ${{ needs.branch_build_setup.outputs.gh_branch_name }} TARGET_BRANCH: ${{ needs.branch_build_setup.outputs.gh_branch_name }}
BUILDX_DRIVER: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }} BUILDX_DRIVER: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }}
BUILDX_VERSION: ${{ needs.branch_build_setup.outputs.gh_buildx_version }} BUILDX_VERSION: ${{ needs.branch_build_setup.outputs.gh_buildx_version }}
@ -139,9 +199,9 @@ jobs:
- name: Set Space Docker Tag - name: Set Space Docker Tag
run: | run: |
if [ "${{ github.event_name }}" == "release" ]; then if [ "${{ github.event_name }}" == "release" ]; then
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-space:stable,${{ secrets.DOCKERHUB_USERNAME }}/plane-space:${{ github.event.release.tag_name }} TAG=makeplane/plane-space:stable,makeplane/plane-space:${{ github.event.release.tag_name }}
elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-space:latest TAG=makeplane/plane-space:latest
else else
TAG=${{ env.SPACE_TAG }} TAG=${{ env.SPACE_TAG }}
fi fi
@ -176,12 +236,12 @@ jobs:
DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }} DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
branch_build_push_backend: branch_build_push_apiserver:
if: ${{ needs.branch_build_setup.outputs.build_backend == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }} if: ${{ needs.branch_build_setup.outputs.build_apiserver == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
needs: [branch_build_setup] needs: [branch_build_setup]
env: env:
BACKEND_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:${{ needs.branch_build_setup.outputs.gh_branch_name }} BACKEND_TAG: makeplane/plane-backend:${{ needs.branch_build_setup.outputs.gh_branch_name }}
TARGET_BRANCH: ${{ needs.branch_build_setup.outputs.gh_branch_name }} TARGET_BRANCH: ${{ needs.branch_build_setup.outputs.gh_branch_name }}
BUILDX_DRIVER: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }} BUILDX_DRIVER: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }}
BUILDX_VERSION: ${{ needs.branch_build_setup.outputs.gh_buildx_version }} BUILDX_VERSION: ${{ needs.branch_build_setup.outputs.gh_buildx_version }}
@ -191,9 +251,9 @@ jobs:
- name: Set Backend Docker Tag - name: Set Backend Docker Tag
run: | run: |
if [ "${{ github.event_name }}" == "release" ]; then if [ "${{ github.event_name }}" == "release" ]; then
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:stable,${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:${{ github.event.release.tag_name }} TAG=makeplane/plane-backend:stable,makeplane/plane-backend:${{ github.event.release.tag_name }}
elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:latest TAG=makeplane/plane-backend:latest
else else
TAG=${{ env.BACKEND_TAG }} TAG=${{ env.BACKEND_TAG }}
fi fi
@ -233,7 +293,7 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
needs: [branch_build_setup] needs: [branch_build_setup]
env: env:
PROXY_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:${{ needs.branch_build_setup.outputs.gh_branch_name }} PROXY_TAG: makeplane/plane-proxy:${{ needs.branch_build_setup.outputs.gh_branch_name }}
TARGET_BRANCH: ${{ needs.branch_build_setup.outputs.gh_branch_name }} TARGET_BRANCH: ${{ needs.branch_build_setup.outputs.gh_branch_name }}
BUILDX_DRIVER: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }} BUILDX_DRIVER: ${{ needs.branch_build_setup.outputs.gh_buildx_driver }}
BUILDX_VERSION: ${{ needs.branch_build_setup.outputs.gh_buildx_version }} BUILDX_VERSION: ${{ needs.branch_build_setup.outputs.gh_buildx_version }}
@ -243,9 +303,9 @@ jobs:
- name: Set Proxy Docker Tag - name: Set Proxy Docker Tag
run: | run: |
if [ "${{ github.event_name }}" == "release" ]; then if [ "${{ github.event_name }}" == "release" ]; then
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:stable,${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:${{ github.event.release.tag_name }} TAG=makeplane/plane-proxy:stable,makeplane/plane-proxy:${{ github.event.release.tag_name }}
elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then elif [ "${{ env.TARGET_BRANCH }}" == "master" ]; then
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:latest TAG=makeplane/plane-proxy:latest
else else
TAG=${{ env.PROXY_TAG }} TAG=${{ env.PROXY_TAG }}
fi fi

View File

@ -10,42 +10,50 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
apiserver_changed: ${{ steps.changed-files.outputs.apiserver_any_changed }} apiserver_changed: ${{ steps.changed-files.outputs.apiserver_any_changed }}
admin_changed: ${{ steps.changed-files.outputs.admin_any_changed }}
space_changed: ${{ steps.changed-files.outputs.space_any_changed }}
web_changed: ${{ steps.changed-files.outputs.web_any_changed }} web_changed: ${{ steps.changed-files.outputs.web_any_changed }}
space_changed: ${{ steps.changed-files.outputs.deploy_any_changed }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
uses: tj-actions/changed-files@v41 uses: tj-actions/changed-files@v44
with: with:
files_yaml: | files_yaml: |
apiserver: apiserver:
- apiserver/** - apiserver/**
web: admin:
- web/** - admin/**
- packages/** - packages/**
- 'package.json' - 'package.json'
- 'yarn.lock' - 'yarn.lock'
- 'tsconfig.json' - 'tsconfig.json'
- 'turbo.json' - 'turbo.json'
deploy: space:
- space/** - space/**
- packages/** - packages/**
- 'package.json' - 'package.json'
- 'yarn.lock' - 'yarn.lock'
- 'tsconfig.json' - 'tsconfig.json'
- 'turbo.json' - 'turbo.json'
web:
- web/**
- packages/**
- 'package.json'
- 'yarn.lock'
- 'tsconfig.json'
- 'turbo.json'
lint-apiserver: lint-apiserver:
needs: get-changed-files needs: get-changed-files
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: needs.get-changed-files.outputs.apiserver_changed == 'true' if: needs.get-changed-files.outputs.apiserver_changed == 'true'
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.x' # Specify the Python version you need python-version: "3.x" # Specify the Python version you need
- name: Install Pylint - name: Install Pylint
run: python -m pip install ruff run: python -m pip install ruff
- name: Install Apiserver Dependencies - name: Install Apiserver Dependencies
@ -53,52 +61,77 @@ jobs:
- name: Lint apiserver - name: Lint apiserver
run: ruff check --fix apiserver run: ruff check --fix apiserver
lint-web: lint-admin:
needs: get-changed-files needs: get-changed-files
if: needs.get-changed-files.outputs.web_changed == 'true' if: needs.get-changed-files.outputs.admin_changed == 'true'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with: with:
node-version: 18.x node-version: 18.x
- run: yarn install - run: yarn install
- run: yarn lint --filter=web - run: yarn lint --filter=admin
lint-space: lint-space:
needs: get-changed-files needs: get-changed-files
if: needs.get-changed-files.outputs.space_changed == 'true' if: needs.get-changed-files.outputs.space_changed == 'true'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with: with:
node-version: 18.x node-version: 18.x
- run: yarn install - run: yarn install
- run: yarn lint --filter=space - run: yarn lint --filter=space
build-web: lint-web:
needs: lint-web needs: get-changed-files
if: needs.get-changed-files.outputs.web_changed == 'true'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with: with:
node-version: 18.x node-version: 18.x
- run: yarn install - run: yarn install
- run: yarn build --filter=web - run: yarn lint --filter=web
build-admin:
needs: lint-admin
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 18.x
- run: yarn install
- run: yarn build --filter=admin
build-space: build-space:
needs: lint-space needs: lint-space
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with: with:
node-version: 18.x node-version: 18.x
- run: yarn install - run: yarn install
- run: yarn build --filter=space - run: yarn build --filter=space
build-web:
needs: lint-web
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 18.x
- run: yarn install
- run: yarn build --filter=web

View File

@ -3,7 +3,7 @@ name: "CodeQL"
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: ["develop", "preview", "master"] branches: ["preview", "master"]
pull_request: pull_request:
branches: ["develop", "preview", "master"] branches: ["develop", "preview", "master"]
schedule: schedule:

View File

@ -1,28 +1,53 @@
name: Create Sync Action name: Create PR on Sync
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: branches:
- preview - "sync/**"
env: env:
SOURCE_BRANCH_NAME: ${{ github.ref_name }} CURRENT_BRANCH: ${{ github.ref_name }}
SOURCE_BRANCH: ${{ vars.SYNC_SOURCE_BRANCH_NAME }} # The sync branch such as "sync/ce"
TARGET_BRANCH: ${{ vars.SYNC_TARGET_BRANCH_NAME }} # The target branch that you would like to merge changes like develop
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} # Personal access token required to modify contents and workflows
REVIEWER: ${{ vars.SYNC_PR_REVIEWER }}
ACCOUNT_USER_NAME: ${{ vars.ACCOUNT_USER_NAME }}
ACCOUNT_USER_EMAIL: ${{ vars.ACCOUNT_USER_EMAIL }}
jobs: jobs:
sync_changes: Check_Branch:
runs-on: ubuntu-latest
outputs:
BRANCH_MATCH: ${{ steps.check-branch.outputs.MATCH }}
steps:
- name: Check if current branch matches the secret
id: check-branch
run: |
if [ "$CURRENT_BRANCH" = "$SOURCE_BRANCH" ]; then
echo "MATCH=true" >> $GITHUB_OUTPUT
else
echo "MATCH=false" >> $GITHUB_OUTPUT
fi
Auto_Merge:
if: ${{ needs.Check_Branch.outputs.BRANCH_MATCH == 'true' }}
needs: [Check_Branch]
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
pull-requests: write pull-requests: write
contents: read contents: write
steps: steps:
- name: Checkout Code - name: Checkout code
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.1
with: with:
persist-credentials: false fetch-depth: 0 # Fetch all history for all branches and tags
fetch-depth: 0
- name: Setup GH CLI - name: Setup Git
run: |
git config user.name "$ACCOUNT_USER_NAME"
git config user.email "$ACCOUNT_USER_EMAIL"
- name: Setup GH CLI and Git Config
run: | run: |
type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y) type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
@ -31,25 +56,14 @@ jobs:
sudo apt update sudo apt update
sudo apt install gh -y sudo apt install gh -y
- name: Push Changes to Target Repo A - name: Create PR to Target Branch
env:
GH_TOKEN: ${{ secrets.ACCESS_TOKEN }}
run: | run: |
TARGET_REPO="${{ secrets.TARGET_REPO_A }}" # get all pull requests and check if there is already a PR
TARGET_BRANCH="${{ secrets.TARGET_REPO_A_BRANCH_NAME }}" PR_EXISTS=$(gh pr list --base $TARGET_BRANCH --head $SOURCE_BRANCH --state open --json number | jq '.[] | .number')
SOURCE_BRANCH="${{ env.SOURCE_BRANCH_NAME }}" if [ -n "$PR_EXISTS" ]; then
echo "Pull Request already exists: $PR_EXISTS"
git checkout $SOURCE_BRANCH else
git remote add target-origin-a "https://$GH_TOKEN@github.com/$TARGET_REPO.git" echo "Creating new pull request"
git push target-origin-a $SOURCE_BRANCH:$TARGET_BRANCH PR_URL=$(gh pr create --base $TARGET_BRANCH --head $SOURCE_BRANCH --title "sync: community changes" --body "")
echo "Pull Request created: $PR_URL"
- name: Push Changes to Target Repo B fi
env:
GH_TOKEN: ${{ secrets.ACCESS_TOKEN }}
run: |
TARGET_REPO="${{ secrets.TARGET_REPO_B }}"
TARGET_BRANCH="${{ secrets.TARGET_REPO_B_BRANCH_NAME }}"
SOURCE_BRANCH="${{ env.SOURCE_BRANCH_NAME }}"
git remote add target-origin-b "https://$GH_TOKEN@github.com/$TARGET_REPO.git"
git push target-origin-b $SOURCE_BRANCH:$TARGET_BRANCH

View File

@ -5,18 +5,24 @@ on:
inputs: inputs:
web-build: web-build:
required: false required: false
description: 'Build Web' description: "Build Web"
type: boolean type: boolean
default: true default: true
space-build: space-build:
required: false required: false
description: 'Build Space' description: "Build Space"
type: boolean
default: false
admin-build:
required: false
description: "Build Admin"
type: boolean type: boolean
default: false default: false
env: env:
BUILD_WEB: ${{ github.event.inputs.web-build }} BUILD_WEB: ${{ github.event.inputs.web-build }}
BUILD_SPACE: ${{ github.event.inputs.space-build }} BUILD_SPACE: ${{ github.event.inputs.space-build }}
BUILD_ADMIN: ${{ github.event.inputs.admin-build }}
jobs: jobs:
setup-feature-build: setup-feature-build:
@ -27,9 +33,11 @@ jobs:
run: | run: |
echo "BUILD_WEB=$BUILD_WEB" echo "BUILD_WEB=$BUILD_WEB"
echo "BUILD_SPACE=$BUILD_SPACE" echo "BUILD_SPACE=$BUILD_SPACE"
echo "BUILD_ADMIN=$BUILD_ADMIN"
outputs: outputs:
web-build: ${{ env.BUILD_WEB}} web-build: ${{ env.BUILD_WEB}}
space-build: ${{env.BUILD_SPACE}} space-build: ${{env.BUILD_SPACE}}
admin-build: ${{env.BUILD_ADMIN}}
feature-build-web: feature-build-web:
if: ${{ needs.setup-feature-build.outputs.web-build == 'true' }} if: ${{ needs.setup-feature-build.outputs.web-build == 'true' }}
@ -45,7 +53,7 @@ jobs:
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: '18' node-version: "18"
- name: Install AWS cli - name: Install AWS cli
run: | run: |
sudo apt-get update sudo apt-get update
@ -71,7 +79,7 @@ jobs:
FILE_EXPIRY=$(date -u -d "+2 days" +"%Y-%m-%dT%H:%M:%SZ") FILE_EXPIRY=$(date -u -d "+2 days" +"%Y-%m-%dT%H:%M:%SZ")
aws s3 cp $TAR_NAME s3://${{ env.AWS_BUCKET }}/${{github.sha}}/$TAR_NAME --expires $FILE_EXPIRY aws s3 cp $TAR_NAME s3://${{ env.AWS_BUCKET }}/${{github.sha}}/$TAR_NAME --expires $FILE_EXPIRY
feature-build-space: feature-build-space:
if: ${{ needs.setup-feature-build.outputs.space-build == 'true' }} if: ${{ needs.setup-feature-build.outputs.space-build == 'true' }}
needs: setup-feature-build needs: setup-feature-build
@ -81,7 +89,7 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ vars.FEATURE_PREVIEW_AWS_ACCESS_KEY_ID }} AWS_ACCESS_KEY_ID: ${{ vars.FEATURE_PREVIEW_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.FEATURE_PREVIEW_AWS_SECRET_ACCESS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.FEATURE_PREVIEW_AWS_SECRET_ACCESS_KEY }}
AWS_BUCKET: ${{ vars.FEATURE_PREVIEW_AWS_BUCKET }} AWS_BUCKET: ${{ vars.FEATURE_PREVIEW_AWS_BUCKET }}
NEXT_PUBLIC_DEPLOY_WITH_NGINX: 1 NEXT_PUBLIC_SPACE_BASE_PATH: "/spaces"
NEXT_PUBLIC_API_BASE_URL: ${{ vars.FEATURE_PREVIEW_NEXT_PUBLIC_API_BASE_URL }} NEXT_PUBLIC_API_BASE_URL: ${{ vars.FEATURE_PREVIEW_NEXT_PUBLIC_API_BASE_URL }}
outputs: outputs:
do-build: ${{ needs.setup-feature-build.outputs.space-build }} do-build: ${{ needs.setup-feature-build.outputs.space-build }}
@ -90,7 +98,7 @@ jobs:
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: '18' node-version: "18"
- name: Install AWS cli - name: Install AWS cli
run: | run: |
sudo apt-get update sudo apt-get update
@ -117,9 +125,60 @@ jobs:
FILE_EXPIRY=$(date -u -d "+2 days" +"%Y-%m-%dT%H:%M:%SZ") FILE_EXPIRY=$(date -u -d "+2 days" +"%Y-%m-%dT%H:%M:%SZ")
aws s3 cp $TAR_NAME s3://${{ env.AWS_BUCKET }}/${{github.sha}}/$TAR_NAME --expires $FILE_EXPIRY aws s3 cp $TAR_NAME s3://${{ env.AWS_BUCKET }}/${{github.sha}}/$TAR_NAME --expires $FILE_EXPIRY
feature-build-admin:
if: ${{ needs.setup-feature-build.outputs.admin-build == 'true' }}
needs: setup-feature-build
name: Feature Build Admin
runs-on: ubuntu-latest
env:
AWS_ACCESS_KEY_ID: ${{ vars.FEATURE_PREVIEW_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.FEATURE_PREVIEW_AWS_SECRET_ACCESS_KEY }}
AWS_BUCKET: ${{ vars.FEATURE_PREVIEW_AWS_BUCKET }}
NEXT_PUBLIC_ADMIN_BASE_PATH: "/god-mode"
NEXT_PUBLIC_API_BASE_URL: ${{ vars.FEATURE_PREVIEW_NEXT_PUBLIC_API_BASE_URL }}
outputs:
do-build: ${{ needs.setup-feature-build.outputs.admin-build }}
s3-url: ${{ steps.build-admin.outputs.S3_PRESIGNED_URL }}
steps:
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "18"
- name: Install AWS cli
run: |
sudo apt-get update
sudo apt-get install -y python3-pip
pip3 install awscli
- name: Checkout
uses: actions/checkout@v4
with:
path: plane
- name: Install Dependencies
run: |
cd $GITHUB_WORKSPACE/plane
yarn install
- name: Build Admin
id: build-admin
run: |
cd $GITHUB_WORKSPACE/plane
yarn build --filter=admin
cd $GITHUB_WORKSPACE
TAR_NAME="admin.tar.gz"
tar -czf $TAR_NAME ./plane
FILE_EXPIRY=$(date -u -d "+2 days" +"%Y-%m-%dT%H:%M:%SZ")
aws s3 cp $TAR_NAME s3://${{ env.AWS_BUCKET }}/${{github.sha}}/$TAR_NAME --expires $FILE_EXPIRY
feature-deploy: feature-deploy:
if: ${{ always() && (needs.setup-feature-build.outputs.web-build == 'true' || needs.setup-feature-build.outputs.space-build == 'true') }} if: ${{ always() && (needs.setup-feature-build.outputs.web-build == 'true' || needs.setup-feature-build.outputs.space-build == 'true' || needs.setup-feature-build.outputs.admin-build == 'true') }}
needs: [feature-build-web, feature-build-space] needs:
[
setup-feature-build,
feature-build-web,
feature-build-space,
feature-build-admin,
]
name: Feature Deploy name: Feature Deploy
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
@ -164,7 +223,12 @@ jobs:
SPACE_S3_URL=$(aws s3 presign s3://${{ vars.FEATURE_PREVIEW_AWS_BUCKET }}/${{github.sha}}/space.tar.gz --expires-in 3600) SPACE_S3_URL=$(aws s3 presign s3://${{ vars.FEATURE_PREVIEW_AWS_BUCKET }}/${{github.sha}}/space.tar.gz --expires-in 3600)
fi fi
if [ ${{ env.BUILD_WEB }} == true ] || [ ${{ env.BUILD_SPACE }} == true ]; then ADMIN_S3_URL=""
if [ ${{ env.BUILD_ADMIN }} == true ]; then
ADMIN_S3_URL=$(aws s3 presign s3://${{ vars.FEATURE_PREVIEW_AWS_BUCKET }}/${{github.sha}}/admin.tar.gz --expires-in 3600)
fi
if [ ${{ env.BUILD_WEB }} == true ] || [ ${{ env.BUILD_SPACE }} == true ] || [ ${{ env.BUILD_ADMIN }} == true ]; then
helm --kube-insecure-skip-tls-verify repo add feature-preview ${{ vars.FEATURE_PREVIEW_HELM_CHART_URL }} helm --kube-insecure-skip-tls-verify repo add feature-preview ${{ vars.FEATURE_PREVIEW_HELM_CHART_URL }}
@ -181,6 +245,9 @@ jobs:
--set space.image=${{vars.FEATURE_PREVIEW_DOCKER_BASE}} \ --set space.image=${{vars.FEATURE_PREVIEW_DOCKER_BASE}} \
--set space.enabled=${{ env.BUILD_SPACE || false }} \ --set space.enabled=${{ env.BUILD_SPACE || false }} \
--set space.artifact_url=$SPACE_S3_URL \ --set space.artifact_url=$SPACE_S3_URL \
--set admin.image=${{vars.FEATURE_PREVIEW_DOCKER_BASE}} \
--set admin.enabled=${{ env.BUILD_ADMIN || false }} \
--set admin.artifact_url=$ADMIN_S3_URL \
--set shared_config.deploy_script_url=$DEPLOY_SCRIPT_URL \ --set shared_config.deploy_script_url=$DEPLOY_SCRIPT_URL \
--set shared_config.api_base_url=${{vars.FEATURE_PREVIEW_NEXT_PUBLIC_API_BASE_URL}} \ --set shared_config.api_base_url=${{vars.FEATURE_PREVIEW_NEXT_PUBLIC_API_BASE_URL}} \
--output json \ --output json \

44
.github/workflows/repo-sync.yml vendored Normal file
View File

@ -0,0 +1,44 @@
name: Sync Repositories
on:
workflow_dispatch:
push:
branches:
- preview
env:
SOURCE_BRANCH_NAME: ${{ github.ref_name }}
jobs:
sync_changes:
runs-on: ubuntu-20.04
permissions:
pull-requests: write
contents: read
steps:
- name: Checkout Code
uses: actions/checkout@v4.1.1
with:
persist-credentials: false
fetch-depth: 0
- name: Setup GH CLI
run: |
type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt install gh -y
- name: Push Changes to Target Repo
env:
GH_TOKEN: ${{ secrets.ACCESS_TOKEN }}
run: |
TARGET_REPO="${{ vars.SYNC_TARGET_REPO }}"
TARGET_BRANCH="${{ vars.SYNC_TARGET_BRANCH_NAME }}"
SOURCE_BRANCH="${{ env.SOURCE_BRANCH_NAME }}"
git checkout $SOURCE_BRANCH
git remote add target-origin-a "https://$GH_TOKEN@github.com/$TARGET_REPO.git"
git push target-origin-a $SOURCE_BRANCH:$TARGET_BRANCH

8
.gitignore vendored
View File

@ -1,3 +1,7 @@
pg_data
redis_data
minio_data
node_modules node_modules
.next .next
@ -81,3 +85,7 @@ tmp/
## packages ## packages
dist dist
.temp/ .temp/
deploy/selfhost/plane-app/
## Storybook
*storybook.log
output.css

0
.husky/pre-commit Normal file
View File

3
.lintstagedrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"*.{ts,tsx,js,jsx}": ["eslint -c ./.eslintrc-staged.js", "prettier --check"]
}

View File

@ -1,129 +1,77 @@
FROM node:18-alpine AS builder FROM debian:bookworm
RUN apk add --no-cache libc6-compat
# Set working directory
WORKDIR /app
ENV NEXT_PUBLIC_API_BASE_URL=http://NEXT_PUBLIC_API_BASE_URL_PLACEHOLDER
RUN yarn global add turbo RUN --mount=type=cache,target=/var/cache/apt \
RUN apk add tree apt-get update && apt-get install -y wget nginx procps lsb-release tmux python3 python3-pip nodejs npm curl
COPY . .
RUN turbo prune --scope=app --scope=plane-deploy --docker RUN wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio_20240629012047.0.0_amd64.deb -O minio.deb
CMD tree -I node_modules/ RUN apt-get install -y -f ./minio.deb
# Add lockfile and package.json's of isolated subworkspace RUN wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz
FROM node:18-alpine AS installer RUN tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz
RUN apk add --no-cache libc6-compat ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/root/go/bin
WORKDIR /app RUN go install github.com/DarthSim/overmind/v2@latest
ARG NEXT_PUBLIC_API_BASE_URL=http://localhost:8000
# First install the dependencies (as they change less often)
COPY .gitignore .gitignore
COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/yarn.lock ./yarn.lock
RUN yarn install
# # Build the project RUN npm i -g yarn && yarn set version 1.22.19
COPY --from=builder /app/out/full/ .
COPY turbo.json turbo.json
COPY replace-env-vars.sh /usr/local/bin/
USER root
RUN chmod +x /usr/local/bin/replace-env-vars.sh
RUN yarn turbo run build SHELL ["/bin/bash", "-c"]
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL \ RUN --mount=type=cache,target=/root/.cache/yarn \
BUILT_NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL --mount=type=cache,target=/var/cache/apt \
sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' \
&& wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \
&& apt-get update \
&& apt-get -y install postgresql libpq-dev
RUN /usr/local/bin/replace-env-vars.sh http://NEXT_PUBLIC_WEBAPP_URL_PLACEHOLDER ${NEXT_PUBLIC_API_BASE_URL} RUN --mount=type=cache,target=/root/.cache/yarn \
--mount=type=cache,target=/var/cache/apt \
set -eo pipefail; \
curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg; \
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb bookworm main" | tee /etc/apt/sources.list.d/redis.list; \
apt-get update; \
apt-get install -y redis
FROM python:3.11.1-alpine3.17 AS backend RUN apt-get install --reinstall python3 python3-pip
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /code
RUN apk --no-cache add \
"libpq~=15" \
"libxslt~=1.1" \
"nodejs-current~=19" \
"xmlsec~=1.2" \
"nginx" \
"nodejs" \
"npm" \
"supervisor"
COPY apiserver/requirements.txt ./
COPY apiserver/requirements ./requirements
RUN apk add --no-cache libffi-dev
RUN apk add --no-cache --virtual .build-deps \
"bash~=5.2" \
"g++~=12.2" \
"gcc~=12.2" \
"cargo~=1.64" \
"git~=2" \
"make~=4.3" \
"postgresql13-dev~=13" \
"libc-dev" \
"linux-headers" \
&& \
pip install -r requirements.txt --compile --no-cache-dir \
&& \
apk del .build-deps
# Add in Django deps and generate Django's static files
COPY apiserver/manage.py manage.py
COPY apiserver/plane plane/
COPY apiserver/templates templates/
RUN apk --no-cache add "bash~=5.2"
COPY apiserver/bin ./bin/
RUN chmod +x ./bin/takeoff ./bin/worker
RUN chmod -R 777 /code
# Expose container port and run entry point script
RUN mkdir /app
WORKDIR /app WORKDIR /app
# Don't run production as root COPY package.json turbo.json yarn.lock app.json .
RUN addgroup --system --gid 1001 plane COPY packages packages
RUN adduser --system --uid 1001 captain COPY web web
COPY space space
COPY --from=installer /app/apps/app/next.config.js . RUN --mount=type=cache,target=/root/.cache/yarn \
COPY --from=installer /app/apps/app/package.json . yarn install --frozen-lockfile \
COPY --from=installer /app/apps/space/next.config.js . && yarn build \
COPY --from=installer /app/apps/space/package.json . && rm -rf node_modules
COPY --from=installer --chown=captain:plane /app/apps/app/.next/standalone ./ RUN mkdir -p web/.next/standalone/web/.next \
&& mv web/public web/.next/standalone/web/public \
&& mv web/.next/static web/.next/standalone/web/.next/static \
&& mkdir -p space/.next/standalone/_next \
&& mv space/public space/.next/standalone/space/public \
&& mv space/.next/static space/.next/standalone/space/.next/static
COPY --from=installer --chown=captain:plane /app/apps/app/.next/static ./apps/app/.next/static ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
COPY --from=installer --chown=captain:plane /app/apps/space/.next/standalone ./ COPY apiserver apiserver
COPY --from=installer --chown=captain:plane /app/apps/space/.next ./apps/space/.next
ENV NEXT_TELEMETRY_DISABLED 1 RUN --mount=type=cache,target=/root/.cache/pip \
cd apiserver \
&& pip install --break-system-packages -r requirements.txt --compile \
&& pip install --break-system-packages -r requirements/local.txt --compile
# RUN rm /etc/nginx/conf.d/default.conf COPY docker/etc/ /etc/
#######################################################################
COPY nginx/nginx-single-docker-image.conf /etc/nginx/http.d/default.conf
#######################################################################
COPY nginx/supervisor.conf /code/supervisor.conf RUN chmod -R 755 /root \
&& chmod -R 755 apiserver \
&& chmod -R 755 web \
&& chmod -R 755 space \
&& chmod +x /etc/db-run.sh
ARG NEXT_PUBLIC_API_BASE_URL=http://localhost:8000 RUN apt-get install -y python-is-python3 python3-setuptools
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL \
BUILT_NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
USER root ENTRYPOINT ["bash", "/etc/entrypoint.sh"]
COPY replace-env-vars.sh /usr/local/bin/
COPY start.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/replace-env-vars.sh
RUN chmod +x /usr/local/bin/start.sh
EXPOSE 80
CMD ["supervisord","-c","/code/supervisor.conf"]

View File

@ -7,7 +7,7 @@
</p> </p>
<h3 align="center"><b>Plane</b></h3> <h3 align="center"><b>Plane</b></h3>
<p align="center"><b>Open-source project management that unlocks customer value.</b></p> <p align="center"><b>Open-source project management that unlocks customer value</b></p>
<p align="center"> <p align="center">
<a href="https://discord.com/invite/A92xrEGCge"> <a href="https://discord.com/invite/A92xrEGCge">
@ -40,22 +40,22 @@
</a> </a>
</p> </p>
Meet [Plane](https://dub.sh/plane-website-readme). An open-source software development tool to manage issues, sprints, and product roadmaps with peace of mind. 🧘‍♀️ Meet [Plane](https://dub.sh/plane-website-readme), an open-source project management tool to track issues, run ~sprints~ cycles, and manage product roadmaps without the chaos of managing the tool itself. 🧘‍♀️
> Plane is still in its early days, not everything will be perfect yet, and hiccups may happen. Please let us know of any suggestions, ideas, or bugs that you encounter on our [Discord](https://discord.com/invite/A92xrEGCge) or GitHub issues, and we will use your feedback to improve in our upcoming releases. > Plane is evolving every day. Your suggestions, ideas, and reported bugs help us immensely. Do not hesitate to join in the conversation on [Discord](https://discord.com/invite/A92xrEGCge) or raise a GitHub issue. We read everything and respond to most.
## ⚡ Installation ## ⚡ Installation
The easiest way to get started with Plane is by creating a [Plane Cloud](https://app.plane.so) account where we offer a hosted solution for users. The easiest way to get started with Plane is by creating a [Plane Cloud](https://app.plane.so) account.
If you want more control over your data, prefer to self-host Plane, please refer to our [deployment documentation](https://docs.plane.so/docker-compose). If you would like to self-host Plane, please see our [deployment guide](https://docs.plane.so/docker-compose).
| Installation Methods | Documentation Link | | Installation methods | Docs link |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Docker | [![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)](https://docs.plane.so/self-hosting/methods/docker-compose) | | Docker | [![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)](https://docs.plane.so/self-hosting/methods/docker-compose) |
| Kubernetes | [![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?style=for-the-badge&logo=kubernetes&logoColor=white)](https://docs.plane.so/kubernetes) | | Kubernetes | [![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?style=for-the-badge&logo=kubernetes&logoColor=white)](https://docs.plane.so/kubernetes) |
`Instance admin` can configure instance settings using our [God-mode](https://docs.plane.so/instance-admin) feature. `Instance admins` can configure instance settings with [God-mode](https://docs.plane.so/instance-admin).
## 🚀 Features ## 🚀 Features

44
SECURITY.md Normal file
View File

@ -0,0 +1,44 @@
# Security Policy
This document outlines security procedures and vulnerabilities reporting for the Plane project.
At Plane, we safeguarding the security of our systems with top priority. Despite our efforts, vulnerabilities may still exist. We greatly appreciate your assistance in identifying and reporting any such vulnerabilities to help us maintain the integrity of our systems and protect our clients.
To report a security vulnerability, please email us directly at security@plane.so with a detailed description of the vulnerability and steps to reproduce it. Please refrain from disclosing the vulnerability publicly until we have had an opportunity to review and address it.
## Out of Scope Vulnerabilities
We appreciate your help in identifying vulnerabilities. However, please note that the following types of vulnerabilities are considered out of scope:
- Attacks requiring MITM or physical access to a user's device.
- Content spoofing and text injection issues without demonstrating an attack vector or ability to modify HTML/CSS.
- Email spoofing.
- Missing DNSSEC, CAA, CSP headers.
- Lack of Secure or HTTP only flag on non-sensitive cookies.
## Reporting Process
If you discover a vulnerability, please adhere to the following reporting process:
1. Email your findings to security@plane.so.
2. Refrain from running automated scanners on our infrastructure or dashboard without prior consent. Contact us to set up a sandbox environment if necessary.
3. Do not exploit the vulnerability for malicious purposes, such as downloading excessive data or altering user data.
4. Maintain confidentiality and refrain from disclosing the vulnerability until it has been resolved.
5. Avoid using physical security attacks, social engineering, distributed denial of service, spam, or third-party applications.
When reporting a vulnerability, please provide sufficient information to allow us to reproduce and address the issue promptly. Include the IP address or URL of the affected system, along with a detailed description of the vulnerability.
## Our Commitment
We are committed to promptly addressing reported vulnerabilities and maintaining open communication throughout the resolution process. Here's what you can expect from us:
- **Response Time:** We will acknowledge receipt of your report within three business days and provide an expected resolution date.
- **Legal Protection:** We will not pursue legal action against you for reporting vulnerabilities, provided you adhere to the reporting guidelines.
- **Confidentiality:** Your report will be treated with strict confidentiality. We will not disclose your personal information to third parties without your consent.
- **Progress Updates:** We will keep you informed of our progress in resolving the reported vulnerability.
- **Recognition:** With your permission, we will publicly acknowledge you as the discoverer of the vulnerability.
- **Timely Resolution:** We strive to resolve all reported vulnerabilities promptly and will actively participate in the publication process once the issue is resolved.
We appreciate your cooperation in helping us maintain the security of our systems and protecting our clients. Thank you for your contributions to our security efforts.
reference: https://supabase.com/.well-known/security.txt

3
admin/.env.example Normal file
View File

@ -0,0 +1,3 @@
NEXT_PUBLIC_API_BASE_URL=""
NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
NEXT_PUBLIC_WEB_BASE_URL=""

52
admin/.eslintrc.js Normal file
View File

@ -0,0 +1,52 @@
module.exports = {
root: true,
extends: ["custom"],
parser: "@typescript-eslint/parser",
settings: {
"import/resolver": {
typescript: {},
node: {
moduleDirectory: ["node_modules", "."],
},
},
},
rules: {
"import/order": [
"error",
{
groups: ["builtin", "external", "internal", "parent", "sibling",],
pathGroups: [
{
pattern: "react",
group: "external",
position: "before",
},
{
pattern: "lucide-react",
group: "external",
position: "after",
},
{
pattern: "@headlessui/**",
group: "external",
position: "after",
},
{
pattern: "@plane/**",
group: "external",
position: "after",
},
{
pattern: "@/**",
group: "internal",
}
],
pathGroupsExcludedImportTypes: ["builtin", "internal", "react"],
alphabetize: {
order: "asc",
caseInsensitive: true,
},
},
],
},
}

6
admin/.prettierignore Normal file
View File

@ -0,0 +1,6 @@
.next
.vercel
.tubro
out/
dis/
build/

5
admin/.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"printWidth": 120,
"tabWidth": 2,
"trailingComma": "es5"
}

86
admin/Dockerfile.admin Normal file
View File

@ -0,0 +1,86 @@
# *****************************************************************************
# STAGE 1: Build the project
# *****************************************************************************
FROM node:18-alpine AS builder
RUN apk add --no-cache libc6-compat
WORKDIR /app
RUN yarn global add turbo
COPY . .
RUN turbo prune --scope=admin --docker
# *****************************************************************************
# STAGE 2: Install dependencies & build the project
# *****************************************************************************
FROM node:18-alpine AS installer
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY .gitignore .gitignore
COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/yarn.lock ./yarn.lock
RUN yarn install --network-timeout 500000
COPY --from=builder /app/out/full/ .
COPY turbo.json turbo.json
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
RUN yarn turbo run build --filter=admin
# *****************************************************************************
# STAGE 3: Copy the project and start it
# *****************************************************************************
FROM node:18-alpine AS runner
WORKDIR /app
COPY --from=installer /app/admin/next.config.js .
COPY --from=installer /app/admin/package.json .
COPY --from=installer /app/admin/.next/standalone ./
COPY --from=installer /app/admin/.next/static ./admin/.next/static
COPY --from=installer /app/admin/public ./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
EXPOSE 3000

17
admin/Dockerfile.dev Normal file
View File

@ -0,0 +1,17 @@
FROM node:18-alpine
RUN apk add --no-cache libc6-compat
# Set working directory
WORKDIR /app
COPY . .
RUN yarn global add turbo
RUN yarn install
ENV NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
EXPOSE 3000
VOLUME [ "/app/node_modules", "/app/admin/node_modules" ]
CMD ["yarn", "dev", "--filter=admin"]

128
admin/app/ai/form.tsx Normal file
View File

@ -0,0 +1,128 @@
import { FC } from "react";
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";
// components
import { ControllerInput, TControllerInputFormField } from "@/components/common";
// hooks
import { useInstance } from "@/hooks/store";
type IInstanceAIForm = {
config: IFormattedInstanceConfiguration;
};
type AIFormValues = Record<TInstanceAIConfigurationKeys, string>;
export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
const { config } = props;
// store
const { updateInstanceConfigurations } = useInstance();
// form data
const {
handleSubmit,
control,
formState: { errors, isSubmitting },
} = useForm<AIFormValues>({
defaultValues: {
OPENAI_API_KEY: config["OPENAI_API_KEY"],
GPT_ENGINE: config["GPT_ENGINE"],
},
});
const aiFormFields: TControllerInputFormField[] = [
{
key: "GPT_ENGINE",
type: "text",
label: "GPT_ENGINE",
description: (
<>
Choose an OpenAI engine.{" "}
<a
href="https://platform.openai.com/docs/models/overview"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
Learn more
</a>
</>
),
placeholder: "gpt-3.5-turbo",
error: Boolean(errors.GPT_ENGINE),
required: false,
},
{
key: "OPENAI_API_KEY",
type: "password",
label: "API key",
description: (
<>
You will find your API key{" "}
<a
href="https://platform.openai.com/api-keys"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
here.
</a>
</>
),
placeholder: "sk-asddassdfasdefqsdfasd23das3dasdcasd",
error: Boolean(errors.OPENAI_API_KEY),
required: false,
},
];
const onSubmit = async (formData: AIFormValues) => {
const payload: Partial<AIFormValues> = { ...formData };
await updateInstanceConfigurations(payload)
.then(() =>
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success",
message: "AI Settings updated successfully",
})
)
.catch((err) => console.error(err));
};
return (
<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>
<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) => (
<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}
/>
))}
</div>
</div>
<div className="space-y-4">
<Button variant="primary" 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>If you have a preferred AI models vendor, please get in touch with us.</div>
</div>
</div>
</div>
);
};

11
admin/app/ai/layout.tsx Normal file
View File

@ -0,0 +1,11 @@
import { ReactNode } from "react";
import { Metadata } from "next";
import { AdminLayout } from "@/layouts/admin-layout";
export const metadata: Metadata = {
title: "AI Settings - God Mode",
};
export default function AILayout({ children }: { children: ReactNode }) {
return <AdminLayout>{children}</AdminLayout>;
}

48
admin/app/ai/page.tsx Normal file
View File

@ -0,0 +1,48 @@
"use client";
import { observer } from "mobx-react-lite";
import useSWR from "swr";
import { Loader } from "@plane/ui";
// components
import { PageHeader } from "@/components/core";
// hooks
import { useInstance } from "@/hooks/store";
// components
import { InstanceAIForm } from "./form";
const InstanceAIPage = observer(() => {
// store
const { fetchInstanceConfigurations, formattedConfig } = useInstance();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
return (
<>
<PageHeader title="Artificial Intelligence - God Mode" />
<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.
</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>
</>
);
});
export default InstanceAIPage;

View File

@ -0,0 +1,51 @@
"use client";
import { FC } from "react";
// helpers
import { cn } from "helpers/common.helper";
type Props = {
name: string;
description: string;
icon: JSX.Element;
config: JSX.Element;
disabled?: boolean;
withBorder?: boolean;
};
export const AuthenticationMethodCard: FC<Props> = (props) => {
const { name, description, icon, config, disabled = false, withBorder = true } = props;
return (
<div
className={cn("w-full flex items-center gap-14 rounded", {
"px-4 py-3 border border-custom-border-200": withBorder,
})}
>
<div className="flex grow items-center gap-4">
<div className="shrink-0">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-custom-background-80">{icon}</div>
</div>
<div className="grow">
<div
className={cn("font-medium leading-5 text-custom-text-100", {
"text-sm": withBorder,
"text-xl": !withBorder,
})}
>
{name}
</div>
<div
className={cn("font-normal leading-5 text-custom-text-300", {
"text-xs": withBorder,
"text-sm": !withBorder,
})}
>
{description}
</div>
</div>
</div>
<div className={`shrink-0 ${disabled && "opacity-70"}`}>{config}</div>
</div>
);
};

View File

@ -0,0 +1,36 @@
"use client";
import React from "react";
import { observer } from "mobx-react-lite";
// hooks
import { TInstanceAuthenticationMethodKeys } from "@plane/types";
import { ToggleSwitch } from "@plane/ui";
import { useInstance } from "@/hooks/store";
// ui
// types
type Props = {
disabled: boolean;
updateConfig: (key: TInstanceAuthenticationMethodKeys, value: string) => void;
};
export const EmailCodesConfiguration: React.FC<Props> = observer((props) => {
const { disabled, updateConfig } = props;
// store
const { formattedConfig } = useInstance();
// derived values
const enableMagicLogin = formattedConfig?.ENABLE_MAGIC_LINK_LOGIN ?? "";
return (
<ToggleSwitch
value={Boolean(parseInt(enableMagicLogin))}
onChange={() => {
Boolean(parseInt(enableMagicLogin)) === true
? updateConfig("ENABLE_MAGIC_LINK_LOGIN", "0")
: updateConfig("ENABLE_MAGIC_LINK_LOGIN", "1");
}}
size="sm"
disabled={disabled}
/>
);
});

View File

@ -0,0 +1,59 @@
"use client";
import React from "react";
import { observer } from "mobx-react-lite";
import Link from "next/link";
// icons
import { Settings2 } from "lucide-react";
// types
import { TInstanceAuthenticationMethodKeys } from "@plane/types";
// ui
import { ToggleSwitch, getButtonStyling } from "@plane/ui";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useInstance } from "@/hooks/store";
type Props = {
disabled: boolean;
updateConfig: (key: TInstanceAuthenticationMethodKeys, value: string) => void;
};
export const GithubConfiguration: React.FC<Props> = observer((props) => {
const { disabled, updateConfig } = props;
// store
const { formattedConfig } = useInstance();
// derived values
const enableGithubConfig = formattedConfig?.IS_GITHUB_ENABLED ?? "";
const isGithubConfigured = !!formattedConfig?.GITHUB_CLIENT_ID && !!formattedConfig?.GITHUB_CLIENT_SECRET;
return (
<>
{isGithubConfigured ? (
<div className="flex items-center gap-4">
<Link href="/authentication/github" className={cn(getButtonStyling("link-primary", "md"), "font-medium")}>
Edit
</Link>
<ToggleSwitch
value={Boolean(parseInt(enableGithubConfig))}
onChange={() => {
Boolean(parseInt(enableGithubConfig)) === true
? updateConfig("IS_GITHUB_ENABLED", "0")
: updateConfig("IS_GITHUB_ENABLED", "1");
}}
size="sm"
disabled={disabled}
/>
</div>
) : (
<Link
href="/authentication/github"
className={cn(getButtonStyling("neutral-primary", "sm"), "text-custom-text-300")}
>
<Settings2 className="h-4 w-4 p-0.5 text-custom-text-300/80" />
Configure
</Link>
)}
</>
);
});

View File

@ -0,0 +1,59 @@
"use client";
import React from "react";
import { observer } from "mobx-react-lite";
import Link from "next/link";
// icons
import { Settings2 } from "lucide-react";
// types
import { TInstanceAuthenticationMethodKeys } from "@plane/types";
// ui
import { ToggleSwitch, getButtonStyling } from "@plane/ui";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useInstance } from "@/hooks/store";
type Props = {
disabled: boolean;
updateConfig: (key: TInstanceAuthenticationMethodKeys, value: string) => void;
};
export const GoogleConfiguration: React.FC<Props> = observer((props) => {
const { disabled, updateConfig } = props;
// store
const { formattedConfig } = useInstance();
// derived values
const enableGoogleConfig = formattedConfig?.IS_GOOGLE_ENABLED ?? "";
const isGoogleConfigured = !!formattedConfig?.GOOGLE_CLIENT_ID && !!formattedConfig?.GOOGLE_CLIENT_SECRET;
return (
<>
{isGoogleConfigured ? (
<div className="flex items-center gap-4">
<Link href="/authentication/google" className={cn(getButtonStyling("link-primary", "md"), "font-medium")}>
Edit
</Link>
<ToggleSwitch
value={Boolean(parseInt(enableGoogleConfig))}
onChange={() => {
Boolean(parseInt(enableGoogleConfig)) === true
? updateConfig("IS_GOOGLE_ENABLED", "0")
: updateConfig("IS_GOOGLE_ENABLED", "1");
}}
size="sm"
disabled={disabled}
/>
</div>
) : (
<Link
href="/authentication/google"
className={cn(getButtonStyling("neutral-primary", "sm"), "text-custom-text-300")}
>
<Settings2 className="h-4 w-4 p-0.5 text-custom-text-300/80" />
Configure
</Link>
)}
</>
);
});

View File

@ -0,0 +1,5 @@
export * from "./email-config-switch";
export * from "./password-config-switch";
export * from "./authentication-method-card";
export * from "./github-config";
export * from "./google-config";

View File

@ -0,0 +1,36 @@
"use client";
import React from "react";
import { observer } from "mobx-react-lite";
// hooks
import { TInstanceAuthenticationMethodKeys } from "@plane/types";
import { ToggleSwitch } from "@plane/ui";
import { useInstance } from "@/hooks/store";
// ui
// types
type Props = {
disabled: boolean;
updateConfig: (key: TInstanceAuthenticationMethodKeys, value: string) => void;
};
export const PasswordLoginConfiguration: React.FC<Props> = observer((props) => {
const { disabled, updateConfig } = props;
// store
const { formattedConfig } = useInstance();
// derived values
const enableEmailPassword = formattedConfig?.ENABLE_EMAIL_PASSWORD ?? "";
return (
<ToggleSwitch
value={Boolean(parseInt(enableEmailPassword))}
onChange={() => {
Boolean(parseInt(enableEmailPassword)) === true
? updateConfig("ENABLE_EMAIL_PASSWORD", "0")
: updateConfig("ENABLE_EMAIL_PASSWORD", "1");
}}
size="sm"
disabled={disabled}
/>
);
});

View File

@ -0,0 +1,213 @@
import { FC, useState } from "react";
import isEmpty from "lodash/isEmpty";
import Link from "next/link";
import { useForm } from "react-hook-form";
// types
import { IFormattedInstanceConfiguration, TInstanceGithubAuthenticationConfigurationKeys } from "@plane/types";
// ui
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
// components
import {
ConfirmDiscardModal,
ControllerInput,
CopyField,
TControllerInputFormField,
TCopyField,
} from "@/components/common";
// helpers
import { API_BASE_URL, cn } from "@/helpers/common.helper";
// hooks
import { useInstance } from "@/hooks/store";
type Props = {
config: IFormattedInstanceConfiguration;
};
type GithubConfigFormValues = Record<TInstanceGithubAuthenticationConfigurationKeys, string>;
export const InstanceGithubConfigForm: FC<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<GithubConfigFormValues>({
defaultValues: {
GITHUB_CLIENT_ID: config["GITHUB_CLIENT_ID"],
GITHUB_CLIENT_SECRET: config["GITHUB_CLIENT_SECRET"],
},
});
const originURL = !isEmpty(API_BASE_URL) ? API_BASE_URL : typeof window !== "undefined" ? window.location.origin : "";
const GITHUB_FORM_FIELDS: TControllerInputFormField[] = [
{
key: "GITHUB_CLIENT_ID",
type: "text",
label: "Client ID",
description: (
<>
You will get this from your{" "}
<a
tabIndex={-1}
href="https://github.com/settings/applications/new"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
GitHub OAuth application settings.
</a>
</>
),
placeholder: "70a44354520df8bd9bcd",
error: Boolean(errors.GITHUB_CLIENT_ID),
required: true,
},
{
key: "GITHUB_CLIENT_SECRET",
type: "password",
label: "Client secret",
description: (
<>
Your client secret is also found in your{" "}
<a
tabIndex={-1}
href="https://github.com/settings/applications/new"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
GitHub OAuth application settings.
</a>
</>
),
placeholder: "9b0050f94ec1b744e32ce79ea4ffacd40d4119cb",
error: Boolean(errors.GITHUB_CLIENT_SECRET),
required: true,
},
];
const GITHUB_SERVICE_FIELD: TCopyField[] = [
{
key: "Origin_URL",
label: "Origin URL",
url: originURL,
description: (
<>
We will auto-generate this. Paste this into the Authorized origin URL field{" "}
<a
tabIndex={-1}
href="https://github.com/settings/applications/new"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
here.
</a>
</>
),
},
{
key: "Callback_URI",
label: "Callback URI",
url: `${originURL}/auth/github/callback/`,
description: (
<>
We will auto-generate this. Paste this into your Authorized Callback URI field{" "}
<a
tabIndex={-1}
href="https://github.com/settings/applications/new"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
here.
</a>
</>
),
},
];
const onSubmit = async (formData: GithubConfigFormValues) => {
const payload: Partial<GithubConfigFormValues> = { ...formData };
await updateInstanceConfigurations(payload)
.then((response = []) => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success",
message: "Github Configuration Settings updated successfully",
});
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,
});
})
.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">
<div className="pt-2 text-xl font-medium">Configuration</div>
{GITHUB_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}
/>
))}
<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("link-neutral", "md"), "font-medium")}
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 py-4 my-2 bg-custom-background-80/60 rounded-lg">
<div className="pt-2 text-xl font-medium">Service provider details</div>
{GITHUB_SERVICE_FIELD.map((field) => (
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
))}
</div>
</div>
</div>
</div>
</>
);
};

View File

@ -0,0 +1,114 @@
"use client";
import { useState } from "react";
import { observer } from "mobx-react-lite";
import Image from "next/image";
import { useTheme } from "next-themes";
import useSWR from "swr";
import { Loader, ToggleSwitch, setPromiseToast } from "@plane/ui";
// components
import { PageHeader } from "@/components/core";
// helpers
import { resolveGeneralTheme } from "@/helpers/common.helper";
// 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
import { AuthenticationMethodCard } from "../components";
import { InstanceGithubConfigForm } from "./form";
const InstanceGithubAuthenticationPage = observer(() => {
// store
const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance();
// state
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
// theme
const { resolvedTheme } = useTheme();
// config
const enableGithubConfig = formattedConfig?.IS_GITHUB_ENABLED ?? "";
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
const updateConfig = async (key: "IS_GITHUB_ENABLED", value: string) => {
setIsSubmitting(true);
const payload = {
[key]: value,
};
const updateConfigPromise = updateInstanceConfigurations(payload);
setPromiseToast(updateConfigPromise, {
loading: "Saving Configuration...",
success: {
title: "Configuration saved",
message: () => `Github authentication is now ${value ? "active" : "disabled"}.`,
},
error: {
title: "Error",
message: () => "Failed to save configuration",
},
});
await updateConfigPromise
.then(() => {
setIsSubmitting(false);
})
.catch((err) => {
console.error(err);
setIsSubmitting(false);
});
};
return (
<>
<PageHeader title="Authentication - God Mode" />
<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={Boolean(parseInt(enableGithubConfig))}
onChange={() => {
Boolean(parseInt(enableGithubConfig)) === true
? updateConfig("IS_GITHUB_ENABLED", "0")
: updateConfig("IS_GITHUB_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 p-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>
</>
);
});
export default InstanceGithubAuthenticationPage;

View File

@ -0,0 +1,211 @@
import { FC, useState } from "react";
import isEmpty from "lodash/isEmpty";
import Link from "next/link";
import { useForm } from "react-hook-form";
// types
import { IFormattedInstanceConfiguration, TInstanceGoogleAuthenticationConfigurationKeys } from "@plane/types";
// ui
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
// components
import {
ConfirmDiscardModal,
ControllerInput,
CopyField,
TControllerInputFormField,
TCopyField,
} from "@/components/common";
// helpers
import { API_BASE_URL, cn } from "@/helpers/common.helper";
// hooks
import { useInstance } from "@/hooks/store";
type Props = {
config: IFormattedInstanceConfiguration;
};
type GoogleConfigFormValues = Record<TInstanceGoogleAuthenticationConfigurationKeys, string>;
export const InstanceGoogleConfigForm: FC<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<GoogleConfigFormValues>({
defaultValues: {
GOOGLE_CLIENT_ID: config["GOOGLE_CLIENT_ID"],
GOOGLE_CLIENT_SECRET: config["GOOGLE_CLIENT_SECRET"],
},
});
const originURL = !isEmpty(API_BASE_URL) ? API_BASE_URL : typeof window !== "undefined" ? window.location.origin : "";
const GOOGLE_FORM_FIELDS: TControllerInputFormField[] = [
{
key: "GOOGLE_CLIENT_ID",
type: "text",
label: "Client ID",
description: (
<>
Your client ID lives in your Google API Console.{" "}
<a
tabIndex={-1}
href="https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow#creatingcred"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
Learn more
</a>
</>
),
placeholder: "840195096245-0p2tstej9j5nc4l8o1ah2dqondscqc1g.apps.googleusercontent.com",
error: Boolean(errors.GOOGLE_CLIENT_ID),
required: true,
},
{
key: "GOOGLE_CLIENT_SECRET",
type: "password",
label: "Client secret",
description: (
<>
Your client secret should also be in your Google API Console.{" "}
<a
tabIndex={-1}
href="https://developers.google.com/identity/oauth2/web/guides/get-google-api-clientid"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
Learn more
</a>
</>
),
placeholder: "GOCShX-ADp4cI0kPqav1gGCBg5bE02E",
error: Boolean(errors.GOOGLE_CLIENT_SECRET),
required: true,
},
];
const GOOGLE_SERVICE_DETAILS: TCopyField[] = [
{
key: "Origin_URL",
label: "Origin URL",
url: originURL,
description: (
<p>
We will auto-generate this. Paste this into your Authorized JavaScript origins field. For this OAuth client{" "}
<a
href="https://console.cloud.google.com/apis/credentials/oauthclient"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
here.
</a>
</p>
),
},
{
key: "Callback_URI",
label: "Callback URI",
url: `${originURL}/auth/google/callback/`,
description: (
<p>
We will auto-generate this. Paste this into your Authorized Redirect URI field. For this OAuth client{" "}
<a
href="https://console.cloud.google.com/apis/credentials/oauthclient"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
here.
</a>
</p>
),
},
];
const onSubmit = async (formData: GoogleConfigFormValues) => {
const payload: Partial<GoogleConfigFormValues> = { ...formData };
await updateInstanceConfigurations(payload)
.then((response = []) => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success",
message: "Google Configuration Settings updated successfully",
});
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));
};
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">
<div className="pt-2 text-xl font-medium">Configuration</div>
{GOOGLE_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}
/>
))}
<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("link-neutral", "md"), "font-medium")}
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 py-4 my-2 bg-custom-background-80/60 rounded-lg">
<div className="pt-2 text-xl font-medium">Service provider details</div>
{GOOGLE_SERVICE_DETAILS.map((field) => (
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
))}
</div>
</div>
</div>
</div>
</>
);
};

View File

@ -0,0 +1,102 @@
"use client";
import { useState } from "react";
import { observer } from "mobx-react-lite";
import Image from "next/image";
import useSWR from "swr";
import { Loader, ToggleSwitch, setPromiseToast } from "@plane/ui";
// components
import { PageHeader } from "@/components/core";
// hooks
import { useInstance } from "@/hooks/store";
// icons
import GoogleLogo from "@/public/logos/google-logo.svg";
// local components
import { AuthenticationMethodCard } from "../components";
import { InstanceGoogleConfigForm } from "./form";
const InstanceGoogleAuthenticationPage = observer(() => {
// store
const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance();
// state
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
// config
const enableGoogleConfig = formattedConfig?.IS_GOOGLE_ENABLED ?? "";
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
const updateConfig = async (key: "IS_GOOGLE_ENABLED", value: string) => {
setIsSubmitting(true);
const payload = {
[key]: value,
};
const updateConfigPromise = updateInstanceConfigurations(payload);
setPromiseToast(updateConfigPromise, {
loading: "Saving Configuration...",
success: {
title: "Configuration saved",
message: () => `Google authentication is now ${value ? "active" : "disabled"}.`,
},
error: {
title: "Error",
message: () => "Failed to save configuration",
},
});
await updateConfigPromise
.then(() => {
setIsSubmitting(false);
})
.catch((err) => {
console.error(err);
setIsSubmitting(false);
});
};
return (
<>
<PageHeader title="Authentication - God Mode" />
<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
accounts."
icon={<Image src={GoogleLogo} height={24} width={24} alt="Google Logo" />}
config={
<ToggleSwitch
value={Boolean(parseInt(enableGoogleConfig))}
onChange={() => {
Boolean(parseInt(enableGoogleConfig)) === true
? updateConfig("IS_GOOGLE_ENABLED", "0")
: 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 p-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>
</>
);
});
export default InstanceGoogleAuthenticationPage;

View File

@ -0,0 +1,11 @@
import { ReactNode } from "react";
import { Metadata } from "next";
import { AdminLayout } from "@/layouts/admin-layout";
export const metadata: Metadata = {
title: "Authentication Settings - God Mode",
};
export default function AuthenticationLayout({ children }: { children: ReactNode }) {
return <AdminLayout>{children}</AdminLayout>;
}

View File

@ -0,0 +1,159 @@
"use client";
import { useState } from "react";
import { observer } from "mobx-react-lite";
import Image from "next/image";
import { useTheme } from "next-themes";
import useSWR from "swr";
import { Mails, KeyRound } from "lucide-react";
import { TInstanceConfigurationKeys } from "@plane/types";
import { Loader, setPromiseToast } from "@plane/ui";
// components
import { PageHeader } from "@/components/core";
// hooks
// helpers
import { resolveGeneralTheme } from "@/helpers/common.helper";
import { useInstance } from "@/hooks/store";
// images
import githubLightModeImage from "@/public/logos/github-black.png";
import githubDarkModeImage from "@/public/logos/github-white.png";
import GoogleLogo from "@/public/logos/google-logo.svg";
// local components
import {
AuthenticationMethodCard,
EmailCodesConfiguration,
PasswordLoginConfiguration,
GithubConfiguration,
GoogleConfiguration,
} from "./components";
type TInstanceAuthenticationMethodCard = {
key: string;
name: string;
description: string;
icon: JSX.Element;
config: JSX.Element;
};
const InstanceAuthenticationPage = observer(() => {
// store
const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
// state
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
// theme
const { resolvedTheme } = useTheme();
const updateConfig = async (key: TInstanceConfigurationKeys, value: string) => {
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",
},
});
await updateConfigPromise
.then(() => {
setIsSubmitting(false);
})
.catch((err) => {
console.error(err);
setIsSubmitting(false);
});
};
// Authentication methods
const authenticationMethodsCard: TInstanceAuthenticationMethodCard[] = [
{
key: "email-codes",
name: "Email codes",
description: "Login or sign up using codes sent via emails. You need to have email setup here and enabled.",
icon: <Mails className="h-6 w-6 p-0.5 text-custom-text-300/80" />,
config: <EmailCodesConfiguration disabled={isSubmitting} updateConfig={updateConfig} />,
},
{
key: "password-login",
name: "Password based login",
description: "Allow members to create accounts with passwords for emails to sign in.",
icon: <KeyRound className="h-6 w-6 p-0.5 text-custom-text-300/80" />,
config: <PasswordLoginConfiguration disabled={isSubmitting} updateConfig={updateConfig} />,
},
{
key: "google",
name: "Google",
description: "Allow members to login or sign up to plane with their Google accounts.",
icon: <Image src={GoogleLogo} height={20} width={20} alt="Google Logo" />,
config: <GoogleConfiguration disabled={isSubmitting} updateConfig={updateConfig} />,
},
{
key: "github",
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={20}
width={20}
alt="GitHub Logo"
/>
),
config: <GithubConfiguration disabled={isSubmitting} updateConfig={updateConfig} />,
},
];
return (
<>
<PageHeader title="Authentication - God Mode" />
<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 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="text-lg font-medium">Authentication modes</div>
{authenticationMethodsCard.map((method) => (
<AuthenticationMethodCard
key={method.key}
name={method.name}
description={method.description}
icon={method.icon}
config={method.config}
disabled={isSubmitting}
/>
))}
</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>
</div>
</>
);
});
export default InstanceAuthenticationPage;

View File

@ -0,0 +1,222 @@
import React, { FC, useMemo, useState } from "react";
import { useForm } from "react-hook-form";
// types
import { IFormattedInstanceConfiguration, TInstanceEmailConfigurationKeys } from "@plane/types";
// ui
import { Button, CustomSelect, TOAST_TYPE, setToast } from "@plane/ui";
// components
import { ControllerInput, TControllerInputFormField } from "@/components/common";
// hooks
import { useInstance } from "@/hooks/store";
// local components
import { SendTestEmailModal } from "./test-email-modal";
type IInstanceEmailForm = {
config: IFormattedInstanceConfiguration;
};
type EmailFormValues = Record<TInstanceEmailConfigurationKeys, string>;
type TEmailSecurityKeys = "EMAIL_USE_TLS" | "EMAIL_USE_SSL" | "NONE";
const EMAIL_SECURITY_OPTIONS: { [key in TEmailSecurityKeys]: string } = {
EMAIL_USE_TLS: "TLS",
EMAIL_USE_SSL: "SSL",
NONE: "No email security",
};
export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
const { config } = props;
// states
const [isSendTestEmailModalOpen, setIsSendTestEmailModalOpen] = useState(false);
// store hooks
const { updateInstanceConfigurations } = useInstance();
// form data
const {
handleSubmit,
watch,
setValue,
control,
formState: { errors, isValid, isDirty, isSubmitting },
} = useForm<EmailFormValues>({
defaultValues: {
EMAIL_HOST: config["EMAIL_HOST"],
EMAIL_PORT: config["EMAIL_PORT"],
EMAIL_HOST_USER: config["EMAIL_HOST_USER"],
EMAIL_HOST_PASSWORD: config["EMAIL_HOST_PASSWORD"],
EMAIL_USE_TLS: config["EMAIL_USE_TLS"],
EMAIL_USE_SSL: config["EMAIL_USE_SSL"],
EMAIL_FROM: config["EMAIL_FROM"],
},
});
const emailFormFields: TControllerInputFormField[] = [
{
key: "EMAIL_HOST",
type: "text",
label: "Host",
placeholder: "email.google.com",
error: Boolean(errors.EMAIL_HOST),
required: true,
},
{
key: "EMAIL_PORT",
type: "text",
label: "Port",
placeholder: "8080",
error: Boolean(errors.EMAIL_PORT),
required: true,
},
{
key: "EMAIL_FROM",
type: "text",
label: "Sender email address",
description:
"This is the email address your users will see when getting emails from this instance. You will need to verify this address.",
placeholder: "no-reply@projectplane.so",
error: Boolean(errors.EMAIL_FROM),
required: true,
},
];
const OptionalEmailFormFields: TControllerInputFormField[] = [
{
key: "EMAIL_HOST_USER",
type: "text",
label: "Username",
placeholder: "getitdone@projectplane.so",
error: Boolean(errors.EMAIL_HOST_USER),
required: false,
},
{
key: "EMAIL_HOST_PASSWORD",
type: "password",
label: "Password",
placeholder: "Password",
error: Boolean(errors.EMAIL_HOST_PASSWORD),
required: false,
},
];
const onSubmit = async (formData: EmailFormValues) => {
const payload: Partial<EmailFormValues> = { ...formData };
await updateInstanceConfigurations(payload)
.then(() =>
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success",
message: "Email Settings updated successfully",
})
)
.catch((err) => console.error(err));
};
const useTLSValue = watch("EMAIL_USE_TLS");
const useSSLValue = watch("EMAIL_USE_SSL");
const emailSecurityKey: TEmailSecurityKeys = useMemo(() => {
if (useTLSValue === "1") return "EMAIL_USE_TLS";
if (useSSLValue === "1") return "EMAIL_USE_SSL";
return "NONE";
}, [useTLSValue, useSSLValue]);
const handleEmailSecurityChange = (key: TEmailSecurityKeys) => {
if (key === "EMAIL_USE_SSL") {
setValue("EMAIL_USE_TLS", "0");
setValue("EMAIL_USE_SSL", "1");
}
if (key === "EMAIL_USE_TLS") {
setValue("EMAIL_USE_TLS", "1");
setValue("EMAIL_USE_SSL", "0");
}
if (key === "NONE") {
setValue("EMAIL_USE_TLS", "0");
setValue("EMAIL_USE_SSL", "0");
}
};
return (
<div className="space-y-8">
<div>
<SendTestEmailModal isOpen={isSendTestEmailModalOpen} handleClose={() => setIsSendTestEmailModalOpen(false)} />
<div className="grid-col grid w-full max-w-4xl grid-cols-1 items-start justify-between gap-10 lg:grid-cols-2">
{emailFormFields.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}
/>
))}
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-300">Email security</h4>
<CustomSelect
value={emailSecurityKey}
label={EMAIL_SECURITY_OPTIONS[emailSecurityKey]}
onChange={handleEmailSecurityChange}
buttonClassName="rounded-md border-custom-border-200"
optionsClassName="w-full"
input
>
{Object.entries(EMAIL_SECURITY_OPTIONS).map(([key, value]) => (
<CustomSelect.Option key={key} value={key} className="w-full">
{value}
</CustomSelect.Option>
))}
</CustomSelect>
</div>
</div>
<div className="flex flex-col gap-6 my-6 pt-4 border-t border-custom-border-100">
<div className="flex w-full max-w-md 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 (optional)</div>
<div className="text-xs font-normal text-custom-text-300">
We recommend setting up a username password for your SMTP server
</div>
</div>
</div>
</div>
<div className="grid-col grid w-full max-w-4xl grid-cols-1 items-center justify-between gap-10 lg:grid-cols-2">
{OptionalEmailFormFields.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}
/>
))}
</div>
</div>
</div>
<div className="flex max-w-4xl items-center py-1 gap-4">
<Button
variant="primary"
onClick={handleSubmit(onSubmit)}
loading={isSubmitting}
disabled={!isValid || !isDirty}
>
{isSubmitting ? "Saving..." : "Save changes"}
</Button>
<Button
variant="outline-primary"
onClick={() => setIsSendTestEmailModalOpen(true)}
loading={isSubmitting}
disabled={!isValid}
>
Send test email
</Button>
</div>
</div>
);
};

View File

@ -0,0 +1,15 @@
import { ReactNode } from "react";
import { Metadata } from "next";
import { AdminLayout } from "@/layouts/admin-layout";
interface EmailLayoutProps {
children: ReactNode;
}
export const metadata: Metadata = {
title: "Email Settings - God Mode",
};
const EmailLayout = ({ children }: EmailLayoutProps) => <AdminLayout>{children}</AdminLayout>;
export default EmailLayout;

51
admin/app/email/page.tsx Normal file
View File

@ -0,0 +1,51 @@
"use client";
import { observer } from "mobx-react-lite";
import useSWR from "swr";
import { Loader } from "@plane/ui";
// components
import { PageHeader } from "@/components/core";
// hooks
import { useInstance } from "@/hooks/store";
// components
import { InstanceEmailForm } from "./email-config-form";
const InstanceEmailPage = observer(() => {
// store
const { fetchInstanceConfigurations, formattedConfig } = useInstance();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
return (
<>
<PageHeader title="Email - God Mode" />
<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">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>
</div>
</div>
<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>
</>
);
});
export default InstanceEmailPage;

View File

@ -0,0 +1,135 @@
import React, { FC, useEffect, useState } from "react";
import { Dialog, Transition } from "@headlessui/react";
// ui
import { Button, Input } from "@plane/ui";
// services
import { InstanceService } from "@/services/instance.service";
type Props = {
isOpen: boolean;
handleClose: () => void;
};
enum ESendEmailSteps {
SEND_EMAIL = "SEND_EMAIL",
SUCCESS = "SUCCESS",
FAILED = "FAILED",
}
const instanceService = new InstanceService();
export const SendTestEmailModal: FC<Props> = (props) => {
const { isOpen, handleClose } = props;
// state
const [receiverEmail, setReceiverEmail] = useState("");
const [sendEmailStep, setSendEmailStep] = useState<ESendEmailSteps>(ESendEmailSteps.SEND_EMAIL);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState("");
// reset state
const resetState = () => {
setReceiverEmail("");
setSendEmailStep(ESendEmailSteps.SEND_EMAIL);
setIsLoading(false);
setError("");
};
useEffect(() => {
if (!isOpen) {
resetState();
}
}, [isOpen]);
const handleSubmit = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.preventDefault();
setIsLoading(true);
await instanceService
.sendTestEmail(receiverEmail)
.then(() => {
setSendEmailStep(ESendEmailSteps.SUCCESS);
})
.catch((error) => {
setError(error?.error || "Failed to send email");
setSendEmailStep(ESendEmailSteps.FAILED);
})
.finally(() => {
setIsLoading(false);
});
};
return (
<Transition.Root show={isOpen} as={React.Fragment}>
<Dialog as="div" className="relative z-20" onClose={handleClose}>
<Transition.Child
as={React.Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-custom-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}
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"
leave="ease-in duration-200"
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">
{sendEmailStep === ESendEmailSteps.SEND_EMAIL
? "Send test email"
: sendEmailStep === ESendEmailSteps.SUCCESS
? "Email send"
: "Failed"}{" "}
</h3>
<div className="pt-6 pb-2">
{sendEmailStep === ESendEmailSteps.SEND_EMAIL && (
<Input
id="receiver_email"
type="email"
value={receiverEmail}
onChange={(e) => setReceiverEmail(e.target.value)}
placeholder="Receiver email"
className="w-full resize-none text-lg"
tabIndex={1}
/>
)}
{sendEmailStep === ESendEmailSteps.SUCCESS && (
<div className="flex flex-col gap-y-4 text-sm">
<p>
We have sent the test email to {receiverEmail}. Please check your spam folder if you cannot find
it.
</p>
<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>}
<div className="flex items-center gap-2 justify-end mt-5">
<Button variant="neutral-primary" size="sm" 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>
)}
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
};

9
admin/app/error.tsx Normal file
View File

@ -0,0 +1,9 @@
"use client";
export default function RootErrorPage() {
return (
<div>
<p>Something went wrong.</p>
</div>
);
}

140
admin/app/general/form.tsx Normal file
View File

@ -0,0 +1,140 @@
"use client";
import { FC } from "react";
import { observer } from "mobx-react-lite";
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";
// components
import { ControllerInput } from "@/components/common";
// hooks
import { useInstance } from "@/hooks/store";
export interface IGeneralConfigurationForm {
instance: IInstance;
instanceAdmins: IInstanceAdmin[];
}
export const GeneralConfigurationForm: FC<IGeneralConfigurationForm> = observer((props) => {
const { instance, instanceAdmins } = props;
// hooks
const { updateInstanceInfo } = useInstance();
// form data
const {
handleSubmit,
control,
formState: { errors, isSubmitting },
} = useForm<Partial<IInstance>>({
defaultValues: {
instance_name: instance?.instance_name,
is_telemetry_enabled: instance?.is_telemetry_enabled,
},
});
const onSubmit = async (formData: Partial<IInstance>) => {
const payload: Partial<IInstance> = { ...formData };
console.log("payload", payload);
await updateInstanceInfo(payload)
.then(() =>
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success",
message: "Settings updated successfully",
})
)
.catch((err) => console.error(err));
};
return (
<div className="space-y-8">
<div className="space-y-3">
<div className="text-lg font-medium">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"
name="instance_name"
control={control}
type="text"
label="Name of instance"
placeholder="Instance name"
error={Boolean(errors.instance_name)}
required
/>
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-300">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"
disabled
/>
</div>
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-300">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"
disabled
/>
</div>
</div>
</div>
<div className="space-y-3">
<div className="text-lg font-medium">Telemetry</div>
<div className="flex items-center gap-14 px-4 py-3 border border-custom-border-200 rounded">
<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>
</div>
<div className="grow">
<div className="text-sm font-medium text-custom-text-100 leading-5">
Allow Plane to collect anonymous usage events
</div>
<div className="text-xs font-normal text-custom-text-300 leading-5">
We collect usage events without any PII to analyse and improve Plane.{" "}
<a
href="https://docs.plane.so/self-hosting/telemetry"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
Know more.
</a>
</div>
</div>
</div>
<div className={`shrink-0 ${isSubmitting && "opacity-70"}`}>
<Controller
control={control}
name="is_telemetry_enabled"
render={({ field: { value, onChange } }) => (
<ToggleSwitch value={value ?? false} onChange={onChange} size="sm" disabled={isSubmitting} />
)}
/>
</div>
</div>
</div>
<div>
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
{isSubmitting ? "Saving..." : "Save changes"}
</Button>
</div>
</div>
);
});

View File

@ -0,0 +1,11 @@
import { ReactNode } from "react";
import { Metadata } from "next";
import { AdminLayout } from "@/layouts/admin-layout";
export const metadata: Metadata = {
title: "General Settings - God Mode",
};
export default function GeneralLayout({ children }: { children: ReactNode }) {
return <AdminLayout>{children}</AdminLayout>;
}

View File

@ -0,0 +1,31 @@
"use client";
import { observer } from "mobx-react-lite";
// hooks
import { useInstance } from "@/hooks/store";
// components
import { GeneralConfigurationForm } from "./form";
function GeneralPage() {
const { instance, instanceAdmins } = useInstance();
console.log("instance", instance);
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>
</>
);
}
export default observer(GeneralPage);

432
admin/app/globals.css Normal file
View File

@ -0,0 +1,432 @@
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600;700;800&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@48,400,0,0&display=swap");
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.text-1\.5xl {
font-size: 1.375rem;
line-height: 1.875rem;
}
.text-2\.5xl {
font-size: 1.75rem;
line-height: 2.25rem;
}
}
@layer base {
html {
font-family: "Inter", sans-serif;
}
:root {
color-scheme: light !important;
--color-primary-10: 236, 241, 255;
--color-primary-20: 217, 228, 255;
--color-primary-30: 197, 214, 255;
--color-primary-40: 178, 200, 255;
--color-primary-50: 159, 187, 255;
--color-primary-60: 140, 173, 255;
--color-primary-70: 121, 159, 255;
--color-primary-80: 101, 145, 255;
--color-primary-90: 82, 132, 255;
--color-primary-100: 63, 118, 255;
--color-primary-200: 57, 106, 230;
--color-primary-300: 50, 94, 204;
--color-primary-400: 44, 83, 179;
--color-primary-500: 38, 71, 153;
--color-primary-600: 32, 59, 128;
--color-primary-700: 25, 47, 102;
--color-primary-800: 19, 35, 76;
--color-primary-900: 13, 24, 51;
--color-background-100: 255, 255, 255; /* primary bg */
--color-background-90: 247, 247, 247; /* secondary bg */
--color-background-80: 232, 232, 232; /* tertiary bg */
--color-text-100: 23, 23, 23; /* primary text */
--color-text-200: 58, 58, 58; /* secondary text */
--color-text-300: 82, 82, 82; /* tertiary text */
--color-text-400: 163, 163, 163; /* placeholder text */
--color-scrollbar: 163, 163, 163; /* scrollbar thumb */
--color-border-100: 245, 245, 245; /* subtle border= 1 */
--color-border-200: 229, 229, 229; /* subtle border- 2 */
--color-border-300: 212, 212, 212; /* strong border- 1 */
--color-border-400: 185, 185, 185; /* strong border- 2 */
--color-shadow-2xs: 0px 0px 1px 0px rgba(23, 23, 23, 0.06), 0px 1px 2px 0px rgba(23, 23, 23, 0.06),
0px 1px 2px 0px rgba(23, 23, 23, 0.14);
--color-shadow-xs: 0px 1px 2px 0px rgba(0, 0, 0, 0.16), 0px 2px 4px 0px rgba(16, 24, 40, 0.12),
0px 1px 8px -1px rgba(16, 24, 40, 0.1);
--color-shadow-sm: 0px 1px 4px 0px rgba(0, 0, 0, 0.01), 0px 4px 8px 0px rgba(0, 0, 0, 0.02),
0px 1px 12px 0px rgba(0, 0, 0, 0.12);
--color-shadow-rg: 0px 3px 6px 0px rgba(0, 0, 0, 0.1), 0px 4px 4px 0px rgba(16, 24, 40, 0.08),
0px 1px 12px 0px rgba(16, 24, 40, 0.04);
--color-shadow-md: 0px 4px 8px 0px rgba(0, 0, 0, 0.12), 0px 6px 12px 0px rgba(16, 24, 40, 0.12),
0px 1px 16px 0px rgba(16, 24, 40, 0.12);
--color-shadow-lg: 0px 6px 12px 0px rgba(0, 0, 0, 0.12), 0px 8px 16px 0px rgba(0, 0, 0, 0.12),
0px 1px 24px 0px rgba(16, 24, 40, 0.12);
--color-shadow-xl: 0px 0px 18px 0px rgba(0, 0, 0, 0.16), 0px 0px 24px 0px rgba(16, 24, 40, 0.16),
0px 0px 52px 0px rgba(16, 24, 40, 0.16);
--color-shadow-2xl: 0px 8px 16px 0px rgba(0, 0, 0, 0.12), 0px 12px 24px 0px rgba(16, 24, 40, 0.12),
0px 1px 32px 0px rgba(16, 24, 40, 0.12);
--color-shadow-3xl: 0px 12px 24px 0px rgba(0, 0, 0, 0.12), 0px 16px 32px 0px rgba(0, 0, 0, 0.12),
0px 1px 48px 0px rgba(16, 24, 40, 0.12);
--color-shadow-4xl: 0px 8px 40px 0px rgba(0, 0, 61, 0.05), 0px 12px 32px -16px rgba(0, 0, 0, 0.05);
--color-sidebar-background-100: var(--color-background-100); /* primary sidebar bg */
--color-sidebar-background-90: var(--color-background-90); /* secondary sidebar bg */
--color-sidebar-background-80: var(--color-background-80); /* tertiary sidebar bg */
--color-sidebar-text-100: var(--color-text-100); /* primary sidebar text */
--color-sidebar-text-200: var(--color-text-200); /* secondary sidebar text */
--color-sidebar-text-300: var(--color-text-300); /* tertiary sidebar text */
--color-sidebar-text-400: var(--color-text-400); /* sidebar placeholder text */
--color-sidebar-border-100: var(--color-border-100); /* subtle sidebar border= 1 */
--color-sidebar-border-200: var(--color-border-100); /* subtle sidebar border- 2 */
--color-sidebar-border-300: var(--color-border-100); /* strong sidebar border- 1 */
--color-sidebar-border-400: var(--color-border-100); /* strong sidebar border- 2 */
--color-sidebar-shadow-2xs: var(--color-shadow-2xs);
--color-sidebar-shadow-xs: var(--color-shadow-xs);
--color-sidebar-shadow-sm: var(--color-shadow-sm);
--color-sidebar-shadow-rg: var(--color-shadow-rg);
--color-sidebar-shadow-md: var(--color-shadow-md);
--color-sidebar-shadow-lg: var(--color-shadow-lg);
--color-sidebar-shadow-xl: var(--color-shadow-xl);
--color-sidebar-shadow-2xl: var(--color-shadow-2xl);
--color-sidebar-shadow-3xl: var(--color-shadow-3xl);
--color-sidebar-shadow-4xl: var(--color-shadow-4xl);
}
[data-theme="light"],
[data-theme="light-contrast"] {
color-scheme: light !important;
--color-background-100: 255, 255, 255; /* primary bg */
--color-background-90: 247, 247, 247; /* secondary bg */
--color-background-80: 232, 232, 232; /* tertiary bg */
}
[data-theme="light"] {
--color-text-100: 23, 23, 23; /* primary text */
--color-text-200: 58, 58, 58; /* secondary text */
--color-text-300: 82, 82, 82; /* tertiary text */
--color-text-400: 163, 163, 163; /* placeholder text */
--color-scrollbar: 163, 163, 163; /* scrollbar thumb */
--color-border-100: 245, 245, 245; /* subtle border= 1 */
--color-border-200: 229, 229, 229; /* subtle border- 2 */
--color-border-300: 212, 212, 212; /* strong border- 1 */
--color-border-400: 185, 185, 185; /* strong border- 2 */
/* onboarding colors */
--gradient-onboarding-100: linear-gradient(106deg, #f2f6ff 29.8%, #e1eaff 99.34%);
--gradient-onboarding-200: linear-gradient(129deg, rgba(255, 255, 255, 0) -22.23%, rgba(255, 255, 255, 0.8) 62.98%);
--gradient-onboarding-300: linear-gradient(164deg, #fff 4.25%, rgba(255, 255, 255, 0.06) 93.5%);
--gradient-onboarding-400: linear-gradient(129deg, rgba(255, 255, 255, 0) -22.23%, rgba(255, 255, 255, 0.8) 62.98%);
--color-onboarding-text-100: 23, 23, 23;
--color-onboarding-text-200: 58, 58, 58;
--color-onboarding-text-300: 82, 82, 82;
--color-onboarding-text-400: 163, 163, 163;
--color-onboarding-background-100: 236, 241, 255;
--color-onboarding-background-200: 255, 255, 255;
--color-onboarding-background-300: 236, 241, 255;
--color-onboarding-background-400: 177, 206, 250;
--color-onboarding-border-100: 229, 229, 229;
--color-onboarding-border-200: 217, 228, 255;
--color-onboarding-border-300: 229, 229, 229, 0.5;
--color-onboarding-shadow-sm: 0px 4px 20px 0px rgba(126, 139, 171, 0.1);
/* toast theme */
--color-toast-success-text: 62, 155, 79;
--color-toast-error-text: 220, 62, 66;
--color-toast-warning-text: 255, 186, 24;
--color-toast-info-text: 51, 88, 212;
--color-toast-loading-text: 28, 32, 36;
--color-toast-secondary-text: 128, 131, 141;
--color-toast-tertiary-text: 96, 100, 108;
--color-toast-success-background: 253, 253, 254;
--color-toast-error-background: 255, 252, 252;
--color-toast-warning-background: 254, 253, 251;
--color-toast-info-background: 253, 253, 254;
--color-toast-loading-background: 253, 253, 254;
--color-toast-success-border: 218, 241, 219;
--color-toast-error-border: 255, 219, 220;
--color-toast-warning-border: 255, 247, 194;
--color-toast-info-border: 210, 222, 255;
--color-toast-loading-border: 224, 225, 230;
}
[data-theme="light-contrast"] {
--color-text-100: 11, 11, 11; /* primary text */
--color-text-200: 38, 38, 38; /* secondary text */
--color-text-300: 58, 58, 58; /* tertiary text */
--color-text-400: 115, 115, 115; /* placeholder text */
--color-scrollbar: 115, 115, 115; /* scrollbar thumb */
--color-border-100: 34, 34, 34; /* subtle border= 1 */
--color-border-200: 38, 38, 38; /* subtle border- 2 */
--color-border-300: 46, 46, 46; /* strong border- 1 */
--color-border-400: 58, 58, 58; /* strong border- 2 */
}
[data-theme="dark"],
[data-theme="dark-contrast"] {
color-scheme: dark !important;
--color-background-100: 25, 25, 25; /* primary bg */
--color-background-90: 32, 32, 32; /* secondary bg */
--color-background-80: 44, 44, 44; /* tertiary bg */
--color-shadow-2xs: 0px 0px 1px 0px rgba(0, 0, 0, 0.15), 0px 1px 3px 0px rgba(0, 0, 0, 0.5);
--color-shadow-xs: 0px 0px 2px 0px rgba(0, 0, 0, 0.2), 0px 2px 4px 0px rgba(0, 0, 0, 0.5);
--color-shadow-sm: 0px 0px 4px 0px rgba(0, 0, 0, 0.2), 0px 2px 6px 0px rgba(0, 0, 0, 0.5);
--color-shadow-rg: 0px 0px 6px 0px rgba(0, 0, 0, 0.2), 0px 4px 6px 0px rgba(0, 0, 0, 0.5);
--color-shadow-md: 0px 2px 8px 0px rgba(0, 0, 0, 0.2), 0px 4px 8px 0px rgba(0, 0, 0, 0.5);
--color-shadow-lg: 0px 4px 12px 0px rgba(0, 0, 0, 0.25), 0px 4px 10px 0px rgba(0, 0, 0, 0.55);
--color-shadow-xl: 0px 0px 14px 0px rgba(0, 0, 0, 0.25), 0px 6px 10px 0px rgba(0, 0, 0, 0.55);
--color-shadow-2xl: 0px 0px 18px 0px rgba(0, 0, 0, 0.25), 0px 8px 12px 0px rgba(0, 0, 0, 0.6);
--color-shadow-3xl: 0px 4px 24px 0px rgba(0, 0, 0, 0.3), 0px 12px 40px 0px rgba(0, 0, 0, 0.65);
}
[data-theme="dark"] {
--color-text-100: 229, 229, 229; /* primary text */
--color-text-200: 163, 163, 163; /* secondary text */
--color-text-300: 115, 115, 115; /* tertiary text */
--color-text-400: 82, 82, 82; /* placeholder text */
--color-scrollbar: 82, 82, 82; /* scrollbar thumb */
--color-border-100: 34, 34, 34; /* subtle border= 1 */
--color-border-200: 38, 38, 38; /* subtle border- 2 */
--color-border-300: 46, 46, 46; /* strong border- 1 */
--color-border-400: 58, 58, 58; /* strong border- 2 */
/* onboarding colors */
--gradient-onboarding-100: linear-gradient(106deg, #18191b 25.17%, #18191b 99.34%);
--gradient-onboarding-200: linear-gradient(129deg, rgba(47, 49, 53, 0.8) -22.23%, rgba(33, 34, 37, 0.8) 62.98%);
--gradient-onboarding-300: linear-gradient(167deg, rgba(47, 49, 53, 0.45) 19.22%, #212225 98.48%);
--color-onboarding-text-100: 237, 238, 240;
--color-onboarding-text-200: 176, 180, 187;
--color-onboarding-text-300: 118, 123, 132;
--color-onboarding-text-400: 105, 110, 119;
--color-onboarding-background-100: 54, 58, 64;
--color-onboarding-background-200: 40, 42, 45;
--color-onboarding-background-300: 40, 42, 45;
--color-onboarding-background-400: 67, 72, 79;
--color-onboarding-border-100: 54, 58, 64;
--color-onboarding-border-200: 54, 58, 64;
--color-onboarding-border-300: 34, 35, 38, 0.5;
--color-onboarding-shadow-sm: 0px 4px 20px 0px rgba(39, 44, 56, 0.1);
/* toast theme */
--color-toast-success-text: 178, 221, 181;
--color-toast-error-text: 206, 44, 49;
--color-toast-warning-text: 255, 186, 24;
--color-toast-info-text: 141, 164, 239;
--color-toast-loading-text: 255, 255, 255;
--color-toast-secondary-text: 185, 187, 198;
--color-toast-tertiary-text: 139, 141, 152;
--color-toast-success-background: 46, 46, 46;
--color-toast-error-background: 46, 46, 46;
--color-toast-warning-background: 46, 46, 46;
--color-toast-info-background: 46, 46, 46;
--color-toast-loading-background: 46, 46, 46;
--color-toast-success-border: 42, 126, 59;
--color-toast-error-border: 100, 23, 35;
--color-toast-warning-border: 79, 52, 34;
--color-toast-info-border: 58, 91, 199;
--color-toast-loading-border: 96, 100, 108;
}
[data-theme="dark-contrast"] {
--color-text-100: 250, 250, 250; /* primary text */
--color-text-200: 241, 241, 241; /* secondary text */
--color-text-300: 212, 212, 212; /* tertiary text */
--color-text-400: 115, 115, 115; /* placeholder text */
--color-scrollbar: 115, 115, 115; /* scrollbar thumb */
--color-border-100: 245, 245, 245; /* subtle border= 1 */
--color-border-200: 229, 229, 229; /* subtle border- 2 */
--color-border-300: 212, 212, 212; /* strong border- 1 */
--color-border-400: 185, 185, 185; /* strong border- 2 */
}
[data-theme="light"],
[data-theme="dark"],
[data-theme="light-contrast"],
[data-theme="dark-contrast"] {
--color-primary-10: 236, 241, 255;
--color-primary-20: 217, 228, 255;
--color-primary-30: 197, 214, 255;
--color-primary-40: 178, 200, 255;
--color-primary-50: 159, 187, 255;
--color-primary-60: 140, 173, 255;
--color-primary-70: 121, 159, 255;
--color-primary-80: 101, 145, 255;
--color-primary-90: 82, 132, 255;
--color-primary-100: 63, 118, 255;
--color-primary-200: 57, 106, 230;
--color-primary-300: 50, 94, 204;
--color-primary-400: 44, 83, 179;
--color-primary-500: 38, 71, 153;
--color-primary-600: 32, 59, 128;
--color-primary-700: 25, 47, 102;
--color-primary-800: 19, 35, 76;
--color-primary-900: 13, 24, 51;
--color-sidebar-background-100: var(--color-background-100); /* primary sidebar bg */
--color-sidebar-background-90: var(--color-background-90); /* secondary sidebar bg */
--color-sidebar-background-80: var(--color-background-80); /* tertiary sidebar bg */
--color-sidebar-text-100: var(--color-text-100); /* primary sidebar text */
--color-sidebar-text-200: var(--color-text-200); /* secondary sidebar text */
--color-sidebar-text-300: var(--color-text-300); /* tertiary sidebar text */
--color-sidebar-text-400: var(--color-text-400); /* sidebar placeholder text */
--color-sidebar-border-100: var(--color-border-100); /* subtle sidebar border= 1 */
--color-sidebar-border-200: var(--color-border-200); /* subtle sidebar border- 2 */
--color-sidebar-border-300: var(--color-border-300); /* strong sidebar border- 1 */
--color-sidebar-border-400: var(--color-border-400); /* strong sidebar border- 2 */
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
font-variant-ligatures: none;
-webkit-font-variant-ligatures: none;
text-rendering: optimizeLegibility;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
}
body {
color: rgba(var(--color-text-100));
}
/* scrollbar style */
@-moz-document url-prefix() {
* {
scrollbar-width: none;
}
.vertical-scrollbar,
.horizontal-scrollbar {
scrollbar-width: initial;
scrollbar-color: rgba(96, 100, 108, 0.1) transparent;
}
.vertical-scrollbar:hover,
.horizontal-scrollbar:hover {
scrollbar-color: rgba(96, 100, 108, 0.25) transparent;
}
.vertical-scrollbar:active,
.horizontal-scrollbar:active {
scrollbar-color: rgba(96, 100, 108, 0.7) transparent;
}
}
.vertical-scrollbar {
overflow-y: auto;
}
.horizontal-scrollbar {
overflow-x: auto;
}
.vertical-scrollbar::-webkit-scrollbar,
.horizontal-scrollbar::-webkit-scrollbar {
display: block;
}
.vertical-scrollbar::-webkit-scrollbar-track,
.horizontal-scrollbar::-webkit-scrollbar-track {
background-color: transparent;
border-radius: 9999px;
}
.vertical-scrollbar::-webkit-scrollbar-thumb,
.horizontal-scrollbar::-webkit-scrollbar-thumb {
background-clip: padding-box;
background-color: rgba(96, 100, 108, 0.1);
border-radius: 9999px;
}
.vertical-scrollbar:hover::-webkit-scrollbar-thumb,
.horizontal-scrollbar:hover::-webkit-scrollbar-thumb {
background-color: rgba(96, 100, 108, 0.25);
}
.vertical-scrollbar::-webkit-scrollbar-thumb:hover,
.horizontal-scrollbar::-webkit-scrollbar-thumb:hover {
background-color: rgba(96, 100, 108, 0.5);
}
.vertical-scrollbar::-webkit-scrollbar-thumb:active,
.horizontal-scrollbar::-webkit-scrollbar-thumb:active {
background-color: rgba(96, 100, 108, 0.7);
}
.vertical-scrollbar::-webkit-scrollbar-corner,
.horizontal-scrollbar::-webkit-scrollbar-corner {
background-color: transparent;
}
.vertical-scrollbar-margin-top-md::-webkit-scrollbar-track {
margin-top: 44px;
}
/* scrollbar sm size */
.scrollbar-sm::-webkit-scrollbar {
height: 12px;
width: 12px;
}
.scrollbar-sm::-webkit-scrollbar-thumb {
border: 3px solid rgba(0, 0, 0, 0);
}
/* scrollbar md size */
.scrollbar-md::-webkit-scrollbar {
height: 14px;
width: 14px;
}
.scrollbar-md::-webkit-scrollbar-thumb {
border: 3px solid rgba(0, 0, 0, 0);
}
/* scrollbar lg size */
.scrollbar-lg::-webkit-scrollbar {
height: 16px;
width: 16px;
}
.scrollbar-lg::-webkit-scrollbar-thumb {
border: 4px solid rgba(0, 0, 0, 0);
}
/* end scrollbar style */
/* progress bar */
.progress-bar {
fill: currentColor;
color: rgba(var(--color-sidebar-background-100));
}
::-webkit-input-placeholder,
::placeholder,
:-ms-input-placeholder {
color: rgb(var(--color-text-400));
}

79
admin/app/image/form.tsx Normal file
View File

@ -0,0 +1,79 @@
import { FC } from "react";
import { useForm } from "react-hook-form";
import { IFormattedInstanceConfiguration, TInstanceImageConfigurationKeys } from "@plane/types";
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
// components
import { ControllerInput } from "@/components/common";
// hooks
import { useInstance } from "@/hooks/store";
type IInstanceImageConfigForm = {
config: IFormattedInstanceConfiguration;
};
type ImageConfigFormValues = Record<TInstanceImageConfigurationKeys, string>;
export const InstanceImageConfigForm: FC<IInstanceImageConfigForm> = (props) => {
const { config } = props;
// store hooks
const { updateInstanceConfigurations } = useInstance();
// form data
const {
handleSubmit,
control,
formState: { errors, isSubmitting },
} = useForm<ImageConfigFormValues>({
defaultValues: {
UNSPLASH_ACCESS_KEY: config["UNSPLASH_ACCESS_KEY"],
},
});
const onSubmit = async (formData: ImageConfigFormValues) => {
const payload: Partial<ImageConfigFormValues> = { ...formData };
await updateInstanceConfigurations(payload)
.then(() =>
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success",
message: "Image Configuration Settings updated successfully",
})
)
.catch((err) => console.error(err));
};
return (
<div className="space-y-8">
<div className="grid-col grid w-full grid-cols-1 items-center justify-between gap-x-16 gap-y-8 lg:grid-cols-2">
<ControllerInput
control={control}
type="password"
name="UNSPLASH_ACCESS_KEY"
label="Access key from your Unsplash account"
description={
<>
You will find your access key in your Unsplash developer console.&nbsp;
<a
href="https://unsplash.com/documentation#creating-a-developer-account"
target="_blank"
className="text-custom-primary-100 hover:underline"
rel="noreferrer"
>
Learn more.
</a>
</>
}
placeholder="oXgq-sdfadsaeweqasdfasdf3234234rassd"
error={Boolean(errors.UNSPLASH_ACCESS_KEY)}
required
/>
</div>
<div>
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
{isSubmitting ? "Saving..." : "Save changes"}
</Button>
</div>
</div>
);
};

View File

@ -0,0 +1,15 @@
import { ReactNode } from "react";
import { Metadata } from "next";
import { AdminLayout } from "@/layouts/admin-layout";
interface ImageLayoutProps {
children: ReactNode;
}
export const metadata: Metadata = {
title: "Images Settings - God Mode",
};
const ImageLayout = ({ children }: ImageLayoutProps) => <AdminLayout>{children}</AdminLayout>;
export default ImageLayout;

44
admin/app/image/page.tsx Normal file
View File

@ -0,0 +1,44 @@
"use client";
import { observer } from "mobx-react-lite";
import useSWR from "swr";
import { Loader } from "@plane/ui";
// components
import { PageHeader } from "@/components/core";
// hooks
import { useInstance } from "@/hooks/store";
// local
import { InstanceImageConfigForm } from "./form";
const InstanceImagePage = observer(() => {
// store
const { formattedConfig, fetchInstanceConfigurations } = useInstance();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
return (
<>
<PageHeader title="Image - God Mode" />
<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>
</>
);
});
export default InstanceImagePage;

48
admin/app/layout.tsx Normal file
View File

@ -0,0 +1,48 @@
"use client";
import { ReactNode } from "react";
import { ThemeProvider, useTheme } from "next-themes";
import { SWRConfig } from "swr";
// ui
import { Toast } from "@plane/ui";
// constants
import { SWR_CONFIG } from "@/constants/swr-config";
// helpers
import { ASSET_PREFIX, resolveGeneralTheme } from "@/helpers/common.helper";
// lib
import { InstanceProvider } from "@/lib/instance-provider";
import { StoreProvider } from "@/lib/store-provider";
import { UserProvider } from "@/lib/user-provider";
// styles
import "./globals.css";
function RootLayout({ children }: { children: ReactNode }) {
// themes
const { resolvedTheme } = useTheme();
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`}>
<ThemeProvider themes={["light", "dark"]} defaultTheme="system" enableSystem>
<Toast theme={resolveGeneralTheme(resolvedTheme)} />
<SWRConfig value={SWR_CONFIG}>
<StoreProvider>
<InstanceProvider>
<UserProvider>{children}</UserProvider>
</InstanceProvider>
</StoreProvider>
</SWRConfig>
</ThemeProvider>
</body>
</html>
);
}
export default RootLayout;

30
admin/app/page.tsx Normal file
View File

@ -0,0 +1,30 @@
import { Metadata } from "next";
// components
import { InstanceSignInForm } from "@/components/login";
// layouts
import { DefaultLayout } from "@/layouts/default-layout";
export const metadata: Metadata = {
title: "Plane | Simple, extensible, open-source project management tool.",
description:
"Open-source project management tool to manage issues, 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 issues, 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, issue tracking, agile, scrum, kanban, collaboration",
twitter: {
site: "@planepowers",
},
};
export default async function LoginPage() {
return (
<DefaultLayout>
<InstanceSignInForm />
</DefaultLayout>
);
}

View File

@ -1,11 +1,14 @@
"use client";
import { FC, useState, useRef } from "react"; import { FC, useState, useRef } from "react";
import { observer } from "mobx-react-lite";
import Link from "next/link"; import Link from "next/link";
import { FileText, HelpCircle, MessagesSquare, MoveLeft } from "lucide-react"; import { ExternalLink, FileText, HelpCircle, MoveLeft } from "lucide-react";
import { Transition } from "@headlessui/react"; import { Transition } from "@headlessui/react";
import { DiscordIcon, GithubIcon, Tooltip } from "@plane/ui";
// hooks // hooks
import { DiscordIcon, GithubIcon } from "@plane/ui"; import { WEB_BASE_URL } from "@/helpers/common.helper";
import { useApplication } from "@/hooks/store"; import { useTheme } from "@/hooks/store";
// icons
// assets // assets
import packageJson from "package.json"; import packageJson from "package.json";
@ -25,56 +28,56 @@ const helpOptions = [
href: "https://github.com/makeplane/plane/issues/new/choose", href: "https://github.com/makeplane/plane/issues/new/choose",
Icon: GithubIcon, Icon: GithubIcon,
}, },
{
name: "Chat with us",
href: null,
onClick: () => (window as any).$crisp.push(["do", "chat:show"]),
Icon: MessagesSquare,
},
]; ];
export const InstanceHelpSection: FC = () => { export const HelpSection: FC = observer(() => {
// states // states
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false); const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
// store // store
const { const { isSidebarCollapsed, toggleSidebar } = useTheme();
theme: { sidebarCollapsed, toggleSidebar },
} = useApplication();
// refs // refs
const helpOptionsRef = useRef<HTMLDivElement | null>(null); const helpOptionsRef = useRef<HTMLDivElement | null>(null);
const redirectionLink = encodeURI(WEB_BASE_URL + "/");
return ( return (
<div <div
className={`flex w-full items-center justify-between gap-1 self-baseline border-t border-custom-border-200 bg-custom-sidebar-background-100 px-4 py-2 ${ className={`flex w-full items-center justify-between gap-1 self-baseline border-t border-custom-sidebar-border-200 bg-custom-sidebar-background-100 px-4 py-2 ${
sidebarCollapsed ? "flex-col" : "" isSidebarCollapsed ? "flex-col" : ""
}`} }`}
> >
<div className={`flex items-center gap-1 ${sidebarCollapsed ? "flex-col justify-center" : "w-full justify-end"}`}> <div className={`flex items-center gap-1 ${isSidebarCollapsed ? "flex-col justify-center" : "w-full"}`}>
<button <Tooltip tooltipContent="Redirect to plane" position="right" className="ml-4" disabled={!isSidebarCollapsed}>
type="button" <a
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 ${ href={redirectionLink}
sidebarCollapsed ? "w-full" : "" 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`}
}`} >
onClick={() => setIsNeedHelpOpen((prev) => !prev)} <ExternalLink size={14} />
> {!isSidebarCollapsed && "Redirect to plane"}
<HelpCircle className="h-3.5 w-3.5" /> </a>
</button> </Tooltip>
<button <Tooltip tooltipContent="Help" position={isSidebarCollapsed ? "right" : "top"} className="ml-4">
type="button" <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 md:hidden" type="button"
onClick={() => toggleSidebar()} 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 ${
> isSidebarCollapsed ? "w-full" : ""
<MoveLeft className="h-3.5 w-3.5" /> }`}
</button> onClick={() => setIsNeedHelpOpen((prev) => !prev)}
<button >
type="button" <HelpCircle className="h-3.5 w-3.5" />
className={`hidden place-items-center rounded-md p-1.5 text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 md:grid ${ </button>
sidebarCollapsed ? "w-full" : "" </Tooltip>
}`} <Tooltip tooltipContent="Toggle sidebar" position={isSidebarCollapsed ? "right" : "top"} className="ml-4">
onClick={() => toggleSidebar()} <button
> type="button"
<MoveLeft className={`h-3.5 w-3.5 duration-300 ${sidebarCollapsed ? "rotate-180" : ""}`} /> 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 ${
</button> isSidebarCollapsed ? "w-full" : ""
}`}
onClick={() => toggleSidebar(!isSidebarCollapsed)}
>
<MoveLeft className={`h-3.5 w-3.5 duration-300 ${isSidebarCollapsed ? "rotate-180" : ""}`} />
</button>
</Tooltip>
</div> </div>
<div className="relative"> <div className="relative">
@ -89,12 +92,12 @@ export const InstanceHelpSection: FC = () => {
> >
<div <div
className={`absolute bottom-2 min-w-[10rem] ${ className={`absolute bottom-2 min-w-[10rem] ${
sidebarCollapsed ? "left-full" : "-left-[75px]" 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-custom-border-200 whitespace-nowrap rounded bg-custom-background-100 p-1 shadow-custom-shadow-xs`}
ref={helpOptionsRef} ref={helpOptionsRef}
> >
<div className="space-y-1 pb-2"> <div className="space-y-1 pb-2">
{helpOptions.map(({ name, Icon, href, onClick }) => { {helpOptions.map(({ name, Icon, href }) => {
if (href) if (href)
return ( return (
<Link href={href} key={name} target="_blank"> <Link href={href} key={name} target="_blank">
@ -111,7 +114,6 @@ export const InstanceHelpSection: FC = () => {
<button <button
key={name} key={name}
type="button" type="button"
onClick={onClick ?? undefined}
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 px-2 py-1 text-xs hover:bg-custom-background-80"
> >
<div className="grid flex-shrink-0 place-items-center"> <div className="grid flex-shrink-0 place-items-center">
@ -128,4 +130,4 @@ export const InstanceHelpSection: FC = () => {
</div> </div>
</div> </div>
); );
}; });

View File

@ -0,0 +1,5 @@
export * from "./root";
export * from "./help-section";
export * from "./sidebar-menu";
export * from "./sidebar-dropdown";
export * from "./sidebar-menu-hamburger-toogle";

View File

@ -0,0 +1,57 @@
"use client";
import { FC, useEffect, useRef } from "react";
import { observer } from "mobx-react-lite";
// hooks
import { HelpSection, SidebarMenu, SidebarDropdown } from "@/components/admin-sidebar";
import { useTheme } from "@/hooks/store";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// components
export interface IInstanceSidebar {}
export const InstanceSidebar: FC<IInstanceSidebar> = observer(() => {
// store
const { isSidebarCollapsed, toggleSidebar } = useTheme();
const ref = useRef<HTMLDivElement>(null);
useOutsideClickDetector(ref, () => {
if (isSidebarCollapsed === false) {
if (window.innerWidth < 768) {
toggleSidebar(!isSidebarCollapsed);
}
}
});
useEffect(() => {
const handleResize = () => {
if (window.innerWidth <= 768) {
toggleSidebar(true);
}
};
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, [toggleSidebar]);
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
fixed md:relative
${isSidebarCollapsed ? "-ml-[280px]" : ""}
sm:${isSidebarCollapsed ? "-ml-[280px]" : ""}
md:ml-0 ${isSidebarCollapsed ? "w-[80px]" : "w-[280px]"}
lg:ml-0 ${isSidebarCollapsed ? "w-[80px]" : "w-[280px]"}
`}
>
<div ref={ref} className="flex h-full w-full flex-1 flex-col">
<SidebarDropdown />
<SidebarMenu />
<HelpSection />
</div>
</div>
);
});

View File

@ -0,0 +1,147 @@
"use client";
import { Fragment, useEffect, useState } from "react";
import { observer } from "mobx-react-lite";
import { useTheme as useNextTheme } from "next-themes";
import { LogOut, UserCog2, Palette } from "lucide-react";
import { Menu, Transition } from "@headlessui/react";
import { Avatar } from "@plane/ui";
// hooks
import { API_BASE_URL, cn } from "@/helpers/common.helper";
import { useTheme, useUser } from "@/hooks/store";
// helpers
// services
import { AuthService } from "@/services/auth.service";
// service initialization
const authService = new AuthService();
export const SidebarDropdown = observer(() => {
// store hooks
const { isSidebarCollapsed } = useTheme();
const { currentUser, signOut } = useUser();
// hooks
const { resolvedTheme, setTheme } = useNextTheme();
// state
const [csrfToken, setCsrfToken] = useState<string | undefined>(undefined);
const handleThemeSwitch = () => {
const newTheme = resolvedTheme === "dark" ? "light" : "dark";
setTheme(newTheme);
};
const handleSignOut = () => signOut();
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",
{
"left-4": isSidebarCollapsed,
}
)}
>
<div className="flex flex-col gap-2.5 pb-2">
<span className="px-2 text-custom-sidebar-text-200">{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"
onClick={handleThemeSwitch}
>
<Palette className="h-4 w-4 stroke-[1.5]" />
Switch to {resolvedTheme === "dark" ? "light" : "dark"} mode
</Menu.Item>
</div>
<div className="py-2">
<form method="POST" action={`${API_BASE_URL}/api/instances/admins/sign-out/`} onSubmit={handleSignOut}>
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<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"
>
<LogOut className="h-4 w-4 stroke-[1.5]" />
Sign out
</Menu.Item>
</form>
</div>
</Menu.Items>
);
useEffect(() => {
if (csrfToken === undefined)
authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
}, [csrfToken]);
return (
<div className="flex max-h-[3.75rem] items-center gap-x-5 gap-y-2 border-b border-custom-sidebar-border-200 px-4 py-3.5">
<div className="h-full w-full truncate">
<div
className={`flex flex-grow items-center gap-x-2 truncate rounded py-1 ${
isSidebarCollapsed ? "justify-center" : ""
}`}
>
<Menu as="div" className="flex-shrink-0">
<Menu.Button
className={cn("grid place-items-center outline-none", {
"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>
</Menu.Button>
{isSidebarCollapsed && (
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
{getSidebarMenuItems()}
</Transition>
)}
</Menu>
{!isSidebarCollapsed && (
<div className="flex w-full gap-2">
<h4 className="grow truncate text-base font-medium text-custom-text-200">Instance admin</h4>
</div>
)}
</div>
</div>
{!isSidebarCollapsed && currentUser && (
<Menu as="div" className="relative flex-shrink-0">
<Menu.Button className="grid place-items-center outline-none">
<Avatar
name={currentUser.display_name}
src={currentUser.avatar ?? undefined}
size={24}
shape="square"
className="!text-base"
/>
</Menu.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
{getSidebarMenuItems()}
</Transition>
</Menu>
)}
</div>
);
});

View File

@ -0,0 +1,20 @@
"use client";
import { FC } from "react";
import { observer } from "mobx-react-lite";
// hooks
import { Menu } from "lucide-react";
import { useTheme } from "@/hooks/store";
// icons
export const SidebarHamburgerToggle: FC = observer(() => {
const { isSidebarCollapsed, toggleSidebar } = useTheme();
return (
<div
className="w-7 h-7 rounded flex justify-center items-center bg-custom-background-80 transition-all hover:bg-custom-background-90 cursor-pointer group md:hidden"
onClick={() => toggleSidebar(!isSidebarCollapsed)}
>
<Menu size={14} className="text-custom-text-200 group-hover:text-custom-text-100 transition-all" />
</div>
);
});

View File

@ -0,0 +1,104 @@
"use client";
import { observer } from "mobx-react-lite";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { Image, BrainCog, Cog, Lock, Mail } from "lucide-react";
import { Tooltip } from "@plane/ui";
// hooks
import { cn } from "@/helpers/common.helper";
import { useTheme } from "@/hooks/store";
// helpers
const INSTANCE_ADMIN_LINKS = [
{
Icon: Cog,
name: "General",
description: "Identify your instances and get key details",
href: `/general/`,
},
{
Icon: Mail,
name: "Email",
description: "Set up emails to your users",
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 SidebarMenu = observer(() => {
// store hooks
const { isSidebarCollapsed, toggleSidebar } = useTheme();
// router
const pathName = usePathname();
const handleItemClick = () => {
if (window.innerWidth < 768) {
toggleSidebar(!isSidebarCollapsed);
}
};
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);
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",
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>
)}
</div>
</Tooltip>
</div>
</Link>
);
})}
</div>
);
});

View File

@ -0,0 +1,90 @@
"use client";
import { FC } from "react";
import { observer } from "mobx-react-lite";
import { usePathname } from "next/navigation";
// mobx
// ui
import { Settings } from "lucide-react";
// icons
import { Breadcrumbs } from "@plane/ui";
// components
import { SidebarHamburgerToggle } from "@/components/admin-sidebar";
import { BreadcrumbLink } from "components/common";
export const InstanceHeader: FC = observer(() => {
const pathName = usePathname();
const getHeaderTitle = (pathName: string) => {
switch (pathName) {
case "general":
return "General";
case "ai":
return "Artificial Intelligence";
case "email":
return "Email";
case "authentication":
return "Authentication";
case "image":
return "Image";
case "google":
return "Google";
case "github":
return "Github";
default:
return pathName.toUpperCase();
}
};
// Function to dynamically generate breadcrumb items based on pathname
const generateBreadcrumbItems = (pathname: string) => {
const pathSegments = pathname.split("/").slice(1); // removing the first empty string.
pathSegments.pop();
let currentUrl = "";
const breadcrumbItems = pathSegments.map((segment) => {
currentUrl += "/" + segment;
return {
title: getHeaderTitle(segment),
href: currentUrl,
};
});
return breadcrumbItems;
};
const breadcrumbItems = generateBreadcrumbItems(pathName);
return (
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-sidebar-border-200 bg-custom-sidebar-background-100 p-4">
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
<SidebarHamburgerToggle />
{breadcrumbItems.length >= 0 && (
<div>
<Breadcrumbs>
<Breadcrumbs.BreadcrumbItem
type="text"
link={
<BreadcrumbLink
href="/general/"
label="Settings"
icon={<Settings className="h-4 w-4 text-custom-text-300" />}
/>
}
/>
{breadcrumbItems.map(
(item) =>
item.title && (
<Breadcrumbs.BreadcrumbItem
key={item.title}
type="text"
link={<BreadcrumbLink href={item.href} label={item.title} />}
/>
)
)}
</Breadcrumbs>
</div>
)}
</div>
</div>
);
});

View File

@ -0,0 +1,32 @@
import { FC } from "react";
import { AlertCircle, CheckCircle2 } from "lucide-react";
type TBanner = {
type: "success" | "error";
message: string;
};
export const Banner: FC<TBanner> = (props) => {
const { type, message } = props;
return (
<div
className={`rounded-md p-2 w-full border ${type === "error" ? "bg-red-500/5 border-red-400" : "bg-green-500/5 border-green-400"}`}
>
<div className="flex items-center justify-center">
<div className="flex-shrink-0">
{type === "error" ? (
<span className="flex items-center justify-center h-6 w-6 rounded-full">
<AlertCircle className="h-5 w-5 text-red-600" aria-hidden="true" />
</span>
) : (
<CheckCircle2 className="h-5 w-5 text-green-600" aria-hidden="true" />
)}
</div>
<div className="ml-1">
<p className={`text-sm font-medium ${type === "error" ? "text-red-600" : "text-green-600"}`}>{message}</p>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,36 @@
import Link from "next/link";
import { Tooltip } from "@plane/ui";
type Props = {
label?: string;
href?: string;
icon?: React.ReactNode | undefined;
};
export const BreadcrumbLink: React.FC<Props> = (props) => {
const { href, label, icon } = props;
return (
<Tooltip tooltipContent={label} position="bottom">
<li className="flex items-center space-x-2" tabIndex={-1}>
<div className="flex flex-wrap items-center gap-2.5">
{href ? (
<Link
className="flex items-center gap-1 text-sm font-medium text-custom-text-300 hover:text-custom-text-100"
href={href}
>
{icon && (
<div className="flex h-5 w-5 items-center justify-center overflow-hidden !text-[1rem]">{icon}</div>
)}
<div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>
</Link>
) : (
<div className="flex cursor-default items-center gap-1 text-sm font-medium text-custom-text-100">
{icon && <div className="flex h-5 w-5 items-center justify-center overflow-hidden">{icon}</div>}
<div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>
</div>
)}
</div>
</li>
</Tooltip>
);
};

View File

@ -0,0 +1,83 @@
import React from "react";
import Link from "next/link";
// headless ui
import { Dialog, Transition } from "@headlessui/react";
// ui
import { Button, getButtonStyling } from "@plane/ui";
type Props = {
isOpen: boolean;
handleClose: () => void;
onDiscardHref: string;
};
export const ConfirmDiscardModal: React.FC<Props> = (props) => {
const { isOpen, handleClose, onDiscardHref } = props;
return (
<Transition.Root show={isOpen} as={React.Fragment}>
<Dialog as="div" className="relative z-50" onClose={handleClose}>
<Transition.Child
as={React.Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-custom-backdrop transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="my-10 flex items-center justify-center p-4 text-center sm:p-0 md:my-32">
<Transition.Child
as={React.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"
leave="ease-in duration-200"
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 overflow-hidden rounded-lg bg-custom-background-100 text-left shadow-custom-shadow-md transition-all sm:my-8 sm:w-[30rem]">
<div className="px-4 pb-4 pt-5 sm:p-6 sm:pb-4">
<div className="sm:flex sm:items-start">
<div className="mt-3 text-center sm:mt-0 sm:text-left">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-custom-text-300"
>
You have unsaved changes
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-custom-text-400">
Changes you made will be lost if you go back. Do you
wish to go back?
</p>
</div>
</div>
</div>
</div>
<div className="flex justify-end items-center p-4 sm:px-6 gap-2">
<Button
variant="neutral-primary"
size="sm"
onClick={handleClose}
>
Keep editing
</Button>
<Link
href={onDiscardHref}
className={getButtonStyling("primary", "sm")}
>
Go back
</Link>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
};

View File

@ -0,0 +1,86 @@
"use client";
import React, { useState } from "react";
import { Controller, Control } from "react-hook-form";
// icons
import { Eye, EyeOff } from "lucide-react";
// ui
import { Input } from "@plane/ui";
// helpers
import { cn } from "@/helpers/common.helper";
type Props = {
control: Control<any>;
type: "text" | "password";
name: string;
label: string;
description?: string | JSX.Element;
placeholder: string;
error: boolean;
required: boolean;
};
export type TControllerInputFormField = {
key: string;
type: "text" | "password";
label: string;
description?: string | JSX.Element;
placeholder: string;
error: boolean;
required: boolean;
};
export const ControllerInput: React.FC<Props> = (props) => {
const { name, control, type, label, description, placeholder, error, required } = props;
// states
const [showPassword, setShowPassword] = useState(false);
return (
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-300">
{label} {!required && "(optional)"}
</h4>
<div className="relative">
<Controller
control={control}
name={name}
rules={{ required: required ? `${label} is required.` : false }}
render={({ field: { value, onChange, ref } }) => (
<Input
id={name}
name={name}
type={type === "password" && showPassword ? "text" : type}
value={value}
onChange={onChange}
ref={ref}
hasError={error}
placeholder={placeholder}
className={cn("w-full rounded-md font-medium", {
"pr-10": type === "password",
})}
/>
)}
/>
{type === "password" &&
(showPassword ? (
<button
tabIndex={-1}
className="absolute right-3 top-2.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(false)}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
tabIndex={-1}
className="absolute right-3 top-2.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(true)}
>
<Eye className="h-4 w-4" />
</button>
))}
</div>
{description && <p className="text-xs text-custom-text-300">{description}</p>}
</div>
);
};

View File

@ -0,0 +1,46 @@
"use client";
import React from "react";
// ui
import { Copy } from "lucide-react";
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
// icons
type Props = {
label: string;
url: string;
description: string | JSX.Element;
};
export type TCopyField = {
key: string;
label: string;
url: string;
description: string | JSX.Element;
};
export const CopyField: React.FC<Props> = (props) => {
const { label, url, description } = props;
return (
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-200">{label}</h4>
<Button
variant="neutral-primary"
className="flex items-center justify-between py-2"
onClick={() => {
navigator.clipboard.writeText(url);
setToast({
type: TOAST_TYPE.INFO,
title: "Copied to clipboard",
message: `The ${label} has been successfully copied to your clipboard`,
});
}}
>
<p className="text-sm font-medium">{url}</p>
<Copy size={18} color="#B9B9B9" />
</Button>
<div className="text-xs text-custom-text-300">{description}</div>
</div>
);
};

View File

@ -0,0 +1,46 @@
import React from "react";
import Image from "next/image";
import { Button } from "@plane/ui";
type Props = {
title: string;
description?: React.ReactNode;
image?: any;
primaryButton?: {
icon?: any;
text: string;
onClick: () => void;
};
secondaryButton?: React.ReactNode;
disabled?: boolean;
};
export const EmptyState: React.FC<Props> = ({
title,
description,
image,
primaryButton,
secondaryButton,
disabled = false,
}) => (
<div className={`flex h-full w-full items-center justify-center`}>
<div className="flex w-full flex-col items-center text-center">
{image && <Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text || "button image"} />}
<h6 className="mb-3 mt-6 text-xl font-semibold sm:mt-8">{title}</h6>
{description && <p className="mb-7 px-5 text-custom-text-300 sm:mb-8">{description}</p>}
<div className="flex items-center gap-4">
{primaryButton && (
<Button
variant="primary"
prependIcon={primaryButton.icon}
onClick={primaryButton.onClick}
disabled={disabled}
>
{primaryButton.text}
</Button>
)}
{secondaryButton}
</div>
</div>
</div>
);

View File

@ -0,0 +1,9 @@
export * from "./breadcrumb-link";
export * from "./confirm-discard-modal";
export * from "./controller-input";
export * from "./copy-field";
export * from "./password-strength-meter";
export * from "./banner";
export * from "./empty-state";
export * from "./logo-spinner";
export * from "./toast";

View File

@ -0,0 +1,17 @@
import Image from "next/image";
import { useTheme } from "next-themes";
// assets
import LogoSpinnerDark from "@/public/images/logo-spinner-dark.gif";
import LogoSpinnerLight from "@/public/images/logo-spinner-light.gif";
export const LogoSpinner = () => {
const { resolvedTheme } = useTheme();
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerDark : LogoSpinnerLight;
return (
<div className="flex items-center justify-center">
<Image src={logoSrc} alt="logo" className="w-[82px] h-[82px] mr-2" priority={false} />
</div>
);
};

View File

@ -0,0 +1,69 @@
"use client";
// helpers
import { CircleCheck } from "lucide-react";
import { cn } from "@/helpers/common.helper";
import { getPasswordStrength } from "@/helpers/password.helper";
// icons
type Props = {
password: string;
};
export const PasswordStrengthMeter: React.FC<Props> = (props: Props) => {
const { password } = props;
const strength = getPasswordStrength(password);
let bars = [];
let text = "";
let textColor = "";
if (password.length === 0) {
bars = [`bg-[#F0F0F3]`, `bg-[#F0F0F3]`, `bg-[#F0F0F3]`];
text = "Password requirements";
} else if (password.length < 8) {
bars = [`bg-[#DC3E42]`, `bg-[#F0F0F3]`, `bg-[#F0F0F3]`];
text = "Password is too short";
textColor = `text-[#DC3E42]`;
} else if (strength < 3) {
bars = [`bg-[#FFBA18]`, `bg-[#FFBA18]`, `bg-[#F0F0F3]`];
text = "Password is weak";
textColor = `text-[#FFBA18]`;
} else {
bars = [`bg-[#3E9B4F]`, `bg-[#3E9B4F]`, `bg-[#3E9B4F]`];
text = "Password is strong";
textColor = `text-[#3E9B4F]`;
}
const criteria = [
{ label: "Min 8 characters", isValid: password.length >= 8 },
{ label: "Min 1 upper-case letter", isValid: /[A-Z]/.test(password) },
{ label: "Min 1 number", isValid: /\d/.test(password) },
{ label: "Min 1 special character", isValid: /[!@#$%^&*]/.test(password) },
];
return (
<div className="w-full">
<div className="flex w-full gap-1.5">
{bars.map((color, index) => (
<div key={index} className={cn("w-full h-1 rounded-full", color)} />
))}
</div>
<p className={cn("text-xs font-medium py-1", textColor)}>{text}</p>
<div className="flex flex-wrap gap-x-4 gap-y-2">
{criteria.map((criterion, index) => (
<div
key={index}
className={cn(
"flex items-center gap-1 text-xs font-medium",
criterion.isValid ? `text-[#3E9B4F]` : "text-custom-text-400"
)}
>
<CircleCheck width={14} height={14} />
{criterion.label}
</div>
))}
</div>
</div>
);
};

View File

@ -0,0 +1,11 @@
import { useTheme } from "next-themes";
// ui
import { Toast as ToastComponent } from "@plane/ui";
// helpers
import { resolveGeneralTheme } from "@/helpers/common.helper";
export const Toast = () => {
const { theme } = useTheme();
return <ToastComponent theme={resolveGeneralTheme(theme)} />;
};

View File

@ -0,0 +1 @@
export * from "./page-header";

View File

@ -0,0 +1,17 @@
"use client";
type TPageHeader = {
title?: string;
description?: string;
};
export const PageHeader: React.FC<TPageHeader> = (props) => {
const { title = "God Mode - Plane", description = "Plane god mode" } = props;
return (
<>
<title>{title}</title>
<meta name="description" content={description} />
</>
);
};

View File

@ -0,0 +1,3 @@
export * from "./instance-not-ready";
export * from "./instance-failure-view";
export * from "./setup-form";

View File

@ -0,0 +1,42 @@
"use client";
import { FC } from "react";
import Image from "next/image";
import { useTheme } from "next-themes";
import { Button } from "@plane/ui";
// assets
import InstanceFailureDarkImage from "@/public/instance/instance-failure-dark.svg";
import InstanceFailureImage from "@/public/instance/instance-failure.svg";
type InstanceFailureViewProps = {
// mutate: () => void;
};
export const InstanceFailureView: FC<InstanceFailureViewProps> = () => {
const { resolvedTheme } = useTheme();
const instanceImage = resolvedTheme === "dark" ? InstanceFailureDarkImage : InstanceFailureImage;
const handleRetry = () => {
window.location.reload();
};
return (
<div className="h-full w-full relative container px-5 mx-auto flex justify-center items-center">
<div className="w-auto max-w-2xl relative space-y-8 py-10">
<div className="relative flex flex-col justify-center items-center space-y-4">
<Image src={instanceImage} alt="Plane Logo" />
<h3 className="font-medium text-2xl text-white ">Unable to fetch instance details.</h3>
<p className="font-medium text-base text-center">
We were unable to fetch the details of the instance. <br />
Fret not, it might just be a connectivity issue.
</p>
</div>
<div className="flex justify-center">
<Button size="md" onClick={handleRetry}>
Retry
</Button>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,30 @@
"use client";
import { FC } from "react";
import Image from "next/image";
import Link from "next/link";
import { Button } from "@plane/ui";
// assets
import PlaneTakeOffImage from "@/public/images/plane-takeoff.png";
export const InstanceNotReady: FC = () => (
<div className="h-full w-full relative container px-5 mx-auto flex justify-center items-center">
<div className="w-auto max-w-2xl relative space-y-8 py-10">
<div className="relative flex flex-col justify-center items-center space-y-4">
<h1 className="text-3xl font-bold pb-3">Welcome aboard Plane!</h1>
<Image src={PlaneTakeOffImage} alt="Plane Logo" />
<p className="font-medium text-base text-onboarding-text-400">
Get started by setting up your instance and workspace
</p>
</div>
<div>
<Link href={"/setup/?auth_enabled=0"}>
<Button size="lg" className="w-full">
Get started
</Button>
</Link>
</div>
</div>
</div>
);

View File

@ -0,0 +1,353 @@
"use client";
import { FC, useEffect, useMemo, useState } from "react";
import { useSearchParams } from "next/navigation";
// icons
import { Eye, EyeOff } from "lucide-react";
// ui
import { Button, Checkbox, Input, Spinner } from "@plane/ui";
// components
import { Banner, PasswordStrengthMeter } from "@/components/common";
// helpers
import { API_BASE_URL } from "@/helpers/common.helper";
import { getPasswordStrength } from "@/helpers/password.helper";
// services
import { AuthService } from "@/services/auth.service";
// service initialization
const authService = new AuthService();
// error codes
enum EErrorCodes {
INSTANCE_NOT_CONFIGURED = "INSTANCE_NOT_CONFIGURED",
ADMIN_ALREADY_EXIST = "ADMIN_ALREADY_EXIST",
REQUIRED_EMAIL_PASSWORD_FIRST_NAME = "REQUIRED_EMAIL_PASSWORD_FIRST_NAME",
INVALID_EMAIL = "INVALID_EMAIL",
INVALID_PASSWORD = "INVALID_PASSWORD",
USER_ALREADY_EXISTS = "USER_ALREADY_EXISTS",
}
type TError = {
type: EErrorCodes | undefined;
message: string | undefined;
};
// form data
type TFormData = {
first_name: string;
last_name: string;
email: string;
company_name: string;
password: string;
confirm_password?: string;
is_telemetry_enabled: boolean;
};
const defaultFromData: TFormData = {
first_name: "",
last_name: "",
email: "",
company_name: "",
password: "",
is_telemetry_enabled: true,
};
export const InstanceSetupForm: FC = (props) => {
const {} = props;
// search params
const searchParams = useSearchParams();
const firstNameParam = searchParams.get("first_name") || undefined;
const lastNameParam = searchParams.get("last_name") || undefined;
const companyParam = searchParams.get("company") || undefined;
const emailParam = searchParams.get("email") || undefined;
const isTelemetryEnabledParam = (searchParams.get("is_telemetry_enabled") === "True" ? true : false) || true;
const errorCode = searchParams.get("error_code") || undefined;
const errorMessage = searchParams.get("error_message") || undefined;
// state
const [showPassword, setShowPassword] = useState({
password: false,
retypePassword: false,
});
const [csrfToken, setCsrfToken] = useState<string | undefined>(undefined);
const [formData, setFormData] = useState<TFormData>(defaultFromData);
const [isPasswordInputFocused, setIsPasswordInputFocused] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isRetryPasswordInputFocused, setIsRetryPasswordInputFocused] = useState(false);
const handleShowPassword = (key: keyof typeof showPassword) =>
setShowPassword((prev) => ({ ...prev, [key]: !prev[key] }));
const handleFormChange = (key: keyof TFormData, value: string | boolean) =>
setFormData((prev) => ({ ...prev, [key]: value }));
useEffect(() => {
if (csrfToken === undefined)
authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
}, [csrfToken]);
useEffect(() => {
if (firstNameParam) setFormData((prev) => ({ ...prev, first_name: firstNameParam }));
if (lastNameParam) setFormData((prev) => ({ ...prev, last_name: lastNameParam }));
if (companyParam) setFormData((prev) => ({ ...prev, company_name: companyParam }));
if (emailParam) setFormData((prev) => ({ ...prev, email: emailParam }));
if (isTelemetryEnabledParam) setFormData((prev) => ({ ...prev, is_telemetry_enabled: isTelemetryEnabledParam }));
}, [firstNameParam, lastNameParam, companyParam, emailParam, isTelemetryEnabledParam]);
// derived values
const errorData: TError = useMemo(() => {
if (errorCode && errorMessage) {
switch (errorCode) {
case EErrorCodes.INSTANCE_NOT_CONFIGURED:
return { type: EErrorCodes.INSTANCE_NOT_CONFIGURED, message: errorMessage };
case EErrorCodes.ADMIN_ALREADY_EXIST:
return { type: EErrorCodes.ADMIN_ALREADY_EXIST, message: errorMessage };
case EErrorCodes.REQUIRED_EMAIL_PASSWORD_FIRST_NAME:
return { type: EErrorCodes.REQUIRED_EMAIL_PASSWORD_FIRST_NAME, message: errorMessage };
case EErrorCodes.INVALID_EMAIL:
return { type: EErrorCodes.INVALID_EMAIL, message: errorMessage };
case EErrorCodes.INVALID_PASSWORD:
return { type: EErrorCodes.INVALID_PASSWORD, message: errorMessage };
case EErrorCodes.USER_ALREADY_EXISTS:
return { type: EErrorCodes.USER_ALREADY_EXISTS, message: errorMessage };
default:
return { type: undefined, message: undefined };
}
} else return { type: undefined, message: undefined };
}, [errorCode, errorMessage]);
const isButtonDisabled = useMemo(
() =>
!isSubmitting &&
formData.first_name &&
formData.email &&
formData.password &&
getPasswordStrength(formData.password) >= 3 &&
formData.password === formData.confirm_password
? false
: true,
[formData.confirm_password, formData.email, formData.first_name, formData.password, isSubmitting]
);
const password = formData?.password ?? "";
const confirmPassword = formData?.confirm_password ?? "";
const renderPasswordMatchError = !isRetryPasswordInputFocused || confirmPassword.length >= password.length;
return (
<div className="max-w-lg lg:max-w-md w-full">
<div className="relative flex flex-col space-y-6">
<div className="text-center space-y-1">
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">
Setup your Plane Instance
</h3>
<p className="font-medium text-onboarding-text-400">
Post setup you will be able to manage this Plane instance.
</p>
</div>
{errorData.type &&
errorData?.message &&
![EErrorCodes.INVALID_EMAIL, EErrorCodes.INVALID_PASSWORD].includes(errorData.type) && (
<Banner type="error" message={errorData?.message} />
)}
<form
className="space-y-4"
method="POST"
action={`${API_BASE_URL}/api/instances/admins/sign-up/`}
onSubmit={() => setIsSubmitting(true)}
onError={() => setIsSubmitting(false)}
>
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<input type="hidden" name="is_telemetry_enabled" value={formData.is_telemetry_enabled ? "True" : "False"} />
<div className="flex flex-col sm:flex-row items-center gap-4">
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="first_name">
First name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="first_name"
name="first_name"
type="text"
inputSize="md"
placeholder="Wilber"
value={formData.first_name}
onChange={(e) => handleFormChange("first_name", e.target.value)}
autoFocus
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="last_name">
Last name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="last_name"
name="last_name"
type="text"
inputSize="md"
placeholder="Wright"
value={formData.last_name}
onChange={(e) => handleFormChange("last_name", e.target.value)}
/>
</div>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
Email <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="email"
name="email"
type="email"
inputSize="md"
placeholder="name@company.com"
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
hasError={errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL ? true : false}
/>
{errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL && errorData.message && (
<p className="px-1 text-xs text-red-500">{errorData.message}</p>
)}
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="company_name">
Company name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="company_name"
name="company_name"
type="text"
inputSize="md"
placeholder="Company name"
value={formData.company_name}
onChange={(e) => handleFormChange("company_name", e.target.value)}
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="password">
Set a password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="password"
name="password"
type={showPassword.password ? "text" : "password"}
inputSize="md"
placeholder="New password..."
value={formData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
hasError={errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD ? true : false}
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
/>
{showPassword.password ? (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("password")}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("password")}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
{errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD && errorData.message && (
<p className="px-1 text-xs text-red-500">{errorData.message}</p>
)}
{isPasswordInputFocused && <PasswordStrengthMeter password={formData.password} />}
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="confirm_password">
Confirm password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
type={showPassword.retypePassword ? "text" : "password"}
id="confirm_password"
name="confirm_password"
inputSize="md"
value={formData.confirm_password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
placeholder="Confirm password"
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
/>
{showPassword.retypePassword ? (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("retypePassword")}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("retypePassword")}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
{!!formData.confirm_password &&
formData.password !== formData.confirm_password &&
renderPasswordMatchError && <span className="text-sm text-red-500">Passwords don{"'"}t match</span>}
</div>
<div className="relative flex items-center pt-2 gap-2">
<div>
<Checkbox
id="is_telemetry_enabled"
onChange={() => handleFormChange("is_telemetry_enabled", !formData.is_telemetry_enabled)}
checked={formData.is_telemetry_enabled}
/>
</div>
<label
className="text-sm text-onboarding-text-300 font-medium cursor-pointer"
htmlFor="is_telemetry_enabled"
>
Allow Plane to anonymously collect usage events.
</label>
<a
tabIndex={-1}
href="https://docs.plane.so/telemetry"
target="_blank"
rel="noopener noreferrer"
className="text-sm font-medium text-blue-500 hover:text-blue-600"
>
See More
</a>
</div>
<div className="py-2">
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"}
</Button>
</div>
</form>
</div>
</div>
);
};

View File

@ -1,2 +1 @@
export * from "./root";
export * from "./sign-in-form"; export * from "./sign-in-form";

View File

@ -0,0 +1,179 @@
"use client";
import { FC, useEffect, useMemo, useState } from "react";
import { useSearchParams } from "next/navigation";
// services
import { Eye, EyeOff } from "lucide-react";
import { Button, Input, Spinner } from "@plane/ui";
// components
import { Banner } from "@/components/common";
// helpers
import { API_BASE_URL } from "@/helpers/common.helper";
import { AuthService } from "@/services/auth.service";
// ui
// icons
// service initialization
const authService = new AuthService();
// error codes
enum EErrorCodes {
INSTANCE_NOT_CONFIGURED = "INSTANCE_NOT_CONFIGURED",
REQUIRED_EMAIL_PASSWORD = "REQUIRED_EMAIL_PASSWORD",
INVALID_EMAIL = "INVALID_EMAIL",
USER_DOES_NOT_EXIST = "USER_DOES_NOT_EXIST",
AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED",
}
type TError = {
type: EErrorCodes | undefined;
message: string | undefined;
};
// form data
type TFormData = {
email: string;
password: string;
};
const defaultFromData: TFormData = {
email: "",
password: "",
};
export const InstanceSignInForm: FC = (props) => {
const {} = props;
// search params
const searchParams = useSearchParams();
const emailParam = searchParams.get("email") || undefined;
const errorCode = searchParams.get("error_code") || undefined;
const errorMessage = searchParams.get("error_message") || undefined;
// state
const [showPassword, setShowPassword] = useState(false);
const [csrfToken, setCsrfToken] = useState<string | undefined>(undefined);
const [formData, setFormData] = useState<TFormData>(defaultFromData);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleFormChange = (key: keyof TFormData, value: string | boolean) =>
setFormData((prev) => ({ ...prev, [key]: value }));
console.log("csrfToken", csrfToken);
useEffect(() => {
if (csrfToken === undefined)
authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
}, [csrfToken]);
useEffect(() => {
if (emailParam) setFormData((prev) => ({ ...prev, email: emailParam }));
}, [emailParam]);
// derived values
const errorData: TError = useMemo(() => {
if (errorCode && errorMessage) {
switch (errorCode) {
case EErrorCodes.INSTANCE_NOT_CONFIGURED:
return { type: EErrorCodes.INVALID_EMAIL, message: errorMessage };
case EErrorCodes.REQUIRED_EMAIL_PASSWORD:
return { type: EErrorCodes.REQUIRED_EMAIL_PASSWORD, message: errorMessage };
case EErrorCodes.INVALID_EMAIL:
return { type: EErrorCodes.INVALID_EMAIL, message: errorMessage };
case EErrorCodes.USER_DOES_NOT_EXIST:
return { type: EErrorCodes.USER_DOES_NOT_EXIST, message: errorMessage };
case EErrorCodes.AUTHENTICATION_FAILED:
return { type: EErrorCodes.AUTHENTICATION_FAILED, message: errorMessage };
default:
return { type: undefined, message: undefined };
}
} else return { type: undefined, message: undefined };
}, [errorCode, errorMessage]);
const isButtonDisabled = useMemo(
() => (!isSubmitting && formData.email && formData.password ? false : true),
[formData.email, formData.password, isSubmitting]
);
return (
<div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10 lg:pt-28 transition-all">
<div className="relative flex flex-col space-y-6">
<div className="text-center space-y-1">
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">
Manage your Plane instance
</h3>
<p className="font-medium text-onboarding-text-400">
Configure instance-wide settings to secure your instance
</p>
</div>
{errorData.type && errorData?.message && <Banner type="error" message={errorData?.message} />}
<form
className="space-y-4"
method="POST"
action={`${API_BASE_URL}/api/instances/admins/sign-in/`}
onSubmit={() => setIsSubmitting(true)}
onError={() => setIsSubmitting(false)}
>
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
Email <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="email"
name="email"
type="email"
inputSize="md"
placeholder="name@company.com"
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
autoFocus
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="password">
Password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="password"
name="password"
type={showPassword ? "text" : "password"}
inputSize="md"
placeholder="Enter your password"
value={formData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
/>
{showPassword ? (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(false)}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(true)}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
</div>
<div className="py-2">
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Sign in"}
</Button>
</div>
</form>
</div>
</div>
);
};

View File

@ -0,0 +1,55 @@
"use client";
import React from "react";
import { observer } from "mobx-react-lite";
import Image from "next/image";
import { useTheme as nextUseTheme } from "next-themes";
// ui
import { Button, getButtonStyling } from "@plane/ui";
// helpers
import { WEB_BASE_URL, resolveGeneralTheme } from "helpers/common.helper";
// hooks
import { useTheme } from "@/hooks/store";
// icons
import TakeoffIconLight from "/public/logos/takeoff-icon-light.svg";
import TakeoffIconDark from "/public/logos/takeoff-icon-dark.svg";
export const NewUserPopup: React.FC = observer(() => {
// hooks
const { isNewUserPopup, toggleNewUserPopup } = useTheme();
// theme
const { resolvedTheme } = nextUseTheme();
const redirectionLink = encodeURI(WEB_BASE_URL + "/create-workspace");
if (!isNewUserPopup) return <></>;
return (
<div className="absolute bottom-8 right-8 p-6 w-96 border border-custom-border-100 shadow-md rounded-lg bg-custom-background-100">
<div className="flex gap-4">
<div className="grow">
<div className="text-base font-semibold">Create workspace</div>
<div className="py-2 text-sm font-medium text-custom-text-300">
Instance setup done! Welcome to Plane instance portal. Start your journey with by creating your first
workspace, you will need to login again.
</div>
<div className="flex items-center gap-4 pt-2">
<a href={redirectionLink} className={getButtonStyling("primary", "sm")}>
Create workspace
</a>
<Button variant="neutral-primary" size="sm" onClick={toggleNewUserPopup}>
Close
</Button>
</div>
</div>
<div className="shrink-0 flex items-center justify-center">
<Image
src={resolveGeneralTheme(resolvedTheme) === "dark" ? TakeoffIconDark : TakeoffIconLight}
height={80}
width={80}
alt="Plane icon"
/>
</div>
</div>
</div>
);
});

8
admin/constants/seo.ts Normal file
View File

@ -0,0 +1,8 @@
export const SITE_NAME = "Plane | Simple, extensible, open-source project management tool.";
export const SITE_TITLE = "Plane | Simple, extensible, open-source project management tool.";
export const SITE_DESCRIPTION =
"Open-source project management tool to manage issues, sprints, and product roadmaps with peace of mind.";
export const SITE_KEYWORDS =
"software development, plan, ship, software, accelerate, code management, release management, project management, issue tracking, agile, scrum, kanban, collaboration";
export const SITE_URL = "https://app.plane.so/";
export const TWITTER_USER_NAME = "Plane | Simple, extensible, open-source project management tool.";

View File

@ -0,0 +1,8 @@
export const SWR_CONFIG = {
refreshWhenHidden: false,
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnMount: true,
refreshInterval: 600000,
errorRetryCount: 3,
};

View File

@ -0,0 +1,136 @@
import { ReactNode } from "react";
import Link from "next/link";
// helpers
import { SUPPORT_EMAIL } from "./common.helper";
export enum EPageTypes {
PUBLIC = "PUBLIC",
NON_AUTHENTICATED = "NON_AUTHENTICATED",
SET_PASSWORD = "SET_PASSWORD",
ONBOARDING = "ONBOARDING",
AUTHENTICATED = "AUTHENTICATED",
}
export enum EAuthModes {
SIGN_IN = "SIGN_IN",
SIGN_UP = "SIGN_UP",
}
export enum EAuthSteps {
EMAIL = "EMAIL",
PASSWORD = "PASSWORD",
UNIQUE_CODE = "UNIQUE_CODE",
}
export enum EErrorAlertType {
BANNER_ALERT = "BANNER_ALERT",
INLINE_FIRST_NAME = "INLINE_FIRST_NAME",
INLINE_EMAIL = "INLINE_EMAIL",
INLINE_PASSWORD = "INLINE_PASSWORD",
INLINE_EMAIL_CODE = "INLINE_EMAIL_CODE",
}
export enum EAuthenticationErrorCodes {
// Admin
ADMIN_ALREADY_EXIST = "5150",
REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME = "5155",
INVALID_ADMIN_EMAIL = "5160",
INVALID_ADMIN_PASSWORD = "5165",
REQUIRED_ADMIN_EMAIL_PASSWORD = "5170",
ADMIN_AUTHENTICATION_FAILED = "5175",
ADMIN_USER_ALREADY_EXIST = "5180",
ADMIN_USER_DOES_NOT_EXIST = "5185",
ADMIN_USER_DEACTIVATED = "5190",
}
export type TAuthErrorInfo = {
type: EErrorAlertType;
code: EAuthenticationErrorCodes;
title: string;
message: ReactNode;
};
const errorCodeMessages: {
[key in EAuthenticationErrorCodes]: { title: string; message: (email?: string | undefined) => ReactNode };
} = {
// admin
[EAuthenticationErrorCodes.ADMIN_ALREADY_EXIST]: {
title: `Admin already exists`,
message: () => `Admin already exists. Please try again.`,
},
[EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME]: {
title: `Email, password and first name required`,
message: () => `Email, password and first name required. Please try again.`,
},
[EAuthenticationErrorCodes.INVALID_ADMIN_EMAIL]: {
title: `Invalid admin email`,
message: () => `Invalid admin email. Please try again.`,
},
[EAuthenticationErrorCodes.INVALID_ADMIN_PASSWORD]: {
title: `Invalid admin password`,
message: () => `Invalid admin password. Please try again.`,
},
[EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD]: {
title: `Email and password required`,
message: () => `Email and password required. Please try again.`,
},
[EAuthenticationErrorCodes.ADMIN_AUTHENTICATION_FAILED]: {
title: `Authentication failed`,
message: () => `Authentication failed. Please try again.`,
},
[EAuthenticationErrorCodes.ADMIN_USER_ALREADY_EXIST]: {
title: `Admin user already exists`,
message: () => (
<div>
Admin user already exists.&nbsp;
<Link className="underline underline-offset-4 font-medium hover:font-bold transition-all" href={`/admin`}>
Sign In
</Link>
&nbsp;now.
</div>
),
},
[EAuthenticationErrorCodes.ADMIN_USER_DOES_NOT_EXIST]: {
title: `Admin user does not exist`,
message: () => (
<div>
Admin user does not exist.&nbsp;
<Link className="underline underline-offset-4 font-medium hover:font-bold transition-all" href={`/admin`}>
Sign In
</Link>
&nbsp;now.
</div>
),
},
[EAuthenticationErrorCodes.ADMIN_USER_DEACTIVATED]: {
title: `User account deactivated`,
message: () => `User account deactivated. Please contact ${!!SUPPORT_EMAIL ? SUPPORT_EMAIL : "administrator"}.`,
},
};
export const authErrorHandler = (
errorCode: EAuthenticationErrorCodes,
email?: string | undefined
): TAuthErrorInfo | undefined => {
const bannerAlertErrorCodes = [
EAuthenticationErrorCodes.ADMIN_ALREADY_EXIST,
EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME,
EAuthenticationErrorCodes.INVALID_ADMIN_EMAIL,
EAuthenticationErrorCodes.INVALID_ADMIN_PASSWORD,
EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD,
EAuthenticationErrorCodes.ADMIN_AUTHENTICATION_FAILED,
EAuthenticationErrorCodes.ADMIN_USER_ALREADY_EXIST,
EAuthenticationErrorCodes.ADMIN_USER_DOES_NOT_EXIST,
EAuthenticationErrorCodes.ADMIN_USER_DEACTIVATED,
];
if (bannerAlertErrorCodes.includes(errorCode))
return {
type: EErrorAlertType.BANNER_ALERT,
code: errorCode,
title: errorCodeMessages[errorCode]?.title || "Error",
message: errorCodeMessages[errorCode]?.message(email) || "Something went wrong. Please try again.",
};
return undefined;
};

View File

@ -0,0 +1,20 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "";
export const ADMIN_BASE_PATH = process.env.NEXT_PUBLIC_ADMIN_BASE_PATH || "";
export const SPACE_BASE_URL = process.env.NEXT_PUBLIC_SPACE_BASE_URL || "";
export const SPACE_BASE_PATH = process.env.NEXT_PUBLIC_SPACE_BASE_PATH || "";
export const WEB_BASE_URL = process.env.NEXT_PUBLIC_WEB_BASE_URL || "";
export const SUPPORT_EMAIL = process.env.NEXT_PUBLIC_SUPPORT_EMAIL || "";
export const ASSET_PREFIX = ADMIN_BASE_PATH;
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
export const resolveGeneralTheme = (resolvedTheme: string | undefined) =>
resolvedTheme?.includes("light") ? "light" : resolvedTheme?.includes("dark") ? "dark" : "system";

2
admin/helpers/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from "./instance.helper";
export * from "./user.helper";

View File

@ -0,0 +1,9 @@
export enum EInstanceStatus {
ERROR = "ERROR",
NOT_YET_READY = "NOT_YET_READY",
}
export type TInstanceStatus = {
status: EInstanceStatus | undefined;
data?: object;
};

View File

@ -0,0 +1,16 @@
import zxcvbn from "zxcvbn";
export const isPasswordCriteriaMet = (password: string) => {
const criteria = [password.length >= 8, /[A-Z]/.test(password), /\d/.test(password), /[!@#$%^&*]/.test(password)];
return criteria.every((criterion) => criterion);
};
export const getPasswordStrength = (password: string) => {
if (password.length === 0) return 0;
if (password.length < 8) return 1;
if (!isPasswordCriteriaMet(password)) return 2;
const result = zxcvbn(password);
return result.score;
};

View File

@ -0,0 +1,21 @@
export enum EAuthenticationPageType {
STATIC = "STATIC",
NOT_AUTHENTICATED = "NOT_AUTHENTICATED",
AUTHENTICATED = "AUTHENTICATED",
}
export enum EInstancePageType {
PRE_SETUP = "PRE_SETUP",
POST_SETUP = "POST_SETUP",
}
export enum EUserStatus {
ERROR = "ERROR",
AUTHENTICATION_NOT_DONE = "AUTHENTICATION_NOT_DONE",
NOT_YET_READY = "NOT_YET_READY",
}
export type TUserStatus = {
status: EUserStatus | undefined;
message?: string;
};

View File

@ -0,0 +1,3 @@
export * from "./use-theme";
export * from "./use-instance";
export * from "./use-user";

View File

@ -0,0 +1,10 @@
import { useContext } from "react";
// store
import { StoreContext } from "@/lib/store-provider";
import { IInstanceStore } from "@/store/instance.store";
export const useInstance = (): IInstanceStore => {
const context = useContext(StoreContext);
if (context === undefined) throw new Error("useInstance must be used within StoreProvider");
return context.instance;
};

View File

@ -0,0 +1,10 @@
import { useContext } from "react";
// store
import { StoreContext } from "@/lib/store-provider";
import { IThemeStore } from "@/store/theme.store";
export const useTheme = (): IThemeStore => {
const context = useContext(StoreContext);
if (context === undefined) throw new Error("useTheme must be used within StoreProvider");
return context.theme;
};

View File

@ -0,0 +1,10 @@
import { useContext } from "react";
// store
import { StoreContext } from "@/lib/store-provider";
import { IUserStore } from "@/store/user.store";
export const useUser = (): IUserStore => {
const context = useContext(StoreContext);
if (context === undefined) throw new Error("useUser must be used within StoreProvider");
return context.user;
};

View File

@ -0,0 +1,21 @@
"use client";
import React, { useEffect } from "react";
const useOutsideClickDetector = (ref: React.RefObject<HTMLElement>, callback: () => void) => {
const handleClick = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
callback();
}
};
useEffect(() => {
document.addEventListener("mousedown", handleClick);
return () => {
document.removeEventListener("mousedown", handleClick);
};
});
};
export default useOutsideClickDetector;

View File

@ -0,0 +1,47 @@
"use client";
import { FC, ReactNode, useEffect } from "react";
import { observer } from "mobx-react-lite";
import { useRouter } from "next/navigation";
// components
import { InstanceSidebar } from "@/components/admin-sidebar";
import { InstanceHeader } from "@/components/auth-header";
import { LogoSpinner } from "@/components/common";
import { NewUserPopup } from "@/components/new-user-popup";
// hooks
import { useUser } from "@/hooks/store";
type TAdminLayout = {
children: ReactNode;
};
export const AdminLayout: FC<TAdminLayout> = observer((props) => {
const { children } = props;
// router
const router = useRouter();
const { isUserLoggedIn } = useUser();
useEffect(() => {
if (isUserLoggedIn === false) {
router.push("/");
}
}, [router, isUserLoggedIn]);
if (isUserLoggedIn === undefined) {
return (
<div className="relative flex h-screen w-full items-center justify-center">
<LogoSpinner />
</div>
);
}
return (
<div className="relative flex h-screen w-screen overflow-hidden">
<InstanceSidebar />
<main className="relative flex h-full w-full flex-col overflow-hidden bg-custom-background-100">
<InstanceHeader />
<div className="h-full w-full overflow-hidden">{children}</div>
</main>
<NewUserPopup />
</div>
);
});

View File

@ -0,0 +1,45 @@
"use client";
import { FC, ReactNode } from "react";
import Image from "next/image";
import Link from "next/link";
import { useTheme } from "next-themes";
// logo/ images
import PlaneBackgroundPatternDark from "public/auth/background-pattern-dark.svg";
import PlaneBackgroundPattern from "public/auth/background-pattern.svg";
import BlackHorizontalLogo from "public/plane-logos/black-horizontal-with-blue-logo.png";
import WhiteHorizontalLogo from "public/plane-logos/white-horizontal-with-blue-logo.png";
type TDefaultLayout = {
children: ReactNode;
withoutBackground?: boolean;
};
export const DefaultLayout: FC<TDefaultLayout> = (props) => {
const { children, withoutBackground = false } = props;
// hooks
const { resolvedTheme } = useTheme();
const patternBackground = resolvedTheme === "dark" ? PlaneBackgroundPatternDark : PlaneBackgroundPattern;
const logo = resolvedTheme === "light" ? BlackHorizontalLogo : WhiteHorizontalLogo;
return (
<div className="relative">
<div className="h-screen w-full overflow-hidden overflow-y-auto flex flex-col">
<div className="container h-[110px] flex-shrink-0 mx-auto px-5 lg:px-0 flex items-center justify-between gap-5 z-50">
<div className="flex items-center gap-x-2 py-10">
<Link href={`/`} className="h-[30px] w-[133px]">
<Image src={logo} alt="Plane logo" />
</Link>
</div>
</div>
{!withoutBackground && (
<div className="absolute inset-0 z-0">
<Image src={patternBackground} className="w-screen h-full object-cover" alt="Plane background pattern" />
</div>
)}
<div className="relative z-10 flex-grow">{children}</div>
</div>
</div>
);
};

View File

@ -0,0 +1,55 @@
import { FC, ReactNode } from "react";
import { observer } from "mobx-react-lite";
import useSWR from "swr";
// components
import { LogoSpinner } from "@/components/common";
import { InstanceSetupForm, InstanceFailureView } from "@/components/instance";
// hooks
import { useInstance } from "@/hooks/store";
// layout
import { DefaultLayout } from "@/layouts/default-layout";
type InstanceProviderProps = {
children: ReactNode;
};
export const InstanceProvider: FC<InstanceProviderProps> = observer((props) => {
const { children } = props;
// store hooks
const { instance, error, fetchInstanceInfo } = useInstance();
// fetching instance details
useSWR("INSTANCE_DETAILS", () => fetchInstanceInfo(), {
revalidateOnFocus: false,
revalidateIfStale: false,
errorRetryCount: 0,
});
if (!instance && !error)
return (
<div className="flex h-screen min-h-[500px] w-full justify-center items-center">
<LogoSpinner />
</div>
);
if (error) {
return (
<DefaultLayout>
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
<InstanceFailureView />
</div>
</DefaultLayout>
);
}
if (!instance?.is_setup_done) {
return (
<DefaultLayout>
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
<InstanceSetupForm />
</div>
</DefaultLayout>
);
}
return <>{children}</>;
});

View File

@ -0,0 +1,34 @@
"use client";
import { ReactNode, createContext } from "react";
// store
import { RootStore } from "@/store/root.store";
let rootStore = new RootStore();
export const StoreContext = createContext(rootStore);
function initializeStore(initialData = {}) {
const singletonRootStore = rootStore ?? new RootStore();
// If your page has Next.js data fetching methods that use a Mobx store, it will
// get hydrated here, check `pages/ssg.js` and `pages/ssr.js` for more details
if (initialData) {
singletonRootStore.hydrate(initialData);
}
// For SSG and SSR always create a new store
if (typeof window === "undefined") return singletonRootStore;
// Create the store once in the client
if (!rootStore) rootStore = singletonRootStore;
return singletonRootStore;
}
export type StoreProviderProps = {
children: ReactNode;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
initialState?: any;
};
export const StoreProvider = ({ children, initialState = {} }: StoreProviderProps) => {
const store = initializeStore(initialState);
return <StoreContext.Provider value={store}>{children}</StoreContext.Provider>;
};

View File

@ -0,0 +1,31 @@
"use client";
import { FC, ReactNode, useEffect } from "react";
import { observer } from "mobx-react-lite";
import useSWR from "swr";
// hooks
import { useInstance, useTheme, useUser } from "@/hooks/store";
interface IUserProvider {
children: ReactNode;
}
export const UserProvider: FC<IUserProvider> = observer(({ children }) => {
// hooks
const { isSidebarCollapsed, toggleSidebar } = useTheme();
const { currentUser, fetchCurrentUser } = useUser();
const { fetchInstanceAdmins } = useInstance();
useSWR("CURRENT_USER", () => fetchCurrentUser(), {
shouldRetryOnError: false,
});
useSWR("INSTANCE_ADMINS", () => fetchInstanceAdmins());
useEffect(() => {
const localValue = localStorage && localStorage.getItem("god_mode_sidebar_collapsed");
const localBoolValue = localValue ? (localValue === "true" ? true : false) : false;
if (isSidebarCollapsed === undefined && localBoolValue != isSidebarCollapsed) toggleSidebar(localBoolValue);
}, [isSidebarCollapsed, currentUser, toggleSidebar]);
return <>{children}</>;
});

5
admin/next-env.d.ts vendored Normal file
View File

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

14
admin/next.config.js Normal file
View File

@ -0,0 +1,14 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
trailingSlash: true,
reactStrictMode: false,
swcMinify: true,
output: "standalone",
images: {
unoptimized: true,
},
basePath: process.env.NEXT_PUBLIC_ADMIN_BASE_PATH || "",
};
module.exports = nextConfig;

50
admin/package.json Normal file
View File

@ -0,0 +1,50 @@
{
"name": "admin",
"version": "0.21.0",
"private": true,
"scripts": {
"dev": "turbo run develop",
"develop": "next dev --port 3001",
"build": "next build",
"preview": "next build && next start",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@headlessui/react": "^1.7.19",
"@plane/types": "*",
"@plane/ui": "*",
"@plane/constants": "*",
"@tailwindcss/typography": "^0.5.9",
"@types/lodash": "^4.17.0",
"autoprefixer": "10.4.14",
"axios": "^1.6.7",
"js-cookie": "^3.0.5",
"lodash": "^4.17.21",
"lucide-react": "^0.356.0",
"mobx": "^6.12.0",
"mobx-react-lite": "^4.0.5",
"next": "^14.2.3",
"next-themes": "^0.2.1",
"postcss": "^8.4.38",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.51.0",
"swr": "^2.2.4",
"tailwindcss": "3.3.2",
"uuid": "^9.0.1",
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@types/js-cookie": "^3.0.6",
"@types/node": "18.16.1",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@types/uuid": "^9.0.8",
"@types/zxcvbn": "^4.4.4",
"eslint-config-custom": "*",
"tailwind-config-custom": "*",
"tsconfig": "*",
"typescript": "^5.4.2"
}
}

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