fix: views issues mutation, sidebar link highlight (#1025)

* fix: views issues mutation, sidebar link highlight

* fix: show only specific states when type filter is set

* fix: delete comment mutation

* style: bulk delete issues modal

* fix: project settings features mutation
This commit is contained in:
Aaryan Khandelwal 2023-05-11 02:15:39 +05:30 committed by GitHub
parent 4884ecd668
commit df96d40cfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 165 additions and 74 deletions

View File

@ -167,7 +167,7 @@ export const SingleBoard: React.FC<Props> = ({
Add Issue
</button>
}
optionsPosition="left"
position="left"
noBorder
>
<CustomMenu.MenuItem onClick={addIssueToState}>

View File

@ -121,7 +121,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-brand-surface-2 shadow-2xl ring-1 ring-black ring-opacity-5 transition-all">
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 rounded-xl border border-brand-base bg-brand-base shadow-2xl transition-all">
<form>
<Combobox
onChange={(val: string) => {
@ -149,7 +149,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
<Combobox.Options
static
className="max-h-80 scroll-py-2 divide-y divide-gray-500 divide-opacity-10 overflow-y-auto"
className="max-h-80 scroll-py-2 divide-y divide-brand-base overflow-y-auto"
>
{filteredIssues.length > 0 ? (
<li className="p-2">
@ -158,15 +158,15 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
Select issues to delete
</h2>
)}
<ul className="text-sm text-gray-700">
<ul className="text-sm text-brand-secondary">
{filteredIssues.map((issue) => (
<Combobox.Option
key={issue.id}
as="div"
value={issue.id}
className={({ active }) =>
className={({ active, selected }) =>
`flex cursor-pointer select-none items-center justify-between rounded-md px-3 py-2 ${
active ? "bg-gray-900 bg-opacity-5 text-brand-base" : ""
active ? "bg-brand-surface-2 text-brand-base" : ""
}`
}
>
@ -182,7 +182,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
backgroundColor: issue.state_detail.color,
}}
/>
<span className="flex-shrink-0 text-xs text-brand-secondary">
<span className="flex-shrink-0 text-xs">
{issue.project_detail.identifier}-{issue.sequence_id}
</span>
<span>{issue.name}</span>

View File

@ -110,7 +110,7 @@ export const ImageUploadModal: React.FC<Props> = ({
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
<div className="fixed inset-0 bg-brand-backdrop bg-opacity-50 transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-30 overflow-y-auto">
@ -124,7 +124,7 @@ export const ImageUploadModal: React.FC<Props> = ({
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-brand-surface-2 px-5 py-8 text-left shadow-xl transition-all sm:w-full sm:max-w-xl sm:p-6">
<Dialog.Panel className="relative transform overflow-hidden rounded-lg border border-brand-base bg-brand-base px-5 py-8 text-left shadow-xl transition-all sm:w-full sm:max-w-xl sm:p-6">
<div className="space-y-5">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-brand-base">
Upload Image
@ -133,9 +133,9 @@ export const ImageUploadModal: React.FC<Props> = ({
<div className="flex items-center gap-3">
<div
{...getRootProps()}
className={`relative block h-80 w-full rounded-lg p-12 text-center focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 ${
className={`relative grid h-80 w-full cursor-pointer place-items-center rounded-lg p-12 text-center focus:outline-none focus:ring-2 focus:ring-brand-accent focus:ring-offset-2 ${
(image === null && isDragActive) || !value
? "border-2 border-dashed border-brand-base hover:border-gray-400"
? "border-2 border-dashed border-brand-base hover:bg-brand-surface-1"
: ""
}`}
>
@ -143,7 +143,7 @@ export const ImageUploadModal: React.FC<Props> = ({
<>
<button
type="button"
className="absolute top-0 right-0 z-40 translate-x-1/2 -translate-y-1/2 rounded bg-brand-surface-1 px-2 py-0.5 text-xs font-medium text-gray-600"
className="absolute top-0 right-0 z-40 translate-x-1/2 -translate-y-1/2 rounded bg-brand-surface-1 px-2 py-0.5 text-xs font-medium text-brand-secondary"
>
Edit
</button>
@ -152,17 +152,18 @@ export const ImageUploadModal: React.FC<Props> = ({
objectFit="cover"
src={image ? URL.createObjectURL(image) : value ? value : ""}
alt="image"
className="rounded-lg"
/>
</>
) : (
<>
<UserCircleIcon className="mx-auto h-16 w-16 text-gray-400" />
<span className="mt-2 block text-sm font-medium text-brand-base">
<div>
<UserCircleIcon className="mx-auto h-16 w-16 text-brand-secondary" />
<span className="mt-2 block text-sm font-medium text-brand-secondary">
{isDragActive
? "Drop image here to upload"
: "Drag & drop image here"}
</span>
</>
</div>
)}
<input {...getInputProps()} type="text" />

View File

@ -3,12 +3,12 @@ export * from "./list-view";
export * from "./sidebar";
export * from "./bulk-delete-issues-modal";
export * from "./existing-issues-list-modal";
export * from "./filters-list";
export * from "./gpt-assistant-modal";
export * from "./image-upload-modal";
export * from "./issues-view-filter";
export * from "./issues-view";
export * from "./link-modal";
export * from "./image-picker-popover";
export * from "./filter-list";
export * from "./feeds";
export * from "./theme-switch";

View File

@ -168,7 +168,7 @@ export const SingleList: React.FC<Props> = ({
<PlusIcon className="h-4 w-4" />
</div>
}
optionsPosition="right"
position="right"
noBorder
>
<CustomMenu.MenuItem onClick={addIssueToState}>Create new</CustomMenu.MenuItem>
@ -204,7 +204,8 @@ export const SingleList: React.FC<Props> = ({
makeIssueCopy={() => makeIssueCopy(issue)}
handleDeleteIssue={handleDeleteIssue}
removeIssue={() => {
if (removeIssue !== null && issue.bridge_id) removeIssue(issue.bridge_id, issue.id);
if (removeIssue !== null && issue.bridge_id)
removeIssue(issue.bridge_id, issue.id);
}}
isCompleted={isCompleted}
userAuth={userAuth}

View File

@ -143,6 +143,7 @@ export const JiraGetImportDetail: React.FC = () => {
)}
</span>
}
verticalPosition="top"
>
{projects.length > 0 ? (
projects.map((project) => (

View File

@ -152,6 +152,9 @@ export const IssueActivitySection: React.FC = () => {
const handleCommentDelete = async (commentId: string) => {
if (!workspaceSlug || !projectId || !issueId) return;
mutateIssueActivities((prevData) => prevData?.filter((p) => p.id !== commentId), false);
await issuesService
.deleteIssueComment(
workspaceSlug as string,

View File

@ -120,11 +120,11 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
};
const handleCycleChange = useCallback(
(cycleDetail: ICycle) => {
(cycleDetails: ICycle) => {
if (!workspaceSlug || !projectId || !issueDetail) return;
issuesService
.addIssueToCycle(workspaceSlug as string, projectId as string, cycleDetail.id, {
.addIssueToCycle(workspaceSlug as string, projectId as string, cycleDetails.id, {
issues: [issueDetail.id],
})
.then((res) => {
@ -361,6 +361,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
target_date: val,
})
}
className="bg-brand-surface-1"
disabled={isNotAllowed}
/>
)}

View File

@ -322,7 +322,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue }) => {
Add sub-issue
</>
}
optionsPosition="left"
position="left"
noBorder
noChevron
>

View File

@ -322,7 +322,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
)}
<button
type="button"
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-brand-surface-2 ${
className={`flex items-center gap-1 rounded bg-brand-surface-1 px-1.5 py-1 text-xs hover:bg-brand-surface-2 ${
iAmFeelingLucky ? "cursor-wait bg-brand-surface-2" : ""
}`}
onClick={handleAutoGenerateDescription}
@ -338,7 +338,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
</button>
<button
type="button"
className="-mr-2 flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-brand-surface-2"
className="-mr-2 flex items-center gap-1 rounded bg-brand-surface-1 px-1.5 py-1 text-xs hover:bg-brand-surface-2"
onClick={() => setGptAssistantModal((prevData) => !prevData)}
>
<SparklesIcon className="h-4 w-4" />
@ -346,7 +346,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
</button>
<button
type="button"
className="-mr-2 flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-brand-surface-2"
className="-mr-2 flex items-center gap-1 rounded bg-brand-surface-1 px-1.5 py-1 text-xs hover:bg-brand-surface-2"
onClick={() => setCreateBlockForm(true)}
>
<PencilIcon className="h-3.5 w-3.5" />
@ -354,7 +354,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
<CustomMenu
customButton={
<button
className="flex w-full cursor-pointer items-center justify-between gap-1 rounded px-2.5 py-1 text-left text-xs duration-300 hover:bg-brand-surface-2"
className="flex w-full cursor-pointer items-center justify-between gap-1 rounded bg-brand-surface-1 px-2.5 py-1 text-left text-xs duration-300 hover:bg-brand-surface-2"
onClick={() => setIsMenuActive(!isMenuActive)}
>
<BoltIcon className="h-4.5 w-3.5" />

View File

@ -37,8 +37,8 @@ export const ProjectSidebarList: FC = () => {
const { setToastAlert } = useToast();
const { data: favoriteProjects } = useSWR(
workspaceSlug ? FAVORITE_PROJECTS_LIST(workspaceSlug as string) : null,
() => (workspaceSlug ? projectService.getFavoriteProjects(workspaceSlug as string) : null)
workspaceSlug ? FAVORITE_PROJECTS_LIST(workspaceSlug.toString()) : null,
() => (workspaceSlug ? projectService.getFavoriteProjects(workspaceSlug.toString()) : null)
);
const { data: projects } = useSWR(

View File

@ -17,7 +17,8 @@ type Props = {
textAlignment?: "left" | "center" | "right";
noBorder?: boolean;
noChevron?: boolean;
optionsPosition?: "left" | "right";
position?: "left" | "right";
verticalPosition?: "top" | "bottom";
customButton?: JSX.Element;
};
@ -40,7 +41,8 @@ const CustomMenu = ({
textAlignment,
noBorder = false,
noChevron = false,
optionsPosition = "right",
position = "right",
verticalPosition = "bottom",
customButton,
}: Props) => (
<Menu as="div" className={`relative w-min whitespace-nowrap text-left ${className}`}>
@ -103,9 +105,9 @@ const CustomMenu = ({
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
className={`absolute z-20 mt-1 overflow-y-scroll whitespace-nowrap rounded-md border border-brand-base bg-brand-surface-1 p-1 text-xs shadow-lg focus:outline-none ${
optionsPosition === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
} ${
className={`absolute z-20 overflow-y-scroll whitespace-nowrap rounded-md border border-brand-base bg-brand-surface-1 p-1 text-xs shadow-lg focus:outline-none ${
position === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
} ${verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"} ${
height === "sm"
? "max-h-28"
: height === "md"

View File

@ -15,6 +15,7 @@ type CustomSearchSelectProps = {
textAlignment?: "left" | "center" | "right";
height?: "sm" | "md" | "rg" | "lg";
position?: "right" | "left";
verticalPosition?: "top" | "bottom";
noChevron?: boolean;
customButton?: JSX.Element;
optionsClassName?: string;
@ -32,6 +33,7 @@ export const CustomSearchSelect = ({
onChange,
options,
position = "left",
verticalPosition = "bottom",
noChevron = false,
customButton,
optionsClassName = "",
@ -99,7 +101,9 @@ export const CustomSearchSelect = ({
<Combobox.Options
className={`${optionsClassName} absolute min-w-[10rem] border border-brand-base p-2 ${
position === "right" ? "right-0" : "left-0"
} z-10 mt-1 origin-top-right rounded-md bg-brand-surface-1 text-xs shadow-lg focus:outline-none`}
} ${
verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"
} z-10 origin-top-right rounded-md bg-brand-surface-1 text-xs shadow-lg focus:outline-none`}
>
<div className="flex w-full items-center justify-start rounded-sm border-[0.6px] border-brand-base bg-brand-surface-1 px-2">
<MagnifyingGlassIcon className="h-3 w-3 text-brand-secondary" />

View File

@ -13,6 +13,7 @@ type CustomSelectProps = {
textAlignment?: "left" | "center" | "right";
maxHeight?: "sm" | "rg" | "md" | "lg" | "none";
position?: "right" | "left";
verticalPosition?: "top" | "bottom";
width?: "auto" | string;
input?: boolean;
noChevron?: boolean;
@ -30,6 +31,7 @@ const CustomSelect = ({
onChange,
maxHeight = "none",
position = "left",
verticalPosition = "bottom",
width = "auto",
input = false,
noChevron = false,
@ -80,6 +82,8 @@ const CustomSelect = ({
<Listbox.Options
className={`${optionsClassName} absolute border border-brand-base ${
position === "right" ? "right-0" : "left-0"
} ${
verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"
} z-10 mt-1 origin-top-right overflow-y-auto rounded-md bg-brand-surface-1 text-xs shadow-lg focus:outline-none ${
width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width
} ${input ? "max-h-48" : ""} ${

View File

@ -18,13 +18,16 @@ const EmptySpace: React.FC<EmptySpaceProps> = ({ title, description, children, I
<div className="max-w-lg">
{Icon ? (
<div className="mb-4">
<Icon className="h-14 w-14 text-gray-400" />
<Icon className="h-14 w-14 text-brand-secondary" />
</div>
) : null}
<h2 className="text-lg font-medium text-brand-base">{title}</h2>
<div className="mt-1 text-sm text-brand-secondary">{description}</div>
<ul role="list" className="mt-6 divide-y divide-gray-200 border-t border-brand-base border-b">
<ul
role="list"
className="mt-6 divide-y divide-brand-base border-t border-b border-brand-base"
>
{children}
</ul>
{link ? (
@ -57,7 +60,7 @@ const EmptySpaceItem: React.FC<EmptySpaceItemProps> = ({ title, description, Ico
} space-x-3 py-4`}
>
<div className="flex-shrink-0">
<span className={`inline-flex h-10 w-10 items-center justify-center rounded-lg bg-brand-accent`}>
<span className="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-brand-accent">
<Icon className="h-6 w-6 text-white" aria-hidden="true" />
</span>
</div>
@ -67,7 +70,7 @@ const EmptySpaceItem: React.FC<EmptySpaceItemProps> = ({ title, description, Ico
</div>
<div className="flex-shrink-0 self-center">
<ChevronRightIcon
className="h-5 w-5 text-gray-400 group-hover:text-brand-secondary"
className="h-5 w-5 text-brand-base group-hover:text-brand-secondary"
aria-hidden="true"
/>
</div>

View File

@ -43,9 +43,9 @@ export const WorkspaceSidebarMenu: React.FC = () => {
<a
className={`${
(
link.name === "Dashboard"
? router.asPath === link.href
: router.asPath.includes(link.href)
link.name === "Settings"
? router.asPath.includes(link.href)
: router.asPath === link.href
)
? "bg-brand-surface-2 text-brand-base"
: "text-brand-secondary hover:bg-brand-surface-2 hover:text-brand-secondary focus:bg-brand-surface-2 focus:text-brand-secondary"

View File

@ -106,13 +106,19 @@ export const MODULE_ISSUES_WITH_PARAMS = (moduleId: string, params?: any) => {
const paramsKey = paramsToKey(params);
return `MODULE_ISSUES_WITH_PARAMS_${moduleId}_${paramsKey.toUpperCase()}`;
return `MODULE_ISSUES_WITH_PARAMS_${moduleId.toUpperCase()}_${paramsKey.toUpperCase()}`;
};
export const MODULE_DETAILS = (moduleId: string) => `MODULE_DETAILS_${moduleId.toUpperCase()}`;
export const VIEWS_LIST = (projectId: string) => `VIEWS_LIST_${projectId.toUpperCase()}`;
export const VIEW_ISSUES = (viewId: string) => `VIEW_ISSUES_${viewId.toUpperCase()}`;
export const VIEW_DETAILS = (viewId: string) => `VIEW_DETAILS_${viewId.toUpperCase()}`;
export const VIEW_ISSUES = (viewId: string, params: any) => {
if (!params) return `VIEW_ISSUES_${viewId.toUpperCase()}`;
const paramsKey = paramsToKey(params);
return `VIEW_ISSUES_${viewId.toUpperCase()}_${paramsKey.toUpperCase()}`;
};
// Issues
export const ISSUE_DETAILS = (issueId: string) => `ISSUE_DETAILS_${issueId.toUpperCase()}`;

View File

@ -12,6 +12,8 @@ export const orderArrayBy = (
key: string,
ordering: "ascending" | "descending" = "ascending"
) => {
if (!array || !Array.isArray(array) || array.length === 0) return [];
if (key[0] === "-") {
ordering = "descending";
key = key.slice(1);

View File

@ -21,6 +21,7 @@ import {
MODULE_ISSUES_WITH_PARAMS,
PROJECT_ISSUES_LIST_WITH_PARAMS,
STATES_LIST,
VIEW_ISSUES,
} from "constants/fetch-keys";
const useIssuesView = () => {
@ -40,7 +41,7 @@ const useIssuesView = () => {
} = useContext(issueViewContext);
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
const params: any = {
order_by: orderBy,
@ -99,6 +100,14 @@ const useIssuesView = () => {
: null
);
const { data: viewIssues } = useSWR(
workspaceSlug && projectId && viewId && params ? VIEW_ISSUES(viewId.toString(), params) : null,
workspaceSlug && projectId && viewId && params
? () =>
issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
: null
);
const { data: states } = useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
workspaceSlug && projectId
@ -106,7 +115,17 @@ const useIssuesView = () => {
: null
);
const statesList = getStatesList(states ?? {});
const stateIds = statesList.map((state) => state.id);
const activeStatesList = statesList.filter(
(state) => state.group === "started" || state.group === "unstarted"
);
const backlogStatesList = statesList.filter((state) => state.group === "backlog");
const stateIds =
filters && filters?.type === "active"
? activeStatesList.map((state) => state.id)
: filters?.type === "backlog"
? backlogStatesList.map((state) => state.id)
: statesList.map((state) => state.id);
const emptyStatesObject: { [key: string]: [] } = {};
for (let i = 0; i < stateIds.length; i++) {
@ -118,7 +137,13 @@ const useIssuesView = () => {
[key: string]: IIssue[];
}
| undefined = useMemo(() => {
const issuesToGroup = cycleId ? cycleIssues : moduleId ? moduleIssues : projectIssues;
const issuesToGroup = cycleId
? cycleIssues
: moduleId
? moduleIssues
: viewId
? viewIssues
: projectIssues;
if (Array.isArray(issuesToGroup)) return { allIssues: issuesToGroup };
if (groupByProperty === "state")
@ -129,9 +154,11 @@ const useIssuesView = () => {
projectIssues,
cycleIssues,
moduleIssues,
viewIssues,
groupByProperty,
cycleId,
moduleId,
viewId,
emptyStatesObject,
]);

View File

@ -1,7 +1,11 @@
import useSWR from "swr";
import { useRouter } from "next/router";
import useSWR from "swr";
// services
import projectService from "services/project.service";
// helpers
import { orderArrayBy } from "helpers/array.helper";
// fetch-keys
import { PROJECTS_LIST } from "constants/fetch-keys";
@ -20,7 +24,7 @@ const useProjects = () => {
.filter((_item, index) => index < 3);
return {
projects: projects || [],
projects: orderArrayBy(projects ?? [], "is_favorite", "descending") || [],
recentProjects: recentProjects || [],
mutateProjects,
};

View File

@ -30,7 +30,7 @@ const SettingsNavbar: React.FC<Props> = ({ profilePage = false }) => {
href: `/${workspaceSlug}/settings/integrations`,
},
{
label: "Import/ Export",
label: "Import/Export",
href: `/${workspaceSlug}/settings/import-export`,
},
];
@ -94,7 +94,11 @@ const SettingsNavbar: React.FC<Props> = ({ profilePage = false }) => {
<a>
<div
className={`rounded-3xl border border-brand-base px-5 py-1.5 text-sm sm:px-7 sm:py-2 sm:text-base ${
router.asPath === link.href
(
link.label === "Import/Export"
? router.asPath.includes(link.href)
: router.asPath === link.href
)
? "border-brand-accent bg-brand-accent text-white"
: "border-brand-base bg-brand-surface-2 hover:bg-brand-surface-1"
}`}

View File

@ -164,7 +164,7 @@ const IssueDetailsPage: NextPage = () => {
</a>
</Link>
<CustomMenu ellipsis optionsPosition="left">
<CustomMenu ellipsis position="left">
{siblingIssues && siblingIssues.length > 0 ? (
siblingIssues.map((issue: IIssue) => (
<CustomMenu.MenuItem key={issue.id}>

View File

@ -563,7 +563,7 @@ const SinglePage: NextPage = () => {
</div>
</div>
) : (
<Loader>
<Loader className="p-8">
<Loader.Item height="200px" />
</Loader>
)}

View File

@ -18,10 +18,10 @@ import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
import { ContrastIcon, PeopleGroupIcon, ViewListIcon } from "components/icons";
import { DocumentTextIcon } from "@heroicons/react/24/outline";
// types
import { IProject } from "types";
import { IFavoriteProject, IProject } from "types";
import type { NextPage } from "next";
// fetch-keys
import { PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
import { FAVORITE_PROJECTS_LIST, PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
import { SettingsHeader } from "components/project";
const featuresList = [
@ -84,16 +84,29 @@ const FeaturesSettings: NextPage = () => {
);
const handleSubmit = async (formData: Partial<IProject>) => {
if (!workspaceSlug || !projectId) return;
if (!workspaceSlug || !projectId || !projectDetails) return;
mutate<IProject>(
PROJECT_DETAILS(projectId as string),
(prevData) => ({ ...(prevData as IProject), ...formData }),
false
);
if (projectDetails.is_favorite)
mutate<IFavoriteProject[]>(
FAVORITE_PROJECTS_LIST(workspaceSlug.toString()),
(prevData) =>
prevData?.map((p) => {
if (p.project === projectId)
return {
...p,
project_detail: {
...p.project_detail,
...formData,
},
};
return p;
}),
false
);
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string),
PROJECTS_LIST(workspaceSlug.toString()),
(prevData) =>
prevData?.map((p) => {
if (p.id === projectId)
@ -107,19 +120,34 @@ const FeaturesSettings: NextPage = () => {
false
);
mutate<IProject>(
PROJECT_DETAILS(projectId as string),
(prevData) => ({ ...(prevData as IProject), ...formData }),
false
);
setToastAlert({
type: "success",
title: "Success!",
message: "Project feature updated successfully.",
});
await projectService
.updateProject(workspaceSlug as string, projectId as string, formData)
.then((res) => {
.then(() => {
mutate(
projectDetails.is_favorite
? FAVORITE_PROJECTS_LIST(workspaceSlug.toString())
: PROJECTS_LIST(workspaceSlug.toString())
);
mutate(PROJECT_DETAILS(projectId as string));
mutate(PROJECTS_LIST(workspaceSlug as string));
setToastAlert({
title: "Success!",
type: "success",
message: "Project features updated successfully.",
});
})
.catch((err) => {
console.error(err);
setToastAlert({
type: "error",
title: "Error!",
message: "Project feature could not be updated. Please try again.",
});
});
};

View File

@ -229,7 +229,7 @@ const GeneralSettings: NextPage = () => {
</div>
<div className="col-span-12 sm:col-span-6">
{watch("cover_image") ? (
<div className="h-32 w-full rounded border p-1">
<div className="h-32 w-full rounded border border-brand-base p-1">
<div className="relative h-full w-full rounded">
<Image
src={watch("cover_image")!}

View File

@ -95,7 +95,7 @@ const SingleView: React.FC = () => {
document.dispatchEvent(e);
}}
>
<PlusIcon className="w-4 h-4" />
<PlusIcon className="h-4 w-4" />
Add Issue
</PrimaryButton>
</div>

View File

@ -119,7 +119,7 @@ const ProjectViews: NextPage = () => {
/>
)
) : (
<Loader className="space-y-3">
<Loader className="space-y-3 p-8">
<Loader.Item height="30px" />
<Loader.Item height="30px" />
<Loader.Item height="30px" />

View File

@ -19,12 +19,12 @@ import { Loader, EmptyState, PrimaryButton } from "components/ui";
import { Breadcrumbs, BreadcrumbItem } from "components/breadcrumbs";
// icons
import { PlusIcon } from "@heroicons/react/24/outline";
// images
import emptyProject from "public/empty-state/empty-project.svg";
// types
import type { NextPage } from "next";
// fetch-keys
import { PROJECT_MEMBERS } from "constants/fetch-keys";
// image
import emptyProject from "public/empty-state/empty-project.svg";
const ProjectsPage: NextPage = () => {
// router