mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
214 lines
7.0 KiB
TypeScript
214 lines
7.0 KiB
TypeScript
|
import React, { useState } from "react";
|
||
|
|
||
|
import Link from "next/link";
|
||
|
|
||
|
import { useRouter } from "next/router";
|
||
|
|
||
|
import useSWR from "swr";
|
||
|
|
||
|
// services
|
||
|
import workspaceService from "services/workspace.service";
|
||
|
// hooks
|
||
|
import useUser from "hooks/use-user";
|
||
|
// layouts
|
||
|
import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
|
||
|
// components
|
||
|
import { CreateUpdateViewModal, DeleteViewModal, SingleViewItem } from "components/views";
|
||
|
import { WorkspaceIssuesViewOptions } from "components/issues/workspace-views/workspace-issue-view-option";
|
||
|
// ui
|
||
|
import { EmptyState, Input, Loader, PrimaryButton } from "components/ui";
|
||
|
// icons
|
||
|
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||
|
import { PlusIcon } from "lucide-react";
|
||
|
import { PhotoFilterOutlined } from "@mui/icons-material";
|
||
|
// image
|
||
|
import emptyView from "public/empty-state/view.svg";
|
||
|
// types
|
||
|
import type { NextPage } from "next";
|
||
|
import { IView } from "types";
|
||
|
// constants
|
||
|
import { WORKSPACE_VIEWS_LIST } from "constants/fetch-keys";
|
||
|
// helper
|
||
|
import { truncateText } from "helpers/string.helper";
|
||
|
|
||
|
const WorkspaceViews: NextPage = () => {
|
||
|
const [query, setQuery] = useState("");
|
||
|
|
||
|
const [createUpdateViewModal, setCreateUpdateViewModal] = useState(false);
|
||
|
const [selectedViewToUpdate, setSelectedViewToUpdate] = useState<IView | null>(null);
|
||
|
|
||
|
const [deleteViewModal, setDeleteViewModal] = useState(false);
|
||
|
const [selectedViewToDelete, setSelectedViewToDelete] = useState<IView | null>(null);
|
||
|
|
||
|
const router = useRouter();
|
||
|
const { workspaceSlug } = router.query;
|
||
|
|
||
|
const { user } = useUser();
|
||
|
|
||
|
const { data: workspaceViews } = useSWR(
|
||
|
workspaceSlug ? WORKSPACE_VIEWS_LIST(workspaceSlug as string) : null,
|
||
|
workspaceSlug ? () => workspaceService.getAllViews(workspaceSlug as string) : null
|
||
|
);
|
||
|
|
||
|
const defaultWorkspaceViewsList = [
|
||
|
{
|
||
|
key: "all",
|
||
|
label: "All Issues",
|
||
|
href: `/${workspaceSlug}/workspace-views/all-issues`,
|
||
|
},
|
||
|
{
|
||
|
key: "assigned",
|
||
|
label: "Assigned",
|
||
|
href: `/${workspaceSlug}/workspace-views/assigned`,
|
||
|
},
|
||
|
{
|
||
|
key: "created",
|
||
|
label: "Created",
|
||
|
href: `/${workspaceSlug}/workspace-views/created`,
|
||
|
},
|
||
|
{
|
||
|
key: "subscribed",
|
||
|
label: "Subscribed",
|
||
|
href: `/${workspaceSlug}/workspace-views/subscribed`,
|
||
|
},
|
||
|
];
|
||
|
|
||
|
const filteredDefaultOptions =
|
||
|
query === ""
|
||
|
? defaultWorkspaceViewsList
|
||
|
: defaultWorkspaceViewsList?.filter((option) =>
|
||
|
option.label.toLowerCase().includes(query.toLowerCase())
|
||
|
);
|
||
|
|
||
|
const filteredOptions =
|
||
|
query === ""
|
||
|
? workspaceViews
|
||
|
: workspaceViews?.filter((option) => option.name.toLowerCase().includes(query.toLowerCase()));
|
||
|
|
||
|
const handleEditView = (view: IView) => {
|
||
|
setSelectedViewToUpdate(view);
|
||
|
setCreateUpdateViewModal(true);
|
||
|
};
|
||
|
|
||
|
const handleDeleteView = (view: IView) => {
|
||
|
setSelectedViewToDelete(view);
|
||
|
setDeleteViewModal(true);
|
||
|
};
|
||
|
|
||
|
return (
|
||
|
<WorkspaceAuthorizationLayout
|
||
|
breadcrumbs={
|
||
|
<div className="flex gap-2 items-center">
|
||
|
<span className="text-sm font-medium">Workspace Views</span>
|
||
|
</div>
|
||
|
}
|
||
|
right={
|
||
|
<div className="flex items-center gap-2">
|
||
|
<WorkspaceIssuesViewOptions />
|
||
|
|
||
|
<PrimaryButton
|
||
|
className="flex items-center gap-2"
|
||
|
onClick={() => setCreateUpdateViewModal(true)}
|
||
|
>
|
||
|
<PlusIcon className="h-4 w-4" />
|
||
|
New View
|
||
|
</PrimaryButton>
|
||
|
</div>
|
||
|
}
|
||
|
>
|
||
|
<CreateUpdateViewModal
|
||
|
isOpen={createUpdateViewModal}
|
||
|
handleClose={() => {
|
||
|
setCreateUpdateViewModal(false);
|
||
|
setSelectedViewToUpdate(null);
|
||
|
}}
|
||
|
data={selectedViewToUpdate}
|
||
|
viewType="workspace"
|
||
|
user={user}
|
||
|
/>
|
||
|
<DeleteViewModal
|
||
|
isOpen={deleteViewModal}
|
||
|
data={selectedViewToDelete}
|
||
|
setIsOpen={setDeleteViewModal}
|
||
|
viewType="workspace"
|
||
|
user={user}
|
||
|
/>
|
||
|
<div className="flex flex-col">
|
||
|
<div className="h-full w-full flex flex-col overflow-hidden">
|
||
|
<div className="flex items-center gap-2.5 w-full px-5 py-3 border-b border-custom-border-200">
|
||
|
<MagnifyingGlassIcon className="h-4 w-4 text-custom-text-200" />
|
||
|
<Input
|
||
|
className="w-full bg-transparent text-xs leading-5 text-custom-text-200 placeholder:text-custom-text-400 !p-0 focus:outline-none"
|
||
|
value={query}
|
||
|
onChange={(e) => setQuery(e.target.value)}
|
||
|
placeholder="Search"
|
||
|
mode="trueTransparent"
|
||
|
/>
|
||
|
</div>
|
||
|
</div>
|
||
|
{filteredDefaultOptions &&
|
||
|
filteredDefaultOptions.length > 0 &&
|
||
|
filteredDefaultOptions.map((option) => (
|
||
|
<div className="group hover:bg-custom-background-90 border-b border-custom-border-200">
|
||
|
<Link href={option.href}>
|
||
|
<a className="flex items-center justify-between relative rounded px-5 py-4 w-full">
|
||
|
<div className="flex items-center justify-between w-full">
|
||
|
<div className="flex items-center gap-4">
|
||
|
<div
|
||
|
className={`flex items-center justify-center h-10 w-10 rounded bg-custom-background-90 group-hover:bg-custom-background-100`}
|
||
|
>
|
||
|
<PhotoFilterOutlined className="!text-base !leading-6" />
|
||
|
</div>
|
||
|
<div className="flex flex-col">
|
||
|
<p className="truncate text-sm leading-4 font-medium">
|
||
|
{truncateText(option.label, 75)}
|
||
|
</p>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</a>
|
||
|
</Link>
|
||
|
</div>
|
||
|
))}
|
||
|
|
||
|
{filteredOptions ? (
|
||
|
filteredOptions.length > 0 ? (
|
||
|
<div>
|
||
|
{filteredOptions.map((view) => (
|
||
|
<SingleViewItem
|
||
|
key={view.id}
|
||
|
view={view}
|
||
|
viewType="workspace"
|
||
|
handleEditView={() => handleEditView(view)}
|
||
|
handleDeleteView={() => handleDeleteView(view)}
|
||
|
/>
|
||
|
))}
|
||
|
</div>
|
||
|
) : (
|
||
|
<EmptyState
|
||
|
title="Get focused with views"
|
||
|
description="Views aid in saving your issues by applying various filters and grouping options."
|
||
|
image={emptyView}
|
||
|
primaryButton={{
|
||
|
icon: <PlusIcon className="h-4 w-4" />,
|
||
|
text: "New View",
|
||
|
onClick: () => setCreateUpdateViewModal(true),
|
||
|
}}
|
||
|
/>
|
||
|
)
|
||
|
) : (
|
||
|
<Loader className="space-y-1.5">
|
||
|
<Loader.Item height="72px" />
|
||
|
<Loader.Item height="72px" />
|
||
|
<Loader.Item height="72px" />
|
||
|
<Loader.Item height="72px" />
|
||
|
<Loader.Item height="72px" />
|
||
|
</Loader>
|
||
|
)}
|
||
|
</div>
|
||
|
</WorkspaceAuthorizationLayout>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
export default WorkspaceViews;
|