Merge branch 'develop' of github.com:makeplane/plane into chore/serializers

This commit is contained in:
NarayanBavisetti 2023-08-18 16:05:42 +05:30
commit ea3b9456f9
54 changed files with 641 additions and 253 deletions

View File

@ -1159,7 +1159,7 @@ class UserWorkspaceDashboardEndpoint(BaseAPIView):
upcoming_issues = Issue.issue_objects.filter(
~Q(state__group__in=["completed", "cancelled"]),
target_date__gte=timezone.now(),
start_date__gte=timezone.now(),
workspace__slug=slug,
assignees__in=[request.user],
completed_at__isnull=True,

View File

@ -227,6 +227,11 @@ class Migration(migrations.Migration):
'unique_together': {('issue', 'actor')},
},
),
migrations.AlterField(
model_name='modulelink',
name='title',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.RunPython(generate_display_name),
migrations.RunPython(rectify_field_issue_activity),
migrations.RunPython(update_assignee_issue_activity),

View File

@ -98,7 +98,7 @@ class ModuleIssue(ProjectBaseModel):
class ModuleLink(ProjectBaseModel):
title = models.CharField(max_length=255, null=True)
title = models.CharField(max_length=255, blank=True, null=True)
url = models.URLField()
module = models.ForeignKey(
Module, on_delete=models.CASCADE, related_name="link_module"

View File

@ -1,3 +1,8 @@
// ui
import { ProfileEmptyState } from "components/ui";
// image
import emptyUsers from "public/empty-state/empty_users.svg";
type Props = {
users: {
avatar: string | null;
@ -8,10 +13,16 @@ type Props = {
id: string;
}[];
title: string;
emptyStateMessage: string;
workspaceSlug: string;
};
export const AnalyticsLeaderboard: React.FC<Props> = ({ users, title, workspaceSlug }) => (
export const AnalyticsLeaderboard: React.FC<Props> = ({
users,
title,
emptyStateMessage,
workspaceSlug,
}) => (
<div className="p-3 border border-custom-border-200 rounded-[10px]">
<h6 className="text-base font-medium">{title}</h6>
{users.length > 0 ? (
@ -47,7 +58,9 @@ export const AnalyticsLeaderboard: React.FC<Props> = ({ users, title, workspaceS
))}
</div>
) : (
<div className="text-custom-text-200 text-center text-sm py-8">No matching data found.</div>
<div className="px-7 py-4">
<ProfileEmptyState title="No Data yet" description={emptyStateMessage} image={emptyUsers} />
</div>
)}
</div>
);

View File

@ -63,6 +63,7 @@ export const ScopeAndDemand: React.FC<Props> = ({ fullScreen = true }) => {
id: user?.created_by__id,
}))}
title="Most issues created"
emptyStateMessage="Co-workers and the number issues created by them appears here."
workspaceSlug={workspaceSlug?.toString() ?? ""}
/>
<AnalyticsLeaderboard
@ -75,6 +76,7 @@ export const ScopeAndDemand: React.FC<Props> = ({ fullScreen = true }) => {
id: user?.assignees__id,
}))}
title="Most issues closed"
emptyStateMessage="Co-workers and the number issues closed by them appears here."
workspaceSlug={workspaceSlug?.toString() ?? ""}
/>
<div className={fullScreen ? "md:col-span-2" : ""}>

View File

@ -1,5 +1,7 @@
// ui
import { BarGraph } from "components/ui";
import { BarGraph, ProfileEmptyState } from "components/ui";
// image
import emptyBarGraph from "public/empty-state/empty_bar_graph.svg";
// types
import { IDefaultAnalyticsResponse } from "types";
@ -70,8 +72,12 @@ export const AnalyticsScope: React.FC<Props> = ({ defaultAnalytics }) => (
}}
/>
) : (
<div className="text-custom-text-200 text-center text-sm py-8">
No matching data found.
<div className="px-7 py-4">
<ProfileEmptyState
title="No Data yet"
description="Analysis of pending issues by co-workers appears here."
image={emptyBarGraph}
/>
</div>
)}
</div>

View File

@ -1,5 +1,7 @@
// ui
import { LineGraph } from "components/ui";
import { LineGraph, ProfileEmptyState } from "components/ui";
// image
import emptyGraph from "public/empty-state/empty_graph.svg";
// types
import { IDefaultAnalyticsResponse } from "types";
// constants
@ -48,7 +50,13 @@ export const AnalyticsYearWiseIssues: React.FC<Props> = ({ defaultAnalytics }) =
enableArea
/>
) : (
<div className="text-custom-text-200 text-center text-sm py-8">No matching data found.</div>
<div className="px-7 py-4">
<ProfileEmptyState
title="No Data yet"
description="Close issues to view analysis of the same in the form of a graph."
image={emptyGraph}
/>
</div>
)}
</div>
);

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

@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect } from "react";
// react-hook-form
import { useForm } from "react-hook-form";
@ -7,12 +7,15 @@ import { Dialog, Transition } from "@headlessui/react";
// ui
import { Input, PrimaryButton, SecondaryButton } from "components/ui";
// types
import type { IIssueLink, ModuleLink } from "types";
import type { IIssueLink, linkDetails, ModuleLink } from "types";
type Props = {
isOpen: boolean;
handleClose: () => void;
onFormSubmit: (formData: IIssueLink | ModuleLink) => Promise<void>;
data?: linkDetails | null;
status: boolean;
createIssueLink: (formData: IIssueLink | ModuleLink) => Promise<void>;
updateIssueLink: (formData: IIssueLink | ModuleLink, linkId: string) => Promise<void>;
};
const defaultValues: IIssueLink | ModuleLink = {
@ -20,7 +23,14 @@ const defaultValues: IIssueLink | ModuleLink = {
url: "",
};
export const LinkModal: React.FC<Props> = ({ isOpen, handleClose, onFormSubmit }) => {
export const LinkModal: React.FC<Props> = ({
isOpen,
handleClose,
createIssueLink,
updateIssueLink,
status,
data,
}) => {
const {
register,
formState: { errors, isSubmitting },
@ -30,11 +40,6 @@ export const LinkModal: React.FC<Props> = ({ isOpen, handleClose, onFormSubmit }
defaultValues,
});
const onSubmit = async (formData: IIssueLink | ModuleLink) => {
await onFormSubmit({ title: formData.title, url: formData.url });
onClose();
};
const onClose = () => {
handleClose();
const timeout = setTimeout(() => {
@ -43,6 +48,27 @@ export const LinkModal: React.FC<Props> = ({ isOpen, handleClose, onFormSubmit }
}, 500);
};
const handleFormSubmit = async (formData: IIssueLink | ModuleLink) => {
if (!data) await createIssueLink({ title: formData.title, url: formData.url });
else await updateIssueLink({ title: formData.title, url: formData.url }, data.id);
onClose();
};
const handleCreateUpdatePage = async (formData: IIssueLink | ModuleLink) => {
await handleFormSubmit(formData);
reset({
...defaultValues,
});
};
useEffect(() => {
reset({
...defaultValues,
...data,
});
}, [data, reset]);
return (
<Transition.Root show={isOpen} as={React.Fragment}>
<Dialog as="div" className="relative z-20" onClose={onClose}>
@ -70,14 +96,14 @@ export const LinkModal: React.FC<Props> = ({ isOpen, handleClose, onFormSubmit }
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-custom-background-100 border border-custom-border-200 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
<form onSubmit={handleSubmit(onSubmit)}>
<form onSubmit={handleSubmit(handleCreateUpdatePage)}>
<div>
<div className="space-y-5">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-custom-text-100"
>
Add Link
{status ? "Update Link" : "Add Link"}
</Dialog.Title>
<div className="mt-2 space-y-3">
<div>
@ -113,7 +139,13 @@ export const LinkModal: React.FC<Props> = ({ isOpen, handleClose, onFormSubmit }
<div className="mt-5 flex justify-end gap-2">
<SecondaryButton onClick={onClose}>Cancel</SecondaryButton>
<PrimaryButton type="submit" loading={isSubmitting}>
{isSubmitting ? "Adding Link..." : "Add Link"}
{status
? isSubmitting
? "Updating Link..."
: "Update Link"
: isSubmitting
? "Adding Link..."
: "Add Link"}
</PrimaryButton>
</div>
</form>

View File

@ -1,25 +1,24 @@
// icons
import { ArrowTopRightOnSquareIcon, LinkIcon, TrashIcon } from "@heroicons/react/24/outline";
import { Icon } from "components/ui";
// helpers
import { timeAgo } from "helpers/date-time.helper";
// types
import { IUserLite, UserAuth } from "types";
import { linkDetails, UserAuth } from "types";
type Props = {
links: {
id: string;
created_at: Date;
created_by: string;
created_by_detail: IUserLite;
metadata: any;
title: string;
url: string;
}[];
links: linkDetails[];
handleDeleteLink: (linkId: string) => void;
handleEditLink: (link: linkDetails) => void;
userAuth: UserAuth;
};
export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }) => {
export const LinksList: React.FC<Props> = ({
links,
handleDeleteLink,
handleEditLink,
userAuth,
}) => {
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
return (
@ -28,6 +27,13 @@ export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }
<div key={link.id} className="relative">
{!isNotAllowed && (
<div className="absolute top-1.5 right-1.5 z-[1] flex items-center gap-1">
<button
type="button"
className="grid h-7 w-7 place-items-center rounded bg-custom-background-90 p-1 outline-none hover:bg-custom-background-80"
onClick={() => handleEditLink(link)}
>
<Icon iconName="edit" className="text-custom-text-200" />
</button>
<a
href={link.url}
target="_blank"

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

@ -45,7 +45,6 @@ type TSingleStatProps = {
handleDeleteCycle: () => void;
handleAddToFavorites: () => void;
handleRemoveFromFavorites: () => void;
isCompleted?: boolean;
};
const stateGroups = [
@ -82,7 +81,6 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
handleDeleteCycle,
handleAddToFavorites,
handleRemoveFromFavorites,
isCompleted = false,
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -90,6 +88,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
const { setToastAlert } = useToast();
const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date);
const isCompleted = cycleStatus === "completed";
const endDate = new Date(cycle.end_date ?? "");
const startDate = new Date(cycle.start_date ?? "");

View File

@ -34,7 +34,6 @@ type TSingleStatProps = {
handleDeleteCycle: () => void;
handleAddToFavorites: () => void;
handleRemoveFromFavorites: () => void;
isCompleted?: boolean;
};
const stateGroups = [
@ -113,7 +112,6 @@ export const SingleCycleList: React.FC<TSingleStatProps> = ({
handleDeleteCycle,
handleAddToFavorites,
handleRemoveFromFavorites,
isCompleted = false,
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -121,6 +119,7 @@ export const SingleCycleList: React.FC<TSingleStatProps> = ({
const { setToastAlert } = useToast();
const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date);
const isCompleted = cycleStatus === "completed";
const endDate = new Date(cycle.end_date ?? "");
const startDate = new Date(cycle.start_date ?? "");

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

@ -37,7 +37,7 @@ import { LinkIcon, CalendarDaysIcon, TrashIcon, PlusIcon } from "@heroicons/reac
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
// types
import type { ICycle, IIssue, IIssueLink, IModule } from "types";
import type { ICycle, IIssue, IIssueLink, linkDetails, IModule } from "types";
// fetch-keys
import { ISSUE_DETAILS } from "constants/fetch-keys";
@ -77,6 +77,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
}) => {
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
const [linkModal, setLinkModal] = useState(false);
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<linkDetails | null>(null);
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
@ -156,6 +157,43 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
});
};
const handleUpdateLink = async (formData: IIssueLink, linkId: string) => {
if (!workspaceSlug || !projectId || !issueDetail) return;
const payload = { metadata: {}, ...formData };
const updatedLinks = issueDetail.issue_link.map((l) =>
l.id === linkId
? {
...l,
title: formData.title,
url: formData.url,
}
: l
);
mutate<IIssue>(
ISSUE_DETAILS(issueDetail.id),
(prevData) => ({ ...(prevData as IIssue), issue_link: updatedLinks }),
false
);
await issuesService
.updateIssueLink(
workspaceSlug as string,
projectId as string,
issueDetail.id,
linkId,
payload
)
.then((res) => {
mutate(ISSUE_DETAILS(issueDetail.id));
})
.catch((err) => {
console.log(err);
});
};
const handleDeleteLink = async (linkId: string) => {
if (!workspaceSlug || !projectId || !issueDetail) return;
@ -220,14 +258,25 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
const maxDate = targetDate ? new Date(targetDate) : null;
maxDate?.setDate(maxDate.getDate());
const handleEditLink = (link: linkDetails) => {
setSelectedLinkToUpdate(link);
setLinkModal(true);
};
const isNotAllowed = memberRole.isGuest || memberRole.isViewer;
return (
<>
<LinkModal
isOpen={linkModal}
handleClose={() => setLinkModal(false)}
onFormSubmit={handleCreateLink}
handleClose={() => {
setLinkModal(false);
setSelectedLinkToUpdate(null);
}}
data={selectedLinkToUpdate}
status={selectedLinkToUpdate ? true : false}
createIssueLink={handleCreateLink}
updateIssueLink={handleUpdateLink}
/>
<DeleteIssueModal
handleClose={() => setDeleteIssueModal(false)}
@ -396,7 +445,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 +474,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}
/>
@ -488,6 +539,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
<LinksList
links={issueDetail.issue_link}
handleDeleteLink={handleDeleteLink}
handleEditLink={handleEditLink}
userAuth={memberRole}
/>
) : null}

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

@ -37,7 +37,7 @@ import { LinkIcon } from "@heroicons/react/20/solid";
import { renderDateFormat, renderShortDateWithYearFormat } from "helpers/date-time.helper";
import { capitalizeFirstLetter, copyTextToClipboard } from "helpers/string.helper";
// types
import { ICurrentUserResponse, IIssue, IModule, ModuleLink } from "types";
import { ICurrentUserResponse, IIssue, linkDetails, IModule, ModuleLink } from "types";
// fetch-keys
import { MODULE_DETAILS } from "constants/fetch-keys";
// constant
@ -61,6 +61,7 @@ type Props = {
export const ModuleDetailsSidebar: React.FC<Props> = ({ module, isOpen, moduleIssues, user }) => {
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
const [moduleLinkModal, setModuleLinkModal] = useState(false);
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<linkDetails | null>(null);
const router = useRouter();
const { workspaceSlug, projectId, moduleId } = router.query;
@ -115,6 +116,37 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({ module, isOpen, moduleIs
});
};
const handleUpdateLink = async (formData: ModuleLink, linkId: string) => {
if (!workspaceSlug || !projectId || !module) return;
const payload = { metadata: {}, ...formData };
const updatedLinks = module.link_module.map((l) =>
l.id === linkId
? {
...l,
title: formData.title,
url: formData.url,
}
: l
);
mutate<IModule>(
MODULE_DETAILS(module.id),
(prevData) => ({ ...(prevData as IModule), link_module: updatedLinks }),
false
);
await modulesService
.updateModuleLink(workspaceSlug as string, projectId as string, module.id, linkId, payload)
.then((res) => {
mutate(MODULE_DETAILS(module.id));
})
.catch((err) => {
console.log(err);
});
};
const handleDeleteLink = async (linkId: string) => {
if (!workspaceSlug || !projectId || !module) return;
@ -170,12 +202,23 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({ module, isOpen, moduleIs
? Math.round((module.completed_issues / module.total_issues) * 100)
: null;
const handleEditLink = (link: linkDetails) => {
setSelectedLinkToUpdate(link);
setModuleLinkModal(true);
};
return (
<>
<LinkModal
isOpen={moduleLinkModal}
handleClose={() => setModuleLinkModal(false)}
onFormSubmit={handleCreateLink}
handleClose={() => {
setModuleLinkModal(false);
setSelectedLinkToUpdate(null);
}}
data={selectedLinkToUpdate}
status={selectedLinkToUpdate ? true : false}
createIssueLink={handleCreateLink}
updateIssueLink={handleUpdateLink}
/>
<DeleteModuleModal
isOpen={moduleDeleteModal}
@ -544,7 +587,7 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({ module, isOpen, moduleIs
</Disclosure>
</div>
<div className="flex w-full flex-col border-t border-custom-border-200 px-6 py-6 text-xs">
<div className="flex w-full flex-col border-t border-custom-border-200 px-6 pt-6 pb-10 text-xs">
<div className="flex w-full items-center justify-between">
<h4 className="text-sm font-medium text-custom-text-200">Links</h4>
<button
@ -558,6 +601,7 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({ module, isOpen, moduleIs
{memberRole && module.link_module && module.link_module.length > 0 ? (
<LinksList
links={module.link_module}
handleEditLink={handleEditLink}
handleDeleteLink={handleDeleteLink}
userAuth={memberRole}
/>

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

@ -1,7 +1,7 @@
// ui
import { BarGraph, ProfileEmptyState, Loader } from "components/ui";
// image
import priorityGraph from "public/empty-state/priority_graph.svg";
import emptyBarGraph from "public/empty-state/empty_bar_graph.svg";
// helpers
import { capitalizeFirstLetter } from "helpers/string.helper";
// types
@ -67,7 +67,7 @@ export const ProfilePriorityDistribution: React.FC<Props> = ({ userProfile }) =>
<ProfileEmptyState
title="No Data yet"
description="Create issues to view the them by priority in the graph for better analysis."
image={priorityGraph}
image={emptyBarGraph}
/>
</div>
)}

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

@ -106,7 +106,7 @@ export const AssigneesList: React.FC<AsigneesListProps> = ({
))}
{users.length > length ? (
<div className="-ml-3.5 relative h-6 w-6 rounded">
<div className="grid place-items-center rounded bg-custom-background-80 text-xs capitalize h-6 w-6 text-custom-text-200 border-[0.5px] border-custom-border-300">
<div className="flex items-center rounded bg-custom-background-80 text-xs capitalize h-6 w-6 text-custom-text-200 border-[0.5px] border-custom-border-300">
<Icon iconName="add" className="text-xs !leading-3 -mr-0.5" />
{users.length - length}
</div>

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

@ -11,8 +11,8 @@ type Props = {
export const ProfileEmptyState: React.FC<Props> = ({ title, description, image }) => (
<div className={`h-full w-full mx-auto grid place-items-center p-8 `}>
<div className="text-center flex flex-col items-center w-full">
<div className="flex items-center justify-center h-14 w-14 rounded-full bg-custom-primary-10">
<Image src={image} className="w-8" alt={title} />
<div className="flex items-center justify-center h-14 w-14 rounded-full bg-custom-background-90">
<Image src={image} width={32} alt={title} />
</div>
<h6 className="text-base font-semibold mt-3.5 mb-3">{title}</h6>
{description && <p className="text-sm text-custom-text-300">{description}</p>}

View File

@ -42,10 +42,10 @@ export const ANALYTICS_X_AXIS_VALUES: { value: TXAxisValues; label: string }[] =
value: "target_date",
label: "Due date",
},
// {
// value: "start_date",
// label: "Start date",
// },
{
value: "start_date",
label: "Start date",
},
{
value: "created_at",
label: "Created date",

View File

@ -362,7 +362,13 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> =
},
});
if (property === "kanban") {
const additionalProperties = {
groupByProperty: state.groupByProperty,
orderBy: state.orderBy,
};
if (property === "kanban" && state.groupByProperty === null) {
additionalProperties.groupByProperty = "state";
dispatch({
type: "SET_GROUP_BY_PROPERTY",
payload: {
@ -371,6 +377,7 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> =
});
}
if (property === "calendar") {
additionalProperties.groupByProperty = null;
dispatch({
type: "SET_GROUP_BY_PROPERTY",
payload: {
@ -378,13 +385,22 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> =
},
});
}
if (property === "gantt_chart") {
additionalProperties.orderBy = "sort_order";
dispatch({
type: "SET_ORDER_BY_PROPERTY",
payload: {
orderBy: "sort_order",
},
});
}
if (!workspaceSlug || !projectId) return;
saveDataToServer(workspaceSlug as string, projectId as string, {
...state,
issueView: property,
groupByProperty: "state",
...additionalProperties,
});
},
[workspaceSlug, projectId, state]

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

@ -1,5 +1,5 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.718262 14.3359C0.718262 13.2314 1.61369 12.3359 2.71826 12.3359H4.91545C6.02002 12.3359 6.91544 13.2314 6.91544 14.3359V27.0542H0.718262V14.3359Z" fill="#C5D6FF"/>
<path d="M10.7886 2.71875C10.7886 1.61418 11.684 0.71875 12.7886 0.71875H14.9858C16.0903 0.71875 16.9858 1.61418 16.9858 2.71875V27.0568H10.7886V2.71875Z" fill="#9FBBFF"/>
<path d="M20.8589 9.6875C20.8589 8.58293 21.7543 7.6875 22.8589 7.6875H25.0561C26.1606 7.6875 27.0561 8.58293 27.0561 9.6875V27.0537H20.8589V9.6875Z" fill="#C5D6FF"/>
<path d="M0.718262 14.3359C0.718262 13.2314 1.61369 12.3359 2.71826 12.3359H4.91545C6.02002 12.3359 6.91544 13.2314 6.91544 14.3359V27.0542H0.718262V14.3359Z" fill="#DEE2E6"/>
<path d="M10.7886 2.71875C10.7886 1.61418 11.684 0.71875 12.7886 0.71875H14.9858C16.0903 0.71875 16.9858 1.61418 16.9858 2.71875V27.0568H10.7886V2.71875Z" fill="#CED4DA"/>
<path d="M20.8591 9.6875C20.8591 8.58293 21.7546 7.6875 22.8591 7.6875H25.0563C26.1609 7.6875 27.0563 8.58293 27.0563 9.6875V27.0537H20.8591V9.6875Z" fill="#DEE2E6"/>
</svg>

Before

Width:  |  Height:  |  Size: 618 B

After

Width:  |  Height:  |  Size: 618 B

View File

@ -0,0 +1,10 @@
<svg width="37" height="26" viewBox="0 0 37 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 23.4992C1 23.4992 1.4588 24.9268 4.50198 25.0001H5C11.2284 24.8579 11 17.9987 11.5 13.9989C12 9.99916 11.8458 0.903962 17 1.00077C21.2817 1.08118 23.8932 13.213 28 11.9989C29.9905 11.4105 33.5 5.00007 33.5 5.00007V25.0001H5C4.82675 25.004 4.66084 25.0039 4.50198 25.0001H1V23.4992Z" fill="url(#paint0_linear_6920_91013)"/>
<path d="M1.47602 23.3462C1.39154 23.0833 1.10992 22.9387 0.847024 23.0231C0.584123 23.1076 0.43949 23.3892 0.523977 23.6521L1.47602 23.3462ZM5 25.0001L5.01141 25.4999L5 25.0001ZM17 1.00077L17.0094 0.500855L17 1.00077ZM28 11.9989L28.1417 12.4784L28 11.9989ZM30.8333 5.00007C30.8333 6.47282 32.0272 7.66673 33.5 7.66673C34.9728 7.66673 36.1667 6.47282 36.1667 5.00007C36.1667 3.52731 34.9728 2.3334 33.5 2.3334C32.0272 2.3334 30.8333 3.52731 30.8333 5.00007ZM1 23.4992C0.523977 23.6521 0.52413 23.6526 0.524285 23.6531C0.52434 23.6533 0.524498 23.6538 0.524608 23.6541C0.524828 23.6548 0.525058 23.6555 0.525298 23.6562C0.525777 23.6576 0.526296 23.6592 0.526856 23.6608C0.527975 23.6641 0.529258 23.6678 0.530714 23.6719C0.533627 23.6801 0.537237 23.6898 0.541628 23.7009C0.550409 23.723 0.562322 23.7509 0.578044 23.7836C0.6095 23.849 0.656181 23.9336 0.723434 24.0304C0.85859 24.2247 1.07399 24.4637 1.40832 24.6923C2.07953 25.1511 3.1901 25.5415 5.01141 25.4999L4.98859 24.5002C3.31786 24.5383 2.42842 24.1783 1.97265 23.8667C1.74348 23.7101 1.61364 23.5589 1.54443 23.4594C1.5095 23.4092 1.48921 23.3709 1.47925 23.3502C1.47426 23.3399 1.47185 23.3339 1.47149 23.3329C1.4713 23.3325 1.47164 23.3333 1.47241 23.3355C1.4728 23.3366 1.4733 23.338 1.47391 23.3398C1.47421 23.3407 1.47454 23.3416 1.47489 23.3427C1.47507 23.3432 1.47525 23.3438 1.47544 23.3444C1.47553 23.3447 1.47568 23.3451 1.47573 23.3453C1.47587 23.3457 1.47602 23.3462 1 23.4992ZM5.01141 25.4999C6.66694 25.4621 7.93434 24.9744 8.89485 24.1662C9.84776 23.3644 10.4597 22.2801 10.8699 21.1118C11.2798 19.9446 11.4979 18.6657 11.6457 17.4454C11.7197 16.8339 11.7767 16.2309 11.8304 15.6611C11.8844 15.0885 11.9348 14.5519 11.9961 14.061L11.0039 13.9369C10.9402 14.4459 10.8884 14.9987 10.8348 15.5673C10.781 16.1387 10.7251 16.7289 10.6529 17.3252C10.5082 18.5203 10.3005 19.7151 9.9264 20.7804C9.5527 21.8447 9.02264 22.7518 8.25102 23.401C7.48698 24.0439 6.44728 24.4669 4.98859 24.5002L5.01141 25.4999ZM11.9961 14.061C12.1219 13.0547 12.2094 11.7012 12.3432 10.315C12.479 8.90792 12.665 7.42713 12.9907 6.08122C13.318 4.72822 13.776 3.55792 14.4276 2.73995C15.062 1.94357 15.8775 1.47977 16.9906 1.50068L17.0094 0.500855C15.5454 0.473358 14.4474 1.11016 13.6454 2.11689C12.8607 3.10204 12.3596 4.43737 12.0187 5.84604C11.6761 7.26181 11.4848 8.79873 11.3478 10.2189C11.2088 11.66 11.1281 12.9433 11.0039 13.9369L11.9961 14.061ZM16.9906 1.50068C17.822 1.51629 18.6613 2.12546 19.5584 3.23777C20.4409 4.33201 21.276 5.77974 22.1347 7.23885C22.979 8.67358 23.8508 10.128 24.7826 11.1422C25.7068 12.1481 26.8286 12.8666 28.1417 12.4784L27.8583 11.5194C27.118 11.7383 26.3733 11.3955 25.519 10.4657C24.6724 9.54415 23.8537 8.1882 22.9965 6.73168C22.1537 5.29954 21.2765 3.77513 20.3368 2.60999C19.4116 1.46291 18.3189 0.525449 17.0094 0.500855L16.9906 1.50068ZM28.1417 12.4784C28.4806 12.3783 28.8126 12.1684 29.1222 11.9181C29.4369 11.6636 29.7572 11.3437 30.0725 10.9897C30.7031 10.2817 31.3428 9.40414 31.9115 8.55769C32.4817 7.70903 32.9879 6.88053 33.3511 6.26503C33.5329 5.95699 33.6793 5.70163 33.7805 5.52293C33.831 5.43356 33.8703 5.36332 33.8971 5.31521C33.9105 5.29115 33.9207 5.27262 33.9277 5.25999C33.9312 5.25367 33.9338 5.24883 33.9356 5.24551C33.9366 5.24385 33.9373 5.24258 33.9377 5.24168C33.938 5.24124 33.9382 5.24089 33.9383 5.24063C33.9384 5.24051 33.9385 5.24039 33.9385 5.24033C33.9385 5.24024 33.9386 5.24017 33.5 5.00007C33.0614 4.75996 33.0614 4.75994 33.0614 4.75995C33.0614 4.75998 33.0614 4.76001 33.0614 4.76008C33.0613 4.76022 33.0612 4.76045 33.061 4.76078C33.0606 4.76144 33.06 4.76249 33.0593 4.76391C33.0577 4.76676 33.0553 4.77113 33.0521 4.77696C33.0456 4.78862 33.0359 4.80616 33.0231 4.82919C32.9975 4.87527 32.9594 4.94333 32.9102 5.0304C32.8116 5.20456 32.6683 5.45462 32.4899 5.75676C32.133 6.3616 31.6374 7.17248 31.0814 7.99999C30.5239 8.82971 29.913 9.66513 29.3257 10.3246C29.0321 10.6543 28.7517 10.9317 28.4935 11.1404C28.2302 11.3533 28.0171 11.4725 27.8583 11.5194L28.1417 12.4784Z" fill="#ACB5BD"/>
<defs>
<linearGradient id="paint0_linear_6920_91013" x1="17" y1="0.5" x2="17.25" y2="25.003" gradientUnits="userSpaceOnUse">
<stop offset="0.253958" stop-color="#C6CED6" stop-opacity="0.7"/>
<stop offset="1" stop-color="#858E96" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,3 @@
<svg width="43" height="42" viewBox="0 0 43 42" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.5 21.375C27.1977 21.375 31.8125 16.7602 31.8125 11.0625C31.8125 5.36484 27.1977 0.75 21.5 0.75C15.8023 0.75 11.1875 5.36484 11.1875 11.0625C11.1875 16.7602 15.8023 21.375 21.5 21.375ZM21.5 26.5312C14.6164 26.5312 0.875 29.9859 0.875 36.8438V42H42.125V36.8438C42.125 29.9859 28.3836 26.5312 21.5 26.5312Z" fill="#CED4DA"/>
</svg>

After

Width:  |  Height:  |  Size: 438 B

View File

@ -1,3 +1,3 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.3704 21.7617C11.349 21.5892 9.64994 20.7655 8.27325 19.2907C6.89653 17.816 6.20817 16.0515 6.20817 13.9974C6.20817 11.8239 6.96342 9.98197 8.47392 8.47148C9.98441 6.96098 11.8264 6.20573 13.9998 6.20573C16.0539 6.20573 17.8184 6.89181 19.2932 8.26397C20.768 9.63613 21.5916 11.3402 21.7642 13.3762L20.0506 12.8449C19.7825 11.3974 19.0895 10.1969 17.9714 9.24335C16.8532 8.28984 15.5294 7.81308 13.9998 7.81308C12.2835 7.81308 10.8237 8.4147 9.62039 9.61795C8.41714 10.8212 7.81552 12.281 7.81552 13.9974C7.81552 15.5157 8.29456 16.8389 9.25262 17.9668C10.2107 19.0946 11.4089 19.793 12.8474 20.0618L13.3704 21.7617ZM13.9998 27.4557C12.1381 27.4557 10.3885 27.1024 8.75109 26.396C7.11366 25.6896 5.68932 24.7308 4.47807 23.5198C3.26682 22.3088 2.30791 20.8847 1.60135 19.2475C0.894785 17.6104 0.541504 15.8611 0.541504 13.9997C0.541504 12.1383 0.894726 10.3887 1.60117 8.75084C2.30762 7.11296 3.26634 5.68825 4.47736 4.47669C5.6884 3.26511 7.11249 2.30594 8.74964 1.59919C10.3868 0.89244 12.136 0.539062 13.9974 0.539062C15.8588 0.539062 17.6085 0.892345 19.2463 1.59891C20.8842 2.30547 22.3089 3.26438 23.5205 4.47563C24.7321 5.68688 25.6912 7.11122 26.398 8.74865C27.1047 10.3861 27.4581 12.1356 27.4581 13.9974C27.4581 14.1735 27.4545 14.3497 27.4472 14.5259C27.4399 14.702 27.4245 14.8782 27.4009 15.0544L25.8508 14.5804V13.9974C25.8508 10.6991 24.7002 7.89934 22.399 5.59816C20.0978 3.297 17.2981 2.14641 13.9998 2.14641C10.7015 2.14641 7.90178 3.297 5.6006 5.59816C3.29944 7.89934 2.14885 10.6991 2.14885 13.9974C2.14885 17.2956 3.29944 20.0954 5.6006 22.3966C7.90178 24.6977 10.7015 25.8483 13.9998 25.8483H14.5828L15.0704 27.3984C14.892 27.422 14.7136 27.4375 14.5351 27.4448C14.3567 27.452 14.1783 27.4557 13.9998 27.4557ZM25.156 27.284L19.1079 21.2223L18.0999 24.2953C18.0033 24.555 17.8256 24.679 17.567 24.6672C17.3084 24.6554 17.1437 24.5196 17.0728 24.2599L14.2831 14.94C14.2214 14.7366 14.265 14.5604 14.4139 14.4115C14.5628 14.2625 14.739 14.219 14.9424 14.2807L24.2759 17.0704C24.5266 17.1412 24.6578 17.306 24.6696 17.5646C24.6814 17.8232 24.562 17.9963 24.3113 18.0838L21.2247 19.1055L27.2864 25.1536C27.3954 25.2592 27.4499 25.3837 27.4499 25.527C27.4499 25.6704 27.3954 25.7965 27.2864 25.9055L25.9079 27.284C25.8023 27.393 25.6778 27.4474 25.5344 27.4474C25.3911 27.4474 25.265 27.393 25.156 27.284Z" fill="#9FBBFF"/>
<path d="M13.3707 21.7617C11.3493 21.5892 9.65018 20.7655 8.27349 19.2907C6.89677 17.816 6.20842 16.0515 6.20842 13.9974C6.20842 11.8239 6.96366 9.98197 8.47416 8.47148C9.98466 6.96098 11.8266 6.20573 14 6.20573C16.0542 6.20573 17.8187 6.89181 19.2934 8.26397C20.7682 9.63613 21.5919 11.3402 21.7644 13.3762L20.0508 12.8449C19.7828 11.3974 19.0897 10.1969 17.9716 9.24335C16.8535 8.28984 15.5296 7.81308 14 7.81308C12.2837 7.81308 10.8239 8.4147 9.62063 9.61795C8.41739 10.8212 7.81577 12.281 7.81577 13.9974C7.81577 15.5157 8.2948 16.8389 9.25287 17.9668C10.2109 19.0946 11.4092 19.793 12.8476 20.0618L13.3707 21.7617ZM14 27.4557C12.1383 27.4557 10.3887 27.1024 8.75133 26.396C7.1139 25.6896 5.68956 24.7308 4.47831 23.5198C3.26706 22.3088 2.30815 20.8847 1.60159 19.2475C0.895029 17.6104 0.541748 15.8611 0.541748 13.9997C0.541748 12.1383 0.894971 10.3887 1.60142 8.75084C2.30786 7.11296 3.26659 5.68825 4.4776 4.47669C5.68864 3.26511 7.11273 2.30594 8.74988 1.59919C10.387 0.89244 12.1363 0.539062 13.9977 0.539062C15.8591 0.539062 17.6087 0.892345 19.2466 1.59891C20.8844 2.30547 22.3092 3.26438 23.5207 4.47563C24.7323 5.68688 25.6915 7.11122 26.3982 8.74865C27.105 10.3861 27.4583 12.1356 27.4583 13.9974C27.4583 14.1735 27.4547 14.3497 27.4474 14.5259C27.4402 14.702 27.4247 14.8782 27.4011 15.0544L25.851 14.5804V13.9974C25.851 10.6991 24.7004 7.89934 22.3993 5.59816C20.0981 3.297 17.2983 2.14641 14 2.14641C10.7018 2.14641 7.90203 3.297 5.60084 5.59816C3.29968 7.89934 2.1491 10.6991 2.1491 13.9974C2.1491 17.2956 3.29968 20.0954 5.60084 22.3966C7.90203 24.6977 10.7018 25.8483 14 25.8483H14.583L15.0707 27.3984C14.8923 27.422 14.7138 27.4375 14.5354 27.4448C14.3569 27.452 14.1785 27.4557 14 27.4557ZM25.1563 27.284L19.1082 21.2223L18.1002 24.2953C18.0035 24.555 17.8259 24.679 17.5672 24.6672C17.3086 24.6554 17.1439 24.5196 17.0731 24.2599L14.2834 14.94C14.2216 14.7366 14.2652 14.5604 14.4141 14.4115C14.5631 14.2625 14.7393 14.219 14.9427 14.2807L24.2762 17.0704C24.5268 17.1412 24.658 17.306 24.6699 17.5646C24.6817 17.8232 24.5622 17.9963 24.3116 18.0838L21.2249 19.1055L27.2866 25.1536C27.3956 25.2592 27.4501 25.3837 27.4501 25.527C27.4501 25.6704 27.3956 25.7965 27.2866 25.9055L25.9082 27.284C25.8025 27.393 25.678 27.4474 25.5347 27.4474C25.3914 27.4474 25.2652 27.393 25.1563 27.284Z" fill="#CED4DA"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,26 +1,26 @@
<svg width="34" height="34" viewBox="0 0 34 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1_6456_90931" fill="white">
<mask id="path-1-inside-1_6456_90192" fill="white">
<path d="M34 17C34 26.3888 26.3888 34 17 34C7.61116 34 0 26.3888 0 17C0 7.61116 7.61116 0 17 0C26.3888 0 34 7.61116 34 17ZM5.09529 17C5.09529 23.5748 10.4252 28.9047 17 28.9047C23.5748 28.9047 28.9047 23.5748 28.9047 17C28.9047 10.4252 23.5748 5.09529 17 5.09529C10.4252 5.09529 5.09529 10.4252 5.09529 17Z"/>
</mask>
<path d="M34 17C34 26.3888 26.3888 34 17 34C7.61116 34 0 26.3888 0 17C0 7.61116 7.61116 0 17 0C26.3888 0 34 7.61116 34 17ZM5.09529 17C5.09529 23.5748 10.4252 28.9047 17 28.9047C23.5748 28.9047 28.9047 23.5748 28.9047 17C28.9047 10.4252 23.5748 5.09529 17 5.09529C10.4252 5.09529 5.09529 10.4252 5.09529 17Z" fill="#C5D6FF" stroke="#ECF1FF" stroke-width="0.482269" mask="url(#path-1-inside-1_6456_90931)"/>
<mask id="path-2-inside-2_6456_90931" fill="white">
<path d="M34 17C34 26.3888 26.3888 34 17 34C7.61116 34 0 26.3888 0 17C0 7.61116 7.61116 0 17 0C26.3888 0 34 7.61116 34 17ZM5.09529 17C5.09529 23.5748 10.4252 28.9047 17 28.9047C23.5748 28.9047 28.9047 23.5748 28.9047 17C28.9047 10.4252 23.5748 5.09529 17 5.09529C10.4252 5.09529 5.09529 10.4252 5.09529 17Z" fill="#DEE2E6" stroke="#F8F9FA" stroke-width="0.482269" mask="url(#path-1-inside-1_6456_90192)"/>
<mask id="path-2-inside-2_6456_90192" fill="white">
<path d="M17 0C19.8546 3.40409e-08 22.6633 0.718838 25.1669 2.09021C27.6705 3.46158 29.7885 5.44133 31.3255 7.84684C32.8625 10.2524 33.769 13.0062 33.9613 15.8543C34.1537 18.7024 33.6258 21.5531 32.4263 24.1435C31.2268 26.7339 29.3943 28.9804 27.0978 30.6761C24.8013 32.3717 22.1149 33.4617 19.2862 33.8456C16.4575 34.2295 13.5777 33.8949 10.9124 32.8727C8.24708 31.8504 5.8821 30.1735 4.03558 27.9965L7.92131 24.7006C9.21439 26.2251 10.8705 27.3994 12.737 28.1153C14.6034 28.8311 16.6201 29.0654 18.601 28.7966C20.5818 28.5277 22.4631 27.7644 24.0713 26.577C25.6794 25.3896 26.9627 23.8164 27.8027 22.0024C28.6427 20.1885 29.0124 18.1922 28.8776 16.1977C28.7429 14.2032 28.1081 12.2748 27.0318 10.5903C25.9555 8.90572 24.4723 7.51935 22.7191 6.55901C20.9659 5.59867 18.999 5.09529 17 5.09529V0Z"/>
</mask>
<path d="M17 0C19.8546 3.40409e-08 22.6633 0.718838 25.1669 2.09021C27.6705 3.46158 29.7885 5.44133 31.3255 7.84684C32.8625 10.2524 33.769 13.0062 33.9613 15.8543C34.1537 18.7024 33.6258 21.5531 32.4263 24.1435C31.2268 26.7339 29.3943 28.9804 27.0978 30.6761C24.8013 32.3717 22.1149 33.4617 19.2862 33.8456C16.4575 34.2295 13.5777 33.8949 10.9124 32.8727C8.24708 31.8504 5.8821 30.1735 4.03558 27.9965L7.92131 24.7006C9.21439 26.2251 10.8705 27.3994 12.737 28.1153C14.6034 28.8311 16.6201 29.0654 18.601 28.7966C20.5818 28.5277 22.4631 27.7644 24.0713 26.577C25.6794 25.3896 26.9627 23.8164 27.8027 22.0024C28.6427 20.1885 29.0124 18.1922 28.8776 16.1977C28.7429 14.2032 28.1081 12.2748 27.0318 10.5903C25.9555 8.90572 24.4723 7.51935 22.7191 6.55901C20.9659 5.59867 18.999 5.09529 17 5.09529V0Z" fill="#9FBBFF" stroke="#ECF1FF" stroke-width="0.482269" mask="url(#path-2-inside-2_6456_90931)"/>
<mask id="path-3-inside-3_6456_90931" fill="white">
<path d="M17 0C19.8546 3.40409e-08 22.6633 0.718838 25.1669 2.09021C27.6705 3.46158 29.7885 5.44133 31.3255 7.84684C32.8625 10.2524 33.769 13.0062 33.9613 15.8543C34.1537 18.7024 33.6258 21.5531 32.4263 24.1435C31.2268 26.7339 29.3943 28.9804 27.0978 30.6761C24.8013 32.3717 22.1149 33.4617 19.2862 33.8456C16.4575 34.2295 13.5777 33.8949 10.9124 32.8727C8.24708 31.8504 5.8821 30.1735 4.03558 27.9965L7.92131 24.7006C9.21439 26.2251 10.8705 27.3994 12.737 28.1153C14.6034 28.8311 16.6201 29.0654 18.601 28.7966C20.5818 28.5277 22.4631 27.7644 24.0713 26.577C25.6794 25.3896 26.9627 23.8164 27.8027 22.0024C28.6427 20.1885 29.0124 18.1922 28.8776 16.1977C28.7429 14.2032 28.1081 12.2748 27.0318 10.5903C25.9555 8.90572 24.4723 7.51935 22.7191 6.55901C20.9659 5.59867 18.999 5.09529 17 5.09529V0Z" fill="#ACB5BD" stroke="#F8F9FA" stroke-width="0.482269" mask="url(#path-2-inside-2_6456_90192)"/>
<mask id="path-3-inside-3_6456_90192" fill="white">
<path d="M17 0C21.3598 5.19899e-08 25.5529 1.67502 28.7128 4.67885C31.8726 7.68269 33.7577 11.7857 33.9782 16.1399C34.1988 20.4941 32.7381 24.7666 29.898 28.0744C27.0578 31.3822 23.0554 33.4724 18.718 33.913C14.3805 34.3536 10.0395 33.1109 6.5923 30.4417C3.14507 27.7726 0.855066 23.881 0.195603 19.5714C-0.46386 15.2618 0.557627 10.8635 3.04894 7.28569C5.54025 3.70783 9.31095 1.22387 13.5817 0.347219L14.6062 5.33844C11.6155 5.95234 8.975 7.69179 7.23039 10.1973C5.48578 12.7028 4.77045 15.7828 5.23226 18.8007C5.69407 21.8186 7.2977 24.5438 9.71173 26.4129C12.1257 28.2821 15.1656 29.1523 18.203 28.8438C21.2405 28.5352 24.0433 27.0715 26.0321 24.7552C28.021 22.4388 29.0439 19.4469 28.8895 16.3977C28.735 13.3486 27.415 10.4753 25.2022 8.37178C22.9894 6.26826 20.0531 5.09529 17 5.09529V0Z"/>
</mask>
<path d="M17 0C21.3598 5.19899e-08 25.5529 1.67502 28.7128 4.67885C31.8726 7.68269 33.7577 11.7857 33.9782 16.1399C34.1988 20.4941 32.7381 24.7666 29.898 28.0744C27.0578 31.3822 23.0554 33.4724 18.718 33.913C14.3805 34.3536 10.0395 33.1109 6.5923 30.4417C3.14507 27.7726 0.855066 23.881 0.195603 19.5714C-0.46386 15.2618 0.557627 10.8635 3.04894 7.28569C5.54025 3.70783 9.31095 1.22387 13.5817 0.347219L14.6062 5.33844C11.6155 5.95234 8.975 7.69179 7.23039 10.1973C5.48578 12.7028 4.77045 15.7828 5.23226 18.8007C5.69407 21.8186 7.2977 24.5438 9.71173 26.4129C12.1257 28.2821 15.1656 29.1523 18.203 28.8438C21.2405 28.5352 24.0433 27.0715 26.0321 24.7552C28.021 22.4388 29.0439 19.4469 28.8895 16.3977C28.735 13.3486 27.415 10.4753 25.2022 8.37178C22.9894 6.26826 20.0531 5.09529 17 5.09529V0Z" fill="#C5D6FF" stroke="#ECF1FF" stroke-width="0.482269" mask="url(#path-3-inside-3_6456_90931)"/>
<mask id="path-4-inside-4_6456_90931" fill="white">
<path d="M17 0C21.3598 5.19899e-08 25.5529 1.67502 28.7128 4.67885C31.8726 7.68269 33.7577 11.7857 33.9782 16.1399C34.1988 20.4941 32.7381 24.7666 29.898 28.0744C27.0578 31.3822 23.0554 33.4724 18.718 33.913C14.3805 34.3536 10.0395 33.1109 6.5923 30.4417C3.14507 27.7726 0.855066 23.881 0.195603 19.5714C-0.46386 15.2618 0.557627 10.8635 3.04894 7.28569C5.54025 3.70783 9.31095 1.22387 13.5817 0.347219L14.6062 5.33844C11.6155 5.95234 8.975 7.69179 7.23039 10.1973C5.48578 12.7028 4.77045 15.7828 5.23226 18.8007C5.69407 21.8186 7.2977 24.5438 9.71173 26.4129C12.1257 28.2821 15.1656 29.1523 18.203 28.8438C21.2405 28.5352 24.0433 27.0715 26.0321 24.7552C28.021 22.4388 29.0439 19.4469 28.8895 16.3977C28.735 13.3486 27.415 10.4753 25.2022 8.37178C22.9894 6.26826 20.0531 5.09529 17 5.09529V0Z" fill="#CED4DA" stroke="#F8F9FA" stroke-width="0.482269" mask="url(#path-3-inside-3_6456_90192)"/>
<mask id="path-4-inside-4_6456_90192" fill="white">
<path d="M17 0C21.5087 5.37655e-08 25.8327 1.79107 29.0208 4.97918C32.2089 8.1673 34 12.4913 34 17C34 21.5087 32.2089 25.8327 29.0208 29.0208C25.8327 32.2089 21.5087 34 17 34V28.9047C20.1573 28.9047 23.1853 27.6505 25.4179 25.4179C27.6505 23.1853 28.9047 20.1573 28.9047 17C28.9047 13.8427 27.6505 10.8147 25.4179 8.5821C23.1853 6.34953 20.1573 5.09529 17 5.09529V0Z"/>
</mask>
<path d="M17 0C21.5087 5.37655e-08 25.8327 1.79107 29.0208 4.97918C32.2089 8.1673 34 12.4913 34 17C34 21.5087 32.2089 25.8327 29.0208 29.0208C25.8327 32.2089 21.5087 34 17 34V28.9047C20.1573 28.9047 23.1853 27.6505 25.4179 25.4179C27.6505 23.1853 28.9047 20.1573 28.9047 17C28.9047 13.8427 27.6505 10.8147 25.4179 8.5821C23.1853 6.34953 20.1573 5.09529 17 5.09529V0Z" fill="#9FBBFF" stroke="#ECF1FF" stroke-width="0.482269" mask="url(#path-4-inside-4_6456_90931)"/>
<mask id="path-5-inside-5_6456_90931" fill="white">
<path d="M17 0C21.5087 5.37655e-08 25.8327 1.79107 29.0208 4.97918C32.2089 8.1673 34 12.4913 34 17C34 21.5087 32.2089 25.8327 29.0208 29.0208C25.8327 32.2089 21.5087 34 17 34V28.9047C20.1573 28.9047 23.1853 27.6505 25.4179 25.4179C27.6505 23.1853 28.9047 20.1573 28.9047 17C28.9047 13.8427 27.6505 10.8147 25.4179 8.5821C23.1853 6.34953 20.1573 5.09529 17 5.09529V0Z" fill="#E9ECEF" stroke="#F8F9FA" stroke-width="0.482269" mask="url(#path-4-inside-4_6456_90192)"/>
<mask id="path-5-inside-5_6456_90192" fill="white">
<path d="M17 0C19.9437 3.51029e-08 22.8369 0.764364 25.3965 2.21827C27.956 3.67217 30.0942 5.76578 31.6017 8.29416C33.1091 10.8225 33.9342 13.699 33.9962 16.642C34.0582 19.5851 33.3549 22.4937 31.9553 25.0833L27.4728 22.6606C28.453 20.8471 28.9455 18.8103 28.9021 16.7493C28.8587 14.6884 28.2809 12.6741 27.2252 10.9035C26.1696 9.13293 24.6722 7.66682 22.8798 6.64869C21.0874 5.63055 19.0614 5.09529 17 5.09529V0Z"/>
</mask>
<path d="M17 0C19.9437 3.51029e-08 22.8369 0.764364 25.3965 2.21827C27.956 3.67217 30.0942 5.76578 31.6017 8.29416C33.1091 10.8225 33.9342 13.699 33.9962 16.642C34.0582 19.5851 33.3549 22.4937 31.9553 25.0833L27.4728 22.6606C28.453 20.8471 28.9455 18.8103 28.9021 16.7493C28.8587 14.6884 28.2809 12.6741 27.2252 10.9035C26.1696 9.13293 24.6722 7.66682 22.8798 6.64869C21.0874 5.63055 19.0614 5.09529 17 5.09529V0Z" fill="#C5D6FF" stroke="#ECF1FF" stroke-width="0.482269" mask="url(#path-5-inside-5_6456_90931)"/>
<mask id="path-6-inside-6_6456_90931" fill="white">
<path d="M17 0C19.9437 3.51029e-08 22.8369 0.764364 25.3965 2.21827C27.956 3.67217 30.0942 5.76578 31.6017 8.29416C33.1091 10.8225 33.9342 13.699 33.9962 16.642C34.0582 19.5851 33.3549 22.4937 31.9553 25.0833L27.4728 22.6606C28.453 20.8471 28.9455 18.8103 28.9021 16.7493C28.8587 14.6884 28.2809 12.6741 27.2252 10.9035C26.1696 9.13293 24.6722 7.66682 22.8798 6.64869C21.0874 5.63055 19.0614 5.09529 17 5.09529V0Z" fill="#DEE2E6" stroke="#F8F9FA" stroke-width="0.482269" mask="url(#path-5-inside-5_6456_90192)"/>
<mask id="path-6-inside-6_6456_90192" fill="white">
<path d="M17 0C20.6461 4.34798e-08 24.1957 1.17228 27.1246 3.34381C30.0536 5.51533 32.2068 8.57102 33.2664 12.0598L28.391 13.5405C27.649 11.0974 26.1411 8.95755 24.0901 7.43688C22.039 5.91621 19.5533 5.09529 17 5.09529V0Z"/>
</mask>
<path d="M17 0C20.6461 4.34798e-08 24.1957 1.17228 27.1246 3.34381C30.0536 5.51533 32.2068 8.57102 33.2664 12.0598L28.391 13.5405C27.649 11.0974 26.1411 8.95755 24.0901 7.43688C22.039 5.91621 19.5533 5.09529 17 5.09529V0Z" fill="#9FBBFF" stroke="#ECF1FF" stroke-width="0.482269" mask="url(#path-6-inside-6_6456_90931)"/>
<path d="M17 0C20.6461 4.34798e-08 24.1957 1.17228 27.1246 3.34381C30.0536 5.51533 32.2068 8.57102 33.2664 12.0598L28.391 13.5405C27.649 11.0974 26.1411 8.95755 24.0901 7.43688C22.039 5.91621 19.5533 5.09529 17 5.09529V0Z" fill="#E9ECEF" stroke="#F8F9FA" stroke-width="0.482269" mask="url(#path-6-inside-6_6456_90192)"/>
</svg>

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -459,6 +459,29 @@ class ProjectIssuesServices extends APIService {
});
}
async updateIssueLink(
workspaceSlug: string,
projectId: string,
issueId: string,
linkId: string,
data: {
metadata: any;
title: string;
url: string;
},
): Promise<any> {
return this.patch(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-links/${linkId}/`,
data
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async deleteIssueLink(
workspaceSlug: string,
projectId: string,

View File

@ -212,6 +212,28 @@ class ProjectIssuesServices extends APIService {
});
}
async updateModuleLink(
workspaceSlug: string,
projectId: string,
moduleId: string,
linkId: string,
data: {
metadata: any;
title: string;
url: string;
},
): Promise<any> {
return this.patch(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/module-links/${linkId}/`,
data
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async deleteModuleLink(
workspaceSlug: string,
projectId: string,

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

@ -56,6 +56,16 @@ export interface IIssueLink {
url: string;
}
export interface linkDetails {
created_at: Date;
created_by: string;
created_by_detail: IUserLite;
id: string;
metadata: any;
title: string;
url: string;
}
export interface IIssue {
archived_at: string;
assignees: string[];
@ -80,15 +90,7 @@ export interface IIssue {
estimate_point: number | null;
id: string;
issue_cycle: IIssueCycle | null;
issue_link: {
created_at: Date;
created_by: string;
created_by_detail: IUserLite;
id: string;
metadata: any;
title: string;
url: string;
}[];
issue_link: linkDetails[];
issue_module: IIssueModule | null;
labels: string[];
label_details: any[];

View File

@ -7,6 +7,7 @@ import type {
IWorkspaceLite,
IProjectLite,
IIssueFilterOptions,
linkDetails,
} from "types";
export interface IModule {
@ -26,15 +27,7 @@ export interface IModule {
id: string;
lead: string | null;
lead_detail: IUserLite | null;
link_module: {
created_at: Date;
created_by: string;
created_by_detail: IUserLite;
id: string;
metadata: any;
title: string;
url: string;
}[];
link_module: linkDetails[];
links_list: ModuleLink[];
members: string[];
members_list: string[];

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 <></>;
};

View File

@ -1,5 +1,5 @@
#!/bin/bash
cp ./.env.example ./.env
# cp ./.env.example ./.env
# Export for tr error in mac
export LC_ALL=C
@ -14,3 +14,16 @@ echo -e "SECRET_KEY=\"$(tr -dc 'a-z0-9' < /dev/urandom | head -c50)\"" >> ./.en
# WEB_URL for email redirection and image saving
echo -e "WEB_URL=$1" >> ./.env
# Generate Prompt for taking tiptap auth key
echo -e "\n\e[1;38m Instructions for generating TipTap Pro Extensions Auth Token \e[0m \n"
echo -e "\e[1;38m 1. Head over to TipTap cloud's Pro Extensions Page, https://collab.tiptap.dev/pro-extensions \e[0m"
echo -e "\e[1;38m 2. Copy the token given to you under the first paragraph, after 'Here it is' \e[0m \n"
read -p $'\e[1;32m Please Enter Your TipTap Pro Extensions Authentication Token: \e[0m \e[1;36m' authToken
echo "@tiptap-pro:registry=https://registry.tiptap.dev/
//registry.tiptap.dev/:_authToken=${authToken}" > .npmrc