diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 46a6b69371..8d72ac5db7 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -32,7 +32,6 @@ from plane.api.serializers import ( LabelSerializer, ) from plane.app.permissions import ( - WorkspaceEntityPermission, ProjectEntityPermission, ProjectLitePermission, ProjectMemberPermission, diff --git a/apiserver/plane/app/views/cycle/issue.py b/apiserver/plane/app/views/cycle/issue.py index 2a5505dd05..9a029eb257 100644 --- a/apiserver/plane/app/views/cycle/issue.py +++ b/apiserver/plane/app/views/cycle/issue.py @@ -38,7 +38,7 @@ from plane.db.models import ( ) from plane.bgtasks.issue_activites_task import issue_activity from plane.utils.issue_filters import issue_filters - +from plane.utils.user_timezone_converter import user_timezone_converter class CycleIssueViewSet(WebhookMixin, BaseViewSet): serializer_class = CycleIssueSerializer @@ -191,6 +191,11 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): "is_draft", "archived_at", ) + datetime_fields = ["created_at", "updated_at"] + issues = user_timezone_converter( + issues, datetime_fields, request.user.user_timezone + ) + return Response(issues, status=status.HTTP_200_OK) def create(self, request, slug, project_id, cycle_id): diff --git a/apiserver/plane/app/views/issue/archive.py b/apiserver/plane/app/views/issue/archive.py index d9274ae4fa..af019a7ec6 100644 --- a/apiserver/plane/app/views/issue/archive.py +++ b/apiserver/plane/app/views/issue/archive.py @@ -47,7 +47,7 @@ from plane.db.models import ( ) from plane.bgtasks.issue_activites_task import issue_activity from plane.utils.issue_filters import issue_filters - +from plane.utils.user_timezone_converter import user_timezone_converter class IssueArchiveViewSet(BaseViewSet): permission_classes = [ @@ -239,6 +239,11 @@ class IssueArchiveViewSet(BaseViewSet): "is_draft", "archived_at", ) + datetime_fields = ["created_at", "updated_at"] + issues = user_timezone_converter( + issue_queryset, datetime_fields, request.user.user_timezone + ) + return Response(issues, status=status.HTTP_200_OK) def retrieve(self, request, slug, project_id, pk=None): diff --git a/apiserver/plane/app/views/issue/base.py b/apiserver/plane/app/views/issue/base.py index 23df585402..7a0e5d9b1e 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -50,6 +50,7 @@ from plane.db.models import ( Project, ) from plane.utils.issue_filters import issue_filters +from plane.utils.user_timezone_converter import user_timezone_converter # Module imports from .. import BaseAPIView, BaseViewSet, WebhookMixin @@ -241,6 +242,10 @@ class IssueListEndpoint(BaseAPIView): "is_draft", "archived_at", ) + datetime_fields = ["created_at", "updated_at"] + issues = user_timezone_converter( + issues, datetime_fields, request.user.user_timezone + ) return Response(issues, status=status.HTTP_200_OK) @@ -440,6 +445,10 @@ class IssueViewSet(WebhookMixin, BaseViewSet): "is_draft", "archived_at", ) + datetime_fields = ["created_at", "updated_at"] + issues = user_timezone_converter( + issue_queryset, datetime_fields, request.user.user_timezone + ) return Response(issues, status=status.HTTP_200_OK) def create(self, request, slug, project_id): @@ -503,6 +512,10 @@ class IssueViewSet(WebhookMixin, BaseViewSet): ) .first() ) + datetime_fields = ["created_at", "updated_at"] + issue = user_timezone_converter( + issue, datetime_fields, request.user.user_timezone + ) return Response(issue, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/apiserver/plane/app/views/issue/draft.py b/apiserver/plane/app/views/issue/draft.py index 077d7dcaf7..fe75c61f1f 100644 --- a/apiserver/plane/app/views/issue/draft.py +++ b/apiserver/plane/app/views/issue/draft.py @@ -45,6 +45,7 @@ from plane.db.models import ( Project, ) from plane.utils.issue_filters import issue_filters +from plane.utils.user_timezone_converter import user_timezone_converter # Module imports from .. import BaseViewSet @@ -229,6 +230,10 @@ class IssueDraftViewSet(BaseViewSet): "is_draft", "archived_at", ) + datetime_fields = ["created_at", "updated_at"] + issues = user_timezone_converter( + issue_queryset, datetime_fields, request.user.user_timezone + ) return Response(issues, status=status.HTTP_200_OK) def create(self, request, slug, project_id): diff --git a/apiserver/plane/app/views/issue/sub_issue.py b/apiserver/plane/app/views/issue/sub_issue.py index da479e0e99..2ee4574eb6 100644 --- a/apiserver/plane/app/views/issue/sub_issue.py +++ b/apiserver/plane/app/views/issue/sub_issue.py @@ -31,6 +31,7 @@ from plane.db.models import ( IssueAttachment, ) from plane.bgtasks.issue_activites_task import issue_activity +from plane.utils.user_timezone_converter import user_timezone_converter from collections import defaultdict @@ -132,6 +133,10 @@ class SubIssuesEndpoint(BaseAPIView): "is_draft", "archived_at", ) + datetime_fields = ["created_at", "updated_at"] + sub_issues = user_timezone_converter( + sub_issues, datetime_fields, request.user.user_timezone + ) return Response( { "sub_issues": sub_issues, diff --git a/apiserver/plane/app/views/module/archive.py b/apiserver/plane/app/views/module/archive.py index 9c0b6cca34..8a5345ff4c 100644 --- a/apiserver/plane/app/views/module/archive.py +++ b/apiserver/plane/app/views/module/archive.py @@ -32,6 +32,8 @@ from plane.db.models import ( ModuleLink, ) from plane.utils.analytics_plot import burndown_plot +from plane.utils.user_timezone_converter import user_timezone_converter + # Module imports from .. import BaseAPIView @@ -199,6 +201,10 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): "updated_at", "archived_at", ) + datetime_fields = ["created_at", "updated_at"] + modules = user_timezone_converter( + modules, datetime_fields, request.user.user_timezone + ) return Response(modules, status=status.HTTP_200_OK) else: queryset = ( diff --git a/apiserver/plane/app/views/module/base.py b/apiserver/plane/app/views/module/base.py index 4cd52b3b14..59f26a0364 100644 --- a/apiserver/plane/app/views/module/base.py +++ b/apiserver/plane/app/views/module/base.py @@ -48,6 +48,8 @@ from plane.db.models import ( Project, ) from plane.utils.analytics_plot import burndown_plot +from plane.utils.user_timezone_converter import user_timezone_converter + # Module imports from .. import BaseAPIView, BaseViewSet, WebhookMixin @@ -236,6 +238,10 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): "updated_at", ) ).first() + datetime_fields = ["created_at", "updated_at"] + module = user_timezone_converter( + module, datetime_fields, request.user.user_timezone + ) return Response(module, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -277,6 +283,10 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): "created_at", "updated_at", ) + datetime_fields = ["created_at", "updated_at"] + modules = user_timezone_converter( + modules, datetime_fields, request.user.user_timezone + ) return Response(modules, status=status.HTTP_200_OK) def retrieve(self, request, slug, project_id, pk): @@ -454,6 +464,10 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): "created_at", "updated_at", ).first() + datetime_fields = ["created_at", "updated_at"] + module = user_timezone_converter( + module, datetime_fields, request.user.user_timezone + ) return Response(module, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/apiserver/plane/app/views/module/issue.py b/apiserver/plane/app/views/module/issue.py index d264333407..e0fcb2d3c9 100644 --- a/apiserver/plane/app/views/module/issue.py +++ b/apiserver/plane/app/views/module/issue.py @@ -31,7 +31,7 @@ from plane.db.models import ( ) from plane.bgtasks.issue_activites_task import issue_activity from plane.utils.issue_filters import issue_filters - +from plane.utils.user_timezone_converter import user_timezone_converter class ModuleIssueViewSet(WebhookMixin, BaseViewSet): serializer_class = ModuleIssueSerializer @@ -150,6 +150,11 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet): "is_draft", "archived_at", ) + datetime_fields = ["created_at", "updated_at"] + issues = user_timezone_converter( + issues, datetime_fields, request.user.user_timezone + ) + return Response(issues, status=status.HTTP_200_OK) # create multiple issues inside a module diff --git a/apiserver/plane/app/views/view/base.py b/apiserver/plane/app/views/view/base.py index 35772ccf3b..7736e465c9 100644 --- a/apiserver/plane/app/views/view/base.py +++ b/apiserver/plane/app/views/view/base.py @@ -42,7 +42,7 @@ from plane.db.models import ( IssueAttachment, ) from plane.utils.issue_filters import issue_filters - +from plane.utils.user_timezone_converter import user_timezone_converter class GlobalViewViewSet(BaseViewSet): serializer_class = IssueViewSerializer @@ -255,6 +255,10 @@ class GlobalViewIssuesViewSet(BaseViewSet): "is_draft", "archived_at", ) + datetime_fields = ["created_at", "updated_at"] + issues = user_timezone_converter( + issues, datetime_fields, request.user.user_timezone + ) return Response(issues, status=status.HTTP_200_OK) diff --git a/apiserver/plane/utils/user_timezone_converter.py b/apiserver/plane/utils/user_timezone_converter.py new file mode 100644 index 0000000000..579b96c263 --- /dev/null +++ b/apiserver/plane/utils/user_timezone_converter.py @@ -0,0 +1,25 @@ +import pytz + +def user_timezone_converter(queryset, datetime_fields, user_timezone): + # Create a timezone object for the user's timezone + user_tz = pytz.timezone(user_timezone) + + # Check if queryset is a dictionary (single item) or a list of dictionaries + if isinstance(queryset, dict): + queryset_values = [queryset] + else: + queryset_values = list(queryset.values()) + + # Iterate over the dictionaries in the list + for item in queryset_values: + # Iterate over the datetime fields + for field in datetime_fields: + # Convert the datetime field to the user's timezone + if item[field]: + item[field] = item[field].astimezone(user_tz) + + # If queryset was a single item, return a single item + if isinstance(queryset, dict): + return queryset_values[0] + else: + return queryset_values diff --git a/packages/editor/core/src/styles/editor.css b/packages/editor/core/src/styles/editor.css index f2e32e1a04..dddf54b753 100644 --- a/packages/editor/core/src/styles/editor.css +++ b/packages/editor/core/src/styles/editor.css @@ -1,3 +1,14 @@ +.ProseMirror { + --font-size-h1: 1.5rem; + --font-size-h2: 1.3125rem; + --font-size-h3: 1.125rem; + --font-size-h4: 0.9375rem; + --font-size-h5: 0.8125rem; + --font-size-h6: 0.75rem; + --font-size-regular: 0.9375rem; + --font-size-list: var(--font-size-regular); +} + .ProseMirror p.is-editor-empty:first-child::before { content: attr(data-placeholder); float: left; @@ -56,7 +67,7 @@ /* to-do list */ ul[data-type="taskList"] li { - font-size: 1rem; + font-size: var(--font-size-list); line-height: 1.5; } @@ -162,7 +173,7 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { cursor: text; line-height: 1.2; font-family: inherit; - font-size: 14px; + font-size: var(--font-size-regular); color: inherit; -moz-box-sizing: border-box; box-sizing: border-box; @@ -310,15 +321,15 @@ ul[data-type="taskList"] ul[data-type="taskList"] { .prose :where(h1):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 2rem; margin-bottom: 4px; - font-size: 1.875rem; - font-weight: 700; + font-size: var(--font-size-h1); + font-weight: 600; line-height: 1.3; } .prose :where(h2):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 1.4rem; margin-bottom: 1px; - font-size: 1.5rem; + font-size: var(--font-size-h2); font-weight: 600; line-height: 1.3; } @@ -326,21 +337,23 @@ ul[data-type="taskList"] ul[data-type="taskList"] { .prose :where(h3):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 1rem; margin-bottom: 1px; - font-size: 1.25rem; + font-size: var(--font-size-h3); + font-weight: 600; line-height: 1.3; } .prose :where(h4):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 1rem; margin-bottom: 1px; - font-size: 1rem; + font-size: var(--font-size-h4); + font-weight: 600; line-height: 1.5; } .prose :where(h5):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 1rem; margin-bottom: 1px; - font-size: 0.9rem; + font-size: var(--font-size-h5); font-weight: 600; line-height: 1.5; } @@ -348,7 +361,7 @@ ul[data-type="taskList"] ul[data-type="taskList"] { .prose :where(h6):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 1rem; margin-bottom: 1px; - font-size: 0.83rem; + font-size: var(--font-size-h6); font-weight: 600; line-height: 1.5; } @@ -356,14 +369,14 @@ ul[data-type="taskList"] ul[data-type="taskList"] { .prose :where(p):not(:where([class~="not-prose"], [class~="not-prose"] *)) { margin-top: 0.25rem; margin-bottom: 1px; - padding: 3px 2px; - font-size: 1rem; + padding: 3px 0; + font-size: var(--font-size-regular); line-height: 1.5; } .prose :where(ol):not(:where([class~="not-prose"], [class~="not-prose"] *)) li p, .prose :where(ul):not(:where([class~="not-prose"], [class~="not-prose"] *)) li p { - font-size: 1rem; + font-size: var(--font-size-list); line-height: 1.5; } diff --git a/packages/types/src/pages.d.ts b/packages/types/src/pages.d.ts index 8d6d148a2d..63ad93cafd 100644 --- a/packages/types/src/pages.d.ts +++ b/packages/types/src/pages.d.ts @@ -17,14 +17,9 @@ export type TPage = { project: string | undefined; updated_at: Date | undefined; updated_by: string | undefined; - view_props: TPageViewProps | undefined; workspace: string | undefined; }; -export type TPageViewProps = { - full_width?: boolean; -}; - // page filters export type TPageNavigationTabs = "public" | "private" | "archived"; diff --git a/space/components/editor/rich-text-read-only-editor.tsx b/space/components/editor/rich-text-read-only-editor.tsx index 562e635814..56694e91f7 100644 --- a/space/components/editor/rich-text-read-only-editor.tsx +++ b/space/components/editor/rich-text-read-only-editor.tsx @@ -18,7 +18,7 @@ export const RichTextReadOnlyEditor = React.forwardRef ); } diff --git a/web/components/dashboard/widgets/recent-collaborators/collaborators-list.tsx b/web/components/dashboard/widgets/recent-collaborators/collaborators-list.tsx index 3367f8b8c1..b4a210e0f6 100644 --- a/web/components/dashboard/widgets/recent-collaborators/collaborators-list.tsx +++ b/web/components/dashboard/widgets/recent-collaborators/collaborators-list.tsx @@ -33,7 +33,7 @@ const CollaboratorListItem: React.FC = observer((prop
); }); diff --git a/web/components/inbox/content/issue-properties.tsx b/web/components/inbox/content/issue-properties.tsx index 2adf4bdc7a..89ddbdef7a 100644 --- a/web/components/inbox/content/issue-properties.tsx +++ b/web/components/inbox/content/issue-properties.tsx @@ -33,7 +33,7 @@ export const InboxIssueContentProperties: React.FC = observer((props) => if (!issue || !issue?.id) return <>; return (
-
+
Properties
diff --git a/web/components/inbox/content/issue-root.tsx b/web/components/inbox/content/issue-root.tsx index d0f682447f..72a5e833dc 100644 --- a/web/components/inbox/content/issue-root.tsx +++ b/web/components/inbox/content/issue-root.tsx @@ -114,7 +114,7 @@ export const InboxIssueMainContent: React.FC = observer((props) => { return ( <> -
+
= observer((props) => { issueOperations={issueOperations} disabled={!isEditable} value={issue.name} + containerClassName="-ml-3" /> {loader === "issue-loading" ? ( @@ -140,6 +141,7 @@ export const InboxIssueMainContent: React.FC = observer((props) => { disabled={!isEditable} issueOperations={issueOperations} setIsSubmitting={(value) => setIsSubmitting(value)} + containerClassName="-ml-3 border-none" /> )} @@ -152,12 +154,15 @@ export const InboxIssueMainContent: React.FC = observer((props) => { /> )}
- + +
+ +
= observer((props) => { duplicateIssueDetails={inboxIssue?.duplicate_issue_detail} /> -
+
diff --git a/web/components/inbox/content/root.tsx b/web/components/inbox/content/root.tsx index 719b1c19f1..7874b74a51 100644 --- a/web/components/inbox/content/root.tsx +++ b/web/components/inbox/content/root.tsx @@ -52,7 +52,7 @@ export const InboxContentRoot: FC = observer((props) => { isSubmitting={isSubmitting} />
-
+
= observer((props) data={formData} handleData={handleFormData} editorRef={descriptionEditorRef} + containerClassName="border-[0.5px] border-custom-border-200 py-3" />
diff --git a/web/components/inbox/modals/create-edit-modal/edit-root.tsx b/web/components/inbox/modals/create-edit-modal/edit-root.tsx index 6ffb6efeed..fb5da771ad 100644 --- a/web/components/inbox/modals/create-edit-modal/edit-root.tsx +++ b/web/components/inbox/modals/create-edit-modal/edit-root.tsx @@ -138,6 +138,7 @@ export const InboxIssueEditRoot: FC = observer((props) => { data={formData} handleData={handleFormData} editorRef={descriptionEditorRef} + containerClassName="border-[0.5px] border-custom-border-200 py-3" />
diff --git a/web/components/inbox/modals/create-edit-modal/issue-description.tsx b/web/components/inbox/modals/create-edit-modal/issue-description.tsx index 74c62f97c8..98d2b8efad 100644 --- a/web/components/inbox/modals/create-edit-modal/issue-description.tsx +++ b/web/components/inbox/modals/create-edit-modal/issue-description.tsx @@ -11,6 +11,7 @@ import { getDescriptionPlaceholder } from "@/helpers/issue.helper"; import { useProjectInbox } from "@/hooks/store"; type TInboxIssueDescription = { + containerClassName?: string; workspaceSlug: string; projectId: string; workspaceId: string; @@ -21,7 +22,7 @@ type TInboxIssueDescription = { // TODO: have to implement GPT Assistance export const InboxIssueDescription: FC = observer((props) => { - const { workspaceSlug, projectId, workspaceId, data, handleData, editorRef } = props; + const {containerClassName, workspaceSlug, projectId, workspaceId, data, handleData, editorRef } = props; // hooks const { loader } = useProjectInbox(); @@ -42,6 +43,7 @@ export const InboxIssueDescription: FC = observer((props dragDropEnabled={false} onChange={(_description: object, description_html: string) => handleData("description_html", description_html)} placeholder={getDescriptionPlaceholder} + containerClassName={containerClassName} />
); diff --git a/web/components/inbox/modals/create-issue-modal.tsx b/web/components/inbox/modals/create-issue-modal.tsx deleted file mode 100644 index 3cc642be89..0000000000 --- a/web/components/inbox/modals/create-issue-modal.tsx +++ /dev/null @@ -1,326 +0,0 @@ -import { Fragment, useRef, useState } from "react"; -import { observer } from "mobx-react"; -import { useRouter } from "next/router"; -import { Controller, useForm } from "react-hook-form"; -import { Sparkle } from "lucide-react"; -import { Transition, Dialog } from "@headlessui/react"; -import { EditorRefApi } from "@plane/rich-text-editor"; -// types -import { TIssue } from "@plane/types"; -// ui -import { Button, Input, ToggleSwitch, TOAST_TYPE, setToast } from "@plane/ui"; -// components -import { GptAssistantPopover } from "@/components/core"; -import { PriorityDropdown } from "@/components/dropdowns"; -import { RichTextEditor } from "@/components/editor/rich-text-editor/rich-text-editor"; -import { ISSUE_CREATED } from "@/constants/event-tracker"; -import { useApplication, useEventTracker, useWorkspace, useProjectInbox } from "@/hooks/store"; -// services -import { AIService } from "@/services/ai.service"; -// components -// ui -// types -// constants - -type Props = { - isOpen: boolean; - onClose: () => void; -}; - -const defaultValues: Partial = { - name: "", - description_html: "

", - priority: "none", -}; - -// services -const aiService = new AIService(); - -export const CreateInboxIssueModal: React.FC = observer((props) => { - const { isOpen, onClose } = props; - // router - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - if (!workspaceSlug || !projectId) return null; - // states - const [createMore, setCreateMore] = useState(false); - const [gptAssistantModal, setGptAssistantModal] = useState(false); - const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false); - // refs - const editorRef = useRef(null); - // hooks - const workspaceStore = useWorkspace(); - const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug.toString() as string)?.id.toString() as string; - - // store hooks - const { createInboxIssue } = useProjectInbox(); - const { - config: { envConfig }, - } = useApplication(); - const { captureIssueEvent } = useEventTracker(); - // form info - const { - control, - formState: { errors, isSubmitting }, - handleSubmit, - reset, - watch, - getValues, - } = useForm>({ defaultValues }); - const issueName = watch("name"); - - const handleClose = () => { - onClose(); - reset(defaultValues); - editorRef?.current?.clearEditor(); - }; - - const handleFormSubmit = async (formData: Partial) => { - if (!workspaceSlug || !projectId) return; - await createInboxIssue(workspaceSlug.toString(), projectId.toString(), formData) - .then((res) => { - if (!createMore) { - router.push(`/${workspaceSlug}/projects/${projectId}/inbox/?currentTab=open&inboxIssueId=${res?.issue?.id}`); - handleClose(); - } else { - reset(defaultValues); - editorRef?.current?.clearEditor(); - } - captureIssueEvent({ - eventName: ISSUE_CREATED, - payload: { - ...formData, - state: "SUCCESS", - element: "Inbox page", - }, - path: router.pathname, - }); - }) - .catch((error) => { - console.error(error); - captureIssueEvent({ - eventName: ISSUE_CREATED, - payload: { - ...formData, - state: "FAILED", - element: "Inbox page", - }, - path: router.pathname, - }); - }); - }; - - const handleAiAssistance = async (response: string) => { - if (!workspaceSlug || !projectId) return; - editorRef.current?.setEditorValueAtCursorPosition(response); - }; - - const handleAutoGenerateDescription = async () => { - const issueName = getValues("name"); - if (!workspaceSlug || !projectId || !issueName) return; - - setIAmFeelingLucky(true); - - aiService - .createGptTask(workspaceSlug as string, projectId as string, { - prompt: issueName, - task: "Generate a proper description for this issue.", - }) - .then((res) => { - if (res.response === "") - setToast({ - type: TOAST_TYPE.ERROR, - title: "Error!", - message: - "Issue title isn't informative enough to generate the description. Please try with a different title.", - }); - else handleAiAssistance(res.response_html); - }) - .catch((err) => { - const error = err?.data?.error; - - if (err.status === 429) - setToast({ - type: TOAST_TYPE.ERROR, - title: "Error!", - message: error || "You have reached the maximum number of requests of 50 requests per month per user.", - }); - else - setToast({ - type: TOAST_TYPE.ERROR, - title: "Error!", - message: error || "Some error occurred. Please try again.", - }); - }) - .finally(() => setIAmFeelingLucky(false)); - }; - - return ( - - - -
- - -
-
- - -
-
-

Create Inbox Issue

-
-
-
- ( - - )} - /> -
-
-
- {watch("name") && issueName !== "" && ( - - )} - - {envConfig?.has_openai_configured && ( - { - setGptAssistantModal((prevData) => !prevData); - // this is done so that the title do not reset after gpt popover closed - reset(getValues()); - }} - onResponse={(response) => { - handleAiAssistance(response); - }} - button={ - - } - className="!min-w-[38rem]" - placement="top-end" - /> - )} -
- ( -

" : value} - ref={editorRef} - workspaceSlug={workspaceSlug.toString()} - workspaceId={workspaceId} - projectId={projectId.toString()} - dragDropEnabled={false} - onChange={(_description: object, description_html: string) => { - onChange(description_html); - }} - /> - )} - /> -
- -
- ( -
- -
- )} - /> -
-
-
-
-
-
setCreateMore((prevData) => !prevData)} - > - Create more - {}} size="md" /> -
-
- - -
-
-
-
-
-
-
-
-
- ); -}); diff --git a/web/components/issues/description-input.tsx b/web/components/issues/description-input.tsx index b8cfbd5827..538f5444ee 100644 --- a/web/components/issues/description-input.tsx +++ b/web/components/issues/description-input.tsx @@ -15,6 +15,7 @@ import { getDescriptionPlaceholder } from "@/helpers/issue.helper"; import { useWorkspace } from "@/hooks/store"; export type IssueDescriptionInputProps = { + containerClassName?: string; workspaceSlug: string; projectId: string; issueId: string; @@ -28,6 +29,7 @@ export type IssueDescriptionInputProps = { export const IssueDescriptionInput: FC = observer((props) => { const { + containerClassName, workspaceSlug, projectId, issueId, @@ -110,11 +112,12 @@ export const IssueDescriptionInput: FC = observer((p placeholder={ placeholder ? placeholder : (isFocused, value) => getDescriptionPlaceholder(isFocused, value) } + containerClassName={containerClassName} /> ) : ( ) } diff --git a/web/components/issues/issue-detail/main-content.tsx b/web/components/issues/issue-detail/main-content.tsx index 541dd1f3e8..309f86bcfb 100644 --- a/web/components/issues/issue-detail/main-content.tsx +++ b/web/components/issues/issue-detail/main-content.tsx @@ -54,7 +54,7 @@ export const IssueMainContent: React.FC = observer((props) => { return ( <> -
+
{issue.parent_id && ( = observer((props) => { issueOperations={issueOperations} disabled={!isEditable} value={issue.name} + containerClassName="-ml-3" /> {/* {issue?.description_html === issueDescription && ( */} @@ -97,6 +98,7 @@ export const IssueMainContent: React.FC = observer((props) => { disabled={!isEditable} issueOperations={issueOperations} setIsSubmitting={(value) => setIsSubmitting(value)} + containerClassName="-ml-3 border-none" /> {/* )} */} @@ -121,14 +123,18 @@ export const IssueMainContent: React.FC = observer((props) => { )}
- +
+ +
- +
+ +
); }); diff --git a/web/components/issues/issue-detail/root.tsx b/web/components/issues/issue-detail/root.tsx index f9e3c83f71..c061c6a635 100644 --- a/web/components/issues/issue-detail/root.tsx +++ b/web/components/issues/issue-detail/root.tsx @@ -357,7 +357,7 @@ export const IssueDetailRoot: FC = observer((props) => { /> ) : (
-
+
= observer((props portalElement={portalElement} placement={placements} menuItemsClassName="z-[14]" + maxHeight="lg" closeOnSelect > {MENU_ITEMS.map((item) => { diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx b/web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx index 0327755a9c..62b808b3f9 100644 --- a/web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx @@ -123,6 +123,7 @@ export const ArchivedIssueQuickActions: React.FC = observer(( portalElement={portalElement} placement={placements} menuItemsClassName="z-[14]" + maxHeight="lg" closeOnSelect > {MENU_ITEMS.map((item) => { diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/cycle-issue.tsx b/web/components/issues/issue-layouts/quick-action-dropdowns/cycle-issue.tsx index 503d8258eb..a35de2735c 100644 --- a/web/components/issues/issue-layouts/quick-action-dropdowns/cycle-issue.tsx +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/cycle-issue.tsx @@ -181,6 +181,7 @@ export const CycleIssueQuickActions: React.FC = observer((pro customButton={customActionButton} portalElement={portalElement} menuItemsClassName="z-[14]" + maxHeight="lg" closeOnSelect > {MENU_ITEMS.map((item) => { diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/draft-issue.tsx b/web/components/issues/issue-layouts/quick-action-dropdowns/draft-issue.tsx index 18c2591072..bbeda85ce1 100644 --- a/web/components/issues/issue-layouts/quick-action-dropdowns/draft-issue.tsx +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/draft-issue.tsx @@ -107,6 +107,7 @@ export const DraftIssueQuickActions: React.FC = observer((pro portalElement={portalElement} placement={placements} menuItemsClassName="z-[14]" + maxHeight="lg" closeOnSelect > {MENU_ITEMS.map((item) => { diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/module-issue.tsx b/web/components/issues/issue-layouts/quick-action-dropdowns/module-issue.tsx index 3cc3343b60..6061c0bee3 100644 --- a/web/components/issues/issue-layouts/quick-action-dropdowns/module-issue.tsx +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/module-issue.tsx @@ -178,6 +178,7 @@ export const ModuleIssueQuickActions: React.FC = observer((pr customButton={customActionButton} portalElement={portalElement} menuItemsClassName="z-[14]" + maxHeight="lg" closeOnSelect > {MENU_ITEMS.map((item) => { diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/project-issue.tsx b/web/components/issues/issue-layouts/quick-action-dropdowns/project-issue.tsx index 0fbe10da9e..b74c9c57dd 100644 --- a/web/components/issues/issue-layouts/quick-action-dropdowns/project-issue.tsx +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/project-issue.tsx @@ -171,6 +171,7 @@ export const ProjectIssueQuickActions: React.FC = observer((p customButton={customActionButton} portalElement={portalElement} menuItemsClassName="z-[14]" + maxHeight="lg" closeOnSelect > {MENU_ITEMS.map((item) => { diff --git a/web/components/issues/issue-modal/form.tsx b/web/components/issues/issue-modal/form.tsx index 43bfb90e5d..6c10c39dc6 100644 --- a/web/components/issues/issue-modal/form.tsx +++ b/web/components/issues/issue-modal/form.tsx @@ -480,6 +480,7 @@ export const IssueFormRoot: FC = observer((props) => { ref={editorRef} tabIndex={getTabIndex("description_html")} placeholder={getDescriptionPlaceholder} + containerClassName="border-[0.5px] border-custom-border-200 py-3" /> )} /> diff --git a/web/components/issues/peek-overview/issue-detail.tsx b/web/components/issues/peek-overview/issue-detail.tsx index 29b83f7888..d7b880266e 100644 --- a/web/components/issues/peek-overview/issue-detail.tsx +++ b/web/components/issues/peek-overview/issue-detail.tsx @@ -69,6 +69,7 @@ export const PeekOverviewIssueDetails: FC = observer( issueOperations={issueOperations} disabled={disabled} value={issue.name} + containerClassName="-ml-3" /> = observer( disabled={disabled} issueOperations={issueOperations} setIsSubmitting={(value) => setIsSubmitting(value)} + containerClassName="-ml-3 border-none" /> {currentUser && ( diff --git a/web/components/issues/title-input.tsx b/web/components/issues/title-input.tsx index 11a82e623b..db9a4199a5 100644 --- a/web/components/issues/title-input.tsx +++ b/web/components/issues/title-input.tsx @@ -3,6 +3,7 @@ import { observer } from "mobx-react"; // components import { TextArea } from "@plane/ui"; // types +import { cn } from "@/helpers/common.helper"; import useDebounce from "@/hooks/use-debounce"; import { TIssueOperations } from "./issue-detail"; // hooks @@ -16,12 +17,26 @@ export type IssueTitleInputProps = { issueOperations: TIssueOperations; projectId: string; issueId: string; + className?: string; + containerClassName?: string; }; export const IssueTitleInput: FC = observer((props) => { - const { disabled, value, workspaceSlug, isSubmitting, setIsSubmitting, issueId, issueOperations, projectId } = props; + const { + disabled, + value, + workspaceSlug, + isSubmitting, + setIsSubmitting, + issueId, + issueOperations, + projectId, + className, + containerClassName, + } = props; // states const [title, setTitle] = useState(""); + const [isLengthVisible, setIsLengthVisible] = useState(false); // hooks const debouncedValue = useDebounce(title, 1500); @@ -76,19 +91,32 @@ export const IssueTitleInput: FC = observer((props) => { if (disabled) return
{title}
; return ( -
+