mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: removed unused packages and upgraded to next 14 (#2944)
* fix: upgrading next package and removed unused deps * chore: unused variable removed * chore: next image icon fix * chore: unused component removed * chore: next image icon fix * chore: replace use-debounce with lodash debounce * chore: unused component removed * resolved: fixed issue with next link component * fix: updates in next config * fix: updating types pages --------- Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
This commit is contained in:
parent
804313413b
commit
ee30eb0590
@ -30,7 +30,7 @@
|
|||||||
"turbo": "^1.10.16"
|
"turbo": "^1.10.16"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/react": "18.2.0"
|
"@types/react": "18.2.39"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.19"
|
"packageManager": "yarn@1.22.19"
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
"postcss": "^8.4.29",
|
"postcss": "^8.4.29",
|
||||||
"eslint-config-next": "13.2.4",
|
"eslint-config-next": "13.2.4",
|
||||||
"@types/node": "18.15.3",
|
"@types/node": "18.15.3",
|
||||||
"@types/react": "^18.2.5",
|
"@types/react": "^18.2.39",
|
||||||
"@types/react-dom": "18.0.11",
|
"@types/react-dom": "18.0.11",
|
||||||
"tailwind-config-custom": "*",
|
"tailwind-config-custom": "*",
|
||||||
"tsconfig": "*",
|
"tsconfig": "*",
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
"@tiptap/core": "^2.1.7",
|
"@tiptap/core": "^2.1.7",
|
||||||
"@tiptap/extension-placeholder": "^2.1.11",
|
"@tiptap/extension-placeholder": "^2.1.11",
|
||||||
"@types/node": "18.15.3",
|
"@types/node": "18.15.3",
|
||||||
"@types/react": "^18.2.5",
|
"@types/react": "^18.2.39",
|
||||||
"@types/react-dom": "18.0.11",
|
"@types/react-dom": "18.0.11",
|
||||||
"eslint": "8.36.0",
|
"eslint": "8.36.0",
|
||||||
"eslint-config-next": "13.2.4",
|
"eslint-config-next": "13.2.4",
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "18.15.3",
|
"@types/node": "18.15.3",
|
||||||
"@types/react": "^18.2.35",
|
"@types/react": "^18.2.39",
|
||||||
"@types/react-dom": "^18.2.14",
|
"@types/react-dom": "^18.2.14",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"postcss": "^8.4.29",
|
"postcss": "^8.4.29",
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "18.15.3",
|
"@types/node": "18.15.3",
|
||||||
"@types/react": "^18.2.35",
|
"@types/react": "^18.2.39",
|
||||||
"@types/react-dom": "^18.2.14",
|
"@types/react-dom": "^18.2.14",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"postcss": "^8.4.29",
|
"postcss": "^8.4.29",
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "18.15.3",
|
"@types/node": "18.15.3",
|
||||||
"@types/react": "^18.2.35",
|
"@types/react": "^18.2.39",
|
||||||
"@types/react-dom": "^18.2.14",
|
"@types/react-dom": "^18.2.14",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"postcss": "^8.4.29",
|
"postcss": "^8.4.29",
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "18.15.3",
|
"@types/node": "18.15.3",
|
||||||
"@types/react": "^18.2.35",
|
"@types/react": "^18.2.39",
|
||||||
"@types/react-dom": "^18.2.14",
|
"@types/react-dom": "^18.2.14",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"tsconfig": "*",
|
"tsconfig": "*",
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.5.2",
|
"@types/node": "^20.5.2",
|
||||||
"@types/react": "^18.2.37",
|
"@types/react": "^18.2.39",
|
||||||
"@types/react-color": "^3.0.9",
|
"@types/react-color": "^3.0.9",
|
||||||
"@types/react-dom": "^18.2.15",
|
"@types/react-dom": "^18.2.15",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
|
@ -77,7 +77,9 @@ export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
|
|||||||
<div className="text-right text-xs">
|
<div className="text-right text-xs">
|
||||||
{isSignUpPage ? (
|
{isSignUpPage ? (
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<a className="text-custom-text-200 hover:text-custom-primary-100">Already have an account? Sign in.</a>
|
<span className="text-custom-text-200 hover:text-custom-primary-100">
|
||||||
|
Already have an account? Sign in.
|
||||||
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
@ -100,9 +102,9 @@ export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
|
|||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
{!isSignUpPage && (
|
{!isSignUpPage && (
|
||||||
<Link href="/sign-up">
|
<Link href="/sign-up">
|
||||||
<a className="block text-custom-text-200 hover:text-custom-primary-100 text-xs mt-4">
|
<span className="block text-custom-text-200 hover:text-custom-primary-100 text-xs mt-4">
|
||||||
Don{"'"}t have an account? Sign up.
|
Don{"'"}t have an account? Sign up.
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -130,11 +130,11 @@ const IssueNavbar = observer(() => {
|
|||||||
) : (
|
) : (
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<Link href={`/login/?next_path=${router.asPath}`}>
|
<Link href={`/login/?next_path=${router.asPath}`}>
|
||||||
<a>
|
<span>
|
||||||
<PrimaryButton className="flex-shrink-0" outline>
|
<PrimaryButton className="flex-shrink-0" outline>
|
||||||
Sign in
|
Sign in
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -53,9 +53,9 @@ export const PeekOverviewIssueActivity: React.FC<Props> = observer((props) => {
|
|||||||
Sign in to add your comment
|
Sign in to add your comment
|
||||||
</p>
|
</p>
|
||||||
<Link href={`/?next_path=${router.asPath}`}>
|
<Link href={`/?next_path=${router.asPath}`}>
|
||||||
<a>
|
<span>
|
||||||
<PrimaryButton className="flex-shrink-0 !px-7">Sign in</PrimaryButton>
|
<PrimaryButton className="flex-shrink-0 !px-7">Sign in</PrimaryButton>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
"@types/js-cookie": "^3.0.3",
|
"@types/js-cookie": "^3.0.3",
|
||||||
"@types/node": "18.14.1",
|
"@types/node": "18.14.1",
|
||||||
"@types/nprogress": "^0.2.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
"@types/react": "18.2.35",
|
"@types/react": "18.2.39",
|
||||||
"@types/react-dom": "18.0.11",
|
"@types/react-dom": "18.0.11",
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.48.2",
|
"@typescript-eslint/eslint-plugin": "^5.48.2",
|
||||||
|
@ -114,7 +114,9 @@ export const EmailSignUpForm: React.FC<Props> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="text-right text-xs">
|
<div className="text-right text-xs">
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<a className="text-custom-text-200 hover:text-custom-primary-100">Already have an account? Sign in.</a>
|
<span className="text-custom-text-200 hover:text-custom-primary-100">
|
||||||
|
Already have an account? Sign in.
|
||||||
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
import { BarDatum } from "@nivo/bar";
|
import { BarDatum } from "@nivo/bar";
|
||||||
// components
|
// components
|
||||||
import { CustomTooltip } from "./custom-tooltip";
|
import { CustomTooltip } from "./custom-tooltip";
|
||||||
|
import { Tooltip } from "@plane/ui";
|
||||||
// ui
|
// ui
|
||||||
import { BarGraph, Tooltip } from "components/ui";
|
import { BarGraph } from "components/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { findStringWithMostCharacters } from "helpers/array.helper";
|
import { findStringWithMostCharacters } from "helpers/array.helper";
|
||||||
import { generateBarColor, generateDisplayName } from "helpers/analytics.helper";
|
import { generateBarColor, generateDisplayName } from "helpers/analytics.helper";
|
||||||
|
@ -38,7 +38,7 @@ export const NotAuthorizedView: React.FC<Props> = ({ actionButton, type }) => {
|
|||||||
<p>
|
<p>
|
||||||
You have signed in as {user.email}. <br />
|
You have signed in as {user.email}. <br />
|
||||||
<Link href={`/?next=${currentPath}`}>
|
<Link href={`/?next=${currentPath}`}>
|
||||||
<a className="font-medium text-custom-text-100">Sign in</a>
|
<span className="font-medium text-custom-text-100">Sign in</span>
|
||||||
</Link>{" "}
|
</Link>{" "}
|
||||||
with different account that has access to this page.
|
with different account that has access to this page.
|
||||||
</p>
|
</p>
|
||||||
@ -46,7 +46,7 @@ export const NotAuthorizedView: React.FC<Props> = ({ actionButton, type }) => {
|
|||||||
<p>
|
<p>
|
||||||
You need to{" "}
|
You need to{" "}
|
||||||
<Link href={`/?next=${currentPath}`}>
|
<Link href={`/?next=${currentPath}`}>
|
||||||
<a className="font-medium text-custom-text-100">Sign in</a>
|
<span className="font-medium text-custom-text-100">Sign in</span>
|
||||||
</Link>{" "}
|
</Link>{" "}
|
||||||
with an account that has access to this page.
|
with an account that has access to this page.
|
||||||
</p>
|
</p>
|
||||||
|
@ -18,14 +18,14 @@ export const NotAWorkspaceMember = () => (
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-center gap-2">
|
<div className="flex items-center justify-center gap-2">
|
||||||
<Link href="/invitations">
|
<Link href="/invitations">
|
||||||
<a>
|
<span>
|
||||||
<Button variant="neutral-primary">Check pending invites</Button>
|
<Button variant="neutral-primary">Check pending invites</Button>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/create-workspace">
|
<Link href="/create-workspace">
|
||||||
<a>
|
<span>
|
||||||
<Button variant="primary">Create new workspace</Button>
|
<Button variant="primary">Create new workspace</Button>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -45,12 +45,12 @@ const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({
|
|||||||
<>
|
<>
|
||||||
{link ? (
|
{link ? (
|
||||||
<Link href={link}>
|
<Link href={link}>
|
||||||
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm ${linkTruncate ? "truncate" : ""}`}>
|
<span className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm ${linkTruncate ? "truncate" : ""}`}>
|
||||||
<p className={`${linkTruncate ? "truncate" : ""}${icon ? "flex items-center gap-2" : ""}`}>
|
<p className={`${linkTruncate ? "truncate" : ""}${icon ? "flex items-center gap-2" : ""}`}>
|
||||||
{icon ?? null}
|
{icon ?? null}
|
||||||
{title}
|
{title}
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<div className={`px-3 text-sm truncate ${unshrinkTitle ? "flex-shrink-0" : ""}`}>
|
<div className={`px-3 text-sm truncate ${unshrinkTitle ? "flex-shrink-0" : ""}`}>
|
||||||
|
@ -28,7 +28,7 @@ export const EmptyState: React.FC<Props> = ({
|
|||||||
}) => (
|
}) => (
|
||||||
<div className={`flex items-center justify-center h-full w-full`}>
|
<div className={`flex items-center justify-center h-full w-full`}>
|
||||||
<div className="text-center flex flex-col items-center w-full">
|
<div className="text-center flex flex-col items-center w-full">
|
||||||
<Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text} />
|
<Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text || "button image"} />
|
||||||
<h6 className="text-xl font-semibold mt-6 sm:mt-8 mb-3">{title}</h6>
|
<h6 className="text-xl font-semibold mt-6 sm:mt-8 mb-3">{title}</h6>
|
||||||
{description && <p className="text-custom-text-300 mb-7 sm:mb-8 px-5">{description}</p>}
|
{description && <p className="text-custom-text-300 mb-7 sm:mb-8 px-5">{description}</p>}
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
|
@ -20,7 +20,6 @@ type Props = {
|
|||||||
text: string;
|
text: string;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
};
|
};
|
||||||
secondaryButton?: React.ReactNode;
|
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,7 +28,6 @@ export const NewEmptyState: React.FC<Props> = ({
|
|||||||
description,
|
description,
|
||||||
image,
|
image,
|
||||||
primaryButton,
|
primaryButton,
|
||||||
secondaryButton,
|
|
||||||
disabled = false,
|
disabled = false,
|
||||||
comicBox,
|
comicBox,
|
||||||
}) => {
|
}) => {
|
||||||
@ -48,7 +46,7 @@ export const NewEmptyState: React.FC<Props> = ({
|
|||||||
<h3 className="font-semibold text-2xl">{title}</h3>
|
<h3 className="font-semibold text-2xl">{title}</h3>
|
||||||
{description && <p className=" text-lg">{description}</p>}
|
{description && <p className=" text-lg">{description}</p>}
|
||||||
<div className="relative w-full max-w-6xl">
|
<div className="relative w-full max-w-6xl">
|
||||||
<Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text} />
|
<Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text || "button image"} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-center items-start relative">
|
<div className="flex justify-center items-start relative">
|
||||||
|
@ -317,9 +317,9 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`}>
|
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`}>
|
||||||
<a className="bg-custom-primary text-white px-4 rounded-md py-2 text-center text-sm font-medium w-full hover:bg-custom-primary/90">
|
<span className="bg-custom-primary text-white px-4 rounded-md py-2 text-center text-sm font-medium w-full hover:bg-custom-primary/90">
|
||||||
View Cycle
|
View Cycle
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -152,7 +152,7 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = (props) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`}>
|
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`}>
|
||||||
<a className="flex flex-col justify-between p-4 h-44 w-full min-w-[250px] text-sm rounded bg-custom-background-100 border border-custom-border-100 hover:shadow-md">
|
<span className="flex flex-col justify-between p-4 h-44 w-full min-w-[250px] text-sm rounded bg-custom-background-100 border border-custom-border-100 hover:shadow-md">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-3 truncate">
|
<div className="flex items-center gap-3 truncate">
|
||||||
<span className="flex-shrink-0">
|
<span className="flex-shrink-0">
|
||||||
@ -268,7 +268,7 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -153,7 +153,7 @@ export const CyclesListItem: FC<TCyclesListItem> = (props) => {
|
|||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
/>
|
/>
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`}>
|
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`}>
|
||||||
<a className="group flex items-center justify-between gap-5 px-5 py-6 h-16 w-full text-sm bg-custom-background-100 border-b border-custom-border-100 hover:bg-custom-background-90">
|
<span className="group flex items-center justify-between gap-5 px-5 py-6 h-16 w-full text-sm bg-custom-background-100 border-b border-custom-border-100 hover:bg-custom-background-90">
|
||||||
<div className="flex items-center gap-3 w-full truncate">
|
<div className="flex items-center gap-3 w-full truncate">
|
||||||
<div className="flex items-center gap-4 truncate">
|
<div className="flex items-center gap-4 truncate">
|
||||||
<span className="flex-shrink-0">
|
<span className="flex-shrink-0">
|
||||||
@ -262,7 +262,7 @@ export const CyclesListItem: FC<TCyclesListItem> = (props) => {
|
|||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -24,7 +24,10 @@ interface ICycleDelete {
|
|||||||
export const CycleDeleteModal: React.FC<ICycleDelete> = observer((props) => {
|
export const CycleDeleteModal: React.FC<ICycleDelete> = observer((props) => {
|
||||||
const { isOpen, handleClose, cycle, workspaceSlug, projectId } = props;
|
const { isOpen, handleClose, cycle, workspaceSlug, projectId } = props;
|
||||||
// store
|
// store
|
||||||
const { cycle: cycleStore, trackEvent: { postHogEventTracker } } = useMobxStore();
|
const {
|
||||||
|
cycle: cycleStore,
|
||||||
|
trackEvent: { postHogEventTracker },
|
||||||
|
} = useMobxStore();
|
||||||
// toast
|
// toast
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
// states
|
// states
|
||||||
@ -36,25 +39,22 @@ export const CycleDeleteModal: React.FC<ICycleDelete> = observer((props) => {
|
|||||||
setLoader(true);
|
setLoader(true);
|
||||||
if (cycle?.id)
|
if (cycle?.id)
|
||||||
try {
|
try {
|
||||||
await cycleStore.removeCycle(workspaceSlug, projectId, cycle?.id).then((res) => {
|
await cycleStore
|
||||||
|
.removeCycle(workspaceSlug, projectId, cycle?.id)
|
||||||
|
.then(() => {
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Success!",
|
title: "Success!",
|
||||||
message: "Cycle deleted successfully.",
|
message: "Cycle deleted successfully.",
|
||||||
});
|
});
|
||||||
postHogEventTracker(
|
postHogEventTracker("CYCLE_DELETE", {
|
||||||
"CYCLE_DELETE",
|
state: "SUCCESS",
|
||||||
{
|
});
|
||||||
state: "SUCCESS"
|
})
|
||||||
}
|
.catch(() => {
|
||||||
);
|
postHogEventTracker("CYCLE_DELETE", {
|
||||||
}).catch((error) => {
|
state: "FAILED",
|
||||||
postHogEventTracker(
|
});
|
||||||
"CYCLE_DELETE",
|
|
||||||
{
|
|
||||||
state: "FAILED"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (cycleId || peekCycle) router.push(`/${workspaceSlug}/projects/${projectId}/cycles`);
|
if (cycleId || peekCycle) router.push(`/${workspaceSlug}/projects/${projectId}/cycles`);
|
||||||
|
@ -67,11 +67,11 @@ const IntegrationGuide = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<Link href={`/${workspaceSlug}/settings/exports?provider=${service.provider}`}>
|
<Link href={`/${workspaceSlug}/settings/exports?provider=${service.provider}`}>
|
||||||
<a>
|
<span>
|
||||||
<Button variant="primary" className="capitalize">
|
<Button variant="primary" className="capitalize">
|
||||||
{service.type}
|
{service.type}
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,7 +119,7 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
|
|||||||
<div className="flex items-center gap-1 p-1 rounded bg-custom-background-80">
|
<div className="flex items-center gap-1 p-1 rounded bg-custom-background-80">
|
||||||
{GLOBAL_VIEW_LAYOUTS.map((layout) => (
|
{GLOBAL_VIEW_LAYOUTS.map((layout) => (
|
||||||
<Link key={layout.key} href={`/${workspaceSlug}/${layout.link}`}>
|
<Link key={layout.key} href={`/${workspaceSlug}/${layout.link}`}>
|
||||||
<a>
|
<span>
|
||||||
<Tooltip tooltipContent={layout.title}>
|
<Tooltip tooltipContent={layout.title}>
|
||||||
<div
|
<div
|
||||||
className={`w-7 h-[22px] rounded grid place-items-center transition-all hover:bg-custom-background-100 overflow-hidden group ${
|
className={`w-7 h-[22px] rounded grid place-items-center transition-all hover:bg-custom-background-100 overflow-hidden group ${
|
||||||
@ -133,7 +133,7 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -185,7 +185,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
|
|||||||
</FiltersDropdown>
|
</FiltersDropdown>
|
||||||
{projectId && inboxStore.isInboxEnabled && inboxDetails && (
|
{projectId && inboxStore.isInboxEnabled && inboxDetails && (
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/inbox/${inboxStore.getInboxId(projectId)}`}>
|
<Link href={`/${workspaceSlug}/projects/${projectId}/inbox/${inboxStore.getInboxId(projectId)}`}>
|
||||||
<a>
|
<span>
|
||||||
<Button variant="neutral-primary" size="sm" className="relative">
|
<Button variant="neutral-primary" size="sm" className="relative">
|
||||||
Inbox
|
Inbox
|
||||||
{inboxDetails.pending_issue_count > 0 && (
|
{inboxDetails.pending_issue_count > 0 && (
|
||||||
@ -194,7 +194,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<Button onClick={() => setAnalyticsModal(true)} variant="neutral-primary" size="sm">
|
<Button onClick={() => setAnalyticsModal(true)} variant="neutral-primary" size="sm">
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import AudioFileIcon from "public/attachment/audio-icon.png";
|
import AudioFileIcon from "public/attachment/audio-icon.png";
|
||||||
|
|
||||||
export const AudioIcon: React.FC<Props> = ({ width, height }) => (
|
export type AudioIconProps = {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AudioIcon: React.FC<AudioIconProps> = ({ width, height }) => (
|
||||||
<Image src={AudioFileIcon} height={height} width={width} alt="AudioFileIcon" />
|
<Image src={AudioFileIcon} height={height} width={width} alt="AudioFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import CMDIcon from "public/mac-command.svg";
|
import CMDIcon from "public/mac-command.svg";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const MacCommandIcon: React.FC<Props> = ({ width = "14", height = "14" }) => (
|
export const MacCommandIcon: React.FC<ImageIconPros> = ({ width = 14, height = 14 }) => (
|
||||||
<Image src={CMDIcon} height={height} width={width} alt="CMDIcon" />
|
<Image src={CMDIcon} height={height} width={width} alt="CMDIcon" />
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import CssFileIcon from "public/attachment/css-icon.png";
|
import CssFileIcon from "public/attachment/css-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const CssIcon: React.FC<Props> = ({ width, height }) => (
|
export const CssIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={CssFileIcon} height={height} width={width} alt="CssFileIcon" />
|
<Image src={CssFileIcon} height={height} width={width} alt="CssFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import CSVFileIcon from "public/attachment/csv-icon.png";
|
import CSVFileIcon from "public/attachment/csv-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const CsvIcon: React.FC<Props> = ({ width, height }) => (
|
export const CsvIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={CSVFileIcon} height={height} width={width} alt="CSVFileIcon" />
|
<Image src={CSVFileIcon} height={height} width={width} alt="CSVFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import DefaultFileIcon from "public/attachment/default-icon.png";
|
import DefaultFileIcon from "public/attachment/default-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const DefaultIcon: React.FC<Props> = ({ width, height }) => (
|
export const DefaultIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={DefaultFileIcon} height={height} width={width} alt="DefaultFileIcon" />
|
<Image src={DefaultFileIcon} height={height} width={width} alt="DefaultFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import DocFileIcon from "public/attachment/doc-icon.png";
|
import DocFileIcon from "public/attachment/doc-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const DocIcon: React.FC<Props> = ({ width, height }) => (
|
export const DocIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={DocFileIcon} height={height} width={width} alt="DocFileIcon" />
|
<Image src={DocFileIcon} height={height} width={width} alt="DocFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import FigmaFileIcon from "public/attachment/figma-icon.png";
|
import FigmaFileIcon from "public/attachment/figma-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const FigmaIcon: React.FC<Props> = ({ width, height }) => (
|
export const FigmaIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={FigmaFileIcon} height={height} width={width} alt="FigmaFileIcon" />
|
<Image src={FigmaFileIcon} height={height} width={width} alt="FigmaFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import HtmlFileIcon from "public/attachment/html-icon.png";
|
import HtmlFileIcon from "public/attachment/html-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const HtmlIcon: React.FC<Props> = ({ width, height }) => (
|
export const HtmlIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={HtmlFileIcon} height={height} width={width} alt="HtmlFileIcon" />
|
<Image src={HtmlFileIcon} height={height} width={width} alt="HtmlFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import ImgFileIcon from "public/attachment/img-icon.png";
|
import ImgFileIcon from "public/attachment/img-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const ImgIcon: React.FC<Props> = ({ width, height }) => (
|
export const ImgIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={ImgFileIcon} height={height} width={width} alt="ImgFileIcon" />
|
<Image src={ImgFileIcon} height={height} width={width} alt="ImgFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import JpgFileIcon from "public/attachment/jpg-icon.png";
|
import JpgFileIcon from "public/attachment/jpg-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const JpgIcon: React.FC<Props> = ({ width, height }) => (
|
export const JpgIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={JpgFileIcon} height={height} width={width} alt="JpgFileIcon" />
|
<Image src={JpgFileIcon} height={height} width={width} alt="JpgFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import JsFileIcon from "public/attachment/js-icon.png";
|
import JsFileIcon from "public/attachment/js-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const JavaScriptIcon: React.FC<Props> = ({ width, height }) => (
|
export const JavaScriptIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={JsFileIcon} height={height} width={width} alt="JsFileIcon" />
|
<Image src={JsFileIcon} height={height} width={width} alt="JsFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import PDFFileIcon from "public/attachment/pdf-icon.png";
|
import PDFFileIcon from "public/attachment/pdf-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const PdfIcon: React.FC<Props> = ({ width, height }) => (
|
export const PdfIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={PDFFileIcon} height={height} width={width} alt="PDFFileIcon" />
|
<Image src={PDFFileIcon} height={height} width={width} alt="PDFFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import PngFileIcon from "public/attachment/png-icon.png";
|
import PngFileIcon from "public/attachment/png-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const PngIcon: React.FC<Props> = ({ width, height }) => (
|
export const PngIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={PngFileIcon} height={height} width={width} alt="PngFileIcon" />
|
<Image src={PngFileIcon} height={height} width={width} alt="PngFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import SheetFileIcon from "public/attachment/excel-icon.png";
|
import SheetFileIcon from "public/attachment/excel-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const SheetIcon: React.FC<Props> = ({ width, height }) => (
|
export const SheetIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={SheetFileIcon} height={height} width={width} alt="SheetFileIcon" />
|
<Image src={SheetFileIcon} height={height} width={width} alt="SheetFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import SvgFileIcon from "public/attachment/svg-icon.png";
|
import SvgFileIcon from "public/attachment/svg-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const SvgIcon: React.FC<Props> = ({ width, height }) => (
|
export const SvgIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={SvgFileIcon} height={height} width={width} alt="SvgFileIcon" />
|
<Image src={SvgFileIcon} height={height} width={width} alt="SvgFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import TxtFileIcon from "public/attachment/txt-icon.png";
|
import TxtFileIcon from "public/attachment/txt-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const TxtIcon: React.FC<Props> = ({ width, height }) => (
|
export const TxtIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={TxtFileIcon} height={height} width={width} alt="TxtFileIcon" />
|
<Image src={TxtFileIcon} height={height} width={width} alt="TxtFileIcon" />
|
||||||
);
|
);
|
||||||
|
5
web/components/icons/types.d.ts
vendored
5
web/components/icons/types.d.ts
vendored
@ -4,3 +4,8 @@ export type Props = {
|
|||||||
height?: string | number;
|
height?: string | number;
|
||||||
color?: string;
|
color?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ImageIconPros = {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
};
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// image
|
||||||
import type { Props } from "./types";
|
|
||||||
import VideoFileIcon from "public/attachment/video-icon.png";
|
import VideoFileIcon from "public/attachment/video-icon.png";
|
||||||
|
// type
|
||||||
|
import type { ImageIconPros } from "./types";
|
||||||
|
|
||||||
export const VideoIcon: React.FC<Props> = ({ width, height }) => (
|
export const VideoIcon: React.FC<ImageIconPros> = ({ width, height }) => (
|
||||||
<Image src={VideoFileIcon} height={height} width={width} alt="VideoFileIcon" />
|
<Image src={VideoFileIcon} height={height} width={width} alt="VideoFileIcon" />
|
||||||
);
|
);
|
||||||
|
@ -27,7 +27,7 @@ export const InboxIssueCard: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/inbox/${inboxId}?inboxIssueId=${issue.issue_inbox[0].id}`}>
|
<Link href={`/${workspaceSlug}/projects/${projectId}/inbox/${inboxId}?inboxIssueId=${issue.issue_inbox[0].id}`}>
|
||||||
<a>
|
<span>
|
||||||
<div
|
<div
|
||||||
id={issue.id}
|
id={issue.id}
|
||||||
className={`relative min-h-[5rem] cursor-pointer select-none space-y-3 py-2 px-4 border-b border-custom-border-200 hover:bg-custom-primary/5 ${
|
className={`relative min-h-[5rem] cursor-pointer select-none space-y-3 py-2 px-4 border-b border-custom-border-200 hover:bg-custom-primary/5 ${
|
||||||
@ -91,7 +91,7 @@ export const InboxIssueCard: React.FC<Props> = (props) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Input, ToggleSwitch } from "@plane/ui";
|
import { Button, Input } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { IInstance, IInstanceAdmin } from "types/instance";
|
import { IInstance, IInstanceAdmin } from "types/instance";
|
||||||
// hooks
|
// hooks
|
||||||
|
@ -97,16 +97,13 @@ export const InstanceHelpSection: FC = () => {
|
|||||||
{helpOptions.map(({ name, Icon, href, onClick }) => {
|
{helpOptions.map(({ name, Icon, href, onClick }) => {
|
||||||
if (href)
|
if (href)
|
||||||
return (
|
return (
|
||||||
<Link href={href} key={name}>
|
<Link href={href} key={name} target="_blank">
|
||||||
<a
|
<span className="flex items-center gap-x-2 rounded px-2 py-1 text-xs hover:bg-custom-background-80">
|
||||||
target="_blank"
|
|
||||||
className="flex items-center gap-x-2 rounded px-2 py-1 text-xs hover:bg-custom-background-80"
|
|
||||||
>
|
|
||||||
<div className="grid place-items-center flex-shrink-0">
|
<div className="grid place-items-center flex-shrink-0">
|
||||||
<Icon className="text-custom-text-200 h-3.5 w-3.5" size={14} />
|
<Icon className="text-custom-text-200 h-3.5 w-3.5" size={14} />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs">{name}</span>
|
<span className="text-xs">{name}</span>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
|
@ -67,12 +67,12 @@ export const InstanceAdminRestriction: FC<InstanceAdminRestrictionProps> = ({ re
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-center gap-2">
|
<div className="flex items-center justify-center gap-2">
|
||||||
<Link href={`/${redirectWorkspaceSlug}`}>
|
<Link href={`/${redirectWorkspaceSlug}`}>
|
||||||
<a>
|
<span>
|
||||||
<Button variant="primary" size="sm">
|
<Button variant="primary" size="sm">
|
||||||
<LayoutGrid width={16} height={16} />
|
<LayoutGrid width={16} height={16} />
|
||||||
To the workspace
|
To the workspace
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,9 +78,9 @@ export const InstanceSidebarDropdown = observer(() => {
|
|||||||
<Tooltip position="bottom-left" tooltipContent="Exit God Mode">
|
<Tooltip position="bottom-left" tooltipContent="Exit God Mode">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<Link href={`/${redirectWorkspaceSlug}`}>
|
<Link href={`/${redirectWorkspaceSlug}`}>
|
||||||
<a>
|
<span>
|
||||||
<LogIn className="h-5 w-5 text-custom-text-200 rotate-180" />
|
<LogIn className="h-5 w-5 text-custom-text-200 rotate-180" />
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -119,10 +119,10 @@ export const InstanceSidebarDropdown = observer(() => {
|
|||||||
{PROFILE_LINKS.map((link) => (
|
{PROFILE_LINKS.map((link) => (
|
||||||
<Menu.Item key={link.key} as="button" type="button">
|
<Menu.Item key={link.key} as="button" type="button">
|
||||||
<Link href={link.link}>
|
<Link href={link.link}>
|
||||||
<a className="flex w-full items-center gap-2 rounded px-2 py-1 hover:bg-custom-sidebar-background-80">
|
<span className="flex w-full items-center gap-2 rounded px-2 py-1 hover:bg-custom-sidebar-background-80">
|
||||||
<link.icon className="h-4 w-4 stroke-[1.5]" />
|
<link.icon className="h-4 w-4 stroke-[1.5]" />
|
||||||
{link.name}
|
{link.name}
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
@ -142,9 +142,9 @@ export const InstanceSidebarDropdown = observer(() => {
|
|||||||
<div className="p-2 pb-0">
|
<div className="p-2 pb-0">
|
||||||
<Menu.Item as="button" type="button" className="w-full">
|
<Menu.Item as="button" type="button" className="w-full">
|
||||||
<Link href={`/${redirectWorkspaceSlug}`}>
|
<Link href={`/${redirectWorkspaceSlug}`}>
|
||||||
<a className="flex w-full items-center justify-center rounded px-2 py-1 text-sm font-medium text-custom-primary-100 hover:text-custom-primary-200 bg-custom-primary-100/20 hover:bg-custom-primary-100/30">
|
<span className="flex w-full items-center justify-center rounded px-2 py-1 text-sm font-medium text-custom-primary-100 hover:text-custom-primary-200 bg-custom-primary-100/20 hover:bg-custom-primary-100/30">
|
||||||
Exit God Mode
|
Exit God Mode
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,7 +54,7 @@ export const InstanceAdminSidebarMenu = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Link key={index} href={item.href}>
|
<Link key={index} href={item.href}>
|
||||||
<a className="block w-full">
|
<span className="block w-full">
|
||||||
<Tooltip tooltipContent={item.name} position="right" className="ml-2" disabled={!sidebarCollapsed}>
|
<Tooltip tooltipContent={item.name} position="right" className="ml-2" disabled={!sidebarCollapsed}>
|
||||||
<div
|
<div
|
||||||
className={`group flex w-full items-center gap-3 rounded-md px-3 py-2 outline-none ${
|
className={`group flex w-full items-center gap-3 rounded-md px-3 py-2 outline-none ${
|
||||||
@ -84,7 +84,7 @@ export const InstanceAdminSidebarMenu = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -165,10 +165,10 @@ export const GithubImporterRoot: React.FC<Props> = () => {
|
|||||||
<form onSubmit={handleSubmit(createGithubImporterService)}>
|
<form onSubmit={handleSubmit(createGithubImporterService)}>
|
||||||
<div className="space-y-2 mt-4">
|
<div className="space-y-2 mt-4">
|
||||||
<Link href={`/${workspaceSlug}/settings/imports`}>
|
<Link href={`/${workspaceSlug}/settings/imports`}>
|
||||||
<div className="inline-flex cursor-pointer items-center gap-2 text-sm font-medium text-custom-text-200 hover:text-custom-text-100">
|
<span className="inline-flex cursor-pointer items-center gap-2 text-sm font-medium text-custom-text-200 hover:text-custom-text-100">
|
||||||
<ArrowLeft className="h-3 w-3" />
|
<ArrowLeft className="h-3 w-3" />
|
||||||
<div>Cancel import & go back</div>
|
<div>Cancel import & go back</div>
|
||||||
</div>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="space-y-4 rounded-[10px] border border-custom-border-200 bg-custom-background-100 p-4">
|
<div className="space-y-4 rounded-[10px] border border-custom-border-200 bg-custom-background-100 p-4">
|
||||||
|
@ -89,9 +89,9 @@ const IntegrationGuide = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<Link href={`/${workspaceSlug}/settings/imports?provider=${service.provider}`}>
|
<Link href={`/${workspaceSlug}/settings/imports?provider=${service.provider}`}>
|
||||||
<a>
|
<span>
|
||||||
<Button variant="primary">{service.type}</Button>
|
<Button variant="primary">{service.type}</Button>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,7 +16,11 @@ export const JiraGetImportDetail: React.FC = observer(() => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
const { project: projectStore, commandPalette: commandPaletteStore, trackEvent: { setTrackElement } } = useMobxStore();
|
const {
|
||||||
|
project: projectStore,
|
||||||
|
commandPalette: commandPaletteStore,
|
||||||
|
trackEvent: { setTrackElement },
|
||||||
|
} = useMobxStore();
|
||||||
const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined;
|
const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -31,10 +35,8 @@ export const JiraGetImportDetail: React.FC = observer(() => {
|
|||||||
<h3 className="font-semibold">Jira Personal Access Token</h3>
|
<h3 className="font-semibold">Jira Personal Access Token</h3>
|
||||||
<p className="text-sm text-custom-text-200">
|
<p className="text-sm text-custom-text-200">
|
||||||
Get to know your access token by navigating to{" "}
|
Get to know your access token by navigating to{" "}
|
||||||
<Link href="https://id.atlassian.com/manage-profile/security/api-tokens">
|
<Link href="https://id.atlassian.com/manage-profile/security/api-tokens" target="_blank" rel="noreferrer">
|
||||||
<a className="text-custom-primary underline" target="_blank" rel="noreferrer">
|
<span className="text-custom-primary underline">Atlassian Settings</span>
|
||||||
Atlassian Settings
|
|
||||||
</a>
|
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -192,7 +194,7 @@ export const JiraGetImportDetail: React.FC = observer(() => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTrackElement("JIRA_IMPORT_DETAIL");
|
setTrackElement("JIRA_IMPORT_DETAIL");
|
||||||
commandPaletteStore.toggleCreateProjectModal(true)
|
commandPaletteStore.toggleCreateProjectModal(true);
|
||||||
}}
|
}}
|
||||||
className="flex cursor-pointer select-none items-center space-x-2 truncate rounded px-1 py-1.5 text-custom-text-200"
|
className="flex cursor-pointer select-none items-center space-x-2 truncate rounded px-1 py-1.5 text-custom-text-200"
|
||||||
>
|
>
|
||||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
|||||||
import { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
import { FormProvider, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
// icons
|
// icons
|
||||||
import { ArrowLeft, Check, List, Settings, Users2 } from "lucide-react";
|
import { ArrowLeft, Check, List, Settings } from "lucide-react";
|
||||||
// services
|
// services
|
||||||
import { JiraImporterService } from "services/integrations";
|
import { JiraImporterService } from "services/integrations";
|
||||||
// fetch keys
|
// fetch keys
|
||||||
@ -100,12 +100,12 @@ export const JiraImporterRoot: React.FC<Props> = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col space-y-2 mt-4">
|
<div className="flex h-full flex-col space-y-2 mt-4">
|
||||||
<Link href={`/${workspaceSlug}/settings/imports`}>
|
<Link href={`/${workspaceSlug}/settings/imports`}>
|
||||||
<div className="inline-flex cursor-pointer items-center gap-2 text-sm font-medium text-custom-text-200 hover:text-custom-text-100">
|
<span className="inline-flex cursor-pointer items-center gap-2 text-sm font-medium text-custom-text-200 hover:text-custom-text-100">
|
||||||
<div>
|
<div>
|
||||||
<ArrowLeft className="h-3 w-3" />
|
<ArrowLeft className="h-3 w-3" />
|
||||||
</div>
|
</div>
|
||||||
<div>Cancel import & go back</div>
|
<div>Cancel import & go back</div>
|
||||||
</div>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="flex h-full flex-col space-y-4 rounded-[10px] border border-custom-border-200 bg-custom-background-100 p-4">
|
<div className="flex h-full flex-col space-y-4 rounded-[10px] border border-custom-border-200 bg-custom-background-100 p-4">
|
||||||
|
@ -105,11 +105,11 @@ export const IssueActivitySection: React.FC<Props> = ({
|
|||||||
<span className="text-gray font-medium">{activityItem.actor_detail.first_name} Bot</span>
|
<span className="text-gray font-medium">{activityItem.actor_detail.first_name} Bot</span>
|
||||||
) : (
|
) : (
|
||||||
<Link href={`/${workspaceSlug}/profile/${activityItem.actor_detail.id}`}>
|
<Link href={`/${workspaceSlug}/profile/${activityItem.actor_detail.id}`}>
|
||||||
<a className="text-gray font-medium">
|
<span className="text-gray font-medium">
|
||||||
{activityItem.actor_detail.is_bot
|
{activityItem.actor_detail.is_bot
|
||||||
? activityItem.actor_detail.first_name
|
? activityItem.actor_detail.first_name
|
||||||
: activityItem.actor_detail.display_name}
|
: activityItem.actor_detail.display_name}
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}{" "}
|
)}{" "}
|
||||||
{message}{" "}
|
{message}{" "}
|
||||||
|
@ -59,8 +59,8 @@ export const IssueAttachments = () => {
|
|||||||
key={file.id}
|
key={file.id}
|
||||||
className="flex h-[60px] items-center justify-between gap-1 rounded-md border-[2px] border-custom-border-200 bg-custom-background-100 px-4 py-2 text-sm"
|
className="flex h-[60px] items-center justify-between gap-1 rounded-md border-[2px] border-custom-border-200 bg-custom-background-100 px-4 py-2 text-sm"
|
||||||
>
|
>
|
||||||
<Link href={file.asset}>
|
<Link href={file.asset} target="_blank">
|
||||||
<a target="_blank">
|
<span>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="h-7 w-7">{getFileIcon(getFileExtension(file.asset))}</div>
|
<div className="h-7 w-7">{getFileIcon(getFileExtension(file.asset))}</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
@ -85,7 +85,7 @@ export const IssueAttachments = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
@ -2,7 +2,7 @@ import { ChangeEvent, FC, useCallback, useEffect, useState } from "react";
|
|||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// hooks
|
// hooks
|
||||||
import useReloadConfirmations from "hooks/use-reload-confirmation";
|
import useReloadConfirmations from "hooks/use-reload-confirmation";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import debounce from "lodash/debounce";
|
||||||
// components
|
// components
|
||||||
import { TextArea } from "@plane/ui";
|
import { TextArea } from "@plane/ui";
|
||||||
import { RichTextEditor } from "@plane/rich-text-editor";
|
import { RichTextEditor } from "@plane/rich-text-editor";
|
||||||
@ -93,7 +93,7 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = (props) => {
|
|||||||
});
|
});
|
||||||
}, [issue, reset]);
|
}, [issue, reset]);
|
||||||
|
|
||||||
const debouncedFormSave = useDebouncedCallback(async () => {
|
const debouncedFormSave = debounce(async () => {
|
||||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
|
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
|
||||||
}, 1500);
|
}, 1500);
|
||||||
|
|
||||||
|
@ -7,12 +7,8 @@ import { EmptyState } from "components/common";
|
|||||||
// assets
|
// assets
|
||||||
import emptyIssue from "public/empty-state/issue.svg";
|
import emptyIssue from "public/empty-state/issue.svg";
|
||||||
import { EProjectStore } from "store/command-palette.store";
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
|
|
||||||
export const ProjectViewEmptyState: React.FC = observer(() => {
|
export const ProjectViewEmptyState: React.FC = observer(() => {
|
||||||
const router = useRouter();
|
|
||||||
const { viewId } = router.query as { viewId: string };
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
commandPalette: commandPaletteStore,
|
commandPalette: commandPaletteStore,
|
||||||
trackEvent: { setTrackElement },
|
trackEvent: { setTrackElement },
|
||||||
|
@ -3,11 +3,10 @@ import { PlusIcon } from "lucide-react";
|
|||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { EmptyState } from "components/common";
|
import { NewEmptyState } from "components/common/new-empty-state";
|
||||||
// assets
|
// assets
|
||||||
import emptyIssue from "public/empty-state/empty_issues.webp";
|
import emptyIssue from "public/empty-state/empty_issues.webp";
|
||||||
import { EProjectStore } from "store/command-palette.store";
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
import { NewEmptyState } from "components/common/new-empty-state";
|
|
||||||
|
|
||||||
export const ProjectEmptyState: React.FC = observer(() => {
|
export const ProjectEmptyState: React.FC = observer(() => {
|
||||||
const {
|
const {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import React, { useState } from "react";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterOption } from "components/issues";
|
||||||
// types
|
// types
|
||||||
import { IIssueDisplayFilterOptions, TIssueExtraOptions } from "types";
|
import { IIssueDisplayFilterOptions, TIssueExtraOptions } from "types";
|
||||||
// constants
|
// constants
|
||||||
|
@ -10,7 +10,6 @@ import { KanbanIssueBlocksList, KanBanQuickAddIssueForm } from "components/issue
|
|||||||
import { IIssueDisplayProperties, IIssue, IState } from "types";
|
import { IIssueDisplayProperties, IIssue, IState } from "types";
|
||||||
// constants
|
// constants
|
||||||
import { getValueFromObject } from "constants/issue";
|
import { getValueFromObject } from "constants/issue";
|
||||||
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
import { IIssueResponse, IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues } from "store/issues/types";
|
import { IIssueResponse, IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues } from "store/issues/types";
|
||||||
import { EProjectStore } from "store/command-palette.store";
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
// services
|
|
||||||
import { ModuleService } from "services/module.service";
|
|
||||||
import { IssueService } from "services/issue";
|
|
||||||
// components
|
// components
|
||||||
import { CustomMenu } from "@plane/ui";
|
import { CustomMenu } from "@plane/ui";
|
||||||
import { CreateUpdateIssueModal } from "components/issues/modal";
|
import { CreateUpdateIssueModal } from "components/issues/modal";
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { AlertCircle, SignalHigh, SignalMedium, SignalLow, Ban } from "lucide-react";
|
|
||||||
// components
|
// components
|
||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
||||||
|
@ -81,7 +81,7 @@ export const KanBanQuickAddIssueForm: React.FC<IKanBanQuickAddIssueForm> = obser
|
|||||||
handleSubmit,
|
handleSubmit,
|
||||||
setFocus,
|
setFocus,
|
||||||
register,
|
register,
|
||||||
formState: { errors, isSubmitting },
|
formState: { isSubmitting },
|
||||||
} = useForm<IIssue>({ defaultValues });
|
} = useForm<IIssue>({ defaultValues });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -4,7 +4,7 @@ import { ListGroupByHeaderRoot } from "./headers/group-by-root";
|
|||||||
import { IssueBlocksList, ListQuickAddIssueForm } from "components/issues";
|
import { IssueBlocksList, ListQuickAddIssueForm } from "components/issues";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IIssueDisplayProperties, IIssueLabel, IProject, IState, IUserLite } from "types";
|
import { IIssue, IIssueDisplayProperties, IIssueLabel, IProject, IState, IUserLite } from "types";
|
||||||
import { IIssueResponse, IGroupedIssues, TUnGroupedIssues, ViewFlags } from "store/issues/types";
|
import { IIssueResponse, IGroupedIssues, TUnGroupedIssues } from "store/issues/types";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
// constants
|
// constants
|
||||||
import { getValueFromObject } from "constants/issue";
|
import { getValueFromObject } from "constants/issue";
|
||||||
|
@ -10,7 +10,6 @@ import { Avatar, AvatarGroup, Tooltip } from "@plane/ui";
|
|||||||
import { Placement } from "@popperjs/core";
|
import { Placement } from "@popperjs/core";
|
||||||
|
|
||||||
export interface IIssuePropertyAssignee {
|
export interface IIssuePropertyAssignee {
|
||||||
view?: "profile" | "workspace" | "project";
|
|
||||||
projectId: string | null;
|
projectId: string | null;
|
||||||
value: string[] | string;
|
value: string[] | string;
|
||||||
onChange: (data: string[]) => void;
|
onChange: (data: string[]) => void;
|
||||||
@ -26,7 +25,6 @@ export interface IIssuePropertyAssignee {
|
|||||||
|
|
||||||
export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer((props) => {
|
export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer((props) => {
|
||||||
const {
|
const {
|
||||||
view,
|
|
||||||
projectId,
|
projectId,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
|
@ -12,7 +12,6 @@ import { Placement } from "@popperjs/core";
|
|||||||
import { RootStore } from "store/root";
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
export interface IIssuePropertyLabels {
|
export interface IIssuePropertyLabels {
|
||||||
view?: "profile" | "workspace" | "project";
|
|
||||||
projectId: string | null;
|
projectId: string | null;
|
||||||
value: string[];
|
value: string[];
|
||||||
onChange: (data: string[]) => void;
|
onChange: (data: string[]) => void;
|
||||||
@ -28,7 +27,6 @@ export interface IIssuePropertyLabels {
|
|||||||
|
|
||||||
export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((props) => {
|
export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((props) => {
|
||||||
const {
|
const {
|
||||||
view,
|
|
||||||
projectId,
|
projectId,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
|
@ -15,7 +15,6 @@ import { Placement } from "@popperjs/core";
|
|||||||
import { RootStore } from "store/root";
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
export interface IIssuePropertyState {
|
export interface IIssuePropertyState {
|
||||||
view?: "profile" | "workspace" | "project";
|
|
||||||
projectId: string | null;
|
projectId: string | null;
|
||||||
value: any | string | null;
|
value: any | string | null;
|
||||||
onChange: (state: IState) => void;
|
onChange: (state: IState) => void;
|
||||||
@ -29,7 +28,6 @@ export interface IIssuePropertyState {
|
|||||||
|
|
||||||
export const IssuePropertyState: React.FC<IIssuePropertyState> = observer((props) => {
|
export const IssuePropertyState: React.FC<IIssuePropertyState> = observer((props) => {
|
||||||
const {
|
const {
|
||||||
view,
|
|
||||||
projectId,
|
projectId,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
|
@ -93,11 +93,11 @@ export const IssueActivityCard: FC<IssueActivityCard> = (props) => {
|
|||||||
<span className="text-gray font-medium">{activityItem.actor_detail.first_name} Bot</span>
|
<span className="text-gray font-medium">{activityItem.actor_detail.first_name} Bot</span>
|
||||||
) : (
|
) : (
|
||||||
<Link href={`/${workspaceSlug}/profile/${activityItem.actor_detail.id}`}>
|
<Link href={`/${workspaceSlug}/profile/${activityItem.actor_detail.id}`}>
|
||||||
<a className="text-gray font-medium">
|
<span className="text-gray font-medium">
|
||||||
{activityItem.actor_detail.is_bot
|
{activityItem.actor_detail.is_bot
|
||||||
? activityItem.actor_detail.first_name
|
? activityItem.actor_detail.first_name
|
||||||
: activityItem.actor_detail.display_name}
|
: activityItem.actor_detail.display_name}
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}{" "}
|
)}{" "}
|
||||||
{message}{" "}
|
{message}{" "}
|
||||||
|
@ -6,7 +6,7 @@ import { RichTextEditor } from "@plane/rich-text-editor";
|
|||||||
import { TextArea } from "@plane/ui";
|
import { TextArea } from "@plane/ui";
|
||||||
import { IssueReaction } from "./reactions";
|
import { IssueReaction } from "./reactions";
|
||||||
// hooks
|
// hooks
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import debounce from "lodash/debounce";
|
||||||
import useReloadConfirmations from "hooks/use-reload-confirmation";
|
import useReloadConfirmations from "hooks/use-reload-confirmation";
|
||||||
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
||||||
// types
|
// types
|
||||||
@ -74,7 +74,7 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
|
|||||||
}
|
}
|
||||||
}, [issueTitleCurrentValue, localTitleValue]);
|
}, [issueTitleCurrentValue, localTitleValue]);
|
||||||
|
|
||||||
const debouncedFormSave = useDebouncedCallback(async () => {
|
const debouncedFormSave = debounce(async () => {
|
||||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
|
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
|
||||||
}, 1500);
|
}, 1500);
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
|
|
||||||
await issueDraftService
|
await issueDraftService
|
||||||
.createDraftIssue(workspaceSlug as string, activeProject ?? "", payload)
|
.createDraftIssue(workspaceSlug as string, activeProject ?? "", payload)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Success!",
|
title: "Success!",
|
||||||
|
@ -139,7 +139,7 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
|
|||||||
)}
|
)}
|
||||||
<DeleteModuleModal data={module} isOpen={deleteModal} onClose={() => setDeleteModal(false)} />
|
<DeleteModuleModal data={module} isOpen={deleteModal} onClose={() => setDeleteModal(false)} />
|
||||||
<Link href={`/${workspaceSlug}/projects/${module.project}/modules/${module.id}`}>
|
<Link href={`/${workspaceSlug}/projects/${module.project}/modules/${module.id}`}>
|
||||||
<a className="flex flex-col justify-between p-4 h-44 w-full min-w-[250px] text-sm rounded bg-custom-background-100 border border-custom-border-100 hover:shadow-md">
|
<span className="flex flex-col justify-between p-4 h-44 w-full min-w-[250px] text-sm rounded bg-custom-background-100 border border-custom-border-100 hover:shadow-md">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<Tooltip tooltipContent={module.name} position="top">
|
<Tooltip tooltipContent={module.name} position="top">
|
||||||
@ -249,7 +249,7 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -128,7 +128,7 @@ export const ModuleListItem: React.FC<Props> = observer((props) => {
|
|||||||
)}
|
)}
|
||||||
<DeleteModuleModal data={module} isOpen={deleteModal} onClose={() => setDeleteModal(false)} />
|
<DeleteModuleModal data={module} isOpen={deleteModal} onClose={() => setDeleteModal(false)} />
|
||||||
<Link href={`/${workspaceSlug}/projects/${module.project}/modules/${module.id}`}>
|
<Link href={`/${workspaceSlug}/projects/${module.project}/modules/${module.id}`}>
|
||||||
<a className="group flex items-center justify-between gap-5 px-5 py-6 h-16 w-full text-sm bg-custom-background-100 border-b border-custom-border-100 hover:bg-custom-background-90">
|
<span className="group flex items-center justify-between gap-5 px-5 py-6 h-16 w-full text-sm bg-custom-background-100 border-b border-custom-border-100 hover:bg-custom-background-90">
|
||||||
<div className="flex items-center gap-3 w-full truncate">
|
<div className="flex items-center gap-3 w-full truncate">
|
||||||
<div className="flex items-center gap-4 truncate">
|
<div className="flex items-center gap-4 truncate">
|
||||||
<span className="flex-shrink-0">
|
<span className="flex-shrink-0">
|
||||||
@ -225,7 +225,7 @@ export const ModuleListItem: React.FC<Props> = observer((props) => {
|
|||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -7,7 +7,6 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import useLocalStorage from "hooks/use-local-storage";
|
import useLocalStorage from "hooks/use-local-storage";
|
||||||
// components
|
// components
|
||||||
import { ModuleCardItem, ModuleListItem, ModulePeekOverview, ModulesListGanttChartView } from "components/modules";
|
import { ModuleCardItem, ModuleListItem, ModulePeekOverview, ModulesListGanttChartView } from "components/modules";
|
||||||
import { EmptyState } from "components/common";
|
|
||||||
// ui
|
// ui
|
||||||
import { Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// assets
|
// assets
|
||||||
|
@ -32,14 +32,12 @@ export const Invitations: React.FC<Props> = (props) => {
|
|||||||
const {
|
const {
|
||||||
workspace: workspaceStore,
|
workspace: workspaceStore,
|
||||||
user: { currentUser, updateCurrentUser },
|
user: { currentUser, updateCurrentUser },
|
||||||
trackEvent: { postHogEventTracker }
|
trackEvent: { postHogEventTracker },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const {
|
const { data: invitations, mutate: mutateInvitations } = useSWR(USER_WORKSPACE_INVITATIONS, () =>
|
||||||
data: invitations,
|
workspaceService.userWorkspaceInvitations()
|
||||||
mutate: mutateInvitations,
|
);
|
||||||
isLoading,
|
|
||||||
} = useSWR(USER_WORKSPACE_INVITATIONS, () => workspaceService.userWorkspaceInvitations());
|
|
||||||
|
|
||||||
const handleInvitation = (workspace_invitation: IWorkspaceMemberInvitation, action: "accepted" | "withdraw") => {
|
const handleInvitation = (workspace_invitation: IWorkspaceMemberInvitation, action: "accepted" | "withdraw") => {
|
||||||
if (action === "accepted") {
|
if (action === "accepted") {
|
||||||
@ -74,7 +72,8 @@ export const Invitations: React.FC<Props> = (props) => {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
postHogEventTracker("WORKSPACE_USER_INVITE_ACCEPT", { state: "FAILED" });
|
postHogEventTracker("WORKSPACE_USER_INVITE_ACCEPT", { state: "FAILED" });
|
||||||
}).finally(() => setIsJoiningWorkspaces(false));
|
})
|
||||||
|
.finally(() => setIsJoiningWorkspaces(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
return invitations && invitations.length > 0 ? (
|
return invitations && invitations.length > 0 ? (
|
||||||
@ -89,7 +88,8 @@ export const Invitations: React.FC<Props> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={invitation.id}
|
key={invitation.id}
|
||||||
className={`flex cursor-pointer items-center gap-2 border p-3.5 rounded ${isSelected
|
className={`flex cursor-pointer items-center gap-2 border p-3.5 rounded ${
|
||||||
|
isSelected
|
||||||
? "border-custom-primary-100"
|
? "border-custom-primary-100"
|
||||||
: "border-onboarding-border-200 hover:bg-onboarding-background-300/30"
|
: "border-onboarding-border-200 hover:bg-onboarding-background-300/30"
|
||||||
}`}
|
}`}
|
||||||
|
@ -20,7 +20,7 @@ export const JoinWorkspaces: React.FC<Props> = ({ stepChange, setTryDiffAccount
|
|||||||
control,
|
control,
|
||||||
setValue,
|
setValue,
|
||||||
watch,
|
watch,
|
||||||
formState: { errors, isSubmitting, isValid },
|
formState: { errors, isSubmitting },
|
||||||
} = useForm<IWorkspace>({
|
} = useForm<IWorkspace>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: "",
|
name: "",
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Image from "next/image";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
@ -9,9 +8,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { TourRoot } from "components/onboarding";
|
import { TourRoot } from "components/onboarding";
|
||||||
import { UserGreetingsView } from "components/user";
|
import { UserGreetingsView } from "components/user";
|
||||||
import { CompletedIssuesGraph, IssuesList, IssuesPieChart, IssuesStats } from "components/workspace";
|
import { CompletedIssuesGraph, IssuesList, IssuesPieChart, IssuesStats } from "components/workspace";
|
||||||
import { Button } from "@plane/ui";
|
|
||||||
// images
|
// images
|
||||||
import emptyDashboard from "public/empty-state/dashboard.svg";
|
|
||||||
import { NewEmptyState } from "components/common/new-empty-state";
|
import { NewEmptyState } from "components/common/new-empty-state";
|
||||||
import emptyProject from "public/empty-state/dashboard_empty_project.webp";
|
import emptyProject from "public/empty-state/dashboard_empty_project.webp";
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ export const PagesListItem: FC<IPagesListItem> = observer((props) => {
|
|||||||
<DeletePageModal isOpen={deletePageModal} onClose={() => setDeletePageModal(false)} data={page} />
|
<DeletePageModal isOpen={deletePageModal} onClose={() => setDeletePageModal(false)} data={page} />
|
||||||
<li>
|
<li>
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/pages/${page.id}`}>
|
<Link href={`/${workspaceSlug}/projects/${projectId}/pages/${page.id}`}>
|
||||||
<a>
|
<span>
|
||||||
<div className="relative rounded p-4 text-custom-text-200 hover:bg-custom-background-80">
|
<div className="relative rounded p-4 text-custom-text-200 hover:bg-custom-background-80">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex overflow-hidden items-center gap-2">
|
<div className="flex overflow-hidden items-center gap-2">
|
||||||
@ -295,7 +295,7 @@ export const PagesListItem: FC<IPagesListItem> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
|
@ -50,7 +50,7 @@ export const ProfileNavbar: React.FC<Props> = (props) => {
|
|||||||
<div className="flex items-center overflow-x-scroll">
|
<div className="flex items-center overflow-x-scroll">
|
||||||
{tabsList.map((tab) => (
|
{tabsList.map((tab) => (
|
||||||
<Link key={tab.route} href={`/${workspaceSlug}/profile/${userId}/${tab.route}`}>
|
<Link key={tab.route} href={`/${workspaceSlug}/profile/${userId}/${tab.route}`}>
|
||||||
<a
|
<span
|
||||||
className={`border-b-2 p-4 text-sm font-medium outline-none whitespace-nowrap ${
|
className={`border-b-2 p-4 text-sm font-medium outline-none whitespace-nowrap ${
|
||||||
router.pathname === tab.selected
|
router.pathname === tab.selected
|
||||||
? "border-custom-primary-100 text-custom-primary-100"
|
? "border-custom-primary-100 text-custom-primary-100"
|
||||||
@ -58,7 +58,7 @@ export const ProfileNavbar: React.FC<Props> = (props) => {
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,7 +43,7 @@ export const ProfileStats: React.FC<Props> = ({ userProfile }) => {
|
|||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{overviewCards.map((card) => (
|
{overviewCards.map((card) => (
|
||||||
<Link key={card.route} href={`/${workspaceSlug}/profile/${userId}/${card.route}`}>
|
<Link key={card.route} href={`/${workspaceSlug}/profile/${userId}/${card.route}`}>
|
||||||
<a className="flex items-center gap-3 p-4 rounded border border-custom-border-100 whitespace-nowrap">
|
<span className="flex items-center gap-3 p-4 rounded border border-custom-border-100 whitespace-nowrap">
|
||||||
<div className="h-11 w-11 bg-custom-background-90 rounded grid place-items-center">
|
<div className="h-11 w-11 bg-custom-background-90 rounded grid place-items-center">
|
||||||
<card.icon className="h-5 w-5" />
|
<card.icon className="h-5 w-5" />
|
||||||
</div>
|
</div>
|
||||||
@ -51,7 +51,7 @@ export const ProfileStats: React.FC<Props> = ({ userProfile }) => {
|
|||||||
<p className="text-custom-text-400 text-sm">{card.title}</p>
|
<p className="text-custom-text-400 text-sm">{card.title}</p>
|
||||||
<p className="text-xl font-semibold">{card.value}</p>
|
<p className="text-xl font-semibold">{card.value}</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -68,9 +68,9 @@ export const ProfileSidebar = () => {
|
|||||||
{user?.id === userId && (
|
{user?.id === userId && (
|
||||||
<div className="absolute top-3.5 right-3.5 h-5 w-5 bg-white rounded grid place-items-center">
|
<div className="absolute top-3.5 right-3.5 h-5 w-5 bg-white rounded grid place-items-center">
|
||||||
<Link href="/profile">
|
<Link href="/profile">
|
||||||
<a className="grid place-items-center text-black">
|
<span className="grid place-items-center text-black">
|
||||||
<Pencil className="h-3 w-3" />
|
<Pencil className="h-3 w-3" />
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -4,12 +4,10 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { ProjectCard } from "components/project";
|
import { ProjectCard } from "components/project";
|
||||||
import { EmptyState } from "components/project/empty-state";
|
|
||||||
import { Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// images
|
// images
|
||||||
import emptyProject from "public/empty-state/empty_project.webp";
|
import emptyProject from "public/empty-state/empty_project.webp";
|
||||||
// icons
|
// icons
|
||||||
import { Plus } from "lucide-react";
|
|
||||||
import { NewEmptyState } from "components/common/new-empty-state";
|
import { NewEmptyState } from "components/common/new-empty-state";
|
||||||
|
|
||||||
export interface IProjectCardList {
|
export interface IProjectCardList {
|
||||||
|
@ -28,7 +28,7 @@ export const EmptyState: React.FC<Props> = ({
|
|||||||
}) => (
|
}) => (
|
||||||
<div className="flex items-center lg:p-20 md:px-10 px-5 justify-center h-full w-full">
|
<div className="flex items-center lg:p-20 md:px-10 px-5 justify-center h-full w-full">
|
||||||
<div className="relative h-full w-full max-w-6xl">
|
<div className="relative h-full w-full max-w-6xl">
|
||||||
<Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text} layout="fill" />
|
<Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text ?? ""} layout="fill" />
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute pt-[30vh] md:pt-[35vh] lg:pt-[45vh] text-center flex flex-col items-center w-full">
|
<div className="absolute pt-[30vh] md:pt-[35vh] lg:pt-[45vh] text-center flex flex-col items-center w-full">
|
||||||
<h6 className="text-xl font-semibold mt-6">{title}</h6>
|
<h6 className="text-xl font-semibold mt-6">{title}</h6>
|
||||||
|
@ -76,27 +76,27 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
|||||||
<div className="flex items-center gap-x-4 gap-y-2">
|
<div className="flex items-center gap-x-4 gap-y-2">
|
||||||
{memberDetails.avatar && memberDetails.avatar !== "" ? (
|
{memberDetails.avatar && memberDetails.avatar !== "" ? (
|
||||||
<Link href={`/${workspaceSlug}/profile/${memberDetails.id}`}>
|
<Link href={`/${workspaceSlug}/profile/${memberDetails.id}`}>
|
||||||
<a className="relative flex h-10 w-10 items-center justify-center rounded p-4 capitalize text-white">
|
<span className="relative flex h-10 w-10 items-center justify-center rounded p-4 capitalize text-white">
|
||||||
<img
|
<img
|
||||||
src={memberDetails.avatar}
|
src={memberDetails.avatar}
|
||||||
alt={memberDetails.display_name || memberDetails.email}
|
alt={memberDetails.display_name || memberDetails.email}
|
||||||
className="absolute top-0 left-0 h-full w-full object-cover rounded"
|
className="absolute top-0 left-0 h-full w-full object-cover rounded"
|
||||||
/>
|
/>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<Link href={`/${workspaceSlug}/profile/${memberDetails.id}`}>
|
<Link href={`/${workspaceSlug}/profile/${memberDetails.id}`}>
|
||||||
<a className="relative flex h-10 w-10 items-center justify-center rounded p-4 capitalize bg-gray-700 text-white">
|
<span className="relative flex h-10 w-10 items-center justify-center rounded p-4 capitalize bg-gray-700 text-white">
|
||||||
{(memberDetails.display_name ?? memberDetails.email ?? "?")[0]}
|
{(memberDetails.display_name ?? memberDetails.email ?? "?")[0]}
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Link href={`/${workspaceSlug}/profile/${memberDetails.id}`}>
|
<Link href={`/${workspaceSlug}/profile/${memberDetails.id}`}>
|
||||||
<a className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
{memberDetails.first_name} {memberDetails.last_name}
|
{memberDetails.first_name} {memberDetails.last_name}
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<p className="text-xs text-custom-text-300">{memberDetails.display_name}</p>
|
<p className="text-xs text-custom-text-300">{memberDetails.display_name}</p>
|
||||||
|
@ -12,8 +12,6 @@ import { Avatar, Button, CustomSelect, CustomSearchSelect } from "@plane/ui";
|
|||||||
import { ProjectMemberService } from "services/project";
|
import { ProjectMemberService } from "services/project";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// helpers
|
|
||||||
import { trackEvent } from "helpers/event-tracker.helper";
|
|
||||||
// types
|
// types
|
||||||
import { IProjectMember, TUserProjectRole } from "types";
|
import { IProjectMember, TUserProjectRole } from "types";
|
||||||
// constants
|
// constants
|
||||||
@ -58,7 +56,7 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||||||
const {
|
const {
|
||||||
user: { currentProjectRole },
|
user: { currentProjectRole },
|
||||||
workspaceMember: { workspaceMembers },
|
workspaceMember: { workspaceMembers },
|
||||||
trackEvent: { postHogEventTracker }
|
trackEvent: { postHogEventTracker },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -94,22 +92,16 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||||||
type: "success",
|
type: "success",
|
||||||
message: "Member added successfully",
|
message: "Member added successfully",
|
||||||
});
|
});
|
||||||
postHogEventTracker(
|
postHogEventTracker("PROJECT_MEMBER_INVITE", {
|
||||||
'PROJECT_MEMBER_INVITE',
|
|
||||||
{
|
|
||||||
...res,
|
...res,
|
||||||
state: "SUCCESS"
|
state: "SUCCESS",
|
||||||
}
|
});
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
postHogEventTracker(
|
postHogEventTracker("PROJECT_MEMBER_INVITE", {
|
||||||
'PROJECT_MEMBER_INVITE',
|
|
||||||
{
|
|
||||||
state: "FAILED",
|
state: "FAILED",
|
||||||
}
|
});
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
reset(defaultValues);
|
reset(defaultValues);
|
||||||
|
@ -75,7 +75,11 @@ export const ProjectSidebarListItem: React.FC<Props> = observer((props) => {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { project, provided, snapshot, handleCopyText, shortContextMenu = false } = props;
|
const { project, provided, snapshot, handleCopyText, shortContextMenu = false } = props;
|
||||||
// store
|
// store
|
||||||
const { project: projectStore, theme: themeStore, trackEvent: { setTrackElement } } = useMobxStore();
|
const {
|
||||||
|
project: projectStore,
|
||||||
|
theme: themeStore,
|
||||||
|
trackEvent: { setTrackElement },
|
||||||
|
} = useMobxStore();
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
@ -308,7 +312,7 @@ export const ProjectSidebarListItem: React.FC<Props> = observer((props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Link key={item.name} href={item.href}>
|
<Link key={item.name} href={item.href}>
|
||||||
<a className="block w-full">
|
<span className="block w-full">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
tooltipContent={`${project?.name}: ${item.name}`}
|
tooltipContent={`${project?.name}: ${item.name}`}
|
||||||
position="right"
|
position="right"
|
||||||
@ -326,7 +330,7 @@ export const ProjectSidebarListItem: React.FC<Props> = observer((props) => {
|
|||||||
{!isCollapsed && item.name}
|
{!isCollapsed && item.name}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -18,7 +18,6 @@ import type { IState } from "types";
|
|||||||
import { STATES_LIST } from "constants/fetch-keys";
|
import { STATES_LIST } from "constants/fetch-keys";
|
||||||
// constants
|
// constants
|
||||||
import { GROUP_CHOICES } from "constants/project";
|
import { GROUP_CHOICES } from "constants/project";
|
||||||
import { stat } from "fs";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
data: IState | null;
|
data: IState | null;
|
||||||
@ -44,7 +43,10 @@ export const CreateUpdateStateInline: React.FC<Props> = observer((props) => {
|
|||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
// store
|
// store
|
||||||
const { projectState: projectStateStore, trackEvent: { postHogEventTracker, setTrackElement } } = useMobxStore();
|
const {
|
||||||
|
projectState: projectStateStore,
|
||||||
|
trackEvent: { postHogEventTracker, setTrackElement },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
@ -95,13 +97,10 @@ export const CreateUpdateStateInline: React.FC<Props> = observer((props) => {
|
|||||||
title: "Success!",
|
title: "Success!",
|
||||||
message: "State created successfully.",
|
message: "State created successfully.",
|
||||||
});
|
});
|
||||||
postHogEventTracker(
|
postHogEventTracker("STATE_CREATE", {
|
||||||
'STATE_CREATE',
|
|
||||||
{
|
|
||||||
...res,
|
...res,
|
||||||
state: "SUCCESS"
|
state: "SUCCESS",
|
||||||
}
|
});
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
if (error.status === 400)
|
if (error.status === 400)
|
||||||
@ -116,12 +115,9 @@ export const CreateUpdateStateInline: React.FC<Props> = observer((props) => {
|
|||||||
title: "Error!",
|
title: "Error!",
|
||||||
message: "State could not be created. Please try again.",
|
message: "State could not be created. Please try again.",
|
||||||
});
|
});
|
||||||
postHogEventTracker(
|
postHogEventTracker("STATE_CREATE", {
|
||||||
'STATE_CREATE',
|
state: "FAILED",
|
||||||
{
|
});
|
||||||
state: "FAILED"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -133,13 +129,10 @@ export const CreateUpdateStateInline: React.FC<Props> = observer((props) => {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
mutate(STATES_LIST(projectId.toString()));
|
mutate(STATES_LIST(projectId.toString()));
|
||||||
handleClose();
|
handleClose();
|
||||||
postHogEventTracker(
|
postHogEventTracker("STATE_UPDATE", {
|
||||||
'STATE_UPDATE',
|
|
||||||
{
|
|
||||||
...res,
|
...res,
|
||||||
state: "SUCCESS",
|
state: "SUCCESS",
|
||||||
}
|
});
|
||||||
);
|
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Success!",
|
title: "Success!",
|
||||||
@ -159,12 +152,9 @@ export const CreateUpdateStateInline: React.FC<Props> = observer((props) => {
|
|||||||
title: "Error!",
|
title: "Error!",
|
||||||
message: "State could not be updated. Please try again.",
|
message: "State could not be updated. Please try again.",
|
||||||
});
|
});
|
||||||
postHogEventTracker(
|
postHogEventTracker("STATE_UPDATE", {
|
||||||
'STATE_UPDATE',
|
|
||||||
{
|
|
||||||
state: "FAILED",
|
state: "FAILED",
|
||||||
}
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -187,7 +177,8 @@ export const CreateUpdateStateInline: React.FC<Props> = observer((props) => {
|
|||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<Popover.Button
|
<Popover.Button
|
||||||
className={`group inline-flex items-center text-base font-medium focus:outline-none ${open ? "text-custom-text-100" : "text-custom-text-200"
|
className={`group inline-flex items-center text-base font-medium focus:outline-none ${
|
||||||
|
open ? "text-custom-text-100" : "text-custom-text-200"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{watch("color") && watch("color") !== "" && (
|
{watch("color") && watch("color") !== "" && (
|
||||||
@ -292,10 +283,14 @@ export const CreateUpdateStateInline: React.FC<Props> = observer((props) => {
|
|||||||
<Button variant="neutral-primary" onClick={handleClose}>
|
<Button variant="neutral-primary" onClick={handleClose}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="primary" type="submit" loading={isSubmitting} onClick={() => {
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
type="submit"
|
||||||
|
loading={isSubmitting}
|
||||||
|
onClick={() => {
|
||||||
setTrackElement("PROJECT_SETTINGS_STATE_PAGE");
|
setTrackElement("PROJECT_SETTINGS_STATE_PAGE");
|
||||||
}
|
}}
|
||||||
}>
|
>
|
||||||
{isSubmitting ? (data ? "Updating..." : "Creating...") : data ? "Update" : "Create"}
|
{isSubmitting ? (data ? "Updating..." : "Creating...") : data ? "Update" : "Create"}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
@ -28,7 +28,10 @@ export const DeleteStateModal: React.FC<Props> = observer((props) => {
|
|||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
// store
|
// store
|
||||||
const { projectState: projectStateStore, trackEvent: { postHogEventTracker } } = useMobxStore();
|
const {
|
||||||
|
projectState: projectStateStore,
|
||||||
|
trackEvent: { postHogEventTracker },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
// states
|
// states
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
@ -47,13 +50,10 @@ export const DeleteStateModal: React.FC<Props> = observer((props) => {
|
|||||||
|
|
||||||
await projectStateStore
|
await projectStateStore
|
||||||
.deleteState(workspaceSlug.toString(), data.project, data.id)
|
.deleteState(workspaceSlug.toString(), data.project, data.id)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
postHogEventTracker(
|
postHogEventTracker("STATE_DELETE", {
|
||||||
'STATE_DELETE',
|
state: "SUCCESS",
|
||||||
{
|
});
|
||||||
state: "SUCCESS"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
handleClose();
|
handleClose();
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@ -70,12 +70,9 @@ export const DeleteStateModal: React.FC<Props> = observer((props) => {
|
|||||||
title: "Error!",
|
title: "Error!",
|
||||||
message: "State could not be deleted. Please try again.",
|
message: "State could not be deleted. Please try again.",
|
||||||
});
|
});
|
||||||
postHogEventTracker(
|
postHogEventTracker("STATE_DELETE", {
|
||||||
'STATE_DELETE',
|
state: "FAILED",
|
||||||
{
|
});
|
||||||
state: "FAILED"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setIsDeleteLoading(false);
|
setIsDeleteLoading(false);
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
export const CircularProgress = ({ progress }: { progress: number }) => {
|
|
||||||
const [circumference, setCircumference] = useState(0);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const radius = 40;
|
|
||||||
const calcCircumference = 2 * Math.PI * radius;
|
|
||||||
setCircumference(calcCircumference);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const progressAngle = (progress / 100) * 360 >= 360 ? 359.9 : (progress / 100) * 360;
|
|
||||||
const progressX = 50 + Math.cos((progressAngle - 90) * (Math.PI / 180)) * 40;
|
|
||||||
const progressY = 50 + Math.sin((progressAngle - 90) * (Math.PI / 180)) * 40;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative h-5 w-5">
|
|
||||||
<svg className="absolute top-0 left-0" viewBox="0 0 100 100">
|
|
||||||
<circle
|
|
||||||
className="stroke-current"
|
|
||||||
cx="50"
|
|
||||||
cy="50"
|
|
||||||
r="40"
|
|
||||||
strokeWidth="12"
|
|
||||||
fill="none"
|
|
||||||
strokeDasharray={`${circumference} ${circumference}`}
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
className="fill-current"
|
|
||||||
d={`M50 10
|
|
||||||
A40 40 0 ${progress > 50 ? 1 : 0} 1 ${progressX} ${progressY}
|
|
||||||
L50 50 Z`}
|
|
||||||
strokeWidth="12"
|
|
||||||
strokeLinecap="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,123 +0,0 @@
|
|||||||
import React, { useEffect, useRef } from "react";
|
|
||||||
|
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
// hooks
|
|
||||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
clickEvent: React.MouseEvent | null;
|
|
||||||
children: React.ReactNode;
|
|
||||||
title?: string | JSX.Element;
|
|
||||||
isOpen: boolean;
|
|
||||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ContextMenu = ({ clickEvent, children, title, isOpen, setIsOpen }: Props) => {
|
|
||||||
const contextMenuRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
// Close the context menu when clicked outside
|
|
||||||
useOutsideClickDetector(contextMenuRef, () => {
|
|
||||||
if (isOpen) setIsOpen(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const hideContextMenu = () => {
|
|
||||||
if (isOpen) setIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const escapeKeyEvent = (e: KeyboardEvent) => {
|
|
||||||
if (e.key === "Escape") hideContextMenu();
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("click", hideContextMenu);
|
|
||||||
window.addEventListener("keydown", escapeKeyEvent);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("click", hideContextMenu);
|
|
||||||
window.removeEventListener("keydown", escapeKeyEvent);
|
|
||||||
};
|
|
||||||
}, [isOpen, setIsOpen]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const contextMenu = contextMenuRef.current;
|
|
||||||
|
|
||||||
if (contextMenu && isOpen) {
|
|
||||||
const contextMenuWidth = contextMenu.clientWidth;
|
|
||||||
const contextMenuHeight = contextMenu.clientHeight;
|
|
||||||
|
|
||||||
const clickX = clickEvent?.pageX || 0;
|
|
||||||
const clickY = clickEvent?.pageY || 0;
|
|
||||||
|
|
||||||
let top = clickY;
|
|
||||||
// check if there's enough space at the bottom, otherwise show at the top
|
|
||||||
if (clickY + contextMenuHeight > window.innerHeight) top = clickY - contextMenuHeight;
|
|
||||||
|
|
||||||
// check if there's enough space on the right, otherwise show on the left
|
|
||||||
let left = clickX;
|
|
||||||
if (clickX + contextMenuWidth > window.innerWidth) left = clickX - contextMenuWidth;
|
|
||||||
|
|
||||||
contextMenu.style.top = `${top}px`;
|
|
||||||
contextMenu.style.left = `${left}px`;
|
|
||||||
}
|
|
||||||
}, [clickEvent, isOpen]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`fixed z-50 top-0 left-0 h-full w-full ${
|
|
||||||
isOpen ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={contextMenuRef}
|
|
||||||
className={`fixed z-50 flex min-w-[8rem] flex-col items-stretch gap-1 rounded-md border border-custom-border-200 bg-custom-background-90 p-2 text-xs shadow-lg`}
|
|
||||||
>
|
|
||||||
{title && (
|
|
||||||
<h4 className="border-b border-custom-border-200 px-1 py-1 pb-2 text-[0.8rem] font-medium">{title}</h4>
|
|
||||||
)}
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type MenuItemProps = {
|
|
||||||
children: JSX.Element | string;
|
|
||||||
renderAs?: "button" | "a";
|
|
||||||
href?: string;
|
|
||||||
onClick?: () => void;
|
|
||||||
className?: string;
|
|
||||||
Icon?: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
const MenuItem: React.FC<MenuItemProps> = ({ children, renderAs, href = "", onClick, className = "", Icon }) => (
|
|
||||||
<>
|
|
||||||
{renderAs === "a" ? (
|
|
||||||
<Link href={href}>
|
|
||||||
<a
|
|
||||||
className={`${className} flex w-full items-center gap-2 rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80`}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
{Icon && <Icon />}
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`${className} flex w-full items-center gap-2 rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80`}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
{Icon && <Icon height={12} width={12} />}
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
ContextMenu.Item = MenuItem;
|
|
||||||
|
|
||||||
export { ContextMenu };
|
|
@ -1,162 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
|
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
// react-poppper
|
|
||||||
import { usePopper } from "react-popper";
|
|
||||||
// headless ui
|
|
||||||
import { Menu } from "@headlessui/react";
|
|
||||||
// ui
|
|
||||||
import { DropdownProps } from "components/ui";
|
|
||||||
// icons
|
|
||||||
import { ChevronDown, MoreHorizontal } from "lucide-react";
|
|
||||||
|
|
||||||
export type CustomMenuProps = DropdownProps & {
|
|
||||||
children: React.ReactNode;
|
|
||||||
ellipsis?: boolean;
|
|
||||||
noBorder?: boolean;
|
|
||||||
verticalEllipsis?: boolean;
|
|
||||||
menuButtonOnClick?: (...args: any) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CustomMenu = ({
|
|
||||||
buttonClassName = "",
|
|
||||||
customButtonClassName = "",
|
|
||||||
placement,
|
|
||||||
children,
|
|
||||||
className = "",
|
|
||||||
customButton,
|
|
||||||
disabled = false,
|
|
||||||
ellipsis = false,
|
|
||||||
label,
|
|
||||||
maxHeight = "md",
|
|
||||||
noBorder = false,
|
|
||||||
noChevron = false,
|
|
||||||
optionsClassName = "",
|
|
||||||
verticalEllipsis = false,
|
|
||||||
width = "auto",
|
|
||||||
menuButtonOnClick,
|
|
||||||
}: CustomMenuProps) => {
|
|
||||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
|
||||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
|
||||||
|
|
||||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
|
||||||
placement: placement ?? "bottom-start",
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<Menu as="div" className={`relative w-min text-left ${className}`}>
|
|
||||||
{({ open }) => (
|
|
||||||
<>
|
|
||||||
{customButton ? (
|
|
||||||
<Menu.Button as={React.Fragment}>
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
onClick={menuButtonOnClick}
|
|
||||||
className={customButtonClassName}
|
|
||||||
>
|
|
||||||
{customButton}
|
|
||||||
</button>
|
|
||||||
</Menu.Button>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{ellipsis || verticalEllipsis ? (
|
|
||||||
<Menu.Button as={React.Fragment}>
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
onClick={menuButtonOnClick}
|
|
||||||
disabled={disabled}
|
|
||||||
className={`relative grid place-items-center rounded p-1 text-custom-text-200 hover:text-custom-text-100 outline-none ${
|
|
||||||
disabled ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-80"
|
|
||||||
} ${buttonClassName}`}
|
|
||||||
>
|
|
||||||
<MoreHorizontal className={`h-3.5 w-3.5 ${verticalEllipsis ? "rotate-90" : ""}`} />
|
|
||||||
</button>
|
|
||||||
</Menu.Button>
|
|
||||||
) : (
|
|
||||||
<Menu.Button as={React.Fragment}>
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={`flex items-center justify-between gap-1 rounded-md px-2.5 py-1 text-xs whitespace-nowrap duration-300 ${
|
|
||||||
open ? "bg-custom-background-90 text-custom-text-100" : "text-custom-text-200"
|
|
||||||
} ${noBorder ? "" : "border border-custom-border-300 shadow-sm focus:outline-none"} ${
|
|
||||||
disabled
|
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
|
||||||
} ${buttonClassName}`}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
{!noChevron && <ChevronDown className="h-3.5 w-3.5" />}
|
|
||||||
</button>
|
|
||||||
</Menu.Button>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Menu.Items>
|
|
||||||
<div
|
|
||||||
className={`z-10 overflow-y-scroll whitespace-nowrap rounded-md border border-custom-border-300 p-1 text-xs shadow-custom-shadow-rg focus:outline-none bg-custom-background-90 my-1 ${
|
|
||||||
maxHeight === "lg"
|
|
||||||
? "max-h-60"
|
|
||||||
: maxHeight === "md"
|
|
||||||
? "max-h-48"
|
|
||||||
: maxHeight === "rg"
|
|
||||||
? "max-h-36"
|
|
||||||
: maxHeight === "sm"
|
|
||||||
? "max-h-28"
|
|
||||||
: ""
|
|
||||||
} ${width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width} ${optionsClassName}`}
|
|
||||||
ref={setPopperElement}
|
|
||||||
style={styles.popper}
|
|
||||||
{...attributes.popper}
|
|
||||||
>
|
|
||||||
<div className="py-1">{children}</div>
|
|
||||||
</div>
|
|
||||||
</Menu.Items>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type MenuItemProps = {
|
|
||||||
children: React.ReactNode;
|
|
||||||
renderAs?: "button" | "a";
|
|
||||||
href?: string;
|
|
||||||
onClick?: (args?: any) => void;
|
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const MenuItem: React.FC<MenuItemProps> = ({ children, renderAs, href, onClick, className = "" }) => (
|
|
||||||
<Menu.Item as="div">
|
|
||||||
{({ active, close }) =>
|
|
||||||
renderAs === "a" ? (
|
|
||||||
<Link href={href ?? ""}>
|
|
||||||
<a
|
|
||||||
className={`inline-block w-full select-none truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80 ${
|
|
||||||
active ? "bg-custom-background-80" : ""
|
|
||||||
} ${className}`}
|
|
||||||
onClick={close}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`w-full select-none truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80 ${
|
|
||||||
active ? "bg-custom-background-80" : ""
|
|
||||||
} ${className}`}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
|
|
||||||
CustomMenu.MenuItem = MenuItem;
|
|
||||||
|
|
||||||
export { CustomMenu };
|
|
@ -1,188 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
|
|
||||||
// react-popper
|
|
||||||
import { usePopper } from "react-popper";
|
|
||||||
// headless ui
|
|
||||||
import { Combobox } from "@headlessui/react";
|
|
||||||
// icons
|
|
||||||
import { Check, ChevronDown, Search } from "lucide-react";
|
|
||||||
// types
|
|
||||||
import { DropdownProps } from "./types";
|
|
||||||
|
|
||||||
export type CustomSearchSelectProps = DropdownProps & {
|
|
||||||
footerOption?: JSX.Element;
|
|
||||||
onChange: any;
|
|
||||||
options:
|
|
||||||
| {
|
|
||||||
value: any;
|
|
||||||
query: string;
|
|
||||||
content: React.ReactNode;
|
|
||||||
}[]
|
|
||||||
| undefined;
|
|
||||||
} & (
|
|
||||||
| { multiple?: false; value: any } // if multiple is false, value can be anything
|
|
||||||
| {
|
|
||||||
multiple?: true;
|
|
||||||
value: any[] | null; // if multiple is true, value should be an array
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const CustomSearchSelect = ({
|
|
||||||
customButtonClassName = "",
|
|
||||||
buttonClassName = "",
|
|
||||||
className = "",
|
|
||||||
customButton,
|
|
||||||
placement,
|
|
||||||
disabled = false,
|
|
||||||
footerOption,
|
|
||||||
input = false,
|
|
||||||
label,
|
|
||||||
maxHeight = "md",
|
|
||||||
multiple = false,
|
|
||||||
noChevron = false,
|
|
||||||
onChange,
|
|
||||||
options,
|
|
||||||
onOpen,
|
|
||||||
optionsClassName = "",
|
|
||||||
value,
|
|
||||||
width = "auto",
|
|
||||||
}: CustomSearchSelectProps) => {
|
|
||||||
const [query, setQuery] = useState("");
|
|
||||||
|
|
||||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
|
||||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
|
||||||
|
|
||||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
|
||||||
placement: placement ?? "bottom-start",
|
|
||||||
});
|
|
||||||
|
|
||||||
const filteredOptions =
|
|
||||||
query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase()));
|
|
||||||
|
|
||||||
const props: any = {
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
disabled,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (multiple) props.multiple = true;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Combobox as="div" className={`relative flex-shrink-0 text-left ${className}`} {...props}>
|
|
||||||
{({ open }: { open: boolean }) => {
|
|
||||||
if (open && onOpen) onOpen();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{customButton ? (
|
|
||||||
<Combobox.Button as={React.Fragment}>
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
|
||||||
disabled
|
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
|
||||||
} ${customButtonClassName}`}
|
|
||||||
>
|
|
||||||
{customButton}
|
|
||||||
</button>
|
|
||||||
</Combobox.Button>
|
|
||||||
) : (
|
|
||||||
<Combobox.Button as={React.Fragment}>
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={`flex items-center justify-between gap-1 w-full rounded-md border border-custom-border-300 shadow-sm duration-300 focus:outline-none ${
|
|
||||||
input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs"
|
|
||||||
} ${
|
|
||||||
disabled
|
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
|
||||||
} ${buttonClassName}`}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
{!noChevron && !disabled && <ChevronDown className="h-3 w-3" aria-hidden="true" />}
|
|
||||||
</button>
|
|
||||||
</Combobox.Button>
|
|
||||||
)}
|
|
||||||
<Combobox.Options as={React.Fragment}>
|
|
||||||
<div
|
|
||||||
className={`z-10 min-w-[10rem] border border-custom-border-300 p-2 rounded-md bg-custom-background-90 text-xs shadow-custom-shadow-rg focus:outline-none my-1 ${
|
|
||||||
width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width
|
|
||||||
} ${optionsClassName}`}
|
|
||||||
ref={setPopperElement}
|
|
||||||
style={styles.popper}
|
|
||||||
{...attributes.popper}
|
|
||||||
>
|
|
||||||
<div className="flex w-full items-center justify-start rounded-sm border-[0.6px] border-custom-border-200 bg-custom-background-90 px-2">
|
|
||||||
<Search className="h-3 w-3 text-custom-text-200" />
|
|
||||||
<Combobox.Input
|
|
||||||
className="w-full bg-transparent py-1 px-2 text-xs text-custom-text-200 placeholder:text-custom-text-400 focus:outline-none"
|
|
||||||
value={query}
|
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
|
||||||
placeholder="Type to search..."
|
|
||||||
displayValue={(assigned: any) => assigned?.name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={`mt-2 space-y-1 ${
|
|
||||||
maxHeight === "lg"
|
|
||||||
? "max-h-60"
|
|
||||||
: maxHeight === "md"
|
|
||||||
? "max-h-48"
|
|
||||||
: maxHeight === "rg"
|
|
||||||
? "max-h-36"
|
|
||||||
: maxHeight === "sm"
|
|
||||||
? "max-h-28"
|
|
||||||
: ""
|
|
||||||
} overflow-y-scroll`}
|
|
||||||
>
|
|
||||||
{filteredOptions ? (
|
|
||||||
filteredOptions.length > 0 ? (
|
|
||||||
filteredOptions.map((option) => (
|
|
||||||
<Combobox.Option
|
|
||||||
key={option.value}
|
|
||||||
value={option.value}
|
|
||||||
className={({ active, selected }) =>
|
|
||||||
`flex items-center justify-between gap-2 cursor-pointer select-none truncate rounded px-1 py-1.5 ${
|
|
||||||
active || selected ? "bg-custom-background-80" : ""
|
|
||||||
} ${selected ? "text-custom-text-100" : "text-custom-text-200"}`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{({ active, selected }) => (
|
|
||||||
<>
|
|
||||||
{option.content}
|
|
||||||
{multiple ? (
|
|
||||||
<div
|
|
||||||
className={`flex items-center justify-center rounded border border-custom-border-400 p-0.5 ${
|
|
||||||
active || selected ? "opacity-100" : "opacity-0"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<Check className={`h-3 w-3 ${selected ? "opacity-100" : "opacity-0"}`} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Check className={`h-3 w-3 ${selected ? "opacity-100" : "opacity-0"}`} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Combobox.Option>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<span className="flex items-center gap-2 p-1">
|
|
||||||
<p className="text-left text-custom-text-200 ">No matching results</p>
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<p className="text-center text-custom-text-200">Loading...</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{footerOption}
|
|
||||||
</div>
|
|
||||||
</Combobox.Options>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Combobox>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,130 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
|
|
||||||
// react-popper
|
|
||||||
import { usePopper } from "react-popper";
|
|
||||||
// headless ui
|
|
||||||
import { Listbox } from "@headlessui/react";
|
|
||||||
// icons
|
|
||||||
import { Check, ChevronDown } from "lucide-react";
|
|
||||||
// types
|
|
||||||
import { DropdownProps } from "./types";
|
|
||||||
|
|
||||||
export type CustomSelectProps = DropdownProps & {
|
|
||||||
children: React.ReactNode;
|
|
||||||
value: any;
|
|
||||||
onChange: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CustomSelect = ({
|
|
||||||
customButtonClassName = "",
|
|
||||||
buttonClassName = "",
|
|
||||||
placement,
|
|
||||||
children,
|
|
||||||
className = "",
|
|
||||||
customButton,
|
|
||||||
disabled = false,
|
|
||||||
input = false,
|
|
||||||
label,
|
|
||||||
maxHeight = "md",
|
|
||||||
noChevron = false,
|
|
||||||
onChange,
|
|
||||||
optionsClassName = "",
|
|
||||||
value,
|
|
||||||
width = "auto",
|
|
||||||
}: CustomSelectProps) => {
|
|
||||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
|
||||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
|
||||||
|
|
||||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
|
||||||
placement: placement ?? "bottom-start",
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Listbox
|
|
||||||
as="div"
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
className={`relative flex-shrink-0 text-left ${className}`}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
{customButton ? (
|
|
||||||
<Listbox.Button as={React.Fragment}>
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
|
||||||
disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80"
|
|
||||||
} ${customButtonClassName}`}
|
|
||||||
>
|
|
||||||
{customButton}
|
|
||||||
</button>
|
|
||||||
</Listbox.Button>
|
|
||||||
) : (
|
|
||||||
<Listbox.Button as={React.Fragment}>
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={`flex items-center justify-between gap-1 w-full rounded-md border border-custom-border-300 shadow-sm duration-300 focus:outline-none ${
|
|
||||||
input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs"
|
|
||||||
} ${
|
|
||||||
disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80"
|
|
||||||
} ${buttonClassName}`}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
{!noChevron && !disabled && <ChevronDown className="h-3 w-3" aria-hidden="true" />}
|
|
||||||
</button>
|
|
||||||
</Listbox.Button>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
<Listbox.Options>
|
|
||||||
<div
|
|
||||||
className={`z-10 border border-custom-border-300 overflow-y-auto rounded-md bg-custom-background-90 text-xs shadow-custom-shadow-rg focus:outline-none my-1 ${
|
|
||||||
maxHeight === "lg"
|
|
||||||
? "max-h-60"
|
|
||||||
: maxHeight === "md"
|
|
||||||
? "max-h-48"
|
|
||||||
: maxHeight === "rg"
|
|
||||||
? "max-h-36"
|
|
||||||
: maxHeight === "sm"
|
|
||||||
? "max-h-28"
|
|
||||||
: ""
|
|
||||||
} ${width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width} ${optionsClassName}`}
|
|
||||||
ref={setPopperElement}
|
|
||||||
style={styles.popper}
|
|
||||||
{...attributes.popper}
|
|
||||||
>
|
|
||||||
<div className="space-y-1 p-2">{children}</div>
|
|
||||||
</div>
|
|
||||||
</Listbox.Options>
|
|
||||||
</Listbox>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type OptionProps = {
|
|
||||||
children: React.ReactNode;
|
|
||||||
value: any;
|
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Option: React.FC<OptionProps> = ({ children, value, className }) => (
|
|
||||||
<Listbox.Option
|
|
||||||
value={value}
|
|
||||||
className={({ active, selected }) =>
|
|
||||||
`cursor-pointer select-none truncate rounded px-1 py-1.5 ${active || selected ? "bg-custom-background-80" : ""} ${
|
|
||||||
selected ? "text-custom-text-100" : "text-custom-text-200"
|
|
||||||
} ${className}`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{({ selected }) => (
|
|
||||||
<div className="flex items-center justify-between gap-2">
|
|
||||||
<div className="flex items-center gap-2">{children}</div>
|
|
||||||
{selected && <Check className="h-4 w-4 flex-shrink-0" />}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Listbox.Option>
|
|
||||||
);
|
|
||||||
|
|
||||||
CustomSelect.Option = Option;
|
|
||||||
|
|
||||||
export { CustomSelect };
|
|
@ -1,5 +0,0 @@
|
|||||||
export * from "./context-menu";
|
|
||||||
export * from "./custom-menu";
|
|
||||||
export * from "./custom-search-select";
|
|
||||||
export * from "./custom-select";
|
|
||||||
export * from "./types.d";
|
|
18
web/components/ui/dropdowns/types.d.ts
vendored
18
web/components/ui/dropdowns/types.d.ts
vendored
@ -1,18 +0,0 @@
|
|||||||
import { Placement } from "@popperjs/core";
|
|
||||||
|
|
||||||
export type DropdownProps = {
|
|
||||||
customButtonClassName?: string;
|
|
||||||
buttonClassName?: string;
|
|
||||||
customButtonClassName?: string;
|
|
||||||
className?: string;
|
|
||||||
customButton?: JSX.Element;
|
|
||||||
disabled?: boolean;
|
|
||||||
input?: boolean;
|
|
||||||
label?: string | JSX.Element;
|
|
||||||
maxHeight?: "sm" | "rg" | "md" | "lg";
|
|
||||||
noChevron?: boolean;
|
|
||||||
onOpen?: () => void;
|
|
||||||
optionsClassName?: string;
|
|
||||||
width?: "auto" | string;
|
|
||||||
placement?: Placement;
|
|
||||||
};
|
|
@ -30,10 +30,10 @@ const EmptySpace: React.FC<EmptySpaceProps> = ({ title, description, children, I
|
|||||||
{link ? (
|
{link ? (
|
||||||
<div className="mt-6 flex">
|
<div className="mt-6 flex">
|
||||||
<Link href={link.href}>
|
<Link href={link.href}>
|
||||||
<a className="text-sm font-medium text-custom-primary hover:text-custom-primary">
|
<span className="text-sm font-medium text-custom-primary hover:text-custom-primary">
|
||||||
{link.text}
|
{link.text}
|
||||||
<span aria-hidden="true"> →</span>
|
<span aria-hidden="true"> →</span>
|
||||||
</a>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
iconName: string;
|
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Icon: React.FC<Props> = ({ iconName, className = "" }) => (
|
|
||||||
<span className={`material-symbols-rounded text-sm leading-5 font-light ${className}`}>{iconName}</span>
|
|
||||||
);
|
|
@ -1,23 +1,12 @@
|
|||||||
export * from "./dropdowns";
|
|
||||||
export * from "./graphs";
|
export * from "./graphs";
|
||||||
export * from "./input";
|
|
||||||
export * from "./text-area";
|
|
||||||
export * from "./date";
|
export * from "./date";
|
||||||
export * from "./datepicker";
|
export * from "./datepicker";
|
||||||
export * from "./empty-space";
|
export * from "./empty-space";
|
||||||
export * from "./icon";
|
|
||||||
export * from "./labels-list";
|
export * from "./labels-list";
|
||||||
export * from "./linear-progress-indicator";
|
|
||||||
export * from "./loader";
|
|
||||||
export * from "./multi-level-dropdown";
|
export * from "./multi-level-dropdown";
|
||||||
export * from "./multi-level-select";
|
export * from "./multi-level-select";
|
||||||
export * from "./progress-bar";
|
|
||||||
export * from "./spinner";
|
|
||||||
export * from "./tooltip";
|
|
||||||
export * from "./toggle-switch";
|
|
||||||
export * from "./markdown-to-component";
|
export * from "./markdown-to-component";
|
||||||
export * from "./product-updates-modal";
|
export * from "./product-updates-modal";
|
||||||
export * from "./integration-and-import-export-banner";
|
export * from "./integration-and-import-export-banner";
|
||||||
export * from "./range-datepicker";
|
export * from "./range-datepicker";
|
||||||
export * from "./circular-progress";
|
|
||||||
export * from "./profile-empty-state";
|
export * from "./profile-empty-state";
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
|
|
||||||
// types
|
|
||||||
import { Props } from "./types";
|
|
||||||
|
|
||||||
export const Input: React.FC<Props> = ({
|
|
||||||
label,
|
|
||||||
value,
|
|
||||||
name,
|
|
||||||
register,
|
|
||||||
validations,
|
|
||||||
error,
|
|
||||||
mode = "primary",
|
|
||||||
onChange,
|
|
||||||
className = "",
|
|
||||||
type,
|
|
||||||
id,
|
|
||||||
size = "rg",
|
|
||||||
fullWidth = true,
|
|
||||||
...rest
|
|
||||||
}) => (
|
|
||||||
<>
|
|
||||||
{label && (
|
|
||||||
<label htmlFor={id} className="text-custom-text-200 mb-2">
|
|
||||||
{label}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
<input
|
|
||||||
type={type}
|
|
||||||
id={id}
|
|
||||||
value={value}
|
|
||||||
{...(register && register(name ?? "", validations))}
|
|
||||||
onChange={(e) => {
|
|
||||||
register && register(name ?? "").onChange(e);
|
|
||||||
onChange && onChange(e);
|
|
||||||
}}
|
|
||||||
className={`block rounded-md bg-transparent text-sm focus:outline-none placeholder-custom-text-400 ${
|
|
||||||
mode === "primary"
|
|
||||||
? "rounded-md border border-custom-border-200"
|
|
||||||
: mode === "transparent"
|
|
||||||
? "rounded border-none bg-transparent ring-0 transition-all focus:ring-1 focus:ring-custom-primary"
|
|
||||||
: mode === "trueTransparent"
|
|
||||||
? "rounded border-none bg-transparent ring-0"
|
|
||||||
: ""
|
|
||||||
} ${error ? "border-red-500" : ""} ${error && mode === "primary" ? "bg-red-500/20" : ""} ${
|
|
||||||
fullWidth ? "w-full" : ""
|
|
||||||
} ${size === "rg" ? "px-3 py-2" : size === "lg" ? "p-3" : ""} ${className}`}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
{error?.message && <div className="text-sm text-red-500">{error.message}</div>}
|
|
||||||
</>
|
|
||||||
);
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user