refactor: view props structure (#2159)

* chore: update view_props types

* refactor: view props structure
This commit is contained in:
Aaryan Khandelwal 2023-09-12 22:27:15 +05:30 committed by GitHub
parent cdb888c23e
commit 8e9a4dca78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 765 additions and 1146 deletions

View file

@ -58,16 +58,8 @@ export const IssuesFilterView: React.FC = () => {
const isArchivedIssues = router.pathname.includes("archived-issues");
const {
issueView,
setIssueView,
groupByProperty,
setGroupByProperty,
orderBy,
setOrderBy,
showEmptyGroups,
showSubIssues,
setShowSubIssues,
setShowEmptyGroups,
displayFilters,
setDisplayFilters,
filters,
setFilters,
resetFilterToDefault,
@ -96,11 +88,11 @@ export const IssuesFilterView: React.FC = () => {
<button
type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none hover:bg-custom-sidebar-background-80 duration-300 ${
issueView === option.type
displayFilters.layout === option.type
? "bg-custom-sidebar-background-80"
: "text-custom-sidebar-text-200"
}`}
onClick={() => setIssueView(option.type)}
onClick={() => setDisplayFilters({ layout: option.type })}
>
<option.Icon
sx={{
@ -174,28 +166,30 @@ export const IssuesFilterView: React.FC = () => {
<Popover.Panel className="absolute right-0 z-30 mt-1 w-screen max-w-xs transform rounded-lg border border-custom-border-200 bg-custom-background-90 p-3 shadow-lg">
<div className="relative divide-y-2 divide-custom-border-200">
<div className="space-y-4 pb-3 text-xs">
{issueView !== "calendar" &&
issueView !== "spreadsheet" &&
issueView !== "gantt_chart" && (
{displayFilters.layout !== "calendar" &&
displayFilters.layout !== "spreadsheet" &&
displayFilters.layout !== "gantt_chart" && (
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Group by</h4>
<div className="w-28">
<CustomMenu
label={
GROUP_BY_OPTIONS.find((option) => option.key === groupByProperty)
?.name ?? "Select"
GROUP_BY_OPTIONS.find(
(option) => option.key === displayFilters.group_by
)?.name ?? "Select"
}
className="!w-full"
buttonClassName="w-full"
>
{GROUP_BY_OPTIONS.map((option) => {
if (issueView === "kanban" && option.key === null) return null;
if (displayFilters.layout === "kanban" && option.key === null)
return null;
if (option.key === "project") return null;
return (
<CustomMenu.MenuItem
key={option.key}
onClick={() => setGroupByProperty(option.key)}
onClick={() => setDisplayFilters({ group_by: option.key })}
>
{option.name}
</CustomMenu.MenuItem>
@ -205,41 +199,45 @@ export const IssuesFilterView: React.FC = () => {
</div>
</div>
)}
{issueView !== "calendar" && issueView !== "spreadsheet" && (
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Order by</h4>
<div className="w-28">
<CustomMenu
label={
ORDER_BY_OPTIONS.find((option) => option.key === orderBy)?.name ??
"Select"
}
className="!w-full"
buttonClassName="w-full"
>
{ORDER_BY_OPTIONS.map((option) =>
groupByProperty === "priority" && option.key === "priority" ? null : (
<CustomMenu.MenuItem
key={option.key}
onClick={() => {
setOrderBy(option.key);
}}
>
{option.name}
</CustomMenu.MenuItem>
)
)}
</CustomMenu>
{displayFilters.layout !== "calendar" &&
displayFilters.layout !== "spreadsheet" && (
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Order by</h4>
<div className="w-28">
<CustomMenu
label={
ORDER_BY_OPTIONS.find(
(option) => option.key === displayFilters.order_by
)?.name ?? "Select"
}
className="!w-full"
buttonClassName="w-full"
>
{ORDER_BY_OPTIONS.map((option) =>
displayFilters.group_by === "priority" &&
option.key === "priority" ? null : (
<CustomMenu.MenuItem
key={option.key}
onClick={() => {
setDisplayFilters({ order_by: option.key });
}}
>
{option.name}
</CustomMenu.MenuItem>
)
)}
</CustomMenu>
</div>
</div>
</div>
)}
)}
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Issue type</h4>
<div className="w-28">
<CustomMenu
label={
FILTER_ISSUE_OPTIONS.find((option) => option.key === filters.type)
?.name ?? "Select"
FILTER_ISSUE_OPTIONS.find(
(option) => option.key === displayFilters.type
)?.name ?? "Select"
}
className="!w-full"
buttonClassName="w-full"
@ -248,7 +246,7 @@ export const IssuesFilterView: React.FC = () => {
<CustomMenu.MenuItem
key={option.key}
onClick={() =>
setFilters({
setDisplayFilters({
type: option.key,
})
}
@ -260,33 +258,40 @@ export const IssuesFilterView: React.FC = () => {
</div>
</div>
{issueView !== "calendar" && issueView !== "spreadsheet" && (
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Show sub-issues</h4>
<div className="w-28">
<ToggleSwitch
value={showSubIssues}
onChange={() => setShowSubIssues(!showSubIssues)}
/>
</div>
</div>
)}
{issueView !== "calendar" &&
issueView !== "spreadsheet" &&
issueView !== "gantt_chart" && (
{displayFilters.layout !== "calendar" &&
displayFilters.layout !== "spreadsheet" && (
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Show empty states</h4>
<h4 className="text-custom-text-200">Show sub-issues</h4>
<div className="w-28">
<ToggleSwitch
value={showEmptyGroups}
onChange={() => setShowEmptyGroups(!showEmptyGroups)}
value={displayFilters.sub_issue ?? true}
onChange={() =>
setDisplayFilters({ sub_issue: !displayFilters.sub_issue })
}
/>
</div>
</div>
)}
{issueView !== "calendar" &&
issueView !== "spreadsheet" &&
issueView !== "gantt_chart" && (
{displayFilters.layout !== "calendar" &&
displayFilters.layout !== "spreadsheet" &&
displayFilters.layout !== "gantt_chart" && (
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Show empty states</h4>
<div className="w-28">
<ToggleSwitch
value={displayFilters.show_empty_groups ?? true}
onChange={() =>
setDisplayFilters({
show_empty_groups: !displayFilters.show_empty_groups,
})
}
/>
</div>
</div>
)}
{displayFilters.layout !== "calendar" &&
displayFilters.layout !== "spreadsheet" &&
displayFilters.layout !== "gantt_chart" && (
<div className="relative flex justify-end gap-x-3">
<button type="button" onClick={() => resetFilterToDefault()}>
Reset to default
@ -302,7 +307,7 @@ export const IssuesFilterView: React.FC = () => {
)}
</div>
{issueView !== "gantt_chart" && (
{displayFilters.layout !== "gantt_chart" && (
<div className="space-y-2 py-3">
<h4 className="text-sm text-custom-text-200">Display Properties</h4>
<div className="flex flex-wrap items-center gap-2 text-custom-text-200">
@ -310,7 +315,7 @@ export const IssuesFilterView: React.FC = () => {
if (key === "estimate" && !isEstimateActive) return null;
if (
issueView === "spreadsheet" &&
displayFilters.layout === "spreadsheet" &&
(key === "attachment_count" ||
key === "link" ||
key === "sub_issue_count")
@ -318,7 +323,7 @@ export const IssuesFilterView: React.FC = () => {
return null;
if (
issueView !== "spreadsheet" &&
displayFilters.layout !== "spreadsheet" &&
(key === "created_on" || key === "updated_on")
)
return null;

View file

@ -58,7 +58,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen, user
);
const { setToastAlert } = useToast();
const { issueView, params } = useIssuesView();
const { displayFilters, params } = useIssuesView();
const { params: calendarParams } = useCalendarIssuesView();
const { order_by, group_by, ...viewGanttParams } = params;
@ -126,8 +126,8 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen, user
message: "Issues deleted successfully!",
});
if (issueView === "calendar") mutate(calendarFetchKey);
else if (issueView === "gantt_chart") mutate(ganttFetchKey);
if (displayFilters.layout === "calendar") mutate(calendarFetchKey);
else if (displayFilters.layout === "gantt_chart") mutate(ganttFetchKey);
else {
if (cycleId) {
mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params));

View file

@ -80,7 +80,7 @@ export const AllViews: React.FC<Props> = ({
const { user } = useUser();
const { memberRole } = useProjectMyMembership();
const { groupedIssues, isEmpty, issueView } = viewProps;
const { groupedIssues, isEmpty, displayFilters } = viewProps;
const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
@ -117,11 +117,11 @@ export const AllViews: React.FC<Props> = ({
</StrictModeDroppable>
{groupedIssues ? (
!isEmpty ||
issueView === "kanban" ||
issueView === "calendar" ||
issueView === "gantt_chart" ? (
displayFilters?.layout === "kanban" ||
displayFilters?.layout === "calendar" ||
displayFilters?.layout === "gantt_chart" ? (
<>
{issueView === "list" ? (
{displayFilters?.layout === "list" ? (
<AllLists
states={states}
addIssueToGroup={addIssueToGroup}
@ -134,7 +134,7 @@ export const AllViews: React.FC<Props> = ({
userAuth={memberRole}
viewProps={viewProps}
/>
) : issueView === "kanban" ? (
) : displayFilters?.layout === "kanban" ? (
<AllBoards
addIssueToGroup={addIssueToGroup}
disableUserActions={disableUserActions}
@ -149,7 +149,7 @@ export const AllViews: React.FC<Props> = ({
userAuth={memberRole}
viewProps={viewProps}
/>
) : issueView === "calendar" ? (
) : displayFilters?.layout === "calendar" ? (
<CalendarView
handleIssueAction={handleIssueAction}
addIssueToDate={addIssueToDate}
@ -157,7 +157,7 @@ export const AllViews: React.FC<Props> = ({
user={user}
userAuth={memberRole}
/>
) : issueView === "spreadsheet" ? (
) : displayFilters?.layout === "spreadsheet" ? (
<SpreadsheetView
handleIssueAction={handleIssueAction}
openIssuesListModal={cycleId || moduleId ? openIssuesListModal : null}
@ -166,7 +166,7 @@ export const AllViews: React.FC<Props> = ({
userAuth={memberRole}
/>
) : (
issueView === "gantt_chart" && <GanttChartView />
displayFilters?.layout === "gantt_chart" && <GanttChartView />
)}
</>
) : router.pathname.includes("archived-issues") ? (

View file

@ -36,7 +36,7 @@ export const AllBoards: React.FC<Props> = ({
userAuth,
viewProps,
}) => {
const { groupByProperty: selectedGroup, groupedIssues, showEmptyGroups } = viewProps;
const { displayFilters, groupedIssues } = viewProps;
return (
<>
@ -44,9 +44,12 @@ export const AllBoards: React.FC<Props> = ({
<div className="horizontal-scroll-enable flex h-full gap-x-4 p-8">
{Object.keys(groupedIssues).map((singleGroup, index) => {
const currentState =
selectedGroup === "state" ? states?.find((s) => s.id === singleGroup) : null;
displayFilters?.group_by === "state"
? states?.find((s) => s.id === singleGroup)
: null;
if (!showEmptyGroups && groupedIssues[singleGroup].length === 0) return null;
if (!displayFilters?.show_empty_groups && groupedIssues[singleGroup].length === 0)
return null;
return (
<SingleBoard
@ -67,13 +70,15 @@ export const AllBoards: React.FC<Props> = ({
/>
);
})}
{!showEmptyGroups && (
{!displayFilters?.show_empty_groups && (
<div className="h-full w-96 flex-shrink-0 space-y-2 p-1">
<h2 className="text-lg font-semibold">Hidden groups</h2>
<div className="space-y-3">
{Object.keys(groupedIssues).map((singleGroup, index) => {
const currentState =
selectedGroup === "state" ? states?.find((s) => s.id === singleGroup) : null;
displayFilters?.group_by === "state"
? states?.find((s) => s.id === singleGroup)
: null;
if (groupedIssues[singleGroup].length === 0)
return (
@ -91,7 +96,7 @@ export const AllBoards: React.FC<Props> = ({
/>
)}
<h4 className="text-sm capitalize">
{selectedGroup === "state"
{displayFilters?.group_by === "state"
? addSpaceIfCamelCase(currentState?.name ?? "")
: addSpaceIfCamelCase(singleGroup)}
</h4>

View file

@ -48,22 +48,28 @@ export const BoardHeader: React.FC<Props> = ({
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { groupedIssues, groupByProperty: selectedGroup } = viewProps;
const { displayFilters, groupedIssues } = viewProps;
console.log("dF", displayFilters);
const { data: issueLabels } = useSWR(
workspaceSlug && projectId && selectedGroup === "labels"
workspaceSlug && projectId && displayFilters?.group_by === "labels"
? PROJECT_ISSUE_LABELS(projectId.toString())
: null,
workspaceSlug && projectId && selectedGroup === "labels"
workspaceSlug && projectId && displayFilters?.group_by === "labels"
? () => issuesService.getIssueLabels(workspaceSlug.toString(), projectId.toString())
: null
);
const { data: members } = useSWR(
workspaceSlug && projectId && (selectedGroup === "created_by" || selectedGroup === "assignees")
workspaceSlug &&
projectId &&
(displayFilters?.group_by === "created_by" || displayFilters?.group_by === "assignees")
? PROJECT_MEMBERS(projectId.toString())
: null,
workspaceSlug && projectId && (selectedGroup === "created_by" || selectedGroup === "assignees")
workspaceSlug &&
projectId &&
(displayFilters?.group_by === "created_by" || displayFilters?.group_by === "assignees")
? () => projectService.projectMembers(workspaceSlug.toString(), projectId.toString())
: null
);
@ -73,7 +79,7 @@ export const BoardHeader: React.FC<Props> = ({
const getGroupTitle = () => {
let title = addSpaceIfCamelCase(groupTitle);
switch (selectedGroup) {
switch (displayFilters?.group_by) {
case "state":
title = addSpaceIfCamelCase(currentState?.name ?? "");
break;
@ -97,7 +103,7 @@ export const BoardHeader: React.FC<Props> = ({
const getGroupIcon = () => {
let icon;
switch (selectedGroup) {
switch (displayFilters?.group_by) {
case "state":
icon = currentState && (
<StateGroupIcon
@ -167,7 +173,7 @@ export const BoardHeader: React.FC<Props> = ({
<span className="flex items-center">{getGroupIcon()}</span>
<h2
className={`text-lg font-semibold truncate ${
selectedGroup === "created_by" ? "" : "capitalize"
displayFilters?.group_by === "created_by" ? "" : "capitalize"
}`}
style={{
writingMode: isCollapsed ? "horizontal-tb" : "vertical-rl",
@ -198,7 +204,7 @@ export const BoardHeader: React.FC<Props> = ({
<Icon iconName="open_in_full" className="text-base font-medium text-custom-text-900" />
)}
</button>
{!disableAddIssue && !disableUserActions && selectedGroup !== "created_by" && (
{!disableAddIssue && !disableUserActions && displayFilters?.group_by !== "created_by" && (
<button
type="button"
className="grid h-7 w-7 place-items-center rounded p-1 text-custom-text-200 outline-none duration-300 hover:bg-custom-background-80"

View file

@ -50,7 +50,7 @@ export const SingleBoard: React.FC<Props> = ({
// collapse/expand
const [isCollapsed, setIsCollapsed] = useState(true);
const { groupedIssues, groupByProperty: selectedGroup, orderBy, properties } = viewProps;
const { displayFilters, groupedIssues, properties } = viewProps;
const router = useRouter();
const { cycleId, moduleId } = router.query;
@ -80,14 +80,14 @@ export const SingleBoard: React.FC<Props> = ({
{(provided, snapshot) => (
<div
className={`relative h-full ${
orderBy !== "sort_order" && snapshot.isDraggingOver
displayFilters?.order_by !== "sort_order" && snapshot.isDraggingOver
? "bg-custom-background-100/20"
: ""
} ${!isCollapsed ? "hidden" : "flex flex-col"}`}
ref={provided.innerRef}
{...provided.droppableProps}
>
{orderBy !== "sort_order" && (
{displayFilters?.order_by !== "sort_order" && (
<>
<div
className={`absolute ${
@ -101,7 +101,11 @@ export const SingleBoard: React.FC<Props> = ({
>
This board is ordered by{" "}
{replaceUnderscoreIfSnakeCase(
orderBy ? (orderBy[0] === "-" ? orderBy.slice(1) : orderBy) : "created_at"
displayFilters?.order_by
? displayFilters?.order_by[0] === "-"
? displayFilters?.order_by.slice(1)
: displayFilters?.order_by
: "created_at"
)}
</div>
</>
@ -145,13 +149,13 @@ export const SingleBoard: React.FC<Props> = ({
))}
<span
style={{
display: orderBy === "sort_order" ? "inline" : "none",
display: displayFilters?.order_by === "sort_order" ? "inline" : "none",
}}
>
{provided.placeholder}
</span>
</div>
{selectedGroup !== "created_by" && (
{displayFilters?.group_by !== "created_by" && (
<div>
{type === "issue"
? !disableAddIssueOption && (

View file

@ -93,7 +93,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
const actionSectionRef = useRef<HTMLDivElement | null>(null);
const { groupByProperty: selectedGroup, orderBy, properties, mutateIssues } = viewProps;
const { displayFilters, properties, mutateIssues } = viewProps;
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
@ -131,9 +131,9 @@ export const SingleBoardIssue: React.FC<Props> = ({
handleIssuesMutation(
formData,
groupTitle ?? "",
selectedGroup,
displayFilters?.group_by ?? null,
index,
orderBy,
displayFilters?.order_by ?? "-created_at",
prevData
),
false
@ -149,24 +149,14 @@ export const SingleBoardIssue: React.FC<Props> = ({
if (moduleId) mutate(MODULE_DETAILS(moduleId as string));
});
},
[
workspaceSlug,
cycleId,
moduleId,
groupTitle,
index,
selectedGroup,
mutateIssues,
orderBy,
user,
]
[displayFilters, workspaceSlug, cycleId, moduleId, groupTitle, index, mutateIssues, user]
);
const getStyle = (
style: DraggingStyle | NotDraggingStyle | undefined,
snapshot: DraggableStateSnapshot
) => {
if (orderBy === "sort_order") return style;
if (displayFilters?.order_by === "sort_order") return style;
if (!snapshot.isDragging) return {};
if (!snapshot.isDropAnimating) return style;

View file

@ -60,7 +60,7 @@ export const CalendarView: React.FC<Props> = ({
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
const { calendarIssues, params, setCalendarDateRange } = useCalendarIssuesView();
const { calendarIssues, params, setDisplayFilters } = useCalendarIssuesView();
const totalDate = eachDayOfInterval({
start: calendarDates.startDate,
@ -152,18 +152,20 @@ export const CalendarView: React.FC<Props> = ({
endDate,
});
setCalendarDateRange(
`${renderDateFormat(startDate)};after,${renderDateFormat(endDate)};before`
);
setDisplayFilters({
calendar_date_range: `${renderDateFormat(startDate)};after,${renderDateFormat(
endDate
)};before`,
});
};
useEffect(() => {
setCalendarDateRange(
`${renderDateFormat(startOfWeek(currentDate))};after,${renderDateFormat(
setDisplayFilters({
calendar_date_range: `${renderDateFormat(startOfWeek(currentDate))};after,${renderDateFormat(
lastDayOfWeek(currentDate)
)};before`
);
}, [currentDate]);
)};before`,
});
}, [currentDate, setDisplayFilters]);
const isNotAllowed = userAuth.isGuest || userAuth.isViewer || disableUserActions;

View file

@ -77,18 +77,8 @@ export const IssuesView: React.FC<Props> = ({
const { setToastAlert } = useToast();
const {
groupedByIssues,
mutateIssues,
issueView,
groupByProperty: selectedGroup,
orderBy,
filters,
isEmpty,
setFilters,
params,
showEmptyGroups,
} = useIssuesView();
const { groupedByIssues, mutateIssues, displayFilters, filters, isEmpty, setFilters, params } =
useIssuesView();
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
const { data: stateGroups } = useSWR(
@ -129,7 +119,7 @@ export const IssuesView: React.FC<Props> = ({
if (destination.droppableId === "trashBox") {
handleDeleteIssue(draggedItem);
} else {
if (orderBy === "sort_order") {
if (displayFilters.order_by === "sort_order") {
let newSortOrder = draggedItem.sort_order;
const destinationGroupArray = groupedByIssues[destination.droppableId];
@ -177,16 +167,19 @@ export const IssuesView: React.FC<Props> = ({
const destinationGroup = destination.droppableId; // destination group id
if (orderBy === "sort_order" || source.droppableId !== destination.droppableId) {
if (
displayFilters.order_by === "sort_order" ||
source.droppableId !== destination.droppableId
) {
// different group/column;
// source.droppableId !== destination.droppableId -> even if order by is not sort_order,
// if the issue is moved to a different group, then we will change the group of the
// dragged item(or issue)
if (selectedGroup === "priority")
if (displayFilters.group_by === "priority")
draggedItem.priority = destinationGroup as TIssuePriorities;
else if (selectedGroup === "state") {
else if (displayFilters.group_by === "state") {
draggedItem.state = destinationGroup;
draggedItem.state_detail = states?.find((s) => s.id === destinationGroup) as IState;
}
@ -213,8 +206,14 @@ export const IssuesView: React.FC<Props> = ({
return {
...prevData,
[sourceGroup]: orderArrayBy(sourceGroupArray, orderBy),
[destinationGroup]: orderArrayBy(destinationGroupArray, orderBy),
[sourceGroup]: orderArrayBy(
sourceGroupArray,
displayFilters.order_by ?? "-created_at"
),
[destinationGroup]: orderArrayBy(
destinationGroupArray,
displayFilters.order_by ?? "-created_at"
),
};
},
false
@ -267,13 +266,13 @@ export const IssuesView: React.FC<Props> = ({
}
},
[
displayFilters.group_by,
displayFilters.order_by,
workspaceSlug,
cycleId,
moduleId,
groupedByIssues,
projectId,
selectedGroup,
orderBy,
handleDeleteIssue,
params,
states,
@ -287,19 +286,19 @@ export const IssuesView: React.FC<Props> = ({
let preloadedValue: string | string[] = groupTitle;
if (selectedGroup === "labels") {
if (displayFilters.group_by === "labels") {
if (groupTitle === "None") preloadedValue = [];
else preloadedValue = [groupTitle];
}
if (selectedGroup)
if (displayFilters.group_by)
setPreloadedData({
[selectedGroup]: preloadedValue,
[displayFilters.group_by]: preloadedValue,
actionType: "createIssue",
});
else setPreloadedData({ actionType: "createIssue" });
},
[setCreateIssueModal, setPreloadedData, selectedGroup]
[displayFilters.group_by, setCreateIssueModal, setPreloadedData]
);
const addIssueToDate = useCallback(
@ -352,7 +351,7 @@ export const IssuesView: React.FC<Props> = ({
CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params),
(prevData: any) => {
if (!prevData) return prevData;
if (selectedGroup) {
if (displayFilters.group_by) {
const filteredData: any = {};
for (const key in prevData) {
filteredData[key] = prevData[key].filter((item: any) => item.id !== issueId);
@ -384,7 +383,7 @@ export const IssuesView: React.FC<Props> = ({
console.log(e);
});
},
[workspaceSlug, projectId, cycleId, params, selectedGroup, setToastAlert]
[displayFilters.group_by, workspaceSlug, projectId, cycleId, params, setToastAlert]
);
const removeIssueFromModule = useCallback(
@ -395,7 +394,7 @@ export const IssuesView: React.FC<Props> = ({
MODULE_ISSUES_WITH_PARAMS(moduleId as string, params),
(prevData: any) => {
if (!prevData) return prevData;
if (selectedGroup) {
if (displayFilters.group_by) {
const filteredData: any = {};
for (const key in prevData) {
filteredData[key] = prevData[key].filter((item: any) => item.id !== issueId);
@ -427,7 +426,7 @@ export const IssuesView: React.FC<Props> = ({
console.log(e);
});
},
[workspaceSlug, projectId, moduleId, params, selectedGroup, setToastAlert]
[displayFilters.group_by, workspaceSlug, projectId, moduleId, params, setToastAlert]
);
const nullFilters = Object.keys(filters).filter(
@ -481,7 +480,6 @@ export const IssuesView: React.FC<Props> = ({
state: null,
start_date: null,
target_date: null,
type: null,
})
}
/>
@ -513,10 +511,10 @@ export const IssuesView: React.FC<Props> = ({
addIssueToGroup={addIssueToGroup}
disableUserActions={disableUserActions}
dragDisabled={
selectedGroup === "created_by" ||
selectedGroup === "labels" ||
selectedGroup === "state_detail.group" ||
selectedGroup === "assignees"
displayFilters.group_by === "created_by" ||
displayFilters.group_by === "labels" ||
displayFilters.group_by === "state_detail.group" ||
displayFilters.group_by === "assignees"
}
emptyState={{
title: cycleId
@ -554,15 +552,12 @@ export const IssuesView: React.FC<Props> = ({
trashBox={trashBox}
setTrashBox={setTrashBox}
viewProps={{
groupByProperty: selectedGroup,
groupedIssues: groupedByIssues,
displayFilters,
isEmpty,
issueView,
mutateIssues,
orderBy,
params,
properties,
showEmptyGroups,
}}
/>
</>

View file

@ -29,7 +29,7 @@ export const AllLists: React.FC<Props> = ({
userAuth,
viewProps,
}) => {
const { groupByProperty: selectedGroup, groupedIssues, showEmptyGroups } = viewProps;
const { displayFilters, groupedIssues } = viewProps;
return (
<>
@ -37,9 +37,12 @@ export const AllLists: React.FC<Props> = ({
<div className="h-full overflow-y-auto">
{Object.keys(groupedIssues).map((singleGroup) => {
const currentState =
selectedGroup === "state" ? states?.find((s) => s.id === singleGroup) : null;
displayFilters?.group_by === "state"
? states?.find((s) => s.id === singleGroup)
: null;
if (!showEmptyGroups && groupedIssues[singleGroup].length === 0) return null;
if (!displayFilters?.show_empty_groups && groupedIssues[singleGroup].length === 0)
return null;
return (
<SingleList

View file

@ -91,7 +91,7 @@ export const SingleListIssue: React.FC<Props> = ({
const { setToastAlert } = useToast();
const { groupByProperty: selectedGroup, orderBy, properties, mutateIssues } = viewProps;
const { displayFilters, properties, mutateIssues } = viewProps;
const partialUpdateIssue = useCallback(
(formData: Partial<IIssue>, issue: IIssue) => {
@ -124,9 +124,9 @@ export const SingleListIssue: React.FC<Props> = ({
handleIssuesMutation(
formData,
groupTitle ?? "",
selectedGroup,
displayFilters?.group_by ?? null,
index,
orderBy,
displayFilters?.order_by ?? "-created_at",
prevData
),
false
@ -148,15 +148,14 @@ export const SingleListIssue: React.FC<Props> = ({
});
},
[
displayFilters,
workspaceSlug,
cycleId,
moduleId,
userId,
groupTitle,
index,
selectedGroup,
mutateIssues,
orderBy,
user,
]
);

View file

@ -69,7 +69,7 @@ export const SingleList: React.FC<Props> = ({
const type = cycleId ? "cycle" : moduleId ? "module" : "issue";
const { groupByProperty: selectedGroup, groupedIssues } = viewProps;
const { displayFilters, groupedIssues } = viewProps;
const { data: issueLabels } = useSWR<IIssueLabels[]>(
workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null,
@ -90,7 +90,7 @@ export const SingleList: React.FC<Props> = ({
const getGroupTitle = () => {
let title = addSpaceIfCamelCase(groupTitle);
switch (selectedGroup) {
switch (displayFilters?.group_by) {
case "state":
title = addSpaceIfCamelCase(currentState?.name ?? "");
break;
@ -113,7 +113,7 @@ export const SingleList: React.FC<Props> = ({
const getGroupIcon = () => {
let icon;
switch (selectedGroup) {
switch (displayFilters?.group_by) {
case "state":
icon = currentState && (
<StateGroupIcon
@ -177,13 +177,13 @@ export const SingleList: React.FC<Props> = ({
<div className="flex items-center justify-between px-4 py-2.5 bg-custom-background-90">
<Disclosure.Button>
<div className="flex items-center gap-x-3">
{selectedGroup !== null && (
{displayFilters?.group_by !== null && (
<div className="flex items-center">{getGroupIcon()}</div>
)}
{selectedGroup !== null ? (
{displayFilters?.group_by !== null ? (
<h2
className={`text-sm font-semibold leading-6 text-custom-text-100 ${
selectedGroup === "created_by" ? "" : "capitalize"
displayFilters?.group_by === "created_by" ? "" : "capitalize"
}`}
>
{getGroupTitle()}

View file

@ -22,10 +22,10 @@ export const SpreadsheetColumns: React.FC<Props> = ({ columnData, gridTemplateCo
const { storedValue: activeSortingProperty, setValue: setActiveSortingProperty } =
useLocalStorage("spreadsheetViewActiveSortingProperty", "");
const { orderBy, setOrderBy } = useSpreadsheetIssuesView();
const { displayFilters, setDisplayFilters } = useSpreadsheetIssuesView();
const handleOrderBy = (order: TIssueOrderByOptions, itemKey: string) => {
setOrderBy(order);
setDisplayFilters({ order_by: order });
setSelectedMenuItem(`${order}_${itemKey}`);
setActiveSortingProperty(order === "-created_at" ? "" : itemKey);
};
@ -239,7 +239,7 @@ export const SpreadsheetColumns: React.FC<Props> = ({ columnData, gridTemplateCo
</CustomMenu.MenuItem>
{selectedMenuItem &&
selectedMenuItem !== "" &&
orderBy !== "-created_at" &&
displayFilters?.order_by !== "-created_at" &&
selectedMenuItem.includes(col.propertyName) && (
<CustomMenu.MenuItem
className={`mt-0.5${

View file

@ -16,7 +16,7 @@ export const CycleIssuesGanttChartView = () => {
const router = useRouter();
const { workspaceSlug, projectId, cycleId } = router.query;
const { orderBy } = useIssuesView();
const { displayFilters } = useIssuesView();
const { user } = useUser();
const { projectDetails } = useProjectDetails();
@ -44,7 +44,7 @@ export const CycleIssuesGanttChartView = () => {
enableBlockLeftResize={isAllowed}
enableBlockRightResize={isAllowed}
enableBlockMove={isAllowed}
enableReorder={orderBy === "sort_order" && isAllowed}
enableReorder={displayFilters.order_by === "sort_order" && isAllowed}
bottomSpacing
/>
</div>

View file

@ -50,7 +50,7 @@ export const DeleteIssueModal: React.FC<Props> = ({
const { workspaceSlug, projectId, cycleId, moduleId, viewId, issueId } = router.query;
const isArchivedIssues = router.pathname.includes("archived-issues");
const { issueView, params } = useIssuesView();
const { displayFilters, params } = useIssuesView();
const { params: calendarParams } = useCalendarIssuesView();
const { params: spreadsheetParams } = useSpreadsheetIssuesView();
@ -73,7 +73,7 @@ export const DeleteIssueModal: React.FC<Props> = ({
await issueServices
.deleteIssue(workspaceSlug as string, data.project, data.id, user)
.then(() => {
if (issueView === "calendar") {
if (displayFilters.layout === "calendar") {
const calendarFetchKey = cycleId
? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), calendarParams)
: moduleId
@ -87,7 +87,7 @@ export const DeleteIssueModal: React.FC<Props> = ({
(prevData) => (prevData ?? []).filter((p) => p.id !== data.id),
false
);
} else if (issueView === "spreadsheet") {
} else if (displayFilters.layout === "spreadsheet") {
const spreadsheetFetchKey = cycleId
? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), spreadsheetParams)
: moduleId

View file

@ -16,7 +16,7 @@ export const IssueGanttChartView = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { orderBy } = useIssuesView();
const { displayFilters } = useIssuesView();
const { user } = useUser();
const { projectDetails } = useProjectDetails();
@ -43,7 +43,7 @@ export const IssueGanttChartView = () => {
enableBlockLeftResize={isAllowed}
enableBlockRightResize={isAllowed}
enableBlockMove={isAllowed}
enableReorder={orderBy === "sort_order" && isAllowed}
enableReorder={displayFilters.order_by === "sort_order" && isAllowed}
bottomSpacing
/>
</div>

View file

@ -78,7 +78,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId, viewId, inboxId } = router.query;
const { issueView, params } = useIssuesView();
const { displayFilters, params } = useIssuesView();
const { params: calendarParams } = useCalendarIssuesView();
const { order_by, group_by, ...viewGanttParams } = params;
const { params: inboxParams } = useInboxView();
@ -247,13 +247,13 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
if (payload.module && payload.module !== "")
await addIssueToModule(res.id, payload.module);
if (issueView === "calendar") mutate(calendarFetchKey);
if (issueView === "gantt_chart")
if (displayFilters.layout === "calendar") mutate(calendarFetchKey);
if (displayFilters.layout === "gantt_chart")
mutate(ganttFetchKey, {
start_target_date: true,
order_by: "sort_order",
});
if (issueView === "spreadsheet") mutate(spreadsheetFetchKey);
if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey);
if (groupedIssues) mutateMyIssues();
setToastAlert({
@ -285,8 +285,8 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
if (isUpdatingSingleIssue) {
mutate<IIssue>(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false);
} else {
if (issueView === "calendar") mutate(calendarFetchKey);
if (issueView === "spreadsheet") mutate(spreadsheetFetchKey);
if (displayFilters.layout === "calendar") mutate(calendarFetchKey);
if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey);
if (payload.parent) mutate(SUB_ISSUES(payload.parent.toString()));
mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params));
}

View file

@ -37,20 +37,8 @@ export const MyIssuesViewOptions: React.FC = () => {
const router = useRouter();
const { workspaceSlug } = router.query;
const {
issueView,
setIssueView,
groupBy,
setGroupBy,
orderBy,
setOrderBy,
showEmptyGroups,
setShowEmptyGroups,
properties,
setProperty,
filters,
setFilters,
} = useMyIssuesFilters(workspaceSlug?.toString());
const { displayFilters, setDisplayFilters, properties, setProperty, filters, setFilters } =
useMyIssuesFilters(workspaceSlug?.toString());
const { isEstimateActive } = useEstimateOption();
@ -68,11 +56,11 @@ export const MyIssuesViewOptions: React.FC = () => {
<button
type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none hover:bg-custom-sidebar-background-80 duration-300 ${
issueView === option.type
displayFilters?.layout === option.type
? "bg-custom-sidebar-background-80"
: "text-custom-sidebar-text-200"
}`}
onClick={() => setIssueView(option.type)}
onClick={() => setDisplayFilters({ layout: option.type })}
>
<option.Icon
sx={{
@ -139,81 +127,89 @@ export const MyIssuesViewOptions: React.FC = () => {
<Popover.Panel className="absolute right-0 z-30 mt-1 w-screen max-w-xs transform rounded-lg border border-custom-border-200 bg-custom-background-90 p-3 shadow-lg">
<div className="relative divide-y-2 divide-custom-border-200">
<div className="space-y-4 pb-3 text-xs">
{issueView !== "calendar" && issueView !== "spreadsheet" && (
<>
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Group by</h4>
<div className="w-28">
<CustomMenu
label={
groupBy === "project"
? "Project"
: GROUP_BY_OPTIONS.find((option) => option.key === groupBy)
?.name ?? "Select"
}
className="!w-full"
buttonClassName="w-full"
>
{GROUP_BY_OPTIONS.map((option) => {
if (issueView === "kanban" && option.key === null) return null;
if (
option.key === "state" ||
option.key === "created_by" ||
option.key === "assignees"
)
return null;
{displayFilters?.layout !== "calendar" &&
displayFilters?.layout !== "spreadsheet" && (
<>
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Group by</h4>
<div className="w-28">
<CustomMenu
label={
displayFilters?.group_by === "project"
? "Project"
: GROUP_BY_OPTIONS.find(
(option) => option.key === displayFilters?.group_by
)?.name ?? "Select"
}
className="!w-full"
buttonClassName="w-full"
>
{GROUP_BY_OPTIONS.map((option) => {
if (displayFilters?.layout === "kanban" && option.key === null)
return null;
if (
option.key === "state" ||
option.key === "created_by" ||
option.key === "assignees"
)
return null;
return (
<CustomMenu.MenuItem
key={option.key}
onClick={() => setGroupBy(option.key)}
>
{option.name}
</CustomMenu.MenuItem>
);
})}
</CustomMenu>
return (
<CustomMenu.MenuItem
key={option.key}
onClick={() => setDisplayFilters({ group_by: option.key })}
>
{option.name}
</CustomMenu.MenuItem>
);
})}
</CustomMenu>
</div>
</div>
</div>
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Order by</h4>
<div className="w-28">
<CustomMenu
label={
ORDER_BY_OPTIONS.find((option) => option.key === orderBy)?.name ??
"Select"
}
className="!w-full"
buttonClassName="w-full"
>
{ORDER_BY_OPTIONS.map((option) => {
if (groupBy === "priority" && option.key === "priority")
return null;
if (option.key === "sort_order") return null;
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Order by</h4>
<div className="w-28">
<CustomMenu
label={
ORDER_BY_OPTIONS.find(
(option) => option.key === displayFilters?.order_by
)?.name ?? "Select"
}
className="!w-full"
buttonClassName="w-full"
>
{ORDER_BY_OPTIONS.map((option) => {
if (
displayFilters?.group_by === "priority" &&
option.key === "priority"
)
return null;
if (option.key === "sort_order") return null;
return (
<CustomMenu.MenuItem
key={option.key}
onClick={() => {
setOrderBy(option.key);
}}
>
{option.name}
</CustomMenu.MenuItem>
);
})}
</CustomMenu>
return (
<CustomMenu.MenuItem
key={option.key}
onClick={() => {
setDisplayFilters({ order_by: option.key });
}}
>
{option.name}
</CustomMenu.MenuItem>
);
})}
</CustomMenu>
</div>
</div>
</div>
</>
)}
</>
)}
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Issue type</h4>
<div className="w-28">
<CustomMenu
label={
FILTER_ISSUE_OPTIONS.find((option) => option.key === filters?.type)
?.name ?? "Select"
FILTER_ISSUE_OPTIONS.find(
(option) => option.key === displayFilters?.type
)?.name ?? "Select"
}
className="!w-full"
buttonClassName="w-full"
@ -222,7 +218,7 @@ export const MyIssuesViewOptions: React.FC = () => {
<CustomMenu.MenuItem
key={option.key}
onClick={() =>
setFilters({
setDisplayFilters({
type: option.key,
})
}
@ -234,16 +230,24 @@ export const MyIssuesViewOptions: React.FC = () => {
</div>
</div>
{issueView !== "calendar" && issueView !== "spreadsheet" && (
<>
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Show empty states</h4>
<div className="w-28">
<ToggleSwitch value={showEmptyGroups} onChange={setShowEmptyGroups} />
{displayFilters?.layout !== "calendar" &&
displayFilters?.layout !== "spreadsheet" && (
<>
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Show empty states</h4>
<div className="w-28">
<ToggleSwitch
value={displayFilters?.show_empty_groups ?? true}
onChange={() =>
setDisplayFilters({
show_empty_groups: !displayFilters?.show_empty_groups,
})
}
/>
</div>
</div>
</div>
</>
)}
</>
)}
</div>
<div className="space-y-2 py-3">
@ -253,7 +257,7 @@ export const MyIssuesViewOptions: React.FC = () => {
if (key === "estimate" && !isEstimateActive) return null;
if (
issueView === "spreadsheet" &&
displayFilters?.layout === "spreadsheet" &&
(key === "attachment_count" ||
key === "link" ||
key === "sub_issue_count")
@ -261,7 +265,7 @@ export const MyIssuesViewOptions: React.FC = () => {
return null;
if (
issueView !== "spreadsheet" &&
displayFilters?.layout !== "spreadsheet" &&
(key === "created_on" || key === "updated_on")
)
return null;

View file

@ -57,8 +57,9 @@ export const MyIssuesView: React.FC<Props> = ({
const { user } = useUserAuth();
const { groupedIssues, mutateMyIssues, isEmpty, params } = useMyIssues(workspaceSlug?.toString());
const { filters, setFilters, issueView, groupBy, orderBy, properties, showEmptyGroups } =
useMyIssuesFilters(workspaceSlug?.toString());
const { filters, setFilters, displayFilters, setDisplayFilters, properties } = useMyIssuesFilters(
workspaceSlug?.toString()
);
const { data: labels } = useSWR(
workspaceSlug && (filters?.labels ?? []).length > 0
@ -81,7 +82,13 @@ export const MyIssuesView: React.FC<Props> = ({
async (result: DropResult) => {
setTrashBox(false);
if (!result.destination || !workspaceSlug || !groupedIssues || groupBy !== "priority") return;
if (
!result.destination ||
!workspaceSlug ||
!groupedIssues ||
displayFilters?.group_by !== "priority"
)
return;
const { source, destination } = result;
@ -96,7 +103,7 @@ export const MyIssuesView: React.FC<Props> = ({
const sourceGroup = source.droppableId;
const destinationGroup = destination.droppableId;
draggedItem[groupBy] = destinationGroup as TIssuePriorities;
draggedItem[displayFilters.group_by] = destinationGroup as TIssuePriorities;
mutate<{
[key: string]: IIssue[];
@ -113,8 +120,14 @@ export const MyIssuesView: React.FC<Props> = ({
return {
...prevData,
[sourceGroup]: orderArrayBy(sourceGroupArray, orderBy),
[destinationGroup]: orderArrayBy(destinationGroupArray, orderBy),
[sourceGroup]: orderArrayBy(
sourceGroupArray,
displayFilters.order_by ?? "-created_at"
),
[destinationGroup]: orderArrayBy(
destinationGroupArray,
displayFilters.order_by ?? "-created_at"
),
};
},
false
@ -134,7 +147,7 @@ export const MyIssuesView: React.FC<Props> = ({
.catch(() => mutate(USER_ISSUES(workspaceSlug.toString(), params)));
}
},
[groupBy, groupedIssues, handleDeleteIssue, orderBy, params, user, workspaceSlug]
[displayFilters, groupedIssues, handleDeleteIssue, params, user, workspaceSlug]
);
const addIssueToGroup = useCallback(
@ -143,19 +156,19 @@ export const MyIssuesView: React.FC<Props> = ({
let preloadedValue: string | string[] = groupTitle;
if (groupBy === "labels") {
if (displayFilters?.group_by === "labels") {
if (groupTitle === "None") preloadedValue = [];
else preloadedValue = [groupTitle];
}
if (groupBy)
if (displayFilters?.group_by)
setPreloadedData({
[groupBy]: preloadedValue,
[displayFilters?.group_by]: preloadedValue,
actionType: "createIssue",
});
else setPreloadedData({ actionType: "createIssue" });
},
[setCreateIssueModal, setPreloadedData, groupBy]
[setCreateIssueModal, setPreloadedData, displayFilters?.group_by]
);
const addIssueToDate = useCallback(
@ -263,7 +276,6 @@ export const MyIssuesView: React.FC<Props> = ({
state_group: null,
start_date: null,
target_date: null,
type: null,
})
}
/>
@ -275,7 +287,7 @@ export const MyIssuesView: React.FC<Props> = ({
addIssueToDate={addIssueToDate}
addIssueToGroup={addIssueToGroup}
disableUserActions={disableUserActions}
dragDisabled={groupBy !== "priority"}
dragDisabled={displayFilters?.group_by !== "priority"}
emptyState={{
title: filters.assignees
? "You don't have any issue assigned to you yet"
@ -304,15 +316,12 @@ export const MyIssuesView: React.FC<Props> = ({
trashBox={trashBox}
setTrashBox={setTrashBox}
viewProps={{
groupByProperty: groupBy,
displayFilters,
groupedIssues,
isEmpty,
issueView,
mutateIssues: mutateMyIssues,
orderBy,
params,
properties,
showEmptyGroups,
}}
/>
</>

View file

@ -34,7 +34,7 @@ export const ViewDueDateSelect: React.FC<Props> = ({
const router = useRouter();
const { workspaceSlug } = router.query;
const { issueView } = useIssuesView();
const { displayFilters } = useIssuesView();
const minDate = issue.start_date ? new Date(issue.start_date) : null;
minDate?.setDate(minDate.getDate());
@ -80,7 +80,9 @@ export const ViewDueDateSelect: React.FC<Props> = ({
);
}}
className={`${issue?.target_date ? "w-[6.5rem]" : "w-[5rem] text-center"} ${
issueView === "kanban" ? "bg-custom-background-90" : "bg-custom-background-100"
displayFilters.layout === "kanban"
? "bg-custom-background-90"
: "bg-custom-background-100"
}`}
minDate={minDate ?? undefined}
noBorder={noBorder}

View file

@ -34,7 +34,7 @@ export const ViewStartDateSelect: React.FC<Props> = ({
const router = useRouter();
const { workspaceSlug } = router.query;
const { issueView } = useIssuesView();
const { displayFilters } = useIssuesView();
const maxDate = issue.target_date ? new Date(issue.target_date) : null;
maxDate?.setDate(maxDate.getDate());
@ -72,7 +72,9 @@ export const ViewStartDateSelect: React.FC<Props> = ({
);
}}
className={`${issue?.start_date ? "w-[6.5rem]" : "w-[5rem] text-center"} ${
issueView === "kanban" ? "bg-custom-background-90" : "bg-custom-background-100"
displayFilters.layout === "kanban"
? "bg-custom-background-90"
: "bg-custom-background-100"
}`}
maxDate={maxDate ?? undefined}
noBorder={noBorder}

View file

@ -20,7 +20,7 @@ export const ModuleIssuesGanttChartView: FC<Props> = ({}) => {
const router = useRouter();
const { workspaceSlug, projectId, moduleId } = router.query;
const { orderBy } = useIssuesView();
const { displayFilters } = useIssuesView();
const { user } = useUser();
const { projectDetails } = useProjectDetails();
@ -48,7 +48,7 @@ export const ModuleIssuesGanttChartView: FC<Props> = ({}) => {
enableBlockLeftResize={isAllowed}
enableBlockRightResize={isAllowed}
enableBlockMove={isAllowed}
enableReorder={orderBy === "sort_order" && isAllowed}
enableReorder={displayFilters.order_by === "sort_order" && isAllowed}
bottomSpacing
/>
</div>

View file

@ -41,16 +41,10 @@ export const ProfileIssuesViewOptions: React.FC = () => {
const { projects } = useProjects();
const {
issueView,
setIssueView,
groupByProperty,
setGroupByProperty,
orderBy,
setOrderBy,
showEmptyGroups,
setShowEmptyGroups,
displayFilters,
setDisplayFilters,
filters,
properties,
displayProperties,
setProperties,
setFilters,
} = useProfileIssues(workspaceSlug?.toString(), userId?.toString());
@ -94,11 +88,11 @@ export const ProfileIssuesViewOptions: React.FC = () => {
<button
type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none hover:bg-custom-sidebar-background-80 duration-300 ${
issueView === option.type
displayFilters.layout === option.type
? "bg-custom-sidebar-background-80"
: "text-custom-sidebar-text-200"
}`}
onClick={() => setIssueView(option.type)}
onClick={() => setDisplayFilters({ layout: option.type })}
>
<option.Icon
sx={{
@ -165,82 +159,89 @@ export const ProfileIssuesViewOptions: React.FC = () => {
<Popover.Panel className="absolute right-0 z-30 mt-1 w-screen max-w-xs transform rounded-lg border border-custom-border-200 bg-custom-background-90 p-3 shadow-lg">
<div className="relative divide-y-2 divide-custom-border-200">
<div className="space-y-4 pb-3 text-xs">
{issueView !== "calendar" && issueView !== "spreadsheet" && (
<>
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Group by</h4>
<div className="w-28">
<CustomMenu
label={
groupByProperty === "project"
? "Project"
: GROUP_BY_OPTIONS.find(
(option) => option.key === groupByProperty
)?.name ?? "Select"
}
className="!w-full"
buttonClassName="w-full"
>
{GROUP_BY_OPTIONS.map((option) => {
if (issueView === "kanban" && option.key === null) return null;
if (
option.key === "state" ||
option.key === "created_by" ||
option.key === "assignees"
)
return null;
{displayFilters.layout !== "calendar" &&
displayFilters.layout !== "spreadsheet" && (
<>
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Group by</h4>
<div className="w-28">
<CustomMenu
label={
displayFilters.group_by === "project"
? "Project"
: GROUP_BY_OPTIONS.find(
(option) => option.key === displayFilters.group_by
)?.name ?? "Select"
}
className="!w-full"
buttonClassName="w-full"
>
{GROUP_BY_OPTIONS.map((option) => {
if (displayFilters.layout === "kanban" && option.key === null)
return null;
if (
option.key === "state" ||
option.key === "created_by" ||
option.key === "assignees"
)
return null;
return (
<CustomMenu.MenuItem
key={option.key}
onClick={() => setGroupByProperty(option.key)}
>
{option.name}
</CustomMenu.MenuItem>
);
})}
</CustomMenu>
return (
<CustomMenu.MenuItem
key={option.key}
onClick={() => setDisplayFilters({ group_by: option.key })}
>
{option.name}
</CustomMenu.MenuItem>
);
})}
</CustomMenu>
</div>
</div>
</div>
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Order by</h4>
<div className="w-28">
<CustomMenu
label={
ORDER_BY_OPTIONS.find((option) => option.key === orderBy)?.name ??
"Select"
}
className="!w-full"
buttonClassName="w-full"
>
{ORDER_BY_OPTIONS.map((option) => {
if (groupByProperty === "priority" && option.key === "priority")
return null;
if (option.key === "sort_order") return null;
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Order by</h4>
<div className="w-28">
<CustomMenu
label={
ORDER_BY_OPTIONS.find(
(option) => option.key === displayFilters.order_by
)?.name ?? "Select"
}
className="!w-full"
buttonClassName="w-full"
>
{ORDER_BY_OPTIONS.map((option) => {
if (
displayFilters.group_by === "priority" &&
option.key === "priority"
)
return null;
if (option.key === "sort_order") return null;
return (
<CustomMenu.MenuItem
key={option.key}
onClick={() => {
setOrderBy(option.key);
}}
>
{option.name}
</CustomMenu.MenuItem>
);
})}
</CustomMenu>
return (
<CustomMenu.MenuItem
key={option.key}
onClick={() => {
setDisplayFilters({ order_by: option.key });
}}
>
{option.name}
</CustomMenu.MenuItem>
);
})}
</CustomMenu>
</div>
</div>
</div>
</>
)}
</>
)}
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Issue type</h4>
<div className="w-28">
<CustomMenu
label={
FILTER_ISSUE_OPTIONS.find((option) => option.key === filters?.type)
?.name ?? "Select"
FILTER_ISSUE_OPTIONS.find(
(option) => option.key === displayFilters?.type
)?.name ?? "Select"
}
className="!w-full"
buttonClassName="w-full"
@ -249,7 +250,7 @@ export const ProfileIssuesViewOptions: React.FC = () => {
<CustomMenu.MenuItem
key={option.key}
onClick={() =>
setFilters({
setDisplayFilters({
type: option.key,
})
}
@ -261,26 +262,34 @@ export const ProfileIssuesViewOptions: React.FC = () => {
</div>
</div>
{issueView !== "calendar" && issueView !== "spreadsheet" && (
<>
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Show empty states</h4>
<div className="w-28">
<ToggleSwitch value={showEmptyGroups} onChange={setShowEmptyGroups} />
{displayFilters.layout !== "calendar" &&
displayFilters.layout !== "spreadsheet" && (
<>
<div className="flex items-center justify-between">
<h4 className="text-custom-text-200">Show empty states</h4>
<div className="w-28">
<ToggleSwitch
value={displayFilters.show_empty_groups ?? true}
onChange={() =>
setDisplayFilters({
show_empty_groups: !displayFilters.show_empty_groups,
})
}
/>
</div>
</div>
</div>
</>
)}
</>
)}
</div>
<div className="space-y-2 py-3">
<h4 className="text-sm text-custom-text-200">Display Properties</h4>
<div className="flex flex-wrap items-center gap-2 text-custom-text-200">
{Object.keys(properties).map((key) => {
{Object.keys(displayProperties).map((key) => {
if (key === "estimate" && !isEstimateActive) return null;
if (
issueView === "spreadsheet" &&
displayFilters.layout === "spreadsheet" &&
(key === "attachment_count" ||
key === "link" ||
key === "sub_issue_count")
@ -288,7 +297,7 @@ export const ProfileIssuesViewOptions: React.FC = () => {
return null;
if (
issueView !== "spreadsheet" &&
displayFilters.layout !== "spreadsheet" &&
(key === "created_on" || key === "updated_on")
)
return null;
@ -298,7 +307,7 @@ export const ProfileIssuesViewOptions: React.FC = () => {
key={key}
type="button"
className={`rounded border px-2 py-1 text-xs capitalize ${
properties[key as keyof Properties]
displayProperties[key as keyof Properties]
? "border-custom-primary bg-custom-primary text-white"
: "border-custom-border-200"
}`}

View file

@ -50,14 +50,12 @@ export const ProfileIssuesView = () => {
const {
groupedIssues,
mutateProfileIssues,
issueView,
groupByProperty,
orderBy,
displayFilters,
setDisplayFilters,
isEmpty,
showEmptyGroups,
filters,
setFilters,
properties,
displayProperties,
params,
} = useProfileIssues(workspaceSlug?.toString(), userId?.toString());
@ -92,7 +90,12 @@ export const ProfileIssuesView = () => {
async (result: DropResult) => {
setTrashBox(false);
if (!result.destination || !workspaceSlug || !groupedIssues || groupByProperty !== "priority")
if (
!result.destination ||
!workspaceSlug ||
!groupedIssues ||
displayFilters.group_by !== "priority"
)
return;
const { source, destination } = result;
@ -108,7 +111,7 @@ export const ProfileIssuesView = () => {
const sourceGroup = source.droppableId;
const destinationGroup = destination.droppableId;
draggedItem[groupByProperty] = destinationGroup as TIssuePriorities;
draggedItem[displayFilters.group_by] = destinationGroup as TIssuePriorities;
mutateProfileIssues((prevData: any) => {
if (!prevData) return prevData;
@ -121,8 +124,11 @@ export const ProfileIssuesView = () => {
return {
...prevData,
[sourceGroup]: orderArrayBy(sourceGroupArray, orderBy),
[destinationGroup]: orderArrayBy(destinationGroupArray, orderBy),
[sourceGroup]: orderArrayBy(sourceGroupArray, displayFilters.order_by ?? "-created_at"),
[destinationGroup]: orderArrayBy(
destinationGroupArray,
displayFilters.order_by ?? "-created_at"
),
};
}, false);
@ -140,15 +146,7 @@ export const ProfileIssuesView = () => {
.catch(() => mutateProfileIssues());
}
},
[
groupByProperty,
groupedIssues,
handleDeleteIssue,
mutateProfileIssues,
orderBy,
user,
workspaceSlug,
]
[displayFilters, groupedIssues, handleDeleteIssue, mutateProfileIssues, user, workspaceSlug]
);
const addIssueToGroup = useCallback(
@ -157,19 +155,19 @@ export const ProfileIssuesView = () => {
let preloadedValue: string | string[] = groupTitle;
if (groupByProperty === "labels") {
if (displayFilters.group_by === "labels") {
if (groupTitle === "None") preloadedValue = [];
else preloadedValue = [groupTitle];
}
if (groupByProperty)
if (displayFilters.group_by)
setPreloadedData({
[groupByProperty]: preloadedValue,
[displayFilters.group_by]: preloadedValue,
actionType: "createIssue",
});
else setPreloadedData({ actionType: "createIssue" });
},
[setCreateIssueModal, setPreloadedData, groupByProperty]
[setCreateIssueModal, setPreloadedData, displayFilters.group_by]
);
const addIssueToDate = useCallback(
@ -277,7 +275,6 @@ export const ProfileIssuesView = () => {
state_group: null,
start_date: null,
target_date: null,
type: null,
})
}
/>
@ -289,7 +286,7 @@ export const ProfileIssuesView = () => {
addIssueToDate={addIssueToDate}
addIssueToGroup={addIssueToGroup}
disableUserActions={false}
dragDisabled={groupByProperty !== "priority"}
dragDisabled={displayFilters.group_by !== "priority"}
emptyState={{
title: router.pathname.includes("assigned")
? `Issues assigned to ${profileData?.user_data.display_name} will appear here`
@ -305,15 +302,12 @@ export const ProfileIssuesView = () => {
trashBox={trashBox}
setTrashBox={setTrashBox}
viewProps={{
groupByProperty,
groupedIssues,
displayFilters,
isEmpty,
issueView,
mutateIssues: mutateProfileIssues,
orderBy,
params,
properties,
showEmptyGroups,
properties: displayProperties,
}}
/>
</>