mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: state description in settings (#275)
* chore: removed minor bugs * feat: state description in settings * feat: group by assignee
This commit is contained in:
parent
9c8c7f1dda
commit
e53ff4c02e
@ -12,80 +12,97 @@ import {
|
|||||||
// helpers
|
// helpers
|
||||||
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue, IProjectMember, NestedKeyOf } from "types";
|
||||||
type Props = {
|
type Props = {
|
||||||
isCollapsed: boolean;
|
|
||||||
setIsCollapsed: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
groupedByIssues: {
|
groupedByIssues: {
|
||||||
[key: string]: IIssue[];
|
[key: string]: IIssue[];
|
||||||
};
|
};
|
||||||
|
selectedGroup: NestedKeyOf<IIssue> | null;
|
||||||
groupTitle: string;
|
groupTitle: string;
|
||||||
createdBy: string | null;
|
|
||||||
bgColor?: string;
|
bgColor?: string;
|
||||||
addIssueToState: () => void;
|
addIssueToState: () => void;
|
||||||
|
members: IProjectMember[] | undefined;
|
||||||
|
isCollapsed: boolean;
|
||||||
|
setIsCollapsed: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BoardHeader: React.FC<Props> = ({
|
export const BoardHeader: React.FC<Props> = ({
|
||||||
isCollapsed,
|
|
||||||
setIsCollapsed,
|
|
||||||
groupedByIssues,
|
groupedByIssues,
|
||||||
|
selectedGroup,
|
||||||
groupTitle,
|
groupTitle,
|
||||||
createdBy,
|
|
||||||
bgColor,
|
bgColor,
|
||||||
addIssueToState,
|
addIssueToState,
|
||||||
}) => (
|
isCollapsed,
|
||||||
<div
|
setIsCollapsed,
|
||||||
className={`flex justify-between p-3 pb-0 ${
|
members,
|
||||||
!isCollapsed ? "flex-col rounded-md border bg-gray-50" : ""
|
}) => {
|
||||||
}`}
|
const createdBy =
|
||||||
>
|
selectedGroup === "created_by"
|
||||||
<div className={`flex items-center ${!isCollapsed ? "flex-col gap-2" : "gap-1"}`}>
|
? members?.find((m) => m.member.id === groupTitle)?.member.first_name ?? "loading..."
|
||||||
<div
|
: null;
|
||||||
className={`flex cursor-pointer items-center gap-x-1 rounded-md bg-slate-900 px-2 ${
|
|
||||||
!isCollapsed ? "mb-2 flex-col gap-y-2 py-2" : ""
|
let assignees: any;
|
||||||
}`}
|
if (selectedGroup === "assignees") {
|
||||||
style={{
|
assignees = groupTitle.split(",");
|
||||||
border: `2px solid ${bgColor}`,
|
assignees = assignees
|
||||||
backgroundColor: `${bgColor}20`,
|
.map((a: string) => members?.find((m) => m.member.id === a)?.member.first_name)
|
||||||
}}
|
.join(", ");
|
||||||
>
|
}
|
||||||
<h2
|
|
||||||
className={`text-[0.9rem] font-medium capitalize`}
|
return (
|
||||||
|
<div
|
||||||
|
className={`flex justify-between p-3 pb-0 ${
|
||||||
|
!isCollapsed ? "flex-col rounded-md border bg-gray-50" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className={`flex items-center ${!isCollapsed ? "flex-col gap-2" : "gap-1"}`}>
|
||||||
|
<div
|
||||||
|
className={`flex cursor-pointer items-center gap-x-1 rounded-md bg-slate-900 px-2 ${
|
||||||
|
!isCollapsed ? "mb-2 flex-col gap-y-2 py-2" : ""
|
||||||
|
}`}
|
||||||
style={{
|
style={{
|
||||||
writingMode: !isCollapsed ? "vertical-rl" : "horizontal-tb",
|
border: `2px solid ${bgColor}`,
|
||||||
|
backgroundColor: `${bgColor}20`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{groupTitle === null || groupTitle === "null"
|
<h2
|
||||||
? "None"
|
className={`text-[0.9rem] font-medium capitalize`}
|
||||||
: createdBy
|
style={{
|
||||||
? createdBy
|
writingMode: !isCollapsed ? "vertical-rl" : "horizontal-tb",
|
||||||
: addSpaceIfCamelCase(groupTitle)}
|
}}
|
||||||
</h2>
|
>
|
||||||
<span className="ml-0.5 text-sm text-gray-500">{groupedByIssues[groupTitle].length}</span>
|
{selectedGroup === "created_by"
|
||||||
|
? createdBy
|
||||||
|
: selectedGroup === "assignees"
|
||||||
|
? assignees
|
||||||
|
: addSpaceIfCamelCase(groupTitle)}
|
||||||
|
</h2>
|
||||||
|
<span className="ml-0.5 text-sm text-gray-500">{groupedByIssues[groupTitle].length}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`flex items-center ${!isCollapsed ? "flex-col pb-2" : ""}`}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200"
|
||||||
|
onClick={() => {
|
||||||
|
setIsCollapsed((prevData) => !prevData);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isCollapsed ? (
|
||||||
|
<ArrowsPointingInIcon className="h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<ArrowsPointingOutIcon className="h-4 w-4" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200"
|
||||||
|
onClick={addIssueToState}
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
<div className={`flex items-center ${!isCollapsed ? "flex-col pb-2" : ""}`}>
|
};
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200"
|
|
||||||
onClick={() => {
|
|
||||||
setIsCollapsed((prevData) => !prevData);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isCollapsed ? (
|
|
||||||
<ArrowsPointingInIcon className="h-4 w-4" />
|
|
||||||
) : (
|
|
||||||
<ArrowsPointingOutIcon className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200"
|
|
||||||
onClick={addIssueToState}
|
|
||||||
>
|
|
||||||
<PlusIcon className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
@ -55,11 +55,6 @@ export const SingleBoard: React.FC<Props> = ({
|
|||||||
|
|
||||||
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
|
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
|
||||||
|
|
||||||
const createdBy =
|
|
||||||
selectedGroup === "created_by"
|
|
||||||
? members?.find((m) => m.member.id === groupTitle)?.member.first_name ?? "loading..."
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (selectedGroup === "priority")
|
if (selectedGroup === "priority")
|
||||||
groupTitle === "high"
|
groupTitle === "high"
|
||||||
? (bgColor = "#dc2626")
|
? (bgColor = "#dc2626")
|
||||||
@ -77,11 +72,12 @@ export const SingleBoard: React.FC<Props> = ({
|
|||||||
<BoardHeader
|
<BoardHeader
|
||||||
addIssueToState={addIssueToState}
|
addIssueToState={addIssueToState}
|
||||||
bgColor={bgColor}
|
bgColor={bgColor}
|
||||||
createdBy={createdBy}
|
selectedGroup={selectedGroup}
|
||||||
groupTitle={groupTitle}
|
groupTitle={groupTitle}
|
||||||
groupedByIssues={groupedByIssues}
|
groupedByIssues={groupedByIssues}
|
||||||
isCollapsed={isCollapsed}
|
isCollapsed={isCollapsed}
|
||||||
setIsCollapsed={setIsCollapsed}
|
setIsCollapsed={setIsCollapsed}
|
||||||
|
members={members}
|
||||||
/>
|
/>
|
||||||
<StrictModeDroppable key={groupTitle} droppableId={groupTitle}>
|
<StrictModeDroppable key={groupTitle} droppableId={groupTitle}>
|
||||||
{(provided, snapshot) => (
|
{(provided, snapshot) => (
|
||||||
@ -97,7 +93,9 @@ export const SingleBoard: React.FC<Props> = ({
|
|||||||
key={issue.id}
|
key={issue.id}
|
||||||
draggableId={issue.id}
|
draggableId={issue.id}
|
||||||
index={index}
|
index={index}
|
||||||
isDragDisabled={isNotAllowed || selectedGroup === "created_by"}
|
isDragDisabled={
|
||||||
|
isNotAllowed || selectedGroup === "created_by" || selectedGroup === "assignees"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{(provided, snapshot) => (
|
{(provided, snapshot) => (
|
||||||
<SingleBoardIssue
|
<SingleBoardIssue
|
||||||
|
@ -50,9 +50,17 @@ export const SingleList: React.FC<Props> = ({
|
|||||||
|
|
||||||
const createdBy =
|
const createdBy =
|
||||||
selectedGroup === "created_by"
|
selectedGroup === "created_by"
|
||||||
? members?.find((m) => m.member.id === groupTitle)?.member.first_name ?? "loading..."
|
? members?.find((m) => m.member.id === groupTitle)?.member.first_name ?? "Loading..."
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
let assignees: any;
|
||||||
|
if (selectedGroup === "assignees") {
|
||||||
|
assignees = groupTitle.split(",");
|
||||||
|
assignees = assignees
|
||||||
|
.map((a: string) => members?.find((m) => m.member.id === a)?.member.first_name)
|
||||||
|
.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Disclosure key={groupTitle} as="div" defaultOpen>
|
<Disclosure key={groupTitle} as="div" defaultOpen>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
@ -67,10 +75,10 @@ export const SingleList: React.FC<Props> = ({
|
|||||||
</span>
|
</span>
|
||||||
{selectedGroup !== null ? (
|
{selectedGroup !== null ? (
|
||||||
<h2 className="font-medium capitalize leading-5">
|
<h2 className="font-medium capitalize leading-5">
|
||||||
{groupTitle === null || groupTitle === "null"
|
{selectedGroup === "created_by"
|
||||||
? "None"
|
|
||||||
: createdBy
|
|
||||||
? createdBy
|
? createdBy
|
||||||
|
: selectedGroup === "assignees"
|
||||||
|
? assignees
|
||||||
: addSpaceIfCamelCase(groupTitle)}
|
: addSpaceIfCamelCase(groupTitle)}
|
||||||
</h2>
|
</h2>
|
||||||
) : (
|
) : (
|
||||||
|
@ -82,14 +82,14 @@ export const SidebarCycleSelect: React.FC<Props> = ({
|
|||||||
{cycles ? (
|
{cycles ? (
|
||||||
cycles.length > 0 ? (
|
cycles.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<CustomSelect.Option value={null} className="capitalize">
|
|
||||||
None
|
|
||||||
</CustomSelect.Option>
|
|
||||||
{cycles.map((option) => (
|
{cycles.map((option) => (
|
||||||
<CustomSelect.Option key={option.id} value={option.id}>
|
<CustomSelect.Option key={option.id} value={option.id}>
|
||||||
{option.name}
|
{option.name}
|
||||||
</CustomSelect.Option>
|
</CustomSelect.Option>
|
||||||
))}
|
))}
|
||||||
|
<CustomSelect.Option value={null} className="capitalize">
|
||||||
|
None
|
||||||
|
</CustomSelect.Option>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center">No cycles found</div>
|
<div className="text-center">No cycles found</div>
|
||||||
|
@ -81,14 +81,14 @@ export const SidebarModuleSelect: React.FC<Props> = ({
|
|||||||
{modules ? (
|
{modules ? (
|
||||||
modules.length > 0 ? (
|
modules.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<CustomSelect.Option value={null} className="capitalize">
|
|
||||||
None
|
|
||||||
</CustomSelect.Option>
|
|
||||||
{modules.map((option) => (
|
{modules.map((option) => (
|
||||||
<CustomSelect.Option key={option.id} value={option.id}>
|
<CustomSelect.Option key={option.id} value={option.id}>
|
||||||
{option.name}
|
{option.name}
|
||||||
</CustomSelect.Option>
|
</CustomSelect.Option>
|
||||||
))}
|
))}
|
||||||
|
<CustomSelect.Option value={null} className="capitalize">
|
||||||
|
None
|
||||||
|
</CustomSelect.Option>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center">No modules found</div>
|
<div className="text-center">No modules found</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
@ -144,6 +144,12 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
|||||||
[workspaceSlug, projectId, issueId, issueDetail]
|
[workspaceSlug, projectId, issueId, issueDetail]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!createLabelForm) return;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}, [createLabelForm, reset]);
|
||||||
|
|
||||||
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
|
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -431,24 +437,25 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
|||||||
</Listbox>
|
</Listbox>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<button
|
{!isNotAllowed && (
|
||||||
type="button"
|
<button
|
||||||
className={`flex ${
|
type="button"
|
||||||
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100"
|
className={`flex ${
|
||||||
} items-center gap-1 rounded-2xl border px-2 py-0.5 text-xs`}
|
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100"
|
||||||
onClick={() => setCreateLabelForm((prevData) => !prevData)}
|
} items-center gap-1 rounded-2xl border px-2 py-0.5 text-xs`}
|
||||||
disabled={isNotAllowed}
|
onClick={() => setCreateLabelForm((prevData) => !prevData)}
|
||||||
>
|
>
|
||||||
{createLabelForm ? (
|
{createLabelForm ? (
|
||||||
<>
|
<>
|
||||||
<XMarkIcon className="h-3 w-3" /> Cancel
|
<XMarkIcon className="h-3 w-3" /> Cancel
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<PlusIcon className="h-3 w-3" /> New
|
<PlusIcon className="h-3 w-3" /> New
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,9 +6,10 @@ import { useRouter } from "next/router";
|
|||||||
|
|
||||||
// components
|
// components
|
||||||
import { DeleteModuleModal } from "components/modules";
|
import { DeleteModuleModal } from "components/modules";
|
||||||
|
// ui
|
||||||
|
import { AssigneesList, Avatar } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
import { CalendarDaysIcon, TrashIcon } from "@heroicons/react/24/outline";
|
import { CalendarDaysIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||||
import User from "public/user.png";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { renderShortNumericDateFormat } from "helpers/date-time.helper";
|
import { renderShortNumericDateFormat } from "helpers/date-time.helper";
|
||||||
// types
|
// types
|
||||||
@ -21,10 +22,12 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const SingleModuleCard: React.FC<Props> = ({ module }) => {
|
export const SingleModuleCard: React.FC<Props> = ({ module }) => {
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug } = router.query;
|
|
||||||
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
|
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
|
||||||
const [selectedModuleForDelete, setSelectedModuleForDelete] = useState<SelectModuleType>();
|
const [selectedModuleForDelete, setSelectedModuleForDelete] = useState<SelectModuleType>();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
const handleDeleteModule = () => {
|
const handleDeleteModule = () => {
|
||||||
if (!module) return;
|
if (!module) return;
|
||||||
|
|
||||||
@ -33,16 +36,7 @@ export const SingleModuleCard: React.FC<Props> = ({ module }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group/card h-full w-full relative select-none p-2">
|
<>
|
||||||
<div className="absolute top-4 right-4 z-50 bg-red-200 opacity-0 group-hover/card:opacity-100">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="grid h-7 w-7 place-items-center bg-white p-1 text-red-500 outline-none duration-300 hover:bg-red-50"
|
|
||||||
onClick={() => handleDeleteModule()}
|
|
||||||
>
|
|
||||||
<TrashIcon className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<DeleteModuleModal
|
<DeleteModuleModal
|
||||||
isOpen={
|
isOpen={
|
||||||
moduleDeleteModal &&
|
moduleDeleteModal &&
|
||||||
@ -52,101 +46,55 @@ export const SingleModuleCard: React.FC<Props> = ({ module }) => {
|
|||||||
setIsOpen={setModuleDeleteModal}
|
setIsOpen={setModuleDeleteModal}
|
||||||
data={selectedModuleForDelete}
|
data={selectedModuleForDelete}
|
||||||
/>
|
/>
|
||||||
<Link href={`/${workspaceSlug}/projects/${module.project}/modules/${module.id}`}>
|
<div className="group/card h-full w-full relative select-none p-2">
|
||||||
<a className="flex flex-col cursor-pointer rounded-md border bg-white p-3 ">
|
<div className="absolute top-4 right-4 z-50 bg-red-200 opacity-0 group-hover/card:opacity-100">
|
||||||
<span className="w-3/4 text-ellipsis overflow-hidden">{module.name}</span>
|
<button
|
||||||
<div className="mt-4 grid grid-cols-2 gap-2 text-xs md:grid-cols-4">
|
type="button"
|
||||||
<div className="space-y-2">
|
className="grid h-7 w-7 place-items-center bg-white p-1 text-red-500 outline-none duration-300 hover:bg-red-50"
|
||||||
<h6 className="text-gray-500">LEAD</h6>
|
onClick={() => handleDeleteModule()}
|
||||||
<div>
|
>
|
||||||
{module.lead ? (
|
<TrashIcon className="h-4 w-4" />
|
||||||
module.lead_detail?.avatar && module.lead_detail.avatar !== "" ? (
|
</button>
|
||||||
<div className="h-5 w-5 rounded-full border-2 border-white">
|
</div>
|
||||||
<Image
|
<Link href={`/${workspaceSlug}/projects/${module.project}/modules/${module.id}`}>
|
||||||
src={module.lead_detail.avatar}
|
<a className="flex flex-col cursor-pointer rounded-md border bg-white p-3 ">
|
||||||
height="100%"
|
<span className="w-3/4 text-ellipsis overflow-hidden">{module.name}</span>
|
||||||
width="100%"
|
<div className="mt-4 grid grid-cols-2 gap-2 text-xs md:grid-cols-4">
|
||||||
className="rounded-full"
|
<div className="space-y-2">
|
||||||
alt={module.lead_detail.first_name}
|
<h6 className="text-gray-500">LEAD</h6>
|
||||||
/>
|
<div>
|
||||||
</div>
|
<Avatar user={module.lead_detail} />
|
||||||
) : (
|
</div>
|
||||||
<div className="grid h-5 w-5 place-items-center rounded-full border-2 border-white bg-gray-700 capitalize text-white">
|
</div>
|
||||||
{module.lead_detail?.first_name && module.lead_detail.first_name !== ""
|
<div className="space-y-2">
|
||||||
? module.lead_detail.first_name.charAt(0)
|
<h6 className="text-gray-500">MEMBERS</h6>
|
||||||
: module.lead_detail?.email.charAt(0)}
|
<div className="flex items-center gap-1 text-xs">
|
||||||
</div>
|
<AssigneesList users={module.members_detail} />
|
||||||
)
|
</div>
|
||||||
) : (
|
</div>
|
||||||
"N/A"
|
<div className="space-y-2">
|
||||||
)}
|
<h6 className="text-gray-500">END DATE</h6>
|
||||||
|
<div className="flex w-min cursor-pointer items-center gap-1 whitespace-nowrap rounded border px-1.5 py-0.5 text-xs shadow-sm">
|
||||||
|
<CalendarDaysIcon className="h-3 w-3" />
|
||||||
|
{renderShortNumericDateFormat(module.target_date ?? "")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<h6 className="text-gray-500">STATUS</h6>
|
||||||
|
<div className="flex items-center gap-2 capitalize">
|
||||||
|
<span
|
||||||
|
className="h-2 w-2 flex-shrink-0 rounded-full"
|
||||||
|
style={{
|
||||||
|
backgroundColor: MODULE_STATUS.find((s) => s.value === module.status)?.color,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{module.status}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
</a>
|
||||||
<h6 className="text-gray-500">MEMBERS</h6>
|
</Link>
|
||||||
<div className="flex items-center gap-1 text-xs">
|
</div>
|
||||||
{module.members && module.members.length > 0 ? (
|
</>
|
||||||
module?.members_detail?.map((member, index: number) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className={`relative z-[1] h-5 w-5 rounded-full ${
|
|
||||||
index !== 0 ? "-ml-2.5" : ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{member?.avatar && member.avatar !== "" ? (
|
|
||||||
<div className="h-5 w-5 rounded-full border-2 border-white bg-white">
|
|
||||||
<Image
|
|
||||||
src={member.avatar}
|
|
||||||
height="100%"
|
|
||||||
width="100%"
|
|
||||||
className="rounded-full"
|
|
||||||
alt={member?.first_name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="grid h-5 w-5 place-items-center rounded-full border-2 border-white bg-gray-700 capitalize text-white">
|
|
||||||
{member?.first_name && member.first_name !== ""
|
|
||||||
? member.first_name.charAt(0)
|
|
||||||
: member?.email?.charAt(0)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<div className="h-5 w-5 rounded-full border-2 border-white bg-white">
|
|
||||||
<Image
|
|
||||||
src={User}
|
|
||||||
height="100%"
|
|
||||||
width="100%"
|
|
||||||
className="rounded-full"
|
|
||||||
alt="No user"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<h6 className="text-gray-500">END DATE</h6>
|
|
||||||
<div className="flex w-min cursor-pointer items-center gap-1 whitespace-nowrap rounded border px-1.5 py-0.5 text-xs shadow-sm">
|
|
||||||
<CalendarDaysIcon className="h-3 w-3" />
|
|
||||||
{renderShortNumericDateFormat(module.target_date ?? "")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<h6 className="text-gray-500">STATUS</h6>
|
|
||||||
<div className="flex items-center gap-2 capitalize">
|
|
||||||
<span
|
|
||||||
className="h-2 w-2 flex-shrink-0 rounded-full"
|
|
||||||
style={{
|
|
||||||
backgroundColor: MODULE_STATUS.find((s) => s.value === module.status)?.color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{module.status}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -142,13 +142,16 @@ export const SingleState: React.FC<Props> = ({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div
|
<span
|
||||||
className="h-3 w-3 flex-shrink-0 rounded-full"
|
className="h-3 w-3 flex-shrink-0 rounded-full"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: state.color,
|
backgroundColor: state.color,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<h6 className="text-sm">{addSpaceIfCamelCase(state.name)}</h6>
|
<div>
|
||||||
|
<h6 className="text-sm">{addSpaceIfCamelCase(state.name)}</h6>
|
||||||
|
<p className="text-xs text-gray-400">{state.description}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{index !== 0 && (
|
{index !== 0 && (
|
||||||
|
@ -13,7 +13,7 @@ import { IUser, IUserLite } from "types";
|
|||||||
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
||||||
|
|
||||||
type AvatarProps = {
|
type AvatarProps = {
|
||||||
user: Partial<IUser> | Partial<IUserLite> | undefined;
|
user?: Partial<IUser> | Partial<IUserLite> | IUser | IUserLite | undefined | null;
|
||||||
index?: number;
|
index?: number;
|
||||||
};
|
};
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
export * from "./avatar";
|
|
@ -1,4 +1,3 @@
|
|||||||
// components
|
|
||||||
export * from "./button";
|
export * from "./button";
|
||||||
export * from "./custom-listbox";
|
export * from "./custom-listbox";
|
||||||
export * from "./custom-menu";
|
export * from "./custom-menu";
|
||||||
@ -11,6 +10,6 @@ export * from "./outline-button";
|
|||||||
export * from "./select";
|
export * from "./select";
|
||||||
export * from "./spinner";
|
export * from "./spinner";
|
||||||
export * from "./text-area";
|
export * from "./text-area";
|
||||||
export * from "./tooltip";
|
|
||||||
export * from "./avatar";
|
export * from "./avatar";
|
||||||
export * from "./datepicker";
|
export * from "./datepicker";
|
||||||
|
export * from "./tooltip";
|
||||||
|
@ -5,6 +5,7 @@ export const GROUP_BY_OPTIONS: Array<{ name: string; key: NestedKeyOf<IIssue> |
|
|||||||
{ name: "State", key: "state_detail.name" },
|
{ name: "State", key: "state_detail.name" },
|
||||||
{ name: "Priority", key: "priority" },
|
{ name: "Priority", key: "priority" },
|
||||||
{ name: "Created By", key: "created_by" },
|
{ name: "Created By", key: "created_by" },
|
||||||
|
{ name: "Assignee", key: "assignees" },
|
||||||
{ name: "None", key: null },
|
{ name: "None", key: null },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ const StatesSettings: NextPage<UserAuth> = (props) => {
|
|||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-3xl font-bold leading-6 text-gray-900">States</h3>
|
<h3 className="text-3xl font-bold leading-6 text-gray-900">States</h3>
|
||||||
<p className="mt-4 text-sm text-gray-500">Manage the state of this project.</p>
|
<p className="mt-4 text-sm text-gray-500">Manage the states of this project.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col justify-between gap-4">
|
<div className="flex flex-col justify-between gap-4">
|
||||||
{states && projectDetails ? (
|
{states && projectDetails ? (
|
||||||
|
Loading…
Reference in New Issue
Block a user