forked from github/plane
style: project settings theming (#936)
* style: project and workspace members theming * style: project features theming * style: project settings states theming * style: project settings labels theming * style: project settings integrations theming
This commit is contained in:
parent
c80094581e
commit
169a60723b
@ -108,28 +108,26 @@ export const CreateUpdateLabelInline = forwardRef<Ref, Props>(function CreateUpd
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex items-center gap-2 scroll-m-8 rounded-[10px] border border-brand-base bg-brand-surface-2 p-5 ${
|
className={`flex scroll-m-8 items-center gap-2 rounded-[10px] border border-brand-base bg-brand-base p-5 ${
|
||||||
labelForm ? "" : "hidden"
|
labelForm ? "" : "hidden"
|
||||||
}`}
|
}`}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
<div className="h-8 w-8 flex-shrink-0">
|
<div className="h-8 w-8 flex-shrink-0">
|
||||||
<Popover className="relative z-10 flex h-full w-full items-center justify-center rounded-xl bg-brand-surface-2">
|
<Popover className="relative z-10 flex h-full w-full items-center justify-center">
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<Popover.Button
|
<Popover.Button
|
||||||
className={`group inline-flex items-center text-base font-medium focus:outline-none focus:ring-2 focus:ring-brand-accent focus:ring-offset-2 ${
|
className={`group inline-flex items-center text-base font-medium focus:outline-none ${
|
||||||
open ? "text-brand-base" : "text-brand-secondary"
|
open ? "text-brand-base" : "text-brand-secondary"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{watch("color") && watch("color") !== "" && (
|
|
||||||
<span
|
<span
|
||||||
className="h-4 w-4 rounded"
|
className="h-5 w-5 rounded"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: watch("color") ?? "#000",
|
backgroundColor: watch("color"),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</Popover.Button>
|
</Popover.Button>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
|
@ -66,7 +66,11 @@ export const SingleLabelGroup: React.FC<Props> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Disclosure as="div" className="rounded-[10px] border border-brand-base bg-brand-surface-1 p-5 text-brand-base" defaultOpen>
|
<Disclosure
|
||||||
|
as="div"
|
||||||
|
className="rounded-[10px] border border-brand-base bg-brand-base p-5 text-brand-base"
|
||||||
|
defaultOpen
|
||||||
|
>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<div className="flex cursor-pointer items-center justify-between gap-2">
|
<div className="flex cursor-pointer items-center justify-between gap-2">
|
||||||
@ -74,7 +78,7 @@ export const SingleLabelGroup: React.FC<Props> = ({
|
|||||||
<span>
|
<span>
|
||||||
<RectangleGroupIcon className="h-4 w-4" />
|
<RectangleGroupIcon className="h-4 w-4" />
|
||||||
</span>
|
</span>
|
||||||
<h6 className="font-medium text-brand-base">{label.name}</h6>
|
<h6>{label.name}</h6>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<CustomMenu ellipsis>
|
<CustomMenu ellipsis>
|
||||||
@ -122,7 +126,7 @@ export const SingleLabelGroup: React.FC<Props> = ({
|
|||||||
key={child.id}
|
key={child.id}
|
||||||
className="group flex items-center justify-between rounded-md border border-brand-base p-2 text-sm"
|
className="group flex items-center justify-between rounded-md border border-brand-base p-2 text-sm"
|
||||||
>
|
>
|
||||||
<h5 className="flex items-center gap-3 text-brand-base">
|
<h5 className="flex items-center gap-3">
|
||||||
<span
|
<span
|
||||||
className="h-2.5 w-2.5 flex-shrink-0 rounded-full"
|
className="h-2.5 w-2.5 flex-shrink-0 rounded-full"
|
||||||
style={{
|
style={{
|
||||||
|
@ -5,7 +5,7 @@ import { CustomMenu } from "components/ui";
|
|||||||
// types
|
// types
|
||||||
import { IIssueLabels } from "types";
|
import { IIssueLabels } from "types";
|
||||||
//icons
|
//icons
|
||||||
import { RectangleGroupIcon, LinkIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline";
|
import { RectangleGroupIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
label: IIssueLabels;
|
label: IIssueLabels;
|
||||||
@ -20,7 +20,7 @@ export const SingleLabel: React.FC<Props> = ({
|
|||||||
editLabel,
|
editLabel,
|
||||||
handleLabelDelete,
|
handleLabelDelete,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="gap-2 space-y-3 divide-y divide-brand-base rounded-[10px] border border-brand-base bg-brand-surface-1 p-5">
|
<div className="gap-2 space-y-3 divide-y divide-brand-base rounded-[10px] border border-brand-base bg-brand-base p-5">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<span
|
<span
|
||||||
@ -29,7 +29,7 @@ export const SingleLabel: React.FC<Props> = ({
|
|||||||
backgroundColor: label.color && label.color !== "" ? label.color : "#000",
|
backgroundColor: label.color && label.color !== "" ? label.color : "#000",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<h6 className="font-medium text-brand-base">{label.name}</h6>
|
<h6 className="text-sm">{label.name}</h6>
|
||||||
</div>
|
</div>
|
||||||
<CustomMenu ellipsis>
|
<CustomMenu ellipsis>
|
||||||
<CustomMenu.MenuItem onClick={() => addLabelToGroup(label)}>
|
<CustomMenu.MenuItem onClick={() => addLabelToGroup(label)}>
|
||||||
|
@ -18,7 +18,6 @@ import SlackLogo from "public/services/slack.png";
|
|||||||
import { IWorkspaceIntegration } from "types";
|
import { IWorkspaceIntegration } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { PROJECT_GITHUB_REPOSITORY } from "constants/fetch-keys";
|
import { PROJECT_GITHUB_REPOSITORY } from "constants/fetch-keys";
|
||||||
import { comboMatches } from "@blueprintjs/core";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
integration: IWorkspaceIntegration;
|
integration: IWorkspaceIntegration;
|
||||||
@ -27,14 +26,12 @@ type Props = {
|
|||||||
const integrationDetails: { [key: string]: any } = {
|
const integrationDetails: { [key: string]: any } = {
|
||||||
github: {
|
github: {
|
||||||
logo: GithubLogo,
|
logo: GithubLogo,
|
||||||
installed:
|
description: "Select GitHub repository to enable sync.",
|
||||||
"Activate GitHub integrations on individual projects to sync with specific repositories.",
|
|
||||||
notInstalled: "Connect with GitHub with your Plane workspace to sync project issues.",
|
|
||||||
},
|
},
|
||||||
slack: {
|
slack: {
|
||||||
logo: SlackLogo,
|
logo: SlackLogo,
|
||||||
installed: "Activate Slack integrations on individual projects to sync with specific cahnnels.",
|
description:
|
||||||
notInstalled: "Connect with Slack with your Plane workspace to sync project issues.",
|
"Connect your slack channel to this project to get regular updates. Control which notification you want to receive.",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -67,19 +64,19 @@ export const SingleIntegration: React.FC<Props> = ({ integration }) => {
|
|||||||
} = repo;
|
} = repo;
|
||||||
|
|
||||||
projectService
|
projectService
|
||||||
.syncGiuthubRepository(workspaceSlug as string, projectId as string, integration.id, {
|
.syncGithubRepository(workspaceSlug as string, projectId as string, integration.id, {
|
||||||
name,
|
name,
|
||||||
owner: login,
|
owner: login,
|
||||||
repository_id: id,
|
repository_id: id,
|
||||||
url: html_url,
|
url: html_url,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
mutate(PROJECT_GITHUB_REPOSITORY(projectId as string));
|
mutate(PROJECT_GITHUB_REPOSITORY(projectId as string));
|
||||||
|
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Success!",
|
title: "Success!",
|
||||||
message: `${login}/${name} respository synced with the project successfully.`,
|
message: `${login}/${name} repository synced with the project successfully.`,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@ -88,7 +85,7 @@ export const SingleIntegration: React.FC<Props> = ({ integration }) => {
|
|||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "error",
|
type: "error",
|
||||||
title: "Error!",
|
title: "Error!",
|
||||||
message: "Respository could not be synced with the project. Please try again.",
|
message: "Repository could not be synced with the project. Please try again.",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -96,24 +93,20 @@ export const SingleIntegration: React.FC<Props> = ({ integration }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{integration && (
|
{integration && (
|
||||||
<div className="flex items-center justify-between gap-2 rounded-[10px] border border-brand-base bg-brand-surface-1 p-5">
|
<div className="flex items-center justify-between gap-2 rounded-[10px] border border-brand-base bg-brand-base p-5">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
<div className="h-12 w-12 flex-shrink-0">
|
<div className="h-12 w-12 flex-shrink-0">
|
||||||
<Image
|
<Image
|
||||||
src={integrationDetails[integration.integration_detail.provider].logo}
|
src={integrationDetails[integration.integration_detail.provider].logo}
|
||||||
alt="GithubLogo"
|
alt={`${integration.integration_detail.title} Logo`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="flex items-center gap-4 text-xl font-semibold">
|
<h3 className="flex items-center gap-4 text-xl font-semibold">
|
||||||
{integration.integration_detail.title}
|
{integration.integration_detail.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-400">
|
<p className="text-sm text-brand-secondary">
|
||||||
{integration.integration_detail.provider === "github"
|
{integrationDetails[integration.integration_detail.provider].description}
|
||||||
? "Select GitHub repository to enable sync."
|
|
||||||
: integration.integration_detail.provider === "slack"
|
|
||||||
? "Connect your slack channel to this project to get regular updates. Control which notification you want to receive"
|
|
||||||
: null}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -155,7 +155,7 @@ export const CreateUpdateStateInline: React.FC<Props> = ({ data, onClose, select
|
|||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
className="flex items-center gap-x-2 rounded-[10px] bg-brand-surface-1 p-5"
|
className="flex items-center gap-x-2 rounded-[10px] bg-brand-base p-5"
|
||||||
>
|
>
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<Popover className="relative flex h-full w-full items-center justify-center">
|
<Popover className="relative flex h-full w-full items-center justify-center">
|
||||||
@ -163,7 +163,7 @@ export const CreateUpdateStateInline: React.FC<Props> = ({ data, onClose, select
|
|||||||
<>
|
<>
|
||||||
<Popover.Button
|
<Popover.Button
|
||||||
className={`group inline-flex items-center text-base font-medium focus:outline-none ${
|
className={`group inline-flex items-center text-base font-medium focus:outline-none ${
|
||||||
open ? "text-gray-900" : "text-gray-500"
|
open ? "text-brand-base" : "text-brand-secondary"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{watch("color") && watch("color") !== "" && (
|
{watch("color") && watch("color") !== "" && (
|
||||||
|
@ -134,21 +134,19 @@ export const SingleState: React.FC<Props> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="group flex items-center justify-between gap-2 border-brand-base bg-brand-base p-5 first:rounded-t-[10px] last:rounded-b-[10px]">
|
||||||
className={`group flex items-center justify-between gap-2 border-brand-base bg-brand-surface-1 p-5 first:rounded-t-[10px] last:rounded-b-[10px]`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
{getStateGroupIcon(state.group, "20", "20", state.color)}
|
{getStateGroupIcon(state.group, "20", "20", state.color)}
|
||||||
<div>
|
<div>
|
||||||
<h6 className="text-brand-muted-1 font-medium">{addSpaceIfCamelCase(state.name)}</h6>
|
<h6 className="text-sm">{addSpaceIfCamelCase(state.name)}</h6>
|
||||||
<p className="text-xs text-gray-400">{state.description}</p>
|
<p className="text-xs text-brand-secondary">{state.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{index !== 0 && (
|
{index !== 0 && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="hidden text-gray-400 hover:text-brand-base group-hover:inline-block"
|
className="hidden text-brand-secondary group-hover:inline-block"
|
||||||
onClick={() => handleMove(state, "up")}
|
onClick={() => handleMove(state, "up")}
|
||||||
>
|
>
|
||||||
<ArrowUpIcon className="h-4 w-4" />
|
<ArrowUpIcon className="h-4 w-4" />
|
||||||
@ -157,18 +155,18 @@ export const SingleState: React.FC<Props> = ({
|
|||||||
{!(index === groupLength - 1) && (
|
{!(index === groupLength - 1) && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="hidden text-gray-400 hover:text-brand-base group-hover:inline-block"
|
className="hidden text-brand-secondary group-hover:inline-block"
|
||||||
onClick={() => handleMove(state, "down")}
|
onClick={() => handleMove(state, "down")}
|
||||||
>
|
>
|
||||||
<ArrowDownIcon className="h-4 w-4" />
|
<ArrowDownIcon className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{state.default ? (
|
{state.default ? (
|
||||||
<span className="text-xs text-gray-400">Default</span>
|
<span className="text-xs text-brand-secondary">Default</span>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="hidden text-xs text-gray-400 hover:text-brand-base group-hover:inline-block"
|
className="hidden text-xs text-brand-secondary group-hover:inline-block"
|
||||||
onClick={handleMakeDefault}
|
onClick={handleMakeDefault}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
>
|
>
|
||||||
@ -177,7 +175,7 @@ export const SingleState: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<button type="button" className="grid place-items-center" onClick={handleEditState}>
|
<button type="button" className="grid place-items-center" onClick={handleEditState}>
|
||||||
<PencilSquareIcon className="h-4 w-4 text-gray-400" />
|
<PencilSquareIcon className="h-4 w-4 text-brand-secondary" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -189,14 +187,14 @@ export const SingleState: React.FC<Props> = ({
|
|||||||
>
|
>
|
||||||
{state.default ? (
|
{state.default ? (
|
||||||
<Tooltip tooltipContent="Cannot delete the default state.">
|
<Tooltip tooltipContent="Cannot delete the default state.">
|
||||||
<TrashIcon className="h-4 w-4 text-red-400" />
|
<TrashIcon className="h-4 w-4 text-red-500" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : groupLength === 1 ? (
|
) : groupLength === 1 ? (
|
||||||
<Tooltip tooltipContent="Cannot have an empty group.">
|
<Tooltip tooltipContent="Cannot have an empty group.">
|
||||||
<TrashIcon className="h-4 w-4 text-red-400" />
|
<TrashIcon className="h-4 w-4 text-red-500" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<TrashIcon className="h-4 w-4 text-red-400" />
|
<TrashIcon className="h-4 w-4 text-red-500" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@ import useSWR, { mutate } from "swr";
|
|||||||
|
|
||||||
// services
|
// services
|
||||||
import projectService from "services/project.service";
|
import projectService from "services/project.service";
|
||||||
import trackEventServices from "services/track-event.service";
|
import trackEventServices, { MiscellaneousEventType } from "services/track-event.service";
|
||||||
// layouts
|
// layouts
|
||||||
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
|
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
|
||||||
// hooks
|
// hooks
|
||||||
@ -23,6 +23,52 @@ import type { NextPage } from "next";
|
|||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
|
import { PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
|
||||||
|
|
||||||
|
const featuresList = [
|
||||||
|
{
|
||||||
|
title: "Cycles",
|
||||||
|
description:
|
||||||
|
"Cycles are enabled for all the projects in this workspace. Access them from the sidebar.",
|
||||||
|
icon: <ContrastIcon color="#3f76ff" width={28} height={28} className="flex-shrink-0" />,
|
||||||
|
property: "cycle_view",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Modules",
|
||||||
|
description:
|
||||||
|
"Modules are enabled for all the projects in this workspace. Access it from the sidebar.",
|
||||||
|
icon: <PeopleGroupIcon color="#ff6b00" width={28} height={28} className="flex-shrink-0" />,
|
||||||
|
property: "module_view",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Views",
|
||||||
|
description:
|
||||||
|
"Views are enabled for all the projects in this workspace. Access it from the sidebar.",
|
||||||
|
icon: <ViewListIcon color="#05c3ff" width={28} height={28} className="flex-shrink-0" />,
|
||||||
|
property: "issue_views_view",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Pages",
|
||||||
|
description:
|
||||||
|
"Pages are enabled for all the projects in this workspace. Access it from the sidebar.",
|
||||||
|
icon: <DocumentTextIcon color="#fcbe1d" width={28} height={28} className="flex-shrink-0" />,
|
||||||
|
property: "page_view",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEventType = (feature: string, toggle: boolean): MiscellaneousEventType => {
|
||||||
|
switch (feature) {
|
||||||
|
case "Cycles":
|
||||||
|
return toggle ? "TOGGLE_CYCLE_ON" : "TOGGLE_CYCLE_OFF";
|
||||||
|
case "Modules":
|
||||||
|
return toggle ? "TOGGLE_MODULE_ON" : "TOGGLE_MODULE_OFF";
|
||||||
|
case "Views":
|
||||||
|
return toggle ? "TOGGLE_VIEW_ON" : "TOGGLE_VIEW_OFF";
|
||||||
|
case "Pages":
|
||||||
|
return toggle ? "TOGGLE_PAGES_ON" : "TOGGLE_PAGES_OFF";
|
||||||
|
default:
|
||||||
|
return toggle ? "TOGGLE_PAGES_ON" : "TOGGLE_PAGES_OFF";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const FeaturesSettings: NextPage = () => {
|
const FeaturesSettings: NextPage = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
@ -91,24 +137,27 @@ const FeaturesSettings: NextPage = () => {
|
|||||||
<section className="space-y-8">
|
<section className="space-y-8">
|
||||||
<h3 className="text-2xl font-semibold">Features</h3>
|
<h3 className="text-2xl font-semibold">Features</h3>
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<div className="flex items-center justify-between gap-x-8 gap-y-2 rounded-[10px] border border-brand-base bg-brand-surface-1 p-5">
|
{featuresList.map((feature) => (
|
||||||
|
<div
|
||||||
|
key={feature.property}
|
||||||
|
className="flex items-center justify-between gap-x-8 gap-y-2 rounded-[10px] border border-brand-base bg-brand-base p-5"
|
||||||
|
>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<ContrastIcon color="#3F76FF" width={28} height={28} className="flex-shrink-0" />
|
{feature.icon}
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-xl font-semibold">Cycles</h4>
|
<h4 className="text-lg font-semibold">{feature.title}</h4>
|
||||||
<p className="text-brand-secondary">
|
<p className="text-sm text-brand-secondary">{feature.description}</p>
|
||||||
Cycles are enabled for all the projects in this workspace. Access them from the
|
|
||||||
navigation bar.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${
|
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${
|
||||||
projectDetails?.cycle_view ? "bg-green-500" : "bg-brand-surface-2"
|
projectDetails?.[feature.property as keyof IProject]
|
||||||
|
? "bg-green-500"
|
||||||
|
: "bg-brand-surface-2"
|
||||||
}`}
|
}`}
|
||||||
role="switch"
|
role="switch"
|
||||||
aria-checked={projectDetails?.cycle_view}
|
aria-checked={projectDetails?.[feature.property as keyof IProject]}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
trackEventServices.trackMiscellaneousEvent(
|
trackEventServices.trackMiscellaneousEvent(
|
||||||
{
|
{
|
||||||
@ -118,143 +167,27 @@ const FeaturesSettings: NextPage = () => {
|
|||||||
projectIdentifier: projectDetails?.identifier,
|
projectIdentifier: projectDetails?.identifier,
|
||||||
projectName: projectDetails?.name,
|
projectName: projectDetails?.name,
|
||||||
},
|
},
|
||||||
!projectDetails?.cycle_view ? "TOGGLE_CYCLE_ON" : "TOGGLE_CYCLE_OFF"
|
!projectDetails?.[feature.property as keyof IProject]
|
||||||
|
? getEventType(feature.title, true)
|
||||||
|
: getEventType(feature.title, false)
|
||||||
);
|
);
|
||||||
handleSubmit({ cycle_view: !projectDetails?.cycle_view });
|
handleSubmit({
|
||||||
|
[feature.property]: !projectDetails?.[feature.property as keyof IProject],
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="sr-only">Use cycles</span>
|
<span className="sr-only">Use {feature.title}</span>
|
||||||
<span
|
<span
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className={`inline-block h-5 w-5 transform rounded-full bg-brand-surface-1 shadow ring-0 transition duration-200 ease-in-out ${
|
className={`inline-block h-5 w-5 transform rounded-full bg-brand-surface-1 shadow ring-0 transition duration-200 ease-in-out ${
|
||||||
projectDetails?.cycle_view ? "translate-x-5" : "translate-x-0"
|
projectDetails?.[feature.property as keyof IProject]
|
||||||
}`}
|
? "translate-x-5"
|
||||||
/>
|
: "translate-x-0"
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between gap-x-8 gap-y-2 rounded-[10px] border border-brand-base bg-brand-surface-1 p-5">
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<PeopleGroupIcon color="#FF6B00" width={28} height={28} className="flex-shrink-0" />
|
|
||||||
<div>
|
|
||||||
<h4 className="-mt-1.5 text-xl font-semibold">Modules</h4>
|
|
||||||
<p className="text-brand-secondary">
|
|
||||||
Modules are enabled for all the projects in this workspace. Access it from the
|
|
||||||
navigation bar.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${
|
|
||||||
projectDetails?.module_view ? "bg-green-500" : "bg-brand-surface-2"
|
|
||||||
}`}
|
|
||||||
role="switch"
|
|
||||||
aria-checked={projectDetails?.module_view}
|
|
||||||
onClick={() => {
|
|
||||||
trackEventServices.trackMiscellaneousEvent(
|
|
||||||
{
|
|
||||||
workspaceId: (projectDetails?.workspace as any)?.id,
|
|
||||||
workspaceSlug,
|
|
||||||
projectId,
|
|
||||||
projectIdentifier: projectDetails?.identifier,
|
|
||||||
projectName: projectDetails?.name,
|
|
||||||
},
|
|
||||||
!projectDetails?.module_view ? "TOGGLE_MODULE_ON" : "TOGGLE_MODULE_OFF"
|
|
||||||
);
|
|
||||||
handleSubmit({ module_view: !projectDetails?.module_view });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Use cycles</span>
|
|
||||||
<span
|
|
||||||
aria-hidden="true"
|
|
||||||
className={`inline-block h-5 w-5 transform rounded-full bg-brand-surface-1 shadow ring-0 transition duration-200 ease-in-out ${
|
|
||||||
projectDetails?.module_view ? "translate-x-5" : "translate-x-0"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between gap-x-8 gap-y-2 rounded-[10px] border border-brand-base bg-brand-surface-1 p-5">
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<ViewListIcon color="#05C3FF" width={28} height={28} className="flex-shrink-0" />
|
|
||||||
<div>
|
|
||||||
<h4 className="-mt-1.5 text-xl font-semibold">Views</h4>
|
|
||||||
<p className="text-brand-secondary">
|
|
||||||
Views are enabled for all the projects in this workspace. Access it from the
|
|
||||||
navigation bar.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${
|
|
||||||
projectDetails?.issue_views_view ? "bg-green-500" : "bg-brand-surface-2"
|
|
||||||
}`}
|
|
||||||
role="switch"
|
|
||||||
aria-checked={projectDetails?.issue_views_view}
|
|
||||||
onClick={() => {
|
|
||||||
trackEventServices.trackMiscellaneousEvent(
|
|
||||||
{
|
|
||||||
workspaceId: (projectDetails?.workspace as any)?.id,
|
|
||||||
workspaceSlug,
|
|
||||||
projectId,
|
|
||||||
projectIdentifier: projectDetails?.identifier,
|
|
||||||
projectName: projectDetails?.name,
|
|
||||||
},
|
|
||||||
!projectDetails?.issue_views_view ? "TOGGLE_VIEW_ON" : "TOGGLE_VIEW_OFF"
|
|
||||||
);
|
|
||||||
handleSubmit({ issue_views_view: !projectDetails?.issue_views_view });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Use views</span>
|
|
||||||
<span
|
|
||||||
aria-hidden="true"
|
|
||||||
className={`inline-block h-5 w-5 transform rounded-full bg-brand-surface-1 shadow ring-0 transition duration-200 ease-in-out ${
|
|
||||||
projectDetails?.issue_views_view ? "translate-x-5" : "translate-x-0"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between gap-x-8 gap-y-2 rounded-[10px] border border-brand-base bg-brand-surface-1 p-5">
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<DocumentTextIcon color="#FCBE1D" width={28} height={28} className="flex-shrink-0" />
|
|
||||||
<div>
|
|
||||||
<h4 className="text-xl font-semibold">Pages</h4>
|
|
||||||
<p className="text-brand-secondary">
|
|
||||||
Pages are enabled for all the projects in this workspace. Access them from the
|
|
||||||
navigation bar.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${
|
|
||||||
projectDetails?.page_view ? "bg-green-500" : "bg-brand-surface-2"
|
|
||||||
}`}
|
|
||||||
role="switch"
|
|
||||||
aria-checked={projectDetails?.page_view}
|
|
||||||
onClick={() => {
|
|
||||||
trackEventServices.trackMiscellaneousEvent(
|
|
||||||
{
|
|
||||||
workspaceId: (projectDetails?.workspace as any)?.id,
|
|
||||||
workspaceSlug,
|
|
||||||
projectId,
|
|
||||||
projectIdentifier: projectDetails?.identifier,
|
|
||||||
projectName: projectDetails?.name,
|
|
||||||
},
|
|
||||||
!projectDetails?.page_view ? "TOGGLE_PAGES_ON" : "TOGGLE_PAGES_OFF"
|
|
||||||
);
|
|
||||||
handleSubmit({ page_view: !projectDetails?.page_view });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Use cycles</span>
|
|
||||||
<span
|
|
||||||
aria-hidden="true"
|
|
||||||
className={`inline-block h-5 w-5 transform rounded-full bg-brand-surface-1 shadow ring-0 transition duration-200 ease-in-out ${
|
|
||||||
projectDetails?.page_view ? "translate-x-5" : "translate-x-0"
|
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<a href="https://plane.so/" target="_blank" rel="noreferrer">
|
<a href="https://plane.so/" target="_blank" rel="noreferrer">
|
||||||
|
@ -10,6 +10,7 @@ import projectService from "services/project.service";
|
|||||||
import workspaceService from "services/workspace.service";
|
import workspaceService from "services/workspace.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
|
import useProjectDetails from "hooks/use-project-details";
|
||||||
// layouts
|
// layouts
|
||||||
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
|
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
|
||||||
// components
|
// components
|
||||||
@ -23,12 +24,7 @@ import { PlusIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
|||||||
// types
|
// types
|
||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import {
|
import { PROJECT_INVITATIONS, PROJECT_MEMBERS, WORKSPACE_DETAILS } from "constants/fetch-keys";
|
||||||
PROJECT_DETAILS,
|
|
||||||
PROJECT_INVITATIONS,
|
|
||||||
PROJECT_MEMBERS,
|
|
||||||
WORKSPACE_DETAILS,
|
|
||||||
} from "constants/fetch-keys";
|
|
||||||
// constants
|
// constants
|
||||||
import { ROLE } from "constants/workspace";
|
import { ROLE } from "constants/workspace";
|
||||||
|
|
||||||
@ -48,24 +44,13 @@ const MembersSettings: NextPage = () => {
|
|||||||
() => (workspaceSlug ? workspaceService.getWorkspace(workspaceSlug as string) : null)
|
() => (workspaceSlug ? workspaceService.getWorkspace(workspaceSlug as string) : null)
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: projectDetails } = useSWR(
|
const { projectDetails } = useProjectDetails();
|
||||||
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? () => projectService.getProject(workspaceSlug as string, projectId as string)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data: projectMembers, mutate: mutateMembers } = useSWR(
|
const { data: projectMembers, mutate: mutateMembers } = useSWR(
|
||||||
workspaceSlug && projectId ? PROJECT_MEMBERS(projectId as string) : null,
|
workspaceSlug && projectId ? PROJECT_MEMBERS(projectId as string) : null,
|
||||||
workspaceSlug && projectId
|
workspaceSlug && projectId
|
||||||
? () => projectService.projectMembers(workspaceSlug as string, projectId as string)
|
? () => projectService.projectMembers(workspaceSlug as string, projectId as string)
|
||||||
: null,
|
: null
|
||||||
{
|
|
||||||
onErrorRetry(err, _, __, revalidate, revalidateOpts) {
|
|
||||||
if (err?.status === 403) return;
|
|
||||||
setTimeout(() => revalidate(revalidateOpts), 5000);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: projectInvitations, mutate: mutateInvitations } = useSWR(
|
const { data: projectInvitations, mutate: mutateInvitations } = useSWR(
|
||||||
@ -176,11 +161,11 @@ const MembersSettings: NextPage = () => {
|
|||||||
<Loader.Item height="40px" />
|
<Loader.Item height="40px" />
|
||||||
</Loader>
|
</Loader>
|
||||||
) : (
|
) : (
|
||||||
<div className="divide-y divide-brand-base rounded-[10px] border border-brand-base bg-brand-surface-1 px-6">
|
<div className="divide-y divide-brand-base rounded-[10px] border border-brand-base bg-brand-base px-6">
|
||||||
{members.length > 0
|
{members.length > 0
|
||||||
? members.map((member) => (
|
? members.map((member) => (
|
||||||
<div key={member.id} className="flex items-center justify-between py-6">
|
<div key={member.id} className="flex items-center justify-between py-6">
|
||||||
<div className="flex items-center gap-x-8 gap-y-2">
|
<div className="flex items-center gap-x-6 gap-y-2">
|
||||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg bg-gray-700 p-4 capitalize text-white">
|
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg bg-gray-700 p-4 capitalize text-white">
|
||||||
{member.avatar && member.avatar !== "" ? (
|
{member.avatar && member.avatar !== "" ? (
|
||||||
<Image
|
<Image
|
||||||
@ -203,12 +188,12 @@ const MembersSettings: NextPage = () => {
|
|||||||
<p className="mt-0.5 text-xs text-brand-secondary">{member.email}</p>
|
<p className="mt-0.5 text-xs text-brand-secondary">{member.email}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!member.member && (
|
|
||||||
<span className="inline-flex items-center rounded-full bg-yellow-100 px-2.5 py-0.5 text-xs font-medium text-yellow-800">
|
|
||||||
Request Pending
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
|
{!member.member && (
|
||||||
|
<div className="mr-2 flex items-center justify-center rounded-full bg-yellow-500/20 px-2 py-1 text-center text-xs text-yellow-500">
|
||||||
|
Pending
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
label={ROLE[member.role as keyof typeof ROLE]}
|
label={ROLE[member.role as keyof typeof ROLE]}
|
||||||
value={member.role}
|
value={member.role}
|
||||||
@ -242,6 +227,7 @@ const MembersSettings: NextPage = () => {
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
position="right"
|
||||||
>
|
>
|
||||||
{Object.keys(ROLE).map((key) => (
|
{Object.keys(ROLE).map((key) => (
|
||||||
<CustomSelect.Option key={key} value={key}>
|
<CustomSelect.Option key={key} value={key}>
|
||||||
|
@ -110,7 +110,10 @@ const StatesSettings: NextPage = () => {
|
|||||||
handleDeleteState={() => setSelectDeleteState(state.id)}
|
handleDeleteState={() => setSelectDeleteState(state.id)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="border-b last:border-b-0" key={state.id}>
|
<div
|
||||||
|
className="border-b border-brand-base last:border-b-0"
|
||||||
|
key={state.id}
|
||||||
|
>
|
||||||
<CreateUpdateStateInline
|
<CreateUpdateStateInline
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setActiveGroup(null);
|
setActiveGroup(null);
|
||||||
|
@ -157,7 +157,7 @@ const MembersSettings: NextPage = () => {
|
|||||||
<Loader.Item height="40px" />
|
<Loader.Item height="40px" />
|
||||||
</Loader>
|
</Loader>
|
||||||
) : (
|
) : (
|
||||||
<div className="divide-y rounded-[10px] divide-brand-base border border-brand-base bg-brand-surface-1 px-6">
|
<div className="divide-y divide-brand-base rounded-[10px] border border-brand-base bg-brand-base px-6">
|
||||||
{members.length > 0
|
{members.length > 0
|
||||||
? members.map((member) => (
|
? members.map((member) => (
|
||||||
<div key={member.id} className="flex items-center justify-between py-6">
|
<div key={member.id} className="flex items-center justify-between py-6">
|
||||||
@ -184,13 +184,12 @@ const MembersSettings: NextPage = () => {
|
|||||||
<p className="text-xs text-brand-secondary">{member.email}</p>
|
<p className="text-xs text-brand-secondary">{member.email}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
{!member?.status && (
|
{!member?.status && (
|
||||||
<div className="flex mr-2 px-2 py-1 bg-yellow-200 text-yellow-700 text-center rounded-full text-xs items-center justify-center">
|
<div className="mr-2 flex items-center justify-center rounded-full bg-yellow-500/20 px-2 py-1 text-center text-xs text-yellow-500">
|
||||||
<p>Pending</p>
|
<p>Pending</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center gap-2 text-xs">
|
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
label={ROLE[member.role as keyof typeof ROLE]}
|
label={ROLE[member.role as keyof typeof ROLE]}
|
||||||
value={member.role}
|
value={member.role}
|
||||||
@ -221,6 +220,7 @@ const MembersSettings: NextPage = () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
position="right"
|
||||||
>
|
>
|
||||||
{Object.keys(ROLE).map((key) => (
|
{Object.keys(ROLE).map((key) => (
|
||||||
<CustomSelect.Option key={key} value={key}>
|
<CustomSelect.Option key={key} value={key}>
|
||||||
@ -243,7 +243,6 @@ const MembersSettings: NextPage = () => {
|
|||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
))
|
))
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
|
@ -244,7 +244,7 @@ class ProjectServices extends APIService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncGiuthubRepository(
|
async syncGithubRepository(
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
workspaceIntegrationId: string,
|
workspaceIntegrationId: string,
|
||||||
|
@ -47,7 +47,7 @@ type ViewEventType = "VIEW_CREATE" | "VIEW_UPDATE" | "VIEW_DELETE";
|
|||||||
|
|
||||||
type IssueCommentType = "ISSUE_COMMENT_CREATE" | "ISSUE_COMMENT_UPDATE" | "ISSUE_COMMENT_DELETE";
|
type IssueCommentType = "ISSUE_COMMENT_CREATE" | "ISSUE_COMMENT_UPDATE" | "ISSUE_COMMENT_DELETE";
|
||||||
|
|
||||||
type MiscellaneousEventType =
|
export type MiscellaneousEventType =
|
||||||
| "TOGGLE_CYCLE_ON"
|
| "TOGGLE_CYCLE_ON"
|
||||||
| "TOGGLE_CYCLE_OFF"
|
| "TOGGLE_CYCLE_OFF"
|
||||||
| "TOGGLE_MODULE_ON"
|
| "TOGGLE_MODULE_ON"
|
||||||
|
@ -96,8 +96,11 @@
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* scrollbar style */
|
body {
|
||||||
|
color: rgba(var(--color-text-base));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* scrollbar style */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -206,7 +209,7 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar{
|
.progress-bar {
|
||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
color: rgba(var(--color-bg-sidebar));
|
color: rgba(var(--color-bg-sidebar));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user