Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: label grouping in dropdowns, default state in project settings #266

Merged
merged 6 commits into from
Feb 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// TODO: Refactor this component: into a different file, use this file to export the components
import React, { useState, useCallback, useEffect } from "react";

import { useRouter } from "next/router";
Expand All @@ -14,7 +13,7 @@ import useTheme from "hooks/use-theme";
import useToast from "hooks/use-toast";
import useUser from "hooks/use-user";
// components
import ShortcutsModal from "components/command-palette/shortcuts";
import { ShortcutsModal } from "components/command-palette";
import { BulkDeleteIssuesModal } from "components/core";
import { CreateProjectModal } from "components/project";
import { CreateUpdateIssueModal } from "components/issues";
Expand All @@ -36,7 +35,7 @@ import { IIssue } from "types";
// fetch-keys
import { USER_ISSUE } from "constants/fetch-keys";

const CommandPalette: React.FC = () => {
export const CommandPalette: React.FC = () => {
const [query, setQuery] = useState("");

const [isPaletteOpen, setIsPaletteOpen] = useState(false);
Expand Down Expand Up @@ -369,5 +368,3 @@ const CommandPalette: React.FC = () => {
</>
);
};

export default CommandPalette;
2 changes: 2 additions & 0 deletions apps/app/components/command-palette/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./command-pallette";
export * from "./shortcuts-modal";
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const shortcuts = [
},
];

const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
const [query, setQuery] = useState("");

const filteredShortcuts = shortcuts.filter((shortcut) =>
Expand Down Expand Up @@ -150,5 +150,3 @@ const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
</Transition.Root>
);
};

export default ShortcutsModal;
1 change: 1 addition & 0 deletions apps/app/components/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./board-view";
export * from "./list-view";
export * from "./sidebar";
export * from "./bulk-delete-issues-modal";
export * from "./existing-issues-list-modal";
export * from "./image-upload-modal";
Expand Down
2 changes: 2 additions & 0 deletions apps/app/components/core/sidebar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./sidebar-progress-stats";
export * from "./single-progress-stats";
6 changes: 2 additions & 4 deletions apps/app/components/core/sidebar/sidebar-progress-stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Tab } from "@headlessui/react";
import issuesServices from "services/issues.service";
import projectService from "services/project.service";
// components
import SingleProgressStats from "components/core/sidebar/single-progress-stats";
import { SingleProgressStats } from "components/core";
// ui
import { Avatar } from "components/ui";
// icons
Expand All @@ -36,7 +36,7 @@ const stateGroupColours: {
completed: "#096e8d",
};

const SidebarProgressStats: React.FC<Props> = ({ groupedIssues, issues }) => {
export const SidebarProgressStats: React.FC<Props> = ({ groupedIssues, issues }) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { data: issueLabels } = useSWR<IIssueLabels[]>(
Expand Down Expand Up @@ -180,5 +180,3 @@ const SidebarProgressStats: React.FC<Props> = ({ groupedIssues, issues }) => {
</div>
);
};

export default SidebarProgressStats;
32 changes: 16 additions & 16 deletions apps/app/components/core/sidebar/single-progress-stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ type TSingleProgressStatsProps = {
total: number;
};

const SingleProgressStats: React.FC<TSingleProgressStatsProps> = ({ title, completed, total }) => (
<>
<div className="flex items-center justify-between w-full py-3 text-xs border-b-[1px] border-gray-200">
<div className="flex items-center justify-start w-1/2 gap-2">{title}</div>
<div className="flex items-center justify-end w-1/2 gap-1 px-2">
<div className="flex h-5 justify-center items-center gap-1 ">
<span className="h-4 w-4 ">
<CircularProgressbar value={completed} maxValue={total} strokeWidth={10} />
</span>
<span className="w-8 text-right">{Math.floor((completed / total) * 100)}%</span>
</div>
<span>of</span>
<span>{total}</span>
export const SingleProgressStats: React.FC<TSingleProgressStatsProps> = ({
title,
completed,
total,
}) => (
<div className="flex items-center justify-between w-full py-3 text-xs border-b-[1px] border-gray-200">
<div className="flex items-center justify-start w-1/2 gap-2">{title}</div>
<div className="flex items-center justify-end w-1/2 gap-1 px-2">
<div className="flex h-5 justify-center items-center gap-1 ">
<span className="h-4 w-4 ">
<CircularProgressbar value={completed} maxValue={total} strokeWidth={10} />
</span>
<span className="w-8 text-right">{Math.floor((completed / total) * 100)}%</span>
</div>
<span>of</span>
<span>{total}</span>
</div>
</>
</div>
);

export default SingleProgressStats;
39 changes: 23 additions & 16 deletions apps/app/components/cycles/modal.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Fragment } from "react";

import { mutate } from "swr";

// headless ui
import { Dialog, Transition } from "@headlessui/react";
// services
import cycleService from "services/cycles.service";
// hooks
import useToast from "hooks/use-toast";
// components
import { CycleForm } from "components/cycles";
// helpers
import { renderDateFormat } from "helpers/date-time.helper";
// types
import type { ICycle } from "types";
// fetch keys
Expand All @@ -20,8 +23,14 @@ export interface CycleModalProps {
initialData?: ICycle;
}

export const CycleModal: React.FC<CycleModalProps> = (props) => {
const { isOpen, handleClose, initialData, projectId, workspaceSlug } = props;
export const CycleModal: React.FC<CycleModalProps> = ({
isOpen,
handleClose,
initialData,
projectId,
workspaceSlug,
}) => {
const { setToastAlert } = useToast();

const createCycle = (payload: Partial<ICycle>) => {
cycleService
Expand All @@ -31,12 +40,11 @@ export const CycleModal: React.FC<CycleModalProps> = (props) => {
handleClose();
})
.catch((err) => {
// TODO: Handle this ERROR.
// Object.keys(err).map((key) => {
// setError(key as keyof typeof defaultValues, {
// message: err[key].join(", "),
// });
// });
setToastAlert({
type: "error",
title: "Error",
message: "Error in creating cycle. Please try again!",
});
});
};

Expand All @@ -48,12 +56,11 @@ export const CycleModal: React.FC<CycleModalProps> = (props) => {
handleClose();
})
.catch((err) => {
// TODO: Handle this ERROR.
// Object.keys(err).map((key) => {
// setError(key as keyof typeof defaultValues, {
// message: err[key].join(", "),
// });
// });
setToastAlert({
type: "error",
title: "Error",
message: "Error in updating cycle. Please try again!",
});
});
};

Expand Down
100 changes: 61 additions & 39 deletions apps/app/components/issues/select/label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useForm } from "react-hook-form";
// headless ui
import { Combobox, Transition } from "@headlessui/react";
// icons
import { TagIcon } from "@heroicons/react/24/outline";
import { RectangleGroupIcon, TagIcon } from "@heroicons/react/24/outline";
// services
import issuesServices from "services/issues.service";
// types
Expand Down Expand Up @@ -58,8 +58,6 @@ export const IssueLabelSelect: React.FC<Props> = ({ value, onChange, projectId }
};

const {
register,
handleSubmit,
formState: { isSubmitting },
setFocus,
reset,
Expand All @@ -69,16 +67,10 @@ export const IssueLabelSelect: React.FC<Props> = ({ value, onChange, projectId }
isOpen && setFocus("name");
}, [isOpen, setFocus]);

const options = issueLabels?.map((label) => ({
value: label.id,
display: label.name,
color: label.color,
}));

const filteredOptions =
query === ""
? options
: options?.filter((option) => option.display.toLowerCase().includes(query.toLowerCase()));
? issueLabels
: issueLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase()));

return (
<>
Expand All @@ -98,10 +90,9 @@ export const IssueLabelSelect: React.FC<Props> = ({ value, onChange, projectId }
<TagIcon className="h-3 w-3 text-gray-500" />
<span className={`flex items-center gap-2 ${!value ? "" : "text-gray-900"}`}>
{Array.isArray(value)
? value
.map((v) => options?.find((option) => option.value === v)?.display)
.join(", ") || "Labels"
: options?.find((option) => option.value === value)?.display || "Labels"}
? value.map((v) => issueLabels?.find((l) => l.id === v)?.name).join(", ") ||
"Labels"
: issueLabels?.find((l) => l.id === value)?.name || "Labels"}
</span>
</Combobox.Button>

Expand All @@ -122,31 +113,62 @@ export const IssueLabelSelect: React.FC<Props> = ({ value, onChange, projectId }
displayValue={(assigned: any) => assigned?.name}
/>
<div className="py-1">
{filteredOptions ? (
{issueLabels && filteredOptions ? (
filteredOptions.length > 0 ? (
filteredOptions.map((option) => (
<Combobox.Option
key={option.value}
className={({ active, selected }) =>
`${active ? "bg-indigo-50" : ""} ${
selected ? "bg-indigo-50 font-medium" : ""
} flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900`
}
value={option.value}
>
{issueLabels && (
<>
<span
className="h-1.5 w-1.5 flex-shrink-0 rounded-full"
style={{
backgroundColor: option.color,
}}
/>
{option.display}
</>
)}
</Combobox.Option>
))
filteredOptions.map((label) => {
const children = issueLabels?.filter((l) => l.parent === label.id);

if (children.length === 0) {
if (!label.parent)
return (
<Combobox.Option
key={label.id}
className={({ active, selected }) =>
`${active ? "bg-indigo-50" : ""} ${
selected ? "bg-indigo-50 font-medium" : ""
} flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900`
}
value={label.id}
>
<span
className="h-1.5 w-1.5 flex-shrink-0 rounded-full"
style={{
backgroundColor: label?.color ?? "green",
}}
/>
{label.name}
</Combobox.Option>
);
} else
return (
<div className="bg-gray-50 border-y border-gray-400">
<div className="flex select-none font-medium items-center gap-2 truncate p-2 text-gray-900">
<RectangleGroupIcon className="h-3 w-3" /> {label.name}
</div>
<div>
{children.map((child) => (
<Combobox.Option
key={child.id}
className={({ active, selected }) =>
`${active ? "bg-indigo-50" : ""} ${
selected ? "bg-indigo-50 font-medium" : ""
} flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900`
}
value={child.id}
>
<span
className="h-2 w-2 flex-shrink-0 rounded-full"
style={{
backgroundColor: child?.color ?? "green",
}}
/>
{child.name}
</Combobox.Option>
))}
</div>
</div>
);
})
) : (
<p className="text-xs text-gray-500 px-2">No labels found</p>
)
Expand Down
Loading