style: kanban dropdowns, github integration loaders

This commit is contained in:
Aaryan Khandelwal 2023-02-23 22:34:36 +05:30
parent 69e8b504de
commit b4c4271f66
12 changed files with 91 additions and 60 deletions

View File

@ -15,7 +15,7 @@ const Breadcrumbs = ({ children }: BreadcrumbsProps) => {
<>
<div className="flex items-center">
<div
className="grid h-8 w-8 cursor-pointer place-items-center rounded border border-gray-300 text-center text-sm hover:bg-gray-100"
className="grid h-8 w-8 cursor-pointer place-items-center flex-shrink-0 rounded border border-gray-300 text-center text-sm hover:bg-gray-100"
onClick={() => router.back()}
>
<ArrowLeftIcon className="h-3 w-3" />

View File

@ -226,12 +226,13 @@ export const SingleBoardIssue: React.FC<Props> = ({
</h5>
</a>
</Link>
<div className="flex flex-wrap items-center gap-x-1 gap-y-2 text-xs">
<div className="relative flex flex-wrap items-center gap-x-1 gap-y-2 text-xs">
{properties.priority && selectedGroup !== "priority" && (
<ViewPrioritySelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed}
selfPositioned
/>
)}
{properties.state && selectedGroup !== "state_detail.name" && (
@ -239,6 +240,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed}
selfPositioned
/>
)}
{properties.due_date && (
@ -276,6 +278,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed}
selfPositioned
/>
)}
</div>

View File

@ -29,7 +29,7 @@ export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }
{links.map((link) => (
<div key={link.id} className="relative">
{!isNotAllowed && (
<div className="absolute top-1.5 right-1.5 z-10 flex items-center gap-1">
<div className="absolute top-1.5 right-1.5 z-[1] flex items-center gap-1">
<Link href={link.url}>
<a
className="grid h-7 w-7 place-items-center rounded bg-gray-100 p-1 outline-none"
@ -56,8 +56,8 @@ export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }
<h5 className="w-4/5">{link.title}</h5>
<p className="mt-0.5 text-gray-500">
Added {timeAgo(link.created_at)}
<br />
by {link.created_by_detail.email}
{/* <br />
by {link.created_by_detail.email} */}
</p>
</div>
</a>

View File

@ -18,14 +18,14 @@ import { PROJECT_MEMBERS } from "constants/fetch-keys";
type Props = {
issue: IIssue;
partialUpdateIssue: (formData: Partial<IIssue>) => void;
position?: "left" | "right";
selfPositioned?: boolean;
isNotAllowed: boolean;
};
export const ViewAssigneeSelect: React.FC<Props> = ({
issue,
partialUpdateIssue,
position = "right",
selfPositioned = false,
isNotAllowed,
}) => {
const router = useRouter();
@ -50,7 +50,7 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
partialUpdateIssue({ assignees_list: newData });
}}
className="group relative flex-shrink-0"
className={`group ${!selfPositioned ? "relative" : ""} flex-shrink-0`}
disabled={isNotAllowed}
>
{({ open }) => (
@ -86,11 +86,7 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
className={`absolute z-10 mt-1 max-h-48 overflow-auto rounded-md bg-white py-1 text-xs shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none ${
position === "left" ? "left-0" : "right-0"
}`}
>
<Listbox.Options className="absolute right-0 z-10 mt-1 max-h-48 overflow-auto rounded-md bg-white py-1 text-xs shadow-lg min-w-full ring-1 ring-black ring-opacity-5 focus:outline-none">
{members?.map((member) => (
<Listbox.Option
key={member.member.id}

View File

@ -12,14 +12,14 @@ import { PRIORITIES } from "constants/project";
type Props = {
issue: IIssue;
partialUpdateIssue: (formData: Partial<IIssue>) => void;
position?: "left" | "right";
selfPositioned?: boolean;
isNotAllowed: boolean;
};
export const ViewPrioritySelect: React.FC<Props> = ({
issue,
partialUpdateIssue,
position = "right",
selfPositioned = false,
isNotAllowed,
}) => (
<CustomSelect
@ -53,6 +53,7 @@ export const ViewPrioritySelect: React.FC<Props> = ({
} border-none`}
noChevron
disabled={isNotAllowed}
selfPositioned={selfPositioned}
>
{PRIORITIES?.map((priority) => (
<CustomSelect.Option key={priority} value={priority} className="capitalize">

View File

@ -17,14 +17,14 @@ import { STATE_LIST } from "constants/fetch-keys";
type Props = {
issue: IIssue;
partialUpdateIssue: (formData: Partial<IIssue>) => void;
position?: "left" | "right";
selfPositioned?: boolean;
isNotAllowed: boolean;
};
export const ViewStateSelect: React.FC<Props> = ({
issue,
partialUpdateIssue,
position = "right",
selfPositioned = false,
isNotAllowed,
}) => {
const router = useRouter();
@ -67,6 +67,7 @@ export const ViewStateSelect: React.FC<Props> = ({
maxHeight="md"
noChevron
disabled={isNotAllowed}
selfPositioned={selfPositioned}
>
{states?.map((state) => (
<CustomSelect.Option key={state.id} value={state.id}>

View File

@ -8,7 +8,7 @@ import workspaceService from "services/workspace.service";
// hooks
import useToast from "hooks/use-toast";
// ui
import { Button } from "components/ui";
import { Button, Loader } from "components/ui";
// icons
import GithubLogo from "public/logos/github-black.png";
import useSWR, { mutate } from "swr";
@ -105,38 +105,49 @@ const OAuthPopUp = ({ integration }: any) => {
<div>
<h3 className="flex items-center gap-4 font-semibold text-xl">
{integration.title}
{isInstalled ? (
<span className="flex items-center text-green-500 font-normal text-sm gap-1">
<span className="h-1.5 w-1.5 bg-green-500 flex-shrink-0 rounded-full" /> Installed
</span>
) : (
<span className="flex items-center text-gray-400 font-normal text-sm gap-1">
<span className="h-1.5 w-1.5 bg-gray-400 flex-shrink-0 rounded-full" /> Not
Installed
</span>
)}
{workspaceIntegrations ? (
isInstalled ? (
<span className="flex items-center text-green-500 font-normal text-sm gap-1">
<span className="h-1.5 w-1.5 bg-green-500 flex-shrink-0 rounded-full" /> Installed
</span>
) : (
<span className="flex items-center text-gray-400 font-normal text-sm gap-1">
<span className="h-1.5 w-1.5 bg-gray-400 flex-shrink-0 rounded-full" /> Not
Installed
</span>
)
) : null}
</h3>
<p className="text-gray-400 text-sm">
{isInstalled
? "Activate GitHub integrations on individual projects to sync with specific repositories."
: "Connect with GitHub with your Plane workspace to sync project issues."}
{workspaceIntegrations
? isInstalled
? "Activate GitHub integrations on individual projects to sync with specific repositories."
: "Connect with GitHub with your Plane workspace to sync project issues."
: "Loading..."}
</p>
</div>
</div>
{isInstalled ? (
<Button
theme="danger"
size="rg"
className="text-xs"
onClick={handleRemoveIntegration}
disabled={deletingIntegration}
>
{deletingIntegration ? "Removing..." : "Remove installation"}
</Button>
{workspaceIntegrations ? (
isInstalled ? (
<Button
theme="danger"
size="rg"
className="text-xs"
onClick={handleRemoveIntegration}
disabled={deletingIntegration}
>
{deletingIntegration ? "Removing..." : "Remove installation"}
</Button>
) : (
<Button theme="secondary" size="rg" className="text-xs" onClick={startAuth}>
Add installation
</Button>
)
) : (
<Button theme="secondary" size="rg" className="text-xs" onClick={startAuth}>
Add installation
</Button>
<Loader>
<Loader.Item height="35px" width="150px" />
</Loader>
)}
</div>
);

View File

@ -15,7 +15,9 @@ type CustomSelectProps = {
input?: boolean;
noChevron?: boolean;
buttonClassName?: string;
optionsClassName?: string;
disabled?: boolean;
selfPositioned?: boolean;
};
const CustomSelect = ({
@ -29,13 +31,15 @@ const CustomSelect = ({
input = false,
noChevron = false,
buttonClassName = "",
optionsClassName = "",
disabled = false,
selfPositioned = false,
}: CustomSelectProps) => (
<Listbox
as="div"
value={value}
onChange={onChange}
className="relative flex-shrink-0 text-left"
className={`${!selfPositioned ? "relative" : ""} flex-shrink-0 text-left`}
disabled={disabled}
>
<div>
@ -67,8 +71,8 @@ const CustomSelect = ({
leaveTo="transform opacity-0 scale-95"
>
<Listbox.Options
className={`absolute right-0 z-10 mt-1 origin-top-right overflow-y-auto rounded-md bg-white text-xs shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none ${
width === "auto" ? "min-w-full whitespace-nowrap" : "w-56"
className={`${optionsClassName} absolute right-0 z-10 mt-1 origin-top-right overflow-y-auto rounded-md bg-white text-xs shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none ${
width === "auto" ? "min-w-full whitespace-nowrap" : width
} ${input ? "max-h-48" : ""} ${
maxHeight === "lg"
? "max-h-60"
@ -97,9 +101,9 @@ const Option: React.FC<OptionProps> = ({ children, value, className }) => (
<Listbox.Option
value={value}
className={({ active, selected }) =>
`${active || selected ? "bg-indigo-50" : ""} ${
`${className} ${active || selected ? "bg-indigo-50" : ""} ${
selected ? "font-medium" : ""
} relative flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900 ${className}`
} relative flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900`
}
>
{children}

View File

@ -4,6 +4,9 @@ export const replaceUnderscoreIfSnakeCase = (str: string) => str.replace(/_/g, "
export const capitalizeFirstLetter = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
export const truncateText = (str: string, length: number) =>
str.length > length ? `${str.substring(0, length)}...` : str;
export const createSimilarString = (str: string) => {
const shuffled = str
.split("")

View File

@ -23,6 +23,8 @@ import projectService from "services/project.service";
// ui
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
// helpers
import { truncateText } from "helpers/string.helper";
// types
import { CycleIssueResponse, UserAuth } from "types";
// fetch-keys
@ -135,7 +137,7 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
label={
<>
<CyclesIcon className="h-3 w-3" />
{cycleDetails?.name}
{cycleDetails?.name && truncateText(cycleDetails.name, 40)}
</>
}
className="ml-1.5"
@ -147,7 +149,7 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
renderAs="a"
href={`/${workspaceSlug}/projects/${activeProject?.id}/cycles/${cycle.id}`}
>
{cycle.name}
{truncateText(cycle.name, 40)}
</CustomMenu.MenuItem>
))}
</CustomMenu>

View File

@ -27,6 +27,8 @@ import { ModuleDetailsSidebar } from "components/modules";
// ui
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
// helpers
import { truncateText } from "helpers/string.helper";
// types
import { IModule, ModuleIssueResponse, UserAuth } from "types";
@ -130,7 +132,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
label={
<>
<RectangleGroupIcon className="h-3 w-3" />
{moduleDetails?.name}
{moduleDetails?.name && truncateText(moduleDetails.name, 40)}
</>
}
className="ml-1.5"
@ -142,7 +144,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
renderAs="a"
href={`/${workspaceSlug}/projects/${projectId}/modules/${module.id}`}
>
{module.name}
{truncateText(module.name, 40)}
</CustomMenu.MenuItem>
))}
</CustomMenu>

View File

@ -13,6 +13,7 @@ import AppLayout from "layouts/app-layout";
// componentss
import OAuthPopUp from "components/popup";
// ui
import { Loader } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
// types
import type { NextPage, GetServerSideProps } from "next";
@ -54,13 +55,20 @@ const WorkspaceIntegrations: NextPage<UserAuth> = (props) => {
<p className="mt-4 text-sm text-gray-500">Manage the workspace integrations.</p>
</div>
<div className="space-y-4">
{integrations?.map((integration) => (
<OAuthPopUp
key={integration.id}
workspaceSlug={workspaceSlug}
integration={integration}
/>
))}
{integrations ? (
integrations.map((integration) => (
<OAuthPopUp
key={integration.id}
workspaceSlug={workspaceSlug}
integration={integration}
/>
))
) : (
<Loader className="space-y-5">
<Loader.Item height="60px" />
<Loader.Item height="60px" />
</Loader>
)}
</div>
</section>
</AppLayout>