diff --git a/web/components/analytics/custom-analytics/select/project.tsx b/web/components/analytics/custom-analytics/select/project.tsx index c57e56709..a4220266b 100644 --- a/web/components/analytics/custom-analytics/select/project.tsx +++ b/web/components/analytics/custom-analytics/select/project.tsx @@ -34,7 +34,7 @@ export const SelectProject: React.FC = ({ value, onChange, projects }) => .join(", ") : "All projects" } - optionsClassName="min-w-full" + optionsClassName="min-w-full max-w-[20rem]" multiple /> ); diff --git a/web/components/core/modals/gpt-assistant-modal.tsx b/web/components/core/modals/gpt-assistant-modal.tsx index d677e7daa..82b082c29 100644 --- a/web/components/core/modals/gpt-assistant-modal.tsx +++ b/web/components/core/modals/gpt-assistant-modal.tsx @@ -92,7 +92,7 @@ export const GptAssistantModal: React.FC = (props) => { .catch((err) => { const error = err?.data?.error; - if (err.status === 429) + if (err?.status === 429) setToastAlert({ type: "error", title: "Error!", diff --git a/web/components/issues/draft-issue-form.tsx b/web/components/issues/draft-issue-form.tsx index e80c4609a..be9857dc8 100644 --- a/web/components/issues/draft-issue-form.tsx +++ b/web/components/issues/draft-issue-form.tsx @@ -31,6 +31,8 @@ import type { IUser, IIssue, ISearchIssueResponse } from "types"; // components import { RichTextEditorWithRef } from "@plane/rich-text-editor"; import useEditorSuggestions from "hooks/use-editor-suggestions"; +import { observer } from "mobx-react-lite"; +import { useMobxStore } from "lib/mobx/store-provider"; const aiService = new AIService(); const fileService = new FileService(); @@ -89,7 +91,7 @@ interface IssueFormProps { )[]; } -export const DraftIssueForm: FC = (props) => { +export const DraftIssueForm: FC = observer((props) => { const { handleFormSubmit, data, @@ -100,30 +102,30 @@ export const DraftIssueForm: FC = (props) => { createMore, setCreateMore, status, - user, fieldsToShow, handleDiscard, } = props; - + // states const [stateModal, setStateModal] = useState(false); const [labelModal, setLabelModal] = useState(false); const [parentIssueListModalOpen, setParentIssueListModalOpen] = useState(false); const [selectedParentIssue, setSelectedParentIssue] = useState(null); - const [gptAssistantModal, setGptAssistantModal] = useState(false); const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false); - + // hooks const { setValue: setLocalStorageValue } = useLocalStorage("draftedIssue", {}); - + const { setToastAlert } = useToast(); + const editorSuggestions = useEditorSuggestions(); + // refs const editorRef = useRef(null); - + // router const router = useRouter(); const { workspaceSlug } = router.query; - - const { setToastAlert } = useToast(); - - const editorSuggestions = useEditorSuggestions(); - + // store + const { + appConfig: { envConfig }, + } = useMobxStore(); + // form info const { formState: { errors, isSubmitting }, handleSubmit, @@ -440,21 +442,23 @@ export const DraftIssueForm: FC = (props) => { /> )} /> - { - setGptAssistantModal(false); - // this is done so that the title do not reset after gpt popover closed - reset(getValues()); - }} - inset="top-2 left-0" - content="" - htmlContent={watch("description_html")} - onResponse={(response) => { - handleAiAssistance(response); - }} - projectId={projectId} - /> + {envConfig?.has_openai_configured && ( + { + setGptAssistantModal(false); + // this is done so that the title do not reset after gpt popover closed + reset(getValues()); + }} + inset="top-2 left-0" + content="" + htmlContent={watch("description_html")} + onResponse={(response) => { + handleAiAssistance(response); + }} + projectId={projectId} + /> + )} )}
@@ -623,4 +627,4 @@ export const DraftIssueForm: FC = (props) => { ); -}; +}); diff --git a/web/components/issues/form.tsx b/web/components/issues/form.tsx index f33d8e6be..454647bfb 100644 --- a/web/components/issues/form.tsx +++ b/web/components/issues/form.tsx @@ -94,28 +94,29 @@ export const IssueForm: FC = observer((props) => { fieldsToShow, handleFormDirty, } = props; - + // states const [stateModal, setStateModal] = useState(false); const [labelModal, setLabelModal] = useState(false); const [parentIssueListModalOpen, setParentIssueListModalOpen] = useState(false); const [selectedParentIssue, setSelectedParentIssue] = useState(null); - const [gptAssistantModal, setGptAssistantModal] = useState(false); const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false); - + // refs const editorRef = useRef(null); - + // router const router = useRouter(); const { workspaceSlug } = router.query; - - const { user: userStore } = useMobxStore(); - + // store + const { + user: userStore, + appConfig: { envConfig }, + } = useMobxStore(); const user = userStore.currentUser; - + console.log("envConfig", envConfig); + // hooks const editorSuggestion = useEditorSuggestions(); - const { setToastAlert } = useToast(); - + // form info const { formState: { errors, isSubmitting, isDirty }, handleSubmit, @@ -396,21 +397,23 @@ export const IssueForm: FC = observer((props) => { /> )} /> - { - setGptAssistantModal(false); - // this is done so that the title do not reset after gpt popover closed - reset(getValues()); - }} - inset="top-2 left-0" - content="" - htmlContent={watch("description_html")} - onResponse={(response) => { - handleAiAssistance(response); - }} - projectId={projectId} - /> + {envConfig?.has_openai_configured && ( + { + setGptAssistantModal(false); + // this is done so that the title do not reset after gpt popover closed + reset(getValues()); + }} + inset="top-2 left-0" + content="" + htmlContent={watch("description_html")} + onResponse={(response) => { + handleAiAssistance(response); + }} + projectId={projectId} + /> + )}
)}
diff --git a/web/components/issues/issue-peek-overview/view.tsx b/web/components/issues/issue-peek-overview/view.tsx index 3d29c1545..88afbd487 100644 --- a/web/components/issues/issue-peek-overview/view.tsx +++ b/web/components/issues/issue-peek-overview/view.tsx @@ -225,6 +225,7 @@ export const IssueView: FC = observer((props) => { size="sm" prependIcon={} variant="outline-primary" + className="hover:!bg-custom-primary-100/20" onClick={() => issueSubscription && issueSubscription.subscribed ? issueSubscriptionRemove() diff --git a/web/components/issues/sidebar.tsx b/web/components/issues/sidebar.tsx index e67b73baa..c0bb2da18 100644 --- a/web/components/issues/sidebar.tsx +++ b/web/components/issues/sidebar.tsx @@ -33,7 +33,7 @@ import { import { CustomDatePicker } from "components/ui"; // icons import { Bell, CalendarDays, LinkIcon, Plus, Signal, Tag, Trash2, Triangle, User2 } from "lucide-react"; -import { ContrastIcon, DiceIcon, DoubleCircleIcon, UserGroupIcon } from "@plane/ui"; +import { Button, ContrastIcon, DiceIcon, DoubleCircleIcon, UserGroupIcon } from "@plane/ui"; // helpers import { copyTextToClipboard } from "helpers/string.helper"; // types @@ -273,17 +273,18 @@ export const IssueDetailsSidebar: React.FC = observer((props) => { !issueDetail?.assignees.includes(user?.id ?? "") && !router.pathname.includes("[archivedIssueId]") && (fieldsToShow.includes("all") || fieldsToShow.includes("subscribe")) && ( - + )} {(fieldsToShow.includes("all") || fieldsToShow.includes("link")) && (
- setGptAssistantModal(false)} - inset="top-8 left-0" - content={watch("description_html")} - htmlContent={watch("description_html")} - onResponse={(response) => { - if (data && handleAiAssistance) { - handleAiAssistance(response); - editorRef.current?.setEditorValue(`${watch("description_html")}

${response}

` ?? ""); - } else { - setValue("description", {}); - setValue("description_html", `${watch("description_html")}

${response}

`); + {envConfig?.has_openai_configured && ( + setGptAssistantModal(false)} + inset="top-8 left-0" + content={watch("description_html")} + htmlContent={watch("description_html")} + onResponse={(response) => { + if (data && handleAiAssistance) { + handleAiAssistance(response); + editorRef.current?.setEditorValue(`${watch("description_html")}

${response}

` ?? ""); + } else { + setValue("description", {}); + setValue("description_html", `${watch("description_html")}

${response}

`); - editorRef.current?.setEditorValue(watch("description_html") ?? ""); - } - }} - projectId={projectId?.toString() ?? ""} - /> + editorRef.current?.setEditorValue(watch("description_html") ?? ""); + } + }} + projectId={projectId?.toString() ?? ""} + /> + )} ); }; diff --git a/web/components/project/card.tsx b/web/components/project/card.tsx index 9bfe6b7a7..25faed4b3 100644 --- a/web/components/project/card.tsx +++ b/web/components/project/card.tsx @@ -113,7 +113,7 @@ export const ProjectCard: React.FC = observer((props) => { className="absolute top-0 left-0 h-full w-full object-cover rounded-t" /> -
+
diff --git a/web/components/project/member-list-item.tsx b/web/components/project/member-list-item.tsx index 84d29079c..308ca8827 100644 --- a/web/components/project/member-list-item.tsx +++ b/web/components/project/member-list-item.tsx @@ -11,7 +11,7 @@ import { ConfirmProjectMemberRemove } from "components/project"; // ui import { CustomSelect, Tooltip } from "@plane/ui"; // icons -import { ChevronDown, XCircle } from "lucide-react"; +import { ChevronDown, Dot, XCircle } from "lucide-react"; // constants import { ROLE } from "constants/workspace"; // types @@ -116,7 +116,15 @@ export const ProjectMemberListItem: React.FC = observer((props) => { ) : (

{member.display_name || member.email}

)} -

{member.email ?? member.display_name}

+
+

{member.display_name}

+ {isAdmin && ( + <> + +

{member.email}

+ + )} +
diff --git a/web/components/web-hooks/webhooks-list-item.tsx b/web/components/web-hooks/webhooks-list-item.tsx index 4d99e1d22..549caf024 100644 --- a/web/components/web-hooks/webhooks-list-item.tsx +++ b/web/components/web-hooks/webhooks-list-item.tsx @@ -1,10 +1,10 @@ -import { FC, useState } from "react"; +import { FC } from "react"; import { ToggleSwitch } from "@plane/ui"; -import { Pencil, XCircle } from "lucide-react"; -import { IWebhook } from "types"; import Link from "next/link"; import { RootStore } from "store/root"; import { useMobxStore } from "lib/mobx/store-provider"; +// types +import { IWebhook } from "types"; interface IWebhookListItem { workspaceSlug: string; diff --git a/web/components/workspace/issues-stats.tsx b/web/components/workspace/issues-stats.tsx index 22e966ac8..aef5cd108 100644 --- a/web/components/workspace/issues-stats.tsx +++ b/web/components/workspace/issues-stats.tsx @@ -23,7 +23,10 @@ export const IssuesStats: React.FC = ({ data }) => {

Issues assigned to you

{data ? ( -
router.push(`/${workspaceSlug}/me/my-issues`)}> +
router.push(`/${workspaceSlug}/workspace-views/assigned`)} + > {data.assigned_issues_count}
) : ( diff --git a/web/components/workspace/settings/members-list-item.tsx b/web/components/workspace/settings/members-list-item.tsx index d98846e74..7c00f4f12 100644 --- a/web/components/workspace/settings/members-list-item.tsx +++ b/web/components/workspace/settings/members-list-item.tsx @@ -11,7 +11,7 @@ import { ConfirmWorkspaceMemberRemove } from "components/workspace"; // ui import { CustomSelect, Tooltip } from "@plane/ui"; // icons -import { ChevronDown, XCircle } from "lucide-react"; +import { ChevronDown, Dot, XCircle } from "lucide-react"; // constants import { ROLE } from "constants/workspace"; import { TUserWorkspaceRole } from "types"; @@ -132,7 +132,15 @@ export const WorkspaceMembersListItem: FC = (props) => { ) : (

{member.display_name || member.email}

)} -

{member.email ?? member.display_name}

+
+

{member.display_name}

+ {isAdmin && ( + <> + +

{member.email}

+ + )} +
diff --git a/web/components/workspace/settings/members-list.tsx b/web/components/workspace/settings/members-list.tsx index b4f263c49..2244c5cad 100644 --- a/web/components/workspace/settings/members-list.tsx +++ b/web/components/workspace/settings/members-list.tsx @@ -56,7 +56,7 @@ export const WorkspaceMembersList: FC<{ searchQuery: string }> = observer(({ sea ); return ( -
+
{workspaceMembersWithInvitations.length > 0 ? searchedMembers?.map((member) => ) : null} diff --git a/web/constants/issue.ts b/web/constants/issue.ts index d80430d0f..7979672fd 100644 --- a/web/constants/issue.ts +++ b/web/constants/issue.ts @@ -239,7 +239,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { filters: ["priority", "state_group", "labels", "start_date", "target_date"], display_properties: true, display_filters: { - group_by: ["state_detail.group", "priority", "project", "labels", null], + group_by: ["state_detail.group", "priority", "project", "labels"], order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], type: [null, "active", "backlog"], }, @@ -282,7 +282,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { filters: ["priority", "state_group", "labels", "start_date", "target_date"], display_properties: true, display_filters: { - group_by: ["state_detail.group", "priority", "project", "labels", null], + group_by: ["state_detail.group", "priority", "project", "labels"], order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], type: [null, "active", "backlog"], }, diff --git a/web/types/app.d.ts b/web/types/app.d.ts index d5a7953b1..05f0fc7e5 100644 --- a/web/types/app.d.ts +++ b/web/types/app.d.ts @@ -11,4 +11,6 @@ export interface IAppConfig { slack_client_id: string | null; posthog_api_key: string | null; posthog_host: string | null; + has_openai_configured: boolean; + has_unsplash_configured: boolean; }