Compare commits

..
Author SHA1 Message Date
Vamsi KuramaandGitHub 11faf3f810 Merge pull request #1600 from makeplane/stage-release
promote: stage-release to master
2023-07-20 19:36:43 +05:30
Aaryan KhandelwalandGitHub 30ea1adf61 Merge pull request #1597 from makeplane/develop
promote: develop to stage-release
2023-07-20 19:32:04 +05:30
Dakshesh JainandGitHub 546aa40aa3 fix: un-read count not mutating (#1598)
* refactor: height of popover & api fetch call

* fix: notification subscribe endpoint

* fix: un-read count not mutating
2023-07-20 19:15:13 +05:30
NikhilandGitHub 78669363b1 fix: restrict notifications for created by when the actor is same (#1596) 2023-07-20 18:53:02 +05:30
guru_sainathandGitHub bca749986a Merge pull request #1595 from makeplane/develop
promote: develop to stage-release
2023-07-20 18:11:18 +05:30
gurusainath 229938114d Merge branch 'develop' of gurusainath:makeplane/plane into develop 2023-07-20 17:25:31 +05:30
gurusainath c5e418ab47 Merge branch 'stage-release' of gurusainath:makeplane/plane into develop 2023-07-20 17:25:00 +05:30
ecdd1f1d03 promote: develop to stage-release (#1594)
* fix: onboarding invitations overflow (#1575)

* fix: onboarding invitations overflow

* fix: user avatar in the notification card

* style: update graph grid color

* fix: no 'Create by me' label coming up (#1573)

* feat: added new issue subscriber table

* dev: notification model

* feat: added CRUD operation for issue subscriber

* Revert "feat: added CRUD operation for issue subscriber"

This reverts commit b22e062576.

* feat: added CRUD operation for issue subscriber

* dev: notification models and operations

* dev: remove delete endpoint response data

* dev: notification endpoints and fix bg worker for saving notifications

* feat: added list and unsubscribe function in issue subscriber

* dev: filter by snoozed and response update for list and permissions

* dev: update issue notifications

* dev: notification  segregation

* dev: update notifications

* dev: notification filtering

* dev: add issue name in notifications

* dev: notification new endpoints

* fix: pushing local settings

* feat: notification workflow setup and made basic UI

* style: improved UX with toast alerts and other interactions

refactor: changed classnames according to new theme structure, changed all icons to material icons

* feat: showing un-read notification count

* feat: not showing 'subscribe' button on issue created by user & assigned to user

not showing 'Create by you' for view & guest of the workspace

* fix: 'read' -> 'unread' heading, my issue wrong filter

* feat: made snooze dropdown & modal

feat: switched to calendar

* fix: minor ui fixes

* feat: snooze modal date/time select

* fix: params for read/un-read notification

* style: snooze notification modal

* fix: no label for 'Create by me'

* fix: no label for 'Create by me'

* fix: removed console log

* fix: tooltip going behind popover

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>

* style: tooltip on notification header actions (#1577)

* style: tooltip on notification header

* chore: update tooltip content

---------

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

* fix: user migrations for back population (#1578)

* fix: total notifications count (#1579)

* fix: notification card (#1583)

* feat: add new icons package (#1586)

* feat: add material icons package

* chore: replace issue view icons

* chore: notification ordering (#1584)

* fix: uuid error when cycle and module updates (#1585)

* refactor: height of popover & api fetch call (#1587)

* fix: snooze dropdown overflow (#1588)

* fix: notification subscribe endpoint (#1593)

* refactor: height of popover & api fetch call

* fix: notification subscribe endpoint

* chore: notification empty state overflow (#1592)

* chore: notification empty state overflow

* fix: white logo

* fix: custom theme default values

* fix: custom theme default values

* fix: issues count to remove archived issues (#1591)

* dev: background migration for user custom themes (#1590)

---------

Co-authored-by: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: Nikhil <118773738+pablohashescobar@users.noreply.github.com>
Co-authored-by: gurusainath <gurusainath007@gmail.com>
2023-07-20 17:24:17 +05:30
NikhilandGitHub e687cd6f6a dev: background migration for user custom themes (#1590) 2023-07-20 16:59:09 +05:30
NikhilandGitHub 9b6721790f fix: issues count to remove archived issues (#1591) 2023-07-20 16:58:34 +05:30
gurusainath 05df65577a Merge branch 'stage-release' of gurusainath:makeplane/plane into develop 2023-07-20 16:54:40 +05:30
Aaryan KhandelwalandGitHub 52d21b9dda chore: notification empty state overflow (#1592)
* chore: notification empty state overflow

* fix: white logo

* fix: custom theme default values

* fix: custom theme default values
2023-07-20 16:51:00 +05:30
Dakshesh JainandGitHub 51f10d5f36 fix: notification subscribe endpoint (#1593)
* refactor: height of popover & api fetch call

* fix: notification subscribe endpoint
2023-07-20 16:35:18 +05:30
9275e6f373 promote: develop to stage-release (#1589)
* fix: onboarding invitations overflow (#1575)

* fix: onboarding invitations overflow

* fix: user avatar in the notification card

* style: update graph grid color

* fix: no 'Create by me' label coming up (#1573)

* feat: added new issue subscriber table

* dev: notification model

* feat: added CRUD operation for issue subscriber

* Revert "feat: added CRUD operation for issue subscriber"

This reverts commit b22e062576.

* feat: added CRUD operation for issue subscriber

* dev: notification models and operations

* dev: remove delete endpoint response data

* dev: notification endpoints and fix bg worker for saving notifications

* feat: added list and unsubscribe function in issue subscriber

* dev: filter by snoozed and response update for list and permissions

* dev: update issue notifications

* dev: notification  segregation

* dev: update notifications

* dev: notification filtering

* dev: add issue name in notifications

* dev: notification new endpoints

* fix: pushing local settings

* feat: notification workflow setup and made basic UI

* style: improved UX with toast alerts and other interactions

refactor: changed classnames according to new theme structure, changed all icons to material icons

* feat: showing un-read notification count

* feat: not showing 'subscribe' button on issue created by user & assigned to user

not showing 'Create by you' for view & guest of the workspace

* fix: 'read' -> 'unread' heading, my issue wrong filter

* feat: made snooze dropdown & modal

feat: switched to calendar

* fix: minor ui fixes

* feat: snooze modal date/time select

* fix: params for read/un-read notification

* style: snooze notification modal

* fix: no label for 'Create by me'

* fix: no label for 'Create by me'

* fix: removed console log

* fix: tooltip going behind popover

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>

* style: tooltip on notification header actions (#1577)

* style: tooltip on notification header

* chore: update tooltip content

---------

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

* fix: user migrations for back population (#1578)

* fix: total notifications count (#1579)

* fix: notification card (#1583)

* feat: add new icons package (#1586)

* feat: add material icons package

* chore: replace issue view icons

* chore: notification ordering (#1584)

* fix: uuid error when cycle and module updates (#1585)

* refactor: height of popover & api fetch call (#1587)

* fix: snooze dropdown overflow (#1588)

---------

Co-authored-by: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: Nikhil <118773738+pablohashescobar@users.noreply.github.com>
2023-07-20 15:14:57 +05:30
Aaryan KhandelwalandGitHub 4aef8c2242 fix: snooze dropdown overflow (#1588) 2023-07-20 14:49:30 +05:30
Dakshesh JainandGitHub 780573dadd refactor: height of popover & api fetch call (#1587) 2023-07-20 14:33:24 +05:30
NikhilandGitHub 5e625ab132 fix: uuid error when cycle and module updates (#1585) 2023-07-20 14:18:28 +05:30
NikhilandGitHub 34123681cf chore: notification ordering (#1584) 2023-07-20 14:18:06 +05:30
Aaryan KhandelwalandGitHub c72ff782ac feat: add new icons package (#1586)
* feat: add material icons package

* chore: replace issue view icons
2023-07-20 14:17:21 +05:30
Aaryan KhandelwalandGitHub 6eb72507a5 fix: notification card (#1583) 2023-07-20 10:52:52 +05:30
Aaryan KhandelwalandGitHub 26b18b431b fix: total notifications count (#1579) 2023-07-19 20:37:40 +05:30
NikhilandGitHub 1bae9289f5 fix: user migrations for back population (#1578) 2023-07-19 20:06:12 +05:30
5c5bcb33e3 style: tooltip on notification header actions (#1577)
* style: tooltip on notification header

* chore: update tooltip content

---------

Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
2023-07-19 18:42:32 +05:30
bed5f76082 fix: no 'Create by me' label coming up (#1573)
* feat: added new issue subscriber table

* dev: notification model

* feat: added CRUD operation for issue subscriber

* Revert "feat: added CRUD operation for issue subscriber"

This reverts commit b22e062576.

* feat: added CRUD operation for issue subscriber

* dev: notification models and operations

* dev: remove delete endpoint response data

* dev: notification endpoints and fix bg worker for saving notifications

* feat: added list and unsubscribe function in issue subscriber

* dev: filter by snoozed and response update for list and permissions

* dev: update issue notifications

* dev: notification  segregation

* dev: update notifications

* dev: notification filtering

* dev: add issue name in notifications

* dev: notification new endpoints

* fix: pushing local settings

* feat: notification workflow setup and made basic UI

* style: improved UX with toast alerts and other interactions

refactor: changed classnames according to new theme structure, changed all icons to material icons

* feat: showing un-read notification count

* feat: not showing 'subscribe' button on issue created by user & assigned to user

not showing 'Create by you' for view & guest of the workspace

* fix: 'read' -> 'unread' heading, my issue wrong filter

* feat: made snooze dropdown & modal

feat: switched to calendar

* fix: minor ui fixes

* feat: snooze modal date/time select

* fix: params for read/un-read notification

* style: snooze notification modal

* fix: no label for 'Create by me'

* fix: no label for 'Create by me'

* fix: removed console log

* fix: tooltip going behind popover

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
2023-07-19 17:40:40 +05:30
Aaryan KhandelwalandGitHub 124c2f772e fix: onboarding invitations overflow (#1575)
* fix: onboarding invitations overflow

* fix: user avatar in the notification card

* style: update graph grid color
2023-07-19 17:09:53 +05:30
Vihar KuramaandGitHub b38898753f Merge pull request #1570 from makeplane/develop
promote: develop to stage-release
2023-07-19 16:07:43 +05:30
guru_sainathandGitHub 86a120f11e Merge branch 'stage-release' into develop 2023-07-19 16:05:13 +05:30
Rhea JainandGitHub da603dc3f8 Update README.md (#1543) 2023-07-19 15:28:01 +05:30
NikhilandGitHub 2f3970f641 feat: beat worker for configuration (#1571) 2023-07-19 15:25:33 +05:30
NikhilandGitHub d759438ebd fix: issue automation (#1569) 2023-07-19 15:08:41 +05:30
NikhilandGitHub 0102f1d693 fix: psycopg errors (#1568) 2023-07-19 15:07:54 +05:30
pablohashescobarandGitHub fd3f6ab7a4 Merge pull request #1478 from makeplane/stage-release
promote: stage-release to master
2023-07-05 21:24:39 +05:30
pablohashescobarandGitHub d05d814efe chore: workspace invitation for self-hosted versions (#1477) 2023-07-05 21:21:31 +05:30
Vamsi KuramaandGitHub 6394f517a2 Merge pull request #1413 from makeplane/stage-release
promote: stage-release to master
2023-06-28 19:19:59 +05:30
379d258375 fix: charts design and mutation (#1426) (#1427)
* fix: pie chart overlap issue

* fix: burndown chart mutation

* fix: burndown chart mutation

Co-authored-by: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com>
2023-06-28 19:09:44 +05:30
pablohashescobarandGitHub 6a27827c16 Merge pull request #1425 from makeplane/develop
promote: develop to stage-release
2023-06-28 17:48:53 +05:30
pablohashescobarandGitHub 206ca98307 Merge pull request #1418 from makeplane/develop
promote: develop to stage-release
2023-06-28 11:14:41 +05:30
pablohashescobarandGitHub 473c32d3d4 fix: github importer issue (#1414) (#1415) 2023-06-27 22:56:06 +05:30
guru_sainathandGitHub 124d1c30aa Merge pull request #1410 from makeplane/develop
promote: develop to stage-release
2023-06-27 19:01:26 +05:30
guru_sainathandGitHub fd7274ba1c Merge pull request #1406 from makeplane/develop
promote: develop to stage-release
2023-06-27 16:48:34 +05:30
pablohashescobarandGitHub 46d93525be Merge pull request #1386 from makeplane/develop
promote: develop to stage-release
2023-06-24 01:34:34 +05:30
Vamsi KuramaandGitHub dbbce439ff Merge pull request #1311 from makeplane/stage-release
promote: staging to master
2023-06-16 21:34:03 +05:30
pablohashescobarandGitHub 4c0857233e fix: workspace member invite to avoid lower permission user to invite higher permission member (#1309) 2023-06-16 19:52:24 +05:30
46 changed files with 900 additions and 1057 deletions
+8 -8
View File
@@ -19,14 +19,14 @@
<p>
<a href="https://app.plane.so/#gh-light-mode-only" target="_blank">
<img
src="https://ik.imagekit.io/killbluedog/Plane_Screen.png?updatedAt=1684942001069"
src="https://plane-marketing.s3.ap-south-1.amazonaws.com/Plane+Screen.png"
alt="Plane Screens"
width="100%"
/>
</a>
<a href="https://app.plane.so/#gh-dark-mode-only" target="_blank">
<img
src="https://ik.imagekit.io/killbluedog/Plane_Screens_Dark_Mode.png?updatedAt=1684942388044"
src="https://plane-marketing.s3.ap-south-1.amazonaws.com/Plane+Screens+Dark+Mode.png"
alt="Plane Screens"
width="100%"
/>
@@ -86,7 +86,7 @@ docker compose up -d
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/killbluedog/Plane_Views_Dark_Mode.png?updatedAt=1684943050275"
src="https://plane-marketing.s3.ap-south-1.amazonaws.com/Plane+Views+Dark+Mode.png"
alt="Plane Views"
width="100%"
/>
@@ -95,7 +95,7 @@ docker compose up -d
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/killbluedog/Plane_Issue_Detail_Dark_Mode.png?updatedAt=1684943050202"
src="https://plane-marketing.s3.ap-south-1.amazonaws.com/Plane+Issue+Detail+Dark+Mode.png"
alt="Plane Issue Details"
width="100%"
/>
@@ -104,7 +104,7 @@ docker compose up -d
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/killbluedog/Plane_Cycles___Modules_Dark_Mode.png?updatedAt=1684943050281"
src="https://plane-marketing.s3.ap-south-1.amazonaws.com/Plane+Cycles+%26+Modules+Dark+Mode.png"
alt="Plane Cycles and Modules"
width="100%"
/>
@@ -113,7 +113,7 @@ docker compose up -d
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/killbluedog/Plane_Analytics_Dark_Mode.png?updatedAt=1684944596824"
src="https://plane-marketing.s3.ap-south-1.amazonaws.com/Plane+Analytics+Dark+Mode.png"
alt="Plane Analytics"
width="100%"
/>
@@ -122,7 +122,7 @@ docker compose up -d
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/killbluedog/Plane_Pages_Dark_Mode.png?updatedAt=1684943050202"
src="https://plane-marketing.s3.ap-south-1.amazonaws.com/Plane+Pages+Dark+Mode.png"
alt="Plane Pages"
width="100%"
/>
@@ -132,7 +132,7 @@ docker compose up -d
<p>
<a href="https://plane.so" target="_blank">
<img
src="https://ik.imagekit.io/killbluedog/Plane_Commad_K_Dark_Mode.png?updatedAt=1684943050312"
src="https://plane-marketing.s3.ap-south-1.amazonaws.com/Plane+Commad+K+Dark+Mode.png"
alt="Plane Command Menu"
width="100%"
/>
+1 -1
View File
@@ -49,7 +49,7 @@ USER root
RUN apk --no-cache add "bash~=5.2"
COPY ./bin ./bin/
RUN chmod +x ./bin/takeoff ./bin/worker
RUN chmod +x ./bin/takeoff ./bin/worker ./bin/beat
RUN chmod -R 777 /code
USER captain
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
set -e
python manage.py wait_for_db
celery -A plane beat -l info
+1 -1
View File
@@ -6,4 +6,4 @@ python manage.py migrate
# Create a Default User
python bin/user_script.py
exec gunicorn -w 8 -k uvicorn.workers.UvicornWorker plane.asgi:application --bind 0.0.0.0:8000 --config gunicorn.config.py --max-requests 1200 --max-requests-jitter 1000 --access-logfile -
exec gunicorn -w 8 -k uvicorn.workers.UvicornWorker plane.asgi:application --bind 0.0.0.0:8000 --max-requests 1200 --max-requests-jitter 1000 --access-logfile -
+4 -2
View File
@@ -30,7 +30,6 @@ class NotificationViewSet(BaseViewSet):
def list(self, request, slug):
try:
order_by = request.GET.get("order_by", "-created_at")
snoozed = request.GET.get("snoozed", "false")
archived = request.GET.get("archived", "false")
read = request.GET.get("read", "true")
@@ -40,7 +39,7 @@ class NotificationViewSet(BaseViewSet):
notifications = Notification.objects.filter(
workspace__slug=slug, receiver_id=request.user.id
).order_by(order_by)
).order_by("snoozed_till", "-created_at")
# Filter for snoozed notifications
if snoozed == "false":
@@ -215,6 +214,7 @@ class UnreadNotificationEndpoint(BaseAPIView):
workspace__slug=slug,
receiver_id=request.user.id,
read_at__isnull=True,
archived_at__isnull=True,
entity_identifier__in=IssueSubscriber.objects.filter(
workspace__slug=slug, subscriber_id=request.user.id
).values_list("issue_id", flat=True),
@@ -225,6 +225,7 @@ class UnreadNotificationEndpoint(BaseAPIView):
workspace__slug=slug,
receiver_id=request.user.id,
read_at__isnull=True,
archived_at__isnull=True,
entity_identifier__in=IssueAssignee.objects.filter(
workspace__slug=slug, assignee_id=request.user.id
).values_list("issue_id", flat=True),
@@ -235,6 +236,7 @@ class UnreadNotificationEndpoint(BaseAPIView):
workspace__slug=slug,
receiver_id=request.user.id,
read_at__isnull=True,
archived_at__isnull=True,
entity_identifier__in=Issue.objects.filter(
workspace__slug=slug, created_by=request.user
).values_list("pk", flat=True),
+83 -65
View File
@@ -1028,21 +1028,26 @@ def issue_activity(
actor = User.objects.get(pk=actor_id)
project = Project.objects.get(pk=project_id)
if type not in [
"cycle.activity.created",
"cycle.activity.deleted",
"module.activity.created",
"module.activity.deleted",
]:
issue = Issue.objects.filter(pk=issue_id, project_id=project_id).first()
issue = Issue.objects.filter(pk=issue_id, project_id=project_id).first()
if issue is not None:
issue.updated_at = timezone.now()
issue.save(update_fields=["updated_at"])
if issue is not None:
issue.updated_at = timezone.now()
issue.save(update_fields=["updated_at"])
if subscriber:
# add the user to issue subscriber
try:
_ = IssueSubscriber.objects.get_or_create(
issue_id=issue_id, subscriber=actor
)
except Exception as e:
pass
if subscriber:
# add the user to issue subscriber
try:
_ = IssueSubscriber.objects.get_or_create(
issue_id=issue_id, subscriber=actor
)
except Exception as e:
pass
ACTIVITY_MAPPER = {
"issue.activity.created": create_issue_activity,
@@ -1094,67 +1099,80 @@ def issue_activity(
except Exception as e:
capture_exception(e)
# Create Notifications
bulk_notifications = []
if type not in [
"cycle.activity.created",
"cycle.activity.deleted",
"module.activity.created",
"module.activity.deleted",
]:
# Create Notifications
bulk_notifications = []
issue_subscribers = list(
IssueSubscriber.objects.filter(project=project, issue_id=issue_id)
.exclude(subscriber_id=actor_id)
.values_list("subscriber", flat=True)
)
issue_subscribers = list(
IssueSubscriber.objects.filter(project=project, issue_id=issue_id)
.exclude(subscriber_id=actor_id)
.values_list("subscriber", flat=True)
)
issue_assignees = list(
IssueAssignee.objects.filter(project=project, issue_id=issue_id)
.exclude(assignee_id=actor_id)
.values_list("assignee", flat=True)
)
issue_assignees = list(
IssueAssignee.objects.filter(project=project, issue_id=issue_id)
.exclude(assignee_id=actor_id)
.values_list("assignee", flat=True)
)
issue_subscribers = issue_subscribers + issue_assignees
issue_subscribers = issue_subscribers + issue_assignees
issue = Issue.objects.filter(pk=issue_id, project_id=project_id).first()
issue = Issue.objects.filter(pk=issue_id, project_id=project_id).first()
# Add bot filtering
if issue is not None and issue.created_by_id is not None and not issue.created_by.is_bot:
issue_subscribers = issue_subscribers + [issue.created_by_id]
# Add bot filtering
if (
issue is not None
and issue.created_by_id is not None
and not issue.created_by.is_bot
and str(issue.created_by_id) != str(actor_id)
):
issue_subscribers = issue_subscribers + [issue.created_by_id]
for subscriber in issue_subscribers:
for issue_activity in issue_activities_created:
bulk_notifications.append(
Notification(
workspace=project.workspace,
sender="in_app:issue_activities",
triggered_by_id=actor_id,
receiver_id=subscriber,
entity_identifier=issue_id,
entity_name="issue",
project=project,
title=issue_activity.comment,
data={
"issue": {
"id": str(issue_id),
"name": str(issue.name),
"identifier": str(project.identifier),
"sequence_id": issue.sequence_id,
"state_name": issue.state.name,
"state_group": issue.state.group,
for subscriber in issue_subscribers:
for issue_activity in issue_activities_created:
bulk_notifications.append(
Notification(
workspace=project.workspace,
sender="in_app:issue_activities",
triggered_by_id=actor_id,
receiver_id=subscriber,
entity_identifier=issue_id,
entity_name="issue",
project=project,
title=issue_activity.comment,
data={
"issue": {
"id": str(issue_id),
"name": str(issue.name),
"identifier": str(project.identifier),
"sequence_id": issue.sequence_id,
"state_name": issue.state.name,
"state_group": issue.state.group,
},
"issue_activity": {
"id": str(issue_activity.id),
"verb": str(issue_activity.verb),
"field": str(issue_activity.field),
"actor": str(issue_activity.actor_id),
"new_value": str(issue_activity.new_value),
"old_value": str(issue_activity.old_value),
"issue_comment": str(
issue_activity.issue_comment.comment_stripped
if issue_activity.issue_comment is not None
else ""
),
},
},
"issue_activity": {
"id": str(issue_activity.id),
"verb": str(issue_activity.verb),
"field": str(issue_activity.field),
"actor": str(issue_activity.actor_id),
"new_value": str(issue_activity.new_value),
"old_value": str(issue_activity.old_value),
"issue_comment": str(
issue_activity.issue_comment.comment_stripped if issue_activity.issue_comment is not None else ""
),
},
},
)
)
)
# Bulk create notifications
Notification.objects.bulk_create(bulk_notifications, batch_size=100)
# Bulk create notifications
Notification.objects.bulk_create(bulk_notifications, batch_size=100)
return
except Exception as e:
@@ -8,6 +8,7 @@ import plane.db.models.user
import uuid
def onboarding_default_steps(apps, schema_editor):
default_onboarding_schema = {
"workspace_join": True,
@@ -23,7 +24,7 @@ def onboarding_default_steps(apps, schema_editor):
obj.is_tour_completed = True
updated_user.append(obj)
Model.objects.bulk_update(updated_user, ["onboarding_step"], batch_size=100)
Model.objects.bulk_update(updated_user, ["onboarding_step", "is_tour_completed"], batch_size=100)
class Migration(migrations.Migration):
@@ -79,6 +80,7 @@ class Migration(migrations.Migration):
name="onboarding_step",
field=models.JSONField(default=plane.db.models.user.get_default_onboarding),
),
migrations.RunPython(onboarding_default_steps),
migrations.CreateModel(
name="Notification",
fields=[
@@ -0,0 +1,35 @@
# Generated by Django 4.2.3 on 2023-07-20 09:35
from django.db import migrations, models
def restructure_theming(apps, schema_editor):
Model = apps.get_model("db", "User")
updated_user = []
for obj in Model.objects.exclude(theme={}).all():
current_theme = obj.theme
updated_theme = {
"primary": current_theme.get("accent", ""),
"background": current_theme.get("bgBase", ""),
"sidebarBackground": current_theme.get("sidebar", ""),
"text": current_theme.get("textBase", ""),
"sidebarText": current_theme.get("textBase", ""),
"palette": f"""{current_theme.get("bgBase","")},{current_theme.get("textBase", "")},{current_theme.get("accent", "")},{current_theme.get("sidebar","")},{current_theme.get("textBase", "")}""",
"darkPalette": current_theme.get("darkPalette", "")
}
obj.theme = updated_theme
updated_user.append(obj)
Model.objects.bulk_update(
updated_user, ["theme"], batch_size=100
)
class Migration(migrations.Migration):
dependencies = [
("db", "0037_issue_archived_at_project_archive_in_and_more"),
]
operations = [
migrations.RunPython(restructure_theming)
]
+1 -1
View File
@@ -17,7 +17,7 @@ EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ.get("PGUSER", "plane"),
"USER": "",
"PASSWORD": "",
+1 -1
View File
@@ -17,7 +17,7 @@ DEBUG = int(os.environ.get("DEBUG", 0)) == 1
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"ENGINE": "django.db.backends.postgresql",
"NAME": "plane",
"USER": os.environ.get("PGUSER", ""),
"PASSWORD": os.environ.get("PGPASSWORD", ""),
+1 -1
View File
@@ -16,7 +16,7 @@ from .common import * # noqa
DEBUG = int(os.environ.get("DEBUG", 1)) == 1
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ.get("PGUSER", "plane"),
"USER": "",
"PASSWORD": "",
+2
View File
@@ -30,3 +30,5 @@ openai==0.27.8
slack-sdk==3.21.3
celery==5.3.1
django_celery_beat==2.5.0
psycopg-binary==3.1.9
psycopg-c==3.1.9
+1 -1
View File
@@ -7,5 +7,5 @@ django-storages==1.13.2
boto3==1.27.0
django-anymail==10.0
django-debug-toolbar==4.1.0
gevent==22.10.2
gevent==23.7.0
psycogreen==1.0.2
@@ -120,7 +120,7 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
Please check your inbox at <span className="font-medium">{watch("email")}</span>
</p>
)}
<form className="space-y-4 mt-10">
<form className="space-y-4 mt-10 sm:w-[360px] mx-auto">
<div className="space-y-1">
<Input
id="email"
@@ -11,14 +11,16 @@ import useEstimateOption from "hooks/use-estimate-option";
// components
import { SelectFilters } from "components/views";
// ui
import { CustomMenu, Icon, ToggleSwitch, Tooltip } from "components/ui";
import { CustomMenu, ToggleSwitch, Tooltip } from "components/ui";
// icons
import { ChevronDownIcon } from "@heroicons/react/24/outline";
import {
ChevronDownIcon,
ListBulletIcon,
Squares2X2Icon,
CalendarDaysIcon,
} from "@heroicons/react/24/outline";
CalendarMonthOutlined,
FormatListBulletedOutlined,
GridViewOutlined,
TableChartOutlined,
WaterfallChartOutlined,
} from "@mui/icons-material";
// helpers
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
import { checkIfArraysHaveSameElements } from "helpers/array.helper";
@@ -27,26 +29,26 @@ import { Properties, TIssueViewOptions } from "types";
// constants
import { GROUP_BY_OPTIONS, ORDER_BY_OPTIONS, FILTER_ISSUE_OPTIONS } from "constants/issue";
const issueViewOptions: { type: TIssueViewOptions; icon: any }[] = [
const issueViewOptions: { type: TIssueViewOptions; Icon: any }[] = [
{
type: "list",
icon: <ListBulletIcon className="h-4 w-4" />,
Icon: FormatListBulletedOutlined,
},
{
type: "kanban",
icon: <Squares2X2Icon className="h-4 w-4" />,
Icon: GridViewOutlined,
},
{
type: "calendar",
icon: <CalendarDaysIcon className="h-4 w-4" />,
Icon: CalendarMonthOutlined,
},
{
type: "spreadsheet",
icon: <Icon iconName="table_chart" />,
Icon: TableChartOutlined,
},
{
type: "gantt_chart",
icon: <Icon iconName="waterfall_chart" className="rotate-90" />,
Icon: WaterfallChartOutlined,
},
];
@@ -98,7 +100,12 @@ export const IssuesFilterView: React.FC = () => {
}`}
onClick={() => setIssueView(option.type)}
>
{option.icon}
<option.Icon
sx={{
fontSize: 16,
}}
className={option.type === "gantt_chart" ? "rotate-90" : ""}
/>
</button>
</Tooltip>
))}
@@ -177,7 +184,6 @@ export const IssuesFilterView: React.FC = () => {
GROUP_BY_OPTIONS.find((option) => option.key === groupByProperty)
?.name ?? "Select"
}
width="lg"
>
{GROUP_BY_OPTIONS.map((option) =>
issueView === "kanban" && option.key === null ? null : (
@@ -198,7 +204,6 @@ export const IssuesFilterView: React.FC = () => {
ORDER_BY_OPTIONS.find((option) => option.key === orderBy)?.name ??
"Select"
}
width="lg"
>
{ORDER_BY_OPTIONS.map((option) =>
groupByProperty === "priority" && option.key === "priority" ? null : (
@@ -223,7 +228,6 @@ export const IssuesFilterView: React.FC = () => {
FILTER_ISSUE_OPTIONS.find((option) => option.key === filters.type)
?.name ?? "Select"
}
width="lg"
>
{FILTER_ISSUE_OPTIONS.map((option) => (
<CustomMenu.MenuItem
@@ -69,8 +69,8 @@ export const CustomThemeSelector: React.FC<Props> = ({ preLoadedData }) => {
return { ...prevData, ...res };
}, false);
applyTheme(payload.palette, darkPalette);
setTheme("custom");
applyTheme(payload.palette, darkPalette);
})
.catch((err) => console.log(err));
};
@@ -71,7 +71,21 @@ export const ThemeSwitch: React.FC<Props> = ({
}
onChange={({ value, type }: { value: string; type: string }) => {
if (value === "custom") {
if (user?.theme.palette) setPreLoadedData(user.theme);
if (user?.theme.palette) {
setPreLoadedData({
background: user.theme.background !== "" ? user.theme.background : "#0d101b",
text: user.theme.text !== "" ? user.theme.text : "#c5c5c5",
primary: user.theme.primary !== "" ? user.theme.primary : "#3f76ff",
sidebarBackground:
user.theme.sidebarBackground !== "" ? user.theme.sidebarBackground : "#0d101b",
sidebarText: user.theme.sidebarText !== "" ? user.theme.sidebarText : "#c5c5c5",
darkPalette: false,
palette:
user.theme.palette !== ",,,,"
? user.theme.palette
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
});
}
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
} else {
@@ -87,6 +101,7 @@ export const ThemeSwitch: React.FC<Props> = ({
document.documentElement.style.removeProperty(`--color-sidebar-border-${i}`);
}
}
setTheme(value);
document.documentElement.style.setProperty("color-scheme", type);
}}
@@ -10,7 +10,7 @@ import useToast from "hooks/use-toast";
import { CustomMenu, Icon, Tooltip } from "components/ui";
// helper
import { stripHTML, replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
import { stripHTML, replaceUnderscoreIfSnakeCase, truncateText } from "helpers/string.helper";
import {
formatDateDistance,
render12HourFormatTime,
@@ -32,7 +32,7 @@ type NotificationCardProps = {
const snoozeOptions = [
{
label: "1 days",
label: "1 day",
value: new Date(new Date().getTime() + 24 * 60 * 60 * 1000),
},
{
@@ -79,102 +79,105 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
`/${workspaceSlug}/projects/${notification.project}/issues/${notification.data.issue.id}`
);
}}
className={`group relative py-3 px-6 cursor-pointer ${
className={`group w-full flex items-center gap-4 p-3 pl-6 relative cursor-pointer ${
notification.read_at === null ? "bg-custom-primary-70/5" : "hover:bg-custom-background-200"
}`}
>
{notification.read_at === null && (
<span className="absolute top-1/2 left-2 -translate-y-1/2 w-1.5 h-1.5 bg-custom-primary-100 rounded-full" />
)}
<div className="flex items-center gap-4 w-full">
<div className="relative w-12 h-12 rounded-full">
{notification.triggered_by_details.avatar &&
notification.triggered_by_details.avatar !== "" ? (
<div className="relative w-12 h-12 rounded-full">
{notification.triggered_by_details.avatar &&
notification.triggered_by_details.avatar !== "" ? (
<div className="h-12 w-12 rounded-full">
<Image
src={notification.triggered_by_details.avatar}
alt="profile image"
alt="Profile Image"
layout="fill"
objectFit="cover"
className="rounded-full"
/>
) : (
<div className="w-12 h-12 bg-custom-background-100 rounded-full flex justify-center items-center">
<span className="text-custom-text-100 font-medium text-lg">
{notification.triggered_by_details.first_name[0].toUpperCase()}
</span>
</div>
)}
</div>
<div className="w-full space-y-2.5">
<div className="text-sm">
<span className="font-semibold">
{notification.triggered_by_details.first_name}{" "}
{notification.triggered_by_details.last_name}{" "}
</div>
) : (
<div className="w-12 h-12 bg-custom-background-80 rounded-full flex justify-center items-center">
<span className="text-custom-text-100 font-medium text-lg">
{notification.triggered_by_details.first_name[0].toUpperCase()}
</span>
{notification.data.issue_activity.field !== "comment" &&
notification.data.issue_activity.verb}{" "}
{notification.data.issue_activity.field === "comment"
? "commented"
: notification.data.issue_activity.field === "None"
? null
: replaceUnderscoreIfSnakeCase(notification.data.issue_activity.field)}{" "}
{notification.data.issue_activity.field !== "comment" &&
notification.data.issue_activity.field !== "None"
? "to"
: ""}
<span className="font-semibold">
{" "}
{notification.data.issue_activity.field !== "None" ? (
notification.data.issue_activity.field !== "comment" ? (
notification.data.issue_activity.field === "target_date" ? (
renderShortDateWithYearFormat(notification.data.issue_activity.new_value)
) : notification.data.issue_activity.field === "attachment" ? (
"the issue"
) : stripHTML(notification.data.issue_activity.new_value).length > 55 ? (
stripHTML(notification.data.issue_activity.new_value).slice(0, 50) + "..."
) : (
stripHTML(notification.data.issue_activity.new_value)
)
</div>
)}
</div>
<div className="space-y-2.5 w-full overflow-hidden">
<div className="text-sm w-full break-words">
<span className="font-semibold">
{notification.triggered_by_details.first_name}{" "}
{notification.triggered_by_details.last_name}{" "}
</span>
{notification.data.issue_activity.field !== "comment" &&
notification.data.issue_activity.verb}{" "}
{notification.data.issue_activity.field === "comment"
? "commented"
: notification.data.issue_activity.field === "None"
? null
: replaceUnderscoreIfSnakeCase(notification.data.issue_activity.field)}{" "}
{notification.data.issue_activity.field !== "comment" &&
notification.data.issue_activity.field !== "None"
? "to"
: ""}
<span className="font-semibold">
{" "}
{notification.data.issue_activity.field !== "None" ? (
notification.data.issue_activity.field !== "comment" ? (
notification.data.issue_activity.field === "target_date" ? (
renderShortDateWithYearFormat(notification.data.issue_activity.new_value)
) : notification.data.issue_activity.field === "attachment" ? (
"the issue"
) : stripHTML(notification.data.issue_activity.new_value).length > 55 ? (
stripHTML(notification.data.issue_activity.new_value).slice(0, 50) + "..."
) : (
<span>
{`"`}
{notification.data.issue_activity.new_value.length > 55
? notification?.data?.issue_activity?.issue_comment?.slice(0, 50) + "..."
: notification.data.issue_activity.issue_comment}
{`"`}
</span>
stripHTML(notification.data.issue_activity.new_value)
)
) : (
"the issue and assigned it to you."
)}
</span>
</div>
<div className="w-full flex justify-between text-xs">
<p className="truncate inline max-w-lg text-custom-text-300 mr-3">
{notification.data.issue.identifier}-{notification.data.issue.sequence_id}{" "}
{notification.data.issue.name}
</p>
{notification.snoozed_till ? (
<p className="text-custom-text-300 flex items-center gap-x-1">
<Icon iconName="schedule" />
<span>
Till {renderShortDate(notification.snoozed_till)},{" "}
{render12HourFormatTime(notification.snoozed_till)}
{`"`}
{notification.data.issue_activity.new_value.length > 55
? notification?.data?.issue_activity?.issue_comment?.slice(0, 50) + "..."
: notification.data.issue_activity.issue_comment}
{`"`}
</span>
</p>
)
) : (
<p className="text-custom-text-300">{formatDateDistance(notification.created_at)}</p>
"the issue and assigned it to you."
)}
</div>
</span>
</div>
<div className="flex justify-between gap-2 text-xs">
<p className="text-custom-text-300">
{truncateText(
`${notification.data.issue.identifier}-${notification.data.issue.sequence_id} ${notification.data.issue.name}`,
50
)}
</p>
{notification.snoozed_till ? (
<p className="text-custom-text-300 flex items-center justify-end gap-x-1 flex-shrink-0">
<Icon iconName="schedule" className="!text-base -my-0.5" />
<span>
Till {renderShortDate(notification.snoozed_till)},{" "}
{render12HourFormatTime(notification.snoozed_till)}
</span>
</p>
) : (
<p className="text-custom-text-300 flex-shrink-0">
{formatDateDistance(notification.created_at)}
</p>
)}
</div>
</div>
<div className="absolute py-1 gap-x-3 right-3 top-3 hidden group-hover:flex">
{[
{
id: 1,
name: notification.read_at ? "Mark as Unread" : "Mark as Read",
name: notification.read_at ? "Mark as unread" : "Mark as read",
icon: "chat_bubble",
onClick: () => {
markNotificationReadStatus(notification.id).then(() => {
@@ -189,8 +192,8 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
},
{
id: 2,
name: notification.archived_at ? "Unarchive Notification" : "Archive Notification",
icon: "archive",
name: notification.archived_at ? "Unarchive" : "Archive",
icon: notification.archived_at ? "unarchive" : "archive",
onClick: () => {
markNotificationArchivedStatus(notification.id).then(() => {
setToastAlert({
@@ -203,7 +206,7 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
},
},
].map((item) => (
<Tooltip tooltipContent={item.name} position="top-left">
<Tooltip tooltipContent={item.name}>
<button
type="button"
onClick={(e) => {
@@ -211,52 +214,54 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
item.onClick();
}}
key={item.id}
className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded"
className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded outline-none"
>
<Icon iconName={item.icon} className="h-5 w-5 text-custom-text-300" />
</button>
</Tooltip>
))}
<Tooltip tooltipContent="Snooze Notification" position="top-left">
<CustomMenu
menuButtonOnClick={(e) => {
e.stopPropagation();
}}
customButton={
<button
type="button"
className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded"
>
<Icon iconName="schedule" className="h-5 w-5 text-custom-text-300" />
</button>
}
optionsClassName="!z-20"
>
{snoozeOptions.map((item) => (
<CustomMenu.MenuItem
key={item.label}
renderAs="button"
onClick={(e) => {
e.stopPropagation();
<Tooltip tooltipContent="Snooze">
<div>
<CustomMenu
menuButtonOnClick={(e) => {
e.stopPropagation();
}}
customButton={
<button
type="button"
className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded"
>
<Icon iconName="schedule" className="h-5 w-5 text-custom-text-300" />
</button>
}
optionsClassName="!z-20"
>
{snoozeOptions.map((item) => (
<CustomMenu.MenuItem
key={item.label}
renderAs="button"
onClick={(e) => {
e.stopPropagation();
if (!item.value) {
setSelectedNotificationForSnooze(notification.id);
return;
}
if (!item.value) {
setSelectedNotificationForSnooze(notification.id);
return;
}
markSnoozeNotification(notification.id, item.value).then(() => {
setToastAlert({
title: `Notification snoozed till ${renderLongDateFormat(item.value)}`,
type: "success",
markSnoozeNotification(notification.id, item.value).then(() => {
setToastAlert({
title: `Notification snoozed till ${renderLongDateFormat(item.value)}`,
type: "success",
});
});
});
}}
>
{item.label}
</CustomMenu.MenuItem>
))}
</CustomMenu>
}}
>
{item.label}
</CustomMenu.MenuItem>
))}
</CustomMenu>
</div>
</Tooltip>
</div>
</div>
@@ -12,8 +12,10 @@ import useWorkspaceMembers from "hooks/use-workspace-members";
import useUserNotification from "hooks/use-user-notifications";
// components
import { Icon, Loader, EmptyState } from "components/ui";
import { Icon, Loader, EmptyState, Tooltip } from "components/ui";
import { SnoozeNotificationModal, NotificationCard } from "components/notifications";
// icons
import { NotificationsOutlined } from "@mui/icons-material";
// images
import emptyNotification from "public/empty-state/notification.svg";
// helpers
@@ -69,7 +71,7 @@ export const NotificationPopover = () => {
{
label: "Subscribed",
value: "watching",
unreadCount: notificationCount?.watching_notifications,
unreadCount: notificationCount?.watching_issues,
},
];
@@ -96,10 +98,10 @@ export const NotificationPopover = () => {
className={`group flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium outline-none ${
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"
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80"
} ${sidebarCollapse ? "justify-center" : ""}`}
>
<Icon iconName="notifications" />
<NotificationsOutlined fontSize="small" />
{sidebarCollapse ? null : <span>Notifications</span>}
{totalNotificationCount && totalNotificationCount > 0 ? (
<span className="ml-auto bg-custom-primary-300 rounded-full text-xs text-white px-1.5">
@@ -116,54 +118,62 @@ export const NotificationPopover = () => {
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute bg-custom-background-100 flex flex-col left-0 md:left-full ml-8 z-10 top-0 md:w-[36rem] w-[20rem] h-[27rem] border border-custom-border-300 shadow-lg rounded-xl">
<Popover.Panel className="absolute bg-custom-background-100 flex flex-col left-0 md:left-full ml-8 z-10 top-0 md:w-[36rem] w-[20rem] h-[50vh] border border-custom-border-300 shadow-lg rounded-xl">
<div className="flex items-center justify-between px-5 pt-5">
<h2 className="text-xl font-semibold mb-2">Notifications</h2>
<div className="flex gap-x-4 justify-center items-center text-custom-text-200">
<button
type="button"
onClick={(e) => {
notificationsMutate();
<Tooltip tooltipContent="Refresh">
<button
type="button"
onClick={(e) => {
notificationsMutate();
const target = e.target as HTMLButtonElement;
target?.classList.add("animate-spin");
setTimeout(() => {
target?.classList.remove("animate-spin");
}, 1000);
}}
>
<Icon iconName="refresh" />
</button>
<button
type="button"
onClick={() => {
setSnoozed(false);
setArchived(false);
setReadNotification((prev) => !prev);
}}
>
<Icon iconName="filter_list" />
</button>
<button
type="button"
onClick={() => {
setArchived(false);
setReadNotification(false);
setSnoozed((prev) => !prev);
}}
>
<Icon iconName="schedule" />
</button>
<button
type="button"
onClick={() => {
setSnoozed(false);
setReadNotification(false);
setArchived((prev) => !prev);
}}
>
<Icon iconName="archive" />
</button>
const target = e.target as HTMLButtonElement;
target?.classList.add("animate-spin");
setTimeout(() => {
target?.classList.remove("animate-spin");
}, 1000);
}}
>
<Icon iconName="refresh" />
</button>
</Tooltip>
<Tooltip tooltipContent="Unread notifications">
<button
type="button"
onClick={() => {
setSnoozed(false);
setArchived(false);
setReadNotification((prev) => !prev);
}}
>
<Icon iconName="filter_list" />
</button>
</Tooltip>
<Tooltip tooltipContent="Snoozed notifications">
<button
type="button"
onClick={() => {
setArchived(false);
setReadNotification(false);
setSnoozed((prev) => !prev);
}}
>
<Icon iconName="schedule" />
</button>
</Tooltip>
<Tooltip tooltipContent="Archived notifications">
<button
type="button"
onClick={() => {
setSnoozed(false);
setReadNotification(false);
setArchived((prev) => !prev);
}}
>
<Icon iconName="archive" />
</button>
</Tooltip>
<button type="button" onClick={() => closePopover()}>
<Icon iconName="close" />
</button>
@@ -205,6 +215,7 @@ export const NotificationPopover = () => {
: "border-transparent text-custom-text-200"
}`}
>
{tab.label}
{tab.unreadCount && tab.unreadCount > 0 ? (
<span
className={`ml-2 rounded-full text-xs px-2 py-0.5 ${
@@ -250,7 +261,7 @@ export const NotificationPopover = () => {
{notifications ? (
notifications.length > 0 ? (
<div className="divide-y divide-custom-border-100 overflow-y-auto">
<div className="divide-y divide-custom-border-100 overflow-y-auto h-full">
{notifications.map((notification) => (
<NotificationCard
key={notification.id}
@@ -263,7 +274,7 @@ export const NotificationPopover = () => {
))}
</div>
) : (
<div className="grid h-full w-full place-items-center overflow-hidden">
<div className="grid h-full w-full place-items-center overflow-hidden scale-75">
<EmptyState
title="You're updated with all the notifications"
description="You have read all the notifications."
@@ -273,7 +284,7 @@ export const NotificationPopover = () => {
</div>
)
) : (
<Loader className="p-5 space-y-4">
<Loader className="p-5 space-y-4 overflow-y-auto">
<Loader.Item height="50px" />
<Loader.Item height="50px" />
<Loader.Item height="50px" />
@@ -88,7 +88,7 @@ export const JoinWorkspaces: React.FC<Props> = ({ stepChange }) => {
<div className="w-full space-y-7 sm:space-y-10">
<h5 className="sm:text-lg">We see that someone has invited you to</h5>
<h4 className="text-xl sm:text-2xl font-semibold">Join a workspace</h4>
<div className="md:w-3/5 space-y-4">
<div className="max-h-[37vh] overflow-y-auto md:w-3/5 space-y-4">
{invitations &&
invitations.map((invitation) => {
const isSelected = invitationsRespond.includes(invitation.id);
@@ -146,7 +146,11 @@ export const JoinWorkspaces: React.FC<Props> = ({ stepChange }) => {
>
Accept & Join
</PrimaryButton>
<SecondaryButton className="border border-none bg-transparent" size="md" onClick={finishOnboarding} >
<SecondaryButton
className="border border-none bg-transparent"
size="md"
onClick={finishOnboarding}
>
Skip for now
</SecondaryButton>
</div>
+18 -13
View File
@@ -1,31 +1,37 @@
// ui
import { Icon } from "components/ui";
// icons
import {
ArticleOutlined,
ContrastOutlined,
DatasetOutlined,
FilterNoneOutlined,
PhotoFilterOutlined,
} from "@mui/icons-material";
// types
import { TTourSteps } from "./root";
const sidebarOptions: {
key: TTourSteps;
icon: string;
Icon: any;
}[] = [
{
key: "issues",
icon: "stack",
Icon: FilterNoneOutlined,
},
{
key: "cycles",
icon: "contrast",
Icon: ContrastOutlined,
},
{
key: "modules",
icon: "dataset",
Icon: DatasetOutlined,
},
{
key: "views",
icon: "photo_filter",
Icon: PhotoFilterOutlined,
},
{
key: "pages",
icon: "article",
Icon: ArticleOutlined,
},
];
@@ -52,11 +58,10 @@ export const TourSidebar: React.FC<Props> = ({ step, setStep }) => (
}`}
onClick={() => setStep(option.key)}
>
<Icon
iconName={option.icon}
className={`h-5 w-5 flex-shrink-0 ${
step === option.key ? "text-custom-primary-100" : "text-custom-text-200"
}`}
<option.Icon
sx={{
fontSize: 18,
}}
aria-hidden="true"
/>
{option.key}
@@ -10,9 +10,19 @@ import projectService from "services/project.service";
// hooks
import useToast from "hooks/use-toast";
// ui
import { CustomMenu, Icon, Tooltip } from "components/ui";
import { CustomMenu, Tooltip } from "components/ui";
// icons
import { LinkIcon, StarIcon, TrashIcon } from "@heroicons/react/24/outline";
import {
ArchiveOutlined,
ArticleOutlined,
ContrastOutlined,
DatasetOutlined,
ExpandMoreOutlined,
FilterNoneOutlined,
PhotoFilterOutlined,
SettingsOutlined,
} from "@mui/icons-material";
// helpers
import { truncateText } from "helpers/string.helper";
import { renderEmoji } from "helpers/emoji.helper";
@@ -33,32 +43,32 @@ const navigation = (workspaceSlug: string, projectId: string) => [
{
name: "Issues",
href: `/${workspaceSlug}/projects/${projectId}/issues`,
icon: "stack",
Icon: FilterNoneOutlined,
},
{
name: "Cycles",
href: `/${workspaceSlug}/projects/${projectId}/cycles`,
icon: "contrast",
Icon: ContrastOutlined,
},
{
name: "Modules",
href: `/${workspaceSlug}/projects/${projectId}/modules`,
icon: "dataset",
Icon: DatasetOutlined,
},
{
name: "Views",
href: `/${workspaceSlug}/projects/${projectId}/views`,
icon: "photo_filter",
Icon: PhotoFilterOutlined,
},
{
name: "Pages",
href: `/${workspaceSlug}/projects/${projectId}/pages`,
icon: "article",
Icon: ArticleOutlined,
},
{
name: "Settings",
href: `/${workspaceSlug}/projects/${projectId}/settings`,
icon: "settings",
Icon: SettingsOutlined,
},
];
@@ -164,8 +174,8 @@ export const SingleSidebarProject: React.FC<Props> = ({
)}
</div>
{!sidebarCollapse && (
<Icon
iconName="expand_more"
<ExpandMoreOutlined
fontSize="small"
className={`${open ? "rotate-180" : ""} text-custom-text-200 duration-300`}
/>
)}
@@ -211,7 +221,7 @@ export const SingleSidebarProject: React.FC<Props> = ({
}
>
<div className="flex items-center justify-start gap-2">
<Icon iconName="archive" className="h-4 w-4" />
<ArchiveOutlined fontSize="small" />
<span>Archived Issues</span>
</div>
</CustomMenu.MenuItem>
@@ -248,13 +258,17 @@ export const SingleSidebarProject: React.FC<Props> = ({
disabled={!sidebarCollapse}
>
<div
className={`group flex items-center rounded-md px-2 py-1.5 gap-2 text-xs font-medium outline-none ${
className={`group flex items-center rounded-md px-2 py-1.5 gap-2.5 text-xs font-medium outline-none ${
router.asPath.includes(item.href)
? "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"
} ${sidebarCollapse ? "justify-center" : ""}`}
>
<Icon iconName={item.icon} />
<item.Icon
sx={{
fontSize: 18,
}}
/>
{!sidebarCollapse && item.name}
</div>
</Tooltip>
-187
View File
@@ -1,187 +0,0 @@
import React from "react";
// next
import Link from "next/link";
// headless ui
import { Menu, Transition } from "@headlessui/react";
// icons
import { ChevronDownIcon } from "@heroicons/react/24/outline";
import { Icon } from "./icon";
type Props = {
children: React.ReactNode;
label?: string | JSX.Element;
className?: string;
ellipsis?: boolean;
verticalEllipsis?: boolean;
height?: "sm" | "md" | "rg" | "lg";
width?: "sm" | "md" | "lg" | "xl" | "auto";
textAlignment?: "left" | "center" | "right";
noBorder?: boolean;
noChevron?: boolean;
position?: "left" | "right";
verticalPosition?: "top" | "bottom";
menuItemsClassName?: string;
customButton?: JSX.Element;
menuItemsWhiteBg?: boolean;
};
type MenuItemProps = {
children: JSX.Element | string;
renderAs?: "button" | "a";
href?: string;
onClick?: (args?: any) => void;
className?: string;
};
const CustomMenu = ({
children,
label,
className = "",
ellipsis = false,
verticalEllipsis = false,
height = "md",
width = "auto",
textAlignment,
noBorder = false,
noChevron = false,
position = "right",
verticalPosition = "bottom",
menuItemsClassName = "",
customButton,
menuItemsWhiteBg = false,
}: Props) => (
<Menu as="div" className={`relative w-min whitespace-nowrap text-left ${className}`}>
{({ open }) => (
<>
{customButton ? (
<Menu.Button as="div">{customButton}</Menu.Button>
) : (
<div>
{ellipsis || verticalEllipsis ? (
<Menu.Button
type="button"
className="relative grid place-items-center rounded p-1 text-custom-text-200 hover:bg-custom-background-80 outline-none"
>
<Icon
iconName="more_horiz"
className={`${verticalEllipsis ? "rotate-90" : ""} text-brand-secondary`}
/>
</Menu.Button>
) : (
<Menu.Button
type="button"
className={`flex cursor-pointer items-center justify-between gap-1 px-2.5 py-1 text-xs duration-300 hover:bg-custom-background-80 ${
open ? "bg-custom-background-90 text-custom-text-100" : "text-custom-text-200"
} ${
textAlignment === "right"
? "text-right"
: textAlignment === "center"
? "text-center"
: "text-left"
} ${
noBorder
? "rounded-md"
: "rounded-md border border-custom-border-200 shadow-sm focus:outline-none"
} ${
width === "sm"
? "w-10"
: width === "md"
? "w-20"
: width === "lg"
? "w-32"
: width === "xl"
? "w-48"
: "w-full"
}`}
>
{label}
{!noChevron && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />}
</Menu.Button>
)}
</div>
)}
<Transition
as={React.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"
>
<Menu.Items
className={`absolute z-20 overflow-y-scroll whitespace-nowrap rounded-md border p-1 text-xs shadow-lg focus:outline-none ${
position === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
} ${verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"} ${
height === "sm"
? "max-h-28"
: height === "md"
? "max-h-44"
: height === "rg"
? "max-h-56"
: height === "lg"
? "max-h-80"
: ""
} ${
width === "sm"
? "w-10"
: width === "md"
? "w-20"
: width === "lg"
? "w-32"
: width === "xl"
? "w-48"
: "min-w-full"
} ${
menuItemsWhiteBg
? "border-custom-border-200 bg-custom-background-100"
: "border-custom-border-200 bg-custom-background-90"
} ${menuItemsClassName}`}
>
<div className="py-1">{children}</div>
</Menu.Items>
</Transition>
</>
)}
</Menu>
);
const MenuItem: React.FC<MenuItemProps> = ({
children,
renderAs,
href,
onClick,
className = "",
}) => (
<Menu.Item as="div">
{({ active, close }) =>
renderAs === "a" ? (
<Link href={href ?? ""}>
<a
className={`${className} ${
active ? "bg-custom-background-80" : ""
} hover:text-custom-text-200 inline-block w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80`}
onClick={close}
>
{children}
</a>
</Link>
) : (
<button
type="button"
className={`${className} ${
active ? "bg-custom-background-80" : ""
} hover:text-custom-text-200 w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80`}
onClick={onClick}
>
{children}
</button>
)
}
</Menu.Item>
);
CustomMenu.MenuItem = MenuItem;
export { CustomMenu };
@@ -4,9 +4,10 @@ import Link from "next/link";
// headless ui
import { Menu, Transition } from "@headlessui/react";
// ui
import { DropdownProps } from "components/ui";
// icons
import { DropdownProps, Icon } from "components/ui";
import { ChevronDownIcon } from "@heroicons/react/24/outline";
import { ExpandMoreOutlined, MoreHorizOutlined } from "@mui/icons-material";
export type CustomMenuProps = DropdownProps & {
children: React.ReactNode;
@@ -53,8 +54,8 @@ const CustomMenu = ({
disabled ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-80"
} ${buttonClassName}`}
>
<Icon
iconName="more_horiz"
<MoreHorizOutlined
fontSize="small"
className={`${verticalEllipsis ? "rotate-90" : ""} text-custom-text-200`}
/>
</Menu.Button>
@@ -72,7 +73,14 @@ const CustomMenu = ({
} ${buttonClassName}`}
>
{label}
{!noChevron && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />}
{!noChevron && (
<ExpandMoreOutlined
sx={{
fontSize: 14,
}}
aria-hidden="true"
/>
)}
</Menu.Button>
)}
</>
+53 -146
View File
@@ -1,33 +1,16 @@
import React, { useRef, useState } from "react";
import { useRouter } from "next/router";
import Link from "next/link";
import useSWR from "swr";
// headless ui
import { Transition } from "@headlessui/react";
// services
import workspaceService from "services/workspace.service";
// hooks
import useTheme from "hooks/use-theme";
import useUser from "hooks/use-user";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// components
import UpgradeToProModal from "./upgrade-to-pro-modal";
// ui
import { CircularProgress, Icon } from "components/ui";
// icons
import {
ArrowLongLeftIcon,
ChatBubbleOvalLeftEllipsisIcon,
ArrowUpCircleIcon,
XMarkIcon,
} from "@heroicons/react/24/outline";
import { QuestionMarkCircleIcon, DocumentIcon, DiscordIcon, GithubIcon } from "components/icons";
// fetch-keys
import { WORKSPACE_DETAILS } from "constants/fetch-keys";
import { Bolt, HelpOutlineOutlined, WestOutlined } from "@mui/icons-material";
import { ChatBubbleOvalLeftEllipsisIcon } from "@heroicons/react/24/outline";
import { DocumentIcon, DiscordIcon, GithubIcon } from "components/icons";
const helpOptions = [
{
@@ -58,150 +41,74 @@ export interface WorkspaceHelpSectionProps {
}
export const WorkspaceHelpSection: React.FC<WorkspaceHelpSectionProps> = ({ setSidebarActive }) => {
const [alert, setAlert] = useState(false);
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
const helpOptionsRef = useRef<HTMLDivElement | null>(null);
const router = useRouter();
const { workspaceSlug } = router.query;
const { collapsed: sidebarCollapse, toggleCollapsed } = useTheme();
useOutsideClickDetector(helpOptionsRef, () => setIsNeedHelpOpen(false));
const { user } = useUser();
const [upgradeModal, setUpgradeModal] = useState(false);
const { data: workspaceDetails } = useSWR(
workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null,
workspaceSlug ? () => workspaceService.getWorkspace(workspaceSlug as string) : null
);
const issueNumber = workspaceDetails?.total_issues || 0;
return (
<>
<UpgradeToProModal
isOpen={upgradeModal}
onClose={() => setUpgradeModal(false)}
user={user}
issueNumber={issueNumber}
/>
{!sidebarCollapse && (alert || issueNumber >= 750) && (
<div
className={`border-t border-custom-sidebar-border-200 p-4 ${
issueNumber >= 750
? "bg-red-500/10 text-red-600"
: issueNumber >= 500
? "bg-yellow-500/10 text-yellow-600"
: "text-green-600"
}`}
>
<div className="flex items-center gap-2">
<CircularProgress progress={(issueNumber / 1024) * 100} />
<div>Free Plan</div>
{issueNumber < 750 && (
<div
className="ml-auto text-custom-text-200 cursor-pointer"
onClick={() => setAlert(false)}
>
<XMarkIcon className="h-4 w-4" />
</div>
)}
</div>
<div className="text-custom-text-200 text-xs mt-2">
This workspace has used {issueNumber} of its 1024 issues creation limit (
{((issueNumber / 1024) * 100).toFixed(0)}
%).
</div>
</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-border-200 bg-custom-sidebar-background-100 py-2 px-4 ${
sidebarCollapse ? "flex-col" : ""
}`}
>
{alert || issueNumber >= 750 ? (
<button
type="button"
className={`flex items-center gap-2 rounded-md px-2.5 py-1.5 font-medium outline-none text-sm ${
issueNumber >= 750
? "bg-red-500/10 text-red-500"
: "bg-blue-500/10 text-custom-primary-100"
} ${sidebarCollapse ? "w-full justify-center" : ""}`}
title="Shortcuts"
onClick={() => setUpgradeModal(true)}
>
<ArrowUpCircleIcon className="h-4 w-4" />
{!sidebarCollapse && <span>Learn more</span>}
</button>
) : (
<button
type="button"
className={`flex items-center gap-2 rounded-md px-2.5 py-1.5 font-medium outline-none text-sm ${
issueNumber >= 750
? "bg-red-500/10 text-red-600"
: issueNumber >= 500
? "bg-yellow-500/10 text-yellow-600"
: "bg-green-500/10 text-green-600"
}
${sidebarCollapse ? "w-full justify-center" : ""}`}
title="Shortcuts"
onClick={() => setAlert(true)}
>
<CircularProgress
progress={(issueNumber / 1024) * 100 > 100 ? 100 : (issueNumber / 1024) * 100}
/>
{!sidebarCollapse && <span>Free Plan</span>}
</button>
{!sidebarCollapse && (
<div className="w-1/2 text-center rounded-md px-2.5 py-1.5 font-medium outline-none text-sm bg-green-500/10 text-green-500">
Free Plan
</div>
)}
<button
type="button"
className={`flex items-center gap-x-1 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 ${
sidebarCollapse ? "w-full justify-center" : ""
<div
className={`flex items-center gap-1 ${
sidebarCollapse ? "flex-col justify-center" : "justify-evenly w-1/2"
}`}
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "h",
});
document.dispatchEvent(e);
}}
title="Shortcuts"
>
<Icon iconName="bolt" />
</button>
<button
type="button"
className={`flex items-center gap-x-1 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 ${
sidebarCollapse ? "w-full justify-center" : ""
}`}
onClick={() => setIsNeedHelpOpen((prev) => !prev)}
title="Help"
>
<QuestionMarkCircleIcon className="h-4 w-4 text-custom-text-200" />
</button>
<button
type="button"
className="flex items-center gap-3 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 md:hidden"
onClick={() => setSidebarActive(false)}
>
<ArrowLongLeftIcon className="h-4 w-4 flex-shrink-0 text-custom-text-200 group-hover:text-custom-text-100" />
</button>
<button
type="button"
className={`hidden items-center gap-3 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 md:flex ${
sidebarCollapse ? "w-full justify-center" : ""
}`}
onClick={() => toggleCollapsed()}
>
<ArrowLongLeftIcon
className={`h-4 w-4 flex-shrink-0 text-custom-text-200 duration-300 group-hover:text-custom-text-100 ${
sidebarCollapse ? "rotate-180" : ""
<button
type="button"
className={`rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none ${
sidebarCollapse ? "w-full" : ""
}`}
/>
</button>
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "h",
});
document.dispatchEvent(e);
}}
>
<Bolt fontSize="small" />
</button>
<button
type="button"
className={`rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none ${
sidebarCollapse ? "w-full" : ""
}`}
onClick={() => setIsNeedHelpOpen((prev) => !prev)}
>
<HelpOutlineOutlined fontSize="small" />
</button>
<button
type="button"
className="rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none md:hidden"
onClick={() => setSidebarActive(false)}
>
<WestOutlined fontSize="small" />
</button>
<button
type="button"
className={`hidden md:flex rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none ${
sidebarCollapse ? "w-full" : ""
}`}
onClick={() => toggleCollapsed()}
>
<WestOutlined
fontSize="small"
className={`duration-300 ${sidebarCollapse ? "rotate-180" : ""}`}
/>
</button>
</div>
<div className="relative">
<Transition
@@ -43,7 +43,7 @@ const userLinks = (workspaceSlug: string) => [
export const WorkspaceSidebarDropdown = () => {
const router = useRouter();
const { workspaceSlug } = router.query;
// fetching user details
const { user, mutateUser } = useUser();
const { collapsed: sidebarCollapse } = useThemeHook();
@@ -139,8 +139,8 @@ export const WorkspaceSidebarDropdown = () => {
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
className="fixed left-2 z-20 mt-1 flex w-full max-w-[17rem] origin-top-left flex-col rounded-md
border border-custom-sidebar-border-200 bg-custom-sidebar-background-90 shadow-lg focus:outline-none"
className="fixed left-4 z-20 mt-1 flex flex-col w-full max-w-[17rem] origin-top-left rounded-md
border border-custom-sidebar-border-200 bg-custom-sidebar-background-90 shadow-lg outline-none"
>
<div className="flex flex-col items-start justify-start gap-3 p-3">
<div className="text-sm text-custom-sidebar-text-200">{user?.email}</div>
+15 -10
View File
@@ -5,40 +5,45 @@ import { useRouter } from "next/router";
// hooks
import useTheme from "hooks/use-theme";
// components
import { NotificationPopover } from "components/notifications";
// ui
import { Tooltip } from "components/ui";
// icons
import {
BarChartRounded,
GridViewOutlined,
TaskAltOutlined,
WorkOutlineOutlined,
} from "@mui/icons-material";
const workspaceLinks = (workspaceSlug: string) => [
{
icon: "grid_view",
Icon: GridViewOutlined,
name: "Dashboard",
href: `/${workspaceSlug}`,
},
{
icon: "bar_chart",
Icon: BarChartRounded,
name: "Analytics",
href: `/${workspaceSlug}/analytics`,
},
{
icon: "work",
Icon: WorkOutlineOutlined,
name: "Projects",
href: `/${workspaceSlug}/projects`,
},
{
icon: "task_alt",
Icon: TaskAltOutlined,
name: "My Issues",
href: `/${workspaceSlug}/me/my-issues`,
},
];
// components
import { Icon, Tooltip } from "components/ui";
export const WorkspaceSidebarMenu = () => {
const router = useRouter();
const { workspaceSlug } = router.query;
// theme context
const { collapsed: sidebarCollapse } = useTheme();
return (
@@ -65,7 +70,7 @@ export const WorkspaceSidebarMenu = () => {
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
} ${sidebarCollapse ? "justify-center" : ""}`}
>
<Icon iconName={`${link.icon}`} />
{<link.Icon fontSize="small" />}
{!sidebarCollapse && link.name}
</div>
</Tooltip>
@@ -1,248 +0,0 @@
import React, { useState, useEffect } from "react";
// headless ui
import { Dialog, Transition } from "@headlessui/react";
// icons
import { XCircleIcon, RocketLaunchIcon } from "@heroicons/react/24/outline";
import { CheckCircleIcon } from "@heroicons/react/24/solid";
// ui
import { CircularProgress } from "components/ui";
// types
import type { ICurrentUserResponse, IWorkspace } from "types";
declare global {
interface Window {
supabase: any;
}
}
type Props = {
isOpen: boolean;
onClose: () => void;
user: ICurrentUserResponse | undefined;
issueNumber: number;
};
const UpgradeToProModal: React.FC<Props> = ({ isOpen, onClose, user, issueNumber }) => {
const [supabaseClient, setSupabaseClient] = useState<any>(null);
useEffect(() => {
// Create a Supabase client
if (process.env.NEXT_PUBLIC_SUPABASE_URL && process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY) {
const { createClient } = window.supabase;
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
{
auth: {
autoRefreshToken: false,
persistSession: false,
},
}
);
if (supabase) {
setSupabaseClient(supabase);
}
}
}, []);
const [isLoading, setIsLoading] = useState(false);
const handleClose = () => {
onClose();
setIsLoading(false);
};
const proFeatures = [
"Everything in free",
"Unlimited users",
"Unlimited file uploads",
"Priority Support",
"Custom Theming",
"Access to Roadmap",
"Plane AI (GPT unlimited)",
];
const [errorMessage, setErrorMessage] = useState<null | { status: String; message: string }>(
null
);
const [loader, setLoader] = useState(false);
const submitEmail = async () => {
setLoader(true);
const payload = { email: user?.email || "" };
if (supabaseClient) {
if (payload?.email) {
const emailExists = await supabaseClient
.from("web-waitlist")
.select("id,email,count")
.eq("email", payload?.email);
if (emailExists.data.length === 0) {
const emailCreation = await supabaseClient
.from("web-waitlist")
.insert([{ email: payload?.email, count: 1, last_visited: new Date() }])
.select("id,email,count");
if (emailCreation.status === 201)
setErrorMessage({ status: "success", message: "Successfully registered." });
else setErrorMessage({ status: "insert_error", message: "Insertion Error." });
} else {
const emailCountUpdate = await supabaseClient
.from("web-waitlist")
.upsert({
id: emailExists.data[0]?.id,
count: emailExists.data[0]?.count + 1,
last_visited: new Date(),
})
.select("id,email,count");
if (emailCountUpdate.status === 201)
setErrorMessage({
status: "email_already_exists",
message: "Email already exists.",
});
else setErrorMessage({ status: "update_error", message: "Update Error." });
}
} else setErrorMessage({ status: "email_required", message: "Please provide email." });
} else
setErrorMessage({
status: "supabase_error",
message: "Network error. Please try again later.",
});
setLoader(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 bg-opacity-50 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-20 overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<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 border border-custom-border-200 bg-custom-background-100 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-4xl">
<div className="flex flex-wrap">
<div className="w-full md:w-3/5 p-6 flex flex-col gap-y-6">
<div className="flex gap-2">
<div
className={`font-semibold outline-none text-sm mt-1.5 ${
issueNumber >= 750
? "text-red-600"
: issueNumber >= 500
? "text-yellow-600"
: "text-green-600"
}`}
title="Shortcuts"
>
<CircularProgress
progress={
(issueNumber / 1024) * 100 > 100 ? 100 : (issueNumber / 1024) * 100
}
/>
</div>
<div className="">
<div className="font-semibold text-lg">Upgrade to pro</div>
<div className="text-custom-text-200 text-sm">
This workspace has used {issueNumber} of its 1024 issues creation limit (
{((issueNumber / 1024) * 100).toFixed(2)}%).
</div>
</div>
<div
onClick={handleClose}
className="w-5 h-5 text-custom-text-200 cursor-pointer mt-1.5 md:hidden block ml-auto"
>
<XCircleIcon />
</div>
</div>
<div className="flex gap-2 pt-6">
<div
className={`font-semibold outline-none text-sm mt-1.5 w-5 h-5 text-[#892FFF] flex-shrink-0`}
title="Shortcuts"
>
<RocketLaunchIcon />
</div>
<div className="">
<div className="font-semibold text-lg">Order summary</div>
<div className="text-custom-text-200 text-sm">
Priority support, file uploads, and access to premium features.
</div>
<div className="flex flex-wrap my-4">
{proFeatures.map((feature, index) => (
<div key={index} className="w-1/2 py-2 flex gap-2 my-1.5">
<div className="w-5 h-5 mt-0.5 text-green-600">
<CheckCircleIcon />
</div>
<div>{feature}</div>
</div>
))}
</div>
</div>
</div>
</div>
<div className="w-full md:w-2/5 bg-custom-background-90 p-6 flex flex-col">
<div className="flex justify-between items-center">
<div className="font-semibold text-lg">Summary</div>
<div
onClick={handleClose}
className="w-5 h-5 text-custom-text-200 cursor-pointer mt-1.5 hidden md:block"
>
<XCircleIcon />
</div>
</div>
<div className="text-custom-text-200 text-sm mt-4">
Plane application is currently in dev-mode. We will soon introduce Pro plans
once general availability has been established. Stay tuned for more updates.
In the meantime, Plane remains free and unrestricted.
<br /> <br />
We{"'"}ll ensure a smooth transition from the community version to the Pro
plan for you.
</div>
<button
disabled={loader}
onClick={() => submitEmail()}
type="button"
className="mt-5 md:mt-auto whitespace-nowrap max-w-min items-center gap-x-1 rounded-md px-3 py-2 font-medium outline-none text-sm bg-custom-primary-100 text-white"
>
{loader ? "Loading.." : " Join waitlist"}
</button>
{errorMessage && (
<div
className={`mt-1 text-sm ${
errorMessage && errorMessage?.status === "success"
? "text-green-500"
: " text-red-500"
}`}
>
{errorMessage?.message}
</div>
)}
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
};
export default UpgradeToProModal;
+2 -2
View File
@@ -17,12 +17,12 @@ export const CHARTS_THEME: Theme = {
background: "rgb(var(--color-background-80))",
color: "rgb(var(--color-text-200))",
fontSize: "0.8rem",
border: "1px solid rgb(var(--color-background-80))",
border: "1px solid rgb(var(--color-border-300))",
},
},
grid: {
line: {
stroke: "rgb(var(--color-background-80))",
stroke: "rgb(var(--color-border-100))",
},
},
};
+6 -1
View File
@@ -94,7 +94,12 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
const theme = localStorage.getItem("theme");
if (theme && theme === "custom") {
if (user && user.theme.palette) {
applyTheme(user.theme.palette, user.theme.darkPalette);
applyTheme(
user.theme.palette !== ",,,,"
? user.theme.palette
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
user.theme.darkPalette
);
}
}
}, [user]);
+1 -1
View File
@@ -217,7 +217,7 @@ export const render12HourFormatTime = (date: string | Date): string => {
if (hours > 12) hours -= 12;
}
return hours + ":" + minutes + " " + period;
return hours + ":" + (minutes < 10 ? `0${minutes}` : minutes) + " " + period;
};
export const render24HourFormatTime = (date: string | Date): string => {
@@ -31,11 +31,18 @@ const useUserIssueNotificationSubscription = (
const handleUnsubscribe = useCallback(() => {
if (!workspaceSlug || !projectId || !issueId) return;
mutate(
{
subscribed: false,
},
false
);
userNotificationServices
.unsubscribeFromIssueNotifications(
workspaceSlug as string,
projectId as string,
issueId as string
workspaceSlug.toString(),
projectId.toString(),
issueId.toString()
)
.then(() => {
mutate({
@@ -47,14 +54,18 @@ const useUserIssueNotificationSubscription = (
const handleSubscribe = useCallback(() => {
if (!workspaceSlug || !projectId || !issueId || !user) return;
mutate(
{
subscribed: true,
},
false
);
userNotificationServices
.subscribeToIssueNotifications(
workspaceSlug as string,
projectId as string,
issueId as string,
{
subscriber: user.id,
}
workspaceSlug.toString(),
projectId.toString(),
issueId.toString()
)
.then(() => {
mutate({
+61 -43
View File
@@ -54,44 +54,50 @@ const useUserNotification = () => {
notifications?.find((notification) => notification.id === notificationId)?.read_at !== null;
if (isRead) {
notificationsMutate(
(prev) =>
prev?.map((prevNotification) => {
if (prevNotification.id === notificationId) {
return {
...prevNotification,
read_at: null,
};
}
return prevNotification;
}),
false
);
await userNotificationServices
.markUserNotificationAsUnread(workspaceSlug.toString(), notificationId)
.then(() => {
notificationsMutate((prev) =>
prev?.map((prevNotification) => {
if (prevNotification.id === notificationId) {
return {
...prevNotification,
read_at: null,
};
}
return prevNotification;
})
);
mutateNotificationCount();
})
.catch(() => {
throw new Error("Something went wrong");
})
.finally(() => {
notificationsMutate();
mutateNotificationCount();
});
} else {
notificationsMutate(
(prev) =>
prev?.map((prevNotification) => {
if (prevNotification.id === notificationId) {
return {
...prevNotification,
read_at: new Date(),
};
}
return prevNotification;
}),
false
);
await userNotificationServices
.markUserNotificationAsRead(workspaceSlug.toString(), notificationId)
.then(() => {
notificationsMutate((prev) =>
prev?.map((prevNotification) => {
if (prevNotification.id === notificationId) {
return {
...prevNotification,
read_at: new Date(),
};
}
return prevNotification;
})
);
mutateNotificationCount();
})
.catch(() => {
throw new Error("Something went wrong");
})
.finally(() => {
notificationsMutate();
mutateNotificationCount();
});
}
};
@@ -105,22 +111,26 @@ const useUserNotification = () => {
if (isArchived) {
await userNotificationServices
.markUserNotificationAsUnarchived(workspaceSlug.toString(), notificationId)
.then(() => {
notificationsMutate();
})
.catch(() => {
throw new Error("Something went wrong");
})
.finally(() => {
notificationsMutate();
mutateNotificationCount();
});
} else {
notificationsMutate(
(prev) => prev?.filter((prevNotification) => prevNotification.id !== notificationId),
false
);
await userNotificationServices
.markUserNotificationAsArchived(workspaceSlug.toString(), notificationId)
.then(() => {
notificationsMutate((prev) =>
prev?.filter((prevNotification) => prevNotification.id !== notificationId)
);
})
.catch(() => {
throw new Error("Something went wrong");
})
.finally(() => {
notificationsMutate();
mutateNotificationCount();
});
}
};
@@ -137,19 +147,27 @@ const useUserNotification = () => {
.patchUserNotification(workspaceSlug.toString(), notificationId, {
snoozed_till: null,
})
.then(() => {
.finally(() => {
notificationsMutate();
mutateNotificationCount();
});
} else
} else {
notificationsMutate(
(prevData) => prevData?.filter((prev) => prev.id !== notificationId) || [],
false
);
await userNotificationServices
.patchUserNotification(workspaceSlug.toString(), notificationId, {
snoozed_till: dateTime,
})
.then(() => {
notificationsMutate(
(prevData) => prevData?.filter((prev) => prev.id !== notificationId) || []
);
.catch(() => {
new Error("Something went wrong");
})
.finally(() => {
notificationsMutate();
mutateNotificationCount();
});
}
};
return {
@@ -170,7 +188,7 @@ const useUserNotification = () => {
setSelectedTab,
totalNotificationCount: notificationCount
? notificationCount.created_issues +
notificationCount.watching_notifications +
notificationCount.watching_issues +
notificationCount.my_issues
: null,
notificationCount,
+2
View File
@@ -14,6 +14,8 @@
"@headlessui/react": "^1.7.3",
"@heroicons/react": "^2.0.12",
"@jitsu/nextjs": "^3.1.5",
"@mui/icons-material": "^5.14.1",
"@mui/material": "^5.14.1",
"@nivo/bar": "0.80.0",
"@nivo/calendar": "0.80.0",
"@nivo/core": "0.80.0",
+5 -3
View File
@@ -24,7 +24,9 @@ import {
} from "components/workspace";
import { TourRoot } from "components/onboarding";
// ui
import { Icon, PrimaryButton, ProductUpdatesModal } from "components/ui";
import { PrimaryButton, ProductUpdatesModal } from "components/ui";
// icons
import { BoltOutlined, GridViewOutlined } from "@mui/icons-material";
// images
import emptyDashboard from "public/empty-state/dashboard.svg";
import githubBlackImage from "/public/logos/github-black.png";
@@ -70,7 +72,7 @@ const WorkspacePage: NextPage = () => {
<WorkspaceAuthorizationLayout
left={
<div className="flex items-center gap-2 pl-3">
<Icon iconName="grid_view" />
<GridViewOutlined fontSize="small" />
Dashboard
</div>
}
@@ -80,7 +82,7 @@ const WorkspacePage: NextPage = () => {
onClick={() => setIsProductUpdatesModalOpen(true)}
className="flex items-center gap-1.5 bg-custom-background-80 text-xs font-medium py-1.5 px-3 rounded"
>
<Icon iconName="bolt" className="!text-base -my-1" />
<BoltOutlined fontSize="small" className="-my-1" />
What{"'"}s New?
</button>
<Link href="https://github.com/makeplane/plane" target="_blank" rel="noopener noreferrer">
@@ -22,7 +22,22 @@ const ProfilePreferences = () => {
useEffect(() => {
if (theme === "custom") {
if (myProfile?.theme.palette) setPreLoadedData(myProfile.theme);
if (myProfile?.theme.palette)
setPreLoadedData({
background: myProfile.theme.background !== "" ? myProfile.theme.background : "#0d101b",
text: myProfile.theme.text !== "" ? myProfile.theme.text : "#c5c5c5",
primary: myProfile.theme.primary !== "" ? myProfile.theme.primary : "#3f76ff",
sidebarBackground:
myProfile.theme.sidebarBackground !== ""
? myProfile.theme.sidebarBackground
: "#0d101b",
sidebarText: myProfile.theme.sidebarText !== "" ? myProfile.theme.sidebarText : "#c5c5c5",
darkPalette: false,
palette:
myProfile.theme.palette !== ",,,,"
? myProfile.theme.palette
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
});
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
}
}, [myProfile, theme, customThemeSelectorOptions]);
@@ -44,28 +44,18 @@ const BillingSettings: NextPage = () => {
<section className="space-y-8">
<div>
<h3 className="text-2xl font-semibold leading-6">Billing & Plans</h3>
<p className="mt-4 text-sm text-custom-text-200">[Free launch preview] plan Pro</p>
<p className="mt-4 text-sm text-custom-text-200">Free launch preview</p>
</div>
<div className="space-y-8 md:w-2/3">
<div>
<div className="w-80 rounded-md border border-custom-border-200 bg-custom-background-100 p-4 text-center">
<h4 className="text-md mb-1 leading-6">Payment due</h4>
<h2 className="text-3xl font-extrabold">--</h2>
</div>
</div>
<div>
<h4 className="text-md mb-1 leading-6">Current plan</h4>
<p className="mb-3 text-sm text-custom-text-200">
You are currently using the free plan
</p>
<a href="https://plane.so/pricing" target="_blank" rel="noreferrer">
<SecondaryButton outline>View Plans and Upgrade</SecondaryButton>
<SecondaryButton outline>View Plans</SecondaryButton>
</a>
</div>
<div>
<h4 className="text-md mb-1 leading-6">Billing history</h4>
<p className="mb-3 text-sm text-custom-text-200">There are no invoices to display</p>
</div>
</div>
</section>
</div>
@@ -207,7 +207,10 @@ const WorkspaceSettings: NextPage = () => {
{isImageUploading ? "Uploading..." : "Upload"}
</SecondaryButton>
{activeWorkspace.logo && activeWorkspace.logo !== "" && (
<DangerButton onClick={() => handleDelete(activeWorkspace.logo)}>
<DangerButton
onClick={() => handleDelete(activeWorkspace.logo)}
loading={isImageRemoving}
>
{isImageRemoving ? "Removing..." : "Remove"}
</DangerButton>
)}
+2 -2
View File
@@ -145,11 +145,11 @@ const HomePage: NextPage = () => {
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-custom-text-100">
Sign in to Plane
</h1>
<div className="flex flex-col divide-y divide-custom-border-200 sm:w-[360px] mx-auto">
<div className="flex flex-col divide-y divide-custom-border-200">
<div className="pb-7">
<EmailCodeForm handleSignIn={handleEmailCodeSignIn} />
</div>
<div className="flex flex-col items-center justify-center gap-4 pt-7 overflow-hidden">
<div className="flex flex-col items-center justify-center gap-4 pt-7 sm:w-[360px] mx-auto overflow-hidden">
<GoogleLoginButton handleSignIn={handleGoogleSignIn} />
<GithubLoginButton handleSignIn={handleGitHubSignIn} />
</div>
+82 -80
View File
@@ -105,91 +105,93 @@ const OnBoard: NextPage = () => {
{user?.email}
</div>
</div>
{invitations && invitations.length > 0 ? (
<div className="relative flex justify-center sm:justify-start sm:items-center h-full px-8 pb-8 sm:p-0 sm:pr-[8.33%] sm:w-10/12 md:w-9/12 lg:w-4/5">
<div className="w-full space-y-10">
<h5 className="text-lg">We see that someone has invited you to</h5>
<h4 className="text-2xl font-semibold">Join a workspace</h4>
<div className="md:w-3/5 space-y-4">
{invitations.map((invitation) => {
const isSelected = invitationsRespond.includes(invitation.id);
{invitations ? (
invitations.length > 0 ? (
<div className="relative flex justify-center sm:justify-start sm:items-center h-full px-8 pb-8 sm:p-0 sm:pr-[8.33%] sm:w-10/12 md:w-9/12 lg:w-4/5">
<div className="w-full space-y-10">
<h5 className="text-lg">We see that someone has invited you to</h5>
<h4 className="text-2xl font-semibold">Join a workspace</h4>
<div className="max-h-[37vh] md:w-3/5 space-y-4 overflow-y-auto">
{invitations.map((invitation) => {
const isSelected = invitationsRespond.includes(invitation.id);
return (
<div
key={invitation.id}
className={`flex cursor-pointer items-center gap-2 border py-5 px-3.5 rounded ${
isSelected
? "border-custom-primary-100"
: "border-custom-border-200 hover:bg-custom-background-80"
}`}
onClick={() =>
handleInvitation(invitation, isSelected ? "withdraw" : "accepted")
}
>
<div className="flex-shrink-0">
<div className="grid place-items-center h-9 w-9 rounded">
{invitation.workspace.logo && invitation.workspace.logo !== "" ? (
<img
src={invitation.workspace.logo}
height="100%"
width="100%"
className="rounded"
alt={invitation.workspace.name}
/>
) : (
<span className="grid place-items-center h-9 w-9 py-1.5 px-3 rounded bg-gray-700 uppercase text-white">
{invitation.workspace.name[0]}
</span>
)}
</div>
</div>
<div className="min-w-0 flex-1">
<div className="text-sm font-medium">
{truncateText(invitation.workspace.name, 30)}
</div>
<p className="text-xs text-custom-text-200">{ROLE[invitation.role]}</p>
</div>
<span
className={`flex-shrink-0 ${
isSelected ? "text-custom-primary-100" : "text-custom-text-200"
return (
<div
key={invitation.id}
className={`flex cursor-pointer items-center gap-2 border py-5 px-3.5 rounded ${
isSelected
? "border-custom-primary-100"
: "border-custom-border-200 hover:bg-custom-background-80"
}`}
onClick={() =>
handleInvitation(invitation, isSelected ? "withdraw" : "accepted")
}
>
<CheckCircleIcon className="h-5 w-5" />
</span>
</div>
);
})}
</div>
<div className="flex items-center gap-3">
<PrimaryButton
type="submit"
size="md"
onClick={submitInvitations}
disabled={isJoiningWorkspaces || invitationsRespond.length === 0}
>
Accept & Join
</PrimaryButton>
<Link href="/">
<a>
<SecondaryButton size="md" outline>
Go Home
</SecondaryButton>
</a>
</Link>
<div className="flex-shrink-0">
<div className="grid place-items-center h-9 w-9 rounded">
{invitation.workspace.logo && invitation.workspace.logo !== "" ? (
<img
src={invitation.workspace.logo}
height="100%"
width="100%"
className="rounded"
alt={invitation.workspace.name}
/>
) : (
<span className="grid place-items-center h-9 w-9 py-1.5 px-3 rounded bg-gray-700 uppercase text-white">
{invitation.workspace.name[0]}
</span>
)}
</div>
</div>
<div className="min-w-0 flex-1">
<div className="text-sm font-medium">
{truncateText(invitation.workspace.name, 30)}
</div>
<p className="text-xs text-custom-text-200">{ROLE[invitation.role]}</p>
</div>
<span
className={`flex-shrink-0 ${
isSelected ? "text-custom-primary-100" : "text-custom-text-200"
}`}
>
<CheckCircleIcon className="h-5 w-5" />
</span>
</div>
);
})}
</div>
<div className="flex items-center gap-3">
<PrimaryButton
type="submit"
size="md"
onClick={submitInvitations}
disabled={isJoiningWorkspaces || invitationsRespond.length === 0}
>
Accept & Join
</PrimaryButton>
<Link href="/">
<a>
<SecondaryButton size="md" outline>
Go Home
</SecondaryButton>
</a>
</Link>
</div>
</div>
</div>
</div>
) : (
<div className="fixed top-0 left-0 h-full w-full grid place-items-center">
<EmptyState
title="No pending invites"
description="You can see here if someone invites you to a workspace."
image={emptyInvitation}
buttonText="Back to Dashboard"
onClick={() => router.push("/")}
/>
</div>
)}
) : (
<div className="fixed top-0 left-0 h-full w-full grid place-items-center">
<EmptyState
title="No pending invites"
description="You can see here if someone invites you to a workspace."
image={emptyInvitation}
buttonText="Back to Dashboard"
onClick={() => router.push("/")}
/>
</div>
)
) : null}
</div>
</DefaultLayout>
</UserAuthorizationLayout>
@@ -1,10 +1,10 @@
<svg width="133" height="30" viewBox="0 0 133 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_372_3489)">
<path d="M35.7598 0.00235398H44.7151C46.4036 -0.0301787 48.0819 0.274777 49.6538 0.899752C51.0897 1.48694 52.3232 2.48876 53.2005 3.7804C54.1414 5.27171 54.6058 7.02009 54.5305 8.78788C54.6025 10.5948 54.1394 12.3823 53.2005 13.921C52.338 15.271 51.1056 16.3375 49.6538 16.9901C48.0968 17.6881 46.409 18.0371 44.7062 18.0131H40.3882V30.0024H35.7598V0.00235398ZM40.3882 14.1812H43.4472C44.5022 14.1924 45.5515 14.0225 46.5505 13.6787C47.4425 13.3684 48.2171 12.7851 48.7672 12.0095C49.3447 11.1092 49.6299 10.0487 49.5829 8.97633C49.6547 7.88162 49.368 6.79348 48.7672 5.88031C48.2009 5.1354 47.4202 4.58669 46.5328 4.30986C45.5354 3.98896 44.4937 3.83143 43.4472 3.84322H40.3882V14.1812Z" fill="#e9f4fc"/>
<path d="M63.6538 30.0023H58.9102V0.00234985H63.6538V30.0023Z" fill="#e9f4fc"/>
<path d="M83.1961 30.0023V26.0717C82.9541 26.623 82.6146 27.1249 82.1941 27.5524C81.5462 28.2456 80.7841 28.8194 79.942 29.2485C78.8993 29.7731 77.7457 30.0319 76.5816 30.0023C74.9896 30.0315 73.4209 29.6134 72.0497 28.7943C70.6785 27.9753 69.5585 26.7874 68.8144 25.3628C68.0116 23.8079 67.6091 22.0734 67.644 20.3194C67.6069 18.5595 68.0094 16.8186 68.8144 15.2581C69.5577 13.8315 70.677 12.6411 72.048 11.819C73.419 10.9969 74.9882 10.5752 76.5816 10.6006C77.7134 10.57 78.8375 10.7969 79.8711 11.2647C80.698 11.6453 81.4539 12.1675 82.1055 12.8082C82.5675 13.2374 82.9256 13.7687 83.1518 14.3607V11.139H87.8333V29.9844L83.1961 30.0023ZM72.299 20.3194C72.2745 21.3974 72.5412 22.4617 73.0704 23.3975C73.548 24.2312 74.2333 24.9235 75.0579 25.4053C75.8825 25.8872 76.8175 26.1417 77.7697 26.1435C78.7342 26.1735 79.6872 25.9254 80.5177 25.4281C81.3482 24.9308 82.0218 24.2047 82.4602 23.3347C82.9374 22.3947 83.1751 21.349 83.1518 20.2925C83.1763 19.233 82.9385 18.1841 82.4602 17.2413C81.9964 16.419 81.3267 15.7349 80.5183 15.2581C79.6888 14.7549 78.7367 14.4969 77.7697 14.5133C76.8353 14.5149 75.9179 14.7656 75.1097 15.2401C74.2725 15.73 73.5757 16.4321 73.0881 17.2772C72.5606 18.2015 72.288 19.2521 72.299 20.3194Z" fill="#e9f4fc"/>
<path d="M103.536 10.6096C104.757 10.611 105.964 10.8677 107.082 11.3634C108.238 11.8795 109.223 12.7205 109.92 13.7864C110.714 15.0781 111.104 16.5829 111.037 18.1029V30.0023H106.293V18.9733C106.348 18.3184 106.273 17.6591 106.074 17.0337C105.874 16.4082 105.554 15.8291 105.132 15.3299C104.739 14.9381 104.272 14.6317 103.758 14.4296C103.244 14.2274 102.695 14.1337 102.144 14.1543C101.323 14.1599 100.521 14.3994 99.8295 14.8453C99.0977 15.3167 98.4893 15.9602 98.0562 16.7209C97.5907 17.539 97.3518 18.469 97.3646 19.4131V30.0023H92.6387V11.157H97.3646V14.244C97.5688 13.6234 97.9368 13.0711 98.4286 12.6467C99.081 12.0457 99.8309 11.563 100.645 11.2198C101.557 10.8175 102.541 10.6097 103.536 10.6096Z" fill="#e9f4fc"/>
<path d="M118.973 21.4322C118.987 22.346 119.221 23.2426 119.656 24.0436C120.095 24.8189 120.753 25.4438 121.545 25.8384C122.494 26.3044 123.541 26.5293 124.595 26.4935C125.5 26.5093 126.4 26.3635 127.255 26.0628C127.932 25.8094 128.569 25.4556 129.143 25.0128C129.56 24.6821 129.93 24.2957 130.243 23.8642L132.335 26.7448C131.854 27.3909 131.273 27.9545 130.615 28.414C129.834 28.9511 128.963 29.3402 128.044 29.5626C126.808 29.8643 125.538 30.0031 124.267 29.9754C122.476 30.0226 120.705 29.599 119.124 28.746C117.69 27.949 116.513 26.7492 115.737 25.291C114.909 23.68 114.498 21.8834 114.54 20.0682C114.532 18.3948 114.924 16.7444 115.684 15.2581C116.41 13.8364 117.524 12.6558 118.894 11.857C120.412 11.0607 122.093 10.6338 123.803 10.6105C125.513 10.5871 127.205 10.9678 128.744 11.7224C130.081 12.4518 131.174 13.5669 131.883 14.9261C132.667 16.483 133.051 18.2141 133 19.9605C133 20.0771 133 20.3284 133 20.6963C133.005 20.9431 132.984 21.1897 132.938 21.4322H118.973ZM128.567 17.9503C128.53 17.3776 128.37 16.8201 128.097 16.317C127.762 15.6679 127.264 15.12 126.652 14.7287C125.854 14.2334 124.926 13.9955 123.992 14.0466C123.03 14.0014 122.076 14.2282 121.234 14.7017C120.661 15.0464 120.172 15.5175 119.804 16.08C119.435 16.6424 119.197 17.2817 119.106 17.9503H128.567Z" fill="#e9f4fc"/>
<path d="M35.7598 0.00235398H44.7151C46.4036 -0.0301787 48.0819 0.274777 49.6538 0.899752C51.0897 1.48694 52.3232 2.48876 53.2005 3.7804C54.1414 5.27171 54.6058 7.02009 54.5305 8.78788C54.6025 10.5948 54.1394 12.3823 53.2005 13.921C52.338 15.271 51.1056 16.3375 49.6538 16.9901C48.0968 17.6881 46.409 18.0371 44.7062 18.0131H40.3882V30.0024H35.7598V0.00235398ZM40.3882 14.1812H43.4472C44.5022 14.1924 45.5515 14.0225 46.5505 13.6787C47.4425 13.3684 48.2171 12.7851 48.7672 12.0095C49.3447 11.1092 49.6299 10.0487 49.5829 8.97633C49.6547 7.88162 49.368 6.79348 48.7672 5.88031C48.2009 5.1354 47.4202 4.58669 46.5328 4.30986C45.5354 3.98896 44.4937 3.83143 43.4472 3.84322H40.3882V14.1812Z" fill="#ffffff"/>
<path d="M63.6538 30.0023H58.9102V0.00234985H63.6538V30.0023Z" fill="#ffffff"/>
<path d="M83.1961 30.0023V26.0717C82.9541 26.623 82.6146 27.1249 82.1941 27.5524C81.5462 28.2456 80.7841 28.8194 79.942 29.2485C78.8993 29.7731 77.7457 30.0319 76.5816 30.0023C74.9896 30.0315 73.4209 29.6134 72.0497 28.7943C70.6785 27.9753 69.5585 26.7874 68.8144 25.3628C68.0116 23.8079 67.6091 22.0734 67.644 20.3194C67.6069 18.5595 68.0094 16.8186 68.8144 15.2581C69.5577 13.8315 70.677 12.6411 72.048 11.819C73.419 10.9969 74.9882 10.5752 76.5816 10.6006C77.7134 10.57 78.8375 10.7969 79.8711 11.2647C80.698 11.6453 81.4539 12.1675 82.1055 12.8082C82.5675 13.2374 82.9256 13.7687 83.1518 14.3607V11.139H87.8333V29.9844L83.1961 30.0023ZM72.299 20.3194C72.2745 21.3974 72.5412 22.4617 73.0704 23.3975C73.548 24.2312 74.2333 24.9235 75.0579 25.4053C75.8825 25.8872 76.8175 26.1417 77.7697 26.1435C78.7342 26.1735 79.6872 25.9254 80.5177 25.4281C81.3482 24.9308 82.0218 24.2047 82.4602 23.3347C82.9374 22.3947 83.1751 21.349 83.1518 20.2925C83.1763 19.233 82.9385 18.1841 82.4602 17.2413C81.9964 16.419 81.3267 15.7349 80.5183 15.2581C79.6888 14.7549 78.7367 14.4969 77.7697 14.5133C76.8353 14.5149 75.9179 14.7656 75.1097 15.2401C74.2725 15.73 73.5757 16.4321 73.0881 17.2772C72.5606 18.2015 72.288 19.2521 72.299 20.3194Z" fill="#ffffff"/>
<path d="M103.536 10.6096C104.757 10.611 105.964 10.8677 107.082 11.3634C108.238 11.8795 109.223 12.7205 109.92 13.7864C110.714 15.0781 111.104 16.5829 111.037 18.1029V30.0023H106.293V18.9733C106.348 18.3184 106.273 17.6591 106.074 17.0337C105.874 16.4082 105.554 15.8291 105.132 15.3299C104.739 14.9381 104.272 14.6317 103.758 14.4296C103.244 14.2274 102.695 14.1337 102.144 14.1543C101.323 14.1599 100.521 14.3994 99.8295 14.8453C99.0977 15.3167 98.4893 15.9602 98.0562 16.7209C97.5907 17.539 97.3518 18.469 97.3646 19.4131V30.0023H92.6387V11.157H97.3646V14.244C97.5688 13.6234 97.9368 13.0711 98.4286 12.6467C99.081 12.0457 99.8309 11.563 100.645 11.2198C101.557 10.8175 102.541 10.6097 103.536 10.6096Z" fill="#ffffff"/>
<path d="M118.973 21.4322C118.987 22.346 119.221 23.2426 119.656 24.0436C120.095 24.8189 120.753 25.4438 121.545 25.8384C122.494 26.3044 123.541 26.5293 124.595 26.4935C125.5 26.5093 126.4 26.3635 127.255 26.0628C127.932 25.8094 128.569 25.4556 129.143 25.0128C129.56 24.6821 129.93 24.2957 130.243 23.8642L132.335 26.7448C131.854 27.3909 131.273 27.9545 130.615 28.414C129.834 28.9511 128.963 29.3402 128.044 29.5626C126.808 29.8643 125.538 30.0031 124.267 29.9754C122.476 30.0226 120.705 29.599 119.124 28.746C117.69 27.949 116.513 26.7492 115.737 25.291C114.909 23.68 114.498 21.8834 114.54 20.0682C114.532 18.3948 114.924 16.7444 115.684 15.2581C116.41 13.8364 117.524 12.6558 118.894 11.857C120.412 11.0607 122.093 10.6338 123.803 10.6105C125.513 10.5871 127.205 10.9678 128.744 11.7224C130.081 12.4518 131.174 13.5669 131.883 14.9261C132.667 16.483 133.051 18.2141 133 19.9605C133 20.0771 133 20.3284 133 20.6963C133.005 20.9431 132.984 21.1897 132.938 21.4322H118.973ZM128.567 17.9503C128.53 17.3776 128.37 16.8201 128.097 16.317C127.762 15.6679 127.264 15.12 126.652 14.7287C125.854 14.2334 124.926 13.9955 123.992 14.0466C123.03 14.0014 122.076 14.2282 121.234 14.7017C120.661 15.0464 120.172 15.5175 119.804 16.08C119.435 16.6424 119.197 17.2817 119.106 17.9503H128.567Z" fill="#ffffff"/>
<path d="M29.127 0.271576H9.70898V10.0981H19.418V19.9246H29.127V0.271576Z" fill="#3F76FF"/>
<path d="M9.709 10.0981H0V19.9066H9.709V10.0981Z" fill="#3F76FF"/>
<path d="M19.4266 19.9246H9.73535V29.7511H19.4266V19.9246Z" fill="#3F76FF"/>

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

+3 -7
View File
@@ -111,14 +111,10 @@ class UserNotificationsServices extends APIService {
async subscribeToIssueNotifications(
workspaceSlug: string,
projectId: string,
issueId: string,
data: {
subscriber: string;
}
issueId: string
): Promise<any> {
return this.post(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-subscribers/`,
data
`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/subscribe/`
)
.then((response) => response?.data)
.catch((error) => {
@@ -159,7 +155,7 @@ class UserNotificationsServices extends APIService {
async getUnreadNotificationsCount(workspaceSlug: string): Promise<{
created_issues: number;
my_issues: number;
watching_notifications: number;
watching_issues: number;
}> {
return this.get(`/api/workspaces/${workspaceSlug}/users/notifications/unread/`)
.then((response) => response?.data)
+4
View File
@@ -295,3 +295,7 @@ body {
:-ms-input-placeholder {
color: rgb(var(--color-text-400));
}
.bp4-overlay-content {
z-index: 555 !important;
}
+15
View File
@@ -82,6 +82,21 @@ services:
- plane-db
- plane-redis
plane-beat-worker:
container_name: planebeatworker
image: makeplane/plane-worker:latest
restart: always
command: ./bin/beat
env_file:
- .env
environment:
<<: *api-and-worker-env
depends_on:
- plane-api
- plane-db
- plane-redis
plane-db:
container_name: plane-db
image: postgres:15.2-alpine
+16
View File
@@ -90,6 +90,22 @@ services:
- plane-db
- plane-redis
plane-beat-worker:
container_name: planebeatworker
build:
context: ./apiserver
dockerfile: Dockerfile.api
restart: always
command: ./bin/beat
env_file:
- .env
environment:
<<: *api-and-worker-env
depends_on:
- plane-api
- plane-db
- plane-redis
plane-db:
container_name: plane-db
image: postgres:15.2-alpine
+157
View File
@@ -899,6 +899,13 @@
dependencies:
regenerator-runtime "^0.13.11"
"@babel/runtime@^7.22.5", "@babel/runtime@^7.22.6":
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438"
integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==
dependencies:
regenerator-runtime "^0.13.11"
"@babel/template@^7.18.10", "@babel/template@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
@@ -1005,6 +1012,17 @@
"@emotion/weak-memoize" "^0.3.0"
stylis "4.1.4"
"@emotion/cache@^11.11.0":
version "11.11.0"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff"
integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==
dependencies:
"@emotion/memoize" "^0.8.1"
"@emotion/sheet" "^1.2.2"
"@emotion/utils" "^1.2.1"
"@emotion/weak-memoize" "^0.3.1"
stylis "4.2.0"
"@emotion/css@^11.10.6":
version "11.10.8"
resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.10.8.tgz#9dec9996ad9a1cc28ec8d26b1b27ab0b8f6fb053"
@@ -1028,11 +1046,23 @@
dependencies:
"@emotion/memoize" "^0.8.0"
"@emotion/is-prop-valid@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz#23116cf1ed18bfeac910ec6436561ecb1a3885cc"
integrity sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==
dependencies:
"@emotion/memoize" "^0.8.1"
"@emotion/memoize@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f"
integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==
"@emotion/memoize@^0.8.1":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17"
integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==
"@emotion/react@^11.10.6":
version "11.10.8"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.8.tgz#02e274ecb45e03ab9d7a8eb9f0f0c064613eaf7b"
@@ -1063,6 +1093,11 @@
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c"
integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==
"@emotion/sheet@^1.2.2":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec"
integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==
"@emotion/styled@^11.10.6":
version "11.10.8"
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.8.tgz#a3fd68efd90bd7e8a06b82b95adec643d386fa69"
@@ -1090,11 +1125,21 @@
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561"
integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==
"@emotion/utils@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.1.tgz#bbab58465738d31ae4cb3dbb6fc00a5991f755e4"
integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==
"@emotion/weak-memoize@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
"@emotion/weak-memoize@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6"
integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==
"@eslint-community/eslint-utils@^4.2.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
@@ -1333,11 +1378,37 @@
prop-types "^15.8.1"
react-is "^18.2.0"
"@mui/base@5.0.0-beta.8":
version "5.0.0-beta.8"
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.8.tgz#a0a9531ae9147be92d17e4f0e3b9accc57916841"
integrity sha512-b4vVjMZx5KzzEMf4arXKoeV5ZegAMOoPwoy1vfUBwhvXc2QtaaAyBp50U7OA2L06Leubc1A+lEp3eqwZoFn87g==
dependencies:
"@babel/runtime" "^7.22.6"
"@emotion/is-prop-valid" "^1.2.1"
"@mui/types" "^7.2.4"
"@mui/utils" "^5.14.1"
"@popperjs/core" "^2.11.8"
clsx "^1.2.1"
prop-types "^15.8.1"
react-is "^18.2.0"
"@mui/core-downloads-tracker@^5.12.3":
version "5.12.3"
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.12.3.tgz#3dffe62dccc065ddd7338e97d7be4b917004287e"
integrity sha512-yiJZ+knaknPHuRKhRk4L6XiwppwkAahVal3LuYpvBH7GkA2g+D9WLEXOEnNYtVFUggyKf6fWGLGnx0iqzkU5YA==
"@mui/core-downloads-tracker@^5.14.1":
version "5.14.1"
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.1.tgz#af156cb3e15b202f5c09f66e7d8b71ca86aef525"
integrity sha512-mIa1WmDmNr1LoupV1Rbxt9bTFKMbIn10RHG1bnZ/FJCkAYpuU/D4n+R+ttiycgcZNngU++zyh/OQeJblzbQPzg==
"@mui/icons-material@^5.14.1":
version "5.14.1"
resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.14.1.tgz#2f145c15047a0c7f01353ce620cb88276dadba9e"
integrity sha512-xV/f26muQqtWzerzOIdGPrXoxp/OKaE2G2Wp9gnmG47mHua5Slup/tMc3fA4ZYUreGGrK6+tT81TEvt1Wsng8Q==
dependencies:
"@babel/runtime" "^7.22.6"
"@mui/material@^5.12.1":
version "5.12.3"
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.12.3.tgz#398c1b123fb065763558bc1f9fc47d1f8cb87d0c"
@@ -1356,6 +1427,24 @@
react-is "^18.2.0"
react-transition-group "^4.4.5"
"@mui/material@^5.14.1":
version "5.14.1"
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.14.1.tgz#2711e4ca5c9bdc67b916d01faee650a7a5260bb8"
integrity sha512-WtsgYuageTunLfxH3Ri+o1RuQTFImtRHxMcVNyD0Hhd2/znjW6KODNz0XfjvLRnNCAynBxZNiflcoIBW40h9PQ==
dependencies:
"@babel/runtime" "^7.22.6"
"@mui/base" "5.0.0-beta.8"
"@mui/core-downloads-tracker" "^5.14.1"
"@mui/system" "^5.14.1"
"@mui/types" "^7.2.4"
"@mui/utils" "^5.14.1"
"@types/react-transition-group" "^4.4.6"
clsx "^1.2.1"
csstype "^3.1.2"
prop-types "^15.8.1"
react-is "^18.2.0"
react-transition-group "^4.4.5"
"@mui/private-theming@^5.12.3":
version "5.12.3"
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.12.3.tgz#f5e4704e25d9d91b906561cae573cda8f3801e10"
@@ -1365,6 +1454,15 @@
"@mui/utils" "^5.12.3"
prop-types "^15.8.1"
"@mui/private-theming@^5.13.7":
version "5.13.7"
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.13.7.tgz#2f8ef5da066f3c6c6423bd4260d003a28d10b099"
integrity sha512-qbSr+udcij5F9dKhGX7fEdx2drXchq7htLNr2Qg2Ma+WJ6q0ERlEqGSBiPiVDJkptcjeVL4DGmcf1wl5+vD4EA==
dependencies:
"@babel/runtime" "^7.22.5"
"@mui/utils" "^5.13.7"
prop-types "^15.8.1"
"@mui/styled-engine@^5.12.3":
version "5.12.3"
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.12.3.tgz#3307643d52c81947a624cdd0437536cc8109c4f0"
@@ -1375,6 +1473,16 @@
csstype "^3.1.2"
prop-types "^15.8.1"
"@mui/styled-engine@^5.13.2":
version "5.13.2"
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.13.2.tgz#c87bd61c0ab8086d34828b6defe97c02bcd642ef"
integrity sha512-VCYCU6xVtXOrIN8lcbuPmoG+u7FYuOERG++fpY74hPpEWkyFQG97F+/XfTQVYzlR2m7nPjnwVUgATcTCMEaMvw==
dependencies:
"@babel/runtime" "^7.21.0"
"@emotion/cache" "^11.11.0"
csstype "^3.1.2"
prop-types "^15.8.1"
"@mui/system@^5.12.3":
version "5.12.3"
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.12.3.tgz#306b3cdffa3046067640219c1e5dd7e3dae38ff9"
@@ -1389,6 +1497,20 @@
csstype "^3.1.2"
prop-types "^15.8.1"
"@mui/system@^5.14.1":
version "5.14.1"
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.14.1.tgz#ec8ae69f63963b5916dad4bca2f8a86a001a2392"
integrity sha512-u+xlsU34Jdkgx1CxmBnIC4Y08uPdVX5iEd3S/1dggDFtOGp+Lj8xmKRJAQ8PJOOJLOh8pDwaZx4AwXikL4l1QA==
dependencies:
"@babel/runtime" "^7.22.6"
"@mui/private-theming" "^5.13.7"
"@mui/styled-engine" "^5.13.2"
"@mui/types" "^7.2.4"
"@mui/utils" "^5.14.1"
clsx "^1.2.1"
csstype "^3.1.2"
prop-types "^15.8.1"
"@mui/types@^7.2.4":
version "7.2.4"
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328"
@@ -1405,6 +1527,17 @@
prop-types "^15.8.1"
react-is "^18.2.0"
"@mui/utils@^5.13.7", "@mui/utils@^5.14.1":
version "5.14.1"
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.14.1.tgz#29696371016552a6eb3af975bc7af429ec88b29a"
integrity sha512-39KHKK2JeqRmuUcLDLwM+c2XfVC136C5/yUyQXmO2PVbOb2Bol4KxtkssEqCbTwg87PSCG3f1Tb0keRsK7cVGw==
dependencies:
"@babel/runtime" "^7.22.6"
"@types/prop-types" "^15.7.5"
"@types/react-is" "^18.2.1"
prop-types "^15.8.1"
react-is "^18.2.0"
"@next/env@12.3.2":
version "12.3.2"
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.3.2.tgz#fb819366771f5721e9438ca3a42ad18684f0949b"
@@ -1684,6 +1817,11 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.7.tgz#ccab5c8f7dc557a52ca3288c10075c9ccd37fff7"
integrity sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==
"@popperjs/core@^2.11.8":
version "2.11.8"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@radix-ui/primitive@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.0.tgz#e1d8ef30b10ea10e69c76e896f608d9276352253"
@@ -3230,6 +3368,13 @@
dependencies:
"@types/react" "^17"
"@types/react-is@^18.2.1":
version "18.2.1"
resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-18.2.1.tgz#61d01c2a6fc089a53520c0b66996d458fdc46863"
integrity sha512-wyUkmaaSZEzFZivD8F2ftSyAfk6L+DfFliVj/mYdOXbVjRcS87fQJLTnhk6dRZPuJjI+9g6RZJO4PNCngUrmyw==
dependencies:
"@types/react" "*"
"@types/react-redux@^7.1.20":
version "7.1.25"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.25.tgz#de841631205b24f9dfb4967dd4a7901e048f9a88"
@@ -3247,6 +3392,13 @@
dependencies:
"@types/react" "*"
"@types/react-transition-group@^4.4.6":
version "4.4.6"
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.6.tgz#18187bcda5281f8e10dfc48f0943e2fdf4f75e2e"
integrity sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^18.0.17":
version "18.2.3"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.3.tgz#509ad6c4c77378e686f9bb6e0f8756936392f0e8"
@@ -7947,6 +8099,11 @@ stylis@4.1.4:
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.4.tgz#9cb60e7153d8ac6d02d773552bf51c7a0344535b"
integrity sha512-USf5pszRYwuE6hg9by0OkKChkQYEXfkeTtm0xKw+jqQhwyjCVLdYyMBK7R+n7dhzsblAWJnGxju4vxq5eH20GQ==
stylis@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51"
integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==
sucrase@^3.32.0:
version "3.32.0"
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.32.0.tgz#c4a95e0f1e18b6847127258a75cf360bc568d4a7"