Refactoring Phase 1 (#199)

* style: added cta at the bottom of sidebar, added missing icons as well, showing dynamic workspace member count on workspace dropdown

* refractor: running parallel request,

made create/edit label function to async function

* fix: sidebar dropdown content going below kanban items

outside click detection in need help dropdown

* refractor: making parallel api calls

fix: create state input comes at bottom, create state input gets on focus automatically, form is getting submitted on enter click

* refactoring file structure and signin page

* style: changed text and added spinner for signing in loading

* refractor: removed unused type

* fix: my issue cta in profile page sending to 404 page

* fix: added new s3 bucket url in next.config.js file

increased image modal height

* packaging UI components

* eslint config

* eslint fixes

* refactoring changes

* build fixes

* minor fixes

* adding todo comments for reference

* refactor: cleared unused imports and re ordered imports

* refactor: removed unused imports

* fix: added workspace argument to useissues hook

* refactor: removed api-routes file, unnecessary constants

* refactor: created helpers folder, removed unnecessary constants

* refactor: new context for issue view

* refactoring issues page

* build fixes

* refactoring

* refactor: create issue modal

* refactor: module ui

* fix: sub-issues mutation

* fix: create more option in create issue modal

* description form debounce issue

* refactor: global component for assignees list

* fix: link module interface

* fix: priority icons and sub-issues count added

* fix: cycle mutation in issue details page

* fix: remove issue from cycle mutation

* fix: create issue modal in home page

* fix: removed unnecessary props

* fix: updated create issue form status

* fix: settings auth breaking

* refactor: issue details page

Co-authored-by: Dakshesh Jain <dakshesh.jain14@gmail.com>
Co-authored-by: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com>
Co-authored-by: venkatesh-soulpage <venkatesh.marreboyina@soulpageit.com>
Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia1001@gmail.com>
This commit is contained in:
sriram veeraghanta 2023-01-26 23:42:20 +05:30 committed by GitHub
parent 9134b0c543
commit 9075f9441c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
322 changed files with 14149 additions and 21378 deletions

View file

@ -4,26 +4,25 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr";
// react-beautiful-dnd
import { DragDropContext, DropResult } from "react-beautiful-dnd";
// services
import stateService from "lib/services/state.service";
import issuesService from "lib/services/issues.service";
// constants
import { STATE_LIST, MODULE_ISSUES } from "constants/fetch-keys";
import stateService from "services/state.service";
import issuesService from "services/issues.service";
// hooks
import useIssuesProperties from "hooks/use-issue-properties";
import useIssueView from "hooks/use-issue-view";
// components
import SingleBoard from "components/project/modules/board-view/single-board";
// ui
import { Spinner } from "ui";
import { Spinner } from "components/ui";
// types
import { IIssue, IProjectMember, ModuleIssueResponse, NestedKeyOf, Properties } from "types";
import { IIssue, IProjectMember, ModuleIssueResponse } from "types";
// constants
import { STATE_LIST, MODULE_ISSUES } from "constants/fetch-keys";
type Props = {
groupedByIssues: {
[key: string]: IIssue[];
};
properties: Properties;
selectedGroup: NestedKeyOf<IIssue> | null;
issues: IIssue[];
members: IProjectMember[] | undefined;
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
openIssuesListModal: () => void;
@ -35,15 +34,13 @@ type Props = {
| (Partial<IIssue> & {
actionType: "createIssue" | "edit" | "delete";
})
| undefined
| null
>
>;
};
const ModulesBoardView: React.FC<Props> = ({
groupedByIssues,
properties,
selectedGroup,
issues,
members,
openCreateIssueModal,
openIssuesListModal,
@ -55,6 +52,9 @@ const ModulesBoardView: React.FC<Props> = ({
const router = useRouter();
const { workspaceSlug, projectId, moduleId } = router.query;
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues);
const { data: states } = useSWR(
workspaceSlug && projectId ? STATE_LIST(projectId as string) : null,
workspaceSlug && projectId
@ -126,14 +126,16 @@ const ModulesBoardView: React.FC<Props> = ({
[workspaceSlug, groupedByIssues, projectId, selectedGroup, states, moduleId]
);
if (issueView !== "kanban") return <></>;
return (
<>
{groupedByIssues ? (
<div className="h-full w-full">
<div className="h-screen w-full">
<DragDropContext onDragEnd={handleOnDragEnd}>
<div className="h-full w-full overflow-hidden">
<div className="h-full w-full">
<div className="flex h-full gap-x-4 overflow-x-auto overflow-y-hidden pb-3">
<div className="flex h-full gap-x-4 overflow-x-auto overflow-y-hidden">
{Object.keys(groupedByIssues).map((singleGroup) => (
<SingleBoard
key={singleGroup}

View file

@ -1,26 +1,25 @@
// react
import React, { useState } from "react";
// swr
import { useRouter } from "next/router";
import useSWR from "swr";
// react-beautiful-dnd
import { Draggable } from "react-beautiful-dnd";
// services
import workspaceService from "lib/services/workspace.service";
// hooks
import useUser from "lib/hooks/useUser";
import workspaceService from "services/workspace.service";
// components
import SingleIssue from "components/common/board-view/single-issue";
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
import BoardHeader from "components/common/board-view/board-header";
// ui
import { CustomMenu } from "ui";
import { CustomMenu } from "components/ui";
// icons
import { PlusIcon } from "@heroicons/react/24/outline";
// types
import { IIssue, IWorkspaceMember, NestedKeyOf, Properties } from "types";
// fetch-keys
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
// common
import { addSpaceIfCamelCase } from "constants/common";
import { useRouter } from "next/router";
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
import { Draggable } from "react-beautiful-dnd";
type Props = {
properties: Properties;
@ -41,13 +40,13 @@ type Props = {
| (Partial<IIssue> & {
actionType: "createIssue" | "edit" | "delete";
})
| undefined
| null
>
>;
stateId: string | null;
};
const SingleCycleBoard: React.FC<Props> = ({
const SingleModuleBoard: React.FC<Props> = ({
properties,
groupedByIssues,
selectedGroup,
@ -63,7 +62,7 @@ const SingleCycleBoard: React.FC<Props> = ({
stateId,
}) => {
// Collapse/Expand
const [show, setState] = useState(true);
const [isCollapsed, setIsCollapsed] = useState(true);
const router = useRouter();
const { workspaceSlug } = router.query;
@ -83,71 +82,33 @@ const SingleCycleBoard: React.FC<Props> = ({
);
return (
<div className={`h-full flex-shrink-0 rounded ${!show ? "" : "w-80 border bg-gray-50"}`}>
<div className={`${!show ? "" : "flex h-full flex-col space-y-3 overflow-y-auto"}`}>
<div
className={`flex justify-between p-3 pb-0 ${
!show ? "flex-col rounded-md border bg-gray-50" : ""
}`}
>
<div
className={`flex w-full items-center justify-between ${
!show ? "flex-col gap-2" : "gap-1"
}`}
>
<div
className={`flex cursor-pointer items-center gap-x-1 rounded-md bg-slate-900 px-2 ${
!show ? "mb-2 flex-col gap-y-2 py-2" : ""
}`}
style={{
border: `2px solid ${bgColor}`,
backgroundColor: `${bgColor}20`,
}}
>
<h2
className={`text-[0.9rem] font-medium capitalize`}
style={{
writingMode: !show ? "vertical-rl" : "horizontal-tb",
}}
>
{groupTitle === null || groupTitle === "null"
? "None"
: createdBy
? createdBy
: addSpaceIfCamelCase(groupTitle)}
</h2>
<span className="ml-0.5 text-sm text-gray-500">
{groupedByIssues[groupTitle].length}
</span>
</div>
<CustomMenu width="auto" ellipsis>
<CustomMenu.MenuItem
onClick={() => {
openCreateIssueModal();
if (selectedGroup !== null) {
setPreloadedData({
state: stateId !== null ? stateId : undefined,
[selectedGroup]: groupTitle,
actionType: "createIssue",
});
}
}}
>
Create new
</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={() => openIssuesListModal()}>
Add an existing issue
</CustomMenu.MenuItem>
</CustomMenu>
</div>
</div>
<div className={`h-full flex-shrink-0 rounded ${!isCollapsed ? "" : "w-80 border bg-gray-50"}`}>
<div className={`${!isCollapsed ? "" : "flex h-full flex-col space-y-3 overflow-y-auto"}`}>
<BoardHeader
addIssueToState={() => {
openCreateIssueModal();
if (selectedGroup !== null) {
setPreloadedData({
state: stateId !== null ? stateId : undefined,
[selectedGroup]: groupTitle,
actionType: "createIssue",
});
}
}}
bgColor={bgColor ?? ""}
createdBy={createdBy}
groupTitle={groupTitle}
groupedByIssues={groupedByIssues}
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
selectedGroup={selectedGroup}
/>
<StrictModeDroppable key={groupTitle} droppableId={groupTitle}>
{(provided, snapshot) => (
<div
className={`mt-3 h-full space-y-3 overflow-y-auto px-3 pb-3 ${
snapshot.isDraggingOver ? "bg-indigo-50 bg-opacity-50" : ""
} ${!show ? "hidden" : "block"}`}
} ${!isCollapsed ? "hidden" : "block"}`}
{...provided.droppableProps}
ref={provided.innerRef}
>
@ -158,11 +119,7 @@ const SingleCycleBoard: React.FC<Props> = ({
]?.map((assignee) => {
const tempPerson = people?.find((p) => p.member.id === assignee)?.member;
return {
avatar: tempPerson?.avatar,
first_name: tempPerson?.first_name,
email: tempPerson?.email,
};
return tempPerson;
});
return (
@ -225,4 +182,4 @@ const SingleCycleBoard: React.FC<Props> = ({
);
};
export default SingleCycleBoard;
export default SingleModuleBoard;

View file

@ -5,15 +5,15 @@ import { useRouter } from "next/router";
// swr
import { mutate } from "swr";
// services
import modulesService from "lib/services/modules.service";
// headless ui
import { Dialog, Transition } from "@headlessui/react";
// ui
import { Button } from "ui";
// icons
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// types
import type { IModule } from "types";
import { Button } from "components/ui";
import modulesService from "services/modules.service";
// fetch-keys
import { MODULE_LIST } from "constants/fetch-keys";

View file

@ -7,18 +7,18 @@ import { mutate } from "swr";
import { useForm } from "react-hook-form";
import { Dialog, Transition } from "@headlessui/react";
// types
import type { IModule } from "types";
// components
import SelectLead from "components/project/modules/create-update-module-modal/select-lead";
import SelectMembers from "components/project/modules/create-update-module-modal/select-members";
import SelectStatus from "components/project/modules/create-update-module-modal/select-status";
// ui
import { Button, Input, TextArea } from "ui";
import { Button, Input, TextArea } from "components/ui";
// services
import modulesService from "lib/services/modules.service";
// types
import type { IModule } from "types";
// common
import { renderDateFormat } from "constants/common";
import modulesService from "services/modules.service";
// helpers
import { renderDateFormat } from "helpers/date-time.helper";
// fetch keys
import { MODULE_LIST } from "constants/fetch-keys";
@ -62,7 +62,7 @@ const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, pro
if (!data) {
await modulesService
.createModule(workspaceSlug as string, projectId, payload)
.then((res) => {
.then(() => {
mutate(MODULE_LIST(projectId));
handleClose();
})

View file

@ -4,16 +4,16 @@ import { useRouter } from "next/router";
import useSWR from "swr";
import { Controller } from "react-hook-form";
import type { Control } from "react-hook-form";
// service
import projectServices from "lib/services/project.service";
// ui
import { SearchListbox } from "ui";
// icons
import { Controller, Control } from "react-hook-form";
import { UserIcon } from "@heroicons/react/24/outline";
// types
// import type { Control } from "react-hook-form";
// service
import type { IModule } from "types";
import projectServices from "services/project.service";
// ui
import SearchListbox from "components/search-listbox";
// icons
// types
// fetch-keys
import { PROJECT_MEMBERS } from "constants/fetch-keys";
@ -40,15 +40,13 @@ const SelectLead: React.FC<Props> = ({ control }) => {
<SearchListbox
title="Lead"
optionsFontsize="sm"
options={people?.map((person) => {
return {
value: person.member.id,
display:
person.member.first_name && person.member.first_name !== ""
? person.member.first_name
: person.member.email,
};
})}
options={people?.map((person) => ({
value: person.member.id,
display:
person.member.first_name && person.member.first_name !== ""
? person.member.first_name
: person.member.email,
}))}
value={value}
onChange={onChange}
icon={<UserIcon className="h-3 w-3 text-gray-500" />}

View file

@ -4,16 +4,16 @@ import { useRouter } from "next/router";
import useSWR from "swr";
import { Controller } from "react-hook-form";
import type { Control } from "react-hook-form";
// service
import projectServices from "lib/services/project.service";
// ui
import { SearchListbox } from "ui";
// icons
import { Controller, Control } from "react-hook-form";
import { UserIcon } from "@heroicons/react/24/outline";
// types
// import type { Control } from "react-hook-form";
// service
import type { IModule } from "types";
import projectServices from "services/project.service";
// ui
import SearchListbox from "components/search-listbox";
// icons
// types
// fetch-keys
import { PROJECT_MEMBERS } from "constants/fetch-keys";
@ -40,15 +40,13 @@ const SelectMembers: React.FC<Props> = ({ control }) => {
<SearchListbox
title="Members"
optionsFontsize="sm"
options={people?.map((person) => {
return {
value: person.member.id,
display:
person.member.first_name && person.member.first_name !== ""
? person.member.first_name
: person.member.email,
};
})}
options={people?.map((person) => ({
value: person.member.id,
display:
person.member.first_name && person.member.first_name !== ""
? person.member.first_name
: person.member.email,
}))}
multiple={true}
value={value}
onChange={onChange}

View file

@ -1,14 +1,14 @@
// react
import React from "react";
// react hook form
import { Controller, FieldError } from "react-hook-form";
import type { Control } from "react-hook-form";
// ui
import { CustomListbox } from "ui";
// icons
import { Controller, FieldError, Control } from "react-hook-form";
import { Squares2X2Icon } from "@heroicons/react/24/outline";
// types
// import type { Control } from "react-hook-form";
// ui
import type { IModule } from "types";
import { CustomListbox } from "components/ui";
// icons
// types
import { MODULE_STATUS } from "constants/";
type Props = {
@ -33,9 +33,11 @@ const SelectStatus: React.FC<Props> = (props) => {
: ""
}`}
title="Status"
options={MODULE_STATUS.map((status) => {
return { value: status.value, display: status.label, color: status.color };
})}
options={MODULE_STATUS.map((status) => ({
value: status.value,
display: status.label,
color: status.color,
}))}
value={value}
optionsFontsize="sm"
onChange={onChange}

View file

@ -1,59 +1,54 @@
import React from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// services
import workspaceService from "lib/services/workspace.service";
import stateService from "lib/services/state.service";
// common
import { addSpaceIfCamelCase } from "constants/common";
// icons
import { Disclosure, Transition } from "@headlessui/react";
import { PlusIcon, ChevronDownIcon } from "@heroicons/react/20/solid";
// services
import workspaceService from "services/workspace.service";
import stateService from "services/state.service";
// hooks
import useIssuesProperties from "hooks/use-issue-properties";
import useIssueView from "hooks/use-issue-view";
// components
import SingleListIssue from "components/common/list-view/single-issue";
// headless ui
import { Disclosure, Transition } from "@headlessui/react";
// ui
import { CustomMenu, Spinner } from "ui";
// icons
import { PlusIcon, ChevronDownIcon } from "@heroicons/react/20/solid";
import { CustomMenu, Spinner } from "components/ui";
// helpers
import { addSpaceIfCamelCase } from "helpers/string.helper";
// types
import { IIssue, IWorkspaceMember, NestedKeyOf, Properties } from "types";
import { IIssue, IWorkspaceMember } from "types";
// fetch-keys
import { STATE_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys";
type Props = {
groupedByIssues: {
[key: string]: (IIssue & { bridge?: string })[];
};
properties: Properties;
selectedGroup: NestedKeyOf<IIssue> | null;
issues: IIssue[];
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
openIssuesListModal: () => void;
removeIssueFromModule: (issueId: string) => void;
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
setPreloadedData: React.Dispatch<
React.SetStateAction<
| (Partial<IIssue> & {
actionType: "createIssue" | "edit" | "delete";
})
| undefined
| null
>
>;
};
const ModulesListView: React.FC<Props> = ({
groupedByIssues,
selectedGroup,
issues,
openCreateIssueModal,
openIssuesListModal,
properties,
removeIssueFromModule,
handleDeleteIssue,
setPreloadedData,
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { workspaceSlug, projectId, moduleId } = router.query;
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues);
const { data: people } = useSWR<IWorkspaceMember[]>(
workspaceSlug ? WORKSPACE_MEMBERS : null,
@ -67,6 +62,8 @@ const ModulesListView: React.FC<Props> = ({
: null
);
if (issueView !== "list") return <></>;
return (
<div className="flex h-full flex-col space-y-5">
{Object.keys(groupedByIssues).map((singleGroup) => {
@ -135,10 +132,10 @@ const ModulesListView: React.FC<Props> = ({
<SingleListIssue
key={issue.id}
type="module"
typeId={moduleId as string}
issue={issue}
properties={properties}
editIssue={() => openCreateIssueModal(issue, "edit")}
handleDeleteIssue={() => handleDeleteIssue(issue.id)}
removeIssue={() => removeIssueFromModule(issue.bridge ?? "")}
/>
);

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
@ -7,31 +7,32 @@ import { mutate } from "swr";
import { Controller, useForm } from "react-hook-form";
// services
import modulesService from "lib/services/modules.service";
// hooks
import useToast from "lib/hooks/useToast";
// components
import SelectLead from "components/project/modules/module-detail-sidebar/select-lead";
import SelectMembers from "components/project/modules/module-detail-sidebar/select-members";
import SelectStatus from "components/project/modules/module-detail-sidebar/select-status";
import ModuleLinkModal from "components/project/modules/module-link-modal";
// ui
import { Loader } from "ui";
// icons
import {
CalendarDaysIcon,
ChartPieIcon,
LinkIcon,
PlusIcon,
TrashIcon,
UserIcon,
} from "@heroicons/react/24/outline";
import modulesService from "services/modules.service";
// hooks
import useToast from "hooks/use-toast";
// components
import SelectLead from "components/project/modules/module-detail-sidebar/select-lead";
import SelectMembers from "components/project/modules/module-detail-sidebar/select-members";
import SelectStatus from "components/project/modules/module-detail-sidebar/select-status";
import ModuleLinkModal from "components/project/modules/module-link-modal";
// ui
import { Loader } from "components/ui";
// icons
// helpers
import { timeAgo } from "helpers/date-time.helper";
import { copyTextToClipboard } from "helpers/string.helper";
import { groupBy } from "helpers/array.helper";
// types
import { IModule, ModuleIssueResponse } from "types";
// fetch-keys
import { MODULE_LIST } from "constants/fetch-keys";
// common
import { copyTextToClipboard, groupBy } from "constants/common";
const defaultValues: Partial<IModule> = {
members_list: [],
@ -160,7 +161,7 @@ const ModuleDetailSidebar: React.FC<Props> = ({
</div>
<div className="flex items-center gap-2 sm:basis-1/2">
<div className="grid flex-shrink-0 place-items-center">
<span className="h-4 w-4 rounded-full border-2 border-gray-300 border-r-blue-500"></span>
<span className="h-4 w-4 rounded-full border-2 border-gray-300 border-r-blue-500" />
</div>
{groupedIssues.completed.length}/{moduleIssues?.length}
</div>
@ -256,7 +257,8 @@ const ModuleDetailSidebar: React.FC<Props> = ({
<div>
<h5>{link.title}</h5>
<p className="mt-0.5 text-gray-500">
Added 2 days ago by {link.created_by_detail.email}
Added {timeAgo(link.created_at)} ago by{" "}
{link.created_by_detail.email}
</p>
</div>
</a>
@ -271,13 +273,13 @@ const ModuleDetailSidebar: React.FC<Props> = ({
) : (
<Loader>
<div className="space-y-2">
<Loader.Item height="15px" width="50%"></Loader.Item>
<Loader.Item height="15px" width="30%"></Loader.Item>
<Loader.Item height="15px" width="50%" />
<Loader.Item height="15px" width="30%" />
</div>
<div className="mt-8 space-y-3">
<Loader.Item height="30px"></Loader.Item>
<Loader.Item height="30px"></Loader.Item>
<Loader.Item height="30px"></Loader.Item>
<Loader.Item height="30px" />
<Loader.Item height="30px" />
<Loader.Item height="30px" />
</div>
</Loader>
)}

View file

@ -7,18 +7,16 @@ import useSWR from "swr";
import { Control, Controller } from "react-hook-form";
// services
import workspaceService from "lib/services/workspace.service";
// headless ui
import { Listbox, Transition } from "@headlessui/react";
import { UserIcon } from "@heroicons/react/24/outline";
import workspaceService from "services/workspace.service";
// headless ui
// ui
import { Spinner } from "ui";
import { Spinner } from "components/ui";
// icons
import { UserGroupIcon, UserIcon } from "@heroicons/react/24/outline";
import User from "public/user.png";
// types
import { IModule } from "types";
// constants
import { classNames } from "constants/common";
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
type Props = {
@ -61,10 +59,9 @@ const SelectLead: React.FC<Props> = ({ control, submitChanges }) => {
<div className="relative">
<Listbox.Button className="flex w-full cursor-pointer items-center gap-1 text-xs">
<span
className={classNames(
value ? "" : "text-gray-900",
"hidden truncate text-left sm:block"
)}
className={`hidden truncate text-left sm:block ${
value ? "" : "text-gray-900"
}`}
>
<div className="flex cursor-pointer items-center gap-1 text-xs">
{person && person.avatar && person.avatar !== "" ? (

View file

@ -7,18 +7,17 @@ import useSWR from "swr";
import { Control, Controller } from "react-hook-form";
// services
import workspaceService from "lib/services/workspace.service";
// headless ui
import { Listbox, Transition } from "@headlessui/react";
// ui
import { Spinner } from "ui";
// icons
import { UserGroupIcon } from "@heroicons/react/24/outline";
import workspaceService from "services/workspace.service";
// headless ui
// ui
import { Spinner } from "components/ui";
// icons
import User from "public/user.png";
// types
import { IModule } from "types";
// constants
import { classNames } from "constants/common";
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
type Props = {
@ -59,10 +58,9 @@ const SelectMembers: React.FC<Props> = ({ control, submitChanges }) => {
<div className="relative">
<Listbox.Button className="flex w-full cursor-pointer items-center gap-1 text-xs">
<span
className={classNames(
value ? "" : "text-gray-900",
"hidden truncate text-left sm:block"
)}
className={`hidden truncate text-left sm:block ${
value ? "" : "text-gray-900"
}`}
>
<div className="flex cursor-pointer items-center gap-1 text-xs">
{value && Array.isArray(value) ? (

View file

@ -3,13 +3,12 @@ import React from "react";
// react-hook-form
import { Control, Controller, UseFormWatch } from "react-hook-form";
// ui
import { CustomSelect } from "ui";
// icons
import { Squares2X2Icon } from "@heroicons/react/24/outline";
import { CustomSelect } from "components/ui";
// icons
// types
import { IModule } from "types";
// common
import { classNames } from "constants/common";
// constants
import { MODULE_STATUS } from "constants/";
@ -19,58 +18,54 @@ type Props = {
watch: UseFormWatch<Partial<IModule>>;
};
const SelectStatus: React.FC<Props> = ({ control, submitChanges, watch }) => {
return (
<div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
<Squares2X2Icon className="h-4 w-4 flex-shrink-0" />
<p>Status</p>
</div>
<div className="sm:basis-1/2">
<Controller
control={control}
name="status"
render={({ field: { value } }) => (
<CustomSelect
label={
const SelectStatus: React.FC<Props> = ({ control, submitChanges, watch }) => (
<div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
<Squares2X2Icon className="h-4 w-4 flex-shrink-0" />
<p>Status</p>
</div>
<div className="sm:basis-1/2">
<Controller
control={control}
name="status"
render={({ field: { value } }) => (
<CustomSelect
label={
<span
className={`flex items-center gap-2 text-left capitalize ${
value ? "" : "text-gray-900"
}`}
>
<span
className={classNames(
value ? "" : "text-gray-900",
"flex items-center gap-2 text-left capitalize"
)}
>
className="h-2 w-2 flex-shrink-0 rounded-full"
style={{
backgroundColor: MODULE_STATUS?.find((option) => option.value === value)?.color,
}}
/>
{watch("status")}
</span>
}
value={value}
onChange={(value: any) => {
submitChanges({ status: value });
}}
>
{MODULE_STATUS.map((option) => (
<CustomSelect.Option key={option.value} value={option.value}>
<>
<span
className="h-2 w-2 flex-shrink-0 rounded-full"
style={{
backgroundColor: MODULE_STATUS?.find((option) => option.value === value)
?.color,
}}
></span>
{watch("status")}
</span>
}
value={value}
onChange={(value: any) => {
submitChanges({ status: value });
}}
>
{MODULE_STATUS.map((option) => (
<CustomSelect.Option key={option.value} value={option.value}>
<>
<span
className="h-2 w-2 flex-shrink-0 rounded-full"
style={{ backgroundColor: option.color }}
></span>
{option.label}
</>
</CustomSelect.Option>
))}
</CustomSelect>
)}
/>
</div>
style={{ backgroundColor: option.color }}
/>
{option.label}
</>
</CustomSelect.Option>
))}
</CustomSelect>
)}
/>
</div>
);
};
</div>
);
export default SelectStatus;

View file

@ -7,16 +7,15 @@ import { mutate } from "swr";
import { useForm } from "react-hook-form";
import { Dialog, Transition } from "@headlessui/react";
// services
import modulesService from "lib/services/modules.service";
// hooks
import useUser from "lib/hooks/useUser";
// ui
import { Button, Input } from "ui";
// types
import type { IModule, ModuleLink } from "types";
// services
import modulesService from "services/modules.service";
// ui
import { Button, Input } from "components/ui";
// fetch-keys
import { MODULE_DETAIL, MODULE_LIST } from "constants/fetch-keys";
import { MODULE_LIST } from "constants/fetch-keys";
type Props = {
isOpen: boolean;
@ -46,9 +45,7 @@ const ModuleLinkModal: React.FC<Props> = ({ isOpen, module, handleClose }) => {
const onSubmit = async (formData: ModuleLink) => {
if (!workspaceSlug || !projectId || !module) return;
const previousLinks = module.link_module.map((l) => {
return { title: l.title, url: l.url };
});
const previousLinks = module.link_module.map((l) => ({ title: l.title, url: l.url }));
const payload: Partial<IModule> = {
links_list: [...previousLinks, formData],
@ -56,7 +53,7 @@ const ModuleLinkModal: React.FC<Props> = ({ isOpen, module, handleClose }) => {
await modulesService
.patchModule(workspaceSlug as string, projectId as string, module.id, payload)
.then((res) => {
.then(() => {
mutate<IModule[]>(projectId && MODULE_LIST(projectId as string), (prevData) =>
(prevData ?? []).map((module) => {
if (module.id === moduleId) return { ...module, ...payload };

View file

@ -1,13 +1,15 @@
import React from "react";
import Link from "next/link";
import Image from "next/image";
import { useRouter } from "next/router";
// icons
import { CalendarDaysIcon } from "@heroicons/react/24/outline";
import User from "public/user.png";
// helpers
import { renderShortNumericDateFormat } from "helpers/date-time.helper";
// types
import { IModule } from "types";
// common
import { renderShortNumericDateFormat } from "constants/common";
import { CalendarDaysIcon } from "@heroicons/react/24/outline";
import { MODULE_STATUS } from "constants/";
type Props = {
@ -26,22 +28,26 @@ const SingleModuleCard: React.FC<Props> = ({ module }) => {
<div className="space-y-2">
<h6 className="text-gray-500">LEAD</h6>
<div>
{module.lead_detail?.avatar && module.lead_detail.avatar !== "" ? (
<div className="h-5 w-5 rounded-full border-2 border-white">
<Image
src={module.lead_detail.avatar}
height="100%"
width="100%"
className="rounded-full"
alt={module.lead_detail.first_name}
/>
</div>
{module.lead ? (
module.lead_detail?.avatar && module.lead_detail.avatar !== "" ? (
<div className="h-5 w-5 rounded-full border-2 border-white">
<Image
src={module.lead_detail.avatar}
height="100%"
width="100%"
className="rounded-full"
alt={module.lead_detail.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">
{module.lead_detail?.first_name && module.lead_detail.first_name !== ""
? module.lead_detail.first_name.charAt(0)
: module.lead_detail?.email.charAt(0)}
</div>
)
) : (
<div className="grid h-5 w-5 place-items-center rounded-full border-2 border-white bg-gray-700 capitalize text-white">
{module.lead_detail?.first_name && module.lead_detail.first_name !== ""
? module.lead_detail.first_name.charAt(0)
: module.lead_detail?.email.charAt(0)}
</div>
"N/A"
)}
</div>
</div>
@ -103,7 +109,7 @@ const SingleModuleCard: React.FC<Props> = ({ module }) => {
style={{
backgroundColor: MODULE_STATUS.find((s) => s.value === module.status)?.color,
}}
></span>
/>
{module.status}
</div>
</div>