forked from github/plane
Merge branch 'develop' of github.com:makeplane/plane into chore/api_endpoints
This commit is contained in:
commit
065fee9420
@ -34,7 +34,7 @@ export const SelectProject: React.FC<Props> = ({ value, onChange, projects }) =>
|
||||
.join(", ")
|
||||
: "All projects"
|
||||
}
|
||||
optionsClassName="min-w-full"
|
||||
optionsClassName="min-w-full max-w-[20rem]"
|
||||
multiple
|
||||
/>
|
||||
);
|
||||
|
@ -92,7 +92,7 @@ export const GptAssistantModal: React.FC<Props> = (props) => {
|
||||
.catch((err) => {
|
||||
const error = err?.data?.error;
|
||||
|
||||
if (err.status === 429)
|
||||
if (err?.status === 429)
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
|
@ -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<IssueFormProps> = (props) => {
|
||||
export const DraftIssueForm: FC<IssueFormProps> = observer((props) => {
|
||||
const {
|
||||
handleFormSubmit,
|
||||
data,
|
||||
@ -100,30 +102,30 @@ export const DraftIssueForm: FC<IssueFormProps> = (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<ISearchIssueResponse | null>(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<any>(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<IssueFormProps> = (props) => {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<GptAssistantModal
|
||||
isOpen={gptAssistantModal}
|
||||
handleClose={() => {
|
||||
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 && (
|
||||
<GptAssistantModal
|
||||
isOpen={gptAssistantModal}
|
||||
handleClose={() => {
|
||||
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}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
@ -623,4 +627,4 @@ export const DraftIssueForm: FC<IssueFormProps> = (props) => {
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
@ -94,28 +94,29 @@ export const IssueForm: FC<IssueFormProps> = 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<ISearchIssueResponse | null>(null);
|
||||
|
||||
const [gptAssistantModal, setGptAssistantModal] = useState(false);
|
||||
const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
|
||||
|
||||
// refs
|
||||
const editorRef = useRef<any>(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<IssueFormProps> = observer((props) => {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<GptAssistantModal
|
||||
isOpen={gptAssistantModal}
|
||||
handleClose={() => {
|
||||
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 && (
|
||||
<GptAssistantModal
|
||||
isOpen={gptAssistantModal}
|
||||
handleClose={() => {
|
||||
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}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
|
@ -225,6 +225,7 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
||||
size="sm"
|
||||
prependIcon={<Bell className="h-3 w-3" />}
|
||||
variant="outline-primary"
|
||||
className="hover:!bg-custom-primary-100/20"
|
||||
onClick={() =>
|
||||
issueSubscription && issueSubscription.subscribed
|
||||
? issueSubscriptionRemove()
|
||||
|
@ -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<Props> = observer((props) => {
|
||||
!issueDetail?.assignees.includes(user?.id ?? "") &&
|
||||
!router.pathname.includes("[archivedIssueId]") &&
|
||||
(fieldsToShow.includes("all") || fieldsToShow.includes("subscribe")) && (
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md flex items-center gap-2 border border-custom-primary-100 px-2 py-1 text-xs text-custom-primary-100 shadow-sm duration-300 focus:outline-none"
|
||||
<Button
|
||||
size="sm"
|
||||
prependIcon={<Bell className="h-3 w-3" />}
|
||||
variant="outline-primary"
|
||||
className="hover:!bg-custom-primary-100/20"
|
||||
onClick={() => {
|
||||
if (subscribed) handleUnsubscribe();
|
||||
else handleSubscribe();
|
||||
}}
|
||||
>
|
||||
<Bell className="h-3.5 w-3.5" />
|
||||
{loading ? "Loading..." : subscribed ? "Unsubscribe" : "Subscribe"}
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
{(fieldsToShow.includes("all") || fieldsToShow.includes("link")) && (
|
||||
<button
|
||||
|
@ -429,10 +429,7 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
<Disclosure>
|
||||
{({ open }) => (
|
||||
<div className={`relative flex h-full w-full flex-col ${open ? "" : "flex-row"}`}>
|
||||
<Disclosure.Button
|
||||
className="flex w-full items-center justify-between gap-2 p-1.5"
|
||||
disabled={!isStartValid || !isEndValid}
|
||||
>
|
||||
<Disclosure.Button className="flex w-full items-center justify-between gap-2 p-1.5">
|
||||
<div className="flex items-center justify-start gap-2 text-sm">
|
||||
<span className="font-medium text-custom-text-200">Links</span>
|
||||
</div>
|
||||
|
@ -168,7 +168,7 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||
onClick: () => {
|
||||
markNotificationReadStatusToggle(notification.id).then(() => {
|
||||
setToastAlert({
|
||||
title: notification.read_at ? "Notification marked as unread" : "Notification marked as read",
|
||||
title: notification.read_at ? "Notification marked as read" : "Notification marked as unread",
|
||||
type: "success",
|
||||
});
|
||||
});
|
||||
|
@ -19,6 +19,7 @@ import { IUser, IPageBlock } from "types";
|
||||
// fetch-keys
|
||||
import { PAGE_BLOCKS_LIST } from "constants/fetch-keys";
|
||||
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
|
||||
type Props = {
|
||||
handleClose: () => void;
|
||||
@ -40,19 +41,24 @@ const pagesService = new PageService();
|
||||
const issueService = new IssueService();
|
||||
const fileService = new FileService();
|
||||
|
||||
export const CreateUpdateBlockInline: FC<Props> = ({ handleClose, data, handleAiAssistance, setIsSyncing, focus }) => {
|
||||
export const CreateUpdateBlockInline: FC<Props> = (props) => {
|
||||
const { handleClose, data, handleAiAssistance, setIsSyncing, focus } = props;
|
||||
// states
|
||||
const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
|
||||
const [gptAssistantModal, setGptAssistantModal] = useState(false);
|
||||
|
||||
// store
|
||||
const {
|
||||
appConfig: { envConfig },
|
||||
} = useMobxStore();
|
||||
// refs
|
||||
const editorRef = useRef<any>(null);
|
||||
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, pageId } = router.query;
|
||||
|
||||
// hooks
|
||||
const editorSuggestion = useEditorSuggestions();
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
// form info
|
||||
const {
|
||||
handleSubmit,
|
||||
control,
|
||||
@ -222,9 +228,7 @@ export const CreateUpdateBlockInline: FC<Props> = ({ handleClose, data, handleAi
|
||||
else handleSubmit(createPageBlock)();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", submitForm);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", submitForm);
|
||||
};
|
||||
@ -345,26 +349,28 @@ export const CreateUpdateBlockInline: FC<Props> = ({ handleClose, data, handleAi
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
<GptAssistantModal
|
||||
block={data ? data : undefined}
|
||||
isOpen={gptAssistantModal}
|
||||
handleClose={() => 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")}<p>${response}</p>` ?? "");
|
||||
} else {
|
||||
setValue("description", {});
|
||||
setValue("description_html", `${watch("description_html")}<p>${response}</p>`);
|
||||
{envConfig?.has_openai_configured && (
|
||||
<GptAssistantModal
|
||||
block={data ? data : undefined}
|
||||
isOpen={gptAssistantModal}
|
||||
handleClose={() => 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")}<p>${response}</p>` ?? "");
|
||||
} else {
|
||||
setValue("description", {});
|
||||
setValue("description_html", `${watch("description_html")}<p>${response}</p>`);
|
||||
|
||||
editorRef.current?.setEditorValue(watch("description_html") ?? "");
|
||||
}
|
||||
}}
|
||||
projectId={projectId?.toString() ?? ""}
|
||||
/>
|
||||
editorRef.current?.setEditorValue(watch("description_html") ?? "");
|
||||
}
|
||||
}}
|
||||
projectId={projectId?.toString() ?? ""}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -113,7 +113,7 @@ export const ProjectCard: React.FC<ProjectCardProps> = observer((props) => {
|
||||
className="absolute top-0 left-0 h-full w-full object-cover rounded-t"
|
||||
/>
|
||||
|
||||
<div className="absolute h-10 w-full bottom-4 z-10 flex items-center justify-between px-4">
|
||||
<div className="absolute h-10 w-full bottom-4 z-10 flex items-center justify-between gap-3 px-4">
|
||||
<div className="flex items-center gap-2.5 flex-grow truncate">
|
||||
<div className="h-9 w-9 flex item-center justify-center rounded bg-white/90 flex-shrink-0">
|
||||
<span className="flex items-center justify-center">
|
||||
|
@ -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<Props> = observer((props) => {
|
||||
) : (
|
||||
<h4 className="text-sm cursor-default">{member.display_name || member.email}</h4>
|
||||
)}
|
||||
<p className="mt-0.5 text-xs text-custom-sidebar-text-300">{member.email ?? member.display_name}</p>
|
||||
<div className="flex items-center">
|
||||
<p className="text-xs text-custom-text-300">{member.display_name}</p>
|
||||
{isAdmin && (
|
||||
<>
|
||||
<Dot height={16} width={16} className="text-custom-text-300" />
|
||||
<p className="text-xs text-custom-text-300">{member.email}</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -23,7 +23,10 @@ export const IssuesStats: React.FC<Props> = ({ data }) => {
|
||||
<h4 className="text-sm">Issues assigned to you</h4>
|
||||
<h5 className="mt-2 text-2xl font-semibold">
|
||||
{data ? (
|
||||
<div className="cursor-pointer" onClick={() => router.push(`/${workspaceSlug}/me/my-issues`)}>
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => router.push(`/${workspaceSlug}/workspace-views/assigned`)}
|
||||
>
|
||||
{data.assigned_issues_count}
|
||||
</div>
|
||||
) : (
|
||||
|
@ -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> = (props) => {
|
||||
) : (
|
||||
<h4 className="text-sm cursor-default">{member.display_name || member.email}</h4>
|
||||
)}
|
||||
<p className="mt-0.5 text-xs text-custom-sidebar-text-300">{member.email ?? member.display_name}</p>
|
||||
<div className="flex items-center">
|
||||
<p className="text-xs text-custom-text-300">{member.display_name}</p>
|
||||
{isAdmin && (
|
||||
<>
|
||||
<Dot height={16} width={16} className="text-custom-text-300" />
|
||||
<p className="text-xs text-custom-text-300">{member.email}</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
|
@ -56,7 +56,7 @@ export const WorkspaceMembersList: FC<{ searchQuery: string }> = observer(({ sea
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="divide-y-[0.5px] divide-custom-border-200">
|
||||
<div className="divide-y-[0.5px] divide-custom-border-100">
|
||||
{workspaceMembersWithInvitations.length > 0
|
||||
? searchedMembers?.map((member) => <WorkspaceMembersListItem key={member.id} member={member} />)
|
||||
: null}
|
||||
|
@ -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"],
|
||||
},
|
||||
|
2
web/types/app.d.ts
vendored
2
web/types/app.d.ts
vendored
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user