Merge pull request #2304 from makeplane/develop

promote: develop to stage release
This commit is contained in:
sriram veeraghanta 2023-09-29 15:31:31 +05:30 committed by GitHub
commit 26bb51e686
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 414 additions and 385 deletions

View File

@ -83,88 +83,89 @@ export const IssueColumn: React.FC<Props> = ({
return ( return (
<div className="group flex items-center w-[28rem] text-sm h-11 sticky top-0 bg-custom-background-100 truncate border-b border-r border-custom-border-100"> <div className="group flex items-center w-[28rem] text-sm h-11 sticky top-0 bg-custom-background-100 truncate border-b border-r border-custom-border-100">
<div {properties.key && (
className="flex gap-1.5 px-4 pr-0 py-2.5 items-center w-24" <div
style={issue.parent && nestingLevel !== 0 ? { paddingLeft } : {}} className="flex gap-1.5 px-4 pr-0 py-2.5 items-center min-w-[96px]"
> style={issue.parent && nestingLevel !== 0 ? { paddingLeft } : {}}
<div className="relative flex items-center cursor-pointer text-xs text-center hover:text-custom-text-100"> >
{properties.key && ( <div className="relative flex items-center cursor-pointer text-xs text-center hover:text-custom-text-100">
<span className="flex items-center justify-center font-medium opacity-100 group-hover:opacity-0"> <span className="flex items-center justify-center font-medium opacity-100 group-hover:opacity-0 ">
{issue.project_detail?.identifier}-{issue.sequence_id} {issue.project_detail?.identifier}-{issue.sequence_id}
</span> </span>
)}
{!isNotAllowed && !disableUserActions && (
<div className="absolute top-0 left-2.5 opacity-0 group-hover:opacity-100">
<Popover2
isOpen={isOpen}
canEscapeKeyClose
onInteraction={(nextOpenState) => setIsOpen(nextOpenState)}
content={
<div
className={`flex flex-col gap-1.5 overflow-y-scroll whitespace-nowrap rounded-md border p-1 text-xs shadow-lg focus:outline-none max-h-44 min-w-full border-custom-border-100 bg-custom-background-90`}
>
<button
type="button"
className="hover:text-custom-text-200 w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80"
onClick={() => {
handleEditIssue(issue);
setIsOpen(false);
}}
>
<div className="flex items-center justify-start gap-2">
<PencilIcon className="h-4 w-4" />
<span>Edit issue</span>
</div>
</button>
<button {!isNotAllowed && !disableUserActions && (
type="button" <div className="absolute top-0 left-2.5 opacity-0 group-hover:opacity-100">
className="hover:text-custom-text-200 w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80" <Popover2
onClick={() => { isOpen={isOpen}
handleDeleteIssue(issue); canEscapeKeyClose
setIsOpen(false); onInteraction={(nextOpenState) => setIsOpen(nextOpenState)}
}} content={
<div
className={`flex flex-col gap-1.5 overflow-y-scroll whitespace-nowrap rounded-md border p-1 text-xs shadow-lg focus:outline-none max-h-44 min-w-full border-custom-border-100 bg-custom-background-90`}
> >
<div className="flex items-center justify-start gap-2"> <button
<TrashIcon className="h-4 w-4" /> type="button"
<span>Delete issue</span> className="hover:text-custom-text-200 w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80"
</div> onClick={() => {
</button> handleEditIssue(issue);
setIsOpen(false);
}}
>
<div className="flex items-center justify-start gap-2">
<PencilIcon className="h-4 w-4" />
<span>Edit issue</span>
</div>
</button>
<button <button
type="button" type="button"
className="hover:text-custom-text-200 w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80" className="hover:text-custom-text-200 w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80"
onClick={() => { onClick={() => {
handleCopyText(); handleDeleteIssue(issue);
setIsOpen(false); setIsOpen(false);
}} }}
> >
<div className="flex items-center justify-start gap-2"> <div className="flex items-center justify-start gap-2">
<LinkIcon className="h-4 w-4" /> <TrashIcon className="h-4 w-4" />
<span>Copy issue link</span> <span>Delete issue</span>
</div> </div>
</button> </button>
</div>
} <button
placement="bottom-start" type="button"
className="hover:text-custom-text-200 w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80"
onClick={() => {
handleCopyText();
setIsOpen(false);
}}
>
<div className="flex items-center justify-start gap-2">
<LinkIcon className="h-4 w-4" />
<span>Copy issue link</span>
</div>
</button>
</div>
}
placement="bottom-start"
>
<EllipsisHorizontalIcon className="h-5 w-5 text-custom-text-200" />
</Popover2>
</div>
)}
</div>
{issue.sub_issues_count > 0 && (
<div className="h-6 w-6 flex justify-center items-center">
<button
className="h-5 w-5 hover:bg-custom-background-90 hover:text-custom-text-100 rounded-sm cursor-pointer"
onClick={() => handleToggleExpand(issue.id)}
> >
<EllipsisHorizontalIcon className="h-5 w-5 text-custom-text-200" /> <Icon iconName="chevron_right" className={`${expanded ? "rotate-90" : ""}`} />
</Popover2> </button>
</div> </div>
)} )}
</div> </div>
)}
{issue.sub_issues_count > 0 && (
<div className="h-6 w-6 flex justify-center items-center">
<button
className="h-5 w-5 hover:bg-custom-background-90 hover:text-custom-text-100 rounded-sm cursor-pointer"
onClick={() => handleToggleExpand(issue.id)}
>
<Icon iconName="chevron_right" className={`${expanded ? "rotate-90" : ""}`} />
</button>
</div>
)}
</div>
<span className="flex items-center px-4 py-2.5 h-full truncate flex-grow"> <span className="flex items-center px-4 py-2.5 h-full truncate flex-grow">
<button <button
type="button" type="button"

View File

@ -82,7 +82,7 @@ export const SpreadsheetView: React.FC<Props> = ({
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId, viewId, workspaceViewId } = router.query; const { workspaceSlug, projectId, cycleId, moduleId, viewId, globalViewId } = router.query;
const type = cycleId ? "cycle" : moduleId ? "module" : "issue"; const type = cycleId ? "cycle" : moduleId ? "module" : "issue";
@ -129,7 +129,17 @@ export const SpreadsheetView: React.FC<Props> = ({
router.pathname.includes(path.path) router.pathname.includes(path.path)
); );
const { params: workspaceViewParams, handleFilters } = useWorkspaceView(); const {
params: workspaceViewParams,
filters: workspaceViewFilters,
handleFilters,
} = useWorkspaceView();
const workspaceViewProperties = workspaceViewFilters.display_properties;
const isWorkspaceView = globalViewId || currentWorkspaceIssuePath;
const currentViewProperties = isWorkspaceView ? workspaceViewProperties : properties;
const { params, displayFilters, setDisplayFilters } = useSpreadsheetIssuesView(); const { params, displayFilters, setDisplayFilters } = useSpreadsheetIssuesView();
@ -143,8 +153,8 @@ export const SpreadsheetView: React.FC<Props> = ({
? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params) ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params)
: viewId : viewId
? VIEW_ISSUES(viewId.toString(), params) ? VIEW_ISSUES(viewId.toString(), params)
: workspaceViewId : globalViewId
? WORKSPACE_VIEW_ISSUES(workspaceSlug.toString(), workspaceViewParams) ? WORKSPACE_VIEW_ISSUES(globalViewId.toString(), workspaceViewParams)
: currentWorkspaceIssuePath : currentWorkspaceIssuePath
? WORKSPACE_VIEW_ISSUES(workspaceSlug.toString(), currentWorkspaceIssuePath?.params) ? WORKSPACE_VIEW_ISSUES(workspaceSlug.toString(), currentWorkspaceIssuePath?.params)
: PROJECT_ISSUES_LIST_WITH_PARAMS(issue.project_detail.id, params); : PROJECT_ISSUES_LIST_WITH_PARAMS(issue.project_detail.id, params);
@ -213,7 +223,7 @@ export const SpreadsheetView: React.FC<Props> = ({
cycleId, cycleId,
moduleId, moduleId,
viewId, viewId,
workspaceViewId, globalViewId,
workspaceViewParams, workspaceViewParams,
currentWorkspaceIssuePath, currentWorkspaceIssuePath,
params, params,
@ -224,8 +234,7 @@ export const SpreadsheetView: React.FC<Props> = ({
const isNotAllowed = userAuth.isGuest || userAuth.isViewer; const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
const handleOrderBy = (order: TIssueOrderByOptions, itemKey: string) => { const handleOrderBy = (order: TIssueOrderByOptions, itemKey: string) => {
if (!workspaceViewId || !currentWorkspaceIssuePath) if (globalViewId) handleFilters("display_filters", { order_by: order });
handleFilters("display_filters", { order_by: order });
else setDisplayFilters({ order_by: order }); else setDisplayFilters({ order_by: order });
setSelectedMenuItem(`${order}_${itemKey}`); setSelectedMenuItem(`${order}_${itemKey}`);
setActiveSortingProperty(order === "-created_at" ? "" : itemKey); setActiveSortingProperty(order === "-created_at" ? "" : itemKey);
@ -240,199 +249,203 @@ export const SpreadsheetView: React.FC<Props> = ({
) => ( ) => (
<div className="relative flex flex-col h-max w-full bg-custom-background-100"> <div className="relative flex flex-col h-max w-full bg-custom-background-100">
<div className="flex items-center min-w-[9rem] px-4 py-2.5 text-sm font-medium z-[1] h-11 w-full sticky top-0 bg-custom-background-90 border border-l-0 border-custom-border-100"> <div className="flex items-center min-w-[9rem] px-4 py-2.5 text-sm font-medium z-[1] h-11 w-full sticky top-0 bg-custom-background-90 border border-l-0 border-custom-border-100">
<CustomMenu {currentWorkspaceIssuePath ? (
customButtonClassName="!w-full" <span>{header}</span>
className="!w-full" ) : (
position="left" <CustomMenu
customButton={ customButtonClassName="!w-full"
<div className="!w-full"
className={`relative group flex items-center justify-between gap-1.5 cursor-pointer text-sm text-custom-text-200 hover:text-custom-text-100 w-full py-3 px-2 ${ position="left"
activeSortingProperty === propertyName ? "bg-custom-background-80" : "" customButton={
}`} <div
> className={`relative group flex items-center justify-between gap-1.5 cursor-pointer text-sm text-custom-text-200 hover:text-custom-text-100 w-full py-3 px-2 ${
{activeSortingProperty === propertyName && ( activeSortingProperty === propertyName ? "bg-custom-background-80" : ""
<div className="absolute top-1 right-1.5">
<Icon
iconName="filter_list"
className="flex items-center justify-center h-3.5 w-3.5 rounded-full bg-custom-primary text-xs text-white"
/>
</div>
)}
{header}
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
</div>
}
width="xl"
>
<CustomMenu.MenuItem
onClick={() => {
handleOrderBy(ascendingOrder, propertyName);
}}
>
<div
className={`group flex gap-1.5 px-1 items-center justify-between ${
selectedMenuItem === `${ascendingOrder}_${propertyName}`
? "text-custom-text-100"
: "text-custom-text-200 hover:text-custom-text-100"
}`}
>
<div className="flex gap-2 items-center">
{propertyName === "assignee" || propertyName === "labels" ? (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 rotate-90 text-xs leading-3"
/>
<Icon iconName="sort" className="absolute right-0 text-sm" />
</span>
<span>A</span>
<Icon iconName="east" className="text-sm" />
<span>Z</span>
</>
) : propertyName === "due_date" ||
propertyName === "created_on" ||
propertyName === "updated_on" ? (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 rotate-90 text-xs leading-3"
/>
<Icon iconName="sort" className="absolute right-0 text-sm" />
</span>
<span>New</span>
<Icon iconName="east" className="text-sm" />
<span>Old</span>
</>
) : (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 rotate-90 text-xs leading-3"
/>
<Icon iconName="sort" className="absolute right-0 text-sm" />
</span>
<span>First</span>
<Icon iconName="east" className="text-sm" />
<span>Last</span>
</>
)}
</div>
<CheckIcon
className={`h-3.5 w-3.5 opacity-0 group-hover:opacity-100 ${
selectedMenuItem === `${ascendingOrder}_${propertyName}` ? "opacity-100" : ""
}`} }`}
/>
</div>
</CustomMenu.MenuItem>
<CustomMenu.MenuItem
className={`mt-0.5 ${
selectedMenuItem === `${descendingOrder}_${propertyName}`
? "bg-custom-background-80"
: ""
}`}
key={propertyName}
onClick={() => {
handleOrderBy(descendingOrder, propertyName);
}}
>
<div
className={`group flex gap-1.5 px-1 items-center justify-between ${
selectedMenuItem === `${descendingOrder}_${propertyName}`
? "text-custom-text-100"
: "text-custom-text-200 hover:text-custom-text-100"
}`}
>
<div className="flex gap-2 items-center">
{propertyName === "assignee" || propertyName === "labels" ? (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 -rotate-90 text-xs leading-3"
/>
<Icon
iconName="sort"
className="absolute rotate-180 transform scale-x-[-1] right-0 text-sm"
/>
</span>
<span>Z</span>
<Icon iconName="east" className="text-sm" />
<span>A</span>
</>
) : propertyName === "due_date" ? (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 -rotate-90 text-xs leading-3"
/>
<Icon
iconName="sort"
className="absolute rotate-180 transform scale-x-[-1] right-0 text-sm"
/>
</span>
<span>Old</span>
<Icon iconName="east" className="text-sm" />
<span>New</span>
</>
) : (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 -rotate-90 text-xs leading-3"
/>
<Icon
iconName="sort"
className="absolute rotate-180 transform scale-x-[-1] right-0 text-sm"
/>
</span>
<span>Last</span>
<Icon iconName="east" className="text-sm" />
<span>First</span>
</>
)}
</div>
<CheckIcon
className={`h-3.5 w-3.5 opacity-0 group-hover:opacity-100 ${
selectedMenuItem === `${descendingOrder}_${propertyName}` ? "opacity-100" : ""
}`}
/>
</div>
</CustomMenu.MenuItem>
{selectedMenuItem &&
selectedMenuItem !== "" &&
displayFilters?.order_by !== "-created_at" &&
selectedMenuItem.includes(propertyName) && (
<CustomMenu.MenuItem
className={`mt-0.5${
selectedMenuItem === `-created_at_${propertyName}`
? "bg-custom-background-80"
: ""
}`}
key={propertyName}
onClick={() => {
handleOrderBy("-created_at", propertyName);
}}
> >
<div className={`group flex gap-1.5 px-1 items-center justify-between `}> {activeSortingProperty === propertyName && (
<div className="flex gap-1.5 items-center"> <div className="absolute top-1 right-1.5">
<span className="relative flex items-center justify-center h-6 w-6"> <Icon
<Icon iconName="ink_eraser" className="text-sm" /> iconName="filter_list"
</span> className="flex items-center justify-center h-3.5 w-3.5 rounded-full bg-custom-primary text-xs text-white"
/>
<span>Clear sorting</span>
</div> </div>
)}
{header}
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
</div>
}
width="xl"
>
<CustomMenu.MenuItem
onClick={() => {
handleOrderBy(ascendingOrder, propertyName);
}}
>
<div
className={`group flex gap-1.5 px-1 items-center justify-between ${
selectedMenuItem === `${ascendingOrder}_${propertyName}`
? "text-custom-text-100"
: "text-custom-text-200 hover:text-custom-text-100"
}`}
>
<div className="flex gap-2 items-center">
{propertyName === "assignee" || propertyName === "labels" ? (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 rotate-90 text-xs leading-3"
/>
<Icon iconName="sort" className="absolute right-0 text-sm" />
</span>
<span>A</span>
<Icon iconName="east" className="text-sm" />
<span>Z</span>
</>
) : propertyName === "due_date" ||
propertyName === "created_on" ||
propertyName === "updated_on" ? (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 rotate-90 text-xs leading-3"
/>
<Icon iconName="sort" className="absolute right-0 text-sm" />
</span>
<span>New</span>
<Icon iconName="east" className="text-sm" />
<span>Old</span>
</>
) : (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 rotate-90 text-xs leading-3"
/>
<Icon iconName="sort" className="absolute right-0 text-sm" />
</span>
<span>First</span>
<Icon iconName="east" className="text-sm" />
<span>Last</span>
</>
)}
</div> </div>
</CustomMenu.MenuItem>
)} <CheckIcon
</CustomMenu> className={`h-3.5 w-3.5 opacity-0 group-hover:opacity-100 ${
selectedMenuItem === `${ascendingOrder}_${propertyName}` ? "opacity-100" : ""
}`}
/>
</div>
</CustomMenu.MenuItem>
<CustomMenu.MenuItem
className={`mt-0.5 ${
selectedMenuItem === `${descendingOrder}_${propertyName}`
? "bg-custom-background-80"
: ""
}`}
key={propertyName}
onClick={() => {
handleOrderBy(descendingOrder, propertyName);
}}
>
<div
className={`group flex gap-1.5 px-1 items-center justify-between ${
selectedMenuItem === `${descendingOrder}_${propertyName}`
? "text-custom-text-100"
: "text-custom-text-200 hover:text-custom-text-100"
}`}
>
<div className="flex gap-2 items-center">
{propertyName === "assignee" || propertyName === "labels" ? (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 -rotate-90 text-xs leading-3"
/>
<Icon
iconName="sort"
className="absolute rotate-180 transform scale-x-[-1] right-0 text-sm"
/>
</span>
<span>Z</span>
<Icon iconName="east" className="text-sm" />
<span>A</span>
</>
) : propertyName === "due_date" ? (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 -rotate-90 text-xs leading-3"
/>
<Icon
iconName="sort"
className="absolute rotate-180 transform scale-x-[-1] right-0 text-sm"
/>
</span>
<span>Old</span>
<Icon iconName="east" className="text-sm" />
<span>New</span>
</>
) : (
<>
<span className="relative flex items-center h-6 w-6">
<Icon
iconName="east"
className="absolute left-0 -rotate-90 text-xs leading-3"
/>
<Icon
iconName="sort"
className="absolute rotate-180 transform scale-x-[-1] right-0 text-sm"
/>
</span>
<span>Last</span>
<Icon iconName="east" className="text-sm" />
<span>First</span>
</>
)}
</div>
<CheckIcon
className={`h-3.5 w-3.5 opacity-0 group-hover:opacity-100 ${
selectedMenuItem === `${descendingOrder}_${propertyName}` ? "opacity-100" : ""
}`}
/>
</div>
</CustomMenu.MenuItem>
{selectedMenuItem &&
selectedMenuItem !== "" &&
displayFilters?.order_by !== "-created_at" &&
selectedMenuItem.includes(propertyName) && (
<CustomMenu.MenuItem
className={`mt-0.5${
selectedMenuItem === `-created_at_${propertyName}`
? "bg-custom-background-80"
: ""
}`}
key={propertyName}
onClick={() => {
handleOrderBy("-created_at", propertyName);
}}
>
<div className={`group flex gap-1.5 px-1 items-center justify-between `}>
<div className="flex gap-1.5 items-center">
<span className="relative flex items-center justify-center h-6 w-6">
<Icon iconName="ink_eraser" className="text-sm" />
</span>
<span>Clear sorting</span>
</div>
</div>
</CustomMenu.MenuItem>
)}
</CustomMenu>
)}
</div> </div>
<div className="h-full min-w-[9rem] w-full"> <div className="h-full min-w-[9rem] w-full">
{spreadsheetIssues.map((issue: IIssue, index) => ( {spreadsheetIssues.map((issue: IIssue, index) => (
@ -442,7 +455,7 @@ export const SpreadsheetView: React.FC<Props> = ({
projectId={issue.project_detail.id} projectId={issue.project_detail.id}
partialUpdateIssue={partialUpdateIssue} partialUpdateIssue={partialUpdateIssue}
expandedIssues={expandedIssues} expandedIssues={expandedIssues}
properties={properties} properties={currentViewProperties}
user={user} user={user}
isNotAllowed={isNotAllowed} isNotAllowed={isNotAllowed}
/> />
@ -492,9 +505,11 @@ export const SpreadsheetView: React.FC<Props> = ({
}`} }`}
> >
<div className="flex items-center text-sm font-medium z-[2] h-11 w-full sticky top-0 bg-custom-background-90 border border-l-0 border-custom-border-100"> <div className="flex items-center text-sm font-medium z-[2] h-11 w-full sticky top-0 bg-custom-background-90 border border-l-0 border-custom-border-100">
<span className="flex items-center px-4 py-2.5 h-full w-24 flex-shrink-0"> {currentViewProperties.key && (
ID <span className="flex items-center px-4 py-2.5 h-full w-24 flex-shrink-0">
</span> ID
</span>
)}
<span className="flex items-center px-4 py-2.5 h-full w-full flex-grow"> <span className="flex items-center px-4 py-2.5 h-full w-full flex-grow">
Issue Issue
</span> </span>
@ -508,7 +523,7 @@ export const SpreadsheetView: React.FC<Props> = ({
expandedIssues={expandedIssues} expandedIssues={expandedIssues}
setExpandedIssues={setExpandedIssues} setExpandedIssues={setExpandedIssues}
setCurrentProjectId={setCurrentProjectId} setCurrentProjectId={setCurrentProjectId}
properties={properties} properties={currentViewProperties}
handleIssueAction={handleIssueAction} handleIssueAction={handleIssueAction}
disableUserActions={disableUserActions} disableUserActions={disableUserActions}
userAuth={userAuth} userAuth={userAuth}
@ -516,69 +531,79 @@ export const SpreadsheetView: React.FC<Props> = ({
))} ))}
</div> </div>
</div> </div>
{renderColumn( {currentViewProperties.state &&
"State", renderColumn(
"state", "State",
SpreadsheetStateColumn, "state",
"state__name", SpreadsheetStateColumn,
"-state__name" "state__name",
)} "-state__name"
{renderColumn( )}
"Priority",
"priority", {currentViewProperties.priority &&
SpreadsheetPriorityColumn, renderColumn(
"priority", "Priority",
"-priority" "priority",
)} SpreadsheetPriorityColumn,
{renderColumn( "priority",
"Assignees", "-priority"
"assignee", )}
SpreadsheetAssigneeColumn, {currentViewProperties.assignee &&
"assignees__first_name", renderColumn(
"-assignees__first_name" "Assignees",
)} "assignee",
{renderColumn( SpreadsheetAssigneeColumn,
"Label", "assignees__first_name",
"labels", "-assignees__first_name"
SpreadsheetLabelColumn, )}
"labels__name", {currentViewProperties.labels &&
"-labels__name" renderColumn(
)} "Label",
{renderColumn( "labels",
"Start Date", SpreadsheetLabelColumn,
"start_date", "labels__name",
SpreadsheetStartDateColumn, "-labels__name"
"-start_date", )}
"start_date" {currentViewProperties.start_date &&
)} renderColumn(
{renderColumn( "Start Date",
"Due Date", "start_date",
"due_date", SpreadsheetStartDateColumn,
SpreadsheetDueDateColumn, "-start_date",
"-target_date", "start_date"
"target_date" )}
)} {currentViewProperties.due_date &&
{renderColumn( renderColumn(
"Estimate", "Due Date",
"estimate", "due_date",
SpreadsheetEstimateColumn, SpreadsheetDueDateColumn,
"estimate_point", "-target_date",
"-estimate_point" "target_date"
)} )}
{renderColumn( {currentViewProperties.estimate &&
"Created On", renderColumn(
"created_on", "Estimate",
SpreadsheetCreatedOnColumn, "estimate",
"-created_at", SpreadsheetEstimateColumn,
"created_at" "estimate_point",
)} "-estimate_point"
{renderColumn( )}
"Updated On", {currentViewProperties.created_on &&
"updated_on", renderColumn(
SpreadsheetUpdatedOnColumn, "Created On",
"-updated_at", "created_on",
"updated_at" SpreadsheetCreatedOnColumn,
)} "-created_at",
"created_at"
)}
{currentViewProperties.updated_on &&
renderColumn(
"Updated On",
"updated_on",
SpreadsheetUpdatedOnColumn,
"-updated_at",
"updated_at"
)}
</> </>
) : ( ) : (
<div className="flex flex-col justify-center items-center h-full w-full"> <div className="flex flex-col justify-center items-center h-full w-full">

View File

@ -30,7 +30,7 @@ const issueViewOptions: { type: TIssueViewOptions; Icon: any }[] = [
export const MyIssuesViewOptions: React.FC = () => { export const MyIssuesViewOptions: React.FC = () => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, workspaceViewId } = router.query; const { workspaceSlug, globalViewId } = router.query;
const { displayFilters, setDisplayFilters, filters, setFilters } = useMyIssuesFilters( const { displayFilters, setDisplayFilters, filters, setFilters } = useMyIssuesFilters(
workspaceSlug?.toString() workspaceSlug?.toString()
@ -42,7 +42,7 @@ export const MyIssuesViewOptions: React.FC = () => {
router.pathname.includes(pathname) router.pathname.includes(pathname)
); );
const showFilters = isWorkspaceViewPath || workspaceViewId; const showFilters = isWorkspaceViewPath || globalViewId;
return ( return (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">

View File

@ -13,6 +13,7 @@ import useProjects from "hooks/use-projects";
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
import { useWorkspaceView } from "hooks/use-workspace-view"; import { useWorkspaceView } from "hooks/use-workspace-view";
import useWorkspaceMembers from "hooks/use-workspace-members"; import useWorkspaceMembers from "hooks/use-workspace-members";
import useToast from "hooks/use-toast";
// components // components
import { WorkspaceViewsNavigation } from "components/workspace/views/workpace-view-navigation"; import { WorkspaceViewsNavigation } from "components/workspace/views/workpace-view-navigation";
import { EmptyState, PrimaryButton } from "components/ui"; import { EmptyState, PrimaryButton } from "components/ui";
@ -31,7 +32,9 @@ import { IIssue, IWorkspaceIssueFilterOptions } from "types";
export const WorkspaceViewIssues = () => { export const WorkspaceViewIssues = () => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, viewId } = router.query; const { workspaceSlug, globalViewId } = router.query;
const { setToastAlert } = useToast();
const { memberRole } = useProjectMyMembership(); const { memberRole } = useProjectMyMembership();
const { user } = useUser(); const { user } = useUser();
@ -111,7 +114,9 @@ export const WorkspaceViewIssues = () => {
const nullFilters = const nullFilters =
filters.filters && filters.filters &&
Object.keys(filters.filters).filter( Object.keys(filters.filters).filter(
(key) => filters.filters[key as keyof IWorkspaceIssueFilterOptions] === null (key) =>
filters.filters[key as keyof IWorkspaceIssueFilterOptions] === null ||
(filters.filters[key as keyof IWorkspaceIssueFilterOptions]?.length ?? 0) <= 0
); );
const areFiltersApplied = const areFiltersApplied =
@ -189,16 +194,22 @@ export const WorkspaceViewIssues = () => {
/> />
<PrimaryButton <PrimaryButton
onClick={() => { onClick={() => {
if (viewId) handleFilters("filters", filters.filters, true); if (globalViewId) {
else handleFilters("filters", filters.filters, true);
setToastAlert({
title: "View updated",
message: "Your view has been updated",
type: "success",
});
} else
setCreateViewModal({ setCreateViewModal({
query: filters.filters, query: filters.filters,
}); });
}} }}
className="flex items-center gap-2 text-sm" className="flex items-center gap-2 text-sm"
> >
{!viewId && <PlusIcon className="h-4 w-4" />} {!globalViewId && <PlusIcon className="h-4 w-4" />}
{viewId ? "Update" : "Save"} view {globalViewId ? "Update" : "Save"} view
</PrimaryButton> </PrimaryButton>
</div> </div>
{<div className="mt-3 border-t border-custom-border-200" />} {<div className="mt-3 border-t border-custom-border-200" />}

View File

@ -31,7 +31,7 @@ import { IIssue, IWorkspaceIssueFilterOptions } from "types";
export const WorkspaceAllIssue = () => { export const WorkspaceAllIssue = () => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, viewId } = router.query; const { workspaceSlug, globalViewId } = router.query;
const [createViewModal, setCreateViewModal] = useState<any>(null); const [createViewModal, setCreateViewModal] = useState<any>(null);
@ -128,7 +128,9 @@ export const WorkspaceAllIssue = () => {
const nullFilters = const nullFilters =
filters.filters && filters.filters &&
Object.keys(filters.filters).filter( Object.keys(filters.filters).filter(
(key) => filters.filters[key as keyof IWorkspaceIssueFilterOptions] === null (key) =>
filters.filters[key as keyof IWorkspaceIssueFilterOptions] === null ||
(filters.filters[key as keyof IWorkspaceIssueFilterOptions]?.length ?? 0) <= 0
); );
const areFiltersApplied = const areFiltersApplied =
@ -203,7 +205,7 @@ export const WorkspaceAllIssue = () => {
/> />
<PrimaryButton <PrimaryButton
onClick={() => { onClick={() => {
if (viewId) handleFilters("filters", filters.filters, true); if (globalViewId) handleFilters("filters", filters.filters, true);
else else
setCreateViewModal({ setCreateViewModal({
query: filters.filters, query: filters.filters,
@ -211,8 +213,8 @@ export const WorkspaceAllIssue = () => {
}} }}
className="flex items-center gap-2 text-sm" className="flex items-center gap-2 text-sm"
> >
{!viewId && <PlusIcon className="h-4 w-4" />} {!globalViewId && <PlusIcon className="h-4 w-4" />}
{viewId ? "Update" : "Save"} view {globalViewId ? "Update" : "Save"} view
</PrimaryButton> </PrimaryButton>
</div> </div>
{<div className="mt-3 border-t border-custom-border-200" />} {<div className="mt-3 border-t border-custom-border-200" />}

View File

@ -50,13 +50,6 @@ export const WorkspaceAssignedIssue = () => {
sub_issue: false, sub_issue: false,
}; };
// const { data: viewDetails, error } = useSWR(
// workspaceSlug && workspaceViewId ? WORKSPACE_VIEW_DETAILS(workspaceViewId.toString()) : null,
// workspaceSlug && workspaceViewId
// ? () => workspaceService.getViewDetails(workspaceSlug.toString(), workspaceViewId.toString())
// : null
// );
const { data: viewIssues, mutate: mutateIssues } = useSWR( const { data: viewIssues, mutate: mutateIssues } = useSWR(
workspaceSlug ? WORKSPACE_VIEW_ISSUES(workspaceSlug.toString(), params) : null, workspaceSlug ? WORKSPACE_VIEW_ISSUES(workspaceSlug.toString(), params) : null,
workspaceSlug ? () => workspaceService.getViewIssues(workspaceSlug.toString(), params) : null workspaceSlug ? () => workspaceService.getViewIssues(workspaceSlug.toString(), params) : null

View File

@ -30,13 +30,13 @@ const issueViewOptions: { type: TIssueViewOptions; Icon: any }[] = [
export const WorkspaceIssuesViewOptions: React.FC = () => { export const WorkspaceIssuesViewOptions: React.FC = () => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, workspaceViewId } = router.query; const { workspaceSlug, globalViewId } = router.query;
const { filters, handleFilters } = useWorkspaceView(); const { filters, handleFilters } = useWorkspaceView();
const isWorkspaceViewPath = router.pathname.includes("workspace-views/all-issues"); const isWorkspaceViewPath = router.pathname.includes("workspace-views/all-issues");
const showFilters = isWorkspaceViewPath || workspaceViewId; const showFilters = isWorkspaceViewPath || globalViewId;
return ( return (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">

View File

@ -91,7 +91,7 @@ export const SingleViewItem: React.FC<Props> = ({
const viewRedirectionUrl = const viewRedirectionUrl =
viewType === "project" viewType === "project"
? `/${workspaceSlug}/projects/${projectId}/views/${view.id}` ? `/${workspaceSlug}/projects/${projectId}/views/${view.id}`
: `/${workspaceSlug}/workspace-views/issues?viewId=${view.id}`; : `/${workspaceSlug}/workspace-views/issues?globalViewId=${view.id}`;
return ( return (
<div className="group hover:bg-custom-background-90 border-b border-custom-border-200"> <div className="group hover:bg-custom-background-90 border-b border-custom-border-200">

View File

@ -17,7 +17,7 @@ type Props = {
export const WorkspaceViewsNavigation: React.FC<Props> = ({ handleAddView }) => { export const WorkspaceViewsNavigation: React.FC<Props> = ({ handleAddView }) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, viewId } = router.query; const { workspaceSlug, globalViewId } = router.query;
const { data: workspaceViews } = useSWR( const { data: workspaceViews } = useSWR(
workspaceSlug ? WORKSPACE_VIEWS_LIST(workspaceSlug.toString()) : null, workspaceSlug ? WORKSPACE_VIEWS_LIST(workspaceSlug.toString()) : null,
@ -28,7 +28,7 @@ export const WorkspaceViewsNavigation: React.FC<Props> = ({ handleAddView }) =>
React.useEffect(() => { React.useEffect(() => {
const activeTabElement = document.getElementById("active-tab-global-view"); const activeTabElement = document.getElementById("active-tab-global-view");
if (activeTabElement) activeTabElement.scrollIntoView({ behavior: "smooth", inline: "center" }); if (activeTabElement) activeTabElement.scrollIntoView({ behavior: "smooth", inline: "center" });
}, [viewId, workspaceViews]); }, [globalViewId, workspaceViews]);
const tabsList = [ const tabsList = [
{ {
@ -80,13 +80,13 @@ export const WorkspaceViewsNavigation: React.FC<Props> = ({ handleAddView }) =>
workspaceViews?.map((view) => ( workspaceViews?.map((view) => (
<button <button
className={`border-b-2 min-w-min p-3 text-sm font-medium outline-none whitespace-nowrap flex-shrink-0 ${ className={`border-b-2 min-w-min p-3 text-sm font-medium outline-none whitespace-nowrap flex-shrink-0 ${
view.id === viewId view.id === globalViewId
? "border-custom-primary-100 text-custom-primary-100" ? "border-custom-primary-100 text-custom-primary-100"
: "border-transparent hover:border-custom-border-200 hover:text-custom-text-400" : "border-transparent hover:border-custom-border-200 hover:text-custom-text-400"
}`} }`}
id={view.id === viewId ? `active-tab-global-view` : ``} id={view.id === globalViewId ? `active-tab-global-view` : ``}
onClick={() => onClick={() =>
router.replace(`/${workspaceSlug}/workspace-views/issues?viewId=${view.id}`) router.replace(`/${workspaceSlug}/workspace-views/issues?globalViewId=${view.id}`)
} }
> >
{view.name} {view.name}

View File

@ -162,13 +162,11 @@ export const PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS = (projectId: string, params?
export const WORKSPACE_VIEWS_LIST = (workspaceSlug: string) => export const WORKSPACE_VIEWS_LIST = (workspaceSlug: string) =>
`WORKSPACE_VIEWS_LIST_${workspaceSlug.toUpperCase()}`; `WORKSPACE_VIEWS_LIST_${workspaceSlug.toUpperCase()}`;
export const WORKSPACE_VIEW_DETAILS = (workspaceViewId: string) => export const WORKSPACE_VIEW_DETAILS = (globalViewId: string) =>
`WORKSPACE_VIEW_DETAILS_${workspaceViewId.toUpperCase()}`; `WORKSPACE_VIEW_DETAILS_${globalViewId.toUpperCase()}`;
export const WORKSPACE_VIEW_ISSUES = (workspaceViewId: string, params: any) => { export const WORKSPACE_VIEW_ISSUES = (globalViewId: string, params: any) => {
if (!params) return `WORKSPACE_VIEW_ISSUES_${workspaceViewId.toUpperCase()}`; if (!params) return `WORKSPACE_VIEW_ISSUES_${globalViewId.toUpperCase()}`;
return `WORKSPACE_VIEW_ISSUES_${workspaceViewId.toUpperCase()}_${paramsToKey( return `WORKSPACE_VIEW_ISSUES_${globalViewId.toUpperCase()}_${paramsToKey(params).toUpperCase()}`;
params
).toUpperCase()}`;
}; };
export const PROJECT_ISSUES_DETAILS = (issueId: string) => export const PROJECT_ISSUES_DETAILS = (issueId: string) =>

View File

@ -67,19 +67,19 @@ export const initialState: IWorkspaceGlobalViewProps = {
const saveViewFilters = async ( const saveViewFilters = async (
workspaceSlug: string, workspaceSlug: string,
viewId: string, globalViewId: string,
state: IWorkspaceGlobalViewProps state: IWorkspaceGlobalViewProps
) => { ) => {
await workspaceService.updateView(workspaceSlug, viewId, { await workspaceService.updateView(workspaceSlug, globalViewId, {
query_data: state, query_data: state,
}); });
}; };
export const WorkspaceViewProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { export const WorkspaceViewProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, viewId } = router.query as { const { workspaceSlug, globalViewId } = router.query as {
workspaceSlug: string; workspaceSlug: string;
viewId: string; globalViewId: string;
}; };
const [filters, setFilters] = useState<IWorkspaceGlobalViewProps>(initialState); const [filters, setFilters] = useState<IWorkspaceGlobalViewProps>(initialState);
@ -98,7 +98,7 @@ export const WorkspaceViewProvider: React.FC<{ children: React.ReactNode }> = ({
}; };
setFilters(() => updatedFilterPayload); setFilters(() => updatedFilterPayload);
if (saveFiltersToServer) saveViewFilters(workspaceSlug, viewId, updatedFilterPayload); if (saveFiltersToServer) saveViewFilters(workspaceSlug, globalViewId, updatedFilterPayload);
}; };
const computedFilter = (filters: any) => { const computedFilter = (filters: any) => {
@ -151,9 +151,9 @@ export const WorkspaceViewProvider: React.FC<{ children: React.ReactNode }> = ({
}; };
const { data: view, isLoading: viewLoading } = useSWR( const { data: view, isLoading: viewLoading } = useSWR(
workspaceSlug && viewId ? WORKSPACE_VIEW_DETAILS(viewId.toString()) : null, workspaceSlug && globalViewId ? WORKSPACE_VIEW_DETAILS(globalViewId.toString()) : null,
workspaceSlug && viewId workspaceSlug && globalViewId
? () => workspaceService.getViewDetails(workspaceSlug.toString(), viewId.toString()) ? () => workspaceService.getViewDetails(workspaceSlug.toString(), globalViewId.toString())
: null : null
); );
@ -162,10 +162,10 @@ export const WorkspaceViewProvider: React.FC<{ children: React.ReactNode }> = ({
mutate: mutateViewIssues, mutate: mutateViewIssues,
isLoading: viewIssueLoading, isLoading: viewIssueLoading,
} = useSWR( } = useSWR(
workspaceSlug && view && viewId && filters workspaceSlug && view && globalViewId && filters
? WORKSPACE_VIEW_ISSUES(viewId.toString(), params) ? WORKSPACE_VIEW_ISSUES(globalViewId.toString(), params)
: null, : null,
workspaceSlug && view && viewId workspaceSlug && view && globalViewId
? () => ? () =>
workspaceService.getViewIssues( workspaceService.getViewIssues(
workspaceSlug.toString(), workspaceSlug.toString(),

View File

@ -18,7 +18,6 @@ const WorkspaceViewAllIssue = () => (
} }
right={ right={
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{/* <MyIssuesViewOptions /> */}
<WorkspaceIssuesViewOptions /> <WorkspaceIssuesViewOptions />
<PrimaryButton <PrimaryButton

View File

@ -103,5 +103,5 @@ export interface IWorkspaceViewProps {
export interface IWorkspaceGlobalViewProps { export interface IWorkspaceGlobalViewProps {
filters: IWorkspaceIssueFilterOptions; filters: IWorkspaceIssueFilterOptions;
display_filters: IWorkspaceIssueDisplayFilterOptions | undefined; display_filters: IWorkspaceIssueDisplayFilterOptions | undefined;
display_properties: Properties | undefined; display_properties: Properties;
} }