chore: pulled latest changes from develop

This commit is contained in:
kunal_17 2023-04-21 22:24:26 +05:30
commit dee8a1dfa0
241 changed files with 3688 additions and 1508 deletions

View File

@ -3,15 +3,19 @@ DJANGO_SETTINGS_MODULE="plane.settings.production"
DATABASE_URL=postgres://plane:xyzzyspoon@db:5432/plane DATABASE_URL=postgres://plane:xyzzyspoon@db:5432/plane
# Cache # Cache
REDIS_URL=redis://redis:6379/ REDIS_URL=redis://redis:6379/
# SMPT # SMTP
EMAIL_HOST="" EMAIL_HOST=""
EMAIL_HOST_USER="" EMAIL_HOST_USER=""
EMAIL_HOST_PASSWORD="" EMAIL_HOST_PASSWORD=""
EMAIL_PORT="587"
EMAIL_USE_TLS="1"
EMAIL_FROM="Team Plane <team@mailer.plane.so>"
# AWS # AWS
AWS_REGION="" AWS_REGION=""
AWS_ACCESS_KEY_ID="" AWS_ACCESS_KEY_ID=""
AWS_SECRET_ACCESS_KEY="" AWS_SECRET_ACCESS_KEY=""
AWS_S3_BUCKET_NAME="" AWS_S3_BUCKET_NAME=""
AWS_S3_ENDPOINT_URL=""
# FE # FE
WEB_URL="localhost/" WEB_URL="localhost/"
# OAUTH # OAUTH

View File

@ -79,6 +79,7 @@ from plane.api.views import (
## End Issues ## End Issues
# States # States
StateViewSet, StateViewSet,
StateDeleteIssueCheckEndpoint,
## End States ## End States
# Estimates # Estimates
EstimateViewSet, EstimateViewSet,
@ -146,6 +147,9 @@ from plane.api.views import (
# Gpt # Gpt
GPTIntegrationEndpoint, GPTIntegrationEndpoint,
## End Gpt ## End Gpt
# Release Notes
ReleaseNotesEndpoint,
## End Release Notes
) )
@ -506,6 +510,11 @@ urlpatterns = [
), ),
name="project-state", name="project-state",
), ),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/states/<uuid:pk>/",
StateDeleteIssueCheckEndpoint.as_view(),
name="state-delete-check",
),
# End States ## # End States ##
# States # States
path( path(
@ -557,6 +566,11 @@ urlpatterns = [
ProjectEstimatePointEndpoint.as_view(), ProjectEstimatePointEndpoint.as_view(),
name="project-estimate-points", name="project-estimate-points",
), ),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/bulk-estimate-points/",
BulkEstimatePointEndpoint.as_view(),
name="bulk-create-estimate-points",
),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/<uuid:estimate_id>/bulk-estimate-points/", "workspaces/<str:slug>/projects/<uuid:project_id>/estimates/<uuid:estimate_id>/bulk-estimate-points/",
BulkEstimatePointEndpoint.as_view(), BulkEstimatePointEndpoint.as_view(),
@ -1284,4 +1298,11 @@ urlpatterns = [
name="importer", name="importer",
), ),
## End Gpt ## End Gpt
# Release Notes
path(
"release-notes/",
ReleaseNotesEndpoint.as_view(),
name="release-notes",
),
## End Release Notes
] ]

View File

@ -42,7 +42,7 @@ from .workspace import (
UserWorkspaceDashboardEndpoint, UserWorkspaceDashboardEndpoint,
WorkspaceThemeViewSet, WorkspaceThemeViewSet,
) )
from .state import StateViewSet from .state import StateViewSet, StateDeleteIssueCheckEndpoint
from .shortcut import ShortCutViewSet from .shortcut import ShortCutViewSet
from .view import IssueViewViewSet, ViewIssuesEndpoint, IssueViewFavoriteViewSet from .view import IssueViewViewSet, ViewIssuesEndpoint, IssueViewFavoriteViewSet
from .cycle import ( from .cycle import (
@ -138,3 +138,6 @@ from .estimate import (
ProjectEstimatePointEndpoint, ProjectEstimatePointEndpoint,
BulkEstimatePointEndpoint, BulkEstimatePointEndpoint,
) )
from .release import ReleaseNotesEndpoint

View File

@ -146,11 +146,13 @@ class BulkEstimatePointEndpoint(BaseAPIView):
ProjectEntityPermission, ProjectEntityPermission,
] ]
def post(self, request, slug, project_id, estimate_id): def post(self, request, slug, project_id):
try: try:
estimate = Estimate.objects.get( if not request.data.get("estimate", False):
pk=estimate_id, workspace__slug=slug, project=project_id return Response(
) {"error": "Estimate is required"},
status=status.HTTP_400_BAD_REQUEST,
)
estimate_points = request.data.get("estimate_points", []) estimate_points = request.data.get("estimate_points", [])
@ -160,6 +162,18 @@ class BulkEstimatePointEndpoint(BaseAPIView):
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
estimate_serializer = EstimateSerializer(data=request.data.get("estimate"))
if not estimate_serializer.is_valid():
return Response(
estimate_serializer.errors, status=status.HTTP_400_BAD_REQUEST
)
try:
estimate = estimate_serializer.save(project_id=project_id)
except IntegrityError:
return Response(
{"errror": "Estimate with the name already exists"},
status=status.HTTP_400_BAD_REQUEST,
)
estimate_points = EstimatePoint.objects.bulk_create( estimate_points = EstimatePoint.objects.bulk_create(
[ [
EstimatePoint( EstimatePoint(
@ -178,9 +192,17 @@ class BulkEstimatePointEndpoint(BaseAPIView):
ignore_conflicts=True, ignore_conflicts=True,
) )
serializer = EstimatePointSerializer(estimate_points, many=True) estimate_point_serializer = EstimatePointSerializer(
estimate_points, many=True
)
return Response(serializer.data, status=status.HTTP_200_OK) return Response(
{
"estimate": estimate_serializer.data,
"estimate_points": estimate_point_serializer.data,
},
status=status.HTTP_200_OK,
)
except Estimate.DoesNotExist: except Estimate.DoesNotExist:
return Response( return Response(
{"error": "Estimate does not exist"}, {"error": "Estimate does not exist"},
@ -212,7 +234,6 @@ class BulkEstimatePointEndpoint(BaseAPIView):
estimate_id=estimate_id, estimate_id=estimate_id,
) )
print(estimate_points)
updated_estimate_points = [] updated_estimate_points = []
for estimate_point in estimate_points: for estimate_point in estimate_points:
# Find the data for that estimate point # Find the data for that estimate point
@ -238,7 +259,7 @@ class BulkEstimatePointEndpoint(BaseAPIView):
{"error": "Estimate does not exist"}, status=status.HTTP_400_BAD_REQUEST {"error": "Estimate does not exist"}, status=status.HTTP_400_BAD_REQUEST
) )
except Exception as e: except Exception as e:
print(e) capture_exception(e)
return Response( return Response(
{"error": "Something went wrong please try again later"}, {"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,

View File

@ -1,13 +1,14 @@
# Python imports # Python imports
import json import json
import random import random
from itertools import groupby, chain from itertools import chain
# Django imports # Django imports
from django.db.models import Prefetch, OuterRef, Func, F, Q from django.db.models import Prefetch, OuterRef, Func, F, Q, Count
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views.decorators.gzip import gzip_page from django.views.decorators.gzip import gzip_page
from django.db.models.functions import Coalesce
# Third Party imports # Third Party imports
from rest_framework.response import Response from rest_framework.response import Response
@ -46,6 +47,7 @@ from plane.db.models import (
Label, Label,
IssueLink, IssueLink,
IssueAttachment, IssueAttachment,
State,
) )
from plane.bgtasks.issue_activites_task import issue_activity from plane.bgtasks.issue_activites_task import issue_activity
from plane.utils.grouper import group_results from plane.utils.grouper import group_results
@ -590,10 +592,33 @@ class SubIssuesEndpoint(BaseAPIView):
.prefetch_related("labels") .prefetch_related("labels")
) )
serializer = IssueLiteSerializer(sub_issues, many=True) state_distribution = (
return Response(serializer.data, status=status.HTTP_200_OK) State.objects.filter(workspace__slug=slug, project_id=project_id)
.annotate(
state_count=Count(
"state_issue",
filter=Q(state_issue__parent_id=issue_id),
)
)
.order_by("group")
.values("group", "state_count")
)
result = {item["group"]: item["state_count"] for item in state_distribution}
serializer = IssueLiteSerializer(
sub_issues,
many=True,
)
return Response(
{
"sub_issues": serializer.data,
"state_distribution": result,
},
status=status.HTTP_200_OK,
)
except Exception as e: except Exception as e:
capture_exception(e) print(e)
return Response( return Response(
{"error": "Something went wrong please try again later"}, {"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,

View File

@ -0,0 +1,21 @@
# Third party imports
from rest_framework.response import Response
from rest_framework import status
from sentry_sdk import capture_exception
# Module imports
from .base import BaseAPIView
from plane.utils.integrations.github import get_release_notes
class ReleaseNotesEndpoint(BaseAPIView):
def get(self, request):
try:
release_notes = get_release_notes()
return Response(release_notes, 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

@ -11,10 +11,10 @@ from sentry_sdk import capture_exception
# Module imports # Module imports
from . import BaseViewSet from . import BaseViewSet, BaseAPIView
from plane.api.serializers import StateSerializer from plane.api.serializers import StateSerializer
from plane.api.permissions import ProjectEntityPermission from plane.api.permissions import ProjectEntityPermission
from plane.db.models import State from plane.db.models import State, Issue
class StateViewSet(BaseViewSet): class StateViewSet(BaseViewSet):
@ -53,7 +53,10 @@ class StateViewSet(BaseViewSet):
) )
except Exception as e: except Exception as e:
capture_exception(e) capture_exception(e)
return Response({"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST) return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
def list(self, request, slug, project_id): def list(self, request, slug, project_id):
try: try:
@ -85,7 +88,37 @@ class StateViewSet(BaseViewSet):
{"error": "Default state cannot be deleted"}, status=False {"error": "Default state cannot be deleted"}, status=False
) )
# Check for any issues in the state
issue_exist = Issue.objects.filter(state=pk).exists()
if issue_exist:
return Response(
{
"error": "The state is not empty, only empty states can be deleted"
},
status=status.HTTP_400_BAD_REQUEST,
)
state.delete() state.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
except State.DoesNotExist: except State.DoesNotExist:
return Response({"error": "State does not exists"}, status=status.HTTP_404) return Response({"error": "State does not exists"}, status=status.HTTP_404)
class StateDeleteIssueCheckEndpoint(BaseAPIView):
permission_classes = [
ProjectEntityPermission,
]
def get(self, request, slug, project_id, pk):
try:
issue_count = Issue.objects.filter(
state=pk, workspace__slug=slug, project_id=project_id
).count()
return Response({"issue_count": issue_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

@ -2,6 +2,7 @@
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.conf import settings
# Third party imports # Third party imports
from celery import shared_task from celery import shared_task
@ -20,7 +21,7 @@ def email_verification(first_name, email, token, current_site):
realtivelink = "/request-email-verification/" + "?token=" + str(token) realtivelink = "/request-email-verification/" + "?token=" + str(token)
abs_url = "http://" + current_site + realtivelink abs_url = "http://" + current_site + realtivelink
from_email_string = f"Team Plane <team@mailer.plane.so>" from_email_string = settings.EMAIL_FROM
subject = f"Verify your Email!" subject = f"Verify your Email!"

View File

@ -2,6 +2,7 @@
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.conf import settings
# Third party imports # Third party imports
from celery import shared_task from celery import shared_task
@ -18,7 +19,7 @@ def forgot_password(first_name, email, uidb64, token, current_site):
realtivelink = f"/email-verify/?uidb64={uidb64}&token={token}/" realtivelink = f"/email-verify/?uidb64={uidb64}&token={token}/"
abs_url = "http://" + current_site + realtivelink abs_url = "http://" + current_site + realtivelink
from_email_string = f"Team Plane <team@mailer.plane.so>" from_email_string = settings.EMAIL_FROM
subject = f"Verify your Email!" subject = f"Verify your Email!"

View File

@ -2,6 +2,7 @@
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.conf import settings
# Third party imports # Third party imports
from celery import shared_task from celery import shared_task
@ -14,7 +15,7 @@ def magic_link(email, key, token, current_site):
realtivelink = f"/magic-sign-in/?password={token}&key={key}" realtivelink = f"/magic-sign-in/?password={token}&key={key}"
abs_url = "http://" + current_site + realtivelink abs_url = "http://" + current_site + realtivelink
from_email_string = f"Team Plane <team@mailer.plane.so>" from_email_string = settings.EMAIL_FROM
subject = f"Login for Plane" subject = f"Login for Plane"

View File

@ -2,6 +2,7 @@
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.conf import settings
# Third party imports # Third party imports
from celery import shared_task from celery import shared_task
@ -22,7 +23,7 @@ def project_invitation(email, project_id, token, current_site):
relativelink = f"/project-member-invitation/{project_member_invite.id}" relativelink = f"/project-member-invitation/{project_member_invite.id}"
abs_url = "http://" + current_site + relativelink abs_url = "http://" + current_site + relativelink
from_email_string = f"Team Plane <team@mailer.plane.so>" from_email_string = settings.EMAIL_FROM
subject = f"{project.created_by.first_name or project.created_by.email} invited you to join {project.name} on Plane" subject = f"{project.created_by.first_name or project.created_by.email} invited you to join {project.name} on Plane"

View File

@ -27,7 +27,7 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor):
) )
abs_url = "http://" + current_site + realtivelink abs_url = "http://" + current_site + realtivelink
from_email_string = f"Team Plane <team@mailer.plane.so>" from_email_string = settings.EMAIL_FROM
subject = f"{invitor or email} invited you to join {workspace.name} on Plane" subject = f"{invitor or email} invited you to join {workspace.name} on Plane"

View File

@ -109,7 +109,7 @@ def send_welcome_email(sender, instance, created, **kwargs):
if created and not instance.is_bot: if created and not instance.is_bot:
first_name = instance.first_name.capitalize() first_name = instance.first_name.capitalize()
to_email = instance.email to_email = instance.email
from_email_string = f"Team Plane <team@mailer.plane.so>" from_email_string = settings.EMAIL_FROM
subject = f"Welcome to Plane ✈️!" subject = f"Welcome to Plane ✈️!"

View File

@ -174,11 +174,12 @@ EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
# Host for sending e-mail. # Host for sending e-mail.
EMAIL_HOST = os.environ.get("EMAIL_HOST") EMAIL_HOST = os.environ.get("EMAIL_HOST")
# Port for sending e-mail. # Port for sending e-mail.
EMAIL_PORT = 587 EMAIL_PORT = int(os.environ.get("EMAIL_PORT", 587))
# Optional SMTP authentication information for EMAIL_HOST. # Optional SMTP authentication information for EMAIL_HOST.
EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER") EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD") EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD")
EMAIL_USE_TLS = True EMAIL_USE_TLS = os.environ.get("EMAIL_USE_TLS", "1") == "1"
EMAIL_FROM = os.environ.get("EMAIL_FROM", "Team Plane <team@mailer.plane.so>")
SIMPLE_JWT = { SIMPLE_JWT = {

View File

@ -83,3 +83,6 @@ LOGGER_BASE_URL = os.environ.get("LOGGER_BASE_URL", False)
CELERY_RESULT_BACKEND = os.environ.get("REDIS_URL") CELERY_RESULT_BACKEND = os.environ.get("REDIS_URL")
CELERY_BROKER_URL = os.environ.get("REDIS_URL") CELERY_BROKER_URL = os.environ.get("REDIS_URL")
GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", False)

View File

@ -105,7 +105,7 @@ if (
AWS_S3_ADDRESSING_STYLE = "auto" AWS_S3_ADDRESSING_STYLE = "auto"
# The full URL to the S3 endpoint. Leave blank to use the default region URL. # The full URL to the S3 endpoint. Leave blank to use the default region URL.
AWS_S3_ENDPOINT_URL = "" AWS_S3_ENDPOINT_URL = os.environ.get("AWS_S3_ENDPOINT_URL", "")
# A prefix to be applied to every stored file. This will be joined to every filename using the "/" separator. # A prefix to be applied to every stored file. This will be joined to every filename using the "/" separator.
AWS_S3_KEY_PREFIX = "" AWS_S3_KEY_PREFIX = ""
@ -240,7 +240,9 @@ SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN", False)
LOGGER_BASE_URL = os.environ.get("LOGGER_BASE_URL", False) LOGGER_BASE_URL = os.environ.get("LOGGER_BASE_URL", False)
redis_url = os.environ.get("REDIS_URL") redis_url = os.environ.get("REDIS_URL")
broker_url = f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={certifi.where()}" broker_url = (
f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={certifi.where()}"
)
if DOCKERIZED: if DOCKERIZED:
CELERY_BROKER_URL = REDIS_URL CELERY_BROKER_URL = REDIS_URL
@ -248,3 +250,5 @@ if DOCKERIZED:
else: else:
CELERY_RESULT_BACKEND = broker_url CELERY_RESULT_BACKEND = broker_url
CELERY_BROKER_URL = broker_url CELERY_BROKER_URL = broker_url
GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", False)

View File

@ -204,3 +204,5 @@ broker_url = f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={cert
CELERY_RESULT_BACKEND = broker_url CELERY_RESULT_BACKEND = broker_url
CELERY_BROKER_URL = broker_url CELERY_BROKER_URL = broker_url
GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", False)

View File

@ -5,6 +5,7 @@ from urllib.parse import urlparse, parse_qs
from datetime import datetime, timedelta from datetime import datetime, timedelta
from cryptography.hazmat.primitives.serialization import load_pem_private_key from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from django.conf import settings
def get_jwt_token(): def get_jwt_token():
@ -128,3 +129,24 @@ def get_github_repo_details(access_tokens_url, owner, repo):
).json() ).json()
return open_issues, total_labels, collaborators return open_issues, total_labels, collaborators
def get_release_notes():
token = settings.GITHUB_ACCESS_TOKEN
if token:
headers = {
"Authorization": "Bearer " + str(token),
"Accept": "application/vnd.github.v3+json",
}
else:
headers = {
"Accept": "application/vnd.github.v3+json",
}
url = "https://api.github.com/repos/makeplane/plane/releases?per_page=5&page=1"
response = requests.get(url, headers=headers)
if response.status_code != 200:
return {"error": "Unable to render information from Github Repository"}
return response.json()

View File

@ -1,5 +1,6 @@
# Replace with your instance Public IP # Replace with your instance Public IP
# NEXT_PUBLIC_API_BASE_URL = "http://localhost" # NEXT_PUBLIC_API_BASE_URL = "http://localhost"
NEXT_PUBLIC_EXTRA_IMAGE_DOMAINS=
NEXT_PUBLIC_GOOGLE_CLIENTID="" NEXT_PUBLIC_GOOGLE_CLIENTID=""
NEXT_PUBLIC_GITHUB_APP_NAME="" NEXT_PUBLIC_GITHUB_APP_NAME=""
NEXT_PUBLIC_GITHUB_ID="" NEXT_PUBLIC_GITHUB_ID=""

View File

@ -141,7 +141,7 @@ export const EmailCodeForm = ({ onSuccess }: any) => {
<button <button
type="button" type="button"
className={`mt-5 flex w-full justify-end text-xs outline-none ${ className={`mt-5 flex w-full justify-end text-xs outline-none ${
isResendDisabled ? "cursor-default text-gray-400" : "cursor-pointer text-theme" isResendDisabled ? "cursor-default text-gray-400" : "cursor-pointer text-brand-accent"
} `} } `}
onClick={() => { onClick={() => {
setIsCodeResending(true); setIsCodeResending(true);

View File

@ -94,7 +94,7 @@ export const EmailPasswordForm = ({ onSuccess }: any) => {
<div className="mt-2 flex items-center justify-between"> <div className="mt-2 flex items-center justify-between">
<div className="ml-auto text-sm"> <div className="ml-auto text-sm">
<Link href={"/forgot-password"}> <Link href={"/forgot-password"}>
<a className="font-medium text-theme hover:text-indigo-500">Forgot your password?</a> <a className="font-medium text-brand-accent hover:text-indigo-500">Forgot your password?</a>
</Link> </Link>
</div> </div>
</div> </div>

View File

@ -37,7 +37,7 @@ export const GithubLoginButton: FC<GithubLoginButtonProps> = (props) => {
<Link <Link
href={`https://github.com/login/oauth/authorize?client_id=${NEXT_PUBLIC_GITHUB_ID}&redirect_uri=${loginCallBackURL}&scope=read:user,user:email`} 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-md border border-gray-200 p-2 text-sm font-medium text-gray-600 duration-300 hover:bg-gray-50"> <button className="flex w-full items-center justify-center gap-3 rounded-md border border-brand-base p-2 text-sm font-medium text-gray-600 duration-300 hover:bg-gray-50">
<Image src={githubImage} height={22} width={22} color="#000" alt="GitHub Logo" /> <Image src={githubImage} height={22} width={22} color="#000" alt="GitHub Logo" />
<span>Sign In with Github</span> <span>Sign In with Github</span>
</button> </button>

View File

@ -36,16 +36,16 @@ export const NotAuthorizedView: React.FC<Props> = ({ actionButton, type }) => {
alt="ProjectSettingImg" alt="ProjectSettingImg"
/> />
</div> </div>
<h1 className="text-xl font-medium text-gray-900"> <h1 className="text-xl font-medium text-brand-base">
Oops! You are not authorized to view this page Oops! You are not authorized to view this page
</h1> </h1>
<div className="w-full text-base text-gray-500 max-w-md "> <div className="w-full text-base text-brand-secondary max-w-md ">
{user ? ( {user ? (
<p> <p>
You have signed in as {user.email}. <br /> You have signed in as {user.email}. <br />
<Link href={`/signin?next=${currentPath}`}> <Link href={`/signin?next=${currentPath}`}>
<a className="text-gray-900 font-medium">Sign in</a> <a className="text-brand-base font-medium">Sign in</a>
</Link>{" "} </Link>{" "}
with different account that has access to this page. with different account that has access to this page.
</p> </p>
@ -53,7 +53,7 @@ export const NotAuthorizedView: React.FC<Props> = ({ actionButton, type }) => {
<p> <p>
You need to{" "} You need to{" "}
<Link href={`/signin?next=${currentPath}`}> <Link href={`/signin?next=${currentPath}`}>
<a className="text-gray-900 font-medium">Sign in</a> <a className="text-brand-base font-medium">Sign in</a>
</Link>{" "} </Link>{" "}
with an account that has access to this page. with an account that has access to this page.
</p> </p>

View File

@ -16,7 +16,7 @@ const Breadcrumbs = ({ children }: BreadcrumbsProps) => {
<div className="flex items-center"> <div className="flex items-center">
<button <button
type="button" type="button"
className="grid h-8 w-8 flex-shrink-0 cursor-pointer place-items-center rounded border border-gray-300 text-center text-sm hover:bg-gray-100" className="grid h-8 w-8 flex-shrink-0 cursor-pointer place-items-center rounded border border-brand-base text-center text-sm hover:bg-brand-surface-1"
onClick={() => router.back()} onClick={() => router.back()}
> >
<ArrowLeftIcon className="h-3 w-3" /> <ArrowLeftIcon className="h-3 w-3" />
@ -37,7 +37,7 @@ const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({ title, link, icon }) =>
<> <>
{link ? ( {link ? (
<Link href={link}> <Link href={link}>
<a className="border-r-2 border-gray-300 px-3 text-sm"> <a className="border-r-2 border-brand-base px-3 text-sm">
<p className={`${icon ? "flex items-center gap-2" : ""}`}> <p className={`${icon ? "flex items-center gap-2" : ""}`}>
{icon ?? null} {icon ?? null}
{title} {title}

View File

@ -0,0 +1,45 @@
import React, { Dispatch, SetStateAction, useEffect, useState } from "react";
// cmdk
import { Command } from "cmdk";
import { THEMES_OBJ } from "constants/themes";
import { useTheme } from "next-themes";
import { SettingIcon } from "components/icons";
type Props = {
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
};
export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => {
const [mounted, setMounted] = useState(false);
const { setTheme } = useTheme();
// useEffect only runs on the client, so now we can safely show the UI
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}
return (
<>
{THEMES_OBJ.map((theme) => (
<Command.Item
key={theme.value}
onSelect={() => {
setTheme(theme.value);
setIsPaletteOpen(false);
}}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-brand-secondary" />
{theme.label}
</div>
</Command.Item>
))}
</>
);
};

View File

@ -44,6 +44,7 @@ import {
ChangeIssueState, ChangeIssueState,
ChangeIssuePriority, ChangeIssuePriority,
ChangeIssueAssignee, ChangeIssueAssignee,
ChangeInterfaceTheme,
} from "components/command-palette"; } from "components/command-palette";
import { BulkDeleteIssuesModal } from "components/core"; import { BulkDeleteIssuesModal } from "components/core";
import { CreateUpdateCycleModal } from "components/cycles"; import { CreateUpdateCycleModal } from "components/cycles";
@ -379,7 +380,7 @@ export const CommandPalette: React.FC = () => {
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-gray-500 bg-opacity-25 transition-opacity" /> <div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 z-30 overflow-y-auto p-4 sm:p-6 md:p-20"> <div className="fixed inset-0 z-30 overflow-y-auto p-4 sm:p-6 md:p-20">
@ -392,7 +393,7 @@ export const CommandPalette: React.FC = () => {
leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" 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-gray-500 divide-opacity-10 rounded-xl bg-white shadow-2xl ring-1 ring-black ring-opacity-5 transition-all"> <Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-brand-base divide-opacity-10 rounded-xl bg-brand-surface-2 border-brand-base border shadow-2xl transition-all">
<Command <Command
filter={(value, search) => { filter={(value, search) => {
if (value.toLowerCase().includes(search.toLowerCase())) return 1; if (value.toLowerCase().includes(search.toLowerCase())) return 1;
@ -415,7 +416,7 @@ export const CommandPalette: React.FC = () => {
> >
{issueId && issueDetails && ( {issueId && issueDetails && (
<div className="flex p-3"> <div className="flex p-3">
<p className="overflow-hidden truncate rounded-md bg-gray-100 p-1 px-2 text-xs font-medium text-gray-500"> <p className="overflow-hidden truncate rounded-md bg-brand-surface-1 p-1 px-2 text-xs font-medium text-brand-secondary">
{issueDetails.project_detail?.identifier}-{issueDetails.sequence_id}{" "} {issueDetails.project_detail?.identifier}-{issueDetails.sequence_id}{" "}
{issueDetails?.name} {issueDetails?.name}
</p> </p>
@ -423,11 +424,11 @@ export const CommandPalette: React.FC = () => {
)} )}
<div className="relative"> <div className="relative">
<MagnifyingGlassIcon <MagnifyingGlassIcon
className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-gray-900 text-opacity-40" className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-brand-secondary"
aria-hidden="true" aria-hidden="true"
/> />
<Command.Input <Command.Input
className="w-full border-0 border-b bg-transparent p-4 pl-11 text-gray-900 placeholder-gray-500 outline-none focus:ring-0 sm:text-sm" className="w-full border-0 border-b border-brand-base bg-transparent p-4 pl-11 text-brand-base placeholder-gray-500 outline-none focus:ring-0 sm:text-sm"
placeholder={placeholder} placeholder={placeholder}
value={searchTerm} value={searchTerm}
onValueChange={(e) => { onValueChange={(e) => {
@ -441,7 +442,9 @@ export const CommandPalette: React.FC = () => {
resultsCount === 0 && resultsCount === 0 &&
searchTerm !== "" && searchTerm !== "" &&
debouncedSearchTerm !== "" && ( debouncedSearchTerm !== "" && (
<div className="my-4 text-center text-gray-500">No results found.</div> <div className="my-4 text-center text-brand-secondary">
No results found.
</div>
)} )}
{(isLoading || isSearching) && ( {(isLoading || isSearching) && (
@ -502,8 +505,11 @@ export const CommandPalette: React.FC = () => {
value={value} value={value}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 overflow-hidden text-gray-700"> <div className="flex items-center gap-2 overflow-hidden text-brand-secondary">
<Icon className="h-4 w-4 text-gray-500" color="#6b7280" /> <Icon
className="h-4 w-4 text-brand-secondary"
color="#6b7280"
/>
<p className="block flex-1 truncate">{item.name}</p> <p className="block flex-1 truncate">{item.name}</p>
</div> </div>
</Command.Item> </Command.Item>
@ -528,8 +534,8 @@ export const CommandPalette: React.FC = () => {
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<Squares2X2Icon className="h-4 w-4 text-gray-500" /> <Squares2X2Icon className="h-4 w-4 text-brand-secondary" />
Change state... Change state...
</div> </div>
</Command.Item> </Command.Item>
@ -541,8 +547,8 @@ export const CommandPalette: React.FC = () => {
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<ChartBarIcon className="h-4 w-4 text-gray-500" /> <ChartBarIcon className="h-4 w-4 text-brand-secondary" />
Change priority... Change priority...
</div> </div>
</Command.Item> </Command.Item>
@ -554,8 +560,8 @@ export const CommandPalette: React.FC = () => {
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<UsersIcon className="h-4 w-4 text-gray-500" /> <UsersIcon className="h-4 w-4 text-brand-secondary" />
Assign to... Assign to...
</div> </div>
</Command.Item> </Command.Item>
@ -566,15 +572,15 @@ export const CommandPalette: React.FC = () => {
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
{issueDetails?.assignees.includes(user.id) ? ( {issueDetails?.assignees.includes(user.id) ? (
<> <>
<UserMinusIcon className="h-4 w-4 text-gray-500" /> <UserMinusIcon className="h-4 w-4 text-brand-secondary" />
Un-assign from me Un-assign from me
</> </>
) : ( ) : (
<> <>
<UserPlusIcon className="h-4 w-4 text-gray-500" /> <UserPlusIcon className="h-4 w-4 text-brand-secondary" />
Assign to me Assign to me
</> </>
)} )}
@ -582,8 +588,8 @@ export const CommandPalette: React.FC = () => {
</Command.Item> </Command.Item>
<Command.Item onSelect={deleteIssue} className="focus:outline-none"> <Command.Item onSelect={deleteIssue} className="focus:outline-none">
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<TrashIcon className="h-4 w-4 text-gray-500" /> <TrashIcon className="h-4 w-4 text-brand-secondary" />
Delete issue Delete issue
</div> </div>
</Command.Item> </Command.Item>
@ -594,16 +600,19 @@ export const CommandPalette: React.FC = () => {
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<LinkIcon className="h-4 w-4 text-gray-500" /> <LinkIcon className="h-4 w-4 text-brand-secondary" />
Copy issue URL to clipboard Copy issue URL to clipboard
</div> </div>
</Command.Item> </Command.Item>
</> </>
)} )}
<Command.Group heading="Issue"> <Command.Group heading="Issue">
<Command.Item onSelect={createNewIssue} className="focus:bg-gray-200"> <Command.Item
<div className="flex items-center gap-2 text-gray-700"> onSelect={createNewIssue}
className="focus:bg-brand-surface-2"
>
<div className="flex items-center gap-2 text-brand-secondary">
<LayerDiagonalIcon className="h-4 w-4" color="#6b7280" /> <LayerDiagonalIcon className="h-4 w-4" color="#6b7280" />
Create new issue Create new issue
</div> </div>
@ -617,7 +626,7 @@ export const CommandPalette: React.FC = () => {
onSelect={createNewProject} onSelect={createNewProject}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<AssignmentClipboardIcon className="h-4 w-4" color="#6b7280" /> <AssignmentClipboardIcon className="h-4 w-4" color="#6b7280" />
Create new project Create new project
</div> </div>
@ -633,7 +642,7 @@ export const CommandPalette: React.FC = () => {
onSelect={createNewCycle} onSelect={createNewCycle}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<ContrastIcon className="h-4 w-4" color="#6b7280" /> <ContrastIcon className="h-4 w-4" color="#6b7280" />
Create new cycle Create new cycle
</div> </div>
@ -646,7 +655,7 @@ export const CommandPalette: React.FC = () => {
onSelect={createNewModule} onSelect={createNewModule}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<PeopleGroupIcon className="h-4 w-4" color="#6b7280" /> <PeopleGroupIcon className="h-4 w-4" color="#6b7280" />
Create new module Create new module
</div> </div>
@ -656,7 +665,7 @@ export const CommandPalette: React.FC = () => {
<Command.Group heading="View"> <Command.Group heading="View">
<Command.Item onSelect={createNewView} className="focus:outline-none"> <Command.Item onSelect={createNewView} className="focus:outline-none">
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<ViewListIcon className="h-4 w-4" color="#6b7280" /> <ViewListIcon className="h-4 w-4" color="#6b7280" />
Create new view Create new view
</div> </div>
@ -685,7 +694,7 @@ export const CommandPalette: React.FC = () => {
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4" color="#6b7280" /> <SettingIcon className="h-4 w-4" color="#6b7280" />
Search settings... Search settings...
</div> </div>
@ -696,11 +705,24 @@ export const CommandPalette: React.FC = () => {
onSelect={createNewWorkspace} onSelect={createNewWorkspace}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<FolderPlusIcon className="h-4 w-4 text-gray-500" /> <FolderPlusIcon className="h-4 w-4 text-brand-secondary" />
Create new workspace Create new workspace
</div> </div>
</Command.Item> </Command.Item>
<Command.Item
onSelect={() => {
setPlaceholder("Change interface theme...");
setSearchTerm("");
setPages([...pages, "change-interface-theme"]);
}}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-brand-secondary" />
Change interface theme...
</div>
</Command.Item>
</Command.Group> </Command.Group>
<Command.Group heading="Help"> <Command.Group heading="Help">
<Command.Item <Command.Item
@ -713,8 +735,8 @@ export const CommandPalette: React.FC = () => {
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<RocketLaunchIcon className="h-4 w-4 text-gray-500" /> <RocketLaunchIcon className="h-4 w-4 text-brand-secondary" />
Open keyboard shortcuts Open keyboard shortcuts
</div> </div>
</Command.Item> </Command.Item>
@ -725,8 +747,8 @@ export const CommandPalette: React.FC = () => {
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<DocumentIcon className="h-4 w-4 text-gray-500" /> <DocumentIcon className="h-4 w-4 text-brand-secondary" />
Open Plane documentation Open Plane documentation
</div> </div>
</Command.Item> </Command.Item>
@ -737,7 +759,7 @@ export const CommandPalette: React.FC = () => {
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<DiscordIcon className="h-4 w-4" color="#6b7280" /> <DiscordIcon className="h-4 w-4" color="#6b7280" />
Join our Discord Join our Discord
</div> </div>
@ -752,7 +774,7 @@ export const CommandPalette: React.FC = () => {
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<GithubIcon className="h-4 w-4" color="#6b7280" /> <GithubIcon className="h-4 w-4" color="#6b7280" />
Report a bug Report a bug
</div> </div>
@ -764,8 +786,8 @@ export const CommandPalette: React.FC = () => {
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<ChatBubbleOvalLeftEllipsisIcon className="h-4 w-4 text-gray-500" /> <ChatBubbleOvalLeftEllipsisIcon className="h-4 w-4 text-brand-secondary" />
Chat with us Chat with us
</div> </div>
</Command.Item> </Command.Item>
@ -779,8 +801,8 @@ export const CommandPalette: React.FC = () => {
onSelect={() => goToSettings()} onSelect={() => goToSettings()}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-gray-500" /> <SettingIcon className="h-4 w-4 text-brand-secondary" />
General General
</div> </div>
</Command.Item> </Command.Item>
@ -788,8 +810,8 @@ export const CommandPalette: React.FC = () => {
onSelect={() => goToSettings("members")} onSelect={() => goToSettings("members")}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-gray-500" /> <SettingIcon className="h-4 w-4 text-brand-secondary" />
Members Members
</div> </div>
</Command.Item> </Command.Item>
@ -797,8 +819,8 @@ export const CommandPalette: React.FC = () => {
onSelect={() => goToSettings("billing")} onSelect={() => goToSettings("billing")}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-gray-500" /> <SettingIcon className="h-4 w-4 text-brand-secondary" />
Billings and Plans Billings and Plans
</div> </div>
</Command.Item> </Command.Item>
@ -806,8 +828,8 @@ export const CommandPalette: React.FC = () => {
onSelect={() => goToSettings("integrations")} onSelect={() => goToSettings("integrations")}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-gray-500" /> <SettingIcon className="h-4 w-4 text-brand-secondary" />
Integrations Integrations
</div> </div>
</Command.Item> </Command.Item>
@ -815,8 +837,8 @@ export const CommandPalette: React.FC = () => {
onSelect={() => goToSettings("import-export")} onSelect={() => goToSettings("import-export")}
className="focus:outline-none" className="focus:outline-none"
> >
<div className="flex items-center gap-2 text-gray-700"> <div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-gray-500" /> <SettingIcon className="h-4 w-4 text-brand-secondary" />
Import/Export Import/Export
</div> </div>
</Command.Item> </Command.Item>
@ -842,6 +864,9 @@ export const CommandPalette: React.FC = () => {
setIsPaletteOpen={setIsPaletteOpen} setIsPaletteOpen={setIsPaletteOpen}
/> />
)} )}
{page === "change-interface-theme" && (
<ChangeInterfaceTheme setIsPaletteOpen={setIsPaletteOpen} />
)}
</Command.List> </Command.List>
</Command> </Command>
</Dialog.Panel> </Dialog.Panel>

View File

@ -3,3 +3,4 @@ export * from "./shortcuts-modal";
export * from "./change-issue-state"; export * from "./change-issue-state";
export * from "./change-issue-priority"; export * from "./change-issue-priority";
export * from "./change-issue-assignee"; export * from "./change-issue-assignee";
export * from "./change-interface-theme";

View File

@ -71,7 +71,7 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> <div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 z-20 overflow-y-auto"> <div className="fixed inset-0 z-20 overflow-y-auto">
@ -85,29 +85,29 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
> >
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg"> <Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-brand-surface-2 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg">
<div className="bg-white p-5"> <div className="bg-brand-surface-2 p-5">
<div className="sm:flex sm:items-start"> <div className="sm:flex sm:items-start">
<div className="flex w-full flex-col gap-y-4 text-center sm:text-left"> <div className="flex w-full flex-col gap-y-4 text-center sm:text-left">
<Dialog.Title <Dialog.Title
as="h3" as="h3"
className="flex justify-between text-lg font-medium leading-6 text-gray-900" className="flex justify-between text-lg font-medium leading-6 text-brand-base"
> >
<span>Keyboard Shortcuts</span> <span>Keyboard Shortcuts</span>
<span> <span>
<button type="button" onClick={() => setIsOpen(false)}> <button type="button" onClick={() => setIsOpen(false)}>
<XMarkIcon <XMarkIcon
className="h-6 w-6 text-gray-400 hover:text-gray-500" className="h-6 w-6 text-gray-400 hover:text-brand-secondary"
aria-hidden="true" aria-hidden="true"
/> />
</button> </button>
</span> </span>
</Dialog.Title> </Dialog.Title>
<div> <div>
<div className="flex w-full items-center justify-start gap-1 rounded border-[0.6px] border-gray-200 bg-gray-100 px-3 py-2"> <div className="flex w-full items-center justify-start gap-1 rounded border-[0.6px] border-brand-base bg-brand-surface-1 px-3 py-2">
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-gray-500" /> <MagnifyingGlassIcon className="h-3.5 w-3.5 text-brand-secondary" />
<Input <Input
className="w-full border-none bg-transparent py-1 px-2 text-xs text-gray-500 focus:outline-none" className="w-full border-none bg-transparent py-1 px-2 text-xs text-brand-secondary focus:outline-none"
id="search" id="search"
name="search" name="search"
type="text" type="text"
@ -123,16 +123,16 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
<div key={shortcut.keys} className="flex w-full flex-col"> <div key={shortcut.keys} className="flex w-full flex-col">
<div className="flex flex-col gap-y-3"> <div className="flex flex-col gap-y-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<p className="text-sm text-gray-500">{shortcut.description}</p> <p className="text-sm text-brand-secondary">{shortcut.description}</p>
<div className="flex items-center gap-x-2.5"> <div className="flex items-center gap-x-2.5">
{shortcut.keys.split(",").map((key, index) => ( {shortcut.keys.split(",").map((key, index) => (
<span key={index} className="flex items-center gap-1"> <span key={index} className="flex items-center gap-1">
{key === "Ctrl" ? ( {key === "Ctrl" ? (
<span className="flex h-full items-center rounded-sm border border-gray-200 bg-gray-100 p-2"> <span className="flex h-full items-center rounded-sm border border-brand-base bg-brand-surface-1 p-2">
<MacCommandIcon /> <MacCommandIcon />
</span> </span>
) : ( ) : (
<kbd className="rounded-sm border border-gray-200 bg-gray-100 px-2 py-1 text-sm font-medium text-gray-800"> <kbd className="rounded-sm border border-brand-base bg-brand-surface-1 px-2 py-1 text-sm font-medium text-gray-800">
{key === "Ctrl" ? <MacCommandIcon /> : key} {key === "Ctrl" ? <MacCommandIcon /> : key}
</kbd> </kbd>
)} )}
@ -145,7 +145,7 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
)) ))
) : ( ) : (
<div className="flex flex-col gap-y-3"> <div className="flex flex-col gap-y-3">
<p className="text-sm text-gray-500"> <p className="text-sm text-brand-secondary">
No shortcuts found for{" "} No shortcuts found for{" "}
<span className="font-semibold italic"> <span className="font-semibold italic">
{`"`} {`"`}
@ -162,16 +162,16 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
<div className="flex flex-col gap-y-3"> <div className="flex flex-col gap-y-3">
{shortcuts.map(({ keys, description }, index) => ( {shortcuts.map(({ keys, description }, index) => (
<div key={index} className="flex items-center justify-between"> <div key={index} className="flex items-center justify-between">
<p className="text-sm text-gray-500">{description}</p> <p className="text-sm text-brand-secondary">{description}</p>
<div className="flex items-center gap-x-2.5"> <div className="flex items-center gap-x-2.5">
{keys.split(",").map((key, index) => ( {keys.split(",").map((key, index) => (
<span key={index} className="flex items-center gap-1"> <span key={index} className="flex items-center gap-1">
{key === "Ctrl" ? ( {key === "Ctrl" ? (
<span className="flex h-full items-center rounded-sm border border-gray-200 bg-gray-100 p-2"> <span className="flex h-full items-center rounded-sm border border-brand-base text-brand-secondary bg-brand-surface-1 p-2">
<MacCommandIcon /> <MacCommandIcon />
</span> </span>
) : ( ) : (
<kbd className="rounded-sm border border-gray-200 bg-gray-100 px-2 py-1 text-sm font-medium text-gray-800"> <kbd className="rounded-sm border border-brand-base bg-brand-surface-1 px-2 py-1 text-sm font-medium text-brand-secondary">
{key === "Ctrl" ? <MacCommandIcon /> : key} {key === "Ctrl" ? <MacCommandIcon /> : key}
</kbd> </kbd>
)} )}

View File

@ -81,7 +81,7 @@ export const AllBoards: React.FC<Props> = ({
return ( return (
<div <div
key={index} key={index}
className="flex items-center justify-between gap-2 rounded bg-white p-2 shadow" className="flex items-center justify-between gap-2 rounded bg-brand-surface-1 p-2 shadow"
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{currentState && {currentState &&
@ -92,7 +92,7 @@ export const AllBoards: React.FC<Props> = ({
: addSpaceIfCamelCase(singleGroup)} : addSpaceIfCamelCase(singleGroup)}
</h4> </h4>
</div> </div>
<span className="text-xs text-gray-500">0</span> <span className="text-xs text-brand-secondary">0</span>
</div> </div>
); );
})} })}

View File

@ -123,8 +123,8 @@ export const BoardHeader: React.FC<Props> = ({
return ( return (
<div <div
className={`flex justify-between items-center px-1 ${ className={`flex items-center justify-between px-1 ${
!isCollapsed ? "flex-col rounded-md border bg-gray-50" : "" !isCollapsed ? "flex-col rounded-md bg-brand-surface-1" : ""
}`} }`}
> >
<div className={`flex items-center ${!isCollapsed ? "flex-col gap-2" : "gap-1"}`}> <div className={`flex items-center ${!isCollapsed ? "flex-col gap-2" : "gap-1"}`}>
@ -145,7 +145,7 @@ export const BoardHeader: React.FC<Props> = ({
<span <span
className={`${ className={`${
isCollapsed ? "ml-0.5" : "" isCollapsed ? "ml-0.5" : ""
} rounded-full bg-gray-100 py-1 min-w-[2.5rem] text-xs text-center`} } min-w-[2.5rem] rounded-full bg-brand-surface-2 py-1 text-center text-xs`}
> >
{groupedByIssues?.[groupTitle].length ?? 0} {groupedByIssues?.[groupTitle].length ?? 0}
</span> </span>
@ -155,7 +155,7 @@ export const BoardHeader: React.FC<Props> = ({
<div className={`flex items-center ${!isCollapsed ? "flex-col pb-2" : ""}`}> <div className={`flex items-center ${!isCollapsed ? "flex-col pb-2" : ""}`}>
<button <button
type="button" type="button"
className="grid h-7 w-7 place-items-center rounded p-1 text-gray-700 outline-none duration-300 hover:bg-gray-100" className="grid h-7 w-7 place-items-center rounded p-1 text-brand-secondary outline-none duration-300 hover:bg-brand-surface-2"
onClick={() => { onClick={() => {
setIsCollapsed((prevData) => !prevData); setIsCollapsed((prevData) => !prevData);
}} }}
@ -169,7 +169,7 @@ export const BoardHeader: React.FC<Props> = ({
{!isCompleted && ( {!isCompleted && (
<button <button
type="button" type="button"
className="grid h-7 w-7 place-items-center rounded p-1 text-gray-700 outline-none duration-300 hover:bg-gray-100" className="grid h-7 w-7 place-items-center rounded p-1 text-brand-secondary outline-none duration-300 hover:bg-brand-surface-2"
onClick={addIssueToState} onClick={addIssueToState}
> >
<PlusIcon className="h-4 w-4" /> <PlusIcon className="h-4 w-4" />

View File

@ -66,7 +66,7 @@ export const SingleBoard: React.FC<Props> = ({
}, [currentState]); }, [currentState]);
return ( return (
<div className={`h-full flex-shrink-0 ${!isCollapsed ? "" : "w-96 bg-gray-50"}`}> <div className={`h-full flex-shrink-0 ${!isCollapsed ? "" : "w-96"}`}>
<div className={`${!isCollapsed ? "" : "flex h-full flex-col space-y-3"}`}> <div className={`${!isCollapsed ? "" : "flex h-full flex-col space-y-3"}`}>
<BoardHeader <BoardHeader
addIssueToState={addIssueToState} addIssueToState={addIssueToState}
@ -81,7 +81,7 @@ export const SingleBoard: React.FC<Props> = ({
{(provided, snapshot) => ( {(provided, snapshot) => (
<div <div
className={`relative h-full overflow-y-auto p-1 ${ className={`relative h-full overflow-y-auto p-1 ${
snapshot.isDraggingOver ? "bg-indigo-50 bg-opacity-50" : "" snapshot.isDraggingOver ? "bg-brand-base/20" : ""
} ${!isCollapsed ? "hidden" : "block"}`} } ${!isCollapsed ? "hidden" : "block"}`}
ref={provided.innerRef} ref={provided.innerRef}
{...provided.droppableProps} {...provided.droppableProps}
@ -91,15 +91,17 @@ export const SingleBoard: React.FC<Props> = ({
<div <div
className={`absolute ${ className={`absolute ${
snapshot.isDraggingOver ? "block" : "hidden" snapshot.isDraggingOver ? "block" : "hidden"
} pointer-events-none top-0 left-0 z-[99] h-full w-full bg-gray-100 opacity-50`} } pointer-events-none top-0 left-0 z-[99] h-full w-full bg-brand-surface-1 opacity-50`}
/> />
<div <div
className={`absolute ${ className={`absolute ${
snapshot.isDraggingOver ? "block" : "hidden" snapshot.isDraggingOver ? "block" : "hidden"
} pointer-events-none top-1/2 left-1/2 z-[99] -translate-y-1/2 -translate-x-1/2 whitespace-nowrap rounded bg-white p-2 text-xs`} } pointer-events-none top-1/2 left-1/2 z-[99] -translate-y-1/2 -translate-x-1/2 whitespace-nowrap rounded bg-brand-base p-2 text-xs`}
> >
This board is ordered by{" "} This board is ordered by{" "}
{replaceUnderscoreIfSnakeCase(orderBy ?? "created_at")} {replaceUnderscoreIfSnakeCase(
orderBy ? (orderBy[0] === "-" ? orderBy.slice(1) : orderBy) : "created_at"
)}
</div> </div>
</> </>
)} )}
@ -146,7 +148,7 @@ export const SingleBoard: React.FC<Props> = ({
{type === "issue" ? ( {type === "issue" ? (
<button <button
type="button" type="button"
className="flex items-center gap-2 font-medium text-theme outline-none" className="flex items-center gap-2 font-medium text-brand-accent outline-none"
onClick={addIssueToState} onClick={addIssueToState}
> >
<PlusIcon className="h-4 w-4" /> <PlusIcon className="h-4 w-4" />
@ -158,7 +160,7 @@ export const SingleBoard: React.FC<Props> = ({
customButton={ customButton={
<button <button
type="button" type="button"
className="flex items-center gap-2 font-medium text-theme outline-none" className="flex items-center gap-2 font-medium text-brand-accent outline-none"
> >
<PlusIcon className="h-4 w-4" /> <PlusIcon className="h-4 w-4" />
Add Issue Add Issue

View File

@ -261,8 +261,8 @@ export const SingleBoardIssue: React.FC<Props> = ({
</a> </a>
</ContextMenu> </ContextMenu>
<div <div
className={`mb-3 rounded bg-white shadow ${ className={`mb-3 rounded bg-brand-base shadow ${
snapshot.isDragging ? "border-2 border-theme shadow-lg" : "" snapshot.isDragging ? "border-2 border-brand-accent shadow-lg" : ""
}`} }`}
ref={provided.innerRef} ref={provided.innerRef}
{...provided.draggableProps} {...provided.draggableProps}
@ -312,12 +312,12 @@ export const SingleBoardIssue: React.FC<Props> = ({
<Link href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}> <Link href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}>
<a> <a>
{properties.key && ( {properties.key && (
<div className="mb-2.5 text-xs font-medium text-gray-700"> <div className="mb-2.5 text-xs font-medium text-brand-secondary">
{issue.project_detail.identifier}-{issue.sequence_id} {issue.project_detail.identifier}-{issue.sequence_id}
</div> </div>
)} )}
<h5 <h5
className="break-all text-sm group-hover:text-theme" className="break-all text-sm group-hover:text-brand-accent"
style={{ lineClamp: 3, WebkitLineClamp: 3 }} style={{ lineClamp: 3, WebkitLineClamp: 3 }}
> >
{truncateText(issue.name, 100)} {truncateText(issue.name, 100)}
@ -349,7 +349,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
/> />
)} )}
{properties.sub_issue_count && ( {properties.sub_issue_count && (
<div className="flex flex-shrink-0 items-center gap-1 rounded-md border px-3 py-1.5 text-xs shadow-sm"> <div className="flex flex-shrink-0 items-center gap-1 rounded-md border border-brand-base px-3 py-1.5 text-xs shadow-sm">
{issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"} {issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
</div> </div>
)} )}
@ -358,7 +358,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
{issue.label_details.map((label) => ( {issue.label_details.map((label) => (
<div <div
key={label.id} key={label.id}
className="group flex items-center gap-1 rounded-2xl border px-2 py-0.5 text-xs" className="group flex items-center gap-1 rounded-2xl border border-brand-base px-2 py-0.5 text-xs text-brand-secondary"
> >
<span <span
className="h-1.5 w-1.5 flex-shrink-0 rounded-full" className="h-1.5 w-1.5 flex-shrink-0 rounded-full"
@ -389,7 +389,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
/> />
)} )}
{properties.link && ( {properties.link && (
<div className="flex items-center rounded-md shadow-sm px-2.5 py-1 cursor-default text-xs border border-gray-200"> <div className="flex cursor-default items-center rounded-md border border-brand-base px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Link" tooltipContent={`${issue.link_count}`}> <Tooltip tooltipHeading="Link" tooltipContent={`${issue.link_count}`}>
<div className="flex items-center gap-1 text-gray-500"> <div className="flex items-center gap-1 text-gray-500">
<LinkIcon className="h-3.5 w-3.5 text-gray-500" /> <LinkIcon className="h-3.5 w-3.5 text-gray-500" />
@ -399,10 +399,10 @@ export const SingleBoardIssue: React.FC<Props> = ({
</div> </div>
)} )}
{properties.attachment_count && ( {properties.attachment_count && (
<div className="flex items-center rounded-md shadow-sm px-2.5 py-1 cursor-default text-xs border border-gray-200"> <div className="flex cursor-default items-center rounded-md border border-brand-base px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Attachment" tooltipContent={`${issue.attachment_count}`}> <Tooltip tooltipHeading="Attachment" tooltipContent={`${issue.attachment_count}`}>
<div className="flex items-center gap-1 text-gray-500"> <div className="flex items-center gap-1 text-gray-500">
<PaperClipIcon className="h-3.5 w-3.5 text-gray-500 -rotate-45" /> <PaperClipIcon className="h-3.5 w-3.5 -rotate-45 text-gray-500" />
{issue.attachment_count} {issue.attachment_count}
</div> </div>
</Tooltip> </Tooltip>

View File

@ -121,7 +121,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-white shadow-2xl ring-1 ring-black ring-opacity-5 transition-all"> <Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-brand-surface-2 shadow-2xl ring-1 ring-black ring-opacity-5 transition-all">
<form> <form>
<Combobox <Combobox
onChange={(val: string) => { onChange={(val: string) => {
@ -136,12 +136,12 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
> >
<div className="relative m-1"> <div className="relative m-1">
<MagnifyingGlassIcon <MagnifyingGlassIcon
className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-gray-900 text-opacity-40" className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-brand-base text-opacity-40"
aria-hidden="true" aria-hidden="true"
/> />
<input <input
type="text" type="text"
className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-gray-900 placeholder-gray-500 outline-none focus:ring-0 sm:text-sm" className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-brand-base placeholder-gray-500 outline-none focus:ring-0 sm:text-sm"
placeholder="Search..." placeholder="Search..."
onChange={(event) => setQuery(event.target.value)} onChange={(event) => setQuery(event.target.value)}
/> />
@ -154,7 +154,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
{filteredIssues.length > 0 ? ( {filteredIssues.length > 0 ? (
<li className="p-2"> <li className="p-2">
{query === "" && ( {query === "" && (
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-gray-900"> <h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-brand-base">
Select issues to delete Select issues to delete
</h2> </h2>
)} )}
@ -166,7 +166,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
value={issue.id} value={issue.id}
className={({ active }) => className={({ active }) =>
`flex cursor-pointer select-none items-center justify-between rounded-md px-3 py-2 ${ `flex cursor-pointer select-none items-center justify-between rounded-md px-3 py-2 ${
active ? "bg-gray-900 bg-opacity-5 text-gray-900" : "" active ? "bg-gray-900 bg-opacity-5 text-brand-base" : ""
}` }`
} }
> >
@ -182,7 +182,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
backgroundColor: issue.state_detail.color, backgroundColor: issue.state_detail.color,
}} }}
/> />
<span className="flex-shrink-0 text-xs text-gray-500"> <span className="flex-shrink-0 text-xs text-brand-secondary">
{issue.project_detail.identifier}-{issue.sequence_id} {issue.project_detail.identifier}-{issue.sequence_id}
</span> </span>
<span>{issue.name}</span> <span>{issue.name}</span>
@ -194,9 +194,9 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
) : ( ) : (
<div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center"> <div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center">
<LayerDiagonalIcon height="56" width="56" /> <LayerDiagonalIcon height="56" width="56" />
<h3 className="text-gray-500"> <h3 className="text-brand-secondary">
No issues found. Create a new issue with{" "} No issues found. Create a new issue with{" "}
<pre className="inline rounded bg-gray-200 px-2 py-1">C</pre>. <pre className="inline rounded bg-brand-surface-2 px-2 py-1">C</pre>.
</h3> </h3>
</div> </div>
)} )}

View File

@ -26,14 +26,17 @@ import {
import { Popover, Transition } from "@headlessui/react"; import { Popover, Transition } from "@headlessui/react";
import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd"; import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd";
import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import StrictModeDroppable from "components/dnd/StrictModeDroppable";
import { CustomMenu } from "components/ui"; import { CustomMenu, Spinner } from "components/ui";
// icon // icon
import { import {
CheckIcon, CheckIcon,
ChevronDownIcon, ChevronDownIcon,
ChevronLeftIcon, ChevronLeftIcon,
ChevronRightIcon, ChevronRightIcon,
PlusIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
// hooks
import useIssuesView from "hooks/use-issues-view";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issues.service";
import cyclesService from "services/cycles.service"; import cyclesService from "services/cycles.service";
@ -49,12 +52,16 @@ import { IIssue } from "types";
import { monthOptions, yearOptions } from "constants/calendar"; import { monthOptions, yearOptions } from "constants/calendar";
import modulesService from "services/modules.service"; import modulesService from "services/modules.service";
type Props = {
addIssueToDate: (date: string) => void;
};
interface ICalendarRange { interface ICalendarRange {
startDate: Date; startDate: Date;
endDate: Date; endDate: Date;
} }
export const CalendarView = () => { export const CalendarView: React.FC<Props> = ({ addIssueToDate }) => {
const [showWeekEnds, setShowWeekEnds] = useState<boolean>(false); const [showWeekEnds, setShowWeekEnds] = useState<boolean>(false);
const [currentDate, setCurrentDate] = useState<Date>(new Date()); const [currentDate, setCurrentDate] = useState<Date>(new Date());
const [isMonthlyView, setIsMonthlyView] = useState<boolean>(true); const [isMonthlyView, setIsMonthlyView] = useState<boolean>(true);
@ -62,6 +69,8 @@ export const CalendarView = () => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query; const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
const { params } = useIssuesView();
const [calendarDateRange, setCalendarDateRange] = useState<ICalendarRange>({ const [calendarDateRange, setCalendarDateRange] = useState<ICalendarRange>({
startDate: startOfWeek(currentDate), startDate: startOfWeek(currentDate),
endDate: lastDayOfWeek(currentDate), endDate: lastDayOfWeek(currentDate),
@ -77,11 +86,13 @@ export const CalendarView = () => {
workspaceSlug && projectId ? PROJECT_CALENDAR_ISSUES(projectId as string) : null, workspaceSlug && projectId ? PROJECT_CALENDAR_ISSUES(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId
? () => ? () =>
issuesService.getIssuesWithParams( issuesService.getIssuesWithParams(workspaceSlug as string, projectId as string, {
workspaceSlug as string, ...params,
projectId as string, target_date: `${renderDateFormat(calendarDateRange.startDate)};after,${renderDateFormat(
targetDateFilter calendarDateRange.endDate
) )};before`,
group_by: null,
})
: null : null
); );
@ -95,7 +106,13 @@ export const CalendarView = () => {
workspaceSlug as string, workspaceSlug as string,
projectId as string, projectId as string,
cycleId as string, cycleId as string,
targetDateFilter {
...params,
target_date: `${renderDateFormat(
calendarDateRange.startDate
)};after,${renderDateFormat(calendarDateRange.endDate)};before`,
group_by: null,
}
) )
: null : null
); );
@ -110,7 +127,13 @@ export const CalendarView = () => {
workspaceSlug as string, workspaceSlug as string,
projectId as string, projectId as string,
moduleId as string, moduleId as string,
targetDateFilter {
...params,
target_date: `${renderDateFormat(
calendarDateRange.startDate
)};after,${renderDateFormat(calendarDateRange.endDate)};before`,
group_by: null,
}
) )
: null : null
); );
@ -127,7 +150,11 @@ export const CalendarView = () => {
const currentViewDays = showWeekEnds ? totalDate : onlyWeekDays; const currentViewDays = showWeekEnds ? totalDate : onlyWeekDays;
const calendarIssues = cycleCalendarIssues ?? moduleCalendarIssues ?? projectCalendarIssues; const calendarIssues = cycleId
? cycleCalendarIssues
: moduleId
? moduleCalendarIssues
: projectCalendarIssues;
const currentViewDaysData = currentViewDays.map((date: Date) => { const currentViewDaysData = currentViewDays.map((date: Date) => {
const filterIssue = const filterIssue =
@ -198,17 +225,17 @@ export const CalendarView = () => {
}); });
}; };
return ( return calendarIssues ? (
<DragDropContext onDragEnd={onDragEnd}> <DragDropContext onDragEnd={onDragEnd}>
<div className="h-full overflow-y-auto rounded-lg text-gray-600 -m-2"> <div className="-m-2 h-full overflow-y-auto rounded-lg text-brand-secondary">
<div className="mb-4 flex items-center justify-between"> <div className="mb-4 flex items-center justify-between">
<div className="relative flex h-full w-full gap-2 items-center justify-start text-sm "> <div className="relative flex h-full w-full items-center justify-start gap-2 text-sm ">
<Popover className="flex h-full items-center justify-start rounded-lg"> <Popover className="flex h-full items-center justify-start rounded-lg">
{({ open }) => ( {({ open }) => (
<> <>
<Popover.Button className={`group flex h-full items-start gap-1 text-gray-800`}> <Popover.Button className={`group flex h-full items-start gap-1 text-brand-base`}>
<div className="flex items-center justify-center gap-2 text-2xl font-semibold"> <div className="flex items-center justify-center gap-2 text-2xl font-semibold">
<span className="text-black">{formatDate(currentDate, "Month")}</span>{" "} <span>{formatDate(currentDate, "Month")}</span>{" "}
<span>{formatDate(currentDate, "yyyy")}</span> <span>{formatDate(currentDate, "yyyy")}</span>
</div> </div>
</Popover.Button> </Popover.Button>
@ -222,30 +249,30 @@ export const CalendarView = () => {
leaveFrom="opacity-100 translate-y-0" leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1" leaveTo="opacity-0 translate-y-1"
> >
<Popover.Panel className="absolute top-10 left-0 z-20 w-full max-w-xs flex flex-col transform overflow-hidden bg-white shadow-lg rounded-[10px]"> <Popover.Panel className="absolute top-10 left-0 z-20 flex w-full max-w-xs transform flex-col overflow-hidden rounded-[10px] bg-brand-surface-2 shadow-lg">
<div className="flex justify-center items-center text-sm gap-5 px-2 py-2"> <div className="flex items-center justify-center gap-5 px-2 py-2 text-sm">
{yearOptions.map((year) => ( {yearOptions.map((year) => (
<button <button
onClick={() => updateDate(updateDateWithYear(year.label, currentDate))} onClick={() => updateDate(updateDateWithYear(year.label, currentDate))}
className={` ${ className={` ${
isSameYear(year.value, currentDate) isSameYear(year.value, currentDate)
? "text-sm font-medium text-gray-800" ? "text-sm font-medium text-brand-base"
: "text-xs text-gray-400 " : "text-xs text-brand-secondary "
} hover:text-sm hover:text-gray-800 hover:font-medium `} } hover:text-sm hover:font-medium hover:text-brand-base`}
> >
{year.label} {year.label}
</button> </button>
))} ))}
</div> </div>
<div className="grid grid-cols-4 px-2 border-t border-gray-200"> <div className="grid grid-cols-4 border-t border-brand-base px-2">
{monthOptions.map((month) => ( {monthOptions.map((month) => (
<button <button
onClick={() => onClick={() =>
updateDate(updateDateWithMonth(month.value, currentDate)) updateDate(updateDateWithMonth(month.value, currentDate))
} }
className={`text-gray-400 text-xs px-2 py-2 hover:font-medium hover:text-gray-800 ${ className={`px-2 py-2 text-xs text-brand-secondary hover:font-medium hover:text-brand-base ${
isSameMonth(month.value, currentDate) isSameMonth(month.value, currentDate)
? "font-medium text-gray-800" ? "font-medium text-brand-base"
: "" : ""
}`} }`}
> >
@ -295,9 +322,9 @@ export const CalendarView = () => {
</div> </div>
</div> </div>
<div className="flex w-full gap-2 items-center justify-end"> <div className="flex w-full items-center justify-end gap-2">
<button <button
className="group flex cursor-pointer items-center gap-2 rounded-md border bg-white px-4 py-1.5 text-sm hover:bg-gray-100 hover:text-gray-900 focus:outline-none" className="group flex cursor-pointer items-center gap-2 rounded-md border border-brand-base bg-brand-surface-2 px-4 py-1.5 text-sm hover:bg-brand-surface-1 hover:text-brand-base focus:outline-none"
onClick={() => { onClick={() => {
if (isMonthlyView) { if (isMonthlyView) {
updateDate(new Date()); updateDate(new Date());
@ -312,10 +339,11 @@ export const CalendarView = () => {
> >
Today{" "} Today{" "}
</button> </button>
<CustomMenu <CustomMenu
customButton={ customButton={
<div <div
className={`group flex cursor-pointer items-center gap-2 rounded-md border bg-white px-3 py-1.5 text-sm hover:bg-gray-100 hover:text-gray-900 focus:outline-none `} className={`group flex cursor-pointer items-center gap-2 rounded-md border border-brand-base bg-brand-surface-2 px-3 py-1.5 text-sm hover:bg-brand-surface-1 hover:text-brand-base focus:outline-none `}
> >
{isMonthlyView ? "Monthly" : "Weekly"} {isMonthlyView ? "Monthly" : "Weekly"}
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" /> <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
@ -330,7 +358,7 @@ export const CalendarView = () => {
endDate: lastDayOfWeek(currentDate), endDate: lastDayOfWeek(currentDate),
}); });
}} }}
className="w-52 text-sm text-gray-600" className="w-52 text-sm text-brand-secondary"
> >
<div className="flex w-full max-w-[260px] items-center justify-between gap-2"> <div className="flex w-full max-w-[260px] items-center justify-between gap-2">
<span className="flex items-center gap-2">Monthly View</span> <span className="flex items-center gap-2">Monthly View</span>
@ -349,7 +377,7 @@ export const CalendarView = () => {
endDate: getCurrentWeekEndDate(currentDate), endDate: getCurrentWeekEndDate(currentDate),
}); });
}} }}
className="w-52 text-sm text-gray-600" className="w-52 text-sm text-brand-secondary"
> >
<div className="flex w-full items-center justify-between gap-2"> <div className="flex w-full items-center justify-between gap-2">
<span className="flex items-center gap-2">Weekly View</span> <span className="flex items-center gap-2">Weekly View</span>
@ -360,12 +388,12 @@ export const CalendarView = () => {
/> />
</div> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
<div className="mt-1 flex w-52 items-center justify-between border-t border-gray-200 py-2 px-1 text-sm text-gray-600"> <div className="mt-1 flex w-52 items-center justify-between border-t border-brand-base py-2 px-1 text-sm text-brand-secondary">
<h4>Show weekends</h4> <h4>Show weekends</h4>
<button <button
type="button" type="button"
className={`relative inline-flex h-3.5 w-6 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${ className={`relative inline-flex h-3.5 w-6 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${
showWeekEnds ? "bg-green-500" : "bg-gray-200" showWeekEnds ? "bg-green-500" : "bg-brand-surface-2"
}`} }`}
role="switch" role="switch"
aria-checked={showWeekEnds} aria-checked={showWeekEnds}
@ -374,7 +402,7 @@ export const CalendarView = () => {
<span className="sr-only">Show weekends</span> <span className="sr-only">Show weekends</span>
<span <span
aria-hidden="true" aria-hidden="true"
className={`inline-block h-2.5 w-2.5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${ className={`inline-block h-2.5 w-2.5 transform rounded-full bg-brand-surface-2 shadow ring-0 transition duration-200 ease-in-out ${
showWeekEnds ? "translate-x-2.5" : "translate-x-0" showWeekEnds ? "translate-x-2.5" : "translate-x-0"
}`} }`}
/> />
@ -392,7 +420,7 @@ export const CalendarView = () => {
{weeks.map((date, index) => ( {weeks.map((date, index) => (
<div <div
key={index} key={index}
className={`flex items-center justify-start p-1.5 gap-2 border-gray-300 bg-gray-100 text-base font-medium text-gray-600 ${ className={`flex items-center justify-start gap-2 border-brand-base bg-brand-surface-1 p-1.5 text-base font-medium text-brand-secondary ${
!isMonthlyView !isMonthlyView
? showWeekEnds ? showWeekEnds
? (index + 1) % 7 === 0 ? (index + 1) % 7 === 0
@ -424,7 +452,7 @@ export const CalendarView = () => {
key={index} key={index}
ref={provided.innerRef} ref={provided.innerRef}
{...provided.droppableProps} {...provided.droppableProps}
className={`flex flex-col gap-1.5 border-t border-gray-300 p-2.5 text-left text-sm font-medium hover:bg-gray-100 ${ className={`group flex flex-col gap-1.5 border-t border-brand-base p-2.5 text-left text-sm font-medium hover:bg-brand-surface-1 ${
showWeekEnds showWeekEnds
? (index + 1) % 7 === 0 ? (index + 1) % 7 === 0
? "" ? ""
@ -444,7 +472,7 @@ export const CalendarView = () => {
ref={provided.innerRef} ref={provided.innerRef}
{...provided.draggableProps} {...provided.draggableProps}
{...provided.dragHandleProps} {...provided.dragHandleProps}
className={`w-full cursor-pointer truncate rounded bg-white p-1.5 hover:scale-105 ${ className={`w-full cursor-pointer truncate rounded bg-brand-surface-2 p-1.5 hover:scale-105 ${
snapshot.isDragging ? "shadow-lg" : "" snapshot.isDragging ? "shadow-lg" : ""
}`} }`}
> >
@ -458,6 +486,15 @@ export const CalendarView = () => {
)} )}
</Draggable> </Draggable>
))} ))}
<div className="flex items-center justify-center p-1.5 text-sm text-brand-secondary opacity-0 group-hover:opacity-100">
<button
className="flex items-center justify-center gap-2 text-center"
onClick={() => addIssueToDate(date.date)}
>
<PlusIcon className="h-4 w-4 text-brand-secondary" />
Add new issue
</button>
</div>
{provided.placeholder} {provided.placeholder}
</div> </div>
)} )}
@ -466,5 +503,9 @@ export const CalendarView = () => {
</div> </div>
</div> </div>
</DragDropContext> </DragDropContext>
) : (
<div className="flex h-full w-full items-center justify-center">
<Spinner />
</div>
); );
}; };

View File

@ -0,0 +1,267 @@
import { useEffect, useState } from "react";
// react-hook-form
import { useForm } from "react-hook-form";
// ui
import { Input, PrimaryButton, SecondaryButton } from "components/ui";
const defaultValues = {
palette: "",
};
export const ThemeForm: React.FC<any> = ({ handleFormSubmit, handleClose, status, data }) => {
const {
register,
formState: { errors, isSubmitting },
handleSubmit,
reset,
} = useForm<any>({
defaultValues,
});
const [darkPalette, setDarkPalette] = useState(false);
const handleUpdateTheme = async (formData: any) => {
await handleFormSubmit({ ...formData, darkPalette });
reset({
...defaultValues,
});
};
useEffect(() => {
reset({
...defaultValues,
...data,
});
}, [data, reset]);
// --color-bg-base: 25, 27, 27;
// --color-bg-surface-1: 31, 32, 35;
// --color-bg-surface-2: 39, 42, 45;
// --color-border: 46, 50, 52;
// --color-bg-sidebar: 19, 20, 22;
// --color-accent: 60, 133, 217;
// --color-text-base: 255, 255, 255;
// --color-text-secondary: 142, 148, 146;
return (
<form onSubmit={handleSubmit(handleUpdateTheme)}>
<div className="space-y-5">
<h3 className="text-lg font-medium leading-6 text-brand-base">Customize your theme</h3>
<div className="space-y-4">
<div className="mt-6 grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-6">
<div className="sm:col-span-2">
<Input
id="bgBase"
label="Background"
name="bgBase"
type="name"
placeholder="#FFFFFF"
autoComplete="off"
error={errors.bgBase}
register={register}
validations={{
required: "Background color is required",
pattern: {
value: /^#(?:[0-9a-fA-F]{3}){1,2}$/g,
message: "Background color should be hex format",
},
}}
/>
</div>
<div className="sm:col-span-2">
<Input
id="bgSurface1"
label="Background surface 1"
name="bgSurface1"
type="name"
placeholder="#FFFFFF"
autoComplete="off"
error={errors.bgSurface1}
register={register}
validations={{
required: "Background surface 1 color is required",
pattern: {
value: /^#(?:[0-9a-fA-F]{3}){1,2}$/g,
message: "Background surface 1 color should be hex format",
},
}}
/>
</div>
<div className="sm:col-span-2">
<Input
id="bgSurface2"
label="Background surface 2"
name="bgSurface1"
type="name"
placeholder="#FFFFFF"
autoComplete="off"
error={errors.bgSurface1}
register={register}
validations={{
required: "Background surface 2 color is required",
pattern: {
value: /^#(?:[0-9a-fA-F]{3}){1,2}$/g,
message: "Background surface 2 color should be hex format",
},
}}
/>
</div>
<div className="sm:col-span-2">
<Input
id="border"
label="Border"
name="border"
type="name"
placeholder="#FFFFFF"
autoComplete="off"
error={errors.border}
register={register}
validations={{
required: "Border color is required",
pattern: {
value: /^#(?:[0-9a-fA-F]{3}){1,2}$/g,
message: "Border color should be hex format",
},
}}
/>
</div>
<div className="sm:col-span-2">
<Input
id="sidebar"
label="Sidebar"
name="sidebar"
type="name"
placeholder="#FFFFFF"
autoComplete="off"
error={errors.sidebar}
register={register}
validations={{
required: "Sidebar color is required",
pattern: {
value: /^#(?:[0-9a-fA-F]{3}){1,2}$/g,
message: "Sidebar color should be hex format",
},
}}
/>
</div>
<div className="sm:col-span-2">
<Input
id="accent"
label="Accent"
name="accent"
type="name"
placeholder="#FFFFFF"
autoComplete="off"
error={errors.accent}
register={register}
validations={{
required: "Accent color is required",
pattern: {
value: /^#(?:[0-9a-fA-F]{3}){1,2}$/g,
message: "Accent color should be hex format",
},
}}
/>
</div>
<div className="sm:col-span-3">
<Input
id="textBase"
label="Text primary"
name="textBase"
type="name"
placeholder="#FFFFFF"
autoComplete="off"
error={errors.textBase}
register={register}
validations={{
required: "Text primary color is required",
pattern: {
value: /^#(?:[0-9a-fA-F]{3}){1,2}$/g,
message: "Text primary color should be hex format",
},
}}
/>
</div>
<div className="sm:col-span-3">
<Input
id="textSecondary"
label="Text secondary"
name="textSecondary"
type="name"
placeholder="#FFFFFF"
autoComplete="off"
error={errors.textSecondary}
register={register}
validations={{
required: "Text secondary color is required",
pattern: {
value: /^#(?:[0-9a-fA-F]{3}){1,2}$/g,
message: "Text secondary color should be hex format",
},
}}
/>
</div>
</div>
<div>
<Input
id="palette"
label="All colors"
name="palette"
type="name"
placeholder="Enter comma separated hex colors"
autoComplete="off"
error={errors.palette}
register={register}
validations={{
required: "Color values is required",
pattern: {
value: /^(#(?:[0-9a-fA-F]{3}){1,2},){7}#(?:[0-9a-fA-F]{3}){1,2}$/g,
message: "Color values should be hex format, separated by commas",
},
}}
/>
</div>
<div
className="flex cursor-pointer items-center gap-1"
onClick={() => setDarkPalette((prevData) => !prevData)}
>
<span className="text-xs">Dark palette</span>
<button
type="button"
className={`pointer-events-none relative inline-flex h-4 w-7 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent ${
darkPalette ? "bg-brand-accent" : "bg-gray-300"
} transition-colors duration-300 ease-in-out focus:outline-none`}
role="switch"
aria-checked="false"
>
<span className="sr-only">Dark palette</span>
<span
aria-hidden="true"
className={`pointer-events-none inline-block h-3 w-3 ${
darkPalette ? "translate-x-3" : "translate-x-0"
} transform rounded-full bg-brand-surface-2 shadow ring-0 transition duration-300 ease-in-out`}
/>
</button>
</div>
</div>
</div>
<div className="mt-5 flex justify-end gap-2">
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
<PrimaryButton type="submit" loading={isSubmitting}>
{status
? isSubmitting
? "Updating Theme..."
: "Update Theme"
: isSubmitting
? "Creating Theme..."
: "Set Theme"}
</PrimaryButton>
</div>
</form>
);
};

View File

@ -0,0 +1,65 @@
import React from "react";
// headless ui
import { Dialog, Transition } from "@headlessui/react";
// components
import { ThemeForm } from "./custom-theme-form";
// helpers
import { applyTheme } from "helpers/theme.helper";
// fetch-keys
type Props = {
isOpen: boolean;
handleClose: () => void;
};
export const CustomThemeModal: React.FC<Props> = ({ isOpen, handleClose }) => {
const onClose = () => {
handleClose();
};
const handleFormSubmit = async (formData: any) => {
applyTheme(formData.palette, formData.darkPalette);
onClose();
};
return (
<Transition.Root show={isOpen} as={React.Fragment}>
<Dialog as="div" className="relative z-20" onClose={handleClose}>
<Transition.Child
as={React.Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-20 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-brand-surface-1 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<ThemeForm
handleClose={handleClose}
handleFormSubmit={handleFormSubmit}
status={false}
/>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
};

View File

@ -130,7 +130,7 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-white shadow-2xl ring-1 ring-black ring-opacity-5 transition-all"> <Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-brand-surface-2 shadow-2xl ring-1 ring-black ring-opacity-5 transition-all">
<form> <form>
<Controller <Controller
control={control} control={control}
@ -139,11 +139,11 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
<Combobox as="div" {...field} multiple> <Combobox as="div" {...field} multiple>
<div className="relative m-1"> <div className="relative m-1">
<MagnifyingGlassIcon <MagnifyingGlassIcon
className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-gray-900 text-opacity-40" className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-brand-base text-opacity-40"
aria-hidden="true" aria-hidden="true"
/> />
<Combobox.Input <Combobox.Input
className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-gray-900 placeholder-gray-500 outline-none focus:ring-0 sm:text-sm" className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-brand-base placeholder-gray-500 outline-none focus:ring-0 sm:text-sm"
placeholder="Search..." placeholder="Search..."
onChange={(e) => setQuery(e.target.value)} onChange={(e) => setQuery(e.target.value)}
/> />
@ -156,7 +156,7 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
{filteredIssues.length > 0 ? ( {filteredIssues.length > 0 ? (
<li className="p-2"> <li className="p-2">
{query === "" && ( {query === "" && (
<h2 className="mb-2 px-3 text-xs font-semibold text-gray-900"> <h2 className="mb-2 px-3 text-xs font-semibold text-brand-base">
Select issues to add Select issues to add
</h2> </h2>
)} )}
@ -169,7 +169,7 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
value={issue.id} value={issue.id}
className={({ active }) => className={({ active }) =>
`flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 ${ `flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 ${
active ? "bg-gray-900 bg-opacity-5 text-gray-900" : "" active ? "bg-gray-900 bg-opacity-5 text-brand-base" : ""
}` }`
} }
> >
@ -182,7 +182,7 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
backgroundColor: issue.state_detail.color, backgroundColor: issue.state_detail.color,
}} }}
/> />
<span className="flex-shrink-0 text-xs text-gray-500"> <span className="flex-shrink-0 text-xs text-brand-secondary">
{issue.project_detail.identifier}-{issue.sequence_id} {issue.project_detail.identifier}-{issue.sequence_id}
</span> </span>
{issue.name} {issue.name}
@ -195,9 +195,9 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
) : ( ) : (
<div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center"> <div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center">
<LayerDiagonalIcon height="56" width="56" /> <LayerDiagonalIcon height="56" width="56" />
<h3 className="text-gray-500"> <h3 className="text-brand-secondary">
No issues found. Create a new issue with{" "} No issues found. Create a new issue with{" "}
<pre className="inline rounded bg-gray-200 px-2 py-1">C</pre>. <pre className="inline rounded bg-brand-surface-2 px-2 py-1">C</pre>.
</h3> </h3>
</div> </div>
)} )}

View File

@ -55,23 +55,23 @@ const activityDetails: {
}, },
modules: { modules: {
message: "set the module to", message: "set the module to",
icon: <RectangleGroupIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <RectangleGroupIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
state: { state: {
message: "set the state to", message: "set the state to",
icon: <Squares2X2Icon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <Squares2X2Icon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
priority: { priority: {
message: "set the priority to", message: "set the priority to",
icon: <ChartBarIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <ChartBarIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
name: { name: {
message: "set the name to", message: "set the name to",
icon: <ChatBubbleBottomCenterTextIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <ChatBubbleBottomCenterTextIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
description: { description: {
message: "updated the description.", message: "updated the description.",
icon: <ChatBubbleBottomCenterTextIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <ChatBubbleBottomCenterTextIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
estimate_point: { estimate_point: {
message: "set the estimate point to", message: "set the estimate point to",
@ -79,15 +79,15 @@ const activityDetails: {
}, },
target_date: { target_date: {
message: "set the due date to", message: "set the due date to",
icon: <CalendarDaysIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <CalendarDaysIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
parent: { parent: {
message: "set the parent to", message: "set the parent to",
icon: <UserIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <UserIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
issue: { issue: {
message: "deleted the issue.", message: "deleted the issue.",
icon: <TrashIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <TrashIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
estimate: { estimate: {
message: "updated the estimate", message: "updated the estimate",
@ -217,7 +217,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
</div> </div>
)} )}
<span className="absolute -bottom-0.5 -right-1 rounded-tl bg-white px-0.5 py-px"> <span className="absolute -bottom-0.5 -right-1 rounded-tl bg-brand-surface-2 px-0.5 py-px">
<ChatBubbleLeftEllipsisIcon <ChatBubbleLeftEllipsisIcon
className="h-3.5 w-3.5 text-gray-400" className="h-3.5 w-3.5 text-gray-400"
aria-hidden="true" aria-hidden="true"
@ -230,7 +230,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
{activity.actor_detail.first_name} {activity.actor_detail.first_name}
{activity.actor_detail.is_bot ? "Bot" : " " + activity.actor_detail.last_name} {activity.actor_detail.is_bot ? "Bot" : " " + activity.actor_detail.last_name}
</div> </div>
<p className="mt-0.5 text-xs text-gray-500"> <p className="mt-0.5 text-xs text-brand-secondary">
Commented {timeAgo(activity.created_at)} Commented {timeAgo(activity.created_at)}
</p> </p>
</div> </div>
@ -244,7 +244,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
editable={false} editable={false}
onBlur={() => ({})} onBlur={() => ({})}
noBorder noBorder
customClassName="text-xs bg-gray-100" customClassName="text-xs bg-brand-surface-1"
/> />
</div> </div>
</div> </div>
@ -259,7 +259,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
<div className="relative pb-1"> <div className="relative pb-1">
{activities.length > 1 && activityIdx !== activities.length - 1 ? ( {activities.length > 1 && activityIdx !== activities.length - 1 ? (
<span <span
className="absolute top-5 left-5 -ml-px h-full w-0.5 bg-gray-200" className="absolute top-5 left-5 -ml-px h-full w-0.5 bg-brand-surface-2"
aria-hidden="true" aria-hidden="true"
/> />
) : null} ) : null}
@ -268,7 +268,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
<div> <div>
<div className="relative px-1.5"> <div className="relative px-1.5">
<div className="mt-1.5"> <div className="mt-1.5">
<div className="ring-6 flex h-7 w-7 items-center justify-center rounded-full bg-gray-100 ring-white"> <div className="ring-6 flex h-7 w-7 items-center justify-center rounded-full bg-brand-surface-1 ring-white">
{activity.field ? ( {activity.field ? (
activityDetails[activity.field as keyof typeof activityDetails]?.icon activityDetails[activity.field as keyof typeof activityDetails]?.icon
) : activity.actor_detail.avatar && ) : activity.actor_detail.avatar &&
@ -292,7 +292,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
</div> </div>
</div> </div>
<div className="min-w-0 flex-1 py-3"> <div className="min-w-0 flex-1 py-3">
<div className="text-xs text-gray-500"> <div className="text-xs text-brand-secondary">
<span className="text-gray font-medium"> <span className="text-gray font-medium">
{activity.actor_detail.first_name} {activity.actor_detail.first_name}
{activity.actor_detail.is_bot {activity.actor_detail.is_bot
@ -300,7 +300,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
: " " + activity.actor_detail.last_name} : " " + activity.actor_detail.last_name}
</span> </span>
<span> {action} </span> <span> {action} </span>
<span className="text-xs font-medium text-gray-900"> {value} </span> <span className="text-xs font-medium text-brand-base"> {value} </span>
<span className="whitespace-nowrap">{timeAgo(activity.created_at)}</span> <span className="whitespace-nowrap">{timeAgo(activity.created_at)}</span>
</div> </div>
</div> </div>

View File

@ -57,9 +57,9 @@ export const FilterList: React.FC<any> = ({ filters, setFilters }) => {
return ( return (
<div <div
key={key} key={key}
className="flex items-center gap-x-2 rounded-full border bg-white px-2 py-1" className="flex items-center gap-x-2 rounded-full border border-brand-base bg-brand-surface-2 px-2 py-1"
> >
<span className="font-medium capitalize text-gray-500"> <span className="font-medium capitalize text-brand-secondary">
{replaceUnderscoreIfSnakeCase(key)}: {replaceUnderscoreIfSnakeCase(key)}:
</span> </span>
{filters[key as keyof IIssueFilterOptions] === null || {filters[key as keyof IIssueFilterOptions] === null ||
@ -131,7 +131,7 @@ export const FilterList: React.FC<any> = ({ filters, setFilters }) => {
? "bg-yellow-100 text-yellow-500 hover:bg-yellow-100" ? "bg-yellow-100 text-yellow-500 hover:bg-yellow-100"
: priority === "low" : priority === "low"
? "bg-green-100 text-green-500 hover:bg-green-100" ? "bg-green-100 text-green-500 hover:bg-green-100"
: "bg-gray-100 text-gray-700 hover:bg-gray-100" : "bg-brand-surface-1 text-gray-700 hover:bg-brand-surface-1"
}`} }`}
> >
<span>{getPriorityIcon(priority)}</span> <span>{getPriorityIcon(priority)}</span>
@ -339,7 +339,7 @@ export const FilterList: React.FC<any> = ({ filters, setFilters }) => {
created_by: null, created_by: null,
}) })
} }
className="flex items-center gap-x-1 rounded-full border bg-white px-3 py-1.5 text-xs" className="flex items-center gap-x-1 rounded-full border border-brand-base bg-brand-surface-2 px-3 py-1.5 text-xs"
> >
<span className="font-medium">Clear all filters</span> <span className="font-medium">Clear all filters</span>
<XMarkIcon className="h-4 w-4" /> <XMarkIcon className="h-4 w-4" />

View File

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

View File

@ -65,7 +65,7 @@ export const ImagePickerPopover: React.FC<Props> = ({ label, value, onChange })
return ( return (
<Popover className="relative z-[2]" ref={ref}> <Popover className="relative z-[2]" ref={ref}>
<Popover.Button <Popover.Button
className="rounded border border-gray-500 bg-white px-2 py-1 text-xs text-gray-700" className="rounded-md border border-brand-base bg-brand-surface-2 px-2 py-1 text-xs text-gray-700"
onClick={() => setIsOpen((prev) => !prev)} onClick={() => setIsOpen((prev) => !prev)}
> >
{label} {label}
@ -79,16 +79,16 @@ export const ImagePickerPopover: React.FC<Props> = ({ label, value, onChange })
leaveFrom="transform opacity-100 scale-100" leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95" leaveTo="transform opacity-0 scale-95"
> >
<Popover.Panel className="absolute right-0 z-10 mt-2 rounded-md bg-white shadow-lg"> <Popover.Panel className="absolute right-0 z-10 mt-2 rounded-md bg-brand-surface-2 shadow-lg">
<div className="h-96 w-80 overflow-auto rounded border bg-white p-5 shadow-2xl sm:max-w-2xl md:w-96 lg:w-[40rem]"> <div className="h-96 w-80 overflow-auto rounded border border-brand-base bg-brand-surface-2 p-5 shadow-2xl sm:max-w-2xl md:w-96 lg:w-[40rem]">
<Tab.Group> <Tab.Group>
<Tab.List as="span" className="inline-block rounded bg-gray-200 p-1"> <Tab.List as="span" className="inline-block rounded bg-brand-surface-2 p-1">
{tabOptions.map((tab) => ( {tabOptions.map((tab) => (
<Tab <Tab
key={tab.key} key={tab.key}
className={({ selected }) => className={({ selected }) =>
`rounded py-1 px-4 text-center text-sm outline-none transition-colors ${ `rounded py-1 px-4 text-center text-sm outline-none transition-colors ${
selected ? "bg-theme text-white" : "text-black" selected ? "bg-brand-accent text-white" : "text-brand-base"
}` }`
} }
> >

View File

@ -110,7 +110,7 @@ export const ImageUploadModal: React.FC<Props> = ({
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> <div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 z-30 overflow-y-auto"> <div className="fixed inset-0 z-30 overflow-y-auto">
@ -124,9 +124,9 @@ export const ImageUploadModal: React.FC<Props> = ({
leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
> >
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white 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 bg-brand-surface-2 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"> <div className="space-y-5">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900"> <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-brand-base">
Upload Image Upload Image
</Dialog.Title> </Dialog.Title>
<div className="space-y-3"> <div className="space-y-3">
@ -135,7 +135,7 @@ export const ImageUploadModal: React.FC<Props> = ({
{...getRootProps()} {...getRootProps()}
className={`relative block h-80 w-full rounded-lg p-12 text-center focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 ${ className={`relative block h-80 w-full rounded-lg p-12 text-center focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 ${
(image === null && isDragActive) || !value (image === null && isDragActive) || !value
? "border-2 border-dashed border-gray-300 hover:border-gray-400" ? "border-2 border-dashed border-brand-base hover:border-gray-400"
: "" : ""
}`} }`}
> >
@ -143,7 +143,7 @@ export const ImageUploadModal: React.FC<Props> = ({
<> <>
<button <button
type="button" type="button"
className="absolute top-0 right-0 z-40 translate-x-1/2 -translate-y-1/2 rounded bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-600" className="absolute top-0 right-0 z-40 translate-x-1/2 -translate-y-1/2 rounded bg-brand-surface-1 px-2 py-0.5 text-xs font-medium text-gray-600"
> >
Edit Edit
</button> </button>
@ -157,7 +157,7 @@ export const ImageUploadModal: React.FC<Props> = ({
) : ( ) : (
<> <>
<UserCircleIcon className="mx-auto h-16 w-16 text-gray-400" /> <UserCircleIcon className="mx-auto h-16 w-16 text-gray-400" />
<span className="mt-2 block text-sm font-medium text-gray-900"> <span className="mt-2 block text-sm font-medium text-brand-base">
{isDragActive {isDragActive
? "Drop image here to upload" ? "Drop image here to upload"
: "Drag & drop image here"} : "Drag & drop image here"}

View File

@ -11,3 +11,4 @@ export * from "./link-modal";
export * from "./image-picker-popover"; export * from "./image-picker-popover";
export * from "./filter-list"; export * from "./filter-list";
export * from "./feeds"; export * from "./feeds";
export * from "./theme-switch";

View File

@ -12,8 +12,12 @@ import { SelectFilters } from "components/views";
// ui // ui
import { CustomMenu } from "components/ui"; import { CustomMenu } from "components/ui";
// icons // icons
import { ChevronDownIcon, ListBulletIcon, CalendarDaysIcon } from "@heroicons/react/24/outline"; import {
import { Squares2X2Icon } from "@heroicons/react/20/solid"; ChevronDownIcon,
ListBulletIcon,
Squares2X2Icon,
CalendarDaysIcon,
} from "@heroicons/react/24/outline";
// helpers // helpers
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
// types // types
@ -53,30 +57,30 @@ export const IssuesFilterView: React.FC = () => {
<div className="flex items-center gap-x-1"> <div className="flex items-center gap-x-1">
<button <button
type="button" type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200 ${ className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-brand-surface-2 ${
issueView === "list" ? "bg-gray-200" : "" issueView === "list" ? "bg-brand-surface-2" : ""
}`} }`}
onClick={() => setIssueView("list")} onClick={() => setIssueView("list")}
> >
<ListBulletIcon className="h-4 w-4" /> <ListBulletIcon className="h-4 w-4 text-brand-secondary" />
</button> </button>
<button <button
type="button" type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200 ${ className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-brand-surface-2 ${
issueView === "kanban" ? "bg-gray-200" : "" issueView === "kanban" ? "bg-brand-surface-2" : ""
}`} }`}
onClick={() => setIssueView("kanban")} onClick={() => setIssueView("kanban")}
> >
<Squares2X2Icon className="h-4 w-4" /> <Squares2X2Icon className="h-4 w-4 text-brand-secondary" />
</button> </button>
<button <button
type="button" type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200 ${ className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-brand-surface-2 ${
issueView === "calendar" ? "bg-gray-200" : "" issueView === "calendar" ? "bg-brand-surface-2" : ""
}`} }`}
onClick={() => setIssueView("calendar")} onClick={() => setIssueView("calendar")}
> >
<CalendarDaysIcon className="h-4 w-4" /> <CalendarDaysIcon className="h-4 w-4 text-brand-secondary" />
</button> </button>
</div> </div>
<SelectFilters <SelectFilters
@ -113,8 +117,8 @@ export const IssuesFilterView: React.FC = () => {
{({ open }) => ( {({ open }) => (
<> <>
<Popover.Button <Popover.Button
className={`group flex items-center gap-2 rounded-md border bg-transparent px-3 py-1.5 text-xs hover:bg-gray-100 hover:text-gray-900 focus:outline-none ${ className={`group flex items-center gap-2 rounded-md border border-brand-base bg-transparent px-3 py-1.5 text-xs hover:bg-brand-surface-1 hover:text-brand-base focus:outline-none ${
open ? "bg-gray-100 text-gray-900" : "text-gray-500" open ? "bg-brand-surface-1 text-brand-base" : "text-brand-secondary"
}`} }`}
> >
View View
@ -130,55 +134,59 @@ export const IssuesFilterView: React.FC = () => {
leaveFrom="opacity-100 translate-y-0" leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1" leaveTo="opacity-0 translate-y-1"
> >
<Popover.Panel className="absolute right-0 z-20 mt-1 w-screen max-w-xs transform overflow-hidden rounded-lg bg-white p-3 shadow-lg"> <Popover.Panel className="absolute right-0 z-20 mt-1 w-screen max-w-xs transform overflow-hidden rounded-lg border border-brand-base bg-brand-surface-1 p-3 shadow-lg">
<div className="relative divide-y-2"> <div className="relative divide-y-2 divide-brand-base">
<div className="space-y-4 pb-3 text-xs"> <div className="space-y-4 pb-3 text-xs">
{issueView !== "calendar" && (
<>
<div className="flex items-center justify-between">
<h4 className="text-brand-secondary">Group by</h4>
<CustomMenu
label={
GROUP_BY_OPTIONS.find((option) => option.key === groupByProperty)
?.name ?? "Select"
}
width="lg"
>
{GROUP_BY_OPTIONS.map((option) =>
issueView === "kanban" && option.key === null ? null : (
<CustomMenu.MenuItem
key={option.key}
onClick={() => setGroupByProperty(option.key)}
>
{option.name}
</CustomMenu.MenuItem>
)
)}
</CustomMenu>
</div>
<div className="flex items-center justify-between">
<h4 className="text-brand-secondary">Order by</h4>
<CustomMenu
label={
ORDER_BY_OPTIONS.find((option) => option.key === orderBy)?.name ??
"Select"
}
width="lg"
>
{ORDER_BY_OPTIONS.map((option) =>
groupByProperty === "priority" && option.key === "priority" ? null : (
<CustomMenu.MenuItem
key={option.key}
onClick={() => {
setOrderBy(option.key);
}}
>
{option.name}
</CustomMenu.MenuItem>
)
)}
</CustomMenu>
</div>
</>
)}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h4 className="text-gray-600">Group by</h4> <h4 className="text-brand-secondary">Issue type</h4>
<CustomMenu
label={
GROUP_BY_OPTIONS.find((option) => option.key === groupByProperty)?.name ??
"Select"
}
width="lg"
>
{GROUP_BY_OPTIONS.map((option) =>
issueView === "kanban" && option.key === null ? null : (
<CustomMenu.MenuItem
key={option.key}
onClick={() => setGroupByProperty(option.key)}
>
{option.name}
</CustomMenu.MenuItem>
)
)}
</CustomMenu>
</div>
<div className="flex items-center justify-between">
<h4 className="text-gray-600">Order by</h4>
<CustomMenu
label={
ORDER_BY_OPTIONS.find((option) => option.key === orderBy)?.name ??
"Select"
}
width="lg"
>
{ORDER_BY_OPTIONS.map((option) =>
groupByProperty === "priority" && option.key === "priority" ? null : (
<CustomMenu.MenuItem
key={option.key}
onClick={() => {
setOrderBy(option.key);
}}
>
{option.name}
</CustomMenu.MenuItem>
)
)}
</CustomMenu>
</div>
<div className="flex items-center justify-between">
<h4 className="text-gray-600">Issue type</h4>
<CustomMenu <CustomMenu
label={ label={
FILTER_ISSUE_OPTIONS.find((option) => option.key === filters.type) FILTER_ISSUE_OPTIONS.find((option) => option.key === filters.type)
@ -200,62 +208,69 @@ export const IssuesFilterView: React.FC = () => {
))} ))}
</CustomMenu> </CustomMenu>
</div> </div>
<div className="flex items-center justify-between">
<h4 className="text-gray-600">Show empty states</h4>
<button
type="button"
className={`relative inline-flex h-3.5 w-6 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${
showEmptyGroups ? "bg-green-500" : "bg-gray-200"
}`}
role="switch"
aria-checked={showEmptyGroups}
onClick={() => setShowEmptyGroups(!showEmptyGroups)}
>
<span className="sr-only">Show empty groups</span>
<span
aria-hidden="true"
className={`inline-block h-2.5 w-2.5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
showEmptyGroups ? "translate-x-2.5" : "translate-x-0"
}`}
/>
</button>
</div>
<div className="relative flex justify-end gap-x-3">
<button type="button" onClick={() => resetFilterToDefault()}>
Reset to default
</button>
<button
type="button"
className="font-medium text-theme"
onClick={() => setNewFilterDefaultView()}
>
Set as default
</button>
</div>
</div>
<div className="space-y-2 py-3">
<h4 className="text-sm text-gray-600">Display Properties</h4>
<div className="flex flex-wrap items-center gap-2">
{Object.keys(properties).map((key) => {
if (key === "estimate" && !isEstimateActive) return null;
return ( {issueView !== "calendar" && (
<>
<div className="flex items-center justify-between">
<h4 className="text-brand-secondary">Show empty states</h4>
<button <button
key={key}
type="button" type="button"
className={`rounded border px-2 py-1 text-xs capitalize ${ className={`relative inline-flex h-3.5 w-6 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${
properties[key as keyof Properties] showEmptyGroups ? "bg-green-500" : "bg-brand-surface-2"
? "border-theme bg-theme text-white"
: "border-gray-300"
}`} }`}
onClick={() => setProperties(key as keyof Properties)} role="switch"
aria-checked={showEmptyGroups}
onClick={() => setShowEmptyGroups(!showEmptyGroups)}
> >
{key === "key" ? "ID" : replaceUnderscoreIfSnakeCase(key)} <span className="sr-only">Show empty groups</span>
<span
aria-hidden="true"
className={`inline-block h-2.5 w-2.5 transform rounded-full bg-brand-surface-2 shadow ring-0 transition duration-200 ease-in-out ${
showEmptyGroups ? "translate-x-2.5" : "translate-x-0"
}`}
/>
</button> </button>
); </div>
})} <div className="relative flex justify-end gap-x-3">
</div> <button type="button" onClick={() => resetFilterToDefault()}>
Reset to default
</button>
<button
type="button"
className="font-medium text-brand-accent"
onClick={() => setNewFilterDefaultView()}
>
Set as default
</button>
</div>
</>
)}
</div> </div>
{issueView !== "calendar" && (
<div className="space-y-2 py-3">
<h4 className="text-sm text-brand-secondary">Display Properties</h4>
<div className="flex flex-wrap items-center gap-2">
{Object.keys(properties).map((key) => {
if (key === "estimate" && !isEstimateActive) return null;
return (
<button
key={key}
type="button"
className={`rounded border px-2 py-1 text-xs capitalize ${
properties[key as keyof Properties]
? "border-brand-accent bg-brand-accent text-brand-base"
: "border-brand-base"
}`}
onClick={() => setProperties(key as keyof Properties)}
>
{key === "key" ? "ID" : replaceUnderscoreIfSnakeCase(key)}
</button>
);
})}
</div>
</div>
)}
</div> </div>
</Popover.Panel> </Popover.Panel>
</Transition> </Transition>

View File

@ -280,6 +280,17 @@ export const IssuesView: React.FC<Props> = ({
[setCreateIssueModal, setPreloadedData, selectedGroup] [setCreateIssueModal, setPreloadedData, selectedGroup]
); );
const addIssueToDate = useCallback(
(date: string) => {
setCreateIssueModal(true);
setPreloadedData({
target_date: date,
actionType: "createIssue",
});
},
[setCreateIssueModal, setPreloadedData]
);
const makeIssueCopy = useCallback( const makeIssueCopy = useCallback(
(issue: IIssue) => { (issue: IIssue) => {
setCreateIssueModal(true); setCreateIssueModal(true);
@ -385,49 +396,48 @@ export const IssuesView: React.FC<Props> = ({
handleClose={() => setTransferIssuesModal(false)} handleClose={() => setTransferIssuesModal(false)}
isOpen={transferIssuesModal} isOpen={transferIssuesModal}
/> />
{issueView !== "calendar" && ( <>
<> <div
<div className={`flex items-center justify-between gap-2 ${
className={`flex items-center justify-between gap-2 ${ issueView === "list" ? (areFiltersApplied ? "mt-6 px-8" : "") : "-mt-2"
issueView === "list" && areFiltersApplied ? "px-8 mt-6" : "-mt-2" }`}
}`} >
> <FilterList filters={filters} setFilters={setFilters} />
<FilterList filters={filters} setFilters={setFilters} />
{areFiltersApplied && (
<PrimaryButton
onClick={() => {
if (viewId) {
setFilters({}, true);
setToastAlert({
title: "View updated",
message: "Your view has been updated",
type: "success",
});
} else
setCreateViewModal({
query: filters,
});
}}
className="flex items-center gap-2 text-sm"
>
{!viewId && <PlusIcon className="h-4 w-4" />}
{viewId ? "Update" : "Save"} view
</PrimaryButton>
)}
</div>
{areFiltersApplied && ( {areFiltersApplied && (
<div className={` ${issueView === "list" ? "mt-4" : "my-4"} border-t`} /> <PrimaryButton
onClick={() => {
if (viewId) {
setFilters({}, true);
setToastAlert({
title: "View updated",
message: "Your view has been updated",
type: "success",
});
} else
setCreateViewModal({
query: filters,
});
}}
className="flex items-center gap-2 text-sm"
>
{!viewId && <PlusIcon className="h-4 w-4" />}
{viewId ? "Update" : "Save"} view
</PrimaryButton>
)} )}
</> </div>
)} {areFiltersApplied && (
<div className={` ${issueView === "list" ? "mt-4" : "my-4"} border-t`} />
)}
</>
<DragDropContext onDragEnd={handleOnDragEnd}> <DragDropContext onDragEnd={handleOnDragEnd}>
<StrictModeDroppable droppableId="trashBox"> <StrictModeDroppable droppableId="trashBox">
{(provided, snapshot) => ( {(provided, snapshot) => (
<div <div
className={`${ className={`${
trashBox ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0" trashBox ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
} fixed top-9 right-9 z-30 flex h-28 w-96 flex-col items-center justify-center gap-2 rounded border-2 border-red-500 bg-red-100 p-3 text-xs font-medium italic text-red-500 ${ } fixed top-9 right-9 z-30 flex h-28 w-96 flex-col items-center justify-center gap-2 rounded border-2 border-red-500/20 bg-red-500/20 p-3 text-xs font-medium italic text-red-500 ${
snapshot.isDraggingOver ? "bg-red-500 text-white" : "" snapshot.isDraggingOver ? "bg-red-500/100 text-white" : ""
} duration-200`} } duration-200`}
ref={provided.innerRef} ref={provided.innerRef}
{...provided.droppableProps} {...provided.droppableProps}
@ -481,7 +491,7 @@ export const IssuesView: React.FC<Props> = ({
userAuth={memberRole} userAuth={memberRole}
/> />
) : ( ) : (
<CalendarView /> <CalendarView addIssueToDate={addIssueToDate} />
)} )}
</> </>
) : type === "issue" ? ( ) : type === "issue" ? (
@ -502,8 +512,8 @@ export const IssuesView: React.FC<Props> = ({
title="Create a new issue" title="Create a new issue"
description={ description={
<span> <span>
Use <pre className="inline rounded bg-gray-200 px-2 py-1">C</pre> shortcut to Use <pre className="inline rounded bg-brand-surface-2 px-2 py-1">C</pre>{" "}
create a new issue shortcut to create a new issue
</span> </span>
} }
Icon={PlusIcon} Icon={PlusIcon}

View File

@ -56,7 +56,7 @@ export const LinkModal: React.FC<Props> = ({ isOpen, handleClose, onFormSubmit }
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> <div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto"> <div className="fixed inset-0 z-10 overflow-y-auto">
@ -70,11 +70,11 @@ export const LinkModal: React.FC<Props> = ({ isOpen, handleClose, onFormSubmit }
leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
> >
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white 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-brand-surface-2 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)}> <form onSubmit={handleSubmit(onSubmit)}>
<div> <div>
<div className="space-y-5"> <div className="space-y-5">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900"> <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-brand-base">
Add Link Add Link
</Dialog.Title> </Dialog.Title>
<div className="mt-2 space-y-3"> <div className="mt-2 space-y-3">

View File

@ -44,7 +44,6 @@ import {
MODULE_ISSUES_WITH_PARAMS, MODULE_ISSUES_WITH_PARAMS,
PROJECT_ISSUES_LIST_WITH_PARAMS, PROJECT_ISSUES_LIST_WITH_PARAMS,
} from "constants/fetch-keys"; } from "constants/fetch-keys";
import { DIVIDER } from "@blueprintjs/core/lib/esm/common/classes";
type Props = { type Props = {
type?: string; type?: string;
@ -217,7 +216,7 @@ export const SingleListIssue: React.FC<Props> = ({
</a> </a>
</ContextMenu> </ContextMenu>
<div <div
className="flex items-center justify-between gap-2 px-4 py-2.5 border-b border-gray-300 last:border-b-0" className="flex items-center justify-between gap-2 border-b border-brand-base bg-brand-base px-4 py-2.5 last:border-b-0"
onContextMenu={(e) => { onContextMenu={(e) => {
e.preventDefault(); e.preventDefault();
setContextMenu(true); setContextMenu(true);
@ -231,13 +230,15 @@ export const SingleListIssue: React.FC<Props> = ({
tooltipHeading="Issue ID" tooltipHeading="Issue ID"
tooltipContent={`${issue.project_detail?.identifier}-${issue.sequence_id}`} tooltipContent={`${issue.project_detail?.identifier}-${issue.sequence_id}`}
> >
<span className="flex-shrink-0 text-xs text-gray-400"> <span className="flex-shrink-0 text-xs text-brand-secondary">
{issue.project_detail?.identifier}-{issue.sequence_id} {issue.project_detail?.identifier}-{issue.sequence_id}
</span> </span>
</Tooltip> </Tooltip>
)} )}
<Tooltip position="top-left" tooltipHeading="Title" tooltipContent={issue.name}> <Tooltip position="top-left" tooltipHeading="Title" tooltipContent={issue.name}>
<span className="text-[0.825rem] text-gray-800">{truncateText(issue.name, 50)}</span> <span className="text-[0.825rem] text-brand-base">
{truncateText(issue.name, 50)}
</span>
</Tooltip> </Tooltip>
</a> </a>
</Link> </Link>
@ -267,7 +268,7 @@ export const SingleListIssue: React.FC<Props> = ({
/> />
)} )}
{properties.sub_issue_count && ( {properties.sub_issue_count && (
<div className="flex items-center gap-1 rounded-md border px-3 py-1.5 text-xs shadow-sm"> <div className="flex items-center gap-1 rounded-md border border-brand-base px-3 py-1 text-xs text-brand-secondary shadow-sm">
{issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"} {issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
</div> </div>
)} )}
@ -276,7 +277,7 @@ export const SingleListIssue: React.FC<Props> = ({
{issue.label_details.map((label) => ( {issue.label_details.map((label) => (
<span <span
key={label.id} key={label.id}
className="group flex items-center gap-1 rounded-2xl border px-2 py-0.5 text-xs" className="group flex items-center gap-1 rounded-2xl border border-brand-base px-2 py-0.5 text-xs text-brand-secondary"
> >
<span <span
className="h-1.5 w-1.5 rounded-full" className="h-1.5 w-1.5 rounded-full"
@ -308,8 +309,8 @@ export const SingleListIssue: React.FC<Props> = ({
/> />
)} )}
{properties.link && ( {properties.link && (
<div className="flex items-center rounded-md shadow-sm px-2.5 py-1 cursor-default text-xs border border-gray-200"> <div className="flex cursor-default items-center rounded-md border border-brand-base px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Link" tooltipContent={`${issue.link_count}`}> <Tooltip tooltipHeading="Links" tooltipContent={`${issue.link_count}`}>
<div className="flex items-center gap-1 text-gray-500"> <div className="flex items-center gap-1 text-gray-500">
<LinkIcon className="h-3.5 w-3.5 text-gray-500" /> <LinkIcon className="h-3.5 w-3.5 text-gray-500" />
{issue.link_count} {issue.link_count}
@ -318,10 +319,10 @@ export const SingleListIssue: React.FC<Props> = ({
</div> </div>
)} )}
{properties.attachment_count && ( {properties.attachment_count && (
<div className="flex items-center rounded-md shadow-sm px-2.5 py-1 cursor-default text-xs border border-gray-200"> <div className="flex cursor-default items-center rounded-md border border-brand-base px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Attachment" tooltipContent={`${issue.attachment_count}`}> <Tooltip tooltipHeading="Attachments" tooltipContent={`${issue.attachment_count}`}>
<div className="flex items-center gap-1 text-gray-500"> <div className="flex items-center gap-1 text-gray-500">
<PaperClipIcon className="h-3.5 w-3.5 text-gray-500 -rotate-45" /> <PaperClipIcon className="h-3.5 w-3.5 -rotate-45 text-gray-500" />
{issue.attachment_count} {issue.attachment_count}
</div> </div>
</Tooltip> </Tooltip>

View File

@ -130,35 +130,31 @@ export const SingleList: React.FC<Props> = ({
}; };
return ( return (
<Disclosure key={groupTitle} as="div" defaultOpen> <Disclosure as="div" defaultOpen>
{({ open }) => ( {({ open }) => (
<div className="bg-white"> <div>
<div <div className="flex items-center justify-between px-4 py-2.5">
className={`flex items-center justify-between bg-gray-100 px-4 py-2.5 ${
open ? "" : "rounded-[10px]"
}`}
>
<Disclosure.Button> <Disclosure.Button>
<div className="flex items-center gap-x-3"> <div className="flex items-center gap-x-3">
{selectedGroup !== null && ( {selectedGroup !== null && (
<div className="flex items-center">{getGroupIcon()}</div> <div className="flex items-center">{getGroupIcon()}</div>
)} )}
{selectedGroup !== null ? ( {selectedGroup !== null ? (
<h2 className="text-sm font-semibold capitalize leading-6 text-gray-800"> <h2 className="text-sm font-semibold capitalize leading-6 text-brand-base">
{getGroupTitle()} {getGroupTitle()}
</h2> </h2>
) : ( ) : (
<h2 className="font-medium leading-5">All Issues</h2> <h2 className="font-medium leading-5">All Issues</h2>
)} )}
<span className="rounded-full bg-gray-200 py-1 min-w-[2.5rem] text-center text-xs text-black"> <span className="text-brand-2 min-w-[2.5rem] rounded-full bg-brand-surface-2 py-1 text-center text-xs">
{groupedByIssues?.[groupTitle].length ?? 0} {groupedByIssues[groupTitle as keyof IIssue].length}
</span> </span>
</div> </div>
</Disclosure.Button> </Disclosure.Button>
{type === "issue" ? ( {type === "issue" ? (
<button <button
type="button" type="button"
className="p-1 text-gray-500 hover:bg-gray-100" className="p-1 text-brand-secondary hover:bg-brand-surface-2"
onClick={addIssueToState} onClick={addIssueToState}
> >
<PlusIcon className="h-4 w-4" /> <PlusIcon className="h-4 w-4" />
@ -168,7 +164,7 @@ export const SingleList: React.FC<Props> = ({
) : ( ) : (
<CustomMenu <CustomMenu
customButton={ customButton={
<div className="flex items-center cursor-pointer"> <div className="flex cursor-pointer items-center">
<PlusIcon className="h-4 w-4" /> <PlusIcon className="h-4 w-4" />
</div> </div>
} }
@ -215,7 +211,9 @@ export const SingleList: React.FC<Props> = ({
/> />
)) ))
) : ( ) : (
<p className="px-4 py-3 text-sm text-gray-500">No issues.</p> <p className="bg-brand-base px-4 py-2.5 text-sm text-brand-secondary">
No issues.
</p>
) )
) : ( ) : (
<div className="flex h-full w-full items-center justify-center">Loading...</div> <div className="flex h-full w-full items-center justify-center">Loading...</div>

View File

@ -1,8 +1,7 @@
import Link from "next/link"; import Link from "next/link";
// icons // icons
import { LinkIcon, TrashIcon } from "@heroicons/react/24/outline"; import { ArrowTopRightOnSquareIcon, LinkIcon, TrashIcon } from "@heroicons/react/24/outline";
import { ExternalLinkIcon } from "components/icons";
// helpers // helpers
import { timeAgo } from "helpers/date-time.helper"; import { timeAgo } from "helpers/date-time.helper";
// types // types
@ -33,15 +32,15 @@ export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }
<div className="absolute top-1.5 right-1.5 z-[1] flex items-center gap-1"> <div className="absolute top-1.5 right-1.5 z-[1] flex items-center gap-1">
<Link href={link.url}> <Link href={link.url}>
<a <a
className="grid h-7 w-7 place-items-center rounded bg-gray-100 p-1 outline-none" className="grid h-7 w-7 place-items-center rounded bg-brand-surface-1 p-1 outline-none hover:bg-brand-surface-2"
target="_blank" target="_blank"
> >
<ExternalLinkIcon width="14" height="14" /> <ArrowTopRightOnSquareIcon className="h-4 w-4 text-brand-secondary" />
</a> </a>
</Link> </Link>
<button <button
type="button" type="button"
className="grid h-7 w-7 place-items-center rounded bg-gray-100 p-1 text-red-500 outline-none duration-300 hover:bg-red-50" className="grid h-7 w-7 place-items-center rounded bg-brand-surface-1 p-1 text-red-500 outline-none duration-300 hover:bg-red-500/20"
onClick={() => handleDeleteLink(link.id)} onClick={() => handleDeleteLink(link.id)}
> >
<TrashIcon className="h-4 w-4" /> <TrashIcon className="h-4 w-4" />
@ -49,13 +48,13 @@ export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }
</div> </div>
)} )}
<Link href={link.url}> <Link href={link.url}>
<a className="relative flex gap-2 rounded-md border bg-gray-50 p-2" target="_blank"> <a className="relative flex gap-2 rounded-md bg-brand-base p-2" target="_blank">
<div className="mt-0.5"> <div className="mt-0.5">
<LinkIcon className="h-3.5 w-3.5" /> <LinkIcon className="h-3.5 w-3.5" />
</div> </div>
<div> <div>
<h5 className="w-4/5 break-all">{link.title}</h5> <h5 className="w-4/5 break-all">{link.title}</h5>
<p className="mt-0.5 text-gray-500"> <p className="mt-0.5 text-brand-secondary">
Added {timeAgo(link.created_at)} Added {timeAgo(link.created_at)}
<br /> <br />
by{" "} by{" "}

View File

@ -100,13 +100,13 @@ export const SidebarProgressStats: React.FC<Props> = ({
> >
<Tab.List <Tab.List
as="div" as="div"
className={`flex w-full items-center justify-between rounded-md bg-gray-100 px-1 py-1.5 className={`flex w-full items-center justify-between rounded-md bg-brand-surface-1 px-1 py-1.5
${module ? "text-xs" : "text-sm"} `} ${module ? "text-xs" : "text-sm"} `}
> >
<Tab <Tab
className={({ selected }) => className={({ selected }) =>
`w-full rounded px-3 py-1 text-gray-900 ${ `w-full rounded px-3 py-1 text-brand-base ${
selected ? " bg-theme text-white" : " hover:bg-hover-gray" selected ? " bg-brand-accent text-white" : " hover:bg-brand-surface-2"
}` }`
} }
> >
@ -114,8 +114,8 @@ export const SidebarProgressStats: React.FC<Props> = ({
</Tab> </Tab>
<Tab <Tab
className={({ selected }) => className={({ selected }) =>
`w-full rounded px-3 py-1 text-gray-900 ${ `w-full rounded px-3 py-1 text-brand-base ${
selected ? " bg-theme text-white" : " hover:bg-hover-gray" selected ? " bg-brand-accent text-white" : " hover:bg-brand-surface-2"
}` }`
} }
> >
@ -123,8 +123,8 @@ export const SidebarProgressStats: React.FC<Props> = ({
</Tab> </Tab>
<Tab <Tab
className={({ selected }) => className={({ selected }) =>
`w-full rounded px-3 py-1 text-gray-900 ${ `w-full rounded px-3 py-1 text-brand-base ${
selected ? " bg-theme text-white" : " hover:bg-hover-gray" selected ? " bg-brand-accent text-white" : " hover:bg-brand-surface-2"
}` }`
} }
> >
@ -166,7 +166,7 @@ export const SidebarProgressStats: React.FC<Props> = ({
<SingleProgressStats <SingleProgressStats
title={ title={
<> <>
<div className="h-5 w-5 rounded-full border-2 border-white bg-white"> <div className="h-5 w-5 rounded-full border-2 border-white bg-brand-surface-2">
<Image <Image
src={User} src={User}
height="100%" height="100%"

View File

@ -19,8 +19,8 @@ export const SingleProgressStats: React.FC<TSingleProgressStatsProps> = ({
}) => ( }) => (
<div <div
className={`flex w-full items-center justify-between rounded p-2 text-xs ${ className={`flex w-full items-center justify-between rounded p-2 text-xs ${
onClick ? "cursor-pointer hover:bg-gray-100" : "" onClick ? "cursor-pointer hover:bg-brand-surface-1" : ""
} ${selected ? "bg-gray-100" : ""}`} } ${selected ? "bg-brand-surface-1" : ""}`}
onClick={onClick} onClick={onClick}
> >
<div className="flex w-1/2 items-center justify-start gap-2">{title}</div> <div className="flex w-1/2 items-center justify-start gap-2">{title}</div>

View File

@ -0,0 +1,60 @@
import { useState, useEffect, ChangeEvent } from "react";
import { useTheme } from "next-themes";
import { THEMES_OBJ } from "constants/themes";
import { CustomSelect } from "components/ui";
import { CustomThemeModal } from "./custom-theme-modal";
export const ThemeSwitch = () => {
const [mounted, setMounted] = useState(false);
const [customThemeModal, setCustomThemeModal] = useState(false);
const { theme, setTheme } = useTheme();
// useEffect only runs on the client, so now we can safely show the UI
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}
return (
<>
<CustomSelect
value={theme}
label={theme ? THEMES_OBJ.find((t) => t.value === theme)?.label : "Select your theme"}
onChange={({ value, type }: { value: string; type: string }) => {
if (value === "custom") {
if (!customThemeModal) setCustomThemeModal(true);
} else {
const cssVars = [
"--color-bg-base",
"--color-bg-surface-1",
"--color-bg-surface-2",
"--color-border",
"--color-bg-sidebar",
"--color-accent",
"--color-text-base",
"--color-text-secondary",
];
cssVars.forEach((cssVar) => document.documentElement.style.removeProperty(cssVar));
}
document.documentElement.style.setProperty("color-scheme", type);
setTheme(value);
}}
input
width="w-full"
position="right"
>
{THEMES_OBJ.map(({ value, label, type }) => (
<CustomSelect.Option key={value} value={{ value, type }}>
{label}
</CustomSelect.Option>
))}
</CustomSelect>
{/* <CustomThemeModal isOpen={customThemeModal} handleClose={() => setCustomThemeModal(false)} /> */}
</>
);
};

View File

@ -64,7 +64,7 @@ export const CompletedCyclesList: React.FC<CompletedCyclesListProps> = ({
{completedCycles ? ( {completedCycles ? (
completedCycles.completed_cycles.length > 0 ? ( completedCycles.completed_cycles.length > 0 ? (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div className="flex items-center gap-2 text-sm text-gray-500"> <div className="flex items-center gap-2 text-sm text-brand-secondary">
<ExclamationIcon height={14} width={14} /> <ExclamationIcon height={14} width={14} />
<span>Completed cycles are not editable.</span> <span>Completed cycles are not editable.</span>
</div> </div>

View File

@ -62,8 +62,8 @@ export const CyclesList: React.FC<TCycleStatsViewProps> = ({
</div> </div>
) : type === "current" ? ( ) : type === "current" ? (
showNoCurrentCycleMessage && ( showNoCurrentCycleMessage && (
<div className="flex items-center justify-between bg-white w-full px-6 py-4 rounded-[10px]"> <div className="flex items-center justify-between bg-brand-surface-2 w-full px-6 py-4 rounded-[10px]">
<h3 className="text-base font-medium text-black "> No current cycle is present.</h3> <h3 className="text-base font-medium text-brand-base "> No current cycle is present.</h3>
<button onClick={() => setShowNoCurrentCycleMessage(false)}> <button onClick={() => setShowNoCurrentCycleMessage(false)}>
<XMarkIcon className="h-4 w-4" /> <XMarkIcon className="h-4 w-4" />
</button> </button>

View File

@ -139,7 +139,7 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> <div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 z-20 overflow-y-auto"> <div className="fixed inset-0 z-20 overflow-y-auto">
@ -153,8 +153,8 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
> >
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 sm:w-[40rem]"> <Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-brand-surface-2 text-left shadow-xl transition-all sm:my-8 sm:w-[40rem]">
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> <div className="bg-brand-surface-2 px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div className="sm:flex sm:items-start"> <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-100 sm:mx-0 sm:h-10 sm:w-10"> <div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<ExclamationTriangleIcon <ExclamationTriangleIcon
@ -163,11 +163,11 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
/> />
</div> </div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900"> <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-brand-base">
Delete Cycle Delete Cycle
</Dialog.Title> </Dialog.Title>
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-gray-500"> <p className="text-sm text-brand-secondary">
Are you sure you want to delete cycle-{" "} Are you sure you want to delete cycle-{" "}
<span className="font-bold">{data?.name}</span>? All of the data related <span className="font-bold">{data?.name}</span>? All of the data related
to the cycle will be permanently removed. This action cannot be undone. to the cycle will be permanently removed. This action cannot be undone.

View File

@ -37,30 +37,30 @@ export const EmptyCycle = () => {
return ( return (
<div className="flex h-full w-full flex-col items-center justify-center gap-5 "> <div className="flex h-full w-full flex-col items-center justify-center gap-5 ">
<div className="relative h-32 w-72"> <div className="relative h-32 w-72">
<div className="absolute right-0 top-0 flex w-64 flex-col rounded-[10px] bg-white text-xs shadow"> <div className="absolute right-0 top-0 flex w-64 flex-col rounded-[10px] bg-brand-surface-2 text-xs shadow">
<div className="flex flex-col items-start justify-center gap-2.5 p-3.5"> <div className="flex flex-col items-start justify-center gap-2.5 p-3.5">
<span className="text-sm font-semibold text-black">Cycle Name</span> <span className="text-sm font-semibold text-brand-base">Cycle Name</span>
<div className="flex h-full w-full items-center gap-4"> <div className="flex h-full w-full items-center gap-4">
<span className="h-2 w-20 rounded-full bg-gray-200" /> <span className="h-2 w-20 rounded-full bg-brand-surface-2" />
<span className="h-2 w-20 rounded-full bg-gray-200" /> <span className="h-2 w-20 rounded-full bg-brand-surface-2" />
</div> </div>
</div> </div>
<div className="border-t border-gray-200 bg-gray-100 px-4 py-3"> <div className="border-t border-brand-base bg-brand-surface-1 px-4 py-3">
<LinearProgressIndicator data={emptyCycleData} /> <LinearProgressIndicator data={emptyCycleData} />
</div> </div>
</div> </div>
<div className="absolute left-0 bottom-0 flex w-64 flex-col rounded-[10px] bg-white text-xs shadow"> <div className="absolute left-0 bottom-0 flex w-64 flex-col rounded-[10px] bg-brand-surface-2 text-xs shadow">
<div className="flex flex-col items-start justify-center gap-2.5 p-3.5"> <div className="flex flex-col items-start justify-center gap-2.5 p-3.5">
<span className="text-sm font-semibold text-black">Cycle Name</span> <span className="text-sm font-semibold text-brand-base">Cycle Name</span>
<div className="flex h-full w-full items-center gap-4"> <div className="flex h-full w-full items-center gap-4">
<span className="h-2 w-20 rounded-full bg-gray-200" /> <span className="h-2 w-20 rounded-full bg-brand-surface-2" />
<span className="h-2 w-20 rounded-full bg-gray-200" /> <span className="h-2 w-20 rounded-full bg-brand-surface-2" />
</div> </div>
</div> </div>
<div className="border-t border-gray-200 bg-gray-100 px-4 py-3"> <div className="border-t border-brand-base bg-brand-surface-1 px-4 py-3">
<LinearProgressIndicator data={emptyCycleData} /> <LinearProgressIndicator data={emptyCycleData} />
</div> </div>
</div> </div>
@ -68,7 +68,7 @@ export const EmptyCycle = () => {
<div className="flex flex-col items-center justify-center gap-4 text-center "> <div className="flex flex-col items-center justify-center gap-4 text-center ">
<h3 className="text-xl font-semibold">Create New Cycle</h3> <h3 className="text-xl font-semibold">Create New Cycle</h3>
<p className="text-sm text-gray-500"> <p className="text-sm text-brand-secondary">
Sprint more effectively with Cycles by confining your project <br /> to a fixed amount of Sprint more effectively with Cycles by confining your project <br /> to a fixed amount of
time. Create new cycle now. time. Create new cycle now.
</p> </p>

View File

@ -94,7 +94,7 @@ export const CycleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, stat
return ( return (
<form onSubmit={handleSubmit(handleCreateUpdateCycle)}> <form onSubmit={handleSubmit(handleCreateUpdateCycle)}>
<div className="space-y-5"> <div className="space-y-5">
<h3 className="text-lg font-medium leading-6 text-gray-900"> <h3 className="text-lg font-medium leading-6 text-brand-base">
{status ? "Update" : "Create"} Cycle {status ? "Update" : "Create"} Cycle
</h3> </h3>
<div className="space-y-3"> <div className="space-y-3">
@ -196,7 +196,7 @@ export const CycleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, stat
</div> </div>
</div> </div>
</div> </div>
<div className="-mx-5 mt-5 flex justify-end gap-2 border-t px-5 pt-5"> <div className="-mx-5 mt-5 flex justify-end gap-2 border-t border-brand-base px-5 pt-5">
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton> <SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
<PrimaryButton <PrimaryButton
type="submit" type="submit"

View File

@ -151,7 +151,7 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> <div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 z-20 overflow-y-auto"> <div className="fixed inset-0 z-20 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0"> <div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
@ -164,7 +164,7 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
> >
<Dialog.Panel className="relative transform rounded-lg bg-white 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 bg-brand-surface-1 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<CycleForm <CycleForm
handleFormSubmit={handleFormSubmit} handleFormSubmit={handleFormSubmit}
handleClose={handleClose} handleClose={handleClose}

View File

@ -59,9 +59,9 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({
{({ open }) => ( {({ open }) => (
<> <>
<Listbox.Button <Listbox.Button
className={`flex cursor-pointer items-center gap-1 rounded-md border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 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-brand-base px-2 py-1 text-xs shadow-sm duration-300 hover:bg-brand-surface-1 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500`}
> >
<CyclesIcon className="h-3 w-3 text-gray-500" /> <CyclesIcon className="h-3 w-3 text-brand-secondary" />
<div className="flex items-center gap-2 truncate"> <div className="flex items-center gap-2 truncate">
{cycles?.find((c) => c.id === value)?.name ?? "Cycles"} {cycles?.find((c) => c.id === value)?.name ?? "Cycles"}
</div> </div>
@ -75,7 +75,7 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<Listbox.Options <Listbox.Options
className={`absolute mt-1 max-h-32 min-w-[8rem] overflow-y-auto whitespace-nowrap bg-white shadow-lg text-xs z-10 rounded-md py-1 ring-1 ring-black ring-opacity-5 focus:outline-none`} className={`absolute mt-1 max-h-32 min-w-[8rem] overflow-y-auto whitespace-nowrap bg-brand-surface-2 shadow-lg text-xs z-10 rounded-md py-1 ring-1 ring-black ring-opacity-5 focus:outline-none`}
> >
<div className="py-1"> <div className="py-1">
{options ? ( {options ? (
@ -93,7 +93,7 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({
: "" : ""
} ${ } ${
active ? "bg-indigo-50" : "" active ? "bg-indigo-50" : ""
} relative cursor-pointer select-none p-2 text-gray-900` } relative cursor-pointer select-none p-2 text-brand-base`
} }
value={option.value} value={option.value}
> >
@ -103,14 +103,14 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({
</Listbox.Option> </Listbox.Option>
)) ))
) : ( ) : (
<p className="text-center text-sm text-gray-500">No options</p> <p className="text-center text-sm text-brand-secondary">No options</p>
) )
) : ( ) : (
<p className="text-center text-sm text-gray-500">Loading...</p> <p className="text-center text-sm text-brand-secondary">Loading...</p>
)} )}
<button <button
type="button" type="button"
className="relative w-full flex select-none items-center gap-x-2 p-2 text-gray-400 hover:bg-indigo-50 hover:text-gray-900" className="relative w-full flex select-none items-center gap-x-2 p-2 text-gray-400 hover:bg-indigo-50 hover:text-brand-base"
onClick={openCycleModal} onClick={openCycleModal}
> >
<PlusIcon className="h-4 w-4 text-gray-400" aria-hidden="true" /> <PlusIcon className="h-4 w-4 text-gray-400" aria-hidden="true" />

View File

@ -135,7 +135,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div <div
className={`fixed top-0 ${ className={`fixed top-0 ${
isOpen ? "right-0" : "-right-[24rem]" isOpen ? "right-0" : "-right-[24rem]"
} z-20 h-full w-[24rem] overflow-y-auto border-l bg-gray-50 py-5 duration-300`} } z-20 h-full w-[24rem] overflow-y-auto border-l border-brand-base bg-brand-surface-2 py-5 duration-300`}
> >
{cycle ? ( {cycle ? (
<> <>
@ -143,19 +143,19 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div className="flex gap-2.5 px-5 text-sm"> <div className="flex gap-2.5 px-5 text-sm">
<div className="flex items-center "> <div className="flex items-center ">
<span <span
className={`flex items-center rounded border-[0.5px] border-gray-200 bg-gray-100 px-2.5 py-1.5 text-center text-sm capitalize text-gray-800 `} className={`flex items-center rounded border-[0.5px] border-brand-base bg-brand-surface-1 px-2.5 py-1.5 text-center text-sm capitalize text-brand-muted-1 `}
> >
{capitalizeFirstLetter(cycleStatus)} {capitalizeFirstLetter(cycleStatus)}
</span> </span>
</div> </div>
<div className="relative flex h-full w-52 items-center justify-center gap-2 text-sm text-gray-800"> <div className="relative flex h-full w-52 items-center justify-center gap-2 text-sm text-brand-muted-1">
<Popover className="flex h-full items-center justify-center rounded-lg"> <Popover className="flex h-full items-center justify-center rounded-lg">
{({ open }) => ( {({ open }) => (
<> <>
<Popover.Button <Popover.Button
disabled={isCompleted ?? false} disabled={isCompleted ?? false}
className={`group flex h-full items-center gap-1 rounded border-[0.5px] border-gray-200 bg-gray-100 px-2.5 py-1.5 text-gray-800 ${ className={`group flex h-full items-center gap-1 rounded border-[0.5px] border-brand-base bg-brand-surface-1 px-2.5 py-1.5 text-brand-muted-1 ${
open ? "bg-gray-100" : "" open ? "bg-brand-surface-1" : ""
}`} }`}
> >
<CalendarDaysIcon className="h-3 w-3" /> <CalendarDaysIcon className="h-3 w-3" />
@ -216,8 +216,8 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<> <>
<Popover.Button <Popover.Button
disabled={isCompleted ?? false} disabled={isCompleted ?? false}
className={`group flex items-center gap-1 rounded border-[0.5px] border-gray-200 bg-gray-100 px-2.5 py-1.5 text-gray-800 ${ className={`group flex items-center gap-1 rounded border-[0.5px] border-brand-base bg-brand-surface-1 px-2.5 py-1.5 text-brand-muted-1 ${
open ? "bg-gray-100" : "" open ? "bg-brand-surface-1" : ""
}`} }`}
> >
<CalendarDaysIcon className="h-3 w-3 " /> <CalendarDaysIcon className="h-3 w-3 " />
@ -278,7 +278,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div className="flex flex-col gap-6 px-6 py-6 w-full"> <div className="flex flex-col gap-6 px-6 py-6 w-full">
<div className="flex flex-col items-start justify-start gap-2 w-full"> <div className="flex flex-col items-start justify-start gap-2 w-full">
<div className="flex items-start justify-between gap-2 w-full"> <div className="flex items-start justify-between gap-2 w-full">
<h4 className="text-xl font-semibold text-gray-900">{cycle.name}</h4> <h4 className="text-xl font-semibold text-brand-base">{cycle.name}</h4>
<CustomMenu width="lg" ellipsis> <CustomMenu width="lg" ellipsis>
{!isCompleted && ( {!isCompleted && (
<CustomMenu.MenuItem onClick={() => setCycleDeleteModal(true)}> <CustomMenu.MenuItem onClick={() => setCycleDeleteModal(true)}>
@ -297,7 +297,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</CustomMenu> </CustomMenu>
</div> </div>
<span className="whitespace-normal text-sm leading-5 text-black"> <span className="whitespace-normal text-sm leading-5 text-brand-base">
{cycle.description} {cycle.description}
</span> </span>
</div> </div>
@ -323,7 +323,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
{cycle.owned_by.first_name.charAt(0)} {cycle.owned_by.first_name.charAt(0)}
</span> </span>
)} )}
<span className="text-gray-900">{cycle.owned_by.first_name}</span> <span className="text-brand-base">{cycle.owned_by.first_name}</span>
</div> </div>
</div> </div>
@ -333,7 +333,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<span>Progress</span> <span>Progress</span>
</div> </div>
<div className="flex items-center gap-2.5 text-gray-800"> <div className="flex items-center gap-2.5 text-brand-muted-1">
<span className="h-4 w-4"> <span className="h-4 w-4">
<ProgressBar value={cycle.completed_issues} maxValue={cycle.total_issues} /> <ProgressBar value={cycle.completed_issues} maxValue={cycle.total_issues} />
</span> </span>
@ -344,7 +344,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</div> </div>
</div> </div>
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-gray-300 px-6 py-6 "> <div className="flex w-full flex-col items-center justify-start gap-2 border-t border-brand-base p-6">
<Disclosure defaultOpen> <Disclosure defaultOpen>
{({ open }) => ( {({ open }) => (
<div <div
@ -352,7 +352,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
> >
<div className="flex w-full items-center justify-between gap-2 "> <div className="flex w-full items-center justify-between gap-2 ">
<div className="flex items-center justify-start gap-2 text-sm"> <div className="flex items-center justify-start gap-2 text-sm">
<span className="font-medium text-gray-500">Progress</span> <span className="font-medium text-brand-secondary">Progress</span>
{!open && progressPercentage ? ( {!open && progressPercentage ? (
<span className="rounded bg-[#09A953]/10 px-1.5 py-0.5 text-xs text-[#09A953]"> <span className="rounded bg-[#09A953]/10 px-1.5 py-0.5 text-xs text-[#09A953]">
{progressPercentage ? `${progressPercentage}%` : ""} {progressPercentage ? `${progressPercentage}%` : ""}
@ -371,7 +371,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
) : ( ) : (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<ExclamationIcon height={14} width={14} /> <ExclamationIcon height={14} width={14} />
<span className="text-xs italic text-gray-500"> <span className="text-xs italic text-brand-secondary">
{cycleStatus === "upcoming" {cycleStatus === "upcoming"
? "Cycle is yet to start." ? "Cycle is yet to start."
: "Invalid date. Please enter valid date."} : "Invalid date. Please enter valid date."}
@ -386,7 +386,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div className="flex items-start justify-between gap-4 py-2 text-xs"> <div className="flex items-start justify-between gap-4 py-2 text-xs">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<span> <span>
<DocumentIcon className="h-3 w-3 text-gray-500" /> <DocumentIcon className="h-3 w-3 text-brand-secondary" />
</span> </span>
<span> <span>
Pending Issues -{" "} Pending Issues -{" "}
@ -395,7 +395,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</span> </span>
</div> </div>
<div className="flex items-center gap-3 text-gray-900"> <div className="flex items-center gap-3 text-brand-base">
<div className="flex items-center justify-center gap-1"> <div className="flex items-center justify-center gap-1">
<span className="h-2.5 w-2.5 rounded-full bg-[#A9BBD0]" /> <span className="h-2.5 w-2.5 rounded-full bg-[#A9BBD0]" />
<span>Ideal</span> <span>Ideal</span>
@ -424,7 +424,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</Disclosure> </Disclosure>
</div> </div>
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-gray-300 px-6 py-6 "> <div className="flex w-full flex-col items-center justify-start gap-2 border-t border-brand-base p-6">
<Disclosure defaultOpen> <Disclosure defaultOpen>
{({ open }) => ( {({ open }) => (
<div <div
@ -432,7 +432,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
> >
<div className="flex w-full items-center justify-between gap-2"> <div className="flex w-full items-center justify-between gap-2">
<div className="flex items-center justify-start gap-2 text-sm"> <div className="flex items-center justify-start gap-2 text-sm">
<span className="font-medium text-gray-500">Other Information</span> <span className="font-medium text-brand-secondary">Other Information</span>
</div> </div>
{cycle.total_issues > 0 ? ( {cycle.total_issues > 0 ? (
@ -445,7 +445,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
) : ( ) : (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<ExclamationIcon height={14} width={14} /> <ExclamationIcon height={14} width={14} />
<span className="text-xs italic text-gray-500"> <span className="text-xs italic text-brand-secondary">
No issues found. Please add issue. No issues found. Please add issue.
</span> </span>
</div> </div>

View File

@ -238,7 +238,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
return ( return (
<div> <div>
<div className="flex flex-col rounded-[10px] bg-white text-xs shadow"> <div className="flex flex-col rounded-[10px] bg-brand-surface-2 text-xs shadow">
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`}> <Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`}>
<a className="w-full"> <a className="w-full">
<div className="flex h-full flex-col gap-4 rounded-b-[10px] p-4"> <div className="flex h-full flex-col gap-4 rounded-b-[10px] p-4">
@ -271,13 +271,13 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
<div className="flex items-center justify-start gap-5"> <div className="flex items-center justify-start gap-5">
<div className="flex items-start gap-1 "> <div className="flex items-start gap-1 ">
<CalendarDaysIcon className="h-4 w-4 text-gray-900" /> <CalendarDaysIcon className="h-4 w-4 text-brand-base" />
<span className="text-gray-400">Start :</span> <span className="text-brand-secondary">Start :</span>
<span>{renderShortDateWithYearFormat(startDate)}</span> <span>{renderShortDateWithYearFormat(startDate)}</span>
</div> </div>
<div className="flex items-start gap-1 "> <div className="flex items-start gap-1 ">
<TargetIcon className="h-4 w-4 text-gray-900" /> <TargetIcon className="h-4 w-4 text-brand-base" />
<span className="text-gray-400">End :</span> <span className="text-brand-secondary">End :</span>
<span>{renderShortDateWithYearFormat(endDate)}</span> <span>{renderShortDateWithYearFormat(endDate)}</span>
</div> </div>
</div> </div>
@ -293,11 +293,11 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
alt={cycle.owned_by.first_name} alt={cycle.owned_by.first_name}
/> />
) : ( ) : (
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-gray-800 capitalize text-white"> <span className="flex h-5 w-5 items-center justify-center rounded-full bg-brand-base capitalize bg-brand-secondary">
{cycle.owned_by.first_name.charAt(0)} {cycle.owned_by.first_name.charAt(0)}
</span> </span>
)} )}
<span className="text-gray-900">{cycle.owned_by.first_name}</span> <span className="text-brand-base">{cycle.owned_by.first_name}</span>
</div> </div>
<div className="flex items-center"> <div className="flex items-center">
{!isCompleted && ( {!isCompleted && (
@ -306,7 +306,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
e.preventDefault(); e.preventDefault();
handleEditCycle(); handleEditCycle();
}} }}
className="flex cursor-pointer items-center rounded p-1 duration-300 hover:bg-gray-100" className="flex cursor-pointer items-center rounded p-1 duration-300 hover:bg-brand-surface-1"
> >
<span> <span>
<PencilIcon className="h-4 w-4" /> <PencilIcon className="h-4 w-4" />
@ -350,7 +350,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
<Disclosure> <Disclosure>
{({ open }) => ( {({ open }) => (
<div <div
className={`flex h-full w-full flex-col border-t border-gray-200 bg-gray-100 ${ className={`flex h-full w-full flex-col border-t border-brand-base bg-brand-surface-1 ${
open ? "" : "flex-row" open ? "" : "flex-row"
}`} }`}
> >
@ -368,7 +368,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
</div> </div>
<Transition show={open}> <Transition show={open}>
<Disclosure.Panel> <Disclosure.Panel>
<div className="overflow-hidden rounded-b-md bg-white py-3 shadow"> <div className="overflow-hidden rounded-b-md bg-brand-surface-2 py-3 shadow">
<div className="col-span-2 space-y-3 px-4"> <div className="col-span-2 space-y-3 px-4">
<div className="space-y-3 text-xs"> <div className="space-y-3 text-xs">
{stateGroups.map((group) => ( {stateGroups.map((group) => (
@ -388,7 +388,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
<div> <div>
<span> <span>
{cycle[group.key as keyof ICycle] as number}{" "} {cycle[group.key as keyof ICycle] as number}{" "}
<span className="text-gray-500"> <span className="text-brand-secondary">
-{" "} -{" "}
{cycle.total_issues > 0 {cycle.total_issues > 0
? `${Math.round( ? `${Math.round(

View File

@ -89,7 +89,7 @@ export const TransferIssuesModal: React.FC<Props> = ({ isOpen, handleClose }) =>
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> <div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 z-10"> <div className="fixed inset-0 z-10">
@ -103,7 +103,7 @@ export const TransferIssuesModal: React.FC<Props> = ({ isOpen, handleClose }) =>
leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
> >
<Dialog.Panel className="relative transform rounded-lg bg-white py-5 text-left shadow-xl transition-all sm:w-full sm:max-w-2xl"> <Dialog.Panel className="relative transform rounded-lg bg-brand-surface-1 py-5 text-left shadow-xl transition-all sm:w-full sm:max-w-2xl">
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div className="flex items-center justify-between px-5"> <div className="flex items-center justify-between px-5">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -114,8 +114,8 @@ export const TransferIssuesModal: React.FC<Props> = ({ isOpen, handleClose }) =>
<XMarkIcon className="h-4 w-4" /> <XMarkIcon className="h-4 w-4" />
</button> </button>
</div> </div>
<div className="flex items-center gap-2 pb-3 mt-2 px-5 border-b border-gray-200"> <div className="flex items-center gap-2 pb-3 px-5 border-b border-brand-base">
<MagnifyingGlassIcon className="h-4 w-4 text-gray-500" /> <MagnifyingGlassIcon className="h-4 w-4 text-brand-secondary" />
<input <input
className="outline-none" className="outline-none"
placeholder="Search for a cycle..." placeholder="Search for a cycle..."
@ -129,7 +129,7 @@ export const TransferIssuesModal: React.FC<Props> = ({ isOpen, handleClose }) =>
filteredOptions.map((option: ICycle) => ( filteredOptions.map((option: ICycle) => (
<button <button
key={option.id} key={option.id}
className="flex items-center gap-4 py-3 px-2 text-gray-600 text-sm rounded w-full hover:bg-gray-100" className="flex items-center gap-4 px-4 py-3 text-gray-600 text-sm rounded w-full hover:bg-brand-surface-1"
onClick={() => { onClick={() => {
transferIssue({ transferIssue({
new_cycle_id: option?.id, new_cycle_id: option?.id,
@ -149,14 +149,14 @@ export const TransferIssuesModal: React.FC<Props> = ({ isOpen, handleClose }) =>
) : ( ) : (
<div className="flex items-center justify-center gap-4 p-5 text-sm w-full"> <div className="flex items-center justify-center gap-4 p-5 text-sm w-full">
<ExclamationIcon height={14} width={14} /> <ExclamationIcon height={14} width={14} />
<span className="text-center text-gray-500"> <span className="text-center text-brand-secondary">
You dont have any current cycle. Please create one to transfer the You dont have any current cycle. Please create one to transfer the
issues. issues.
</span> </span>
</div> </div>
) )
) : ( ) : (
<p className="text-center text-gray-500">Loading...</p> <p className="text-center text-brand-secondary">Loading...</p>
)} )}
</div> </div>
</div> </div>

View File

@ -38,7 +38,7 @@ export const TransferIssues: React.FC<Props> = ({ handleClick }) => {
: 0; : 0;
return ( return (
<div className="flex items-center justify-between -mt-4 mb-4"> <div className="flex items-center justify-between -mt-4 mb-4">
<div className="flex items-center gap-2 text-sm text-gray-500"> <div className="flex items-center gap-2 text-sm text-brand-secondary">
<ExclamationIcon height={14} width={14} /> <ExclamationIcon height={14} width={14} />
<span>Completed cycles are not editable.</span> <span>Completed cycles are not editable.</span>
</div> </div>

View File

@ -55,7 +55,7 @@ const EmojiIconPicker: React.FC<Props> = ({
return ( return (
<Popover className="relative z-[1]" ref={ref}> <Popover className="relative z-[1]" ref={ref}>
<Popover.Button <Popover.Button
className="rounded-full bg-gray-100 p-2 outline-none sm:text-sm" className="rounded-full bg-brand-surface-1 p-2 outline-none sm:text-sm"
onClick={() => setIsOpen((prev) => !prev)} onClick={() => setIsOpen((prev) => !prev)}
> >
{label} {label}
@ -69,8 +69,8 @@ const EmojiIconPicker: React.FC<Props> = ({
leaveFrom="transform opacity-100 scale-100" leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95" leaveTo="transform opacity-0 scale-95"
> >
<Popover.Panel className="absolute z-10 mt-2 w-[250px] rounded-[4px] bg-white shadow-lg"> <Popover.Panel className="absolute z-10 mt-2 w-[250px] rounded-[4px] bg-brand-surface-2 shadow-lg">
<div className="h-[230px] w-[250px] overflow-auto border rounded-[4px] bg-white p-2 shadow-xl"> <div className="h-[230px] w-[250px] overflow-auto border border-brand-base rounded-[4px] bg-brand-surface-2 p-2 shadow-xl">
<Tab.Group as="div" className="flex h-full w-full flex-col"> <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"> <Tab.List className="flex-0 -mx-2 flex justify-around gap-1 p-1">
{tabOptions.map((tab) => ( {tabOptions.map((tab) => (
@ -100,7 +100,7 @@ const EmojiIconPicker: React.FC<Props> = ({
{recentEmojis.map((emoji) => ( {recentEmojis.map((emoji) => (
<button <button
type="button" type="button"
className="h-4 w-4 select-none text-base hover:bg-hover-gray flex items-center justify-between" className="h-4 w-4 select-none text-sm hover:bg-brand-surface-2 flex items-center justify-between"
key={emoji} key={emoji}
onClick={() => { onClick={() => {
onChange(emoji); onChange(emoji);
@ -119,7 +119,7 @@ const EmojiIconPicker: React.FC<Props> = ({
{emojis.map((emoji) => ( {emojis.map((emoji) => (
<button <button
type="button" type="button"
className="h-4 w-4 ml-1 select-none text-base hover:bg-hover-gray flex justify-center items-center" className="h-4 w-4 mb-1 select-none text-sm hover:bg-brand-surface-2 flex items-center"
key={emoji} key={emoji}
onClick={() => { onClick={() => {
onChange(emoji); onChange(emoji);
@ -184,7 +184,7 @@ const EmojiIconPicker: React.FC<Props> = ({
{icons.material_rounded.map((icon) => ( {icons.material_rounded.map((icon) => (
<button <button
type="button" type="button"
className="h-4 w-4 mb-1 select-none text-lg hover:bg-hover-gray flex items-center" className="h-4 w-4 mb-1 select-none text-lg hover:bg-brand-surface-2 flex items-center"
key={icon.name} key={icon.name}
onClick={() => { onClick={() => {
if (onIconsClick) onIconsClick(icon.name); if (onIconsClick) onIconsClick(icon.name);

View File

@ -41,6 +41,11 @@ export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data,
defaultValues, defaultValues,
}); });
const onClose = () => {
handleClose();
reset();
};
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
@ -62,7 +67,6 @@ export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data,
(prevData) => [res, ...(prevData ?? [])], (prevData) => [res, ...(prevData ?? [])],
false false
); );
handleClose();
}) })
.catch(() => { .catch(() => {
setToastAlert({ setToastAlert({
@ -71,6 +75,8 @@ export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data,
message: "Error: Estimate could not be created", message: "Error: Estimate could not be created",
}); });
}); });
onClose();
}; };
const updateEstimate = async (formData: IEstimate) => { const updateEstimate = async (formData: IEstimate) => {
@ -102,7 +108,8 @@ export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data,
message: "Error: Estimate could not be updated", message: "Error: Estimate could not be updated",
}); });
}); });
handleClose();
onClose();
}; };
useEffect(() => { useEffect(() => {

View File

@ -52,7 +52,7 @@ const IntegrationGuide = () => {
handleClose={() => setDeleteImportModal(false)} handleClose={() => setDeleteImportModal(false)}
data={importToDelete} data={importToDelete}
/> />
<div className="space-y-2"> <div className="space-y-2 h-full">
{!provider && ( {!provider && (
<> <>
<div className="flex items-center gap-2 mb-5"> <div className="flex items-center gap-2 mb-5">

View File

@ -0,0 +1,49 @@
import React from "react";
// react hook form
import { useFormContext } from "react-hook-form";
// types
import { IJiraImporterForm } from "types";
export const JiraConfirmImport: React.FC = () => {
const { watch } = useFormContext<IJiraImporterForm>();
return (
<div className="h-full w-full overflow-y-auto">
<div className="grid grid-cols-1 gap-10 md:grid-cols-2">
<div className="col-span-2">
<h3 className="text-lg font-semibold">Confirm</h3>
</div>
<div className="col-span-1">
<p className="text-sm text-gray-500">Migrating</p>
</div>
<div className="col-span-1 flex items-center justify-between">
<div>
<h4 className="mb-2 text-xl font-semibold">{watch("data.total_issues")}</h4>
<p className="text-sm text-gray-500">Issues</p>
</div>
<div>
<h4 className="mb-2 text-xl font-semibold">{watch("data.total_states")}</h4>
<p className="text-sm text-gray-500">States</p>
</div>
<div>
<h4 className="mb-2 text-xl font-semibold">{watch("data.total_modules")}</h4>
<p className="text-sm text-gray-500">Modules</p>
</div>
<div>
<h4 className="mb-2 text-xl font-semibold">{watch("data.total_labels")}</h4>
<p className="text-sm text-gray-500">Labels</p>
</div>
<div>
<h4 className="mb-2 text-xl font-semibold">
{watch("data.users").filter((user) => user.import).length}
</h4>
<p className="text-sm text-gray-500">User</p>
</div>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,178 @@
import React from "react";
// next
import Link from "next/link";
// react hook form
import { useFormContext, Controller } from "react-hook-form";
// icons
import { PlusIcon } from "@heroicons/react/20/solid";
// hooks
import useProjects from "hooks/use-projects";
// components
import { Input, CustomSelect } from "components/ui";
import { IJiraImporterForm } from "types";
export const JiraGetImportDetail: React.FC = () => {
const {
register,
control,
formState: { errors },
} = useFormContext<IJiraImporterForm>();
const { projects } = useProjects();
return (
<div className="h-full w-full space-y-8 overflow-y-auto">
<div className="grid grid-cols-1 gap-10 md:grid-cols-2">
<div className="col-span-1">
<h3 className="text-lg font-semibold">Jira Personal Access Token</h3>
<p className="text-sm text-gray-500">
Get to know your access token by navigating to{" "}
<Link href="https://id.atlassian.com/manage-profile/security/api-tokens">
<a
className="font-medium text-gray-600 hover:text-gray-900"
target="_blank"
rel="noreferrer"
>
Atlassian Settings
</a>
</Link>
</p>
</div>
<div className="col-span-1">
<Input
id="metadata.api_token"
name="metadata.api_token"
placeholder="XXXXXXXX"
validations={{
required: "Please enter your personal access token.",
}}
register={register}
error={errors.metadata?.api_token}
/>
</div>
</div>
<div className="grid grid-cols-1 gap-10 md:grid-cols-2">
<div className="col-span-1">
<h3 className="text-lg font-semibold">Jira Project Key</h3>
<p className="text-sm text-gray-500">If XXX-123 is your issue, then enter XXX</p>
</div>
<div className="col-span-1">
<Input
id="metadata.project_key"
name="metadata.project_key"
placeholder="LIN"
register={register}
validations={{
required: "Please enter your project key.",
}}
error={errors.metadata?.project_key}
/>
</div>
</div>
<div className="grid grid-cols-1 gap-10 md:grid-cols-2">
<div className="col-span-1">
<h3 className="text-lg font-semibold">Jira Email Address</h3>
<p className="text-sm text-gray-500">
Enter the Gmail account that you use in Jira account
</p>
</div>
<div className="col-span-1">
<Input
id="metadata.email"
name="metadata.email"
type="email"
placeholder="name@company.com"
register={register}
validations={{
required: "Please enter email address.",
}}
error={errors.metadata?.email}
/>
</div>
</div>
<div className="grid grid-cols-1 gap-10 md:grid-cols-2">
<div className="col-span-1">
<h3 className="text-lg font-semibold">Jira Installation or Cloud Host Name</h3>
<p className="text-sm text-gray-500">Enter your companies cloud host name</p>
</div>
<div className="col-span-1">
<Input
id="metadata.cloud_hostname"
name="metadata.cloud_hostname"
type="email"
placeholder="my-company.atlassian.net"
register={register}
validations={{
required: "Please enter your cloud host name.",
}}
error={errors.metadata?.cloud_hostname}
/>
</div>
</div>
<div className="grid grid-cols-1 gap-10 md:grid-cols-2">
<div className="col-span-1">
<h3 className="text-lg font-semibold">Import to project</h3>
<p className="text-sm text-gray-500">Select which project you want to import to.</p>
</div>
<div className="col-span-1">
<Controller
control={control}
name="project_id"
rules={{ required: "Please select a project." }}
render={({ field: { value, onChange } }) => (
<CustomSelect
value={value}
input
width="w-full"
onChange={onChange}
label={
<span>
{value && value !== ""
? projects.find((p) => p.id === value)?.name
: "Select Project"}
</span>
}
>
{projects.length > 0 ? (
projects.map((project) => (
<CustomSelect.Option key={project.id} value={project.id}>
{project.name}
</CustomSelect.Option>
))
) : (
<div className="flex cursor-pointer select-none items-center space-x-2 truncate rounded px-1 py-1.5 text-gray-500">
<p>You don{"'"}t have any project. Please create a project first.</p>
</div>
)}
<div>
<button
type="button"
onClick={() => {
const event = new KeyboardEvent("keydown", { key: "p" });
document.dispatchEvent(event);
}}
className="flex cursor-pointer select-none items-center space-x-2 truncate rounded px-1 py-1.5 text-gray-500"
>
<PlusIcon className="h-4 w-4 text-gray-500" />
<span>Create new project</span>
</button>
</div>
</CustomSelect>
)}
/>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,145 @@
import { FC } from "react";
// next
import { useRouter } from "next/router";
// react-hook-form
import { useFormContext, useFieldArray, Controller } from "react-hook-form";
// hooks
import useWorkspaceMembers from "hooks/use-workspace-members";
// components
import { ToggleSwitch, Input, CustomSelect, CustomSearchSelect, Avatar } from "components/ui";
import { IJiraImporterForm } from "types";
export const JiraImportUsers: FC = () => {
const {
control,
watch,
register,
formState: { errors },
} = useFormContext<IJiraImporterForm>();
const { fields } = useFieldArray({
control,
name: "data.users",
});
const router = useRouter();
const { workspaceSlug } = router.query;
const { workspaceMembers: members } = useWorkspaceMembers(workspaceSlug?.toString());
const options =
members?.map((member) => ({
value: member.member.email,
query:
(member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email) +
" " +
member.member.last_name ?? "",
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name + " (" + member.member.email + ")"
: member.member.email}
</div>
),
})) ?? [];
return (
<div className="h-full w-full space-y-10 divide-y-2 overflow-y-auto">
<div className="grid grid-cols-1 gap-10 md:grid-cols-2">
<div className="col-span-1">
<h3 className="text-lg font-semibold">Users</h3>
<p className="text-sm text-gray-500">Update, invite or choose not to invite assignee</p>
</div>
<div className="col-span-1">
<Controller
control={control}
name="data.invite_users"
render={({ field: { value, onChange } }) => (
<ToggleSwitch onChange={onChange} value={value} />
)}
/>
</div>
</div>
{watch("data.invite_users") && (
<div className="pt-6">
<div className="grid grid-cols-3 gap-3">
<div className="col-span-1 text-gray-500">Name</div>
<div className="col-span-1 text-gray-500">Import as</div>
</div>
<div className="mt-5 space-y-3">
{fields.map((user, index) => (
<div className="grid grid-cols-3 gap-3" key={`${user.email}-${user.username}`}>
<div className="col-span-1">
<p>{user.username}</p>
</div>
<div className="col-span-1">
<Controller
control={control}
name={`data.users.${index}.import`}
render={({ field: { value, onChange } }) => (
<CustomSelect
input
value={value}
onChange={onChange}
width="w-full"
label={
<span className="capitalize">
{Boolean(value) ? value : ("Ignore" as any)}
</span>
}
>
<CustomSelect.Option value="invite">Invite by email</CustomSelect.Option>
<CustomSelect.Option value="map">Map to existing</CustomSelect.Option>
<CustomSelect.Option value={false}>Do not import</CustomSelect.Option>
</CustomSelect>
)}
/>
</div>
<div className="col-span-1">
{watch(`data.users.${index}.import`) === "invite" && (
<Input
id={`data.users.${index}.email`}
name={`data.users.${index}.email`}
type="text"
register={register}
validations={{
required: "This field is required",
}}
error={errors?.data?.users?.[index]?.email}
/>
)}
{watch(`data.users.${index}.import`) === "map" && (
<Controller
control={control}
name={`data.users.${index}.email`}
render={({ field: { value, onChange } }) => (
<CustomSearchSelect
value={value}
input
label={value !== "" ? value : "Select user from project"}
options={options}
onChange={onChange}
optionsClassName="w-full"
/>
)}
/>
)}
</div>
</div>
))}
</div>
</div>
)}
</div>
);
};

View File

@ -1 +1,39 @@
export * from "./root"; export * from "./root";
export * from "./give-details";
export * from "./jira-project-detail";
export * from "./import-users";
export * from "./confirm-import";
import { IJiraImporterForm } from "types";
export type TJiraIntegrationSteps =
| "import-configure"
| "display-import-data"
| "select-import-data"
| "import-users"
| "import-confirmation";
export interface IJiraIntegrationData {
state: TJiraIntegrationSteps;
}
export const jiraFormDefaultValues: IJiraImporterForm = {
metadata: {
cloud_hostname: "",
api_token: "",
project_key: "",
email: "",
},
config: {
epics_to_modules: false,
},
data: {
users: [],
invite_users: true,
total_issues: 0,
total_labels: 0,
total_modules: 0,
total_states: 0,
},
project_id: "",
};

View File

@ -0,0 +1,168 @@
import React, { useEffect } from "react";
// next
import { useRouter } from "next/router";
// swr
import useSWR from "swr";
// react hook form
import { useFormContext, Controller } from "react-hook-form";
// services
import jiraImporterService from "services/integration/jira.service";
// fetch keys
import { JIRA_IMPORTER_DETAIL } from "constants/fetch-keys";
import { IJiraImporterForm, IJiraMetadata } from "types";
// components
import { Spinner, ToggleSwitch } from "components/ui";
import type { IJiraIntegrationData, TJiraIntegrationSteps } from "./";
type Props = {
setCurrentStep: React.Dispatch<React.SetStateAction<IJiraIntegrationData>>;
setDisableTopBarAfter: React.Dispatch<React.SetStateAction<TJiraIntegrationSteps | null>>;
};
export const JiraProjectDetail: React.FC<Props> = (props) => {
const { setCurrentStep, setDisableTopBarAfter } = props;
const {
watch,
setValue,
control,
formState: { errors },
} = useFormContext<IJiraImporterForm>();
const router = useRouter();
const { workspaceSlug } = router.query;
const params: IJiraMetadata = {
api_token: watch("metadata.api_token"),
project_key: watch("metadata.project_key"),
email: watch("metadata.email"),
cloud_hostname: watch("metadata.cloud_hostname"),
};
const { data: projectInfo, error } = useSWR(
workspaceSlug &&
!errors.metadata?.api_token &&
!errors.metadata?.project_key &&
!errors.metadata?.email &&
!errors.metadata?.cloud_hostname
? JIRA_IMPORTER_DETAIL(workspaceSlug.toString(), params)
: null,
workspaceSlug &&
!errors.metadata?.api_token &&
!errors.metadata?.project_key &&
!errors.metadata?.email &&
!errors.metadata?.cloud_hostname
? () => jiraImporterService.getJiraProjectInfo(workspaceSlug.toString(), params)
: null
);
useEffect(() => {
if (!projectInfo) return;
setValue("data.total_issues", projectInfo.issues);
setValue("data.total_labels", projectInfo.labels);
setValue(
"data.users",
projectInfo.users?.map((user) => ({
email: user.emailAddress,
import: false,
username: user.displayName,
}))
);
setValue("data.total_states", projectInfo.states);
setValue("data.total_modules", projectInfo.modules);
}, [projectInfo, setValue]);
useEffect(() => {
if (error) setDisableTopBarAfter("display-import-data");
else setDisableTopBarAfter(null);
}, [error, setDisableTopBarAfter]);
useEffect(() => {
if (!projectInfo && !error) setDisableTopBarAfter("display-import-data");
else if (!error) setDisableTopBarAfter(null);
}, [projectInfo, error, setDisableTopBarAfter]);
if (!projectInfo && !error) {
return (
<div className="flex h-full w-full items-center justify-center">
<Spinner />
</div>
);
}
if (error) {
return (
<div className="flex h-full w-full items-center justify-center">
<p className="text-sm text-gray-500">
Something went wrong. Please{" "}
<button
onClick={() => setCurrentStep({ state: "import-configure" })}
type="button"
className="inline text-blue-500 underline"
>
go back
</button>{" "}
and check your Jira project details.
</p>
</div>
);
}
return (
<div className="h-full w-full space-y-10 overflow-y-auto">
<div className="grid grid-cols-1 gap-10 md:grid-cols-2">
<div className="col-span-1">
<h3 className="text-lg font-semibold">Import Data</h3>
<p className="text-sm text-gray-500">Import Completed. We have found:</p>
</div>
<div className="col-span-1 flex items-center justify-between">
<div>
<h4 className="mb-2 text-xl font-semibold">{projectInfo?.issues}</h4>
<p className="text-sm text-gray-500">Issues</p>
</div>
<div>
<h4 className="mb-2 text-xl font-semibold">{projectInfo?.states}</h4>
<p className="text-sm text-gray-500">States</p>
</div>
<div>
<h4 className="mb-2 text-xl font-semibold">{projectInfo?.modules}</h4>
<p className="text-sm text-gray-500">Modules</p>
</div>
<div>
<h4 className="mb-2 text-xl font-semibold">{projectInfo?.labels}</h4>
<p className="text-sm text-gray-500">Labels</p>
</div>
<div>
<h4 className="mb-2 text-xl font-semibold">{projectInfo?.users?.length}</h4>
<p className="text-sm text-gray-500">Users</p>
</div>
</div>
</div>
<div className="grid grid-cols-1 gap-10 md:grid-cols-2">
<div className="col-span-1">
<h3 className="text-lg font-semibold">Import Epics</h3>
<p className="text-sm text-gray-500">Import epics as modules</p>
</div>
<div className="col-span-1">
<Controller
control={control}
name="config.epics_to_modules"
render={({ field: { value, onChange } }) => (
<ToggleSwitch onChange={onChange} value={value} />
)}
/>
</div>
</div>
</div>
);
};

View File

@ -1 +1,224 @@
export const JiraImporterRoot = () => <></>; import React, { useState } from "react";
// next
import Link from "next/link";
import Image from "next/image";
import { useRouter } from "next/router";
// swr
import { mutate } from "swr";
// react hook form
import { FormProvider, useForm } from "react-hook-form";
// icons
import { ArrowLeftIcon, ListBulletIcon } from "@heroicons/react/24/outline";
import { CogIcon, CloudUploadIcon, UsersIcon, CheckIcon } from "components/icons";
// services
import jiraImporterService from "services/integration/jira.service";
// fetch keys
import { IMPORTER_SERVICES_LIST } from "constants/fetch-keys";
// components
import { PrimaryButton, SecondaryButton } from "components/ui";
import {
JiraGetImportDetail,
JiraProjectDetail,
JiraImportUsers,
JiraConfirmImport,
jiraFormDefaultValues,
TJiraIntegrationSteps,
IJiraIntegrationData,
} from "./";
import JiraLogo from "public/services/jira.png";
import { IJiraImporterForm } from "types";
const integrationWorkflowData: Array<{
title: string;
key: TJiraIntegrationSteps;
icon: React.FC<React.SVGProps<SVGSVGElement>>;
}> = [
{
title: "Configure",
key: "import-configure",
icon: CogIcon,
},
{
title: "Import Data",
key: "display-import-data",
icon: ListBulletIcon,
},
{
title: "Users",
key: "import-users",
icon: UsersIcon,
},
{
title: "Confirm",
key: "import-confirmation",
icon: CheckIcon,
},
];
export const JiraImporterRoot = () => {
const [currentStep, setCurrentStep] = useState<IJiraIntegrationData>({
state: "import-configure",
});
const [disableTopBarAfter, setDisableTopBarAfter] = useState<TJiraIntegrationSteps | null>(null);
const router = useRouter();
const { workspaceSlug } = router.query;
const methods = useForm<IJiraImporterForm>({
defaultValues: jiraFormDefaultValues,
mode: "all",
reValidateMode: "onChange",
});
const isValid = methods.formState.isValid;
const onSubmit = async (data: IJiraImporterForm) => {
if (!workspaceSlug) return;
await jiraImporterService
.createJiraImporter(workspaceSlug.toString(), data)
.then(() => {
mutate(IMPORTER_SERVICES_LIST(workspaceSlug.toString()));
router.push(`/${workspaceSlug}/settings/import-export`);
})
.catch((err) => {
console.log(err);
});
};
const activeIntegrationState = () => {
const currentElementIndex = integrationWorkflowData.findIndex(
(i) => i?.key === currentStep?.state
);
return currentElementIndex;
};
return (
<div className="flex h-full flex-col space-y-2">
<Link href={`/${workspaceSlug}/settings/import-export`}>
<div className="inline-flex cursor-pointer items-center gap-2 text-sm font-medium text-gray-600 hover:text-gray-900">
<div>
<ArrowLeftIcon className="h-3 w-3" />
</div>
<div>Cancel import & go back</div>
</div>
</Link>
<div className="flex h-full flex-col space-y-4 rounded-[10px] border border-gray-200 bg-white p-4">
<div className="flex items-center gap-2">
<div className="h-10 w-10 flex-shrink-0">
<Image src={JiraLogo} alt="jira logo" />
</div>
<div className="flex h-full w-full items-center justify-center">
{integrationWorkflowData.map((integration, index) => (
<React.Fragment key={integration.key}>
<button
type="button"
onClick={() => {
setCurrentStep({ state: integration.key });
}}
disabled={
index > activeIntegrationState() + 1 ||
Boolean(index === activeIntegrationState() + 1 && disableTopBarAfter)
}
className={`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full border ${
index <= activeIntegrationState()
? `border-[#3F76FF] bg-[#3F76FF] text-white ${
index === activeIntegrationState()
? "border-opacity-100 bg-opacity-100"
: "border-opacity-80 bg-opacity-80"
}`
: "border-gray-300"
}`}
>
<integration.icon
width="18px"
height="18px"
color={index <= activeIntegrationState() ? "#ffffff" : "#d1d5db"}
/>
</button>
{index < integrationWorkflowData.length - 1 && (
<div
key={index}
className={`border-b px-7 ${
index <= activeIntegrationState() - 1 ? `border-[#3F76FF]` : `border-gray-300`
}`}
>
{" "}
</div>
)}
</React.Fragment>
))}
</div>
</div>
<div className="relative h-full w-full pt-6">
<FormProvider {...methods}>
<form className="flex h-full w-full flex-col">
<div className="h-full w-full overflow-y-auto">
{currentStep.state === "import-configure" && <JiraGetImportDetail />}
{currentStep.state === "display-import-data" && (
<JiraProjectDetail
setDisableTopBarAfter={setDisableTopBarAfter}
setCurrentStep={setCurrentStep}
/>
)}
{currentStep?.state === "import-users" && <JiraImportUsers />}
{currentStep?.state === "import-confirmation" && <JiraConfirmImport />}
</div>
<div className="-mx-4 mt-4 flex justify-end gap-4 border-t p-4 pb-0">
{currentStep?.state !== "import-configure" && (
<SecondaryButton
onClick={() => {
const currentElementIndex = integrationWorkflowData.findIndex(
(i) => i?.key === currentStep?.state
);
setCurrentStep({
state: integrationWorkflowData[currentElementIndex - 1]?.key,
});
}}
>
Back
</SecondaryButton>
)}
<PrimaryButton
disabled={
disableTopBarAfter === currentStep?.state ||
!isValid ||
methods.formState.isSubmitting
}
onClick={() => {
const currentElementIndex = integrationWorkflowData.findIndex(
(i) => i?.key === currentStep?.state
);
if (currentElementIndex === integrationWorkflowData.length - 1) {
methods.handleSubmit(onSubmit)();
} else {
setCurrentStep({
state: integrationWorkflowData[currentElementIndex + 1]?.key,
});
}
}}
>
{currentStep?.state === "import-confirmation" ? "Confirm & Import" : "Next"}
</PrimaryButton>
</div>
</form>
</FormProvider>
</div>
</div>
</div>
);
};

View File

@ -99,7 +99,7 @@ export const SingleIntegrationCard: React.FC<Props> = ({ integration }) => {
); );
return ( return (
<div className="flex items-center justify-between gap-2 rounded-[10px] border bg-white p-5"> <div className="flex items-center justify-between gap-2 rounded-[10px] border border-brand-base bg-brand-surface-2 p-5">
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<div className="h-12 w-12 flex-shrink-0"> <div className="h-12 w-12 flex-shrink-0">
<Image <Image

View File

@ -59,42 +59,46 @@ const activityDetails: {
}, },
estimate_point: { estimate_point: {
message: "set the estimate point to", message: "set the estimate point to",
icon: <PlayIcon className="h-3 w-3 text-gray-500 -rotate-90" aria-hidden="true" />, icon: <PlayIcon className="h-3 w-3 -rotate-90 text-gray-500" aria-hidden="true" />,
}, },
labels: { labels: {
icon: <TagIcon height="12" width="12" color="#6b7280" />, icon: <TagIcon height="12" width="12" color="#6b7280" />,
}, },
modules: { modules: {
message: "set the module to", message: "set the module to",
icon: <RectangleGroupIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <RectangleGroupIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
state: { state: {
message: "set the state to", message: "set the state to",
icon: <Squares2X2Icon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <Squares2X2Icon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
priority: { priority: {
message: "set the priority to", message: "set the priority to",
icon: <ChartBarIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <ChartBarIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
name: { name: {
message: "set the name to", message: "set the name to",
icon: <ChatBubbleBottomCenterTextIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: (
<ChatBubbleBottomCenterTextIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />
),
}, },
description: { description: {
message: "updated the description.", message: "updated the description.",
icon: <ChatBubbleBottomCenterTextIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: (
<ChatBubbleBottomCenterTextIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />
),
}, },
target_date: { target_date: {
message: "set the due date to", message: "set the due date to",
icon: <CalendarDaysIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <CalendarDaysIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
parent: { parent: {
message: "set the parent to", message: "set the parent to",
icon: <UserIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />, icon: <UserIcon className="h-3 w-3 text-brand-secondary" aria-hidden="true" />,
}, },
estimate: { estimate: {
message: "updated the estimate", message: "updated the estimate",
icon: <PlayIcon className="h-3 w-3 text-gray-500 -rotate-90" aria-hidden="true" />, icon: <PlayIcon className="h-3 w-3 -rotate-90 text-gray-500" aria-hidden="true" />,
}, },
link: { link: {
message: "updated the link", message: "updated the link",
@ -240,7 +244,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
activityItem.field !== "link" && activityItem.field !== "link" &&
activityItem.field !== "estimate" activityItem.field !== "estimate"
) { ) {
value = <span className="text-gray-600">created this issue.</span>; value = <span className="text-brand-secondary">created this issue.</span>;
} else if (activityItem.field === "state") { } else if (activityItem.field === "state") {
value = activityItem.new_value ? addSpaceIfCamelCase(activityItem.new_value) : "None"; value = activityItem.new_value ? addSpaceIfCamelCase(activityItem.new_value) : "None";
} else if (activityItem.field === "labels") { } else if (activityItem.field === "labels") {
@ -255,7 +259,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
} }
value = ( value = (
<span className="relative inline-flex items-center rounded-full px-2 py-0.5 text-xs ring-1 ring-inset ring-gray-300 hover:bg-gray-50"> <span className="relative inline-flex items-center rounded-full border border-brand-base px-2 py-0.5 text-xs">
<span className="absolute flex flex-shrink-0 items-center justify-center"> <span className="absolute flex flex-shrink-0 items-center justify-center">
<span <span
className="h-1.5 w-1.5 rounded-full" className="h-1.5 w-1.5 rounded-full"
@ -265,7 +269,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
aria-hidden="true" aria-hidden="true"
/> />
</span> </span>
<span className="ml-3 font-medium text-gray-900">{name}</span> <span className="ml-3 font-medium text-brand-base">{name}</span>
</span> </span>
); );
} else if (activityItem.field === "assignees") { } else if (activityItem.field === "assignees") {
@ -298,7 +302,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
<div className="relative pb-1"> <div className="relative pb-1">
{issueActivities.length > 1 && activityItemIdx !== issueActivities.length - 1 ? ( {issueActivities.length > 1 && activityItemIdx !== issueActivities.length - 1 ? (
<span <span
className="absolute top-5 left-5 -ml-px h-full w-0.5 bg-gray-200" className="absolute top-5 left-5 -ml-px h-full w-0.5 bg-brand-surface-2"
aria-hidden="true" aria-hidden="true"
/> />
) : null} ) : null}
@ -307,7 +311,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
<div> <div>
<div className="relative px-1.5"> <div className="relative px-1.5">
<div className="mt-1.5"> <div className="mt-1.5">
<div className="ring-6 flex h-7 w-7 items-center justify-center rounded-full bg-gray-100 ring-white"> <div className="ring-6 flex h-7 w-7 items-center justify-center rounded-full bg-brand-surface-2 ring-white">
{activityItem.field ? ( {activityItem.field ? (
activityDetails[activityItem.field as keyof typeof activityDetails] activityDetails[activityItem.field as keyof typeof activityDetails]
?.icon ?.icon
@ -332,7 +336,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
</div> </div>
</div> </div>
<div className="min-w-0 flex-1 py-3"> <div className="min-w-0 flex-1 py-3">
<div className="text-xs text-gray-500"> <div className="text-xs text-brand-secondary">
<span className="text-gray font-medium"> <span className="text-gray font-medium">
{activityItem.actor_detail.first_name} {activityItem.actor_detail.first_name}
{activityItem.actor_detail.is_bot {activityItem.actor_detail.is_bot
@ -340,7 +344,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
: " " + activityItem.actor_detail.last_name} : " " + activityItem.actor_detail.last_name}
</span> </span>
<span> {action} </span> <span> {action} </span>
<span className="text-xs font-medium text-gray-900"> {value} </span> <span className="text-xs font-medium text-brand-base"> {value} </span>
<span className="whitespace-nowrap"> <span className="whitespace-nowrap">
{timeAgo(activityItem.created_at)} {timeAgo(activityItem.created_at)}
</span> </span>
@ -353,12 +357,13 @@ export const IssueActivitySection: React.FC<Props> = () => {
); );
} else if ("comment_json" in activityItem) } else if ("comment_json" in activityItem)
return ( return (
<CommentCard <div key={activityItem.id} className="mt-4">
key={activityItem.id} <CommentCard
comment={activityItem as any} comment={activityItem as any}
onSubmit={handleCommentUpdate} onSubmit={handleCommentUpdate}
handleCommentDeletion={handleCommentDelete} handleCommentDeletion={handleCommentDelete}
/> />
</div>
); );
})} })}
</ul> </ul>

View File

@ -96,9 +96,9 @@ export const IssueAttachmentUpload = () => {
) : fileError ? ( ) : fileError ? (
<p className="text-center text-red-500">{fileError}</p> <p className="text-center text-red-500">{fileError}</p>
) : isLoading ? ( ) : isLoading ? (
<p className="text-center">Uploading....</p> <p className="text-center">Uploading...</p>
) : ( ) : (
<p className="text-center">Drag and drop/Click to add</p> <p className="text-center">Click or drag a file here</p>
)} )}
</span> </span>
</div> </div>

View File

@ -1,4 +1,4 @@
import React, { useMemo } from "react"; import React from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
@ -9,10 +9,10 @@ import { mutate } from "swr";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
// services // services
import issuesServices from "services/issues.service"; import issuesServices from "services/issues.service";
// hooks
import useToast from "hooks/use-toast";
// ui // ui
import { Loader } from "components/ui"; import { Loader, SecondaryButton } from "components/ui";
// helpers
import { debounce } from "helpers/common.helper";
// types // types
import type { IIssueComment } from "types"; import type { IIssueComment } from "types";
// fetch-keys // fetch-keys
@ -28,8 +28,8 @@ const RemirrorRichTextEditor = dynamic(() => import("components/rich-text-editor
}); });
const defaultValues: Partial<IIssueComment> = { const defaultValues: Partial<IIssueComment> = {
comment_html: "",
comment_json: "", comment_json: "",
comment_html: "",
}; };
export const AddComment: React.FC = () => { export const AddComment: React.FC = () => {
@ -42,9 +42,10 @@ export const AddComment: React.FC = () => {
} = useForm<IIssueComment>({ defaultValues }); } = useForm<IIssueComment>({ defaultValues });
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query; const { workspaceSlug, projectId, issueId } = router.query;
const { setToastAlert } = useToast();
const onSubmit = async (formData: IIssueComment) => { const onSubmit = async (formData: IIssueComment) => {
if ( if (
!workspaceSlug || !workspaceSlug ||
@ -61,53 +62,35 @@ export const AddComment: React.FC = () => {
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string)); mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
reset(defaultValues); reset(defaultValues);
}) })
.catch((error) => { .catch(() =>
console.error(error); setToastAlert({
}); type: "error",
title: "Error!",
message: "Comment could not be posted. Please try again.",
})
);
}; };
const updateDescription = useMemo(
() =>
debounce((key: any, val: any) => {
setValue(key, val);
}, 1000),
[setValue]
);
const updateDescriptionHTML = useMemo(
() =>
debounce((key: any, val: any) => {
setValue(key, val);
}, 1000),
[setValue]
);
return ( return (
<div className="space-y-5"> <div>
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="issue-comments-section"> <div className="issue-comments-section">
<Controller <Controller
name="comment_html" name="comment_json"
control={control} control={control}
render={({ field: { value } }) => ( render={({ field: { value } }) => (
<RemirrorRichTextEditor <RemirrorRichTextEditor
value={value} value={value}
onBlur={(jsonValue, htmlValue) => { onJSONChange={(jsonValue) => setValue("comment_json", jsonValue)}
setValue("comment_json", jsonValue); onHTMLChange={(htmlValue) => setValue("comment_html", htmlValue)}
setValue("comment_html", htmlValue);
}}
placeholder="Enter your comment..." placeholder="Enter your comment..."
/> />
)} )}
/> />
<button <SecondaryButton type="submit" disabled={isSubmitting} className="mt-2">
type="submit"
disabled={isSubmitting}
className="rounded-md bg-gray-300 p-2 px-4 text-sm text-black hover:bg-gray-300 mt-4"
>
{isSubmitting ? "Adding..." : "Comment"} {isSubmitting ? "Adding..." : "Comment"}
</button> </SecondaryButton>
</div> </div>
</form> </form>
</div> </div>

View File

@ -67,7 +67,7 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
</div> </div>
)} )}
<span className="absolute -bottom-0.5 -right-1 rounded-tl bg-white px-0.5 py-px"> <span className="absolute -bottom-0.5 -right-1 rounded-tl bg-brand-surface-2 px-0.5 py-px">
<ChatBubbleLeftEllipsisIcon className="h-3.5 w-3.5 text-gray-400" aria-hidden="true" /> <ChatBubbleLeftEllipsisIcon className="h-3.5 w-3.5 text-gray-400" aria-hidden="true" />
</span> </span>
</div> </div>
@ -77,7 +77,7 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
{comment.actor_detail.first_name} {comment.actor_detail.first_name}
{comment.actor_detail.is_bot ? "Bot" : " " + comment.actor_detail.last_name} {comment.actor_detail.is_bot ? "Bot" : " " + comment.actor_detail.last_name}
</div> </div>
<p className="mt-0.5 text-xs text-gray-500">Commented {timeAgo(comment.created_at)}</p> <p className="mt-0.5 text-xs text-brand-secondary">Commented {timeAgo(comment.created_at)}</p>
</div> </div>
<div className="issue-comments-section p-0"> <div className="issue-comments-section p-0">
{isEditing ? ( {isEditing ? (
@ -117,7 +117,7 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
editable={false} editable={false}
onBlur={() => ({})} onBlur={() => ({})}
noBorder noBorder
customClassName="text-xs bg-gray-100" customClassName="text-xs bg-brand-surface-1"
/> />
)} )}
</div> </div>

View File

@ -88,7 +88,7 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data })
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> <div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto"> <div className="fixed inset-0 z-10 overflow-y-auto">
@ -102,7 +102,7 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data })
leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
> >
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white 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 bg-brand-surface-2 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 flex-col gap-6 p-6">
<div className="flex w-full items-center justify-start gap-6"> <div className="flex w-full items-center justify-start gap-6">
<span className="place-items-center rounded-full bg-red-100 p-4"> <span className="place-items-center rounded-full bg-red-100 p-4">
@ -116,7 +116,7 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data })
</span> </span>
</div> </div>
<span> <span>
<p className="break-all text-sm leading-7 text-gray-500"> <p className="break-all text-sm leading-7 text-brand-secondary">
Are you sure you want to delete issue{" "} Are you sure you want to delete issue{" "}
<span className="break-all font-semibold"> <span className="break-all font-semibold">
{data?.project_detail.identifier}-{data?.sequence_id} {data?.project_detail.identifier}-{data?.sequence_id}

View File

@ -115,7 +115,7 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({ issue, handleFormS
role="textbox" role="textbox"
/> />
{characterLimit && ( {characterLimit && (
<div className="pointer-events-none absolute bottom-0 right-0 z-[2] rounded bg-white p-1 text-xs"> <div className="pointer-events-none absolute bottom-0 right-0 z-[2] rounded bg-brand-surface-2 p-1 text-xs">
<span <span
className={`${ className={`${
watch("name").length === 0 || watch("name").length > 255 ? "text-red-500" : "" watch("name").length === 0 || watch("name").length > 255 ? "text-red-500" : ""
@ -158,7 +158,7 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({ issue, handleFormS
)} )}
/> />
<div <div
className={`absolute -bottom-8 right-0 text-sm text-gray-500 ${ className={`absolute -bottom-8 right-0 text-sm text-brand-secondary ${
isSubmitting ? "block" : "hidden" isSubmitting ? "block" : "hidden"
}`} }`}
> >

View File

@ -216,12 +216,12 @@ export const IssueForm: FC<IssueFormProps> = ({
/> />
)} )}
/> />
<h3 className="text-xl font-semibold leading-6 text-gray-900"> <h3 className="text-xl font-semibold leading-6 text-brand-base">
{status ? "Update" : "Create"} Issue {status ? "Update" : "Create"} Issue
</h3> </h3>
</div> </div>
{watch("parent") && watch("parent") !== "" ? ( {watch("parent") && watch("parent") !== "" ? (
<div className="flex w-min items-center gap-2 whitespace-nowrap rounded bg-gray-100 p-2 text-xs"> <div className="flex w-min items-center gap-2 whitespace-nowrap rounded bg-brand-surface-1 p-2 text-xs">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span <span
className="block h-1.5 w-1.5 rounded-full" className="block h-1.5 w-1.5 rounded-full"
@ -267,7 +267,7 @@ export const IssueForm: FC<IssueFormProps> = ({
/> />
{mostSimilarIssue && ( {mostSimilarIssue && (
<div className="flex items-center gap-x-2"> <div className="flex items-center gap-x-2">
<p className="text-sm text-gray-500"> <p className="text-sm text-brand-secondary">
<Link <Link
href={`/${workspaceSlug}/projects/${projectId}/issues/${mostSimilarIssue.id}`} href={`/${workspaceSlug}/projects/${projectId}/issues/${mostSimilarIssue.id}`}
> >
@ -283,7 +283,7 @@ export const IssueForm: FC<IssueFormProps> = ({
</p> </p>
<button <button
type="button" type="button"
className="text-sm text-blue-500" className="text-sm text-brand-accent"
onClick={() => { onClick={() => {
setMostSimilarIssue(undefined); setMostSimilarIssue(undefined);
}} }}
@ -298,7 +298,7 @@ export const IssueForm: FC<IssueFormProps> = ({
{issueName && issueName !== "" && ( {issueName && issueName !== "" && (
<button <button
type="button" type="button"
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-gray-100 ${ className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-brand-surface-1 ${
iAmFeelingLucky ? "cursor-wait" : "" iAmFeelingLucky ? "cursor-wait" : ""
}`} }`}
onClick={handleAutoGenerateDescription} onClick={handleAutoGenerateDescription}
@ -315,7 +315,7 @@ export const IssueForm: FC<IssueFormProps> = ({
)} )}
<button <button
type="button" type="button"
className="flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-gray-100" className="flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-brand-surface-1"
onClick={() => setGptAssistantModal((prevData) => !prevData)} onClick={() => setGptAssistantModal((prevData) => !prevData)}
> >
<SparklesIcon className="h-4 w-4" /> <SparklesIcon className="h-4 w-4" />
@ -444,7 +444,7 @@ export const IssueForm: FC<IssueFormProps> = ({
</div> </div>
</div> </div>
</div> </div>
<div className="-mx-5 mt-5 flex items-center justify-between gap-2 border-t px-5 pt-5"> <div className="-mx-5 mt-5 flex items-center justify-between gap-2 border-t border-brand-base px-5 pt-5">
<div <div
className="flex cursor-pointer items-center gap-1" className="flex cursor-pointer items-center gap-1"
onClick={() => setCreateMore((prevData) => !prevData)} onClick={() => setCreateMore((prevData) => !prevData)}
@ -453,7 +453,7 @@ export const IssueForm: FC<IssueFormProps> = ({
<button <button
type="button" type="button"
className={`pointer-events-none relative inline-flex h-4 w-7 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent ${ className={`pointer-events-none relative inline-flex h-4 w-7 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent ${
createMore ? "bg-theme" : "bg-gray-300" createMore ? "bg-brand-accent" : "bg-gray-300"
} transition-colors duration-300 ease-in-out focus:outline-none`} } transition-colors duration-300 ease-in-out focus:outline-none`}
role="switch" role="switch"
aria-checked="false" aria-checked="false"
@ -463,7 +463,7 @@ export const IssueForm: FC<IssueFormProps> = ({
aria-hidden="true" aria-hidden="true"
className={`pointer-events-none inline-block h-3 w-3 ${ className={`pointer-events-none inline-block h-3 w-3 ${
createMore ? "translate-x-3" : "translate-x-0" createMore ? "translate-x-3" : "translate-x-0"
} transform rounded-full bg-white shadow ring-0 transition duration-300 ease-in-out`} } transform rounded-full bg-brand-surface-2 shadow ring-0 transition duration-300 ease-in-out`}
/> />
</button> </button>
</div> </div>

View File

@ -219,7 +219,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" /> <div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto"> <div className="fixed inset-0 z-10 overflow-y-auto">
@ -233,7 +233,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
> >
<Dialog.Panel className="relative transform rounded-lg bg-white p-5 text-left shadow-xl transition-all sm:w-full sm:max-w-2xl"> <Dialog.Panel className="relative transform rounded-lg bg-brand-surface-1 p-5 text-left shadow-xl transition-all sm:w-full sm:max-w-2xl">
<IssueForm <IssueForm
issues={issues ?? []} issues={issues ?? []}
handleFormSubmit={handleFormSubmit} handleFormSubmit={handleFormSubmit}

View File

@ -82,7 +82,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
const isNotAllowed = false; const isNotAllowed = false;
return ( return (
<div className="border-b border-gray-300 last:border-b-0 mx-6"> <div className="border-b border-brand-base last:border-b-0 mx-6">
<div key={issue.id} className="flex items-center justify-between gap-2 py-3"> <div key={issue.id} className="flex items-center justify-between gap-2 py-3">
<Link href={`/${workspaceSlug}/projects/${issue?.project_detail?.id}/issues/${issue.id}`}> <Link href={`/${workspaceSlug}/projects/${issue?.project_detail?.id}/issues/${issue.id}`}>
<a className="group relative flex items-center gap-2"> <a className="group relative flex items-center gap-2">
@ -97,7 +97,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
</Tooltip> </Tooltip>
)} )}
<Tooltip position="top-left" tooltipHeading="Title" tooltipContent={issue.name}> <Tooltip position="top-left" tooltipHeading="Title" tooltipContent={issue.name}>
<span className="break-all text-sm text-gray-800"> <span className="break-all text-sm text-brand-base">
{truncateText(issue.name, 50)} {truncateText(issue.name, 50)}
</span> </span>
</Tooltip> </Tooltip>
@ -127,7 +127,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
/> />
)} )}
{properties.sub_issue_count && ( {properties.sub_issue_count && (
<div className="flex items-center gap-1 rounded-md border px-3 py-1.5 text-xs shadow-sm"> <div className="flex items-center gap-1 rounded-md border border-brand-base px-3 py-1.5 text-xs shadow-sm">
{issue?.sub_issues_count} {issue?.sub_issues_count === 1 ? "sub-issue" : "sub-issues"} {issue?.sub_issues_count} {issue?.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
</div> </div>
)} )}
@ -136,7 +136,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
{issue.label_details.map((label) => ( {issue.label_details.map((label) => (
<span <span
key={label.id} key={label.id}
className="group flex items-center gap-1 rounded-2xl border px-2 py-0.5 text-xs" className="group flex items-center gap-1 rounded-2xl border border-brand-base px-2 py-0.5 text-xs"
> >
<span <span
className="h-1.5 w-1.5 flex-shrink-0 rounded-full" className="h-1.5 w-1.5 flex-shrink-0 rounded-full"

View File

@ -71,17 +71,17 @@ export const ParentIssuesListModal: React.FC<Props> = ({
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-white shadow-2xl ring-1 ring-black ring-opacity-5 transition-all"> <Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-brand-surface-2 shadow-2xl ring-1 ring-black ring-opacity-5 transition-all">
{multiple ? ( {multiple ? (
<> <>
<Combobox value={value} onChange={() => ({})} multiple> <Combobox value={value} onChange={() => ({})} multiple>
<div className="relative m-1"> <div className="relative m-1">
<MagnifyingGlassIcon <MagnifyingGlassIcon
className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-gray-900 text-opacity-40" className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-brand-base text-opacity-40"
aria-hidden="true" aria-hidden="true"
/> />
<Combobox.Input <Combobox.Input
className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-gray-900 placeholder-gray-500 outline-none focus:ring-0 sm:text-sm" className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-brand-base placeholder-gray-500 outline-none focus:ring-0 sm:text-sm"
placeholder="Search..." placeholder="Search..."
onChange={(e) => setQuery(e.target.value)} onChange={(e) => setQuery(e.target.value)}
displayValue={() => ""} displayValue={() => ""}
@ -95,7 +95,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
{filteredIssues.length > 0 && ( {filteredIssues.length > 0 && (
<li className="p-2"> <li className="p-2">
{query === "" && ( {query === "" && (
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-gray-900"> <h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-brand-base">
{title} {title}
</h2> </h2>
)} )}
@ -106,7 +106,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
value={issue.id} value={issue.id}
className={({ active }) => className={({ active }) =>
`flex cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 ${ `flex cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 ${
active ? "bg-gray-900 bg-opacity-5 text-gray-900" : "" active ? "bg-gray-900 bg-opacity-5 text-brand-base" : ""
}` }`
} }
> >
@ -119,7 +119,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
backgroundColor: issue.state_detail.color, backgroundColor: issue.state_detail.color,
}} }}
/> />
<span className="flex-shrink-0 text-xs text-gray-500"> <span className="flex-shrink-0 text-xs text-brand-secondary">
{issue.project_detail?.identifier}-{issue.sequence_id} {issue.project_detail?.identifier}-{issue.sequence_id}
</span>{" "} </span>{" "}
{issue.id} {issue.id}
@ -135,10 +135,10 @@ export const ParentIssuesListModal: React.FC<Props> = ({
{query !== "" && filteredIssues.length === 0 && ( {query !== "" && filteredIssues.length === 0 && (
<div className="py-14 px-6 text-center sm:px-14"> <div className="py-14 px-6 text-center sm:px-14">
<RectangleStackIcon <RectangleStackIcon
className="mx-auto h-6 w-6 text-gray-900 text-opacity-40" className="mx-auto h-6 w-6 text-brand-base text-opacity-40"
aria-hidden="true" aria-hidden="true"
/> />
<p className="mt-4 text-sm text-gray-900"> <p className="mt-4 text-sm text-brand-base">
We couldn{"'"}t find any issue with that term. Please try again. We couldn{"'"}t find any issue with that term. Please try again.
</p> </p>
</div> </div>
@ -153,11 +153,11 @@ export const ParentIssuesListModal: React.FC<Props> = ({
<Combobox value={value} onChange={onChange}> <Combobox value={value} onChange={onChange}>
<div className="relative m-1"> <div className="relative m-1">
<MagnifyingGlassIcon <MagnifyingGlassIcon
className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-gray-900 text-opacity-40" className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-brand-base text-opacity-40"
aria-hidden="true" aria-hidden="true"
/> />
<Combobox.Input <Combobox.Input
className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-gray-900 placeholder-gray-500 outline-none focus:ring-0 sm:text-sm" className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-brand-base placeholder-gray-500 outline-none focus:ring-0 sm:text-sm"
placeholder="Search..." placeholder="Search..."
onChange={(e) => setQuery(e.target.value)} onChange={(e) => setQuery(e.target.value)}
displayValue={() => ""} displayValue={() => ""}
@ -171,7 +171,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
{filteredIssues.length > 0 ? ( {filteredIssues.length > 0 ? (
<li className="p-2"> <li className="p-2">
{query === "" && ( {query === "" && (
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-gray-900"> <h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-brand-base">
{title} {title}
</h2> </h2>
)} )}
@ -182,7 +182,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
value={issue.id} value={issue.id}
className={({ active }) => className={({ active }) =>
`flex cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 ${ `flex cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 ${
active ? "bg-gray-900 bg-opacity-5 text-gray-900" : "" active ? "bg-gray-900 bg-opacity-5 text-brand-base" : ""
}` }`
} }
onClick={() => handleClose()} onClick={() => handleClose()}
@ -194,7 +194,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
backgroundColor: issue.state_detail.color, backgroundColor: issue.state_detail.color,
}} }}
/> />
<span className="flex-shrink-0 text-xs text-gray-500"> <span className="flex-shrink-0 text-xs text-brand-secondary">
{issue.project_detail?.identifier}-{issue.sequence_id} {issue.project_detail?.identifier}-{issue.sequence_id}
</span>{" "} </span>{" "}
{issue.name} {issue.name}
@ -206,9 +206,9 @@ export const ParentIssuesListModal: React.FC<Props> = ({
) : ( ) : (
<div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center"> <div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center">
<LayerDiagonalIcon height="56" width="56" /> <LayerDiagonalIcon height="56" width="56" />
<h3 className="text-gray-500"> <h3 className="text-brand-secondary">
No issues found. Create a new issue with{" "} No issues found. Create a new issue with{" "}
<pre className="inline rounded bg-gray-200 px-2 py-1">C</pre>. <pre className="inline rounded bg-brand-surface-2 px-2 py-1">C</pre>.
</h3> </h3>
</div> </div>
)} )}

View File

@ -54,16 +54,15 @@ export const IssueAssigneeSelect: React.FC<Props> = ({ projectId, value = [], on
onChange={onChange} onChange={onChange}
options={options} options={options}
label={ label={
<div className="flex items-center gap-2 text-gray-500"> <div className="flex items-center gap-2 text-brand-secondary">
{value && value.length > 0 && Array.isArray(value) ? ( {value && value.length > 0 && Array.isArray(value) ? (
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
<AssigneesList userIds={value} length={3} showLength={false} /> <AssigneesList userIds={value} length={3} showLength={true} />
<span className="text-gray-500">{value.length} Assignees</span>
</div> </div>
) : ( ) : (
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
<UserGroupIcon className="h-4 w-4 text-gray-500" /> <UserGroupIcon className="h-4 w-4 text-brand-secondary" />
<span className="text-gray-500">Assignee</span> <span className="text-brand-secondary">Assignee</span>
</div> </div>
)} )}
</div> </div>

View File

@ -18,11 +18,11 @@ export const IssueDateSelect: React.FC<Props> = ({ value, onChange }) => (
<> <>
<Popover.Button <Popover.Button
className={({ open }) => className={({ open }) =>
`flex cursor-pointer items-center rounded-md border text-xs shadow-sm duration-200 `flex cursor-pointer items-center rounded-md border border-brand-base text-xs shadow-sm duration-200
${ ${
open open
? "border-theme bg-theme/5 outline-none ring-1 ring-theme " ? "border-brand-accent bg-brand-accent/5 outline-none ring-1 ring-brand-accent "
: "hover:bg-theme/5 " : "hover:bg-brand-accent/5 "
}` }`
} }
> >

View File

@ -60,11 +60,11 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
<> <>
<Combobox.Button <Combobox.Button
className={({ open }) => className={({ open }) =>
`flex cursor-pointer items-center rounded-md border text-xs shadow-sm duration-200 `flex cursor-pointer items-center rounded-md border border-brand-base text-xs shadow-sm duration-200
${ ${
open open
? "border-theme bg-theme/5 outline-none ring-1 ring-theme " ? "border-brand-accent bg-brand-accent/5 outline-none ring-1 ring-brand-accent "
: "hover:bg-theme/5 " : "hover:bg-brand-accent/5 "
}` }`
} }
> >
@ -73,14 +73,13 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
<IssueLabelsList <IssueLabelsList
labels={value.map((v) => issueLabels?.find((l) => l.id === v)?.color) ?? []} labels={value.map((v) => issueLabels?.find((l) => l.id === v)?.color) ?? []}
length={3} length={3}
showLength showLength={true}
/> />
<span className=" text-gray-600">{value.length} Labels</span>
</span> </span>
) : ( ) : (
<span className="flex items-center justify-center gap-2 px-3 py-1.5 text-xs"> <span className="flex items-center justify-center gap-2 px-3 py-1.5 text-xs">
<TagIcon className="h-3 w-3 text-gray-500" /> <TagIcon className="h-3 w-3 text-brand-secondary" />
<span className=" text-gray-500">Label</span> <span className=" text-brand-secondary">Label</span>
</span> </span>
)} )}
</Combobox.Button> </Combobox.Button>
@ -97,12 +96,12 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
> >
<Combobox.Options <Combobox.Options
className={`absolute z-10 mt-1 max-h-52 min-w-[8rem] overflow-auto rounded-md border-none className={`absolute z-10 mt-1 max-h-52 min-w-[8rem] overflow-auto rounded-md border-none
bg-white px-2 py-2 text-xs shadow-md focus:outline-none`} bg-brand-surface-2 px-2 py-2 text-xs shadow-md focus:outline-none`}
> >
<div className="flex w-full items-center justify-start rounded-sm border-[0.6px] bg-gray-100 px-2"> <div className="flex w-full items-center justify-start rounded-sm border-[0.6px] bg-brand-surface-1 px-2">
<MagnifyingGlassIcon className="h-3 w-3 text-gray-500" /> <MagnifyingGlassIcon className="h-3 w-3 text-brand-secondary" />
<Combobox.Input <Combobox.Input
className="w-full bg-transparent py-1 px-2 text-xs text-gray-500 focus:outline-none" className="w-full bg-transparent py-1 px-2 text-xs text-brand-secondary focus:outline-none"
onChange={(event) => setQuery(event.target.value)} onChange={(event) => setQuery(event.target.value)}
placeholder="Search for label..." placeholder="Search for label..."
displayValue={(assigned: any) => assigned?.name} displayValue={(assigned: any) => assigned?.name}
@ -121,7 +120,7 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
key={label.id} key={label.id}
className={({ active }) => className={({ active }) =>
`${ `${
active ? "bg-gray-200" : "" active ? "bg-brand-surface-2" : ""
} group flex min-w-[14rem] cursor-pointer select-none items-center gap-2 truncate rounded px-1 py-1.5 text-gray-600` } group flex min-w-[14rem] cursor-pointer select-none items-center gap-2 truncate rounded px-1 py-1.5 text-gray-600`
} }
value={label.id} value={label.id}
@ -151,8 +150,8 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
); );
} else } else
return ( return (
<div className="border-y border-gray-400 bg-gray-50"> <div className="border-y border-brand-base bg-brand-surface-2">
<div className="flex select-none items-center gap-2 truncate p-2 font-medium text-gray-900"> <div className="flex select-none items-center gap-2 truncate p-2 font-medium text-brand-base">
<RectangleGroupIcon className="h-3 w-3" /> {label.name} <RectangleGroupIcon className="h-3 w-3" /> {label.name}
</div> </div>
<div> <div>
@ -161,7 +160,7 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
key={child.id} key={child.id}
className={({ active }) => className={({ active }) =>
`${ `${
active ? "bg-gray-200" : "" active ? "bg-brand-surface-2" : ""
} group flex min-w-[14rem] cursor-pointer select-none items-center gap-2 truncate rounded px-1 py-1.5 text-gray-600` } group flex min-w-[14rem] cursor-pointer select-none items-center gap-2 truncate rounded px-1 py-1.5 text-gray-600`
} }
value={child.id} value={child.id}
@ -193,14 +192,14 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
); );
}) })
) : ( ) : (
<p className="px-2 text-xs text-gray-500">No labels found</p> <p className="px-2 text-xs text-brand-secondary">No labels found</p>
) )
) : ( ) : (
<p className="px-2 text-xs text-gray-500">Loading...</p> <p className="px-2 text-xs text-brand-secondary">Loading...</p>
)} )}
<button <button
type="button" type="button"
className="flex w-full select-none items-center rounded py-2 px-1 hover:bg-gray-200" className="flex w-full select-none items-center rounded py-2 px-1 hover:bg-brand-surface-2"
onClick={() => setIsOpen(true)} onClick={() => setIsOpen(true)}
> >
<span className="flex items-center justify-start gap-1"> <span className="flex items-center justify-start gap-1">

View File

@ -18,9 +18,9 @@ export const IssuePrioritySelect: React.FC<Props> = ({ value, onChange }) => (
label={ label={
<div className="flex items-center justify-center gap-2 text-xs"> <div className="flex items-center justify-center gap-2 text-xs">
<span className="flex items-center"> <span className="flex items-center">
{getPriorityIcon(value, `text-xs ${value ? "" : "text-gray-500"}`)} {getPriorityIcon(value, `text-xs ${value ? "" : "text-brand-secondary"}`)}
</span> </span>
<span className={`${value ? "text-gray-600" : "text-gray-500"} capitalize`}> <span className={`${value ? "text-gray-600" : "text-brand-secondary"} capitalize`}>
{value ?? "Priority"} {value ?? "Priority"}
</span> </span>
</div> </div>

View File

@ -59,7 +59,7 @@ export const IssueProjectSelect: React.FC<IssueProjectSelectProps> = ({
<p className="text-gray-400">No projects found!</p> <p className="text-gray-400">No projects found!</p>
) )
) : ( ) : (
<div className="px-2 text-sm text-gray-500">Loading...</div> <div className="px-2 text-sm text-brand-secondary">Loading...</div>
)} )}
</CustomSelect> </CustomSelect>
); );

View File

@ -56,7 +56,7 @@ export const IssueStateSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
onChange={onChange} onChange={onChange}
options={options} options={options}
label={ label={
<div className="flex items-center gap-2 text-gray-500"> <div className="flex items-center gap-2 text-brand-secondary">
{selectedOption ? ( {selectedOption ? (
getStateGroupIcon(selectedOption.group, "16", "16", selectedOption.color) getStateGroupIcon(selectedOption.group, "16", "16", selectedOption.color)
) : currentDefaultState ? ( ) : currentDefaultState ? (
@ -70,7 +70,7 @@ export const IssueStateSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
footerOption={ footerOption={
<button <button
type="button" type="button"
className="flex w-full select-none items-center gap-2 rounded px-1 py-1.5 text-xs text-gray-500 hover:bg-hover-gray" className="flex w-full select-none items-center gap-2 rounded px-1 py-1.5 text-xs text-brand-secondary hover:bg-brand-surface-2"
onClick={() => setIsOpen(true)} onClick={() => setIsOpen(true)}
> >
<PlusIcon className="h-4 w-4" aria-hidden="true" /> <PlusIcon className="h-4 w-4" aria-hidden="true" />

View File

@ -56,7 +56,7 @@ export const SidebarAssigneeSelect: React.FC<Props> = ({ value, onChange, userAu
return ( return (
<div className="flex flex-wrap items-center py-2"> <div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2"> <div className="flex items-center gap-x-2 text-sm text-brand-secondary sm:basis-1/2">
<UserGroupIcon className="h-4 w-4 flex-shrink-0" /> <UserGroupIcon className="h-4 w-4 flex-shrink-0" />
<p>Assignees</p> <p>Assignees</p>
</div> </div>
@ -64,11 +64,11 @@ export const SidebarAssigneeSelect: React.FC<Props> = ({ value, onChange, userAu
<CustomSearchSelect <CustomSearchSelect
value={value} value={value}
label={ label={
<div className="flex items-center gap-2 text-gray-500"> <div className="flex items-center gap-2 text-brand-secondary">
{value && value.length > 0 && Array.isArray(value) ? ( {value && value.length > 0 && Array.isArray(value) ? (
<div className="flex items-center justify-center gap-2 -my-0.5"> <div className="-my-0.5 flex items-center justify-center gap-2">
<AssigneesList userIds={value} length={3} showLength={false} /> <AssigneesList userIds={value} length={3} showLength={false} />
<span className="text-gray-500">{value.length} Assignees</span> <span className="text-brand-base">{value.length} Assignees</span>
</div> </div>
) : ( ) : (
"No assignees" "No assignees"

View File

@ -105,7 +105,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
return ( return (
<div className="flex flex-wrap items-start py-2"> <div className="flex flex-wrap items-start py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2"> <div className="flex items-center gap-x-2 text-sm text-brand-secondary sm:basis-1/2">
<BlockedIcon height={16} width={16} /> <BlockedIcon height={16} width={16} />
<p>Blocked by</p> <p>Blocked by</p>
</div> </div>
@ -115,7 +115,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
? watch("blocked_list").map((issue) => ( ? watch("blocked_list").map((issue) => (
<div <div
key={issue} key={issue}
className="group flex cursor-pointer items-center gap-1 rounded-2xl border border-white px-1.5 py-0.5 text-xs text-red-500 duration-300 hover:border-red-500 hover:bg-red-50" className="group flex cursor-pointer items-center gap-1 rounded-2xl border border-brand-base px-1.5 py-0.5 text-xs text-red-500 duration-300 hover:border-red-500/20 hover:bg-red-500/20"
> >
<Link <Link
href={`/${workspaceSlug}/projects/${projectId}/issues/${ href={`/${workspaceSlug}/projects/${projectId}/issues/${
@ -176,7 +176,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-white bg-opacity-80 shadow-2xl ring-1 ring-black ring-opacity-5 backdrop-blur backdrop-filter transition-all"> <Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-brand-surface-2 bg-opacity-80 shadow-2xl ring-1 ring-black ring-opacity-5 backdrop-blur backdrop-filter transition-all">
<form> <form>
<Combobox <Combobox
onChange={(val: string) => { onChange={(val: string) => {
@ -191,12 +191,12 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
> >
<div className="relative m-1"> <div className="relative m-1">
<MagnifyingGlassIcon <MagnifyingGlassIcon
className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-gray-900 text-opacity-40" className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-brand-base text-opacity-40"
aria-hidden="true" aria-hidden="true"
/> />
<input <input
type="text" type="text"
className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-gray-900 placeholder-gray-500 outline-none focus:ring-0 sm:text-sm" className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-brand-base placeholder-gray-500 outline-none focus:ring-0 sm:text-sm"
placeholder="Search..." placeholder="Search..."
onChange={(e) => setQuery(e.target.value)} onChange={(e) => setQuery(e.target.value)}
/> />
@ -209,7 +209,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
{filteredIssues.length > 0 ? ( {filteredIssues.length > 0 ? (
<li className="p-2"> <li className="p-2">
{query === "" && ( {query === "" && (
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-gray-900"> <h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-brand-base">
Select blocked issues Select blocked issues
</h2> </h2>
)} )}
@ -226,7 +226,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
value={issue.id} value={issue.id}
className={({ active }) => className={({ active }) =>
`flex cursor-pointer select-none items-center justify-between rounded-md px-3 py-2 ${ `flex cursor-pointer select-none items-center justify-between rounded-md px-3 py-2 ${
active ? "bg-gray-900 bg-opacity-5 text-gray-900" : "" active ? "bg-gray-900 bg-opacity-5 text-brand-base" : ""
}` }`
} }
> >
@ -244,7 +244,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
backgroundColor: issue.state_detail.color, backgroundColor: issue.state_detail.color,
}} }}
/> />
<span className="flex-shrink-0 text-xs text-gray-500"> <span className="flex-shrink-0 text-xs text-brand-secondary">
{ {
issues?.find((i) => i.id === issue.id)?.project_detail issues?.find((i) => i.id === issue.id)?.project_detail
?.identifier ?.identifier
@ -262,9 +262,9 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
) : ( ) : (
<div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center"> <div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center">
<LayerDiagonalIcon height="56" width="56" /> <LayerDiagonalIcon height="56" width="56" />
<h3 className="text-gray-500"> <h3 className="text-brand-secondary">
No issues found. Create a new issue with{" "} No issues found. Create a new issue with{" "}
<pre className="inline rounded bg-gray-200 px-2 py-1">C</pre>. <pre className="inline rounded bg-brand-surface-2 px-2 py-1">C</pre>.
</h3> </h3>
</div> </div>
)} )}
@ -287,9 +287,9 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
</Transition.Root> </Transition.Root>
<button <button
type="button" type="button"
className={`flex w-full ${ className={`flex w-full text-brand-secondary ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100" isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-brand-surface-2"
} items-center justify-between gap-1 rounded-md border px-2 py-1 text-xs shadow-sm duration-300 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500`} } items-center justify-between gap-1 rounded-md border border-brand-base px-2 py-1 text-xs shadow-sm duration-300 focus:outline-none`}
onClick={() => setIsBlockedModalOpen(true)} onClick={() => setIsBlockedModalOpen(true)}
disabled={isNotAllowed} disabled={isNotAllowed}
> >

View File

@ -105,7 +105,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
return ( return (
<div className="flex flex-wrap items-start py-2"> <div className="flex flex-wrap items-start py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2"> <div className="flex items-center gap-x-2 text-sm text-brand-secondary sm:basis-1/2">
<BlockerIcon height={16} width={16} /> <BlockerIcon height={16} width={16} />
<p>Blocking</p> <p>Blocking</p>
</div> </div>
@ -115,7 +115,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
? watch("blockers_list").map((issue) => ( ? watch("blockers_list").map((issue) => (
<div <div
key={issue} key={issue}
className="group flex cursor-pointer items-center gap-1 rounded-2xl border border-white px-1.5 py-0.5 text-xs text-yellow-500 duration-300 hover:border-yellow-500 hover:bg-yellow-50" className="group flex cursor-pointer items-center gap-1 rounded-2xl border border-brand-base px-1.5 py-0.5 text-xs text-yellow-500 duration-300 hover:border-yellow-500/20 hover:bg-yellow-500/20"
> >
<Link <Link
href={`/${workspaceSlug}/projects/${projectId}/issues/${ href={`/${workspaceSlug}/projects/${projectId}/issues/${
@ -176,7 +176,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-white bg-opacity-80 shadow-2xl ring-1 ring-black ring-opacity-5 backdrop-blur backdrop-filter transition-all"> <Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-brand-surface-2 bg-opacity-80 shadow-2xl ring-1 ring-black ring-opacity-5 backdrop-blur backdrop-filter transition-all">
<Combobox <Combobox
onChange={(val: string) => { onChange={(val: string) => {
const selectedIssues = watchBlocker("blocker_issue_ids"); const selectedIssues = watchBlocker("blocker_issue_ids");
@ -190,12 +190,12 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
> >
<div className="relative m-1"> <div className="relative m-1">
<MagnifyingGlassIcon <MagnifyingGlassIcon
className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-gray-900 text-opacity-40" className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-brand-base text-opacity-40"
aria-hidden="true" aria-hidden="true"
/> />
<input <input
type="text" type="text"
className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-gray-900 placeholder-gray-500 outline-none focus:ring-0 sm:text-sm" className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-brand-base placeholder-gray-500 outline-none focus:ring-0 sm:text-sm"
placeholder="Search..." placeholder="Search..."
onChange={(e) => setQuery(e.target.value)} onChange={(e) => setQuery(e.target.value)}
/> />
@ -208,7 +208,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
{filteredIssues.length > 0 ? ( {filteredIssues.length > 0 ? (
<li className="p-2"> <li className="p-2">
{query === "" && ( {query === "" && (
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-gray-900"> <h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-brand-base">
Select blocker issues Select blocker issues
</h2> </h2>
)} )}
@ -225,7 +225,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
value={issue.id} value={issue.id}
className={({ active }) => className={({ active }) =>
`flex cursor-pointer select-none items-center justify-between rounded-md px-3 py-2 ${ `flex cursor-pointer select-none items-center justify-between rounded-md px-3 py-2 ${
active ? "bg-gray-900 bg-opacity-5 text-gray-900" : "" active ? "bg-gray-900 bg-opacity-5 text-brand-base" : ""
}` }`
} }
> >
@ -243,14 +243,14 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
backgroundColor: issue.state_detail.color, backgroundColor: issue.state_detail.color,
}} }}
/> />
<span className="flex-shrink-0 text-xs text-gray-500"> <span className="flex-shrink-0 text-xs text-brand-secondary">
{ {
issues?.find((i) => i.id === issue.id)?.project_detail issues?.find((i) => i.id === issue.id)?.project_detail
?.identifier ?.identifier
} }
-{issue.sequence_id} -{issue.sequence_id}
</span> </span>
<span>{issue.name}</span> <span className="text-brand-muted-1">{issue.name}</span>
</div> </div>
</Combobox.Option> </Combobox.Option>
); );
@ -260,9 +260,9 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
) : ( ) : (
<div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center"> <div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center">
<LayerDiagonalIcon height="56" width="56" /> <LayerDiagonalIcon height="56" width="56" />
<h3 className="text-gray-500"> <h3 className="text-brand-secondary">
No issues found. Create a new issue with{" "} No issues found. Create a new issue with{" "}
<pre className="inline rounded bg-gray-200 px-2 py-1">C</pre>. <pre className="inline rounded bg-brand-surface-2 px-2 py-1">C</pre>.
</h3> </h3>
</div> </div>
)} )}
@ -284,9 +284,9 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
</Transition.Root> </Transition.Root>
<button <button
type="button" type="button"
className={`flex w-full ${ className={`flex w-full text-brand-secondary ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100" isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-brand-surface-2"
} items-center justify-between gap-1 rounded-md border px-2 py-1 text-xs shadow-sm duration-300 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500`} } items-center justify-between gap-1 rounded-md border border-brand-base px-2 py-1 text-xs shadow-sm duration-300 focus:outline-none`}
onClick={() => setIsBlockerModalOpen(true)} onClick={() => setIsBlockerModalOpen(true)}
disabled={isNotAllowed} disabled={isNotAllowed}
> >

View File

@ -12,7 +12,7 @@ import { Spinner, CustomSelect, Tooltip } from "components/ui";
// helper // helper
import { truncateText } from "helpers/string.helper"; import { truncateText } from "helpers/string.helper";
// icons // icons
import { CyclesIcon } from "components/icons"; import { ContrastIcon } from "components/icons";
// types // types
import { ICycle, IIssue, UserAuth } from "types"; import { ICycle, IIssue, UserAuth } from "types";
// fetch-keys // fetch-keys
@ -60,20 +60,21 @@ export const SidebarCycleSelect: React.FC<Props> = ({
return ( return (
<div className="flex flex-wrap items-center py-2"> <div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2"> <div className="flex items-center gap-x-2 text-sm text-brand-secondary sm:basis-1/2">
<CyclesIcon className="h-4 w-4 flex-shrink-0" /> <ContrastIcon className="h-4 w-4 flex-shrink-0" />
<p>Cycle</p> <p>Cycle</p>
</div> </div>
<div className="space-y-1 sm:basis-1/2"> <div className="space-y-1 sm:basis-1/2">
<CustomSelect <CustomSelect
label={ label={
<Tooltip position="left" tooltipContent={`${issueCycle ? issueCycle.cycle_detail.name : ""}`}> <Tooltip
<span position="left"
className={`w-full max-w-[125px] truncate text-left sm:block ${ tooltipContent={`${issueCycle ? issueCycle.cycle_detail.name : "No cycle"}`}
issueCycle ? "" : "text-gray-900" >
}`} <span className="w-full max-w-[125px] truncate text-left sm:block">
> <span className={`${issueCycle ? "text-brand-base" : "text-brand-secondary"}`}>
{issueCycle ? truncateText(issueCycle.cycle_detail.name, 15) : "None"} {issueCycle ? truncateText(issueCycle.cycle_detail.name, 15) : "No cycle"}
</span>
</span> </span>
</Tooltip> </Tooltip>
} }

View File

@ -24,8 +24,8 @@ export const SidebarEstimateSelect: React.FC<Props> = ({ value, onChange, userAu
return ( return (
<div className="flex flex-wrap items-center py-2"> <div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2"> <div className="flex items-center gap-x-2 text-sm text-brand-secondary sm:basis-1/2">
<PlayIcon className="h-4 w-4 -rotate-90 flex-shrink-0" /> <PlayIcon className="h-4 w-4 flex-shrink-0 -rotate-90" />
<p>Estimate</p> <p>Estimate</p>
</div> </div>
<div className="sm:basis-1/2"> <div className="sm:basis-1/2">
@ -33,10 +33,14 @@ export const SidebarEstimateSelect: React.FC<Props> = ({ value, onChange, userAu
value={value} value={value}
label={ label={
<div className="flex items-center gap-2 text-xs"> <div className="flex items-center gap-2 text-xs">
<PlayIcon className="h-4 w-4 text-gray-700 -rotate-90" /> <PlayIcon
<span className={`${value ? "text-gray-600" : "text-gray-500"}`}> className={`h-4 w-4 -rotate-90 ${
{estimatePoints?.find((e) => e.key === value)?.value ?? "Estimate"} value !== null ? "text-brand-base" : "text-brand-secondary"
</span> }`}
/>
{estimatePoints?.find((e) => e.key === value)?.value ?? (
<span className="text-brand-secondary">No estimates</span>
)}
</div> </div>
} }
onChange={onChange} onChange={onChange}

View File

@ -59,21 +59,27 @@ export const SidebarModuleSelect: React.FC<Props> = ({
return ( return (
<div className="flex flex-wrap items-center py-2"> <div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2"> <div className="flex items-center gap-x-2 text-sm text-brand-secondary sm:basis-1/2">
<RectangleGroupIcon className="h-4 w-4 flex-shrink-0" /> <RectangleGroupIcon className="h-4 w-4 flex-shrink-0" />
<p>Module</p> <p>Module</p>
</div> </div>
<div className="space-y-1 sm:basis-1/2"> <div className="space-y-1 sm:basis-1/2">
<CustomSelect <CustomSelect
label={ label={
<Tooltip position="left" tooltipContent={`${modules?.find((m) => m.id === issueModule?.module)?.name ?? "None"}`}> <Tooltip
<span position="left"
className={`w-full max-w-[125px] truncate text-left sm:block ${ tooltipContent={`${
issueModule ? "" : "text-gray-900" modules?.find((m) => m.id === issueModule?.module)?.name ?? "No module"
}`} }`}
> >
{truncateText(`${modules?.find((m) => m.id === issueModule?.module)?.name ?? "None"}`, 15)} <span className="w-full max-w-[125px] truncate text-left sm:block">
</span> <span className={`${issueModule ? "text-brand-base" : "text-brand-secondary"}`}>
{truncateText(
`${modules?.find((m) => m.id === issueModule?.module)?.name ?? "No module"}`,
15
)}
</span>
</span>
</Tooltip> </Tooltip>
} }
value={issueModule?.module_detail?.id} value={issueModule?.module_detail?.id}
@ -93,7 +99,9 @@ export const SidebarModuleSelect: React.FC<Props> = ({
{modules.map((option) => ( {modules.map((option) => (
<CustomSelect.Option key={option.id} value={option.id}> <CustomSelect.Option key={option.id} value={option.id}>
<Tooltip position="left-bottom" tooltipContent={option.name}> <Tooltip position="left-bottom" tooltipContent={option.name}>
<span className="w-full max-w-[125px] truncate">{truncateText(option.name, 15)}</span> <span className="w-full max-w-[125px] truncate">
{truncateText(option.name, 15)}
</span>
</Tooltip> </Tooltip>
</CustomSelect.Option> </CustomSelect.Option>
))} ))}

View File

@ -52,7 +52,7 @@ export const SidebarParentSelect: React.FC<Props> = ({
return ( return (
<div className="flex flex-wrap items-center py-2"> <div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2"> <div className="flex items-center gap-x-2 text-sm text-brand-secondary sm:basis-1/2">
<UserIcon className="h-4 w-4 flex-shrink-0" /> <UserIcon className="h-4 w-4 flex-shrink-0" />
<p>Parent</p> <p>Parent</p>
</div> </div>
@ -78,8 +78,8 @@ export const SidebarParentSelect: React.FC<Props> = ({
<button <button
type="button" type="button"
className={`flex w-full ${ className={`flex w-full ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100" isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-brand-surface-2"
} items-center justify-between gap-1 rounded-md border px-2 py-1 text-xs shadow-sm duration-300 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500`} } items-center justify-between gap-1 rounded-md border border-brand-base px-2 py-1 text-xs shadow-sm duration-300 focus:outline-none`}
onClick={() => setIsParentModalOpen(true)} onClick={() => setIsParentModalOpen(true)}
disabled={isNotAllowed} disabled={isNotAllowed}
> >

View File

@ -21,21 +21,21 @@ export const SidebarPrioritySelect: React.FC<Props> = ({ value, onChange, userAu
return ( return (
<div className="flex flex-wrap items-center py-2"> <div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2"> <div className="flex items-center gap-x-2 text-sm text-brand-secondary sm:basis-1/2">
<ChartBarIcon className="h-4 w-4 flex-shrink-0" /> <ChartBarIcon className="h-4 w-4 flex-shrink-0" />
<p>Priority</p> <p>Priority</p>
</div> </div>
<div className="sm:basis-1/2"> <div className="sm:basis-1/2">
<CustomSelect <CustomSelect
label={ label={
<span <div className="flex items-center gap-2 text-left capitalize">
className={`flex items-center gap-2 text-left capitalize ${ <span className={`${value ? "text-brand-base" : "text-brand-secondary"}`}>
value ? "" : "text-gray-900" {getPriorityIcon(value ?? "None", "text-sm")}
}`} </span>
> <span className={`${value ? "text-brand-base" : "text-brand-secondary"}`}>
{getPriorityIcon(value && value !== "" ? value ?? "" : "None", "text-sm")} {value ?? "None"}
{value && value !== "" ? value : "None"} </span>
</span> </div>
} }
value={value} value={value}
onChange={onChange} onChange={onChange}

View File

@ -43,14 +43,14 @@ export const SidebarStateSelect: React.FC<Props> = ({ value, onChange, userAuth
return ( return (
<div className="flex flex-wrap items-center py-2"> <div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2"> <div className="flex items-center gap-x-2 text-sm text-brand-secondary sm:basis-1/2">
<Squares2X2Icon className="h-4 w-4 flex-shrink-0" /> <Squares2X2Icon className="h-4 w-4 flex-shrink-0" />
<p>State</p> <p>State</p>
</div> </div>
<div className="sm:basis-1/2"> <div className="sm:basis-1/2">
<CustomSelect <CustomSelect
label={ label={
<div className={`flex items-center gap-2 text-left ${value ? "" : "text-gray-900"}`}> <div className="flex items-center gap-2 text-left text-brand-base">
{getStateGroupIcon( {getStateGroupIcon(
selectedState?.group ?? "backlog", selectedState?.group ?? "backlog",
"16", "16",

View File

@ -229,7 +229,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
isOpen={deleteIssueModal} isOpen={deleteIssueModal}
data={issueDetail ?? null} data={issueDetail ?? null}
/> />
<div className="sticky top-5 w-full divide-y-2 divide-gray-100"> <div className="sticky top-5 w-full divide-y-2 divide-brand-base">
<div className="flex items-center justify-between pb-3"> <div className="flex items-center justify-between pb-3">
<h4 className="text-sm font-medium"> <h4 className="text-sm font-medium">
{issueDetail?.project_detail?.identifier}-{issueDetail?.sequence_id} {issueDetail?.project_detail?.identifier}-{issueDetail?.sequence_id}
@ -237,7 +237,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<button <button
type="button" type="button"
className="rounded-md border p-2 shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" className="rounded-md border border-brand-base p-2 shadow-sm duration-300 hover:bg-brand-surface-1 focus:border-brand-accent focus:outline-none focus:ring-1 focus:ring-brand-accent"
onClick={handleCopyText} onClick={handleCopyText}
> >
<LinkIcon className="h-3.5 w-3.5" /> <LinkIcon className="h-3.5 w-3.5" />
@ -245,7 +245,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
{!isNotAllowed && ( {!isNotAllowed && (
<button <button
type="button" type="button"
className="rounded-md border border-red-500 p-2 text-red-500 shadow-sm duration-300 hover:bg-red-50 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" className="rounded-md border border-red-500 p-2 text-red-500 shadow-sm duration-300 hover:bg-red-500/20 focus:outline-none"
onClick={() => setDeleteIssueModal(true)} onClick={() => setDeleteIssueModal(true)}
> >
<TrashIcon className="h-3.5 w-3.5" /> <TrashIcon className="h-3.5 w-3.5" />
@ -253,7 +253,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
)} )}
</div> </div>
</div> </div>
<div className="divide-y-2 divide-gray-100"> <div className="divide-y-2 divide-brand-base">
<div className="py-1"> <div className="py-1">
<Controller <Controller
control={control} control={control}
@ -316,14 +316,14 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
issueDetail?.parent_detail ? ( issueDetail?.parent_detail ? (
<button <button
type="button" type="button"
className="flex items-center gap-2 rounded bg-gray-100 px-3 py-2 text-xs" className="flex items-center gap-2 rounded bg-brand-surface-1 px-3 py-2 text-xs"
onClick={() => submitChanges({ parent: null })} onClick={() => submitChanges({ parent: null })}
> >
{issueDetail.parent_detail?.name} {issueDetail.parent_detail?.name}
<XMarkIcon className="h-3 w-3" /> <XMarkIcon className="h-3 w-3" />
</button> </button>
) : ( ) : (
<div className="inline-block rounded bg-gray-100 px-3 py-2 text-xs"> <div className="inline-block rounded bg-brand-surface-1 px-3 py-2 text-xs">
No parent selected No parent selected
</div> </div>
) )
@ -344,7 +344,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
userAuth={memberRole} userAuth={memberRole}
/> />
<div className="flex flex-wrap items-center py-2"> <div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2"> <div className="flex items-center gap-x-2 text-sm text-brand-secondary sm:basis-1/2">
<CalendarDaysIcon className="h-4 w-4 flex-shrink-0" /> <CalendarDaysIcon className="h-4 w-4 flex-shrink-0" />
<p>Due date</p> <p>Due date</p>
</div> </div>
@ -382,7 +382,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
</div> </div>
<div className="space-y-3 py-3"> <div className="space-y-3 py-3">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div className="flex basis-1/2 items-center gap-x-2 text-sm"> <div className="flex basis-1/2 items-center gap-x-2 text-sm text-brand-secondary">
<TagIcon className="h-4 w-4" /> <TagIcon className="h-4 w-4" />
<p>Label</p> <p>Label</p>
</div> </div>
@ -395,7 +395,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
return ( return (
<span <span
key={label.id} key={label.id}
className="group flex cursor-pointer items-center gap-1 rounded-2xl border px-1 py-0.5 text-xs hover:border-red-500 hover:bg-red-50" className="group flex cursor-pointer items-center gap-1 rounded-2xl border border-brand-base px-1 py-0.5 text-xs hover:border-red-500/20 hover:bg-red-500/20"
onClick={() => { onClick={() => {
const updatedLabels = watchIssue("labels_list")?.filter( const updatedLabels = watchIssue("labels_list")?.filter(
(l) => l !== labelId (l) => l !== labelId
@ -435,8 +435,8 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
className={`flex ${ className={`flex ${
isNotAllowed isNotAllowed
? "cursor-not-allowed" ? "cursor-not-allowed"
: "cursor-pointer hover:bg-gray-100" : "cursor-pointer hover:bg-brand-surface-1"
} items-center gap-2 rounded-2xl border px-2 py-0.5 text-xs`} } items-center gap-2 rounded-2xl border border-brand-base px-2 py-0.5 text-xs text-brand-secondary`}
> >
Select Label Select Label
</Listbox.Button> </Listbox.Button>
@ -448,7 +448,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<Listbox.Options className="absolute right-0 z-10 mt-1 max-h-28 w-40 overflow-auto rounded-md bg-white py-1 text-xs shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"> <Listbox.Options className="absolute right-0 z-10 mt-1 max-h-28 w-40 overflow-auto rounded-md bg-brand-surface-2 py-1 text-xs shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="py-1"> <div className="py-1">
{issueLabels ? ( {issueLabels ? (
issueLabels.length > 0 ? ( issueLabels.length > 0 ? (
@ -463,9 +463,11 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
<Listbox.Option <Listbox.Option
key={label.id} key={label.id}
className={({ active, selected }) => className={({ active, selected }) =>
`${active || selected ? "bg-indigo-50" : ""} ${ `${
active || selected ? "bg-brand-surface-1" : ""
} ${
selected ? "font-medium" : "" selected ? "font-medium" : ""
} flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900` } flex cursor-pointer select-none items-center gap-2 truncate p-2 text-brand-base`
} }
value={label.id} value={label.id}
> >
@ -483,8 +485,8 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
); );
} else } else
return ( return (
<div className="border-y border-gray-400 bg-gray-50"> <div className="border-y border-brand-base bg-brand-surface-1">
<div className="flex select-none items-center gap-2 truncate p-2 font-medium text-gray-900"> <div className="flex select-none items-center gap-2 truncate p-2 font-medium text-brand-base">
<RectangleGroupIcon className="h-3 w-3" />{" "} <RectangleGroupIcon className="h-3 w-3" />{" "}
{label.name} {label.name}
</div> </div>
@ -495,7 +497,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
className={({ active, selected }) => className={({ active, selected }) =>
`${active || selected ? "bg-indigo-50" : ""} ${ `${active || selected ? "bg-indigo-50" : ""} ${
selected ? "font-medium" : "" selected ? "font-medium" : ""
} flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900` } flex cursor-pointer select-none items-center gap-2 truncate p-2 text-brand-base`
} }
value={child.id} value={child.id}
> >
@ -530,8 +532,10 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
<button <button
type="button" type="button"
className={`flex ${ className={`flex ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100" isNotAllowed
} items-center gap-1 rounded-2xl border px-2 py-0.5 text-xs`} ? "cursor-not-allowed"
: "cursor-pointer hover:bg-brand-surface-1"
} items-center gap-1 rounded-2xl border border-brand-base px-2 py-0.5 text-xs text-brand-secondary`}
onClick={() => setCreateLabelForm((prevData) => !prevData)} onClick={() => setCreateLabelForm((prevData) => !prevData)}
> >
{createLabelForm ? ( {createLabelForm ? (
@ -555,7 +559,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
{({ open }) => ( {({ open }) => (
<> <>
<Popover.Button <Popover.Button
className={`flex items-center gap-1 rounded-md bg-white p-1 outline-none focus:ring-2 focus:ring-indigo-500`} className={`flex items-center gap-1 rounded-md bg-brand-surface-2 p-1 outline-none focus:ring-2 focus:ring-brand-accent`}
> >
{watch("color") && watch("color") !== "" && ( {watch("color") && watch("color") !== "" && (
<span <span
@ -627,7 +631,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
{!isNotAllowed && ( {!isNotAllowed && (
<button <button
type="button" type="button"
className="grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-100" className="grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-brand-surface-1"
onClick={() => setLinkModal(true)} onClick={() => setLinkModal(true)}
> >
<PlusIcon className="h-4 w-4" /> <PlusIcon className="h-4 w-4" />

View File

@ -173,7 +173,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue }) => {
{({ open }) => ( {({ open }) => (
<> <>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Disclosure.Button className="flex items-center gap-1 rounded px-2 py-1 text-xs font-medium hover:bg-gray-100"> <Disclosure.Button className="flex items-center gap-1 rounded px-2 py-1 text-xs font-medium hover:bg-brand-surface-1">
<ChevronRightIcon className={`h-3 w-3 ${open ? "rotate-90" : ""}`} /> <ChevronRightIcon className={`h-3 w-3 ${open ? "rotate-90" : ""}`} />
Sub-issues <span className="ml-1 text-gray-600">{subIssues.length}</span> Sub-issues <span className="ml-1 text-gray-600">{subIssues.length}</span>
</Disclosure.Button> </Disclosure.Button>
@ -181,7 +181,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue }) => {
<div className="flex items-center"> <div className="flex items-center">
<button <button
type="button" type="button"
className="flex items-center gap-1 rounded px-2 py-1 text-xs font-medium hover:bg-gray-100" className="flex items-center gap-1 rounded px-2 py-1 text-xs font-medium hover:bg-brand-surface-1"
onClick={handleCreateIssueModal} onClick={handleCreateIssueModal}
> >
<PlusIcon className="h-3 w-3" /> <PlusIcon className="h-3 w-3" />
@ -210,7 +210,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue }) => {
key={issue.id} key={issue.id}
href={`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`} href={`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`}
> >
<a className="group flex items-center justify-between gap-2 rounded p-2 hover:bg-gray-100"> <a className="group flex items-center justify-between gap-2 rounded p-2 hover:bg-brand-surface-1">
<div className="flex items-center gap-2 rounded text-xs"> <div className="flex items-center gap-2 rounded text-xs">
<span <span
className="block h-1.5 w-1.5 flex-shrink-0 rounded-full" className="block h-1.5 w-1.5 flex-shrink-0 rounded-full"
@ -234,7 +234,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue }) => {
handleSubIssueRemove(issue.id); handleSubIssueRemove(issue.id);
}} }}
> >
<XMarkIcon className="h-4 w-4 text-gray-500 hover:text-gray-900" /> <XMarkIcon className="h-4 w-4 text-brand-secondary hover:text-brand-base" />
</button> </button>
)} )}
</a> </a>

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