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
# Cache
REDIS_URL=redis://redis:6379/
# SMPT
# SMTP
EMAIL_HOST=""
EMAIL_HOST_USER=""
EMAIL_HOST_PASSWORD=""
# AWS
EMAIL_PORT="587"
EMAIL_USE_TLS="1"
EMAIL_FROM="Team Plane <team@mailer.plane.so>"
# AWS
AWS_REGION=""
AWS_ACCESS_KEY_ID=""
AWS_SECRET_ACCESS_KEY=""
AWS_S3_BUCKET_NAME=""
AWS_S3_ENDPOINT_URL=""
# FE
WEB_URL="localhost/"
# OAUTH
@ -21,4 +25,4 @@ DISABLE_COLLECTSTATIC=1
DOCKERIZED=1
# GPT Envs
OPENAI_API_KEY=0
GPT_ENGINE=0
GPT_ENGINE=0

View File

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

View File

@ -146,11 +146,13 @@ class BulkEstimatePointEndpoint(BaseAPIView):
ProjectEntityPermission,
]
def post(self, request, slug, project_id, estimate_id):
def post(self, request, slug, project_id):
try:
estimate = Estimate.objects.get(
pk=estimate_id, workspace__slug=slug, project=project_id
)
if not request.data.get("estimate", False):
return Response(
{"error": "Estimate is required"},
status=status.HTTP_400_BAD_REQUEST,
)
estimate_points = request.data.get("estimate_points", [])
@ -160,6 +162,18 @@ class BulkEstimatePointEndpoint(BaseAPIView):
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(
[
EstimatePoint(
@ -178,9 +192,17 @@ class BulkEstimatePointEndpoint(BaseAPIView):
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:
return Response(
{"error": "Estimate does not exist"},
@ -212,7 +234,6 @@ class BulkEstimatePointEndpoint(BaseAPIView):
estimate_id=estimate_id,
)
print(estimate_points)
updated_estimate_points = []
for estimate_point in estimate_points:
# 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
)
except Exception as e:
print(e)
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,

View File

@ -1,13 +1,14 @@
# Python imports
import json
import random
from itertools import groupby, chain
from itertools import chain
# 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.utils.decorators import method_decorator
from django.views.decorators.gzip import gzip_page
from django.db.models.functions import Coalesce
# Third Party imports
from rest_framework.response import Response
@ -46,6 +47,7 @@ from plane.db.models import (
Label,
IssueLink,
IssueAttachment,
State,
)
from plane.bgtasks.issue_activites_task import issue_activity
from plane.utils.grouper import group_results
@ -590,10 +592,33 @@ class SubIssuesEndpoint(BaseAPIView):
.prefetch_related("labels")
)
serializer = IssueLiteSerializer(sub_issues, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
state_distribution = (
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:
capture_exception(e)
print(e)
return Response(
{"error": "Something went wrong please try again later"},
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
from . import BaseViewSet
from . import BaseViewSet, BaseAPIView
from plane.api.serializers import StateSerializer
from plane.api.permissions import ProjectEntityPermission
from plane.db.models import State
from plane.db.models import State, Issue
class StateViewSet(BaseViewSet):
@ -53,7 +53,10 @@ class StateViewSet(BaseViewSet):
)
except Exception as 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):
try:
@ -85,7 +88,37 @@ class StateViewSet(BaseViewSet):
{"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()
return Response(status=status.HTTP_204_NO_CONTENT)
except State.DoesNotExist:
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.template.loader import render_to_string
from django.utils.html import strip_tags
from django.conf import settings
# Third party imports
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)
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!"

View File

@ -2,6 +2,7 @@
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.conf import settings
# Third party imports
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}/"
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!"

View File

@ -2,6 +2,7 @@
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.conf import settings
# Third party imports
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}"
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"

View File

@ -2,6 +2,7 @@
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.conf import settings
# Third party imports
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}"
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"

View File

@ -27,7 +27,7 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor):
)
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"

View File

@ -109,7 +109,7 @@ def send_welcome_email(sender, instance, created, **kwargs):
if created and not instance.is_bot:
first_name = instance.first_name.capitalize()
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 ✈️!"

View File

@ -174,11 +174,12 @@ EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
# Host for sending e-mail.
EMAIL_HOST = os.environ.get("EMAIL_HOST")
# Port for sending e-mail.
EMAIL_PORT = 587
EMAIL_PORT = int(os.environ.get("EMAIL_PORT", 587))
# Optional SMTP authentication information for EMAIL_HOST.
EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER")
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 = {
@ -210,4 +211,4 @@ SIMPLE_JWT = {
CELERY_TIMEZONE = TIME_ZONE
CELERY_TASK_SERIALIZER = 'json'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_ACCEPT_CONTENT = ['application/json']

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_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"
# 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.
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)
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:
CELERY_BROKER_URL = REDIS_URL
@ -248,3 +250,5 @@ if DOCKERIZED:
else:
CELERY_RESULT_BACKEND = broker_url
CELERY_BROKER_URL = broker_url
GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", False)

View File

@ -203,4 +203,6 @@ redis_url = os.environ.get("REDIS_URL")
broker_url = f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={certifi.where()}"
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 cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.backends import default_backend
from django.conf import settings
def get_jwt_token():
@ -128,3 +129,24 @@ def get_github_repo_details(access_tokens_url, owner, repo):
).json()
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
# NEXT_PUBLIC_API_BASE_URL = "http://localhost"
NEXT_PUBLIC_EXTRA_IMAGE_DOMAINS=
NEXT_PUBLIC_GOOGLE_CLIENTID=""
NEXT_PUBLIC_GITHUB_APP_NAME=""
NEXT_PUBLIC_GITHUB_ID=""
@ -8,4 +9,4 @@ NEXT_PUBLIC_ENABLE_OAUTH=0
NEXT_PUBLIC_ENABLE_SENTRY=0
NEXT_PUBLIC_ENABLE_SESSION_RECORDER=0
NEXT_PUBLIC_TRACK_EVENTS=0
NEXT_PUBLIC_SLACK_CLIENT_ID=""
NEXT_PUBLIC_SLACK_CLIENT_ID=""

View File

@ -141,7 +141,7 @@ export const EmailCodeForm = ({ onSuccess }: any) => {
<button
type="button"
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={() => {
setIsCodeResending(true);

View File

@ -94,7 +94,7 @@ export const EmailPasswordForm = ({ onSuccess }: any) => {
<div className="mt-2 flex items-center justify-between">
<div className="ml-auto text-sm">
<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>
</div>
</div>

View File

@ -37,7 +37,7 @@ export const GithubLoginButton: FC<GithubLoginButtonProps> = (props) => {
<Link
href={`https://github.com/login/oauth/authorize?client_id=${NEXT_PUBLIC_GITHUB_ID}&redirect_uri=${loginCallBackURL}&scope=read:user,user:email`}
>
<button className="flex w-full items-center justify-center gap-3 rounded-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" />
<span>Sign In with Github</span>
</button>

View File

@ -36,16 +36,16 @@ export const NotAuthorizedView: React.FC<Props> = ({ actionButton, type }) => {
alt="ProjectSettingImg"
/>
</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
</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 ? (
<p>
You have signed in as {user.email}. <br />
<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>{" "}
with different account that has access to this page.
</p>
@ -53,7 +53,7 @@ export const NotAuthorizedView: React.FC<Props> = ({ actionButton, type }) => {
<p>
You need to{" "}
<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>{" "}
with an account that has access to this page.
</p>

View File

@ -16,7 +16,7 @@ const Breadcrumbs = ({ children }: BreadcrumbsProps) => {
<div className="flex items-center">
<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()}
>
<ArrowLeftIcon className="h-3 w-3" />
@ -37,7 +37,7 @@ const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({ title, link, icon }) =>
<>
{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" : ""}`}>
{icon ?? null}
{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,
ChangeIssuePriority,
ChangeIssueAssignee,
ChangeInterfaceTheme,
} from "components/command-palette";
import { BulkDeleteIssuesModal } from "components/core";
import { CreateUpdateCycleModal } from "components/cycles";
@ -379,7 +380,7 @@ export const CommandPalette: React.FC = () => {
leaveFrom="opacity-100"
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>
<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"
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
filter={(value, search) => {
if (value.toLowerCase().includes(search.toLowerCase())) return 1;
@ -415,7 +416,7 @@ export const CommandPalette: React.FC = () => {
>
{issueId && issueDetails && (
<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?.name}
</p>
@ -423,11 +424,11 @@ export const CommandPalette: React.FC = () => {
)}
<div className="relative">
<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"
/>
<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}
value={searchTerm}
onValueChange={(e) => {
@ -441,7 +442,9 @@ export const CommandPalette: React.FC = () => {
resultsCount === 0 &&
searchTerm !== "" &&
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) && (
@ -502,8 +505,11 @@ export const CommandPalette: React.FC = () => {
value={value}
className="focus:outline-none"
>
<div className="flex items-center gap-2 overflow-hidden text-gray-700">
<Icon className="h-4 w-4 text-gray-500" color="#6b7280" />
<div className="flex items-center gap-2 overflow-hidden text-brand-secondary">
<Icon
className="h-4 w-4 text-brand-secondary"
color="#6b7280"
/>
<p className="block flex-1 truncate">{item.name}</p>
</div>
</Command.Item>
@ -528,8 +534,8 @@ export const CommandPalette: React.FC = () => {
}}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<Squares2X2Icon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<Squares2X2Icon className="h-4 w-4 text-brand-secondary" />
Change state...
</div>
</Command.Item>
@ -541,8 +547,8 @@ export const CommandPalette: React.FC = () => {
}}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<ChartBarIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<ChartBarIcon className="h-4 w-4 text-brand-secondary" />
Change priority...
</div>
</Command.Item>
@ -554,8 +560,8 @@ export const CommandPalette: React.FC = () => {
}}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<UsersIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<UsersIcon className="h-4 w-4 text-brand-secondary" />
Assign to...
</div>
</Command.Item>
@ -566,15 +572,15 @@ export const CommandPalette: React.FC = () => {
}}
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) ? (
<>
<UserMinusIcon className="h-4 w-4 text-gray-500" />
<UserMinusIcon className="h-4 w-4 text-brand-secondary" />
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
</>
)}
@ -582,8 +588,8 @@ export const CommandPalette: React.FC = () => {
</Command.Item>
<Command.Item onSelect={deleteIssue} className="focus:outline-none">
<div className="flex items-center gap-2 text-gray-700">
<TrashIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<TrashIcon className="h-4 w-4 text-brand-secondary" />
Delete issue
</div>
</Command.Item>
@ -594,16 +600,19 @@ export const CommandPalette: React.FC = () => {
}}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<LinkIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<LinkIcon className="h-4 w-4 text-brand-secondary" />
Copy issue URL to clipboard
</div>
</Command.Item>
</>
)}
<Command.Group heading="Issue">
<Command.Item onSelect={createNewIssue} className="focus:bg-gray-200">
<div className="flex items-center gap-2 text-gray-700">
<Command.Item
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" />
Create new issue
</div>
@ -617,7 +626,7 @@ export const CommandPalette: React.FC = () => {
onSelect={createNewProject}
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" />
Create new project
</div>
@ -633,7 +642,7 @@ export const CommandPalette: React.FC = () => {
onSelect={createNewCycle}
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" />
Create new cycle
</div>
@ -646,7 +655,7 @@ export const CommandPalette: React.FC = () => {
onSelect={createNewModule}
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" />
Create new module
</div>
@ -656,7 +665,7 @@ export const CommandPalette: React.FC = () => {
<Command.Group heading="View">
<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" />
Create new view
</div>
@ -685,7 +694,7 @@ export const CommandPalette: React.FC = () => {
}}
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" />
Search settings...
</div>
@ -696,11 +705,24 @@ export const CommandPalette: React.FC = () => {
onSelect={createNewWorkspace}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<FolderPlusIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<FolderPlusIcon className="h-4 w-4 text-brand-secondary" />
Create new workspace
</div>
</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 heading="Help">
<Command.Item
@ -713,8 +735,8 @@ export const CommandPalette: React.FC = () => {
}}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<RocketLaunchIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<RocketLaunchIcon className="h-4 w-4 text-brand-secondary" />
Open keyboard shortcuts
</div>
</Command.Item>
@ -725,8 +747,8 @@ export const CommandPalette: React.FC = () => {
}}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<DocumentIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<DocumentIcon className="h-4 w-4 text-brand-secondary" />
Open Plane documentation
</div>
</Command.Item>
@ -737,7 +759,7 @@ export const CommandPalette: React.FC = () => {
}}
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" />
Join our Discord
</div>
@ -752,7 +774,7 @@ export const CommandPalette: React.FC = () => {
}}
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" />
Report a bug
</div>
@ -764,8 +786,8 @@ export const CommandPalette: React.FC = () => {
}}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<ChatBubbleOvalLeftEllipsisIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<ChatBubbleOvalLeftEllipsisIcon className="h-4 w-4 text-brand-secondary" />
Chat with us
</div>
</Command.Item>
@ -779,8 +801,8 @@ export const CommandPalette: React.FC = () => {
onSelect={() => goToSettings()}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<SettingIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-brand-secondary" />
General
</div>
</Command.Item>
@ -788,8 +810,8 @@ export const CommandPalette: React.FC = () => {
onSelect={() => goToSettings("members")}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<SettingIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-brand-secondary" />
Members
</div>
</Command.Item>
@ -797,8 +819,8 @@ export const CommandPalette: React.FC = () => {
onSelect={() => goToSettings("billing")}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<SettingIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-brand-secondary" />
Billings and Plans
</div>
</Command.Item>
@ -806,8 +828,8 @@ export const CommandPalette: React.FC = () => {
onSelect={() => goToSettings("integrations")}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<SettingIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-brand-secondary" />
Integrations
</div>
</Command.Item>
@ -815,8 +837,8 @@ export const CommandPalette: React.FC = () => {
onSelect={() => goToSettings("import-export")}
className="focus:outline-none"
>
<div className="flex items-center gap-2 text-gray-700">
<SettingIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 text-brand-secondary">
<SettingIcon className="h-4 w-4 text-brand-secondary" />
Import/Export
</div>
</Command.Item>
@ -842,6 +864,9 @@ export const CommandPalette: React.FC = () => {
setIsPaletteOpen={setIsPaletteOpen}
/>
)}
{page === "change-interface-theme" && (
<ChangeInterfaceTheme setIsPaletteOpen={setIsPaletteOpen} />
)}
</Command.List>
</Command>
</Dialog.Panel>

View File

@ -3,3 +3,4 @@ export * from "./shortcuts-modal";
export * from "./change-issue-state";
export * from "./change-issue-priority";
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"
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>
<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"
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">
<div className="bg-white p-5">
<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-brand-surface-2 p-5">
<div className="sm:flex sm:items-start">
<div className="flex w-full flex-col gap-y-4 text-center sm:text-left">
<Dialog.Title
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>
<button type="button" onClick={() => setIsOpen(false)}>
<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"
/>
</button>
</span>
</Dialog.Title>
<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">
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-gray-500" />
<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-brand-secondary" />
<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"
name="search"
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 className="flex flex-col gap-y-3">
<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">
{shortcut.keys.split(",").map((key, index) => (
<span key={index} className="flex items-center gap-1">
{key === "Ctrl" ? (
<span className="flex h-full items-center rounded-sm border border-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 />
</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}
</kbd>
)}
@ -145,7 +145,7 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
))
) : (
<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{" "}
<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">
{shortcuts.map(({ keys, description }, index) => (
<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">
{keys.split(",").map((key, index) => (
<span key={index} className="flex items-center gap-1">
{key === "Ctrl" ? (
<span className="flex h-full items-center rounded-sm border border-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 />
</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}
</kbd>
)}

View File

@ -81,7 +81,7 @@ export const AllBoards: React.FC<Props> = ({
return (
<div
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">
{currentState &&
@ -92,7 +92,7 @@ export const AllBoards: React.FC<Props> = ({
: addSpaceIfCamelCase(singleGroup)}
</h4>
</div>
<span className="text-xs text-gray-500">0</span>
<span className="text-xs text-brand-secondary">0</span>
</div>
);
})}

View File

@ -123,8 +123,8 @@ export const BoardHeader: React.FC<Props> = ({
return (
<div
className={`flex justify-between items-center px-1 ${
!isCollapsed ? "flex-col rounded-md border bg-gray-50" : ""
className={`flex items-center justify-between px-1 ${
!isCollapsed ? "flex-col rounded-md bg-brand-surface-1" : ""
}`}
>
<div className={`flex items-center ${!isCollapsed ? "flex-col gap-2" : "gap-1"}`}>
@ -145,7 +145,7 @@ export const BoardHeader: React.FC<Props> = ({
<span
className={`${
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}
</span>
@ -155,7 +155,7 @@ export const BoardHeader: React.FC<Props> = ({
<div className={`flex items-center ${!isCollapsed ? "flex-col pb-2" : ""}`}>
<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={() => {
setIsCollapsed((prevData) => !prevData);
}}
@ -169,7 +169,7 @@ export const BoardHeader: React.FC<Props> = ({
{!isCompleted && (
<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}
>
<PlusIcon className="h-4 w-4" />

View File

@ -66,7 +66,7 @@ export const SingleBoard: React.FC<Props> = ({
}, [currentState]);
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"}`}>
<BoardHeader
addIssueToState={addIssueToState}
@ -81,7 +81,7 @@ export const SingleBoard: React.FC<Props> = ({
{(provided, snapshot) => (
<div
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"}`}
ref={provided.innerRef}
{...provided.droppableProps}
@ -91,15 +91,17 @@ export const SingleBoard: React.FC<Props> = ({
<div
className={`absolute ${
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
className={`absolute ${
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{" "}
{replaceUnderscoreIfSnakeCase(orderBy ?? "created_at")}
{replaceUnderscoreIfSnakeCase(
orderBy ? (orderBy[0] === "-" ? orderBy.slice(1) : orderBy) : "created_at"
)}
</div>
</>
)}
@ -146,7 +148,7 @@ export const SingleBoard: React.FC<Props> = ({
{type === "issue" ? (
<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}
>
<PlusIcon className="h-4 w-4" />
@ -158,7 +160,7 @@ export const SingleBoard: React.FC<Props> = ({
customButton={
<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" />
Add Issue

View File

@ -261,8 +261,8 @@ export const SingleBoardIssue: React.FC<Props> = ({
</a>
</ContextMenu>
<div
className={`mb-3 rounded bg-white shadow ${
snapshot.isDragging ? "border-2 border-theme shadow-lg" : ""
className={`mb-3 rounded bg-brand-base shadow ${
snapshot.isDragging ? "border-2 border-brand-accent shadow-lg" : ""
}`}
ref={provided.innerRef}
{...provided.draggableProps}
@ -312,12 +312,12 @@ export const SingleBoardIssue: React.FC<Props> = ({
<Link href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}>
<a>
{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}
</div>
)}
<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 }}
>
{truncateText(issue.name, 100)}
@ -349,7 +349,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
/>
)}
{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"}
</div>
)}
@ -358,7 +358,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
{issue.label_details.map((label) => (
<div
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
className="h-1.5 w-1.5 flex-shrink-0 rounded-full"
@ -389,7 +389,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
/>
)}
{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}`}>
<div className="flex items-center gap-1 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>
)}
{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}`}>
<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}
</div>
</Tooltip>

View File

@ -121,7 +121,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
leaveFrom="opacity-100 scale-100"
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>
<Combobox
onChange={(val: string) => {
@ -136,12 +136,12 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
>
<div className="relative m-1">
<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"
/>
<input
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..."
onChange={(event) => setQuery(event.target.value)}
/>
@ -154,7 +154,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
{filteredIssues.length > 0 ? (
<li className="p-2">
{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
</h2>
)}
@ -166,7 +166,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
value={issue.id}
className={({ active }) =>
`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,
}}
/>
<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}
</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">
<LayerDiagonalIcon height="56" width="56" />
<h3 className="text-gray-500">
<h3 className="text-brand-secondary">
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>
</div>
)}

View File

@ -26,14 +26,17 @@ import {
import { Popover, Transition } from "@headlessui/react";
import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd";
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
import { CustomMenu } from "components/ui";
import { CustomMenu, Spinner } from "components/ui";
// icon
import {
CheckIcon,
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
PlusIcon,
} from "@heroicons/react/24/outline";
// hooks
import useIssuesView from "hooks/use-issues-view";
// services
import issuesService from "services/issues.service";
import cyclesService from "services/cycles.service";
@ -49,12 +52,16 @@ import { IIssue } from "types";
import { monthOptions, yearOptions } from "constants/calendar";
import modulesService from "services/modules.service";
type Props = {
addIssueToDate: (date: string) => void;
};
interface ICalendarRange {
startDate: Date;
endDate: Date;
}
export const CalendarView = () => {
export const CalendarView: React.FC<Props> = ({ addIssueToDate }) => {
const [showWeekEnds, setShowWeekEnds] = useState<boolean>(false);
const [currentDate, setCurrentDate] = useState<Date>(new Date());
const [isMonthlyView, setIsMonthlyView] = useState<boolean>(true);
@ -62,6 +69,8 @@ export const CalendarView = () => {
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
const { params } = useIssuesView();
const [calendarDateRange, setCalendarDateRange] = useState<ICalendarRange>({
startDate: startOfWeek(currentDate),
endDate: lastDayOfWeek(currentDate),
@ -77,11 +86,13 @@ export const CalendarView = () => {
workspaceSlug && projectId ? PROJECT_CALENDAR_ISSUES(projectId as string) : null,
workspaceSlug && projectId
? () =>
issuesService.getIssuesWithParams(
workspaceSlug as string,
projectId as string,
targetDateFilter
)
issuesService.getIssuesWithParams(workspaceSlug as string, projectId as string, {
...params,
target_date: `${renderDateFormat(calendarDateRange.startDate)};after,${renderDateFormat(
calendarDateRange.endDate
)};before`,
group_by: null,
})
: null
);
@ -95,7 +106,13 @@ export const CalendarView = () => {
workspaceSlug as string,
projectId as string,
cycleId as string,
targetDateFilter
{
...params,
target_date: `${renderDateFormat(
calendarDateRange.startDate
)};after,${renderDateFormat(calendarDateRange.endDate)};before`,
group_by: null,
}
)
: null
);
@ -110,7 +127,13 @@ export const CalendarView = () => {
workspaceSlug as string,
projectId as string,
moduleId as string,
targetDateFilter
{
...params,
target_date: `${renderDateFormat(
calendarDateRange.startDate
)};after,${renderDateFormat(calendarDateRange.endDate)};before`,
group_by: null,
}
)
: null
);
@ -127,7 +150,11 @@ export const CalendarView = () => {
const currentViewDays = showWeekEnds ? totalDate : onlyWeekDays;
const calendarIssues = cycleCalendarIssues ?? moduleCalendarIssues ?? projectCalendarIssues;
const calendarIssues = cycleId
? cycleCalendarIssues
: moduleId
? moduleCalendarIssues
: projectCalendarIssues;
const currentViewDaysData = currentViewDays.map((date: Date) => {
const filterIssue =
@ -198,17 +225,17 @@ export const CalendarView = () => {
});
};
return (
return calendarIssues ? (
<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="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">
{({ 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">
<span className="text-black">{formatDate(currentDate, "Month")}</span>{" "}
<span>{formatDate(currentDate, "Month")}</span>{" "}
<span>{formatDate(currentDate, "yyyy")}</span>
</div>
</Popover.Button>
@ -222,30 +249,30 @@ export const CalendarView = () => {
leaveFrom="opacity-100 translate-y-0"
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]">
<div className="flex justify-center items-center text-sm gap-5 px-2 py-2">
<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 items-center justify-center gap-5 px-2 py-2 text-sm">
{yearOptions.map((year) => (
<button
onClick={() => updateDate(updateDateWithYear(year.label, currentDate))}
className={` ${
isSameYear(year.value, currentDate)
? "text-sm font-medium text-gray-800"
: "text-xs text-gray-400 "
} hover:text-sm hover:text-gray-800 hover:font-medium `}
? "text-sm font-medium text-brand-base"
: "text-xs text-brand-secondary "
} hover:text-sm hover:font-medium hover:text-brand-base`}
>
{year.label}
</button>
))}
</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) => (
<button
onClick={() =>
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)
? "font-medium text-gray-800"
? "font-medium text-brand-base"
: ""
}`}
>
@ -295,9 +322,9 @@ export const CalendarView = () => {
</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
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={() => {
if (isMonthlyView) {
updateDate(new Date());
@ -312,10 +339,11 @@ export const CalendarView = () => {
>
Today{" "}
</button>
<CustomMenu
customButton={
<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"}
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
@ -330,7 +358,7 @@ export const CalendarView = () => {
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">
<span className="flex items-center gap-2">Monthly View</span>
@ -349,7 +377,7 @@ export const CalendarView = () => {
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">
<span className="flex items-center gap-2">Weekly View</span>
@ -360,12 +388,12 @@ export const CalendarView = () => {
/>
</div>
</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>
<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 ${
showWeekEnds ? "bg-green-500" : "bg-gray-200"
showWeekEnds ? "bg-green-500" : "bg-brand-surface-2"
}`}
role="switch"
aria-checked={showWeekEnds}
@ -374,7 +402,7 @@ export const CalendarView = () => {
<span className="sr-only">Show weekends</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 ${
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"
}`}
/>
@ -392,7 +420,7 @@ export const CalendarView = () => {
{weeks.map((date, index) => (
<div
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
? showWeekEnds
? (index + 1) % 7 === 0
@ -424,7 +452,7 @@ export const CalendarView = () => {
key={index}
ref={provided.innerRef}
{...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
? (index + 1) % 7 === 0
? ""
@ -444,7 +472,7 @@ export const CalendarView = () => {
ref={provided.innerRef}
{...provided.draggableProps}
{...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" : ""
}`}
>
@ -458,6 +486,15 @@ export const CalendarView = () => {
)}
</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}
</div>
)}
@ -466,5 +503,9 @@ export const CalendarView = () => {
</div>
</div>
</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"
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>
<Controller
control={control}
@ -139,11 +139,11 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
<Combobox as="div" {...field} multiple>
<div className="relative m-1">
<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"
/>
<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..."
onChange={(e) => setQuery(e.target.value)}
/>
@ -156,7 +156,7 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
{filteredIssues.length > 0 ? (
<li className="p-2">
{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
</h2>
)}
@ -169,7 +169,7 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
value={issue.id}
className={({ active }) =>
`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,
}}
/>
<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}
</span>
{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">
<LayerDiagonalIcon height="56" width="56" />
<h3 className="text-gray-500">
<h3 className="text-brand-secondary">
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>
</div>
)}

View File

@ -55,23 +55,23 @@ const activityDetails: {
},
modules: {
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: {
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: {
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: {
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: {
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: {
message: "set the estimate point to",
@ -79,15 +79,15 @@ const activityDetails: {
},
target_date: {
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: {
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: {
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: {
message: "updated the estimate",
@ -217,7 +217,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
</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"
@ -230,7 +230,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
{activity.actor_detail.first_name}
{activity.actor_detail.is_bot ? "Bot" : " " + activity.actor_detail.last_name}
</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)}
</p>
</div>
@ -244,7 +244,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
editable={false}
onBlur={() => ({})}
noBorder
customClassName="text-xs bg-gray-100"
customClassName="text-xs bg-brand-surface-1"
/>
</div>
</div>
@ -259,7 +259,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
<div className="relative pb-1">
{activities.length > 1 && activityIdx !== activities.length - 1 ? (
<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"
/>
) : null}
@ -268,7 +268,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
<div>
<div className="relative px-1.5">
<div className="mt-1.5">
<div className="ring-6 flex h-7 w-7 items-center justify-center rounded-full bg-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 ? (
activityDetails[activity.field as keyof typeof activityDetails]?.icon
) : activity.actor_detail.avatar &&
@ -292,7 +292,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
</div>
</div>
<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">
{activity.actor_detail.first_name}
{activity.actor_detail.is_bot
@ -300,7 +300,7 @@ export const Feeds: React.FC<any> = ({ activities }) => (
: " " + activity.actor_detail.last_name}
</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>
</div>
</div>

View File

@ -57,9 +57,9 @@ export const FilterList: React.FC<any> = ({ filters, setFilters }) => {
return (
<div
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)}:
</span>
{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"
: priority === "low"
? "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>
@ -339,7 +339,7 @@ export const FilterList: React.FC<any> = ({ filters, setFilters }) => {
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>
<XMarkIcon className="h-4 w-4" />

View File

@ -121,7 +121,7 @@ export const GptAssistantModal: React.FC<Props> = ({
return (
<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"
}`}
>

View File

@ -65,7 +65,7 @@ export const ImagePickerPopover: React.FC<Props> = ({ label, value, onChange })
return (
<Popover className="relative z-[2]" ref={ref}>
<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)}
>
{label}
@ -79,16 +79,16 @@ export const ImagePickerPopover: React.FC<Props> = ({ label, value, onChange })
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Popover.Panel className="absolute right-0 z-10 mt-2 rounded-md bg-white 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]">
<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 border-brand-base bg-brand-surface-2 p-5 shadow-2xl sm:max-w-2xl md:w-96 lg:w-[40rem]">
<Tab.Group>
<Tab.List as="span" className="inline-block rounded bg-gray-200 p-1">
<Tab.List as="span" className="inline-block rounded bg-brand-surface-2 p-1">
{tabOptions.map((tab) => (
<Tab
key={tab.key}
className={({ selected }) =>
`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"
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>
<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"
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">
<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
</Dialog.Title>
<div className="space-y-3">
@ -135,7 +135,7 @@ export const ImageUploadModal: React.FC<Props> = ({
{...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 ${
(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
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
</button>
@ -157,7 +157,7 @@ export const ImageUploadModal: React.FC<Props> = ({
) : (
<>
<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
? "Drop image here to upload"
: "Drag & drop image here"}

View File

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

View File

@ -12,8 +12,12 @@ import { SelectFilters } from "components/views";
// ui
import { CustomMenu } from "components/ui";
// icons
import { ChevronDownIcon, ListBulletIcon, CalendarDaysIcon } from "@heroicons/react/24/outline";
import { Squares2X2Icon } from "@heroicons/react/20/solid";
import {
ChevronDownIcon,
ListBulletIcon,
Squares2X2Icon,
CalendarDaysIcon,
} from "@heroicons/react/24/outline";
// helpers
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
// types
@ -53,30 +57,30 @@ export const IssuesFilterView: React.FC = () => {
<div className="flex items-center gap-x-1">
<button
type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200 ${
issueView === "list" ? "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-brand-surface-2" : ""
}`}
onClick={() => setIssueView("list")}
>
<ListBulletIcon className="h-4 w-4" />
<ListBulletIcon className="h-4 w-4 text-brand-secondary" />
</button>
<button
type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200 ${
issueView === "kanban" ? "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-brand-surface-2" : ""
}`}
onClick={() => setIssueView("kanban")}
>
<Squares2X2Icon className="h-4 w-4" />
<Squares2X2Icon className="h-4 w-4 text-brand-secondary" />
</button>
<button
type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200 ${
issueView === "calendar" ? "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-brand-surface-2" : ""
}`}
onClick={() => setIssueView("calendar")}
>
<CalendarDaysIcon className="h-4 w-4" />
<CalendarDaysIcon className="h-4 w-4 text-brand-secondary" />
</button>
</div>
<SelectFilters
@ -113,8 +117,8 @@ export const IssuesFilterView: React.FC = () => {
{({ open }) => (
<>
<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 ${
open ? "bg-gray-100 text-gray-900" : "text-gray-500"
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-brand-surface-1 text-brand-base" : "text-brand-secondary"
}`}
>
View
@ -130,55 +134,59 @@ export const IssuesFilterView: React.FC = () => {
leaveFrom="opacity-100 translate-y-0"
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">
<div className="relative divide-y-2">
<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 divide-brand-base">
<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">
<h4 className="text-gray-600">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-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>
<h4 className="text-brand-secondary">Issue type</h4>
<CustomMenu
label={
FILTER_ISSUE_OPTIONS.find((option) => option.key === filters.type)
@ -200,62 +208,69 @@ export const IssuesFilterView: React.FC = () => {
))}
</CustomMenu>
</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
key={key}
type="button"
className={`rounded border px-2 py-1 text-xs capitalize ${
properties[key as keyof Properties]
? "border-theme bg-theme text-white"
: "border-gray-300"
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-brand-surface-2"
}`}
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>
);
})}
</div>
</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-brand-accent"
onClick={() => setNewFilterDefaultView()}
>
Set as default
</button>
</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>
</Popover.Panel>
</Transition>

View File

@ -280,6 +280,17 @@ export const IssuesView: React.FC<Props> = ({
[setCreateIssueModal, setPreloadedData, selectedGroup]
);
const addIssueToDate = useCallback(
(date: string) => {
setCreateIssueModal(true);
setPreloadedData({
target_date: date,
actionType: "createIssue",
});
},
[setCreateIssueModal, setPreloadedData]
);
const makeIssueCopy = useCallback(
(issue: IIssue) => {
setCreateIssueModal(true);
@ -385,49 +396,48 @@ export const IssuesView: React.FC<Props> = ({
handleClose={() => setTransferIssuesModal(false)}
isOpen={transferIssuesModal}
/>
{issueView !== "calendar" && (
<>
<div
className={`flex items-center justify-between gap-2 ${
issueView === "list" && areFiltersApplied ? "px-8 mt-6" : "-mt-2"
}`}
>
<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>
<>
<div
className={`flex items-center justify-between gap-2 ${
issueView === "list" ? (areFiltersApplied ? "mt-6 px-8" : "") : "-mt-2"
}`}
>
<FilterList filters={filters} setFilters={setFilters} />
{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}>
<StrictModeDroppable droppableId="trashBox">
{(provided, snapshot) => (
<div
className={`${
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 ${
snapshot.isDraggingOver ? "bg-red-500 text-white" : ""
} 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/100 text-white" : ""
} duration-200`}
ref={provided.innerRef}
{...provided.droppableProps}
@ -481,7 +491,7 @@ export const IssuesView: React.FC<Props> = ({
userAuth={memberRole}
/>
) : (
<CalendarView />
<CalendarView addIssueToDate={addIssueToDate} />
)}
</>
) : type === "issue" ? (
@ -502,8 +512,8 @@ export const IssuesView: React.FC<Props> = ({
title="Create a new issue"
description={
<span>
Use <pre className="inline rounded bg-gray-200 px-2 py-1">C</pre> shortcut to
create a new issue
Use <pre className="inline rounded bg-brand-surface-2 px-2 py-1">C</pre>{" "}
shortcut to create a new issue
</span>
}
Icon={PlusIcon}

View File

@ -56,7 +56,7 @@ export const LinkModal: React.FC<Props> = ({ isOpen, handleClose, onFormSubmit }
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
<div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
</Transition.Child>
<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"
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)}>
<div>
<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
</Dialog.Title>
<div className="mt-2 space-y-3">

View File

@ -44,7 +44,6 @@ import {
MODULE_ISSUES_WITH_PARAMS,
PROJECT_ISSUES_LIST_WITH_PARAMS,
} from "constants/fetch-keys";
import { DIVIDER } from "@blueprintjs/core/lib/esm/common/classes";
type Props = {
type?: string;
@ -217,7 +216,7 @@ export const SingleListIssue: React.FC<Props> = ({
</a>
</ContextMenu>
<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) => {
e.preventDefault();
setContextMenu(true);
@ -231,13 +230,15 @@ export const SingleListIssue: React.FC<Props> = ({
tooltipHeading="Issue 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}
</span>
</Tooltip>
)}
<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>
</a>
</Link>
@ -267,7 +268,7 @@ export const SingleListIssue: React.FC<Props> = ({
/>
)}
{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"}
</div>
)}
@ -276,7 +277,7 @@ export const SingleListIssue: React.FC<Props> = ({
{issue.label_details.map((label) => (
<span
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
className="h-1.5 w-1.5 rounded-full"
@ -308,8 +309,8 @@ export const SingleListIssue: React.FC<Props> = ({
/>
)}
{properties.link && (
<div className="flex items-center rounded-md shadow-sm px-2.5 py-1 cursor-default text-xs border border-gray-200">
<Tooltip tooltipHeading="Link" tooltipContent={`${issue.link_count}`}>
<div className="flex cursor-default items-center rounded-md border border-brand-base px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Links" tooltipContent={`${issue.link_count}`}>
<div className="flex items-center gap-1 text-gray-500">
<LinkIcon className="h-3.5 w-3.5 text-gray-500" />
{issue.link_count}
@ -318,10 +319,10 @@ export const SingleListIssue: React.FC<Props> = ({
</div>
)}
{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">
<Tooltip tooltipHeading="Attachment" tooltipContent={`${issue.attachment_count}`}>
<div className="flex cursor-default items-center rounded-md border border-brand-base px-2.5 py-1 text-xs shadow-sm">
<Tooltip tooltipHeading="Attachments" tooltipContent={`${issue.attachment_count}`}>
<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}
</div>
</Tooltip>

View File

@ -130,35 +130,31 @@ export const SingleList: React.FC<Props> = ({
};
return (
<Disclosure key={groupTitle} as="div" defaultOpen>
<Disclosure as="div" defaultOpen>
{({ open }) => (
<div className="bg-white">
<div
className={`flex items-center justify-between bg-gray-100 px-4 py-2.5 ${
open ? "" : "rounded-[10px]"
}`}
>
<div>
<div className="flex items-center justify-between px-4 py-2.5">
<Disclosure.Button>
<div className="flex items-center gap-x-3">
{selectedGroup !== null && (
<div className="flex items-center">{getGroupIcon()}</div>
)}
{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()}
</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">
{groupedByIssues?.[groupTitle].length ?? 0}
<span className="text-brand-2 min-w-[2.5rem] rounded-full bg-brand-surface-2 py-1 text-center text-xs">
{groupedByIssues[groupTitle as keyof IIssue].length}
</span>
</div>
</Disclosure.Button>
{type === "issue" ? (
<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}
>
<PlusIcon className="h-4 w-4" />
@ -168,7 +164,7 @@ export const SingleList: React.FC<Props> = ({
) : (
<CustomMenu
customButton={
<div className="flex items-center cursor-pointer">
<div className="flex cursor-pointer items-center">
<PlusIcon className="h-4 w-4" />
</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>

View File

@ -1,8 +1,7 @@
import Link from "next/link";
// icons
import { LinkIcon, TrashIcon } from "@heroicons/react/24/outline";
import { ExternalLinkIcon } from "components/icons";
import { ArrowTopRightOnSquareIcon, LinkIcon, TrashIcon } from "@heroicons/react/24/outline";
// helpers
import { timeAgo } from "helpers/date-time.helper";
// 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">
<Link href={link.url}>
<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"
>
<ExternalLinkIcon width="14" height="14" />
<ArrowTopRightOnSquareIcon className="h-4 w-4 text-brand-secondary" />
</a>
</Link>
<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)}
>
<TrashIcon className="h-4 w-4" />
@ -49,13 +48,13 @@ export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }
</div>
)}
<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">
<LinkIcon className="h-3.5 w-3.5" />
</div>
<div>
<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)}
<br />
by{" "}

View File

@ -100,13 +100,13 @@ export const SidebarProgressStats: React.FC<Props> = ({
>
<Tab.List
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"} `}
>
<Tab
className={({ selected }) =>
`w-full rounded px-3 py-1 text-gray-900 ${
selected ? " bg-theme text-white" : " hover:bg-hover-gray"
`w-full rounded px-3 py-1 text-brand-base ${
selected ? " bg-brand-accent text-white" : " hover:bg-brand-surface-2"
}`
}
>
@ -114,8 +114,8 @@ export const SidebarProgressStats: React.FC<Props> = ({
</Tab>
<Tab
className={({ selected }) =>
`w-full rounded px-3 py-1 text-gray-900 ${
selected ? " bg-theme text-white" : " hover:bg-hover-gray"
`w-full rounded px-3 py-1 text-brand-base ${
selected ? " bg-brand-accent text-white" : " hover:bg-brand-surface-2"
}`
}
>
@ -123,8 +123,8 @@ export const SidebarProgressStats: React.FC<Props> = ({
</Tab>
<Tab
className={({ selected }) =>
`w-full rounded px-3 py-1 text-gray-900 ${
selected ? " bg-theme text-white" : " hover:bg-hover-gray"
`w-full rounded px-3 py-1 text-brand-base ${
selected ? " bg-brand-accent text-white" : " hover:bg-brand-surface-2"
}`
}
>
@ -166,7 +166,7 @@ export const SidebarProgressStats: React.FC<Props> = ({
<SingleProgressStats
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
src={User}
height="100%"

View File

@ -19,8 +19,8 @@ export const SingleProgressStats: React.FC<TSingleProgressStatsProps> = ({
}) => (
<div
className={`flex w-full items-center justify-between rounded p-2 text-xs ${
onClick ? "cursor-pointer hover:bg-gray-100" : ""
} ${selected ? "bg-gray-100" : ""}`}
onClick ? "cursor-pointer hover:bg-brand-surface-1" : ""
} ${selected ? "bg-brand-surface-1" : ""}`}
onClick={onClick}
>
<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.completed_cycles.length > 0 ? (
<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} />
<span>Completed cycles are not editable.</span>
</div>

View File

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

View File

@ -139,7 +139,7 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
leaveFrom="opacity-100"
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>
<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"
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]">
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<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-brand-surface-2 px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div className="sm:flex sm:items-start">
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<ExclamationTriangleIcon
@ -163,11 +163,11 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
/>
</div>
<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
</Dialog.Title>
<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-{" "}
<span className="font-bold">{data?.name}</span>? All of the data related
to the cycle will be permanently removed. This action cannot be undone.

View File

@ -37,30 +37,30 @@ export const EmptyCycle = () => {
return (
<div className="flex h-full w-full flex-col items-center justify-center gap-5 ">
<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">
<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">
<span className="h-2 w-20 rounded-full bg-gray-200" />
<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-brand-surface-2" />
</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} />
</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">
<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">
<span className="h-2 w-20 rounded-full bg-gray-200" />
<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-brand-surface-2" />
</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} />
</div>
</div>
@ -68,7 +68,7 @@ export const EmptyCycle = () => {
<div className="flex flex-col items-center justify-center gap-4 text-center ">
<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
time. Create new cycle now.
</p>

View File

@ -94,7 +94,7 @@ export const CycleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, stat
return (
<form onSubmit={handleSubmit(handleCreateUpdateCycle)}>
<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
</h3>
<div className="space-y-3">
@ -196,7 +196,7 @@ export const CycleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, stat
</div>
</div>
</div>
<div className="-mx-5 mt-5 flex justify-end gap-2 border-t 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>
<PrimaryButton
type="submit"

View File

@ -151,7 +151,7 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
leaveFrom="opacity-100"
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>
<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">
@ -164,7 +164,7 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform rounded-lg 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
handleFormSubmit={handleFormSubmit}
handleClose={handleClose}

View File

@ -59,9 +59,9 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({
{({ open }) => (
<>
<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">
{cycles?.find((c) => c.id === value)?.name ?? "Cycles"}
</div>
@ -75,7 +75,7 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({
leaveTo="opacity-0"
>
<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">
{options ? (
@ -93,7 +93,7 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({
: ""
} ${
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}
>
@ -103,14 +103,14 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({
</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
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}
>
<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
className={`fixed top-0 ${
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 ? (
<>
@ -143,19 +143,19 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div className="flex gap-2.5 px-5 text-sm">
<div className="flex items-center ">
<span
className={`flex items-center rounded border-[0.5px] border-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)}
</span>
</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">
{({ open }) => (
<>
<Popover.Button
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 ${
open ? "bg-gray-100" : ""
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-brand-surface-1" : ""
}`}
>
<CalendarDaysIcon className="h-3 w-3" />
@ -216,8 +216,8 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<>
<Popover.Button
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 ${
open ? "bg-gray-100" : ""
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-brand-surface-1" : ""
}`}
>
<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 items-start justify-start 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>
{!isCompleted && (
<CustomMenu.MenuItem onClick={() => setCycleDeleteModal(true)}>
@ -297,7 +297,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</CustomMenu>
</div>
<span className="whitespace-normal text-sm leading-5 text-black">
<span className="whitespace-normal text-sm leading-5 text-brand-base">
{cycle.description}
</span>
</div>
@ -323,7 +323,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
{cycle.owned_by.first_name.charAt(0)}
</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>
@ -333,7 +333,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<span>Progress</span>
</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">
<ProgressBar value={cycle.completed_issues} maxValue={cycle.total_issues} />
</span>
@ -344,7 +344,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</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>
{({ open }) => (
<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 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 ? (
<span className="rounded bg-[#09A953]/10 px-1.5 py-0.5 text-xs text-[#09A953]">
{progressPercentage ? `${progressPercentage}%` : ""}
@ -371,7 +371,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
) : (
<div className="flex items-center gap-1">
<ExclamationIcon height={14} width={14} />
<span className="text-xs italic text-gray-500">
<span className="text-xs italic text-brand-secondary">
{cycleStatus === "upcoming"
? "Cycle is yet to start."
: "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-center gap-1">
<span>
<DocumentIcon className="h-3 w-3 text-gray-500" />
<DocumentIcon className="h-3 w-3 text-brand-secondary" />
</span>
<span>
Pending Issues -{" "}
@ -395,7 +395,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</span>
</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">
<span className="h-2.5 w-2.5 rounded-full bg-[#A9BBD0]" />
<span>Ideal</span>
@ -424,7 +424,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</Disclosure>
</div>
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-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>
{({ open }) => (
<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 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>
{cycle.total_issues > 0 ? (
@ -445,7 +445,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
) : (
<div className="flex items-center gap-1">
<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.
</span>
</div>

View File

@ -238,7 +238,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
return (
<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}`}>
<a className="w-full">
<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-start gap-1 ">
<CalendarDaysIcon className="h-4 w-4 text-gray-900" />
<span className="text-gray-400">Start :</span>
<CalendarDaysIcon className="h-4 w-4 text-brand-base" />
<span className="text-brand-secondary">Start :</span>
<span>{renderShortDateWithYearFormat(startDate)}</span>
</div>
<div className="flex items-start gap-1 ">
<TargetIcon className="h-4 w-4 text-gray-900" />
<span className="text-gray-400">End :</span>
<TargetIcon className="h-4 w-4 text-brand-base" />
<span className="text-brand-secondary">End :</span>
<span>{renderShortDateWithYearFormat(endDate)}</span>
</div>
</div>
@ -293,11 +293,11 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
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)}
</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 className="flex items-center">
{!isCompleted && (
@ -306,7 +306,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
e.preventDefault();
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>
<PencilIcon className="h-4 w-4" />
@ -350,7 +350,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
<Disclosure>
{({ open }) => (
<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"
}`}
>
@ -368,7 +368,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
</div>
<Transition show={open}>
<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="space-y-3 text-xs">
{stateGroups.map((group) => (
@ -388,7 +388,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
<div>
<span>
{cycle[group.key as keyof ICycle] as number}{" "}
<span className="text-gray-500">
<span className="text-brand-secondary">
-{" "}
{cycle.total_issues > 0
? `${Math.round(

View File

@ -89,7 +89,7 @@ export const TransferIssuesModal: React.FC<Props> = ({ isOpen, handleClose }) =>
leaveFrom="opacity-100"
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>
<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"
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 items-center justify-between px-5">
<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" />
</button>
</div>
<div className="flex items-center gap-2 pb-3 mt-2 px-5 border-b border-gray-200">
<MagnifyingGlassIcon className="h-4 w-4 text-gray-500" />
<div className="flex items-center gap-2 pb-3 px-5 border-b border-brand-base">
<MagnifyingGlassIcon className="h-4 w-4 text-brand-secondary" />
<input
className="outline-none"
placeholder="Search for a cycle..."
@ -129,7 +129,7 @@ export const TransferIssuesModal: React.FC<Props> = ({ isOpen, handleClose }) =>
filteredOptions.map((option: ICycle) => (
<button
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={() => {
transferIssue({
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">
<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
issues.
</span>
</div>
)
) : (
<p className="text-center text-gray-500">Loading...</p>
<p className="text-center text-brand-secondary">Loading...</p>
)}
</div>
</div>

View File

@ -38,7 +38,7 @@ export const TransferIssues: React.FC<Props> = ({ handleClick }) => {
: 0;
return (
<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} />
<span>Completed cycles are not editable.</span>
</div>

View File

@ -55,7 +55,7 @@ const EmojiIconPicker: React.FC<Props> = ({
return (
<Popover className="relative z-[1]" ref={ref}>
<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)}
>
{label}
@ -69,8 +69,8 @@ const EmojiIconPicker: React.FC<Props> = ({
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Popover.Panel className="absolute z-10 mt-2 w-[250px] rounded-[4px] bg-white shadow-lg">
<div className="h-[230px] w-[250px] overflow-auto border rounded-[4px] bg-white p-2 shadow-xl">
<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 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.List className="flex-0 -mx-2 flex justify-around gap-1 p-1">
{tabOptions.map((tab) => (
@ -100,7 +100,7 @@ const EmojiIconPicker: React.FC<Props> = ({
{recentEmojis.map((emoji) => (
<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}
onClick={() => {
onChange(emoji);
@ -119,7 +119,7 @@ const EmojiIconPicker: React.FC<Props> = ({
{emojis.map((emoji) => (
<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}
onClick={() => {
onChange(emoji);
@ -184,7 +184,7 @@ const EmojiIconPicker: React.FC<Props> = ({
{icons.material_rounded.map((icon) => (
<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}
onClick={() => {
if (onIconsClick) onIconsClick(icon.name);

View File

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

View File

@ -52,7 +52,7 @@ const IntegrationGuide = () => {
handleClose={() => setDeleteImportModal(false)}
data={importToDelete}
/>
<div className="space-y-2">
<div className="space-y-2 h-full">
{!provider && (
<>
<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 "./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 (
<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="h-12 w-12 flex-shrink-0">
<Image

View File

@ -59,42 +59,46 @@ const activityDetails: {
},
estimate_point: {
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: {
icon: <TagIcon height="12" width="12" color="#6b7280" />,
},
modules: {
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: {
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: {
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: {
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: {
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: {
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: {
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: {
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: {
message: "updated the link",
@ -240,7 +244,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
activityItem.field !== "link" &&
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") {
value = activityItem.new_value ? addSpaceIfCamelCase(activityItem.new_value) : "None";
} else if (activityItem.field === "labels") {
@ -255,7 +259,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
}
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="h-1.5 w-1.5 rounded-full"
@ -265,7 +269,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
aria-hidden="true"
/>
</span>
<span className="ml-3 font-medium text-gray-900">{name}</span>
<span className="ml-3 font-medium text-brand-base">{name}</span>
</span>
);
} else if (activityItem.field === "assignees") {
@ -298,7 +302,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
<div className="relative pb-1">
{issueActivities.length > 1 && activityItemIdx !== issueActivities.length - 1 ? (
<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"
/>
) : null}
@ -307,7 +311,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
<div>
<div className="relative px-1.5">
<div className="mt-1.5">
<div className="ring-6 flex h-7 w-7 items-center justify-center rounded-full bg-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 ? (
activityDetails[activityItem.field as keyof typeof activityDetails]
?.icon
@ -332,7 +336,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
</div>
</div>
<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">
{activityItem.actor_detail.first_name}
{activityItem.actor_detail.is_bot
@ -340,7 +344,7 @@ export const IssueActivitySection: React.FC<Props> = () => {
: " " + activityItem.actor_detail.last_name}
</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(activityItem.created_at)}
</span>
@ -353,12 +357,13 @@ export const IssueActivitySection: React.FC<Props> = () => {
);
} else if ("comment_json" in activityItem)
return (
<CommentCard
key={activityItem.id}
comment={activityItem as any}
onSubmit={handleCommentUpdate}
handleCommentDeletion={handleCommentDelete}
/>
<div key={activityItem.id} className="mt-4">
<CommentCard
comment={activityItem as any}
onSubmit={handleCommentUpdate}
handleCommentDeletion={handleCommentDelete}
/>
</div>
);
})}
</ul>

View File

@ -96,9 +96,9 @@ export const IssueAttachmentUpload = () => {
) : fileError ? (
<p className="text-center text-red-500">{fileError}</p>
) : 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>
</div>

View File

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

View File

@ -67,7 +67,7 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
</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" />
</span>
</div>
@ -77,7 +77,7 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
{comment.actor_detail.first_name}
{comment.actor_detail.is_bot ? "Bot" : " " + comment.actor_detail.last_name}
</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 className="issue-comments-section p-0">
{isEditing ? (
@ -117,7 +117,7 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
editable={false}
onBlur={() => ({})}
noBorder
customClassName="text-xs bg-gray-100"
customClassName="text-xs bg-brand-surface-1"
/>
)}
</div>

View File

@ -88,7 +88,7 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data })
leaveFrom="opacity-100"
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>
<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"
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 w-full items-center justify-start gap-6">
<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>
</div>
<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{" "}
<span className="break-all font-semibold">
{data?.project_detail.identifier}-{data?.sequence_id}

View File

@ -115,7 +115,7 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({ issue, handleFormS
role="textbox"
/>
{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
className={`${
watch("name").length === 0 || watch("name").length > 255 ? "text-red-500" : ""
@ -158,7 +158,7 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({ issue, handleFormS
)}
/>
<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"
}`}
>

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
</h3>
</div>
{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">
<span
className="block h-1.5 w-1.5 rounded-full"
@ -267,7 +267,7 @@ export const IssueForm: FC<IssueFormProps> = ({
/>
{mostSimilarIssue && (
<div className="flex items-center gap-x-2">
<p className="text-sm text-gray-500">
<p className="text-sm text-brand-secondary">
<Link
href={`/${workspaceSlug}/projects/${projectId}/issues/${mostSimilarIssue.id}`}
>
@ -283,7 +283,7 @@ export const IssueForm: FC<IssueFormProps> = ({
</p>
<button
type="button"
className="text-sm text-blue-500"
className="text-sm text-brand-accent"
onClick={() => {
setMostSimilarIssue(undefined);
}}
@ -298,7 +298,7 @@ export const IssueForm: FC<IssueFormProps> = ({
{issueName && issueName !== "" && (
<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" : ""
}`}
onClick={handleAutoGenerateDescription}
@ -315,7 +315,7 @@ export const IssueForm: FC<IssueFormProps> = ({
)}
<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)}
>
<SparklesIcon className="h-4 w-4" />
@ -444,7 +444,7 @@ export const IssueForm: FC<IssueFormProps> = ({
</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
className="flex cursor-pointer items-center gap-1"
onClick={() => setCreateMore((prevData) => !prevData)}
@ -453,7 +453,7 @@ export const IssueForm: FC<IssueFormProps> = ({
<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 ${
createMore ? "bg-theme" : "bg-gray-300"
createMore ? "bg-brand-accent" : "bg-gray-300"
} transition-colors duration-300 ease-in-out focus:outline-none`}
role="switch"
aria-checked="false"
@ -463,7 +463,7 @@ export const IssueForm: FC<IssueFormProps> = ({
aria-hidden="true"
className={`pointer-events-none inline-block h-3 w-3 ${
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>
</div>

View File

@ -219,7 +219,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
leaveFrom="opacity-100"
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>
<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"
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
issues={issues ?? []}
handleFormSubmit={handleFormSubmit}

View File

@ -82,7 +82,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
const isNotAllowed = false;
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">
<Link href={`/${workspaceSlug}/projects/${issue?.project_detail?.id}/issues/${issue.id}`}>
<a className="group relative flex items-center gap-2">
@ -97,7 +97,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
</Tooltip>
)}
<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)}
</span>
</Tooltip>
@ -127,7 +127,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
/>
)}
{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"}
</div>
)}
@ -136,7 +136,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
{issue.label_details.map((label) => (
<span
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
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"
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 ? (
<>
<Combobox value={value} onChange={() => ({})} multiple>
<div className="relative m-1">
<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"
/>
<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..."
onChange={(e) => setQuery(e.target.value)}
displayValue={() => ""}
@ -95,7 +95,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
{filteredIssues.length > 0 && (
<li className="p-2">
{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}
</h2>
)}
@ -106,7 +106,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
value={issue.id}
className={({ active }) =>
`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,
}}
/>
<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}
</span>{" "}
{issue.id}
@ -135,10 +135,10 @@ export const ParentIssuesListModal: React.FC<Props> = ({
{query !== "" && filteredIssues.length === 0 && (
<div className="py-14 px-6 text-center sm:px-14">
<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"
/>
<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.
</p>
</div>
@ -153,11 +153,11 @@ export const ParentIssuesListModal: React.FC<Props> = ({
<Combobox value={value} onChange={onChange}>
<div className="relative m-1">
<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"
/>
<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..."
onChange={(e) => setQuery(e.target.value)}
displayValue={() => ""}
@ -171,7 +171,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
{filteredIssues.length > 0 ? (
<li className="p-2">
{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}
</h2>
)}
@ -182,7 +182,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
value={issue.id}
className={({ active }) =>
`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()}
@ -194,7 +194,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
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}
</span>{" "}
{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">
<LayerDiagonalIcon height="56" width="56" />
<h3 className="text-gray-500">
<h3 className="text-brand-secondary">
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>
</div>
)}

View File

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

View File

@ -18,11 +18,11 @@ export const IssueDateSelect: React.FC<Props> = ({ value, onChange }) => (
<>
<Popover.Button
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
? "border-theme bg-theme/5 outline-none ring-1 ring-theme "
: "hover:bg-theme/5 "
? "border-brand-accent bg-brand-accent/5 outline-none ring-1 ring-brand-accent "
: "hover:bg-brand-accent/5 "
}`
}
>

View File

@ -60,11 +60,11 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
<>
<Combobox.Button
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
? "border-theme bg-theme/5 outline-none ring-1 ring-theme "
: "hover:bg-theme/5 "
? "border-brand-accent bg-brand-accent/5 outline-none ring-1 ring-brand-accent "
: "hover:bg-brand-accent/5 "
}`
}
>
@ -73,14 +73,13 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
<IssueLabelsList
labels={value.map((v) => issueLabels?.find((l) => l.id === v)?.color) ?? []}
length={3}
showLength
showLength={true}
/>
<span className=" text-gray-600">{value.length} Labels</span>
</span>
) : (
<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" />
<span className=" text-gray-500">Label</span>
<TagIcon className="h-3 w-3 text-brand-secondary" />
<span className=" text-brand-secondary">Label</span>
</span>
)}
</Combobox.Button>
@ -97,12 +96,12 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
>
<Combobox.Options
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">
<MagnifyingGlassIcon className="h-3 w-3 text-gray-500" />
<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-brand-secondary" />
<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)}
placeholder="Search for label..."
displayValue={(assigned: any) => assigned?.name}
@ -121,7 +120,7 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
key={label.id}
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`
}
value={label.id}
@ -151,8 +150,8 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
);
} else
return (
<div className="border-y border-gray-400 bg-gray-50">
<div className="flex select-none items-center gap-2 truncate p-2 font-medium text-gray-900">
<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-brand-base">
<RectangleGroupIcon className="h-3 w-3" /> {label.name}
</div>
<div>
@ -161,7 +160,7 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
key={child.id}
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`
}
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
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)}
>
<span className="flex items-center justify-start gap-1">

View File

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

View File

@ -59,7 +59,7 @@ export const IssueProjectSelect: React.FC<IssueProjectSelectProps> = ({
<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>
);

View File

@ -56,7 +56,7 @@ export const IssueStateSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
onChange={onChange}
options={options}
label={
<div className="flex items-center gap-2 text-gray-500">
<div className="flex items-center gap-2 text-brand-secondary">
{selectedOption ? (
getStateGroupIcon(selectedOption.group, "16", "16", selectedOption.color)
) : currentDefaultState ? (
@ -70,7 +70,7 @@ export const IssueStateSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
footerOption={
<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)}
>
<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 (
<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" />
<p>Assignees</p>
</div>
@ -64,11 +64,11 @@ export const SidebarAssigneeSelect: React.FC<Props> = ({ value, onChange, userAu
<CustomSearchSelect
value={value}
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) ? (
<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} />
<span className="text-gray-500">{value.length} Assignees</span>
<span className="text-brand-base">{value.length} Assignees</span>
</div>
) : (
"No assignees"

View File

@ -105,7 +105,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
return (
<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} />
<p>Blocked by</p>
</div>
@ -115,7 +115,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
? watch("blocked_list").map((issue) => (
<div
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
href={`/${workspaceSlug}/projects/${projectId}/issues/${
@ -176,7 +176,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
leaveFrom="opacity-100 scale-100"
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>
<Combobox
onChange={(val: string) => {
@ -191,12 +191,12 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
>
<div className="relative m-1">
<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"
/>
<input
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..."
onChange={(e) => setQuery(e.target.value)}
/>
@ -209,7 +209,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
{filteredIssues.length > 0 ? (
<li className="p-2">
{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
</h2>
)}
@ -226,7 +226,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
value={issue.id}
className={({ active }) =>
`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,
}}
/>
<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
?.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">
<LayerDiagonalIcon height="56" width="56" />
<h3 className="text-gray-500">
<h3 className="text-brand-secondary">
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>
</div>
)}
@ -287,9 +287,9 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
</Transition.Root>
<button
type="button"
className={`flex w-full ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100"
} 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`}
className={`flex w-full text-brand-secondary ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-brand-surface-2"
} 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)}
disabled={isNotAllowed}
>

View File

@ -105,7 +105,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
return (
<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} />
<p>Blocking</p>
</div>
@ -115,7 +115,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
? watch("blockers_list").map((issue) => (
<div
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
href={`/${workspaceSlug}/projects/${projectId}/issues/${
@ -176,7 +176,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
leaveFrom="opacity-100 scale-100"
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
onChange={(val: string) => {
const selectedIssues = watchBlocker("blocker_issue_ids");
@ -190,12 +190,12 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
>
<div className="relative m-1">
<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"
/>
<input
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..."
onChange={(e) => setQuery(e.target.value)}
/>
@ -208,7 +208,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
{filteredIssues.length > 0 ? (
<li className="p-2">
{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
</h2>
)}
@ -225,7 +225,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
value={issue.id}
className={({ active }) =>
`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,
}}
/>
<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
?.identifier
}
-{issue.sequence_id}
</span>
<span>{issue.name}</span>
<span className="text-brand-muted-1">{issue.name}</span>
</div>
</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">
<LayerDiagonalIcon height="56" width="56" />
<h3 className="text-gray-500">
<h3 className="text-brand-secondary">
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>
</div>
)}
@ -284,9 +284,9 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
</Transition.Root>
<button
type="button"
className={`flex w-full ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100"
} 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`}
className={`flex w-full text-brand-secondary ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-brand-surface-2"
} 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)}
disabled={isNotAllowed}
>

View File

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

View File

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

View File

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

View File

@ -52,7 +52,7 @@ export const SidebarParentSelect: React.FC<Props> = ({
return (
<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" />
<p>Parent</p>
</div>
@ -78,8 +78,8 @@ export const SidebarParentSelect: React.FC<Props> = ({
<button
type="button"
className={`flex w-full ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100"
} 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`}
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-brand-surface-2"
} 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)}
disabled={isNotAllowed}
>

View File

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

View File

@ -43,14 +43,14 @@ export const SidebarStateSelect: React.FC<Props> = ({ value, onChange, userAuth
return (
<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" />
<p>State</p>
</div>
<div className="sm:basis-1/2">
<CustomSelect
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(
selectedState?.group ?? "backlog",
"16",

View File

@ -229,7 +229,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
isOpen={deleteIssueModal}
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">
<h4 className="text-sm font-medium">
{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">
<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}
>
<LinkIcon className="h-3.5 w-3.5" />
@ -245,7 +245,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
{!isNotAllowed && (
<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)}
>
<TrashIcon className="h-3.5 w-3.5" />
@ -253,7 +253,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
)}
</div>
</div>
<div className="divide-y-2 divide-gray-100">
<div className="divide-y-2 divide-brand-base">
<div className="py-1">
<Controller
control={control}
@ -316,14 +316,14 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
issueDetail?.parent_detail ? (
<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 })}
>
{issueDetail.parent_detail?.name}
<XMarkIcon className="h-3 w-3" />
</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
</div>
)
@ -344,7 +344,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
userAuth={memberRole}
/>
<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" />
<p>Due date</p>
</div>
@ -382,7 +382,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
</div>
<div className="space-y-3 py-3">
<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" />
<p>Label</p>
</div>
@ -395,7 +395,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
return (
<span
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={() => {
const updatedLabels = watchIssue("labels_list")?.filter(
(l) => l !== labelId
@ -435,8 +435,8 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
className={`flex ${
isNotAllowed
? "cursor-not-allowed"
: "cursor-pointer hover:bg-gray-100"
} items-center gap-2 rounded-2xl border px-2 py-0.5 text-xs`}
: "cursor-pointer hover:bg-brand-surface-1"
} items-center gap-2 rounded-2xl border border-brand-base px-2 py-0.5 text-xs text-brand-secondary`}
>
Select Label
</Listbox.Button>
@ -448,7 +448,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
leaveFrom="opacity-100"
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">
{issueLabels ? (
issueLabels.length > 0 ? (
@ -463,9 +463,11 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
<Listbox.Option
key={label.id}
className={({ active, selected }) =>
`${active || selected ? "bg-indigo-50" : ""} ${
`${
active || selected ? "bg-brand-surface-1" : ""
} ${
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}
>
@ -483,8 +485,8 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
);
} else
return (
<div className="border-y border-gray-400 bg-gray-50">
<div className="flex select-none items-center gap-2 truncate p-2 font-medium text-gray-900">
<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-brand-base">
<RectangleGroupIcon className="h-3 w-3" />{" "}
{label.name}
</div>
@ -495,7 +497,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
className={({ active, selected }) =>
`${active || selected ? "bg-indigo-50" : ""} ${
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}
>
@ -530,8 +532,10 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
<button
type="button"
className={`flex ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100"
} items-center gap-1 rounded-2xl border px-2 py-0.5 text-xs`}
isNotAllowed
? "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)}
>
{createLabelForm ? (
@ -555,7 +559,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
{({ open }) => (
<>
<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") !== "" && (
<span
@ -627,7 +631,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
{!isNotAllowed && (
<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)}
>
<PlusIcon className="h-4 w-4" />

View File

@ -173,7 +173,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue }) => {
{({ open }) => (
<>
<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" : ""}`} />
Sub-issues <span className="ml-1 text-gray-600">{subIssues.length}</span>
</Disclosure.Button>
@ -181,7 +181,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue }) => {
<div className="flex items-center">
<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}
>
<PlusIcon className="h-3 w-3" />
@ -210,7 +210,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue }) => {
key={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">
<span
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);
}}
>
<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>
)}
</a>

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