Compare commits

...

12 Commits

Author SHA1 Message Date
Anmol Singh Bhatia
eec73904a0 style: tip tap editor shadow fix 2023-08-18 10:52:49 +05:30
Anmol Singh Bhatia
bec5b93a68 Merge branch 'develop' of github.com:makeplane/plane into fix/date_select_dropdown 2023-08-17 18:13:30 +05:30
guru_sainath
abb8782c44 fix: handled default view on plane deploy (#1893)
* fix: handled default view on plane deploy

* fix: handled default view on refresh
2023-08-17 14:24:33 +05:30
guru_sainath
0afd72db95 fix: updated theming in workspace preferences (#1890) 2023-08-16 20:35:17 +05:30
guru_sainath
65295f6c6f chore: updating the theme using MobX from command k (#1879)
* chore: updating the theme using mobx from command k

* feat: Showing the project published status in the app header

* dev: updated validation and redirection the project publish modal and added redirection on the app header
2023-08-16 18:26:36 +05:30
Aaryan Khandelwal
5b6b43fb83 fix: quick action buttons: (#1884) 2023-08-16 18:25:11 +05:30
Dakshesh Jain
f8497125db style: fixed display name coming twice on profile page (#1889) 2023-08-16 18:18:57 +05:30
Anmol Singh Bhatia
06339106ed style: date picker button theming and issue sidebar date select width adjustment 2023-08-16 17:46:43 +05:30
Anmol Singh Bhatia
5dfd59b3fc style: create issue modal label 2023-08-16 17:15:17 +05:30
Anmol Singh Bhatia
d05fd63e8b style: create issue modal assignee 2023-08-16 17:09:29 +05:30
Anmol Singh Bhatia
37185271a2 style: kanban card height and padding 2023-08-16 16:29:34 +05:30
Anmol Singh Bhatia
1c5d1542e8 fix: kanban card date select dropdown fix 2023-08-16 14:11:07 +05:30
27 changed files with 306 additions and 164 deletions

View File

@@ -9,12 +9,18 @@ import userService from "services/user.service";
import useUser from "hooks/use-user";
// helper
import { unsetCustomCssVariables } from "helpers/theme.helper";
// mobx react lite
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
type Props = {
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
};
export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => {
export const ChangeInterfaceTheme: React.FC<Props> = observer(({ setIsPaletteOpen }) => {
const store: any = useMobxStore();
const [mounted, setMounted] = useState(false);
const { setTheme } = useTheme();
@@ -23,29 +29,11 @@ export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => {
const updateUserTheme = (newTheme: string) => {
if (!user) return;
unsetCustomCssVariables();
setTheme(newTheme);
mutateUser((prevData: any) => {
if (!prevData) return prevData;
return {
...prevData,
theme: {
...prevData?.theme,
theme: newTheme,
},
};
}, false);
userService.updateUser({
theme: {
...user.theme,
theme: newTheme,
},
});
return store.user
.updateCurrentUserSettings({ theme: { ...user.theme, theme: newTheme } })
.then((response: any) => response)
.catch((error: any) => error);
};
// useEffect only runs on the client, so now we can safely show the UI
@@ -74,4 +62,4 @@ export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => {
))}
</>
);
};
});

View File

@@ -145,7 +145,7 @@ export const GptAssistantModal: React.FC<Props> = ({
}`}
>
{((content && content !== "") || (htmlContent && htmlContent !== "<p></p>")) && (
<div id="tiptap-container" className="text-sm">
<div className="text-sm">
Content:
<TiptapEditor
value={htmlContent ?? `<p>${content}</p>`}

View File

@@ -45,18 +45,26 @@ export const ThemeSwitch: React.FC<Props> = observer(
currentThemeObj ? (
<div className="flex items-center gap-2">
<div
className="h-full w-1/2 rounded-l-full"
className="border-1 relative flex h-4 w-4 rotate-45 transform items-center justify-center rounded-full border"
style={{
background: currentThemeObj.icon.color1,
borderColor: currentThemeObj.icon.border,
}}
/>
<div
className="h-full w-1/2 rounded-r-full border-l"
style={{
borderLeftColor: currentThemeObj.icon.border,
background: currentThemeObj.icon.color2,
}}
/>
>
<div
className="h-full w-1/2 rounded-l-full"
style={{
background: currentThemeObj.icon.color1,
}}
/>
<div
className="h-full w-1/2 rounded-r-full border-l"
style={{
borderLeftColor: currentThemeObj.icon.border,
background: currentThemeObj.icon.color2,
}}
/>
</div>
{currentThemeObj.label}
</div>
) : (
"Select your theme"
@@ -98,18 +106,26 @@ export const ThemeSwitch: React.FC<Props> = observer(
<CustomSelect.Option key={value} value={{ value, type }}>
<div className="flex items-center gap-2">
<div
className="h-full w-1/2 rounded-l-full"
className="border-1 relative flex h-4 w-4 rotate-45 transform items-center justify-center rounded-full border"
style={{
background: icon.color1,
borderColor: icon.border,
}}
/>
<div
className="h-full w-1/2 rounded-r-full border-l"
style={{
borderLeftColor: icon.border,
background: icon.color2,
}}
/>
>
<div
className="h-full w-1/2 rounded-l-full"
style={{
background: icon.color1,
}}
/>
<div
className="h-full w-1/2 rounded-r-full border-l"
style={{
borderLeftColor: icon.border,
background: icon.color2,
}}
/>
</div>
{label}
</div>
</CustomSelect.Option>
))}

View File

@@ -88,6 +88,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
const [contextMenu, setContextMenu] = useState(false);
const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 });
const [isMenuActive, setIsMenuActive] = useState(false);
const [isDropdownActive, setIsDropdownActive] = useState(false);
const actionSectionRef = useRef<HTMLDivElement | null>(null);
@@ -245,7 +246,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
setContextMenuPosition({ x: e.pageX, y: e.pageY });
}}
>
<div className="group/card relative select-none p-3.5">
<div className="flex flex-col justify-between gap-1.5 group/card relative select-none px-3.5 py-3 h-[118px]">
{!isNotAllowed && (
<div
ref={actionSectionRef}
@@ -295,16 +296,20 @@ export const SingleBoardIssue: React.FC<Props> = ({
</div>
)}
<Link href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}>
<a>
<a className="flex flex-col gap-1.5">
{properties.key && (
<div className="mb-2.5 text-xs font-medium text-custom-text-200">
<div className="text-xs font-medium text-custom-text-200">
{issue.project_detail.identifier}-{issue.sequence_id}
</div>
)}
<h5 className="text-sm break-words line-clamp-2">{issue.name}</h5>
</a>
</Link>
<div className="mt-2.5 flex overflow-x-scroll items-center gap-2 text-xs">
<div
className={`flex items-center gap-2 text-xs ${
isDropdownActive ? "" : "overflow-x-scroll"
}`}
>
{properties.priority && (
<ViewPrioritySelect
issue={issue}
@@ -327,6 +332,8 @@ export const SingleBoardIssue: React.FC<Props> = ({
<ViewStartDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
handleOnOpen={() => setIsDropdownActive(true)}
handleOnClose={() => setIsDropdownActive(false)}
user={user}
isNotAllowed={isNotAllowed}
/>
@@ -335,6 +342,8 @@ export const SingleBoardIssue: React.FC<Props> = ({
<ViewDueDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
handleOnOpen={() => setIsDropdownActive(true)}
handleOnClose={() => setIsDropdownActive(false)}
user={user}
isNotAllowed={isNotAllowed}
/>

View File

@@ -87,7 +87,7 @@ export const AddComment: React.FC<Props> = ({ issueId, user, disabled = false })
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<div id="tiptap-container" className="issue-comments-section">
<div className="issue-comments-section">
<Controller
name="comment_html"
control={control}
@@ -101,7 +101,7 @@ export const AddComment: React.FC<Props> = ({ issueId, user, disabled = false })
? watch("comment_html")
: value
}
customClassName="p-3 min-h-[50px]"
customClassName="p-3 min-h-[50px] shadow-sm"
debouncedUpdatesEnabled={false}
onChange={(comment_json: Object, comment_html: string) => {
onChange(comment_html);

View File

@@ -101,12 +101,12 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
className={`flex-col gap-2 ${isEditing ? "flex" : "hidden"}`}
onSubmit={handleSubmit(onEnter)}
>
<div id="tiptap-container">
<div>
<TiptapEditor
ref={editorRef}
value={watch("comment_html")}
debouncedUpdatesEnabled={false}
customClassName="min-h-[50px] p-3"
customClassName="min-h-[50px] p-3 shadow-sm"
onChange={(comment_json: Object, comment_html: string) => {
setValue("comment_json", comment_json);
setValue("comment_html", comment_html);

View File

@@ -112,8 +112,9 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
{characterLimit && (
<div className="pointer-events-none absolute bottom-1 right-1 z-[2] rounded bg-custom-background-100 text-custom-text-200 p-0.5 text-xs">
<span
className={`${watch("name").length === 0 || watch("name").length > 255 ? "text-red-500" : ""
}`}
className={`${
watch("name").length === 0 || watch("name").length > 255 ? "text-red-500" : ""
}`}
>
{watch("name").length}
</span>
@@ -122,7 +123,7 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
)}
</div>
<span>{errors.name ? errors.name.message : null}</span>
<div id="tiptap-container" className="relative">
<div className="relative">
<Controller
name="description_html"
control={control}
@@ -133,14 +134,14 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
<Tiptap
value={
!value ||
value === "" ||
(typeof value === "object" && Object.keys(value).length === 0)
value === "" ||
(typeof value === "object" && Object.keys(value).length === 0)
? watch("description_html")
: value
}
debouncedUpdatesEnabled={true}
setIsSubmitting={setIsSubmitting}
customClassName="min-h-[150px]"
customClassName="min-h-[150px] shadow-sm"
editorContentCustomClassNames="pb-9"
onChange={(description: Object, description_html: string) => {
setIsSubmitting("submitting");
@@ -154,8 +155,12 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
);
}}
/>
<div className={`absolute right-5 bottom-5 text-xs text-custom-text-200 border border-custom-border-400 rounded-xl w-[6.5rem] py-1 z-10 flex items-center justify-center ${isSubmitting === 'saved' ? 'fadeOut' : 'fadeIn'}`}>
{isSubmitting === 'submitting' ? 'Saving...' : 'Saved'}
<div
className={`absolute right-5 bottom-5 text-xs text-custom-text-200 border border-custom-border-400 rounded-xl w-[6.5rem] py-1 z-10 flex items-center justify-center ${
isSubmitting === "saved" ? "fadeOut" : "fadeIn"
}`}
>
{isSubmitting === "submitting" ? "Saving..." : "Saved"}
</div>
</div>
</div>

View File

@@ -333,7 +333,7 @@ export const IssueForm: FC<IssueFormProps> = ({
</div>
)}
{(fieldsToShow.includes("all") || fieldsToShow.includes("description")) && (
<div id="tiptap-container" className="relative">
<div className="relative">
<div className="flex justify-end">
{issueName && issueName !== "" && (
<button

View File

@@ -5,9 +5,7 @@ import useSWR from "swr";
// services
import projectServices from "services/project.service";
// ui
import { AssigneesList, Avatar, CustomSearchSelect } from "components/ui";
// icons
import { UserGroupIcon } from "@heroicons/react/24/outline";
import { AssigneesList, Avatar, CustomSearchSelect, Icon } from "components/ui";
// fetch-keys
import { PROJECT_MEMBERS } from "constants/fetch-keys";
@@ -44,15 +42,15 @@ export const IssueAssigneeSelect: React.FC<Props> = ({ projectId, value = [], on
value={value}
onChange={onChange}
options={options}
label={
<div className="flex items-center gap-2 text-custom-text-200">
customButton={
<div className="flex items-center gap-2 cursor-pointer text-xs text-custom-text-200">
{value && value.length > 0 && Array.isArray(value) ? (
<div className="flex items-center justify-center gap-2">
<div className="-my-0.5 flex items-center justify-center gap-2">
<AssigneesList userIds={value} length={3} showLength={true} />
</div>
) : (
<div className="flex items-center justify-center gap-2">
<UserGroupIcon className="h-4 w-4 text-custom-text-200" />
<div className="flex items-center justify-center gap-2 px-1.5 py-1 rounded shadow-sm border border-custom-border-300">
<Icon iconName="person" className="!text-base !leading-4" />
<span className="text-custom-text-200">Assignee</span>
</div>
)}

View File

@@ -59,9 +59,9 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
>
{({ open }: any) => (
<>
<Combobox.Button className="flex cursor-pointer items-center rounded-md border border-custom-border-200 text-xs shadow-sm duration-200 hover:bg-custom-background-80">
<Combobox.Button className="flex cursor-pointer items-center text-xs">
{value && value.length > 0 ? (
<span className="flex items-center justify-center gap-2 px-3 py-1 text-xs">
<span className="flex items-center justify-center gap-2 px-2 py-1 text-xs">
<IssueLabelsList
labels={value.map((v) => issueLabels?.find((l) => l.id === v)?.color) ?? []}
length={3}
@@ -69,7 +69,7 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
/>
</span>
) : (
<span className="flex items-center justify-center gap-2 px-2.5 py-1 text-xs">
<span className="flex items-center justify-center gap-2 px-2.5 py-1 text-xs rounded-md border border-custom-border-200 shadow-sm duration-200 hover:bg-custom-background-80">
<TagIcon className="h-3.5 w-3.5 text-custom-text-200" />
<span className=" text-custom-text-200">Label</span>
</span>

View File

@@ -396,7 +396,8 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
start_date: val,
})
}
className="bg-custom-background-90 w-full"
className="bg-custom-background-100"
wrapperClassName="w-full"
maxDate={maxDate ?? undefined}
disabled={isNotAllowed || uneditable}
/>
@@ -424,7 +425,8 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
target_date: val,
})
}
className="bg-custom-background-90 w-full"
className="bg-custom-background-100"
wrapperClassName="w-full"
minDate={minDate ?? undefined}
disabled={isNotAllowed || uneditable}
/>

View File

@@ -13,6 +13,8 @@ import useIssuesView from "hooks/use-issues-view";
type Props = {
issue: IIssue;
partialUpdateIssue: (formData: Partial<IIssue>, issue: IIssue) => void;
handleOnOpen?: () => void;
handleOnClose?: () => void;
tooltipPosition?: "top" | "bottom";
noBorder?: boolean;
user: ICurrentUserResponse | undefined;
@@ -22,6 +24,8 @@ type Props = {
export const ViewDueDateSelect: React.FC<Props> = ({
issue,
partialUpdateIssue,
handleOnOpen,
handleOnClose,
tooltipPosition = "top",
noBorder = false,
user,
@@ -80,6 +84,8 @@ export const ViewDueDateSelect: React.FC<Props> = ({
}`}
minDate={minDate ?? undefined}
noBorder={noBorder}
handleOnOpen={handleOnOpen}
handleOnClose={handleOnClose}
disabled={isNotAllowed}
/>
</div>

View File

@@ -13,6 +13,8 @@ import useIssuesView from "hooks/use-issues-view";
type Props = {
issue: IIssue;
partialUpdateIssue: (formData: Partial<IIssue>, issue: IIssue) => void;
handleOnOpen?: () => void;
handleOnClose?: () => void;
tooltipPosition?: "top" | "bottom";
noBorder?: boolean;
user: ICurrentUserResponse | undefined;
@@ -22,6 +24,8 @@ type Props = {
export const ViewStartDateSelect: React.FC<Props> = ({
issue,
partialUpdateIssue,
handleOnOpen,
handleOnClose,
tooltipPosition = "top",
noBorder = false,
user,
@@ -72,6 +76,8 @@ export const ViewStartDateSelect: React.FC<Props> = ({
}`}
maxDate={maxDate ?? undefined}
noBorder={noBorder}
handleOnOpen={handleOnOpen}
handleOnClose={handleOnClose}
disabled={isNotAllowed}
/>
</div>

View File

@@ -284,10 +284,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
maxLength={255}
/>
</div>
<div
id="tiptap-container"
className="page-block-section relative -mt-2 text-custom-text-200"
>
<div className="page-block-section relative -mt-2 text-custom-text-200">
<Controller
name="description_html"
control={control}

View File

@@ -105,9 +105,11 @@ export const ProfileSidebar = () => {
</div>
<div className="px-5">
<div className="mt-[38px]">
<h4 className="text-lg font-semibold">{userProjectsData.user_data.display_name}</h4>
<h4 className="text-lg font-semibold">
{userProjectsData.user_data.first_name} {userProjectsData.user_data.last_name}
</h4>
<h6 className="text-custom-text-200 text-sm">
{userProjectsData.user_data.display_name}
({userProjectsData.user_data.display_name})
</h6>
</div>
<div className="mt-6 space-y-5">

View File

@@ -14,6 +14,9 @@ import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
import { RootStore } from "store/root";
import { IProjectPublishSettingsViews } from "store/project-publish";
// hooks
import useToast from "hooks/use-toast";
import useProjectDetails from "hooks/use-project-details";
type Props = {
// user: ICurrentUserResponse | undefined;
@@ -25,7 +28,7 @@ const defaultValues: Partial<any> = {
reactions: false,
votes: false,
inbox: null,
views: [],
views: ["list", "kanban"],
};
const viewOptions = [
@@ -40,6 +43,17 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
const store: RootStore = useMobxStore();
const { projectPublish } = store;
const { projectDetails, mutateProjectDetails } = useProjectDetails();
const { setToastAlert } = useToast();
const handleToastAlert = (title: string, type: string, message: string) => {
setToastAlert({
title: title || "Title",
type: "error" || "warning",
message: message || "Message",
});
};
const { NEXT_PUBLIC_DEPLOY_URL } = process.env;
const plane_deploy_url = NEXT_PUBLIC_DEPLOY_URL
? NEXT_PUBLIC_DEPLOY_URL
@@ -111,32 +125,41 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
}, [workspaceSlug, projectPublish, projectPublish.projectPublishModal]);
const onSettingsPublish = async (formData: any) => {
const payload = {
comments: formData.comments || false,
reactions: formData.reactions || false,
votes: formData.votes || false,
inbox: formData.inbox || null,
views: {
list: formData.views.includes("list") || false,
kanban: formData.views.includes("kanban") || false,
calendar: formData.views.includes("calendar") || false,
gantt: formData.views.includes("gantt") || false,
spreadsheet: formData.views.includes("spreadsheet") || false,
},
};
if (formData.views && formData.views.length > 0) {
const payload = {
comments: formData.comments || false,
reactions: formData.reactions || false,
votes: formData.votes || false,
inbox: formData.inbox || null,
views: {
list: formData.views.includes("list") || false,
kanban: formData.views.includes("kanban") || false,
calendar: formData.views.includes("calendar") || false,
gantt: formData.views.includes("gantt") || false,
spreadsheet: formData.views.includes("spreadsheet") || false,
},
};
return projectPublish
.createProjectSettingsAsync(
workspaceSlug as string,
projectPublish.project_id as string,
payload,
null
)
.then((response) => response)
.catch((error) => {
console.error("error", error);
return error;
});
const _workspaceSlug = workspaceSlug;
const _projectId = projectPublish.project_id;
return projectPublish
.createProjectSettingsAsync(_workspaceSlug as string, _projectId as string, payload, null)
.then((response) => {
mutateProjectDetails();
handleClose();
console.log("_projectId", _projectId);
if (_projectId)
window.open(`${plane_deploy_url}/${_workspaceSlug}/${_projectId}`, "_blank");
return response;
})
.catch((error) => {
console.error("error", error);
return error;
});
} else {
handleToastAlert("Missing fields", "warning", "Please select at least one view to publish");
}
};
const onSettingsUpdate = async (key: string, value: any) => {
@@ -171,7 +194,10 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
payload,
null
)
.then((response) => response)
.then((response) => {
mutateProjectDetails();
return response;
})
.catch((error) => {
console.log("error", error);
return error;
@@ -187,7 +213,9 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
null
)
.then((response) => {
mutateProjectDetails();
reset({ ...defaultValues });
handleClose();
return response;
})
.catch((error) => {

View File

@@ -97,10 +97,14 @@ export const EditorBubbleMenu: FC<EditorBubbleMenuProps> = (props) => {
{items.map((item, index) => (
<button
key={index}
type="button"
onClick={item.command}
className={cn("p-2 text-custom-text-300 hover:bg-custom-primary-100/5 active:bg-custom-primary-100/5 transition-colors", {
"text-custom-text-100 bg-custom-primary-100/5": item.isActive(),
})}
className={cn(
"p-2 text-custom-text-300 hover:bg-custom-primary-100/5 active:bg-custom-primary-100/5 transition-colors",
{
"text-custom-text-100 bg-custom-primary-100/5": item.isActive(),
}
)}
>
<item.icon
className={cn("h-4 w-4", {

View File

@@ -19,7 +19,11 @@ export const LinkSelector: FC<LinkSelectorProps> = ({ editor, isOpen, setIsOpen
return (
<div className="relative">
<button
className={cn("flex h-full items-center space-x-2 px-3 py-1.5 text-sm font-medium text-custom-text-300 hover:bg-custom-background-100 active:bg-custom-background-100", { "bg-custom-background-100": isOpen })}
type="button"
className={cn(
"flex h-full items-center space-x-2 px-3 py-1.5 text-sm font-medium text-custom-text-300 hover:bg-custom-background-100 active:bg-custom-background-100",
{ "bg-custom-background-100": isOpen }
)}
onClick={() => {
setIsOpen(!isOpen);
}}
@@ -48,7 +52,7 @@ export const LinkSelector: FC<LinkSelectorProps> = ({ editor, isOpen, setIsOpen
ref={inputRef}
type="url"
placeholder="Paste a link"
className="flex-1 bg-custom-background-100 border border-custom-primary-300 p-1 text-sm outline-none placeholder:text-custom-text-400"
className="flex-1 bg-custom-background-100 border-r border-custom-border-300 p-1 text-sm outline-none placeholder:text-custom-text-400"
defaultValue={editor.getAttributes("link").href || ""}
/>
{editor.getAttributes("link").href ? (

View File

@@ -91,8 +91,9 @@ export const NodeSelector: FC<NodeSelectorProps> = ({ editor, isOpen, setIsOpen
return (
<div className="relative h-full">
<button
className="flex h-full items-center gap-1 whitespace-nowrap p-2 text-sm font-medium text-custom-text-300 hover:bg-custom-primary-100/5 active:bg-custom-primary-100/5"
type="button"
onClick={() => setIsOpen(!isOpen)}
className="flex h-full items-center gap-1 whitespace-nowrap p-2 text-sm font-medium text-custom-text-300 hover:bg-custom-primary-100/5 active:bg-custom-primary-100/5"
>
<span>{activeItem?.name}</span>
<ChevronDown className="h-4 w-4" />
@@ -103,11 +104,15 @@ export const NodeSelector: FC<NodeSelectorProps> = ({ editor, isOpen, setIsOpen
{items.map((item, index) => (
<button
key={index}
type="button"
onClick={() => {
item.command();
setIsOpen(false);
}}
className={cn("flex items-center justify-between rounded-sm px-2 py-1 text-sm text-custom-text-200 hover:bg-custom-primary-100/5 hover:text-custom-text-100", { "bg-custom-primary-100/5 text-custom-text-100": activeItem.name === item.name })}
className={cn(
"flex items-center justify-between rounded-sm px-2 py-1 text-sm text-custom-text-200 hover:bg-custom-primary-100/5 hover:text-custom-text-100",
{ "bg-custom-primary-100/5 text-custom-text-100": activeItem.name === item.name }
)}
>
<div className="flex items-center space-x-2">
<div className="rounded-sm border border-custom-border-300 p-1">

View File

@@ -112,20 +112,21 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
}, 500);
}, 1000);
const editorClassNames = `relative w-full max-w-screen-lg sm:rounded-lg sm:shadow-lg mt-2 p-3 relative focus:outline-none rounded-md
${noBorder ? '' : 'border border-custom-border-200'
} ${borderOnFocus ? 'focus:border border-custom-border-300' : 'focus:border-0'
} ${customClassName}`;
const editorClassNames = `relative w-full max-w-screen-lg mt-2 p-3 relative focus:outline-none rounded-lg
${noBorder ? "" : "border border-custom-border-200"} ${
borderOnFocus ? "focus:border border-custom-border-300" : "focus:border-0"
} ${customClassName}`;
if (!editor) return null;
editorRef.current = editor;
return (
<div
id="tiptap-container"
onClick={() => {
editor?.chain().focus().run();
}}
className={`tiptap-editor-container ${editorClassNames}`}
className={`tiptap-editor-container cursor-text ${editorClassNames}`}
>
{editor && <EditorBubbleMenu editor={editor} />}
<div className={`${editorContentCustomClassNames}`}>

View File

@@ -264,7 +264,10 @@ const CommandList = ({
>
{items.map((item: CommandItemProps, index: number) => (
<button
className={cn(`flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm text-custom-text-200 hover:bg-custom-primary-100/5 hover:text-custom-text-100`, { "bg-custom-primary-100/5 text-custom-text-100": index === selectedIndex })}
className={cn(
`flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm text-custom-text-200 hover:bg-custom-primary-100/5 hover:text-custom-text-100`,
{ "bg-custom-primary-100/5 text-custom-text-100": index === selectedIndex }
)}
key={index}
onClick={() => selectItem(index)}
>
@@ -282,8 +285,6 @@ const renderItems = () => {
let component: ReactRenderer | null = null;
let popup: any | null = null;
const container = document.querySelector("#tiptap-container") as HTMLElement;
return {
onStart: (props: { editor: Editor; clientRect: DOMRect }) => {
component = new ReactRenderer(CommandList, {
@@ -294,7 +295,7 @@ const renderItems = () => {
// @ts-ignore
popup = tippy("body", {
getReferenceClientRect: props.clientRect,
appendTo: () => container,
appendTo: () => document.querySelector("#tiptap-container"),
content: component.element,
showOnCreate: true,
interactive: true,

View File

@@ -8,10 +8,13 @@ type Props = {
renderAs?: "input" | "button";
value: Date | string | null | undefined;
onChange: (val: string | null) => void;
handleOnOpen?: () => void;
handleOnClose?: () => void;
placeholder?: string;
displayShortForm?: boolean;
error?: boolean;
noBorder?: boolean;
wrapperClassName?: string;
className?: string;
isClearable?: boolean;
disabled?: boolean;
@@ -23,10 +26,13 @@ export const CustomDatePicker: React.FC<Props> = ({
renderAs = "button",
value,
onChange,
handleOnOpen,
handleOnClose,
placeholder = "Select date",
displayShortForm = false,
error = false,
noBorder = false,
wrapperClassName = "",
className = "",
isClearable = true,
disabled = false,
@@ -40,6 +46,9 @@ export const CustomDatePicker: React.FC<Props> = ({
if (!val) onChange(null);
else onChange(renderDateFormat(val));
}}
onCalendarOpen={handleOnOpen}
onCalendarClose={handleOnClose}
wrapperClassName={wrapperClassName}
className={`${
renderAs === "input"
? "block px-2 py-2 text-sm focus:outline-none"

View File

@@ -1,5 +1,12 @@
// next imports
import { useRouter } from "next/router";
import Link from "next/link";
// icons
import { Bars3Icon } from "@heroicons/react/24/outline";
// ui components
import { Tooltip } from "components/ui";
// hooks
import useProjectDetails from "hooks/use-project-details";
type Props = {
breadcrumbs?: JSX.Element;
@@ -9,27 +16,61 @@ type Props = {
noHeader: boolean;
};
const Header: React.FC<Props> = ({ breadcrumbs, left, right, setToggleSidebar, noHeader }) => (
<div
className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 px-5 py-4 ${
noHeader ? "md:hidden" : ""
}`}
>
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
<div className="block md:hidden">
<button
type="button"
className="grid h-8 w-8 place-items-center rounded border border-custom-border-200"
onClick={() => setToggleSidebar((prevData) => !prevData)}
>
<Bars3Icon className="h-5 w-5" />
</button>
const { NEXT_PUBLIC_DEPLOY_URL } = process.env;
const plane_deploy_url = NEXT_PUBLIC_DEPLOY_URL ? NEXT_PUBLIC_DEPLOY_URL : "http://localhost:3001";
const Header: React.FC<Props> = ({ breadcrumbs, left, right, setToggleSidebar, noHeader }) => {
const { projectDetails } = useProjectDetails();
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
return (
<div
className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 px-5 py-4 ${
noHeader ? "md:hidden" : ""
}`}
>
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
<div className="block md:hidden">
<button
type="button"
className="grid h-8 w-8 place-items-center rounded border border-custom-border-200"
onClick={() => setToggleSidebar((prevData) => !prevData)}
>
<Bars3Icon className="h-5 w-5" />
</button>
</div>
<div>{breadcrumbs}</div>
{projectDetails && projectDetails?.is_deployed && (
<Link href={`${plane_deploy_url}/${workspaceSlug}/${projectId}`}>
<a target="_blank" rel="noreferrer">
<Tooltip
tooltipContent="This project is public, and live on web."
position="bottom-left"
>
<div className="transition-all flex-shrink-0 bg-custom-primary-100/20 text-custom-primary-100 p-1 rounded overflow-hidden relative flex items-center gap-1 cursor-pointer group">
<div className="w-[14px] h-[14px] flex justify-center items-center">
<span className="material-symbols-rounded text-[14px]">
radio_button_checked
</span>
</div>
<div className="text-xs font-medium">Public</div>
<div className="w-[14px] h-[14px] hidden group-hover:flex justify-center items-center">
<span className="material-symbols-rounded text-[14px]">open_in_new</span>
</div>
</div>
</Tooltip>
</a>
</Link>
)}
<div className="flex-shrink-0">{left}</div>
</div>
{breadcrumbs}
<div className="flex-shrink-0">{left}</div>
<div className="flex-shrink-0">{right}</div>
</div>
<div className="flex-shrink-0">{right}</div>
</div>
);
);
};
export default Header;

View File

@@ -259,15 +259,13 @@ class ProjectPublishStore implements IProjectPublishStore {
user
);
if (response) {
runInAction(() => {
this.projectPublishSettings = "not-initialized";
this.loader = false;
this.error = null;
});
runInAction(() => {
this.projectPublishSettings = "not-initialized";
this.loader = false;
this.error = null;
});
return response;
}
return response;
} catch (error) {
this.loader = false;
this.error = error;

View File

@@ -57,6 +57,7 @@ export interface IProject {
updated_by: string;
workspace: IWorkspace | string;
workspace_detail: IWorkspaceLite;
is_deployed: boolean;
}
export interface IProjectLite {

View File

@@ -29,15 +29,41 @@ const WorkspaceProjectPage = observer(() => {
// updating default board view when we are in the issues page
useEffect(() => {
if (workspace_slug && project_slug) {
if (!board) {
store.issue.setCurrentIssueBoardView("list");
router.replace(`/${workspace_slug}/${project_slug}?board=${store?.issue?.currentIssueBoardView}`);
} else {
if (board != store?.issue?.currentIssueBoardView) store.issue.setCurrentIssueBoardView(board);
if (workspace_slug && project_slug && store?.project?.workspaceProjectSettings) {
const workspacePRojectSettingViews = store?.project?.workspaceProjectSettings?.views;
const userAccessViews: TIssueBoardKeys[] = [];
Object.keys(workspacePRojectSettingViews).filter((_key) => {
if (_key === "list" && workspacePRojectSettingViews.list === true) userAccessViews.push(_key);
if (_key === "kanban" && workspacePRojectSettingViews.kanban === true) userAccessViews.push(_key);
if (_key === "calendar" && workspacePRojectSettingViews.calendar === true) userAccessViews.push(_key);
if (_key === "spreadsheet" && workspacePRojectSettingViews.spreadsheet === true) userAccessViews.push(_key);
if (_key === "gantt" && workspacePRojectSettingViews.gantt === true) userAccessViews.push(_key);
});
if (userAccessViews && userAccessViews.length > 0) {
if (!board) {
store.issue.setCurrentIssueBoardView(userAccessViews[0]);
router.replace(`/${workspace_slug}/${project_slug}?board=${userAccessViews[0]}`);
} else {
if (userAccessViews.includes(board)) {
if (store.issue.currentIssueBoardView === null) store.issue.setCurrentIssueBoardView(board);
else {
if (board === store.issue.currentIssueBoardView)
router.replace(`/${workspace_slug}/${project_slug}?board=${board}`);
else {
store.issue.setCurrentIssueBoardView(board);
router.replace(`/${workspace_slug}/${project_slug}?board=${board}`);
}
}
} else {
store.issue.setCurrentIssueBoardView(userAccessViews[0]);
router.replace(`/${workspace_slug}/${project_slug}?board=${userAccessViews[0]}`);
}
}
}
}
}, [workspace_slug, project_slug, board, router, store?.issue]);
}, [workspace_slug, project_slug, board, router, store?.issue, store?.project?.workspaceProjectSettings]);
useEffect(() => {
if (workspace_slug && project_slug) {

View File

@@ -24,11 +24,6 @@ const MobxStoreInit = () => {
else localStorage.setItem("app_theme", _theme && _theme != "light" ? "dark" : "light");
}, [store?.theme]);
// updating default board view when we are in the issues page
useEffect(() => {
if (board && board != store?.issue?.currentIssueBoardView) store.issue.setCurrentIssueBoardView(board);
}, [board, store?.issue]);
return <></>;
};