Merge branch 'develop' of github.com:makeplane/plane into develop

This commit is contained in:
NarayanBavisetti 2023-07-19 14:49:06 +05:30
commit 5d320ae33c
333 changed files with 10270 additions and 3908 deletions

View File

@ -462,9 +462,9 @@ class IssueAttachmentSerializer(BaseSerializer):
# Issue Serializer with state details
class IssueStateSerializer(BaseSerializer):
state_detail = StateSerializer(read_only=True, source="state")
project_detail = ProjectSerializer(read_only=True, source="project")
label_details = LabelSerializer(read_only=True, source="labels", many=True)
label_details = LabelLiteSerializer(read_only=True, source="labels", many=True)
state_detail = StateLiteSerializer(read_only=True, source="state")
project_detail = ProjectLiteSerializer(read_only=True, source="project")
assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True)
sub_issues_count = serializers.IntegerField(read_only=True)
bridge_id = serializers.UUIDField(read_only=True)
@ -477,7 +477,7 @@ class IssueStateSerializer(BaseSerializer):
class IssueSerializer(BaseSerializer):
project_detail = ProjectSerializer(read_only=True, source="project")
project_detail = ProjectLiteSerializer(read_only=True, source="project")
state_detail = StateSerializer(read_only=True, source="state")
parent_detail = IssueFlatSerializer(read_only=True, source="parent")
label_details = LabelSerializer(read_only=True, source="labels", many=True)

View File

@ -106,7 +106,7 @@ class ModuleFlatSerializer(BaseSerializer):
class ModuleIssueSerializer(BaseSerializer):
module_detail = ModuleFlatSerializer(read_only=True, source="module")
issue_detail = IssueStateSerializer(read_only=True, source="issue")
issue_detail = ProjectLiteSerializer(read_only=True, source="issue")
sub_issues_count = serializers.IntegerField(read_only=True)
class Meta:
@ -151,7 +151,7 @@ class ModuleLinkSerializer(BaseSerializer):
class ModuleSerializer(BaseSerializer):
project_detail = ProjectSerializer(read_only=True, source="project")
project_detail = ProjectLiteSerializer(read_only=True, source="project")
lead_detail = UserLiteSerializer(read_only=True, source="lead")
members_detail = UserLiteSerializer(read_only=True, many=True, source="members")
link_module = ModuleLinkSerializer(read_only=True, many=True)

View File

@ -1,8 +1,10 @@
# Module imports
from .base import BaseSerializer
from .user import UserLiteSerializer
from plane.db.models import Notification
class NotificationSerializer(BaseSerializer):
triggered_by_details = UserLiteSerializer(read_only=True, source="triggered_by")
class Meta:
model = Notification

View File

@ -110,8 +110,8 @@ class ProjectMemberSerializer(BaseSerializer):
class ProjectMemberInviteSerializer(BaseSerializer):
project = ProjectSerializer(read_only=True)
workspace = WorkSpaceSerializer(read_only=True)
project = ProjectLiteSerializer(read_only=True)
workspace = WorkspaceLiteSerializer(read_only=True)
class Meta:
model = ProjectMemberInvite
@ -125,7 +125,7 @@ class ProjectIdentifierSerializer(BaseSerializer):
class ProjectFavoriteSerializer(BaseSerializer):
project_detail = ProjectSerializer(source="project", read_only=True)
project_detail = ProjectLiteSerializer(source="project", read_only=True)
class Meta:
model = ProjectFavorite
@ -136,11 +136,6 @@ class ProjectFavoriteSerializer(BaseSerializer):
]
class ProjectLiteSerializer(BaseSerializer):
class Meta:
model = Project
fields = ["id", "identifier", "name"]
read_only_fields = fields
class ProjectMemberLiteSerializer(BaseSerializer):

View File

@ -22,6 +22,7 @@ from plane.api.views import (
# User
UserEndpoint,
UpdateUserOnBoardedEndpoint,
UpdateUserTourCompletedEndpoint,
UserActivityEndpoint,
## End User
# Workspaces
@ -152,6 +153,7 @@ from plane.api.views import (
## End Analytics
# Notification
NotificationViewSet,
UnreadNotificationEndpoint,
## End Notification
)
@ -202,7 +204,12 @@ urlpatterns = [
path(
"users/me/onboard/",
UpdateUserOnBoardedEndpoint.as_view(),
name="change-password",
name="user-onboard",
),
path(
"users/me/tour-completed/",
UpdateUserTourCompletedEndpoint.as_view(),
name="user-tour",
),
path("users/activities/", UserActivityEndpoint.as_view(), name="user-activities"),
# user workspaces
@ -472,7 +479,6 @@ urlpatterns = [
"workspaces/<str:slug>/user-favorite-projects/",
ProjectFavoritesViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
@ -1377,5 +1383,10 @@ urlpatterns = [
),
name="notifications",
),
path(
"workspaces/<str:slug>/users/notifications/unread/",
UnreadNotificationEndpoint.as_view(),
name="unread-notifications",
),
## End Notification
]

View File

@ -16,6 +16,7 @@ from .project import (
from .people import (
UserEndpoint,
UpdateUserOnBoardedEndpoint,
UpdateUserTourCompletedEndpoint,
UserActivityEndpoint,
)
@ -144,4 +145,4 @@ from .analytic import (
DefaultAnalyticsEndpoint,
)
from .notification import NotificationViewSet
from .notification import NotificationViewSet, UnreadNotificationEndpoint

View File

@ -345,7 +345,7 @@ class MagicSignInEndpoint(BaseAPIView):
def post(self, request):
try:
user_token = request.data.get("token", "").strip().lower()
user_token = request.data.get("token", "").strip()
key = request.data.get("key", False)
if not key or user_token == "":

View File

@ -706,9 +706,6 @@ class CycleDateCheckEndpoint(BaseAPIView):
class CycleFavoriteViewSet(BaseViewSet):
permission_classes = [
ProjectEntityPermission,
]
serializer_class = CycleFavoriteSerializer
model = CycleFavorite

View File

@ -30,31 +30,6 @@ class GPTIntegrationEndpoint(BaseAPIView):
status=status.HTTP_400_BAD_REQUEST,
)
count = 0
# If logger is enabled check for request limit
if settings.LOGGER_BASE_URL:
try:
headers = {
"Content-Type": "application/json",
}
response = requests.post(
settings.LOGGER_BASE_URL,
json={"user_id": str(request.user.id)},
headers=headers,
)
count = response.json().get("count", 0)
if not response.json().get("success", False):
return Response(
{
"error": "You have surpassed the monthly limit for AI assistance"
},
status=status.HTTP_429_TOO_MANY_REQUESTS,
)
except Exception as e:
capture_exception(e)
prompt = request.data.get("prompt", False)
task = request.data.get("task", False)
@ -82,7 +57,6 @@ class GPTIntegrationEndpoint(BaseAPIView):
{
"response": text,
"response_html": text_html,
"count": count,
"project_detail": ProjectLiteSerializer(project).data,
"workspace_detail": WorkspaceLiteSerializer(workspace).data,
},

View File

@ -45,7 +45,6 @@ from plane.api.serializers import (
IssueLiteSerializer,
IssueAttachmentSerializer,
IssueSubscriberSerializer,
ProjectMemberSerializer,
ProjectMemberLiteSerializer,
)
from plane.api.permissions import (
@ -169,8 +168,8 @@ class IssueViewSet(BaseViewSet):
issue_queryset = (
self.get_queryset()
.filter(**filters)
.annotate(cycle_id=F("issue_cycle__id"))
.annotate(module_id=F("issue_module__id"))
.annotate(cycle_id=F("issue_cycle__cycle_id"))
.annotate(module_id=F("issue_module__module_id"))
.annotate(
link_count=IssueLink.objects.filter(issue=OuterRef("id"))
.order_by()
@ -955,8 +954,8 @@ class IssueArchiveViewSet(BaseViewSet):
issue_queryset = (
self.get_queryset()
.filter(**filters)
.annotate(cycle_id=F("issue_cycle__id"))
.annotate(module_id=F("issue_module__id"))
.annotate(cycle_id=F("issue_cycle__cycle_id"))
.annotate(module_id=F("issue_module__module_id"))
.annotate(
link_count=IssueLink.objects.filter(issue=OuterRef("id"))
.order_by()

View File

@ -480,9 +480,6 @@ class ModuleLinkViewSet(BaseViewSet):
class ModuleFavoriteViewSet(BaseViewSet):
permission_classes = [
ProjectEntityPermission,
]
serializer_class = ModuleFavoriteSerializer
model = ModuleFavorite

View File

@ -8,7 +8,7 @@ from rest_framework.response import Response
from sentry_sdk import capture_exception
# Module imports
from .base import BaseViewSet
from .base import BaseViewSet, BaseAPIView
from plane.db.models import Notification, IssueAssignee, IssueSubscriber, Issue
from plane.api.serializers import NotificationSerializer
@ -25,7 +25,7 @@ class NotificationViewSet(BaseViewSet):
workspace__slug=self.kwargs.get("slug"),
receiver_id=self.request.user.id,
)
.select_related("workspace")
.select_related("workspace", "project," "triggered_by", "receiver")
)
def list(self, request, slug):
@ -33,7 +33,7 @@ class NotificationViewSet(BaseViewSet):
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", "false")
read = request.GET.get("read", "true")
# Filter type
type = request.GET.get("type", "all")
@ -53,8 +53,6 @@ class NotificationViewSet(BaseViewSet):
Q(snoozed_till__lt=timezone.now()) | Q(snoozed_till__isnull=False)
)
if read == "true":
notifications = notifications.filter(read_at__isnull=False)
if read == "false":
notifications = notifications.filter(read_at__isnull=True)
@ -123,7 +121,7 @@ class NotificationViewSet(BaseViewSet):
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
def mark_read(self, request, slug, pk):
try:
notification = Notification.objects.get(
@ -166,7 +164,6 @@ class NotificationViewSet(BaseViewSet):
status=status.HTTP_400_BAD_REQUEST,
)
def archive(self, request, slug, pk):
try:
notification = Notification.objects.get(
@ -209,3 +206,51 @@ class NotificationViewSet(BaseViewSet):
status=status.HTTP_400_BAD_REQUEST,
)
class UnreadNotificationEndpoint(BaseAPIView):
def get(self, request, slug):
try:
# Watching Issues Count
watching_issues_count = Notification.objects.filter(
workspace__slug=slug,
receiver_id=request.user.id,
read_at__isnull=True,
entity_identifier__in=IssueSubscriber.objects.filter(
workspace__slug=slug, subscriber_id=request.user.id
).values_list("issue_id", flat=True),
).count()
# My Issues Count
my_issues_count = Notification.objects.filter(
workspace__slug=slug,
receiver_id=request.user.id,
read_at__isnull=True,
entity_identifier__in=IssueAssignee.objects.filter(
workspace__slug=slug, assignee_id=request.user.id
).values_list("issue_id", flat=True),
).count()
# Created Issues Count
created_issues_count = Notification.objects.filter(
workspace__slug=slug,
receiver_id=request.user.id,
read_at__isnull=True,
entity_identifier__in=Issue.objects.filter(
workspace__slug=slug, created_by=request.user
).values_list("pk", flat=True),
).count()
return Response(
{
"watching_issues": watching_issues_count,
"my_issues": my_issues_count,
"created_issues": created_issues_count,
},
status=status.HTTP_200_OK,
)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)

View File

@ -37,7 +37,9 @@ class UserEndpoint(BaseViewSet):
workspace_invites = WorkspaceMemberInvite.objects.filter(
email=request.user.email
).count()
assigned_issues = Issue.issue_objects.filter(assignees__in=[request.user]).count()
assigned_issues = Issue.issue_objects.filter(
assignees__in=[request.user]
).count()
serialized_data = UserSerializer(request.user).data
serialized_data["workspace"] = {
@ -47,7 +49,9 @@ class UserEndpoint(BaseViewSet):
"fallback_workspace_slug": workspace.slug,
"invites": workspace_invites,
}
serialized_data.setdefault("issues", {})["assigned_issues"] = assigned_issues
serialized_data.setdefault("issues", {})[
"assigned_issues"
] = assigned_issues
return Response(
serialized_data,
@ -59,11 +63,15 @@ class UserEndpoint(BaseViewSet):
workspace_invites = WorkspaceMemberInvite.objects.filter(
email=request.user.email
).count()
assigned_issues = Issue.issue_objects.filter(assignees__in=[request.user]).count()
assigned_issues = Issue.issue_objects.filter(
assignees__in=[request.user]
).count()
fallback_workspace = Workspace.objects.filter(
workspace_member__member=request.user
).order_by("created_at").first()
fallback_workspace = (
Workspace.objects.filter(workspace_member__member=request.user)
.order_by("created_at")
.first()
)
serialized_data = UserSerializer(request.user).data
@ -78,7 +86,9 @@ class UserEndpoint(BaseViewSet):
else None,
"invites": workspace_invites,
}
serialized_data.setdefault("issues", {})["assigned_issues"] = assigned_issues
serialized_data.setdefault("issues", {})[
"assigned_issues"
] = assigned_issues
return Response(
serialized_data,
@ -109,6 +119,23 @@ class UpdateUserOnBoardedEndpoint(BaseAPIView):
)
class UpdateUserTourCompletedEndpoint(BaseAPIView):
def patch(self, request):
try:
user = User.objects.get(pk=request.user.id)
user.is_tour_completed = request.data.get("is_tour_completed", False)
user.save()
return Response(
{"message": "Updated successfully"}, status=status.HTTP_200_OK
)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
class UserActivityEndpoint(BaseAPIView, BasePaginator):
def get(self, request):
try:

View File

@ -96,6 +96,7 @@ class ProjectViewSet(BaseViewSet):
def list(self, request, slug):
try:
is_favorite = request.GET.get("is_favorite", "all")
subquery = ProjectFavorite.objects.filter(
user=self.request.user,
project_id=OuterRef("pk"),
@ -126,6 +127,12 @@ class ProjectViewSet(BaseViewSet):
.values("count")
)
)
if is_favorite == "true":
projects = projects.filter(is_favorite=True)
if is_favorite == "false":
projects = projects.filter(is_favorite=False)
return Response(ProjectDetailSerializer(projects, many=True).data)
except Exception as e:
capture_exception(e)
@ -153,32 +160,32 @@ class ProjectViewSet(BaseViewSet):
states = [
{
"name": "Backlog",
"color": "#5e6ad2",
"color": "#A3A3A3",
"sequence": 15000,
"group": "backlog",
"default": True,
},
{
"name": "Todo",
"color": "#eb5757",
"color": "#3A3A3A",
"sequence": 25000,
"group": "unstarted",
},
{
"name": "In Progress",
"color": "#26b5ce",
"color": "#F59E0B",
"sequence": 35000,
"group": "started",
},
{
"name": "Done",
"color": "#f2c94c",
"color": "#16A34A",
"sequence": 45000,
"group": "completed",
},
{
"name": "Cancelled",
"color": "#4cb782",
"color": "#EF4444",
"sequence": 55000,
"group": "cancelled",
},

View File

@ -101,6 +101,7 @@ class WorkSpaceViewSet(BaseViewSet):
.filter(workspace_member__member=self.request.user)
.annotate(total_members=member_count)
.annotate(total_issues=issue_count)
.select_related("owner")
)
def create(self, request):

View File

@ -570,7 +570,7 @@ def track_archive_at(
comment=f"{actor.email} has restored the issue",
verb="updated",
actor=actor,
field="archvied_at",
field="archived_at",
old_value="archive",
new_value="restore",
)
@ -584,7 +584,7 @@ def track_archive_at(
comment=f"Plane has archived the issue",
verb="updated",
actor=actor,
field="archvied_at",
field="archived_at",
old_value=None,
new_value="archive",
)
@ -1028,10 +1028,12 @@ def issue_activity(
actor = User.objects.get(pk=actor_id)
project = Project.objects.get(pk=project_id)
issue = Issue.objects.filter(pk=issue_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()
issue.save(update_fields=["updated_at"])
if subscriber:
# add the user to issue subscriber
@ -1109,10 +1111,12 @@ def issue_activity(
issue_subscribers = issue_subscribers + issue_assignees
if issue.created_by_id:
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]
issue = Issue.objects.get(project=project, pk=issue_id)
for subscriber in issue_subscribers:
for issue_activity in issue_activities_created:
bulk_notifications.append(
@ -1134,7 +1138,17 @@ def issue_activity(
"state_name": issue.state.name,
"state_group": issue.state.group,
},
"issue_activity": str(issue_activity.id),
"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 ""
),
},
},
)
)

View File

@ -12,7 +12,7 @@ from celery import shared_task
from sentry_sdk import capture_exception
# Module imports
from plane.db.models import Issue, Project, IssueActivity, State
from plane.db.models import Issue, Project, State
from plane.bgtasks.issue_activites_task import issue_activity
@ -49,6 +49,11 @@ def archive_old_issues():
Q(issue_module__module__target_date__lt=timezone.now().date())
& Q(issue_module__isnull=False)
),
).filter(
Q(issue_inbox__status=1)
| Q(issue_inbox__status=-1)
| Q(issue_inbox__status=2)
| Q(issue_inbox__isnull=True)
)
# Check if Issues
@ -65,7 +70,7 @@ def archive_old_issues():
[
issue_activity.delay(
type="issue.activity.updated",
requested_data=json.dumps({"archive_at": issue.archived_at}),
requested_data=json.dumps({"archived_at": issue.archived_at}),
actor_id=str(project.created_by_id),
issue_id=issue.id,
project_id=project_id,
@ -111,14 +116,19 @@ def close_old_issues():
Q(issue_module__module__target_date__lt=timezone.now().date())
& Q(issue_module__isnull=False)
),
).filter(
Q(issue_inbox__status=1)
| Q(issue_inbox__status=-1)
| Q(issue_inbox__status=2)
| Q(issue_inbox__isnull=True)
)
# Check if Issues
if issues:
if project.default_state is None:
close_state = project.default_state
else:
close_state = State.objects.filter(group="cancelled").first()
else:
close_state = project.default_state
issues_to_update = []
for issue in issues:

View File

@ -0,0 +1,264 @@
# Generated by Django 4.2.3 on 2023-07-19 06:52
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import plane.db.models.user
import uuid
def onboarding_default_steps(apps, schema_editor):
default_onboarding_schema = {
"workspace_join": True,
"profile_complete": True,
"workspace_create": True,
"workspace_invite": True,
}
Model = apps.get_model("db", "User")
updated_user = []
for obj in Model.objects.filter(is_onboarded=True):
obj.onboarding_step = default_onboarding_schema
obj.is_tour_completed = True
updated_user.append(obj)
Model.objects.bulk_update(updated_user, ["onboarding_step"], batch_size=100)
class Migration(migrations.Migration):
dependencies = [
("db", "0036_alter_workspace_organization_size"),
]
operations = [
migrations.AddField(
model_name="issue",
name="archived_at",
field=models.DateField(null=True),
),
migrations.AddField(
model_name="project",
name="archive_in",
field=models.IntegerField(
default=0,
validators=[
django.core.validators.MinValueValidator(0),
django.core.validators.MaxValueValidator(12),
],
),
),
migrations.AddField(
model_name="project",
name="close_in",
field=models.IntegerField(
default=0,
validators=[
django.core.validators.MinValueValidator(0),
django.core.validators.MaxValueValidator(12),
],
),
),
migrations.AddField(
model_name="project",
name="default_state",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="default_state",
to="db.state",
),
),
migrations.AddField(
model_name="user",
name="is_tour_completed",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="user",
name="onboarding_step",
field=models.JSONField(default=plane.db.models.user.get_default_onboarding),
),
migrations.CreateModel(
name="Notification",
fields=[
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created At"),
),
(
"updated_at",
models.DateTimeField(
auto_now=True, verbose_name="Last Modified At"
),
),
(
"id",
models.UUIDField(
db_index=True,
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
unique=True,
),
),
("data", models.JSONField(null=True)),
("entity_identifier", models.UUIDField(null=True)),
("entity_name", models.CharField(max_length=255)),
("title", models.TextField()),
("message", models.JSONField(null=True)),
("message_html", models.TextField(blank=True, default="<p></p>")),
("message_stripped", models.TextField(blank=True, null=True)),
("sender", models.CharField(max_length=255)),
("read_at", models.DateTimeField(null=True)),
("snoozed_till", models.DateTimeField(null=True)),
("archived_at", models.DateTimeField(null=True)),
(
"created_by",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="%(class)s_created_by",
to=settings.AUTH_USER_MODEL,
verbose_name="Created By",
),
),
(
"project",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="notifications",
to="db.project",
),
),
(
"receiver",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="received_notifications",
to=settings.AUTH_USER_MODEL,
),
),
(
"triggered_by",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="triggered_notifications",
to=settings.AUTH_USER_MODEL,
),
),
(
"updated_by",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="%(class)s_updated_by",
to=settings.AUTH_USER_MODEL,
verbose_name="Last Modified By",
),
),
(
"workspace",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="notifications",
to="db.workspace",
),
),
],
options={
"verbose_name": "Notification",
"verbose_name_plural": "Notifications",
"db_table": "notifications",
"ordering": ("-created_at",),
},
),
migrations.CreateModel(
name="IssueSubscriber",
fields=[
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created At"),
),
(
"updated_at",
models.DateTimeField(
auto_now=True, verbose_name="Last Modified At"
),
),
(
"id",
models.UUIDField(
db_index=True,
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
unique=True,
),
),
(
"created_by",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="%(class)s_created_by",
to=settings.AUTH_USER_MODEL,
verbose_name="Created By",
),
),
(
"issue",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="issue_subscribers",
to="db.issue",
),
),
(
"project",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="project_%(class)s",
to="db.project",
),
),
(
"subscriber",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="issue_subscribers",
to=settings.AUTH_USER_MODEL,
),
),
(
"updated_by",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="%(class)s_updated_by",
to=settings.AUTH_USER_MODEL,
verbose_name="Last Modified By",
),
),
(
"workspace",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="workspace_%(class)s",
to="db.workspace",
),
),
],
options={
"verbose_name": "Issue Subscriber",
"verbose_name_plural": "Issue Subscribers",
"db_table": "issue_subscribers",
"ordering": ("-created_at",),
"unique_together": {("issue", "subscriber")},
},
),
]

View File

@ -28,7 +28,6 @@ class IssueManager(models.Manager):
| models.Q(issue_inbox__status=2)
| models.Q(issue_inbox__isnull=True)
)
.filter(archived_at__isnull=True)
.exclude(archived_at__isnull=False)
)

View File

@ -18,6 +18,13 @@ from sentry_sdk import capture_exception
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
def get_default_onboarding():
return {
"profile_complete": False,
"workspace_create": False,
"workspace_invite": False,
"workspace_join": False,
}
class User(AbstractBaseUser, PermissionsMixin):
id = models.UUIDField(
@ -73,6 +80,8 @@ class User(AbstractBaseUser, PermissionsMixin):
role = models.CharField(max_length=300, null=True, blank=True)
is_bot = models.BooleanField(default=False)
theme = models.JSONField(default=dict)
is_tour_completed = models.BooleanField(default=False)
onboarding_step = models.JSONField(default=get_default_onboarding)
USERNAME_FIELD = "email"

View File

@ -3,7 +3,7 @@
Django==4.2.3
django-braces==1.15.0
django-taggit==4.0.0
psycopg2==2.9.6
psycopg==3.1.9
django-oauth-toolkit==2.3.0
mistune==3.0.1
djangorestframework==3.14.0

View File

@ -32,6 +32,7 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
setError,
setValue,
getValues,
watch,
formState: { errors, isSubmitting, isValid, isDirty },
} = useForm<EmailCodeFormValues>({
defaultValues: {
@ -112,43 +113,35 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
return (
<>
<form className="space-y-5 py-5 px-5">
{(codeSent || codeResent) && (
<div className="rounded-md bg-green-500/20 p-4">
<div className="flex">
<div className="flex-shrink-0">
<CheckCircleIcon className="h-5 w-5 text-green-500" aria-hidden="true" />
</div>
<div className="ml-3">
<p className="text-sm font-medium text-green-500">
{codeResent
? "Please check your mail for new code."
: "Please check your mail for code."}
</p>
</div>
</div>
</div>
)}
<div>
{(codeSent || codeResent) && (
<p className="text-center mt-4">
We have sent the sign in code.
<br />
Please check your inbox at <span className="font-medium">{watch("email")}</span>
</p>
)}
<form className="space-y-4 mt-10">
<div className="space-y-1">
<Input
id="email"
type="email"
name="email"
register={register}
validations={{
required: "Email ID is required",
required: "Email address is required",
validate: (value) =>
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
value
) || "Email ID is not valid",
) || "Email address is not valid",
}}
error={errors.email}
placeholder="Enter your Email ID"
placeholder="Enter your email address..."
className="border-custom-border-300 h-[46px]"
/>
</div>
{codeSent && (
<div>
<>
<Input
id="token"
type="token"
@ -158,14 +151,15 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
required: "Code is required",
}}
error={errors.token}
placeholder="Enter code"
placeholder="Enter code..."
className="border-custom-border-300 h-[46px]"
/>
<button
type="button"
className={`mt-5 flex w-full justify-end text-xs outline-none ${
className={`flex w-full justify-end text-xs outline-none ${
isResendDisabled
? "cursor-default text-custom-text-200"
: "cursor-pointer text-custom-primary"
: "cursor-pointer text-custom-primary-100"
} `}
onClick={() => {
setIsCodeResending(true);
@ -178,46 +172,43 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
disabled={isResendDisabled}
>
{resendCodeTimer > 0 ? (
<p className="text-right">
Didn{"'"}t receive code? Get new code in {resendCodeTimer} seconds.
</p>
<span className="text-right">Request new code in {resendCodeTimer} seconds</span>
) : isCodeResending ? (
"Sending code..."
"Sending new code..."
) : errorResendingCode ? (
"Please try again later"
) : (
"Resend code"
<span className="font-medium">Resend code</span>
)}
</button>
</div>
</>
)}
{codeSent ? (
<PrimaryButton
type="submit"
className="w-full text-center h-[46px]"
size="md"
onClick={handleSubmit(handleSignin)}
disabled={!isValid && isDirty}
loading={isLoading}
>
{isLoading ? "Signing in..." : "Sign in"}
</PrimaryButton>
) : (
<PrimaryButton
className="w-full text-center h-[46px]"
size="md"
onClick={() => {
handleSubmit(onSubmit)().then(() => {
setResendCodeTimer(30);
});
}}
disabled={!isValid && isDirty}
loading={isSubmitting}
>
{isSubmitting ? "Sending code..." : "Send sign in code"}
</PrimaryButton>
)}
<div>
{codeSent ? (
<PrimaryButton
type="submit"
className="w-full text-center"
size="md"
onClick={handleSubmit(handleSignin)}
disabled={!isValid && isDirty}
loading={isLoading}
>
{isLoading ? "Signing in..." : "Sign in"}
</PrimaryButton>
) : (
<PrimaryButton
className="w-full text-center"
size="md"
onClick={() => {
handleSubmit(onSubmit)().then(() => {
setResendCodeTimer(30);
});
}}
loading={isSubmitting || (!isValid && isDirty)}
>
{isSubmitting ? "Sending code..." : "Send code"}
</PrimaryButton>
)}
</div>
</form>
</>
);

View File

@ -8,7 +8,7 @@ import { useForm } from "react-hook-form";
// components
import { EmailResetPasswordForm } from "components/account";
// ui
import { Input, SecondaryButton } from "components/ui";
import { Input, PrimaryButton } from "components/ui";
// types
type EmailPasswordFormValues = {
email: string;
@ -42,28 +42,39 @@ export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
return (
<>
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-custom-text-100">
{isResettingPassword
? "Reset your password"
: isSignUpPage
? "Sign up on Plane"
: "Sign in to Plane"}
</h1>
{isResettingPassword ? (
<EmailResetPasswordForm setIsResettingPassword={setIsResettingPassword} />
) : (
<form className="mt-5 py-5 px-5" onSubmit={handleSubmit(onSubmit)}>
<div>
<form
className="space-y-4 mt-10 w-full sm:w-[360px] mx-auto"
onSubmit={handleSubmit(onSubmit)}
>
<div className="space-y-1">
<Input
id="email"
type="email"
name="email"
register={register}
validations={{
required: "Email ID is required",
required: "Email address is required",
validate: (value) =>
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
value
) || "Email ID is not valid",
) || "Email address is not valid",
}}
error={errors.email}
placeholder="Enter your email ID"
placeholder="Enter your email address..."
className="border-custom-border-300 h-[46px]"
/>
</div>
<div className="mt-5">
<div className="space-y-1">
<Input
id="password"
type="password"
@ -73,46 +84,45 @@ export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
required: "Password is required",
}}
error={errors.password}
placeholder="Enter your password"
placeholder="Enter your password..."
className="border-custom-border-300 h-[46px]"
/>
</div>
<div className="mt-2 flex items-center justify-between">
<div className="ml-auto text-sm">
{isSignUpPage ? (
<Link href="/">
<a className="font-medium text-custom-primary hover:text-custom-primary">
Already have an account? Sign in.
</a>
</Link>
) : (
<button
type="button"
onClick={() => setIsResettingPassword(true)}
className="font-medium text-custom-primary hover:text-custom-primary"
>
Forgot your password?
</button>
)}
</div>
<div className="text-right text-xs">
{isSignUpPage ? (
<Link href="/">
<a className="text-custom-text-200 hover:text-custom-primary-100">
Already have an account? Sign in.
</a>
</Link>
) : (
<button
type="button"
onClick={() => setIsResettingPassword(true)}
className="text-custom-text-200 hover:text-custom-primary-100"
>
Forgot your password?
</button>
)}
</div>
<div className="mt-5">
<SecondaryButton
<div>
<PrimaryButton
type="submit"
className="w-full text-center"
className="w-full text-center h-[46px]"
disabled={!isValid && isDirty}
loading={isSubmitting}
>
{isSignUpPage
? isSubmitting
? "Signing up..."
: "Sign Up"
: "Sign up"
: isSubmitting
? "Signing in..."
: "Sign In"}
</SecondaryButton>
: "Sign in"}
</PrimaryButton>
{!isSignUpPage && (
<Link href="/sign-up">
<a className="block font-medium text-custom-primary hover:text-custom-primary text-sm mt-1">
<a className="block text-custom-text-200 hover:text-custom-primary-100 text-xs mt-4">
Don{"'"}t have an account? Sign up.
</a>
</Link>

View File

@ -59,32 +59,36 @@ export const EmailResetPasswordForm: React.FC<Props> = ({ setIsResettingPassword
};
return (
<form className="mt-5 py-5 px-5" onSubmit={handleSubmit(forgotPassword)}>
<div>
<form
className="space-y-4 mt-10 w-full sm:w-[360px] mx-auto"
onSubmit={handleSubmit(forgotPassword)}
>
<div className="space-y-1">
<Input
id="email"
type="email"
name="email"
register={register}
validations={{
required: "Email ID is required",
required: "Email address is required",
validate: (value) =>
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
value
) || "Email ID is not valid",
) || "Email address is not valid",
}}
error={errors.email}
placeholder="Enter registered Email ID"
placeholder="Enter registered email address.."
className="border-custom-border-300 h-[46px]"
/>
</div>
<div className="mt-5 flex items-center gap-2">
<div className="mt-5 flex flex-col-reverse sm:flex-row items-center gap-2">
<SecondaryButton
className="w-full text-center"
className="w-full text-center h-[46px]"
onClick={() => setIsResettingPassword(false)}
>
Go Back
</SecondaryButton>
<PrimaryButton type="submit" className="w-full text-center" loading={isSubmitting}>
<PrimaryButton type="submit" className="w-full text-center h-[46px]" loading={isSubmitting}>
{isSubmitting ? "Sending link..." : "Send reset link"}
</PrimaryButton>
</div>

View File

@ -1,9 +1,14 @@
import { useEffect, useState, FC } from "react";
import Link from "next/link";
import Image from "next/image";
import { useRouter } from "next/router";
// next-themes
import { useTheme } from "next-themes";
// images
import githubImage from "/public/logos/github-black.png";
import githubBlackImage from "/public/logos/github-black.png";
import githubWhiteImage from "/public/logos/github-white.png";
const { NEXT_PUBLIC_GITHUB_ID } = process.env;
@ -11,15 +16,15 @@ export interface GithubLoginButtonProps {
handleSignIn: React.Dispatch<string>;
}
export const GithubLoginButton: FC<GithubLoginButtonProps> = (props) => {
const { handleSignIn } = props;
// router
export const GithubLoginButton: FC<GithubLoginButtonProps> = ({ handleSignIn }) => {
const [loginCallBackURL, setLoginCallBackURL] = useState(undefined);
const [gitCode, setGitCode] = useState<null | string>(null);
const {
query: { code },
} = useRouter();
// states
const [loginCallBackURL, setLoginCallBackURL] = useState(undefined);
const [gitCode, setGitCode] = useState<null | string>(null);
const { theme } = useTheme();
useEffect(() => {
if (code && !gitCode) {
@ -35,13 +40,18 @@ export const GithubLoginButton: FC<GithubLoginButtonProps> = (props) => {
}, []);
return (
<div className="w-full flex justify-center items-center px-[3px]">
<div className="w-full flex justify-center items-center">
<Link
href={`https://github.com/login/oauth/authorize?client_id=${NEXT_PUBLIC_GITHUB_ID}&redirect_uri=${loginCallBackURL}&scope=read:user,user:email`}
>
<button className="flex w-full items-center justify-center gap-3 rounded border border-custom-border-100 p-2 text-sm font-medium text-custom-text-200 duration-300 hover:bg-custom-background-80">
<Image src={githubImage} height={20} width={20} color="#000" alt="GitHub Logo" />
<span>Sign In with Github</span>
<button className="flex w-full items-center justify-center gap-2 rounded border border-custom-border-300 p-2 text-sm font-medium text-custom-text-100 duration-300 hover:bg-custom-background-80 h-[46px]">
<Image
src={theme === "dark" ? githubWhiteImage : githubBlackImage}
height={20}
width={20}
alt="GitHub Logo"
/>
<span>Sign in with GitHub</span>
</button>
</Link>
</div>

View File

@ -1,5 +1,5 @@
import { FC, CSSProperties, useEffect, useRef, useCallback, useState } from "react";
// next
import Script from "next/script";
export interface IGoogleLoginButton {
@ -8,18 +8,18 @@ export interface IGoogleLoginButton {
styles?: CSSProperties;
}
export const GoogleLoginButton: FC<IGoogleLoginButton> = (props) => {
const { handleSignIn } = props;
export const GoogleLoginButton: FC<IGoogleLoginButton> = ({ handleSignIn }) => {
const googleSignInButton = useRef<HTMLDivElement>(null);
const [gsiScriptLoaded, setGsiScriptLoaded] = useState(false);
const loadScript = useCallback(() => {
if (!googleSignInButton.current || gsiScriptLoaded) return;
window?.google?.accounts.id.initialize({
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENTID || "",
callback: handleSignIn,
});
window?.google?.accounts.id.renderButton(
googleSignInButton.current,
{
@ -27,11 +27,13 @@ export const GoogleLoginButton: FC<IGoogleLoginButton> = (props) => {
theme: "outline",
size: "large",
logo_alignment: "center",
width: "410",
text: "continue_with",
width: "360",
text: "signin_with",
} as GsiButtonConfiguration // customization attributes
);
window?.google?.accounts.id.prompt(); // also display the One Tap dialog
setGsiScriptLoaded(true);
}, [handleSignIn, gsiScriptLoaded]);
@ -48,7 +50,7 @@ export const GoogleLoginButton: FC<IGoogleLoginButton> = (props) => {
<>
<Script src="https://accounts.google.com/gsi/client" async defer onLoad={loadScript} />
<div
className="overflow-hidden rounded w-full flex justify-center items-center"
className="overflow-hidden rounded w-full flex justify-center items-center !text-sm !font-medium !text-custom-text-100"
id="googleSignInButton"
ref={googleSignInButton}
/>

View File

@ -111,7 +111,7 @@ export const CreateUpdateAnalyticsModal: React.FC<Props> = ({ isOpen, handleClos
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform rounded-lg border border-custom-border-100 bg-custom-background-100 px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<Dialog.Panel className="relative transform rounded-lg border border-custom-border-200 bg-custom-background-100 px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<Dialog.Title

View File

@ -31,7 +31,7 @@ export const CustomTooltip: React.FC<Props> = ({ datum, analytics, params }) =>
}
return (
<div className="flex items-center gap-2 rounded-md border border-custom-border-100 bg-custom-background-80 p-2 text-xs">
<div className="flex items-center gap-2 rounded-md border border-custom-border-200 bg-custom-background-80 p-2 text-xs">
<span
className="h-3 w-3 rounded"
style={{

View File

@ -185,7 +185,7 @@ export const AnalyticsSidebar: React.FC<Props> = ({
<div
className={`px-5 py-2.5 flex items-center justify-between space-y-2 ${
fullScreen
? "border-l border-custom-border-100 md:h-full md:border-l md:border-custom-border-100 md:space-y-4 overflow-hidden md:flex-col md:items-start md:py-5"
? "border-l border-custom-border-200 md:h-full md:border-l md:border-custom-border-200 md:space-y-4 overflow-hidden md:flex-col md:items-start md:py-5"
: ""
}`}
>

View File

@ -37,9 +37,9 @@ export const AnalyticsTable: React.FC<Props> = ({ analytics, barGraphData, param
<div className="flow-root">
<div className="overflow-x-auto">
<div className="inline-block min-w-full align-middle">
<table className="min-w-full divide-y divide-custom-border-100 whitespace-nowrap border-y border-custom-border-100">
<table className="min-w-full divide-y divide-custom-border-200 whitespace-nowrap border-y border-custom-border-200">
<thead className="bg-custom-background-80">
<tr className="divide-x divide-custom-border-100 text-sm text-custom-text-100">
<tr className="divide-x divide-custom-border-200 text-sm text-custom-text-100">
<th scope="col" className="py-3 px-2.5 text-left font-medium">
{ANALYTICS_X_AXIS_VALUES.find((v) => v.value === params.x_axis)?.label}
</th>
@ -80,11 +80,11 @@ export const AnalyticsTable: React.FC<Props> = ({ analytics, barGraphData, param
)}
</tr>
</thead>
<tbody className="divide-y divide-custom-border-100">
<tbody className="divide-y divide-custom-border-200">
{barGraphData.data.map((item, index) => (
<tr
key={`table-row-${index}`}
className="divide-x divide-custom-border-100 text-xs text-custom-text-200"
className="divide-x divide-custom-border-200 text-xs text-custom-text-200"
>
<td
className={`flex items-center gap-2 whitespace-nowrap py-2 px-2.5 font-medium ${

View File

@ -155,7 +155,7 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
} ${isOpen ? "right-0" : "-right-full"} duration-300 transition-all`}
>
<div
className={`flex h-full flex-col overflow-hidden border-custom-border-100 bg-custom-background-100 text-left ${
className={`flex h-full flex-col overflow-hidden border-custom-border-200 bg-custom-background-100 text-left ${
fullScreen ? "rounded-lg border" : "border-l"
}`}
>
@ -186,12 +186,12 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
</div>
</div>
<Tab.Group as={Fragment}>
<Tab.List as="div" className="space-x-2 border-b border-custom-border-100 p-5 pt-0">
<Tab.List as="div" className="space-x-2 border-b border-custom-border-200 p-5 pt-0">
{tabsList.map((tab) => (
<Tab
key={tab}
className={({ selected }) =>
`rounded-3xl border border-custom-border-100 px-4 py-2 text-xs hover:bg-custom-background-80 ${
`rounded-3xl border border-custom-border-200 px-4 py-2 text-xs hover:bg-custom-background-80 ${
selected ? "bg-custom-background-80" : ""
}`
}

View File

@ -10,7 +10,7 @@ type Props = {
};
export const AnalyticsDemand: React.FC<Props> = ({ defaultAnalytics }) => (
<div className="space-y-3 rounded-[10px] border border-custom-border-100 p-3">
<div className="space-y-3 rounded-[10px] border border-custom-border-200 p-3">
<h5 className="text-xs text-red-500">DEMAND</h5>
<div>
<h4 className="text-custom-text-100 text-base font-medium">Total open tasks</h4>
@ -50,7 +50,7 @@ export const AnalyticsDemand: React.FC<Props> = ({ defaultAnalytics }) => (
);
})}
</div>
<div className="!mt-6 flex w-min items-center gap-2 whitespace-nowrap rounded-md border border-custom-border-100 bg-custom-background-80 p-2 text-xs">
<div className="!mt-6 flex w-min items-center gap-2 whitespace-nowrap rounded-md border border-custom-border-200 bg-custom-background-80 p-2 text-xs">
<p className="flex items-center gap-1 text-custom-text-200">
<PlayIcon className="h-4 w-4 -rotate-90" aria-hidden="true" />
<span>Estimate Demand:</span>

View File

@ -10,7 +10,7 @@ type Props = {
};
export const AnalyticsLeaderboard: React.FC<Props> = ({ users, title }) => (
<div className="p-3 border border-custom-border-100 rounded-[10px]">
<div className="p-3 border border-custom-border-200 rounded-[10px]">
<h6 className="text-base font-medium">{title}</h6>
{users.length > 0 ? (
<div className="mt-3 space-y-3">

View File

@ -8,9 +8,9 @@ type Props = {
};
export const AnalyticsScope: React.FC<Props> = ({ defaultAnalytics }) => (
<div className="rounded-[10px] border border-custom-border-100">
<div className="rounded-[10px] border border-custom-border-200">
<h5 className="p-3 text-xs text-green-500">SCOPE</h5>
<div className="divide-y divide-custom-border-100">
<div className="divide-y divide-custom-border-200">
<div>
<h6 className="px-3 text-base font-medium">Pending issues</h6>
{defaultAnalytics.pending_issue_user.length > 0 ? (
@ -27,7 +27,7 @@ export const AnalyticsScope: React.FC<Props> = ({ defaultAnalytics }) => (
);
return (
<div className="rounded-md border border-custom-border-100 bg-custom-background-80 p-2 text-xs">
<div className="rounded-md border border-custom-border-200 bg-custom-background-80 p-2 text-xs">
<span className="font-medium text-custom-text-200">
{assignee
? assignee.assignees__first_name + " " + assignee.assignees__last_name

View File

@ -9,54 +9,46 @@ type Props = {
defaultAnalytics: IDefaultAnalyticsResponse;
};
export const AnalyticsYearWiseIssues: React.FC<Props> = ({ defaultAnalytics }) => {
const currentMonth = new Date().getMonth();
const startMonth = Math.floor(currentMonth / 3) * 3 + 1;
const quarterMonthsList = [startMonth, startMonth + 1, startMonth + 2];
return (
<div className="py-3 border border-custom-border-100 rounded-[10px]">
<h1 className="px-3 text-base font-medium">Issues closed in a year</h1>
{defaultAnalytics.issue_completed_month_wise.length > 0 ? (
<LineGraph
data={[
{
id: "issues_closed",
color: "rgb(var(--color-primary-100))",
data: MONTHS_LIST.map((month) => ({
x: month.label.substring(0, 3),
y:
defaultAnalytics.issue_completed_month_wise.find(
(data) => data.month === month.value
)?.count || 0,
})),
},
]}
customYAxisTickValues={defaultAnalytics.issue_completed_month_wise.map((data) => {
if (quarterMonthsList.includes(data.month)) return data.count;
return 0;
})}
height="300px"
colors={(datum) => datum.color}
curve="monotoneX"
margin={{ top: 20 }}
enableSlices="x"
sliceTooltip={(datum) => (
<div className="rounded-md border border-custom-border-100 bg-custom-background-80 p-2 text-xs">
{datum.slice.points[0].data.yFormatted}
<span className="text-custom-text-200"> issues closed in </span>
{datum.slice.points[0].data.xFormatted}
</div>
)}
theme={{
background: "rgb(var(--color-background-100))",
}}
enableArea
/>
) : (
<div className="text-custom-text-200 text-center text-sm py-8">No matching data found.</div>
)}
</div>
);
};
export const AnalyticsYearWiseIssues: React.FC<Props> = ({ defaultAnalytics }) => (
<div className="py-3 border border-custom-border-200 rounded-[10px]">
<h1 className="px-3 text-base font-medium">Issues closed in a year</h1>
{defaultAnalytics.issue_completed_month_wise.length > 0 ? (
<LineGraph
data={[
{
id: "issues_closed",
color: "rgb(var(--color-primary-100))",
data: MONTHS_LIST.map((month) => ({
x: month.label.substring(0, 3),
y:
defaultAnalytics.issue_completed_month_wise.find(
(data) => data.month === month.value
)?.count || 0,
})),
},
]}
customYAxisTickValues={defaultAnalytics.issue_completed_month_wise.map(
(data) => data.count
)}
height="300px"
colors={(datum) => datum.color}
curve="monotoneX"
margin={{ top: 20 }}
enableSlices="x"
sliceTooltip={(datum) => (
<div className="rounded-md border border-custom-border-200 bg-custom-background-80 p-2 text-xs">
{datum.slice.points[0].data.yFormatted}
<span className="text-custom-text-200"> issues closed in </span>
{datum.slice.points[0].data.xFormatted}
</div>
)}
theme={{
background: "rgb(var(--color-background-100))",
}}
enableArea
/>
) : (
<div className="text-custom-text-200 text-center text-sm py-8">No matching data found.</div>
)}
</div>
);

View File

@ -22,7 +22,7 @@ export const NotAuthorizedView: React.FC<Props> = ({ actionButton, type }) => {
return (
<DefaultLayout>
<div className="flex h-full w-full flex-col items-center justify-center gap-y-5 bg-custom-background-90 text-center">
<div className="flex h-full w-full flex-col items-center justify-center gap-y-5 bg-custom-background-100 text-center">
<div className="h-44 w-72">
<Image
src={type === "project" ? ProjectNotAuthorizedImg : WorkspaceNotAuthorizedImg}

View File

@ -41,7 +41,7 @@ export const JoinProject: React.FC = () => {
};
return (
<div className="flex h-full w-full flex-col items-center justify-center gap-y-5 bg-custom-background-90 text-center">
<div className="flex h-full w-full flex-col items-center justify-center gap-y-5 bg-custom-background-100 text-center">
<div className="h-44 w-72">
<Image src={JoinProjectImg} height="176" width="288" alt="JoinProject" />
</div>

View File

@ -0,0 +1,90 @@
import React, { useState } from "react";
// component
import { CustomSelect, ToggleSwitch } from "components/ui";
import { SelectMonthModal } from "components/automation";
// icons
import { ChevronDownIcon } from "@heroicons/react/24/outline";
// constants
import { PROJECT_AUTOMATION_MONTHS } from "constants/project";
// types
import { IProject } from "types";
type Props = {
projectDetails: IProject | undefined;
handleChange: (formData: Partial<IProject>) => Promise<void>;
};
export const AutoArchiveAutomation: React.FC<Props> = ({ projectDetails, handleChange }) => {
const [monthModal, setmonthModal] = useState(false);
const initialValues: Partial<IProject> = { archive_in: 1 };
return (
<>
<SelectMonthModal
type="auto-archive"
initialValues={initialValues}
isOpen={monthModal}
handleClose={() => setmonthModal(false)}
handleChange={handleChange}
/>
<div className="flex flex-col gap-7 px-6 py-5 rounded-[10px] border border-custom-border-100 bg-custom-background-90">
<div className="flex items-center justify-between gap-x-8 gap-y-2">
<div className="flex flex-col gap-2.5">
<h4 className="text-lg font-semibold">Auto-archive closed issues</h4>
<p className="text-sm text-custom-text-200">
Plane will automatically archive issues that have been completed or cancelled for the
configured time period.
</p>
</div>
<ToggleSwitch
value={projectDetails?.archive_in !== 0}
onChange={() =>
projectDetails?.archive_in === 0
? handleChange({ archive_in: 1 })
: handleChange({ archive_in: 0 })
}
size="sm"
/>
</div>
{projectDetails?.archive_in !== 0 && (
<div className="flex items-center justify-between gap-2 w-full">
<div className="w-1/2 text-base font-medium">
Auto-archive issues that are closed for
</div>
<div className="w-1/2">
<CustomSelect
value={projectDetails?.archive_in}
label={`${projectDetails?.archive_in} ${
projectDetails?.archive_in === 1 ? "Month" : "Months"
}`}
onChange={(val: number) => {
handleChange({ archive_in: val });
}}
input
verticalPosition="top"
width="w-full"
>
<>
{PROJECT_AUTOMATION_MONTHS.map((month) => (
<CustomSelect.Option key={month.label} value={month.value}>
{month.label}
</CustomSelect.Option>
))}
<button
type="button"
className="flex w-full select-none items-center rounded px-1 py-1.5 text-custom-text-200 hover:bg-custom-background-80"
onClick={() => setmonthModal(true)}
>
Customize Time Range
</button>
</>
</CustomSelect>
</div>
</div>
)}
</div>
</>
);
};

View File

@ -0,0 +1,177 @@
import React, { useState } from "react";
import useSWR from "swr";
import { useRouter } from "next/router";
// component
import { CustomSearchSelect, CustomSelect, ToggleSwitch } from "components/ui";
import { SelectMonthModal } from "components/automation";
// icons
import { ChevronDownIcon, Squares2X2Icon } from "@heroicons/react/24/outline";
import { getStateGroupIcon } from "components/icons";
// services
import stateService from "services/state.service";
// constants
import { PROJECT_AUTOMATION_MONTHS } from "constants/project";
import { STATES_LIST } from "constants/fetch-keys";
// types
import { IProject } from "types";
// helper
import { getStatesList } from "helpers/state.helper";
type Props = {
projectDetails: IProject | undefined;
handleChange: (formData: Partial<IProject>) => Promise<void>;
};
export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleChange }) => {
const [monthModal, setmonthModal] = useState(false);
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
workspaceSlug && projectId
? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null
);
const states = getStatesList(stateGroups ?? {});
const options = states
?.filter((state) => state.group === "cancelled")
.map((state) => ({
value: state.id,
query: state.name,
content: (
<div className="flex items-center gap-2">
{getStateGroupIcon(state.group, "16", "16", state.color)}
{state.name}
</div>
),
}));
const multipleOptions = options.length > 1;
const defaultState = stateGroups && stateGroups.cancelled ? stateGroups.cancelled[0].id : null;
const selectedOption = states?.find(
(s) => s.id === projectDetails?.default_state ?? defaultState
);
const currentDefaultState = states.find((s) => s.id === defaultState);
const initialValues: Partial<IProject> = {
close_in: 1,
default_state: defaultState,
};
return (
<>
<SelectMonthModal
type="auto-close"
initialValues={initialValues}
isOpen={monthModal}
handleClose={() => setmonthModal(false)}
handleChange={handleChange}
/>
<div className="flex flex-col gap-7 px-6 py-5 rounded-[10px] border border-custom-border-100 bg-custom-background-90">
<div className="flex items-center justify-between gap-x-8 gap-y-2 ">
<div className="flex flex-col gap-2.5">
<h4 className="text-lg font-semibold">Auto-close inactive issues</h4>
<p className="text-sm text-custom-text-200">
Plane will automatically close the issues that have not been updated for the
configured time period.
</p>
</div>
<ToggleSwitch
value={projectDetails?.close_in !== 0}
onChange={() =>
projectDetails?.close_in === 0
? handleChange({ close_in: 1, default_state: defaultState })
: handleChange({ close_in: 0, default_state: null })
}
size="sm"
/>
</div>
{projectDetails?.close_in !== 0 && (
<div className="flex flex-col gap-4 w-full">
<div className="flex items-center justify-between gap-2 w-full">
<div className="w-1/2 text-base font-medium">
Auto-close issues that are inactive for
</div>
<div className="w-1/2">
<CustomSelect
value={projectDetails?.close_in}
label={`${projectDetails?.close_in} ${
projectDetails?.close_in === 1 ? "Month" : "Months"
}`}
onChange={(val: number) => {
handleChange({ close_in: val });
}}
input
width="w-full"
>
<>
{PROJECT_AUTOMATION_MONTHS.map((month) => (
<CustomSelect.Option key={month.label} value={month.value}>
{month.label}
</CustomSelect.Option>
))}
<button
type="button"
className="flex w-full select-none items-center rounded px-1 py-1.5 text-custom-text-200 hover:bg-custom-background-80"
onClick={() => setmonthModal(true)}
>
Customize Time Range
</button>
</>
</CustomSelect>
</div>
</div>
<div className="flex items-center justify-between gap-2 w-full">
<div className="w-1/2 text-base font-medium">Auto-close Status</div>
<div className="w-1/2 ">
<CustomSearchSelect
value={
projectDetails?.default_state ? projectDetails?.default_state : defaultState
}
label={
<div className="flex items-center gap-2">
{selectedOption ? (
getStateGroupIcon(selectedOption.group, "16", "16", selectedOption.color)
) : currentDefaultState ? (
getStateGroupIcon(
currentDefaultState.group,
"16",
"16",
currentDefaultState.color
)
) : (
<Squares2X2Icon className="h-3.5 w-3.5 text-custom-text-200" />
)}
{selectedOption?.name
? selectedOption.name
: currentDefaultState?.name ?? (
<span className="text-custom-text-200">State</span>
)}
</div>
}
onChange={(val: string) => {
handleChange({ default_state: val });
}}
options={options}
disabled={!multipleOptions}
width="w-full"
input
/>
</div>
</div>
</div>
)}
</div>
</>
);
};

View File

@ -0,0 +1,3 @@
export * from "./auto-close-automation";
export * from "./auto-archive-automation";
export * from "./select-month-modal";

View File

@ -0,0 +1,147 @@
import React from "react";
import { useRouter } from "next/router";
// react-hook-form
import { useForm } from "react-hook-form";
// headless ui
import { Dialog, Transition } from "@headlessui/react";
// ui
import { Input, PrimaryButton, SecondaryButton } from "components/ui";
// types
import type { IProject } from "types";
// types
type Props = {
isOpen: boolean;
type: "auto-close" | "auto-archive";
initialValues: Partial<IProject>;
handleClose: () => void;
handleChange: (formData: Partial<IProject>) => Promise<void>;
};
export const SelectMonthModal: React.FC<Props> = ({
type,
initialValues,
isOpen,
handleClose,
handleChange,
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const {
register,
formState: { errors, isSubmitting },
handleSubmit,
reset,
} = useForm<IProject>({
defaultValues: initialValues,
});
const onClose = () => {
handleClose();
reset(initialValues);
};
const onSubmit = (formData: Partial<IProject>) => {
if (!workspaceSlug && !projectId) return;
handleChange(formData);
onClose();
};
const inputSection = (name: string) => (
<div className="relative flex flex-col gap-1 justify-center w-full">
<Input
type="number"
id={name}
name={name}
placeholder="Enter Months"
autoComplete="off"
register={register}
width="full"
validations={{
required: "Select a month between 1 and 12.",
min: 1,
max: 12,
}}
className="border-custom-border-200"
/>
<span className="absolute text-sm text-custom-text-200 top-2.5 right-8">Months</span>
</div>
);
return (
<Transition.Root show={isOpen} as={React.Fragment}>
<Dialog as="div" className="relative z-30" onClose={onClose}>
<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-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-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 rounded-lg bg-custom-background-90 px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-custom-text-100"
>
Customize Time Range
</Dialog.Title>
<div className="mt-8 flex items-center gap-2">
<div className="flex w-full flex-col gap-1 justify-center">
{type === "auto-close" ? (
<>
{inputSection("close_in")}
{errors.close_in && (
<span className="text-sm px-1 text-red-500">
Select a month between 1 and 12.
</span>
)}
</>
) : (
<>
{inputSection("archive_in")}
{errors.archive_in && (
<span className="text-sm px-1 text-red-500">
Select a month between 1 and 12.
</span>
)}
</>
)}
</div>
</div>
</div>
<div className="mt-5 flex justify-end gap-2">
<SecondaryButton onClick={onClose}>Cancel</SecondaryButton>
<PrimaryButton type="submit" loading={isSubmitting}>
{isSubmitting ? "Submitting..." : "Submit"}
</PrimaryButton>
</div>
</form>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
};

View File

@ -17,7 +17,7 @@ const Breadcrumbs = ({ children }: BreadcrumbsProps) => {
<div className="flex items-center">
<button
type="button"
className="group grid h-7 w-7 flex-shrink-0 cursor-pointer place-items-center rounded border border-custom-sidebar-border-100 text-center text-sm hover:bg-custom-sidebar-background-90"
className="group grid h-7 w-7 flex-shrink-0 cursor-pointer place-items-center rounded border border-custom-sidebar-border-200 text-center text-sm hover:bg-custom-sidebar-background-90"
onClick={() => router.back()}
>
<Icon
@ -41,7 +41,7 @@ const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({ title, link, icon }) =>
<>
{link ? (
<Link href={link}>
<a className="border-r-2 border-custom-sidebar-border-100 px-3 text-sm">
<a className="border-r-2 border-custom-sidebar-border-200 px-3 text-sm">
<p className={`${icon ? "flex items-center gap-2" : ""}`}>
{icon ?? null}
{title}

View File

@ -380,7 +380,6 @@ export const CommandPalette: React.FC = () => {
user={user}
/>
)}
<CreateUpdateIssueModal
isOpen={isIssueModalOpen}
handleClose={() => setIsIssueModalOpen(false)}
@ -421,7 +420,7 @@ export const CommandPalette: React.FC = () => {
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 mx-auto max-w-2xl transform divide-y divide-custom-border-100 divide-opacity-10 rounded-xl border border-custom-border-100 bg-custom-background-100 shadow-2xl transition-all">
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-custom-border-200 divide-opacity-10 rounded-xl border border-custom-border-200 bg-custom-background-100 shadow-2xl transition-all">
<Command
filter={(value, search) => {
if (value.toLowerCase().includes(search.toLowerCase())) return 1;
@ -456,7 +455,7 @@ export const CommandPalette: React.FC = () => {
aria-hidden="true"
/>
<Command.Input
className="w-full border-0 border-b border-custom-border-100 bg-transparent p-4 pl-11 text-custom-text-100 outline-none focus:ring-0 sm:text-sm"
className="w-full border-0 border-b border-custom-border-200 bg-transparent p-4 pl-11 text-custom-text-100 outline-none focus:ring-0 sm:text-sm"
placeholder={placeholder}
value={searchTerm}
onValueChange={(e) => {

View File

@ -104,7 +104,7 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
</span>
</Dialog.Title>
<div>
<div className="flex w-full items-center justify-start gap-1 rounded border-[0.6px] border-custom-border-100 bg-custom-background-90 px-3 py-2">
<div className="flex w-full items-center justify-start gap-1 rounded border-[0.6px] border-custom-border-200 bg-custom-background-90 px-3 py-2">
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-200" />
<Input
className="w-full border-none bg-transparent py-1 px-2 text-xs text-custom-text-200 focus:outline-none"
@ -130,15 +130,15 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
{shortcut.keys.split(",").map((key, index) => (
<span key={index} className="flex items-center gap-1">
{key === "Ctrl" ? (
<span className="flex h-full items-center rounded-sm border border-custom-border-100 bg-custom-background-90 p-1.5">
<span className="flex h-full items-center rounded-sm border border-custom-border-200 bg-custom-background-90 p-1.5">
<CommandIcon className="h-4 w-4 fill-current text-custom-text-200" />
</span>
) : key === "Ctrl" ? (
<kbd className="rounded-sm border border-custom-border-100 bg-custom-background-90 p-1.5 text-sm font-medium text-custom-text-200">
<kbd className="rounded-sm border border-custom-border-200 bg-custom-background-90 p-1.5 text-sm font-medium text-custom-text-200">
<CommandIcon className="h-4 w-4 fill-current text-custom-text-200" />
</kbd>
) : (
<kbd className="rounded-sm border border-custom-border-100 bg-custom-background-90 px-2 py-1 text-sm font-medium text-custom-text-200">
<kbd className="rounded-sm border border-custom-border-200 bg-custom-background-90 px-2 py-1 text-sm font-medium text-custom-text-200">
{key}
</kbd>
)}
@ -173,15 +173,15 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
{keys.split(",").map((key, index) => (
<span key={index} className="flex items-center gap-1">
{key === "Ctrl" ? (
<span className="flex h-full items-center rounded-sm border border-custom-border-100 bg-custom-background-90 p-1.5 text-custom-text-200">
<span className="flex h-full items-center rounded-sm border border-custom-border-200 bg-custom-background-90 p-1.5 text-custom-text-200">
<CommandIcon className="h-4 w-4 fill-current text-custom-text-200" />
</span>
) : key === "Ctrl" ? (
<kbd className="rounded-sm border border-custom-border-100 bg-custom-background-90 p-1.5 text-sm font-medium text-custom-text-200">
<kbd className="rounded-sm border border-custom-border-200 bg-custom-background-90 p-1.5 text-sm font-medium text-custom-text-200">
<CommandIcon className="h-4 w-4 fill-current text-custom-text-200" />
</kbd>
) : (
<kbd className="rounded-sm border border-custom-border-100 bg-custom-background-90 px-2 py-1 text-sm font-medium text-custom-text-200">
<kbd className="rounded-sm border border-custom-border-200 bg-custom-background-90 px-2 py-1 text-sm font-medium text-custom-text-200">
{key}
</kbd>
)}

View File

@ -74,7 +74,7 @@ export const AllBoards: React.FC<Props> = ({
);
})}
{!showEmptyGroups && (
<div className="h-full w-96 flex-shrink-0 space-y-3 p-1">
<div className="h-full w-96 flex-shrink-0 space-y-2 p-1">
<h2 className="text-lg font-semibold">Hidden groups</h2>
<div className="space-y-3">
{Object.keys(groupedByIssues).map((singleGroup, index) => {

View File

@ -60,6 +60,10 @@ export const SingleBoard: React.FC<Props> = ({
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
// Check if it has at least 4 tickets since it is enough to accommodate the Calendar height
const issuesLength = groupedByIssues?.[groupTitle].length;
const hasMinimumNumberOfCards = issuesLength ? issuesLength >= 4 : false;
const isNotAllowed = userAuth.isGuest || userAuth.isViewer || isCompleted;
return (
@ -103,7 +107,11 @@ export const SingleBoard: React.FC<Props> = ({
</div>
</>
)}
<div className="pt-3 overflow-hidden overflow-y-scroll">
<div
className={`pt-3 ${
hasMinimumNumberOfCards ? "overflow-hidden overflow-y-scroll" : ""
} `}
>
{groupedByIssues?.[groupTitle].map((issue, index) => (
<Draggable
key={issue.id}

View File

@ -356,7 +356,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
selfPositioned
/>
)}
{properties.due_date && (
{properties.due_date && issue.target_date && (
<ViewDueDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -364,7 +364,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
isNotAllowed={isNotAllowed}
/>
)}
{properties.labels && (
{properties.labels && issue.labels.length > 0 && (
<ViewLabelSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -382,7 +382,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
selfPositioned
/>
)}
{properties.estimate && (
{properties.estimate && issue.estimate_point !== null && (
<ViewEstimateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -391,8 +391,8 @@ export const SingleBoardIssue: React.FC<Props> = ({
selfPositioned
/>
)}
{properties.sub_issue_count && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-100 px-2.5 py-1 text-xs shadow-sm">
{properties.sub_issue_count && issue.sub_issues_count > 0 && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-200 px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Sub-issue" tooltipContent={`${issue.sub_issues_count}`}>
<div className="flex items-center gap-1 text-custom-text-200">
<LayerDiagonalIcon className="h-3.5 w-3.5" />
@ -401,8 +401,8 @@ export const SingleBoardIssue: React.FC<Props> = ({
</Tooltip>
</div>
)}
{properties.link && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-100 px-2.5 py-1 text-xs shadow-sm">
{properties.link && issue.link_count > 0 && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-200 px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Link" tooltipContent={`${issue.link_count}`}>
<div className="flex items-center gap-1 text-custom-text-200">
<LinkIcon className="h-3.5 w-3.5" />
@ -411,8 +411,8 @@ export const SingleBoardIssue: React.FC<Props> = ({
</Tooltip>
</div>
)}
{properties.attachment_count && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-100 px-2.5 py-1 text-xs shadow-sm">
{properties.attachment_count && issue.attachment_count > 0 && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-200 px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Attachment" tooltipContent={`${issue.attachment_count}`}>
<div className="flex items-center gap-1 text-custom-text-200">
<PaperClipIcon className="h-3.5 w-3.5 -rotate-45" />

View File

@ -92,7 +92,7 @@ export const CalendarHeader: React.FC<Props> = ({
</button>
))}
</div>
<div className="grid grid-cols-4 border-t border-custom-border-100 px-2">
<div className="grid grid-cols-4 border-t border-custom-border-200 px-2">
{MONTHS_LIST.map((month) => (
<button
onClick={() =>
@ -152,7 +152,7 @@ export const CalendarHeader: React.FC<Props> = ({
<div className="flex w-full items-center justify-end gap-2">
<button
className="group flex cursor-pointer items-center gap-2 rounded-md border border-custom-border-100 px-3 py-1 text-sm hover:bg-custom-background-80 hover:text-custom-text-100 focus:outline-none"
className="group flex cursor-pointer items-center gap-2 rounded-md border border-custom-border-200 px-3 py-1 text-sm hover:bg-custom-background-80 hover:text-custom-text-100 focus:outline-none"
onClick={() => {
if (isMonthlyView) {
updateDate(new Date());
@ -170,7 +170,7 @@ export const CalendarHeader: React.FC<Props> = ({
<CustomMenu
customButton={
<div className="group flex cursor-pointer items-center gap-2 rounded-md border border-custom-border-100 px-3 py-1 text-sm hover:bg-custom-background-80 hover:text-custom-text-100 focus:outline-none ">
<div className="group flex cursor-pointer items-center gap-2 rounded-md border border-custom-border-200 px-3 py-1 text-sm hover:bg-custom-background-80 hover:text-custom-text-100 focus:outline-none ">
{isMonthlyView ? "Monthly" : "Weekly"}
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
</div>
@ -207,7 +207,7 @@ export const CalendarHeader: React.FC<Props> = ({
/>
</div>
</CustomMenu.MenuItem>
<div className="mt-1 flex w-52 items-center justify-between border-t border-custom-border-100 py-2 px-1 text-sm text-custom-text-200">
<div className="mt-1 flex w-52 items-center justify-between border-t border-custom-border-200 py-2 px-1 text-sm text-custom-text-200">
<h4>Show weekends</h4>
<ToggleSwitch value={showWeekEnds} onChange={() => setShowWeekEnds(!showWeekEnds)} />
</div>

View File

@ -191,7 +191,7 @@ export const CalendarView: React.FC<Props> = ({
{weeks.map((date, index) => (
<div
key={index}
className={`flex items-center justify-start gap-2 border-custom-border-100 bg-custom-background-90 p-1.5 text-base font-medium text-custom-text-200 ${
className={`flex items-center justify-start gap-2 border-custom-border-200 bg-custom-background-90 p-1.5 text-base font-medium text-custom-text-200 ${
!isMonthlyView
? showWeekEnds
? (index + 1) % 7 === 0

View File

@ -49,7 +49,7 @@ export const SingleCalendarDate: React.FC<Props> = ({
key={index}
ref={provided.innerRef}
{...provided.droppableProps}
className={`group relative flex min-h-[150px] flex-col gap-1.5 border-t border-custom-border-100 p-2.5 text-left text-sm font-medium hover:bg-custom-background-90 ${
className={`group relative flex min-h-[150px] flex-col gap-1.5 border-t border-custom-border-200 p-2.5 text-left text-sm font-medium hover:bg-custom-background-90 ${
isMonthlyView ? "" : "pt-9"
} ${
showWeekEnds
@ -83,7 +83,7 @@ export const SingleCalendarDate: React.FC<Props> = ({
{totalIssues > 4 && (
<button
type="button"
className="w-min whitespace-nowrap rounded-md border border-custom-border-100 bg-custom-background-80 px-1.5 py-1 text-xs"
className="w-min whitespace-nowrap rounded-md border border-custom-border-200 bg-custom-background-80 px-1.5 py-1 text-xs"
onClick={() => setShowAllIssues((prevData) => !prevData)}
>
{showAllIssues ? "Hide" : totalIssues - 4 + " more"}

View File

@ -163,7 +163,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
className={`w-full relative cursor-pointer rounded border border-custom-border-100 px-1.5 py-1.5 text-xs duration-300 hover:cursor-move hover:bg-custom-background-80 ${
className={`w-full relative cursor-pointer rounded border border-custom-border-200 px-1.5 py-1.5 text-xs duration-300 hover:cursor-move hover:bg-custom-background-80 ${
snapshot.isDragging ? "bg-custom-background-80 shadow-lg" : ""
}`}
>
@ -231,7 +231,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
/>
)}
{properties.due_date && (
{properties.due_date && issue.target_date && (
<ViewDueDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -239,7 +239,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
isNotAllowed={isNotAllowed}
/>
)}
{properties.labels && (
{properties.labels && issue.labels.length > 0 && (
<ViewLabelSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -257,7 +257,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
isNotAllowed={isNotAllowed}
/>
)}
{properties.estimate && (
{properties.estimate && issue.estimate_point !== null && (
<ViewEstimateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -266,8 +266,8 @@ export const SingleCalendarIssue: React.FC<Props> = ({
isNotAllowed={isNotAllowed}
/>
)}
{properties.sub_issue_count && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-100 px-2.5 py-1 text-xs shadow-sm">
{properties.sub_issue_count && issue.sub_issues_count > 0 && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-200 px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Sub-issue" tooltipContent={`${issue.sub_issues_count}`}>
<div className="flex items-center gap-1 text-custom-text-200">
<LayerDiagonalIcon className="h-3.5 w-3.5" />
@ -276,8 +276,8 @@ export const SingleCalendarIssue: React.FC<Props> = ({
</Tooltip>
</div>
)}
{properties.link && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-100 px-2.5 py-1 text-xs shadow-sm">
{properties.link && issue.link_count > 0 && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-200 px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Links" tooltipContent={`${issue.link_count}`}>
<div className="flex items-center gap-1 text-custom-text-200">
<LinkIcon className="h-3.5 w-3.5" />
@ -286,8 +286,8 @@ export const SingleCalendarIssue: React.FC<Props> = ({
</Tooltip>
</div>
)}
{properties.attachment_count && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-100 px-2.5 py-1 text-xs shadow-sm">
{properties.attachment_count && issue.attachment_count > 0 && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-200 px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Attachments" tooltipContent={`${issue.attachment_count}`}>
<div className="flex items-center gap-1 text-custom-text-200">
<PaperClipIcon className="h-3.5 w-3.5 -rotate-45" />

View File

@ -5,19 +5,11 @@ import Link from "next/link";
// icons
import {
ArrowTopRightOnSquareIcon,
CalendarDaysIcon,
ChartBarIcon,
ChatBubbleBottomCenterTextIcon,
ChatBubbleLeftEllipsisIcon,
LinkIcon,
PaperClipIcon,
PlayIcon,
RectangleGroupIcon,
Squares2X2Icon,
TrashIcon,
UserIcon,
} from "@heroicons/react/24/outline";
import { BlockedIcon, BlockerIcon, CyclesIcon, TagIcon, UserGroupIcon } from "components/icons";
import { BlockedIcon, BlockerIcon } from "components/icons";
import { Icon } from "components/ui";
// helpers
import { renderShortDateWithYearFormat, timeAgo } from "helpers/date-time.helper";
import { addSpaceIfCamelCase } from "helpers/string.helper";
@ -32,11 +24,11 @@ const activityDetails: {
} = {
assignee: {
message: "removed the assignee",
icon: <UserGroupIcon className="h-3 w-3" color="#6b7280" aria-hidden="true" />,
icon: <Icon iconName="group" className="!text-sm" aria-hidden="true" />,
},
assignees: {
message: "added a new assignee",
icon: <UserGroupIcon className="h-3 w-3" color="#6b7280" aria-hidden="true" />,
icon: <Icon iconName="group" className="!text-sm" aria-hidden="true" />,
},
blocks: {
message: "marked this issue being blocked by",
@ -48,14 +40,14 @@ const activityDetails: {
},
cycles: {
message: "set the cycle to",
icon: <CyclesIcon height="12" width="12" color="#6b7280" />,
icon: <Icon iconName="contrast" className="!text-sm" aria-hidden="true" />,
},
labels: {
icon: <TagIcon height="12" width="12" color="#6b7280" />,
icon: <Icon iconName="sell" className="!text-sm" aria-hidden="true" />,
},
modules: {
message: "set the module to",
icon: <RectangleGroupIcon className="h-3 w-3 text-custom-text-200" aria-hidden="true" />,
icon: <Icon iconName="dataset" className="!text-sm" aria-hidden="true" />,
},
state: {
message: "set the state to",
@ -63,47 +55,47 @@ const activityDetails: {
},
priority: {
message: "set the priority to",
icon: <ChartBarIcon className="h-3 w-3 text-custom-text-200" aria-hidden="true" />,
icon: <Icon iconName="signal_cellular_alt" className="!text-sm" aria-hidden="true" />,
},
name: {
message: "set the name to",
icon: (
<ChatBubbleBottomCenterTextIcon className="h-3 w-3 text-custom-text-200" aria-hidden="true" />
),
icon: <Icon iconName="chat" className="!text-sm" aria-hidden="true" />,
},
description: {
message: "updated the description.",
icon: (
<ChatBubbleBottomCenterTextIcon className="h-3 w-3 text-custom-text-200" aria-hidden="true" />
),
icon: <Icon iconName="chat" className="!text-sm" aria-hidden="true" />,
},
estimate_point: {
message: "set the estimate point to",
icon: <PlayIcon className="h-3 w-3 -rotate-90 text-custom-text-200" aria-hidden="true" />,
icon: <Icon iconName="change_history" className="!text-sm" aria-hidden="true" />,
},
target_date: {
message: "set the due date to",
icon: <CalendarDaysIcon className="h-3 w-3 text-custom-text-200" aria-hidden="true" />,
icon: <Icon iconName="calendar_today" className="!text-sm" aria-hidden="true" />,
},
parent: {
message: "set the parent to",
icon: <UserIcon className="h-3 w-3 text-custom-text-200" aria-hidden="true" />,
icon: <Icon iconName="supervised_user_circle" className="!text-sm" aria-hidden="true" />,
},
issue: {
message: "deleted the issue.",
icon: <TrashIcon className="h-3 w-3 text-custom-text-200" aria-hidden="true" />,
icon: <Icon iconName="delete" className="!text-sm" aria-hidden="true" />,
},
estimate: {
message: "updated the estimate",
icon: <PlayIcon className="h-3 w-3 -rotate-90 text-custom-text-200" aria-hidden="true" />,
icon: <Icon iconName="change_history" className="!text-sm" aria-hidden="true" />,
},
link: {
message: "updated the link",
icon: <LinkIcon className="h-3 w-3 text-custom-text-200" aria-hidden="true" />,
icon: <Icon iconName="link" className="!text-sm" aria-hidden="true" />,
},
attachment: {
message: "updated the attachment",
icon: <PaperClipIcon className="h-3 w-3 text-custom-text-200 " aria-hidden="true" />,
icon: <Icon iconName="attach_file" className="!text-sm" aria-hidden="true" />,
},
archived_at: {
message: "archived",
icon: <Icon iconName="archive" className="!text-sm text-custom-text-200" aria-hidden="true" />,
},
};
@ -144,6 +136,11 @@ export const Feeds: React.FC<any> = ({ activities }) => (
action = `${activity.verb} the`;
} else if (activity.field === "link") {
action = `${activity.verb} the`;
} else if (activity.field === "archived_at") {
action =
activity.new_value && activity.new_value === "restore"
? "restored the issue"
: "archived the issue";
}
// for values that are after the action clause
let value: any = activity.new_value ? activity.new_value : activity.old_value;
@ -205,7 +202,13 @@ export const Feeds: React.FC<any> = ({ activities }) => (
<div key={activity.id} className="mt-2">
<div className="relative flex items-start space-x-3">
<div className="relative px-1">
{activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? (
{activity.field ? (
activity.new_value === "restore" ? (
<Icon iconName="history" className="text-sm text-custom-text-200" />
) : (
activityDetails[activity.field as keyof typeof activityDetails]?.icon
)
) : activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? (
<img
src={activity.actor_detail.avatar}
alt={activity.actor_detail.first_name}
@ -247,7 +250,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
}
editable={false}
noBorder
customClassName="text-xs border border-custom-border-100 bg-custom-background-100"
customClassName="text-xs border border-custom-border-200 bg-custom-background-100"
/>
</div>
</div>
@ -271,7 +274,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
<div>
<div className="relative px-1.5">
<div className="mt-1.5">
<div className="ring-6 flex h-7 w-7 items-center justify-center rounded-full bg-custom-background-80 ring-white">
<div className="ring-6 flex h-7 w-7 items-center justify-center rounded-full bg-custom-background-80 text-custom-text-200 ring-white">
{activity.field ? (
activityDetails[activity.field as keyof typeof activityDetails]?.icon
) : activity.actor_detail.avatar &&
@ -296,14 +299,23 @@ export const Feeds: React.FC<any> = ({ activities }) => (
</div>
<div className="min-w-0 flex-1 py-3">
<div className="text-xs text-custom-text-200">
<span className="text-gray font-medium">
{activity.actor_detail.first_name}
{activity.actor_detail.is_bot
? " Bot"
: " " + activity.actor_detail.last_name}
</span>
{activity.field === "archived_at" && activity.new_value !== "restore" ? (
<span className="text-gray font-medium">Plane</span>
) : (
<span className="text-gray font-medium">
{activity.actor_detail.first_name}
{activity.actor_detail.is_bot
? " Bot"
: " " + activity.actor_detail.last_name}
</span>
)}
<span> {action} </span>
<span className="text-xs font-medium text-custom-text-100"> {value} </span>
{activity.field !== "archived_at" && (
<span className="text-xs font-medium text-custom-text-100">
{" "}
{value}{" "}
</span>
)}
<span className="whitespace-nowrap">{timeAgo(activity.created_at)}</span>
</div>
</div>

View File

@ -109,7 +109,7 @@ export const DueDateFilterModal: React.FC<Props> = ({ isOpen, handleClose }) =>
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 flex transform rounded-lg border border-custom-border-100 bg-custom-background-100 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<Dialog.Panel className="relative flex transform rounded-lg border border-custom-border-200 bg-custom-background-100 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<form className="space-y-4" onSubmit={handleSubmit(handleFormSubmit)}>
<div className="flex w-full justify-between">
<Controller

View File

@ -58,7 +58,7 @@ export const FilterList: React.FC<any> = ({ filters, setFilters }) => {
return (
<div
key={key}
className="flex items-center gap-x-2 rounded-full border border-custom-border-100 bg-custom-background-80 px-2 py-1"
className="flex items-center gap-x-2 rounded-full border border-custom-border-200 bg-custom-background-80 px-2 py-1"
>
<span className="capitalize text-custom-text-200">
{key === "target_date" ? "Due Date" : replaceUnderscoreIfSnakeCase(key)}:
@ -310,7 +310,7 @@ export const FilterList: React.FC<any> = ({ filters, setFilters }) => {
return (
<div
key={date}
className="inline-flex items-center gap-x-1 rounded-full border border-custom-border-100 bg-custom-background-100 px-1 py-0.5"
className="inline-flex items-center gap-x-1 rounded-full border border-custom-border-200 bg-custom-background-100 px-1 py-0.5"
>
<div className="h-1.5 w-1.5 rounded-full" />
<span className="capitalize">
@ -381,7 +381,7 @@ export const FilterList: React.FC<any> = ({ filters, setFilters }) => {
target_date: null,
})
}
className="flex items-center gap-x-1 rounded-full border border-custom-border-100 bg-custom-background-80 px-3 py-1.5 text-xs"
className="flex items-center gap-x-1 rounded-full border border-custom-border-200 bg-custom-background-80 px-3 py-1.5 text-xs"
>
<span>Clear all filters</span>
<XMarkIcon className="h-3 w-3" />

View File

@ -11,7 +11,7 @@ import useEstimateOption from "hooks/use-estimate-option";
// components
import { SelectFilters } from "components/views";
// ui
import { CustomMenu, Icon, ToggleSwitch } from "components/ui";
import { CustomMenu, Icon, ToggleSwitch, Tooltip } from "components/ui";
// icons
import {
ChevronDownIcon,
@ -53,6 +53,7 @@ const issueViewOptions: { type: TIssueViewOptions; icon: any }[] = [
export const IssuesFilterView: React.FC = () => {
const router = useRouter();
const { workspaceSlug, projectId, viewId } = router.query;
const isArchivedIssues = router.pathname.includes("archived-issues");
const {
issueView,
@ -78,22 +79,31 @@ export const IssuesFilterView: React.FC = () => {
return (
<div className="flex items-center gap-2">
<div className="flex items-center gap-x-1">
{issueViewOptions.map((option) => (
<button
key={option.type}
type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none hover:bg-custom-sidebar-background-80 duration-300 ${
issueView === option.type
? "bg-custom-sidebar-background-80"
: "text-custom-sidebar-text-200"
}`}
onClick={() => setIssueView(option.type)}
>
{option.icon}
</button>
))}
</div>
{!isArchivedIssues && (
<div className="flex items-center gap-x-1">
{issueViewOptions.map((option) => (
<Tooltip
key={option.type}
tooltipContent={
<span className="capitalize">{replaceUnderscoreIfSnakeCase(option.type)} View</span>
}
position="bottom"
>
<button
type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none hover:bg-custom-sidebar-background-80 duration-300 ${
issueView === option.type
? "bg-custom-sidebar-background-80"
: "text-custom-sidebar-text-200"
}`}
onClick={() => setIssueView(option.type)}
>
{option.icon}
</button>
</Tooltip>
))}
</div>
)}
<SelectFilters
filters={filters}
onSelect={(option) => {
@ -136,7 +146,7 @@ export const IssuesFilterView: React.FC = () => {
{({ open }) => (
<>
<Popover.Button
className={`group flex items-center gap-2 rounded-md border border-custom-sidebar-border-100 bg-transparent px-3 py-1.5 text-xs hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 focus:outline-none duration-300 ${
className={`group flex items-center gap-2 rounded-md border border-custom-sidebar-border-200 bg-transparent px-3 py-1.5 text-xs hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 focus:outline-none duration-300 ${
open
? "bg-custom-sidebar-background-90 text-custom-sidebar-text-100"
: "text-custom-sidebar-text-200"
@ -155,8 +165,8 @@ export const IssuesFilterView: React.FC = () => {
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute right-0 z-30 mt-1 w-screen max-w-xs transform rounded-lg border border-custom-border-100 bg-custom-background-90 p-3 shadow-lg">
<div className="relative divide-y-2 divide-custom-border-100">
<Popover.Panel className="absolute right-0 z-30 mt-1 w-screen max-w-xs transform rounded-lg border border-custom-border-200 bg-custom-background-90 p-3 shadow-lg">
<div className="relative divide-y-2 divide-custom-border-200">
<div className="space-y-4 pb-3 text-xs">
{issueView !== "calendar" && issueView !== "spreadsheet" && (
<>
@ -282,7 +292,7 @@ export const IssuesFilterView: React.FC = () => {
className={`rounded border px-2 py-1 text-xs capitalize ${
properties[key as keyof Properties]
? "border-custom-primary bg-custom-primary text-white"
: "border-custom-border-100"
: "border-custom-border-200"
}`}
onClick={() => setProperties(key as keyof Properties)}
>

View File

@ -62,7 +62,7 @@ export const ImagePickerPopover: React.FC<Props> = ({ label, value, onChange })
return (
<Popover className="relative z-[2]" ref={ref}>
<Popover.Button
className="rounded-md border border-custom-border-100 bg-custom-background-80 px-2 py-1 text-xs text-custom-text-200"
className="rounded-md border border-custom-border-200 bg-custom-background-80 px-2 py-1 text-xs text-custom-text-200"
onClick={() => setIsOpen((prev) => !prev)}
>
{label}
@ -76,8 +76,8 @@ export const ImagePickerPopover: React.FC<Props> = ({ label, value, onChange })
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Popover.Panel className="absolute right-0 z-10 mt-2 rounded-md border border-custom-border-100 bg-custom-background-80 shadow-lg">
<div className="h-96 w-80 overflow-auto rounded border border-custom-border-100 bg-custom-background-80 p-5 shadow-2xl sm:max-w-2xl md:w-96 lg:w-[40rem]">
<Popover.Panel className="absolute right-0 z-10 mt-2 rounded-md border border-custom-border-200 bg-custom-background-80 shadow-lg">
<div className="h-96 w-80 overflow-auto rounded border border-custom-border-200 bg-custom-background-80 p-5 shadow-2xl sm:max-w-2xl md:w-96 lg:w-[40rem]">
<Tab.Group>
<Tab.List as="span" className="inline-block rounded bg-custom-background-80 p-1">
{tabOptions.map((tab) => (

View File

@ -31,11 +31,12 @@ import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
import { CreateUpdateViewModal } from "components/views";
import { TransferIssues, TransferIssuesModal } from "components/cycles";
// ui
import { EmptyState, PrimaryButton, Spinner } from "components/ui";
import { EmptyState, PrimaryButton, Spinner, Icon } from "components/ui";
// icons
import { PlusIcon, TrashIcon } from "@heroicons/react/24/outline";
// images
import emptyIssue from "public/empty-state/issue.svg";
import emptyIssueArchive from "public/empty-state/issue-archive.svg";
// helpers
import { getStatesList } from "helpers/state.helper";
import { orderArrayBy } from "helpers/array.helper";
@ -488,7 +489,7 @@ export const IssuesView: React.FC<Props> = ({
{viewId ? "Update" : "Save"} view
</PrimaryButton>
</div>
{<div className="mt-3 border-t border-custom-border-100" />}
{<div className="mt-3 border-t border-custom-border-200" />}
</>
)}
@ -577,9 +578,25 @@ export const IssuesView: React.FC<Props> = ({
issueView === "gantt_chart" && <GanttChartView />
)}
</>
) : router.pathname.includes("archived-issues") ? (
<EmptyState
title="Archived Issues will be shown here"
description="All the issues that have been in the completed or canceled groups for the configured period of time can be viewed here."
image={emptyIssueArchive}
buttonText="Go to Automation Settings"
onClick={() => {
router.push(`/${workspaceSlug}/projects/${projectId}/settings/automations`);
}}
/>
) : (
<EmptyState
title="Project issues will appear here"
title={
cycleId
? "Cycle issues will appear here"
: moduleId
? "Module issues will appear here"
: "Project issues will appear here"
}
description="Issues help you track individual pieces of work. With Issues, keep track of what's going on, who is working on it, and what's done."
image={emptyIssue}
buttonText="New Issue"

View File

@ -84,6 +84,7 @@ export const SingleListIssue: React.FC<Props> = ({
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
const isArchivedIssues = router.pathname.includes("archived-issues");
const { setToastAlert } = useToast();
@ -181,7 +182,11 @@ export const SingleListIssue: React.FC<Props> = ({
});
};
const isNotAllowed = userAuth.isGuest || userAuth.isViewer || isCompleted;
const singleIssuePath = isArchivedIssues
? `/${workspaceSlug}/projects/${projectId}/archived-issues/${issue.id}`
: `/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`;
const isNotAllowed = userAuth.isGuest || userAuth.isViewer || isCompleted || isArchivedIssues;
return (
<>
@ -207,25 +212,21 @@ export const SingleListIssue: React.FC<Props> = ({
<ContextMenu.Item Icon={LinkIcon} onClick={handleCopyText}>
Copy issue link
</ContextMenu.Item>
<a
href={`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`}
target="_blank"
rel="noreferrer noopener"
>
<a href={singleIssuePath} target="_blank" rel="noreferrer noopener">
<ContextMenu.Item Icon={ArrowTopRightOnSquareIcon}>
Open issue in new tab
</ContextMenu.Item>
</a>
</ContextMenu>
<div
className="flex flex-wrap items-center justify-between px-4 py-2.5 gap-2 border-b border-custom-border-100 bg-custom-background-100 last:border-b-0"
className="flex flex-wrap items-center justify-between px-4 py-2.5 gap-2 border-b border-custom-border-200 bg-custom-background-100 last:border-b-0"
onContextMenu={(e) => {
e.preventDefault();
setContextMenu(true);
setContextMenuPosition({ x: e.pageX, y: e.pageY });
}}
>
<Link href={`/${workspaceSlug}/projects/${issue?.project_detail?.id}/issues/${issue.id}`}>
<Link href={singleIssuePath}>
<div className="flex-grow cursor-pointer">
<a className="group relative flex items-center gap-2">
{properties.key && (
@ -247,7 +248,11 @@ export const SingleListIssue: React.FC<Props> = ({
</div>
</Link>
<div className="flex w-full flex-shrink flex-wrap items-center gap-2 text-xs sm:w-auto">
<div
className={`flex w-full flex-shrink flex-wrap items-center gap-2 text-xs sm:w-auto ${
isArchivedIssues ? "opacity-60" : ""
}`}
>
{properties.priority && (
<ViewPrioritySelect
issue={issue}
@ -266,7 +271,7 @@ export const SingleListIssue: React.FC<Props> = ({
isNotAllowed={isNotAllowed}
/>
)}
{properties.due_date && (
{properties.due_date && issue.target_date && (
<ViewDueDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -274,7 +279,7 @@ export const SingleListIssue: React.FC<Props> = ({
isNotAllowed={isNotAllowed}
/>
)}
{properties.labels && (
{properties.labels && issue.labels.length > 0 && (
<ViewLabelSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -292,7 +297,7 @@ export const SingleListIssue: React.FC<Props> = ({
isNotAllowed={isNotAllowed}
/>
)}
{properties.estimate && (
{properties.estimate && issue.estimate_point !== null && (
<ViewEstimateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -301,8 +306,8 @@ export const SingleListIssue: React.FC<Props> = ({
isNotAllowed={isNotAllowed}
/>
)}
{properties.sub_issue_count && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-100 px-2.5 py-1 text-xs shadow-sm">
{properties.sub_issue_count && issue.sub_issues_count > 0 && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-200 px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Sub-issue" tooltipContent={`${issue.sub_issues_count}`}>
<div className="flex items-center gap-1 text-custom-text-200">
<LayerDiagonalIcon className="h-3.5 w-3.5" />
@ -311,8 +316,8 @@ export const SingleListIssue: React.FC<Props> = ({
</Tooltip>
</div>
)}
{properties.link && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-100 px-2.5 py-1 text-xs shadow-sm">
{properties.link && issue.link_count > 0 && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-200 px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Links" tooltipContent={`${issue.link_count}`}>
<div className="flex items-center gap-1 text-custom-text-200">
<LinkIcon className="h-3.5 w-3.5" />
@ -321,8 +326,8 @@ export const SingleListIssue: React.FC<Props> = ({
</Tooltip>
</div>
)}
{properties.attachment_count && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-100 px-2.5 py-1 text-xs shadow-sm">
{properties.attachment_count && issue.attachment_count > 0 && (
<div className="flex cursor-default items-center rounded-md border border-custom-border-200 px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Attachments" tooltipContent={`${issue.attachment_count}`}>
<div className="flex items-center gap-1 text-custom-text-200">
<PaperClipIcon className="h-3.5 w-3.5 -rotate-45" />

View File

@ -67,6 +67,7 @@ export const SingleList: React.FC<Props> = ({
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const isArchivedIssues = router.pathname.includes("archived-issues");
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
@ -159,7 +160,9 @@ export const SingleList: React.FC<Props> = ({
</span>
</div>
</Disclosure.Button>
{type === "issue" ? (
{isArchivedIssues ? (
""
) : type === "issue" ? (
<button
type="button"
className="p-1 text-custom-text-200 hover:bg-custom-background-80"

View File

@ -173,7 +173,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen, user
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="relative mx-auto max-w-2xl transform rounded-xl border border-custom-border-100 bg-custom-background-100 shadow-2xl transition-all">
<Dialog.Panel className="relative mx-auto max-w-2xl transform rounded-xl border border-custom-border-200 bg-custom-background-100 shadow-2xl transition-all">
<form>
<Combobox
onChange={(val: string) => {
@ -201,7 +201,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen, user
<Combobox.Options
static
className="max-h-80 scroll-py-2 divide-y divide-custom-border-100 overflow-y-auto"
className="max-h-80 scroll-py-2 divide-y divide-custom-border-200 overflow-y-auto"
>
{filteredIssues.length > 0 ? (
<li className="p-2">

View File

@ -154,7 +154,7 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="relative mx-auto max-w-2xl transform rounded-xl border border-custom-border-100 bg-custom-background-100 shadow-2xl transition-all">
<Dialog.Panel className="relative mx-auto max-w-2xl transform rounded-xl border border-custom-border-200 bg-custom-background-100 shadow-2xl transition-all">
<Combobox
as="div"
onChange={(val: ISearchIssueResponse) => {
@ -182,7 +182,7 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
{selectedIssues.map((issue) => (
<div
key={issue.id}
className="flex items-center gap-1 text-xs border border-custom-border-100 bg-custom-background-80 pl-2 py-1 rounded-md text-custom-text-100 whitespace-nowrap"
className="flex items-center gap-1 text-xs border border-custom-border-200 bg-custom-background-80 pl-2 py-1 rounded-md text-custom-text-100 whitespace-nowrap"
>
{issue.project__identifier}-{issue.sequence_id}
<button
@ -200,7 +200,7 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
))}
</div>
) : (
<div className="w-min text-xs border border-custom-border-100 bg-custom-background-80 p-2 rounded-md whitespace-nowrap">
<div className="w-min text-xs border border-custom-border-200 bg-custom-background-80 p-2 rounded-md whitespace-nowrap">
No issues selected
</div>
)}

View File

@ -143,7 +143,7 @@ export const GptAssistantModal: React.FC<Props> = ({
return (
<div
className={`absolute ${inset} z-20 w-full space-y-4 rounded-[10px] border border-custom-border-100 bg-custom-background-100 p-4 shadow ${
className={`absolute ${inset} z-20 w-full space-y-4 rounded-[10px] border border-custom-border-200 bg-custom-background-100 p-4 shadow ${
isOpen ? "block" : "hidden"
}`}
>

View File

@ -118,7 +118,7 @@ export const ImageUploadModal: React.FC<Props> = ({
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg border border-custom-border-100 bg-custom-background-100 px-5 py-8 text-left shadow-xl transition-all sm:w-full sm:max-w-xl sm:p-6">
<Dialog.Panel className="relative transform overflow-hidden rounded-lg border border-custom-border-200 bg-custom-background-100 px-5 py-8 text-left shadow-xl transition-all sm:w-full sm:max-w-xl sm:p-6">
<div className="space-y-5">
<Dialog.Title
as="h3"
@ -132,7 +132,7 @@ export const ImageUploadModal: React.FC<Props> = ({
{...getRootProps()}
className={`relative grid h-80 w-full cursor-pointer place-items-center rounded-lg p-12 text-center focus:outline-none focus:ring-2 focus:ring-custom-primary focus:ring-offset-2 ${
(image === null && isDragActive) || !value
? "border-2 border-dashed border-custom-border-100 hover:bg-custom-background-90"
? "border-2 border-dashed border-custom-border-200 hover:bg-custom-background-90"
: ""
}`}
>

View File

@ -56,7 +56,7 @@ export const LinkModal: React.FC<Props> = ({ isOpen, handleClose, onFormSubmit }
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
<div className="fixed inset-0 bg-custom-backdrop bg-opacity-50 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
@ -70,7 +70,7 @@ export const LinkModal: React.FC<Props> = ({ isOpen, handleClose, onFormSubmit }
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-custom-background-80 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-custom-background-100 border border-custom-border-200 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<div className="space-y-5">

View File

@ -49,7 +49,7 @@ export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }
)}
<Link href={link.url}>
<a
className="relative flex gap-2 rounded-md bg-custom-background-100 p-2"
className="relative flex gap-2 rounded-md bg-custom-background-90 p-2"
target="_blank"
>
<div className="mt-0.5">

View File

@ -119,7 +119,7 @@ const ProgressChart: React.FC<Props> = ({ distribution, startDate, endDate, tota
gridXValues={chartData.map((item, index) => (index % 2 === 0 ? item.currentDate : ""))}
enableSlices="x"
sliceTooltip={(datum) => (
<div className="rounded-md border border-custom-border-100 bg-custom-background-80 p-2 text-xs">
<div className="rounded-md border border-custom-border-200 bg-custom-background-80 p-2 text-xs">
{datum.slice.points[0].data.yFormatted}
<span className="text-custom-text-200"> issues pending on </span>
{datum.slice.points[0].data.xFormatted}

View File

@ -87,7 +87,7 @@ export const SidebarProgressStats: React.FC<Props> = ({
<Tab
className={({ selected }) =>
`w-full ${
roundedTab ? "rounded-3xl border border-custom-border-100" : "rounded"
roundedTab ? "rounded-3xl border border-custom-border-200" : "rounded"
} px-3 py-1 text-custom-text-100 ${
selected ? " bg-custom-primary text-white" : " hover:bg-custom-background-80"
}`
@ -98,7 +98,7 @@ export const SidebarProgressStats: React.FC<Props> = ({
<Tab
className={({ selected }) =>
`w-full ${
roundedTab ? "rounded-3xl border border-custom-border-100" : "rounded"
roundedTab ? "rounded-3xl border border-custom-border-200" : "rounded"
} px-3 py-1 text-custom-text-100 ${
selected ? " bg-custom-primary text-white" : " hover:bg-custom-background-80"
}`
@ -109,7 +109,7 @@ export const SidebarProgressStats: React.FC<Props> = ({
<Tab
className={({ selected }) =>
`w-full ${
roundedTab ? "rounded-3xl border border-custom-border-100" : "rounded"
roundedTab ? "rounded-3xl border border-custom-border-200" : "rounded"
} px-3 py-1 text-custom-text-100 ${
selected ? " bg-custom-primary text-white" : " hover:bg-custom-background-80"
}`
@ -159,7 +159,7 @@ export const SidebarProgressStats: React.FC<Props> = ({
key={`unassigned-${index}`}
title={
<div className="flex items-center gap-2">
<div className="h-5 w-5 rounded-full border-2 border-custom-border-100 bg-custom-background-80">
<div className="h-5 w-5 rounded-full border-2 border-custom-border-200 bg-custom-background-80">
<img
src="/user.png"
height="100%"

View File

@ -179,10 +179,10 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
return (
<div
className="relative group grid auto-rows-[minmax(44px,1fr)] hover:rounded-sm hover:bg-custom-background-80 border-b border-custom-border-100 w-full min-w-max"
className="relative group grid auto-rows-[minmax(44px,1fr)] hover:rounded-sm hover:bg-custom-background-80 border-b border-custom-border-200 w-full min-w-max"
style={{ gridTemplateColumns }}
>
<div className="flex gap-1.5 items-center px-4 sticky z-[1] left-0 text-custom-text-200 bg-custom-background-100 group-hover:text-custom-text-100 group-hover:bg-custom-background-80 border-custom-border-100 w-full">
<div className="flex gap-1.5 items-center px-4 sticky z-[1] left-0 text-custom-text-200 bg-custom-background-100 group-hover:text-custom-text-100 group-hover:bg-custom-background-80 border-custom-border-200 w-full">
<div className="flex gap-1.5 items-center" style={issue.parent ? { paddingLeft } : {}}>
<div className="relative flex items-center cursor-pointer text-xs text-center hover:text-custom-text-100 w-14">
{properties.key && (
@ -198,7 +198,7 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
onInteraction={(nextOpenState) => setIsOpen(nextOpenState)}
content={
<div
className={`flex flex-col gap-1.5 overflow-y-scroll whitespace-nowrap rounded-md border p-1 text-xs shadow-lg focus:outline-none max-h-44 min-w-full border-custom-border-100 bg-custom-background-90`}
className={`flex flex-col gap-1.5 overflow-y-scroll whitespace-nowrap rounded-md border p-1 text-xs shadow-lg focus:outline-none max-h-44 min-w-full border-custom-border-200 bg-custom-background-90`}
>
<button
type="button"
@ -270,7 +270,7 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
</Link>
</div>
{properties.state && (
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-100">
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
<ViewStateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -284,7 +284,7 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
</div>
)}
{properties.priority && (
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-100">
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
<ViewPrioritySelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -297,7 +297,7 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
</div>
)}
{properties.assignee && (
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-100">
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
<ViewAssigneeSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -310,7 +310,7 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
</div>
)}
{properties.labels && (
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-100">
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
<ViewLabelSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -324,7 +324,7 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
)}
{properties.due_date && (
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-100">
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
<ViewDueDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -336,7 +336,7 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
</div>
)}
{properties.estimate && (
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-100">
<div className="flex items-center text-xs text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
<ViewEstimateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
@ -348,12 +348,12 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
</div>
)}
{properties.created_on && (
<div className="flex items-center text-xs cursor-default text-brand-secondary text-center p-2 group-hover:bg-brand-surface-2 border-brand-base">
<div className="flex items-center text-xs cursor-default text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
{renderLongDetailDateFormat(issue.created_at)}
</div>
)}
{properties.updated_on && (
<div className="flex items-center text-xs cursor-default text-brand-secondary text-center p-2 group-hover:bg-brand-surface-2 border-brand-base">
<div className="flex items-center text-xs cursor-default text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
{renderLongDetailDateFormat(issue.updated_at)}
</div>
)}

View File

@ -56,7 +56,7 @@ export const SpreadsheetColumns: React.FC<Props> = ({ columnData, gridTemplateCo
className="!w-full"
customButton={
<div
className={`relative group flex items-center justify-start gap-1.5 cursor-pointer text-sm text-custom-text-200 text-current hover:text-custom-text-100 w-full py-3 px-2 ${
className={`relative group flex items-center justify-start gap-1.5 cursor-pointer text-sm text-custom-text-200 hover:text-custom-text-100 w-full py-3 px-2 ${
activeSortingProperty === col.propertyName ? "bg-custom-background-80" : ""
}`}
>
@ -90,16 +90,9 @@ export const SpreadsheetColumns: React.FC<Props> = ({ columnData, gridTemplateCo
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
</div>
}
menuItemsWhiteBg
width="xl"
>
<CustomMenu.MenuItem
className={`${
selectedMenuItem === `${col.ascendingOrder}_${col.propertyName}`
? "bg-custom-background-80"
: ""
}`}
key={col.propertyName}
onClick={() => {
handleOrderBy(col.ascendingOrder, col.propertyName);
}}

View File

@ -62,7 +62,7 @@ export const SpreadsheetView: React.FC<Props> = ({
return (
<div className="h-full rounded-lg text-custom-text-200 overflow-x-auto whitespace-nowrap bg-custom-background-100">
<div className="sticky z-[2] top-0 border-b border-custom-border-100 bg-custom-background-90 w-full min-w-max">
<div className="sticky z-[2] top-0 border-b border-custom-border-200 bg-custom-background-90 w-full min-w-max">
<SpreadsheetColumns columnData={columnData} gridTemplateColumns={gridTemplateColumns} />
</div>
{spreadsheetIssues ? (
@ -84,12 +84,12 @@ export const SpreadsheetView: React.FC<Props> = ({
/>
))}
<div
className="relative group grid auto-rows-[minmax(44px,1fr)] hover:rounded-sm hover:bg-custom-background-80 border-b border-custom-border-100 w-full min-w-max"
className="relative group grid auto-rows-[minmax(44px,1fr)] hover:rounded-sm hover:bg-custom-background-80 border-b border-custom-border-200 w-full min-w-max"
style={{ gridTemplateColumns }}
>
{type === "issue" ? (
<button
className="flex gap-1.5 items-center pl-7 py-2.5 text-sm sticky left-0 z-[1] text-custom-text-200 bg-custom-background-100 group-hover:text-custom-text-100 group-hover:bg-custom-background-80 border-custom-border-100 w-full"
className="flex gap-1.5 items-center pl-7 py-2.5 text-sm sticky left-0 z-[1] text-custom-text-200 bg-custom-background-100 group-hover:text-custom-text-100 group-hover:bg-custom-background-80 border-custom-border-200 w-full"
onClick={() => {
const e = new KeyboardEvent("keydown", { key: "c" });
document.dispatchEvent(e);
@ -104,7 +104,7 @@ export const SpreadsheetView: React.FC<Props> = ({
className="sticky left-0 z-[1]"
customButton={
<button
className="flex gap-1.5 items-center pl-7 py-2.5 text-sm sticky left-0 z-[1] text-custom-text-200 bg-custom-background-100 group-hover:text-custom-text-100 group-hover:bg-custom-background-80 border-custom-border-100 w-full"
className="flex gap-1.5 items-center pl-7 py-2.5 text-sm sticky left-0 z-[1] text-custom-text-200 bg-custom-background-100 group-hover:text-custom-text-100 group-hover:bg-custom-background-80 border-custom-border-200 w-full"
type="button"
>
<PlusIcon className="h-4 w-4" />
@ -112,7 +112,7 @@ export const SpreadsheetView: React.FC<Props> = ({
</button>
}
position="left"
menuItemsClassName="left-5 !w-36"
optionsClassName="left-5 !w-36"
noBorder
>
<CustomMenu.MenuItem

View File

@ -81,7 +81,7 @@ export const ColorPickerInput: React.FC<Props> = ({ name, watch, setValue, error
>
{watch(name) && watch(name) !== "" ? (
<span
className="h-4 w-4 rounded border border-custom-border-100"
className="h-4 w-4 rounded border border-custom-border-200"
style={{
backgroundColor: `${watch(name)}`,
}}

View File

@ -21,11 +21,11 @@ type Props = {
};
const defaultValues: ICustomTheme = {
background: "#fff7f7",
text: "#ffc9c9",
primary: "#fe5050",
sidebarBackground: "#ffffff",
sidebarText: "#000000",
background: "#0d101b",
text: "#c5c5c5",
primary: "#3f76ff",
sidebarBackground: "#0d101b",
sidebarText: "#c5c5c5",
darkPalette: false,
palette: "",
};

View File

@ -71,9 +71,8 @@ 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(user.theme);
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
} else {
if (customThemeSelectorOptions) setCustomThemeSelectorOptions(false);
@ -81,9 +80,11 @@ export const ThemeSwitch: React.FC<Props> = ({
for (let i = 10; i <= 900; i >= 100 ? (i += 100) : (i += 10)) {
document.documentElement.style.removeProperty(`--color-background-${i}`);
document.documentElement.style.removeProperty(`--color-text-${i}`);
document.documentElement.style.removeProperty(`--color-border-${i}`);
document.documentElement.style.removeProperty(`--color-primary-${i}`);
document.documentElement.style.removeProperty(`--color-sidebar-background-${i}`);
document.documentElement.style.removeProperty(`--color-sidebar-text-${i}`);
document.documentElement.style.removeProperty(`--color-sidebar-border-${i}`);
}
}
setTheme(value);

View File

@ -209,8 +209,8 @@ export const ActiveCycleDetails: React.FC = () => {
}));
return (
<div className="grid-row-2 grid rounded-[10px] shadow divide-y bg-custom-background-100 border border-custom-border-100">
<div className="grid grid-cols-1 divide-y border-custom-border-100 lg:divide-y-0 lg:divide-x lg:grid-cols-3">
<div className="grid-row-2 grid rounded-[10px] shadow divide-y bg-custom-background-100 border border-custom-border-200">
<div className="grid grid-cols-1 divide-y border-custom-border-200 lg:divide-y-0 lg:divide-x lg:grid-cols-3">
<div className="flex flex-col text-xs">
<div className="h-full w-full">
<div className="flex h-60 flex-col gap-5 justify-between rounded-b-[10px] p-4">
@ -362,8 +362,8 @@ export const ActiveCycleDetails: React.FC = () => {
</div>
</div>
</div>
<div className="grid col-span-2 grid-cols-1 divide-y border-custom-border-100 md:divide-y-0 md:divide-x md:grid-cols-2">
<div className="flex h-60 flex-col border-custom-border-100">
<div className="grid col-span-2 grid-cols-1 divide-y border-custom-border-200 md:divide-y-0 md:divide-x md:grid-cols-2">
<div className="flex h-60 flex-col border-custom-border-200">
<div className="flex h-full w-full flex-col text-custom-text-200 p-4">
<div className="flex w-full items-center gap-2 py-1">
<span>Progress</span>
@ -391,12 +391,12 @@ export const ActiveCycleDetails: React.FC = () => {
</div>
</div>
</div>
<div className="border-custom-border-100 h-60 overflow-y-scroll">
<div className="border-custom-border-200 h-60 overflow-y-scroll">
<ActiveCycleProgressStats cycle={cycle} />
</div>
</div>
</div>
<div className="grid grid-cols-1 divide-y border-custom-border-100 lg:divide-y-0 lg:divide-x lg:grid-cols-2">
<div className="grid grid-cols-1 divide-y border-custom-border-200 lg:divide-y-0 lg:divide-x lg:grid-cols-2">
<div className="flex flex-col justify-between p-4">
<div>
<div className="text-custom-primary">High Priority Issues</div>
@ -406,7 +406,7 @@ export const ActiveCycleDetails: React.FC = () => {
issues.map((issue) => (
<div
key={issue.id}
className="flex flex-wrap rounded-md items-center justify-between gap-2 border border-custom-border-100 bg-custom-background-90 px-3 py-1.5"
className="flex flex-wrap rounded-md items-center justify-between gap-2 border border-custom-border-200 bg-custom-background-90 px-3 py-1.5"
>
<div className="flex flex-col gap-1">
<div>
@ -444,7 +444,7 @@ export const ActiveCycleDetails: React.FC = () => {
{issue.label_details.map((label) => (
<span
key={label.id}
className="group flex items-center gap-1 rounded-2xl border border-custom-border-100 px-2 py-0.5 text-xs text-custom-text-200"
className="group flex items-center gap-1 rounded-2xl border border-custom-border-200 px-2 py-0.5 text-xs text-custom-text-200"
>
<span
className="h-1.5 w-1.5 rounded-full"
@ -517,7 +517,7 @@ export const ActiveCycleDetails: React.FC = () => {
</div>
)}
</div>
<div className="flex flex-col justify-between border-custom-border-100 p-4">
<div className="flex flex-col justify-between border-custom-border-200 p-4">
<div className="flex items-start justify-between gap-4 py-1.5 text-xs">
<div className="flex items-center gap-3 text-custom-text-100">
<div className="flex items-center justify-center gap-1">

View File

@ -52,7 +52,7 @@ export const ActiveCycleProgressStats: React.FC<Props> = ({ cycle }) => {
>
<Tab
className={({ selected }) =>
`px-3 py-1 text-custom-text-100 rounded-3xl border border-custom-border-100 ${
`px-3 py-1 text-custom-text-100 rounded-3xl border border-custom-border-200 ${
selected ? " bg-custom-primary text-white" : " hover:bg-custom-background-80"
}`
}
@ -61,7 +61,7 @@ export const ActiveCycleProgressStats: React.FC<Props> = ({ cycle }) => {
</Tab>
<Tab
className={({ selected }) =>
`px-3 py-1 text-custom-text-100 rounded-3xl border border-custom-border-100 ${
`px-3 py-1 text-custom-text-100 rounded-3xl border border-custom-border-200 ${
selected ? " bg-custom-primary text-white" : " hover:bg-custom-background-80"
}`
}
@ -103,7 +103,7 @@ export const ActiveCycleProgressStats: React.FC<Props> = ({ cycle }) => {
key={`unassigned-${index}`}
title={
<div className="flex items-center gap-2">
<div className="h-5 w-5 rounded-full border-2 border-custom-border-100 bg-custom-background-80">
<div className="h-5 w-5 rounded-full border-2 border-custom-border-200 bg-custom-background-80">
<img
src="/user.png"
height="100%"

View File

@ -22,7 +22,7 @@ export const CyclesListGanttChartView: FC<Props> = ({ cycles }) => {
<div className="relative flex w-full h-full items-center p-1 overflow-hidden gap-1">
<div
className="rounded-sm flex-shrink-0 w-[10px] h-[10px] flex justify-center items-center"
style={{ backgroundColor: "#858e96" }}
style={{ backgroundColor: "rgb(var(--color-primary-100))" }}
/>
<div className="text-custom-text-100 text-sm">{data?.name}</div>
</div>
@ -32,7 +32,10 @@ export const CyclesListGanttChartView: FC<Props> = ({ cycles }) => {
const GanttBlockView = ({ data }: { data: ICycle }) => (
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${data?.id}`}>
<a className="relative flex items-center w-full h-full overflow-hidden shadow-sm">
<div className="flex-shrink-0 w-[4px] h-full" style={{ backgroundColor: "#858e96" }} />
<div
className="flex-shrink-0 w-[4px] h-full"
style={{ backgroundColor: "rgb(var(--color-primary-100))" }}
/>
<Tooltip tooltipContent={data?.name} className={`z-[999999]`}>
<div className="text-custom-text-100 text-[15px] whitespace-nowrap py-[4px] px-2.5 overflow-hidden w-full">
{data?.name}

View File

@ -173,10 +173,10 @@ export const CyclesView: React.FC<Props> = ({ cycles, viewType }) => {
{cycles ? (
cycles.length > 0 ? (
viewType === "list" ? (
<div className="divide-y divide-custom-border-100">
<div className="divide-y divide-custom-border-200">
{cycles.map((cycle) => (
<div className="hover:bg-custom-background-80">
<div className="flex flex-col border-custom-border-100">
<div className="flex flex-col border-custom-border-200">
<SingleCycleList
key={cycle.id}
cycle={cycle}

View File

@ -124,7 +124,7 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
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-100 bg-custom-background-100 text-left shadow-xl transition-all sm:my-8 sm:w-[40rem]">
<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-[40rem]">
<div className="px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div className="sm:flex sm:items-start">
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-500/20 sm:mx-0 sm:h-10 sm:w-10">

View File

@ -107,7 +107,7 @@ export const CycleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, stat
</div>
</div>
</div>
<div className="-mx-5 mt-5 flex justify-end gap-2 border-t border-custom-border-100 px-5 pt-5">
<div className="-mx-5 mt-5 flex justify-end gap-2 border-t border-custom-border-200 px-5 pt-5">
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
<PrimaryButton type="submit" loading={isSubmitting}>
{status

View File

@ -26,7 +26,7 @@ export const CycleIssuesGanttChartView: FC<Props> = ({}) => {
<div className="relative flex w-full h-full items-center p-1 overflow-hidden gap-1">
<div
className="rounded-sm flex-shrink-0 w-[10px] h-[10px] flex justify-center items-center"
style={{ backgroundColor: data?.state_detail?.color || "#858e96" }}
style={{ backgroundColor: data?.state_detail?.color || "rgb(var(--color-primary-100))" }}
/>
<div className="text-custom-text-100 text-sm">{data?.name}</div>
</div>
@ -38,7 +38,7 @@ export const CycleIssuesGanttChartView: FC<Props> = ({}) => {
<a className="relative flex items-center w-full h-full overflow-hidden shadow-sm">
<div
className="flex-shrink-0 w-[4px] h-full"
style={{ backgroundColor: data?.state_detail?.color || "#858e96" }}
style={{ backgroundColor: data?.state_detail?.color || "rgb(var(--color-primary-100))" }}
/>
<Tooltip tooltipContent={data?.name} className={`z-[999999]`}>
<div className="text-custom-text-100 text-[15px] whitespace-nowrap py-[4px] px-2.5 overflow-hidden w-full">

View File

@ -247,7 +247,7 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform rounded-lg border border-custom-border-100 bg-custom-background-100 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<Dialog.Panel className="relative transform rounded-lg border border-custom-border-200 bg-custom-background-100 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<CycleForm
handleFormSubmit={handleFormSubmit}
handleClose={handleClose}

View File

@ -66,7 +66,7 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({
{({ open }) => (
<>
<Listbox.Button
className={`flex cursor-pointer items-center gap-1 rounded-md border border-custom-border-100 px-2 py-1 text-xs shadow-sm duration-300 hover:bg-custom-background-90 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500`}
className={`flex cursor-pointer items-center gap-1 rounded-md border border-custom-border-200 px-2 py-1 text-xs shadow-sm duration-300 hover:bg-custom-background-90 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500`}
>
<CyclesIcon className="h-3 w-3 text-custom-text-200" />
<div className="flex items-center gap-2 truncate">

View File

@ -292,14 +292,14 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div
className={`fixed top-[66px] ${
isOpen ? "right-0" : "-right-[24rem]"
} h-full w-[24rem] overflow-y-auto border-l border-custom-border-100 bg-custom-sidebar-background-100 pt-5 pb-10 duration-300`}
} h-full w-[24rem] overflow-y-auto border-l border-custom-border-200 bg-custom-sidebar-background-100 pt-5 pb-10 duration-300`}
>
{cycle ? (
<>
<div className="flex flex-col items-start justify-center">
<div className="flex gap-2.5 px-5 text-sm">
<div className="flex items-center">
<span className="flex items-center rounded border-[0.5px] border-custom-border-100 bg-custom-background-90 px-2 py-1 text-center text-xs capitalize">
<span className="flex items-center rounded border-[0.5px] border-custom-border-200 bg-custom-background-90 px-2 py-1 text-center text-xs capitalize">
{capitalizeFirstLetter(cycleStatus)}
</span>
</div>
@ -309,7 +309,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<>
<Popover.Button
disabled={isCompleted ?? false}
className={`group flex h-full items-center gap-2 whitespace-nowrap rounded border-[0.5px] border-custom-border-100 bg-custom-background-90 px-2 py-1 text-xs ${
className={`group flex h-full items-center gap-2 whitespace-nowrap rounded border-[0.5px] border-custom-border-200 bg-custom-background-90 px-2 py-1 text-xs ${
cycle.start_date ? "" : "text-custom-text-200"
}`}
>
@ -359,7 +359,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<>
<Popover.Button
disabled={isCompleted ?? false}
className={`group flex items-center gap-2 whitespace-nowrap rounded border-[0.5px] border-custom-border-100 bg-custom-background-90 px-2 py-1 text-xs ${
className={`group flex items-center gap-2 whitespace-nowrap rounded border-[0.5px] border-custom-border-200 bg-custom-background-90 px-2 py-1 text-xs ${
cycle.end_date ? "" : "text-custom-text-200"
}`}
>
@ -477,7 +477,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</div>
</div>
</div>
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-100 p-6">
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-200 p-6">
<Disclosure defaultOpen>
{({ open }) => (
<div
@ -561,7 +561,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
)}
</Disclosure>
</div>
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-100 p-6">
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-200 p-6">
<Disclosure defaultOpen>
{({ open }) => (
<div

View File

@ -128,7 +128,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
return (
<div>
<div className="flex flex-col rounded-[10px] bg-custom-background-100 border border-custom-border-100 text-xs shadow">
<div className="flex flex-col rounded-[10px] bg-custom-background-100 border border-custom-border-200 text-xs shadow">
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`}>
<a className="w-full">
<div className="flex h-full flex-col gap-4 rounded-b-[10px] p-4">
@ -321,7 +321,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
<Disclosure>
{({ open }) => (
<div
className={`flex h-full w-full flex-col rounded-b-[10px] border-t border-custom-border-100 bg-custom-background-80 text-custom-text-200 ${
className={`flex h-full w-full flex-col rounded-b-[10px] border-t border-custom-border-200 bg-custom-background-80 text-custom-text-200 ${
open ? "" : "flex-row"
}`}
>

View File

@ -119,7 +119,7 @@ export const TransferIssuesModal: React.FC<Props> = ({ isOpen, handleClose }) =>
<XMarkIcon className="h-4 w-4" />
</button>
</div>
<div className="flex items-center gap-2 border-b border-custom-border-100 px-5 pb-3">
<div className="flex items-center gap-2 border-b border-custom-border-200 px-5 pb-3">
<MagnifyingGlassIcon className="h-4 w-4 text-custom-text-200" />
<input
className="bg-custom-background-90 outline-none"

View File

@ -63,8 +63,8 @@ const EmojiIconPicker: React.FC<Props> = ({ label, value, onChange, onIconColorC
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Popover.Panel className="absolute z-10 mt-2 w-[250px] rounded-[4px] border border-custom-border-100 bg-custom-background-80 shadow-lg">
<div className="h-[230px] w-[250px] overflow-auto rounded-[4px] border border-custom-border-100 bg-custom-background-80 p-2 shadow-xl">
<Popover.Panel className="absolute z-10 mt-2 w-[250px] rounded-[4px] border border-custom-border-200 bg-custom-background-80 shadow-lg">
<div className="h-[230px] w-[250px] overflow-auto rounded-[4px] border border-custom-border-200 bg-custom-background-80 p-2 shadow-xl">
<Tab.Group as="div" className="flex h-full w-full flex-col">
<Tab.List className="flex-0 -mx-2 flex justify-around gap-1 p-1">
{tabOptions.map((tab) => (
@ -107,7 +107,7 @@ const EmojiIconPicker: React.FC<Props> = ({ label, value, onChange, onIconColorC
</div>
</div>
)}
<hr className="mb-2 h-[1px] w-full border-custom-border-100" />
<hr className="mb-2 h-[1px] w-full border-custom-border-200" />
<div>
<div className="grid grid-cols-8 gap-x-2 gap-y-3">
{emojis.map((emoji) => (
@ -173,7 +173,7 @@ const EmojiIconPicker: React.FC<Props> = ({ label, value, onChange, onIconColorC
/>
</div>
</div>
<hr className="mb-1 h-[1px] w-full border-custom-border-100" />
<hr className="mb-1 h-[1px] w-full border-custom-border-200" />
<div className="mt-1 ml-1 grid grid-cols-8 gap-x-2 gap-y-3">
{icons.material_rounded.map((icon, index) => (
<button

View File

@ -254,7 +254,7 @@ export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data,
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform rounded-lg border border-custom-border-100 bg-custom-background-100 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<Dialog.Panel className="relative transform rounded-lg border border-custom-border-200 bg-custom-background-100 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="space-y-3">
<div className="text-lg font-medium leading-6">

View File

@ -60,7 +60,7 @@ export const DeleteEstimateModal: React.FC<Props> = ({
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg border border-custom-border-100 bg-custom-background-100 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl">
<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-2xl">
<div className="flex flex-col gap-6 p-6">
<div className="flex w-full items-center justify-start gap-6">
<span className="place-items-center rounded-full bg-red-500/20 p-4">

View File

@ -44,7 +44,7 @@ export const GanttChartBlocks: FC<{
</div>
<div
className="rounded shadow-sm bg-custom-background-100 overflow-hidden relative flex items-center h-[34px] border border-custom-border-100"
className="rounded shadow-sm bg-custom-background-100 overflow-hidden relative flex items-center h-[34px] border border-custom-border-200"
style={{
width: `${block?.position?.width}px`,
}}
@ -68,7 +68,7 @@ export const GanttChartBlocks: FC<{
</div>
{/* sidebar */}
{/* <div className="fixed top-0 bottom-0 w-[300px] flex-shrink-0 divide-y divide-custom-border-100 border-r border-custom-border-100 overflow-y-auto">
{/* <div className="fixed top-0 bottom-0 w-[300px] flex-shrink-0 divide-y divide-custom-border-200 border-r border-custom-border-200 overflow-y-auto">
{blocks &&
blocks.length > 0 &&
blocks.map((block: any, _idx: number) => (

View File

@ -7,18 +7,18 @@ export const BiWeekChartView: FC<any> = () => {
return (
<>
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-100">
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-200">
{renderView &&
renderView.length > 0 &&
renderView.map((_itemRoot: any, _idxRoot: any) => (
<div key={`title-${_idxRoot}`} className="relative flex flex-col">
<div className="relative border-b border-custom-border-100">
<div className="relative border-b border-custom-border-200">
<div className="sticky left-0 inline-flex whitespace-nowrap px-2 py-1 text-sm font-medium capitalize">
{_itemRoot?.title}
</div>
</div>
<div className="flex h-full w-full divide-x divide-custom-border-100">
<div className="flex h-full w-full divide-x divide-custom-border-200">
{_itemRoot.children &&
_itemRoot.children.length > 0 &&
_itemRoot.children.map((_item: any, _idx: any) => (
@ -29,7 +29,7 @@ export const BiWeekChartView: FC<any> = () => {
>
<div
className={`flex-shrink-0 border-b py-1 text-center text-sm capitalize font-medium ${
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-100`
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-200`
}`}
>
<div>{_item.title}</div>

View File

@ -7,18 +7,18 @@ export const DayChartView: FC<any> = () => {
return (
<>
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-100">
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-200">
{renderView &&
renderView.length > 0 &&
renderView.map((_itemRoot: any, _idxRoot: any) => (
<div key={`title-${_idxRoot}`} className="relative flex flex-col">
<div className="relative border-b border-custom-border-100">
<div className="relative border-b border-custom-border-200">
<div className="sticky left-0 inline-flex whitespace-nowrap px-2 py-1 text-sm font-medium capitalize">
{_itemRoot?.title}
</div>
</div>
<div className="flex h-full w-full divide-x divide-custom-border-100">
<div className="flex h-full w-full divide-x divide-custom-border-200">
{_itemRoot.children &&
_itemRoot.children.length > 0 &&
_itemRoot.children.map((_item: any, _idx: any) => (
@ -29,7 +29,7 @@ export const DayChartView: FC<any> = () => {
>
<div
className={`flex-shrink-0 border-b py-1 text-center text-sm capitalize font-medium ${
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-100`
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-200`
}`}
>
<div>{_item.title}</div>

View File

@ -7,18 +7,18 @@ export const HourChartView: FC<any> = () => {
return (
<>
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-100">
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-200">
{renderView &&
renderView.length > 0 &&
renderView.map((_itemRoot: any, _idxRoot: any) => (
<div key={`title-${_idxRoot}`} className="relative flex flex-col">
<div className="relative border-b border-custom-border-100">
<div className="relative border-b border-custom-border-200">
<div className="sticky left-0 inline-flex whitespace-nowrap px-2 py-1 text-sm font-medium capitalize">
{_itemRoot?.title}
</div>
</div>
<div className="flex h-full w-full divide-x divide-custom-border-100">
<div className="flex h-full w-full divide-x divide-custom-border-200">
{_itemRoot.children &&
_itemRoot.children.length > 0 &&
_itemRoot.children.map((_item: any, _idx: any) => (
@ -29,7 +29,7 @@ export const HourChartView: FC<any> = () => {
>
<div
className={`flex-shrink-0 border-b py-1 text-center text-sm capitalize font-medium ${
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-100`
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-200`
}`}
>
<div>{_item.title}</div>

View File

@ -219,11 +219,11 @@ export const ChartViewRoot: FC<ChartViewRootProps> = ({
? `fixed top-0 bottom-0 left-0 right-0 z-[999999] bg-custom-background-100`
: `relative`
} ${
border ? `border border-custom-border-100` : ``
border ? `border border-custom-border-200` : ``
} flex h-full flex-col rounded-sm select-none bg-custom-background-100 shadow`}
>
{/* chart title */}
{/* <div className="flex w-full flex-shrink-0 flex-wrap items-center gap-5 gap-y-3 whitespace-nowrap p-2 border-b border-custom-border-100">
{/* <div className="flex w-full flex-shrink-0 flex-wrap items-center gap-5 gap-y-3 whitespace-nowrap p-2 border-b border-custom-border-200">
{title && (
<div className="text-lg font-medium flex gap-2 items-center">
<div>{title}</div>
@ -244,7 +244,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = ({
{/* chart header */}
<div className="flex w-full flex-shrink-0 flex-wrap items-center gap-5 gap-y-3 whitespace-nowrap p-2">
{/* <div
className="transition-all border border-custom-border-100 w-[30px] h-[30px] flex justify-center items-center cursor-pointer rounded-sm hover:bg-custom-background-80"
className="transition-all border border-custom-border-200 w-[30px] h-[30px] flex justify-center items-center cursor-pointer rounded-sm hover:bg-custom-background-80"
onClick={() => setBlocksSidebarView(() => !blocksSidebarView)}
>
{blocksSidebarView ? (
@ -279,7 +279,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = ({
allViews.map((_chatView: any, _idx: any) => (
<div
key={_chatView?.key}
className={`cursor-pointer rounded-sm border border-custom-border-100 p-1 px-2 text-xs ${
className={`cursor-pointer rounded-sm border border-custom-border-200 p-1 px-2 text-xs ${
currentView === _chatView?.key
? `bg-custom-background-80`
: `hover:bg-custom-background-90`
@ -293,7 +293,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = ({
<div className="flex items-center gap-1">
<div
className={`cursor-pointer rounded-sm border border-custom-border-100 p-1 px-2 text-xs hover:bg-custom-background-80`}
className={`cursor-pointer rounded-sm border border-custom-border-200 p-1 px-2 text-xs hover:bg-custom-background-80`}
onClick={handleToday}
>
Today
@ -301,7 +301,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = ({
</div>
<div
className="transition-all border border-custom-border-100 w-[30px] h-[30px] flex justify-center items-center cursor-pointer rounded-sm hover:bg-custom-background-80"
className="transition-all border border-custom-border-200 w-[30px] h-[30px] flex justify-center items-center cursor-pointer rounded-sm hover:bg-custom-background-80"
onClick={() => setFullScreenMode(() => !fullScreenMode)}
>
{fullScreenMode ? (
@ -313,7 +313,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = ({
</div>
{/* content */}
<div className="relative flex h-full w-full flex-1 overflow-hidden border-t border-custom-border-100">
<div className="relative flex h-full w-full flex-1 overflow-hidden border-t border-custom-border-200">
<div
className="relative flex h-full w-full flex-1 flex-col overflow-hidden overflow-x-auto"
id="scroll-container"

View File

@ -7,18 +7,18 @@ export const MonthChartView: FC<any> = () => {
return (
<>
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-100">
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-200">
{renderView &&
renderView.length > 0 &&
renderView.map((_itemRoot: any, _idxRoot: any) => (
<div key={`title-${_idxRoot}`} className="relative flex flex-col">
<div className="relative border-b border-custom-border-100">
<div className="relative border-b border-custom-border-200">
<div className="sticky left-0 inline-flex whitespace-nowrap px-2 py-1 text-sm font-medium capitalize">
{_itemRoot?.title}
</div>
</div>
<div className="flex h-full w-full divide-x divide-custom-border-100">
<div className="flex h-full w-full divide-x divide-custom-border-200">
{_itemRoot.children &&
_itemRoot.children.length > 0 &&
_itemRoot.children.map((_item: any, _idx: any) => (
@ -29,7 +29,7 @@ export const MonthChartView: FC<any> = () => {
>
<div
className={`flex-shrink-0 border-b py-1 text-center text-sm capitalize font-medium ${
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-100`
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-200`
}`}
>
<div>{_item.title}</div>

View File

@ -7,18 +7,18 @@ export const QuarterChartView: FC<any> = () => {
return (
<>
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-100">
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-200">
{renderView &&
renderView.length > 0 &&
renderView.map((_itemRoot: any, _idxRoot: any) => (
<div key={`title-${_idxRoot}`} className="relative flex flex-col">
<div className="relative border-b border-custom-border-100">
<div className="relative border-b border-custom-border-200">
<div className="sticky left-0 inline-flex whitespace-nowrap px-2 py-1 text-sm font-medium capitalize">
{_itemRoot?.title}
</div>
</div>
<div className="flex h-full w-full divide-x divide-custom-border-100">
<div className="flex h-full w-full divide-x divide-custom-border-200">
{_itemRoot.children &&
_itemRoot.children.length > 0 &&
_itemRoot.children.map((_item: any, _idx: any) => (
@ -29,7 +29,7 @@ export const QuarterChartView: FC<any> = () => {
>
<div
className={`flex-shrink-0 border-b py-1 text-center text-sm capitalize font-medium ${
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-100`
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-200`
}`}
>
<div>{_item.title}</div>

View File

@ -7,18 +7,18 @@ export const WeekChartView: FC<any> = () => {
return (
<>
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-100">
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-200">
{renderView &&
renderView.length > 0 &&
renderView.map((_itemRoot: any, _idxRoot: any) => (
<div key={`title-${_idxRoot}`} className="relative flex flex-col">
<div className="relative border-b border-custom-border-100">
<div className="relative border-b border-custom-border-200">
<div className="sticky left-0 inline-flex whitespace-nowrap px-2 py-1 text-sm font-medium capitalize">
{_itemRoot?.title}
</div>
</div>
<div className="flex h-full w-full divide-x divide-custom-border-100">
<div className="flex h-full w-full divide-x divide-custom-border-200">
{_itemRoot.children &&
_itemRoot.children.length > 0 &&
_itemRoot.children.map((_item: any, _idx: any) => (
@ -29,7 +29,7 @@ export const WeekChartView: FC<any> = () => {
>
<div
className={`flex-shrink-0 border-b py-1 text-center text-sm capitalize font-medium ${
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-100`
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-200`
}`}
>
<div>{_item.title}</div>

View File

@ -7,18 +7,18 @@ export const YearChartView: FC<any> = () => {
return (
<>
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-100">
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-200">
{renderView &&
renderView.length > 0 &&
renderView.map((_itemRoot: any, _idxRoot: any) => (
<div key={`title-${_idxRoot}`} className="relative flex flex-col">
<div className="relative border-b border-custom-border-100">
<div className="relative border-b border-custom-border-200">
<div className="sticky left-0 inline-flex whitespace-nowrap px-2 py-1 text-sm font-medium capitalize">
{_itemRoot?.title}
</div>
</div>
<div className="flex h-full w-full divide-x divide-custom-border-100">
<div className="flex h-full w-full divide-x divide-custom-border-200">
{_itemRoot.children &&
_itemRoot.children.length > 0 &&
_itemRoot.children.map((_item: any, _idx: any) => (
@ -29,7 +29,7 @@ export const YearChartView: FC<any> = () => {
>
<div
className={`flex-shrink-0 border-b py-1 text-center text-sm capitalize font-medium ${
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-100`
_item?.today ? `text-red-500 border-red-500` : `border-custom-border-200`
}`}
>
<div>{_item.title}</div>

View File

@ -0,0 +1,19 @@
import React from "react";
import type { Props } from "./types";
export const ArchiveIcon: React.FC<Props> = ({ width = "24", height = "24", className, color }) => (
<svg
width={width}
height={height}
className={className}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.75 19.5C5.41667 19.5 5.125 19.375 4.875 19.125C4.625 18.875 4.5 18.5833 4.5 18.25V7.35417C4.5 7.14583 4.52083 6.96875 4.5625 6.82292C4.60417 6.67708 4.68056 6.54167 4.79167 6.41667L5.95833 4.83333C6.06944 4.70833 6.19792 4.62153 6.34375 4.57292C6.48958 4.52431 6.6624 4.5 6.86221 4.5H17.1378C17.3376 4.5 17.5069 4.52431 17.6458 4.57292C17.7847 4.62153 17.9097 4.70833 18.0208 4.83333L19.2083 6.41667C19.3194 6.54167 19.3958 6.67708 19.4375 6.82292C19.4792 6.96875 19.5 7.14583 19.5 7.35417V18.25C19.5 18.5833 19.375 18.875 19.125 19.125C18.875 19.375 18.5833 19.5 18.25 19.5H5.75ZM6.10417 6.70833H17.875L17.1165 5.75H6.85417L6.10417 6.70833ZM5.75 7.95833V18.25H18.25V7.95833H5.75ZM12 16.375L15.25 13.125L14.4167 12.2917L12.625 14.0833V9.89583H11.375V14.0833L9.58333 12.2917L8.75 13.125L12 16.375Z"
fill={color ? color : "currentColor"}
/>
</svg>
);

View File

@ -0,0 +1,24 @@
import React from "react";
import type { Props } from "./types";
export const BellNotificationIcon: React.FC<Props> = ({
width = "24",
height = "24",
color = "rgb(var(--color-text-200))",
className,
}) => (
<svg
width={width}
height={height}
className={className}
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.35425 17.4154C4.15946 17.4154 3.99618 17.3491 3.8644 17.2166C3.73263 17.084 3.66675 16.9198 3.66675 16.7239C3.66675 16.5279 3.73263 16.365 3.8644 16.2352C3.99618 16.1053 4.15946 16.0404 4.35425 16.0404H5.59175V9.02786C5.59175 7.77509 5.96987 6.64071 6.72612 5.62474C7.48237 4.60877 8.47925 3.97092 9.71675 3.7112V3.04661C9.71675 2.69523 9.84099 2.40495 10.0895 2.17578C10.338 1.94661 10.6397 1.83203 10.9947 1.83203C11.3497 1.83203 11.6532 1.94661 11.9053 2.17578C12.1574 2.40495 12.2834 2.69523 12.2834 3.04661V3.7112C13.5209 3.97092 14.5216 4.60877 15.2855 5.62474C16.0494 6.64071 16.4313 7.77509 16.4313 9.02786V16.0404H17.6459C17.8407 16.0404 18.004 16.1066 18.1358 16.2392C18.2675 16.3717 18.3334 16.536 18.3334 16.7319C18.3334 16.9278 18.2675 17.0907 18.1358 17.2206C18.004 17.3504 17.8407 17.4154 17.6459 17.4154H4.35425ZM11.0001 20.1654C10.5112 20.1654 10.0834 19.9859 9.71675 19.6268C9.35008 19.2678 9.16675 18.8362 9.16675 18.332H12.8334C12.8334 18.8362 12.6539 19.2678 12.2949 19.6268C11.9358 19.9859 11.5042 20.1654 11.0001 20.1654ZM6.96675 16.0404H15.0563V9.02786C15.0563 7.88203 14.6706 6.91571 13.899 6.12891C13.1275 5.3421 12.1727 4.9487 11.0345 4.9487C9.89626 4.9487 8.93376 5.3421 8.14696 6.12891C7.36015 6.91571 6.96675 7.88203 6.96675 9.02786V16.0404Z"
fill={color}
/>
</svg>
);

View File

@ -0,0 +1,19 @@
import React from "react";
import type { Props } from "./types";
export const ClockIcon: React.FC<Props> = ({ width = "24", height = "24", className, color }) => (
<svg
width={width}
height={height}
className={className}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.6876 11.7513V8.1888C12.6876 8.00825 12.6286 7.85894 12.5105 7.74088C12.3924 7.62283 12.2431 7.5638 12.0626 7.5638C11.882 7.5638 11.7327 7.62283 11.6147 7.74088C11.4966 7.85894 11.4376 8.00825 11.4376 8.1888V12.0013C11.4376 12.0846 11.4515 12.161 11.4792 12.2305C11.507 12.2999 11.5487 12.3694 11.6042 12.4388L14.6042 15.543C14.7292 15.6819 14.8855 15.7478 15.073 15.7409C15.2605 15.7339 15.4167 15.668 15.5417 15.543C15.6667 15.418 15.7292 15.2652 15.7292 15.0846C15.7292 14.9041 15.6667 14.7513 15.5417 14.6263L12.6876 11.7513ZM12.0001 20.3346C10.8612 20.3346 9.7848 20.1159 8.77091 19.6784C7.75703 19.2409 6.87161 18.6437 6.11466 17.8867C5.35772 17.1298 4.7605 16.2444 4.323 15.2305C3.8855 14.2166 3.66675 13.1402 3.66675 12.0013C3.66675 10.8624 3.8855 9.78602 4.323 8.77213C4.7605 7.75825 5.35772 6.87283 6.11466 6.11589C6.87161 5.35894 7.75703 4.76172 8.77091 4.32422C9.7848 3.88672 10.8612 3.66797 12.0001 3.66797C13.139 3.66797 14.2154 3.88672 15.2292 4.32422C16.2431 4.76172 17.1286 5.35894 17.8855 6.11589C18.6424 6.87283 19.2397 7.75825 19.6772 8.77213C20.1147 9.78602 20.3334 10.8624 20.3334 12.0013C20.3334 13.1402 20.1147 14.2166 19.6772 15.2305C19.2397 16.2444 18.6424 17.1298 17.8855 17.8867C17.1286 18.6437 16.2431 19.2409 15.2292 19.6784C14.2154 20.1159 13.139 20.3346 12.0001 20.3346ZM12.0001 19.0846C13.9445 19.0846 15.6112 18.3902 17.0001 17.0013C18.389 15.6124 19.0834 13.9457 19.0834 12.0013C19.0834 10.0569 18.389 8.39019 17.0001 7.0013C15.6112 5.61241 13.9445 4.91797 12.0001 4.91797C10.0556 4.91797 8.38897 5.61241 7.00008 7.0013C5.61119 8.39019 4.91675 10.0569 4.91675 12.0013C4.91675 13.9457 5.61119 15.6124 7.00008 17.0013C8.38897 18.3902 10.0556 19.0846 12.0001 19.0846Z"
fill={color ? color : "currentColor"}
/>
</svg>
);

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