forked from github/plane
style: kanban dropdowns, github integration loaders
This commit is contained in:
parent
69e8b504de
commit
b4c4271f66
@ -15,7 +15,7 @@ const Breadcrumbs = ({ children }: BreadcrumbsProps) => {
|
|||||||
<>
|
<>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div
|
<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()}
|
onClick={() => router.back()}
|
||||||
>
|
>
|
||||||
<ArrowLeftIcon className="h-3 w-3" />
|
<ArrowLeftIcon className="h-3 w-3" />
|
||||||
|
@ -226,12 +226,13 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
</h5>
|
</h5>
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</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" && (
|
{properties.priority && selectedGroup !== "priority" && (
|
||||||
<ViewPrioritySelect
|
<ViewPrioritySelect
|
||||||
issue={issue}
|
issue={issue}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
partialUpdateIssue={partialUpdateIssue}
|
||||||
isNotAllowed={isNotAllowed}
|
isNotAllowed={isNotAllowed}
|
||||||
|
selfPositioned
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{properties.state && selectedGroup !== "state_detail.name" && (
|
{properties.state && selectedGroup !== "state_detail.name" && (
|
||||||
@ -239,6 +240,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
issue={issue}
|
issue={issue}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
partialUpdateIssue={partialUpdateIssue}
|
||||||
isNotAllowed={isNotAllowed}
|
isNotAllowed={isNotAllowed}
|
||||||
|
selfPositioned
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{properties.due_date && (
|
{properties.due_date && (
|
||||||
@ -276,6 +278,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
issue={issue}
|
issue={issue}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
partialUpdateIssue={partialUpdateIssue}
|
||||||
isNotAllowed={isNotAllowed}
|
isNotAllowed={isNotAllowed}
|
||||||
|
selfPositioned
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,7 +29,7 @@ export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }
|
|||||||
{links.map((link) => (
|
{links.map((link) => (
|
||||||
<div key={link.id} className="relative">
|
<div key={link.id} className="relative">
|
||||||
{!isNotAllowed && (
|
{!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}>
|
<Link href={link.url}>
|
||||||
<a
|
<a
|
||||||
className="grid h-7 w-7 place-items-center rounded bg-gray-100 p-1 outline-none"
|
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>
|
<h5 className="w-4/5">{link.title}</h5>
|
||||||
<p className="mt-0.5 text-gray-500">
|
<p className="mt-0.5 text-gray-500">
|
||||||
Added {timeAgo(link.created_at)}
|
Added {timeAgo(link.created_at)}
|
||||||
<br />
|
{/* <br />
|
||||||
by {link.created_by_detail.email}
|
by {link.created_by_detail.email} */}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -18,14 +18,14 @@ import { PROJECT_MEMBERS } from "constants/fetch-keys";
|
|||||||
type Props = {
|
type Props = {
|
||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>) => void;
|
partialUpdateIssue: (formData: Partial<IIssue>) => void;
|
||||||
position?: "left" | "right";
|
selfPositioned?: boolean;
|
||||||
isNotAllowed: boolean;
|
isNotAllowed: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewAssigneeSelect: React.FC<Props> = ({
|
export const ViewAssigneeSelect: React.FC<Props> = ({
|
||||||
issue,
|
issue,
|
||||||
partialUpdateIssue,
|
partialUpdateIssue,
|
||||||
position = "right",
|
selfPositioned = false,
|
||||||
isNotAllowed,
|
isNotAllowed,
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -50,7 +50,7 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
|
|||||||
|
|
||||||
partialUpdateIssue({ assignees_list: newData });
|
partialUpdateIssue({ assignees_list: newData });
|
||||||
}}
|
}}
|
||||||
className="group relative flex-shrink-0"
|
className={`group ${!selfPositioned ? "relative" : ""} flex-shrink-0`}
|
||||||
disabled={isNotAllowed}
|
disabled={isNotAllowed}
|
||||||
>
|
>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
@ -86,11 +86,7 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
|
|||||||
leaveFrom="opacity-100"
|
leaveFrom="opacity-100"
|
||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<Listbox.Options
|
<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">
|
||||||
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"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{members?.map((member) => (
|
{members?.map((member) => (
|
||||||
<Listbox.Option
|
<Listbox.Option
|
||||||
key={member.member.id}
|
key={member.member.id}
|
||||||
|
@ -12,14 +12,14 @@ import { PRIORITIES } from "constants/project";
|
|||||||
type Props = {
|
type Props = {
|
||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>) => void;
|
partialUpdateIssue: (formData: Partial<IIssue>) => void;
|
||||||
position?: "left" | "right";
|
selfPositioned?: boolean;
|
||||||
isNotAllowed: boolean;
|
isNotAllowed: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewPrioritySelect: React.FC<Props> = ({
|
export const ViewPrioritySelect: React.FC<Props> = ({
|
||||||
issue,
|
issue,
|
||||||
partialUpdateIssue,
|
partialUpdateIssue,
|
||||||
position = "right",
|
selfPositioned = false,
|
||||||
isNotAllowed,
|
isNotAllowed,
|
||||||
}) => (
|
}) => (
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
@ -53,6 +53,7 @@ export const ViewPrioritySelect: React.FC<Props> = ({
|
|||||||
} border-none`}
|
} border-none`}
|
||||||
noChevron
|
noChevron
|
||||||
disabled={isNotAllowed}
|
disabled={isNotAllowed}
|
||||||
|
selfPositioned={selfPositioned}
|
||||||
>
|
>
|
||||||
{PRIORITIES?.map((priority) => (
|
{PRIORITIES?.map((priority) => (
|
||||||
<CustomSelect.Option key={priority} value={priority} className="capitalize">
|
<CustomSelect.Option key={priority} value={priority} className="capitalize">
|
||||||
|
@ -17,14 +17,14 @@ import { STATE_LIST } from "constants/fetch-keys";
|
|||||||
type Props = {
|
type Props = {
|
||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>) => void;
|
partialUpdateIssue: (formData: Partial<IIssue>) => void;
|
||||||
position?: "left" | "right";
|
selfPositioned?: boolean;
|
||||||
isNotAllowed: boolean;
|
isNotAllowed: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewStateSelect: React.FC<Props> = ({
|
export const ViewStateSelect: React.FC<Props> = ({
|
||||||
issue,
|
issue,
|
||||||
partialUpdateIssue,
|
partialUpdateIssue,
|
||||||
position = "right",
|
selfPositioned = false,
|
||||||
isNotAllowed,
|
isNotAllowed,
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -67,6 +67,7 @@ export const ViewStateSelect: React.FC<Props> = ({
|
|||||||
maxHeight="md"
|
maxHeight="md"
|
||||||
noChevron
|
noChevron
|
||||||
disabled={isNotAllowed}
|
disabled={isNotAllowed}
|
||||||
|
selfPositioned={selfPositioned}
|
||||||
>
|
>
|
||||||
{states?.map((state) => (
|
{states?.map((state) => (
|
||||||
<CustomSelect.Option key={state.id} value={state.id}>
|
<CustomSelect.Option key={state.id} value={state.id}>
|
||||||
|
@ -8,7 +8,7 @@ import workspaceService from "services/workspace.service";
|
|||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// ui
|
// ui
|
||||||
import { Button } from "components/ui";
|
import { Button, Loader } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
import GithubLogo from "public/logos/github-black.png";
|
import GithubLogo from "public/logos/github-black.png";
|
||||||
import useSWR, { mutate } from "swr";
|
import useSWR, { mutate } from "swr";
|
||||||
@ -105,7 +105,8 @@ const OAuthPopUp = ({ integration }: any) => {
|
|||||||
<div>
|
<div>
|
||||||
<h3 className="flex items-center gap-4 font-semibold text-xl">
|
<h3 className="flex items-center gap-4 font-semibold text-xl">
|
||||||
{integration.title}
|
{integration.title}
|
||||||
{isInstalled ? (
|
{workspaceIntegrations ? (
|
||||||
|
isInstalled ? (
|
||||||
<span className="flex items-center text-green-500 font-normal text-sm gap-1">
|
<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 className="h-1.5 w-1.5 bg-green-500 flex-shrink-0 rounded-full" /> Installed
|
||||||
</span>
|
</span>
|
||||||
@ -114,16 +115,21 @@ const OAuthPopUp = ({ integration }: any) => {
|
|||||||
<span className="h-1.5 w-1.5 bg-gray-400 flex-shrink-0 rounded-full" /> Not
|
<span className="h-1.5 w-1.5 bg-gray-400 flex-shrink-0 rounded-full" /> Not
|
||||||
Installed
|
Installed
|
||||||
</span>
|
</span>
|
||||||
)}
|
)
|
||||||
|
) : null}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-400 text-sm">
|
<p className="text-gray-400 text-sm">
|
||||||
{isInstalled
|
{workspaceIntegrations
|
||||||
|
? isInstalled
|
||||||
? "Activate GitHub integrations on individual projects to sync with specific repositories."
|
? "Activate GitHub integrations on individual projects to sync with specific repositories."
|
||||||
: "Connect with GitHub with your Plane workspace to sync project issues."}
|
: "Connect with GitHub with your Plane workspace to sync project issues."
|
||||||
|
: "Loading..."}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isInstalled ? (
|
|
||||||
|
{workspaceIntegrations ? (
|
||||||
|
isInstalled ? (
|
||||||
<Button
|
<Button
|
||||||
theme="danger"
|
theme="danger"
|
||||||
size="rg"
|
size="rg"
|
||||||
@ -137,6 +143,11 @@ const OAuthPopUp = ({ integration }: any) => {
|
|||||||
<Button theme="secondary" size="rg" className="text-xs" onClick={startAuth}>
|
<Button theme="secondary" size="rg" className="text-xs" onClick={startAuth}>
|
||||||
Add installation
|
Add installation
|
||||||
</Button>
|
</Button>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Loader>
|
||||||
|
<Loader.Item height="35px" width="150px" />
|
||||||
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -15,7 +15,9 @@ type CustomSelectProps = {
|
|||||||
input?: boolean;
|
input?: boolean;
|
||||||
noChevron?: boolean;
|
noChevron?: boolean;
|
||||||
buttonClassName?: string;
|
buttonClassName?: string;
|
||||||
|
optionsClassName?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
selfPositioned?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CustomSelect = ({
|
const CustomSelect = ({
|
||||||
@ -29,13 +31,15 @@ const CustomSelect = ({
|
|||||||
input = false,
|
input = false,
|
||||||
noChevron = false,
|
noChevron = false,
|
||||||
buttonClassName = "",
|
buttonClassName = "",
|
||||||
|
optionsClassName = "",
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
selfPositioned = false,
|
||||||
}: CustomSelectProps) => (
|
}: CustomSelectProps) => (
|
||||||
<Listbox
|
<Listbox
|
||||||
as="div"
|
as="div"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="relative flex-shrink-0 text-left"
|
className={`${!selfPositioned ? "relative" : ""} flex-shrink-0 text-left`}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
@ -67,8 +71,8 @@ const CustomSelect = ({
|
|||||||
leaveTo="transform opacity-0 scale-95"
|
leaveTo="transform opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Listbox.Options
|
<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 ${
|
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" : "w-56"
|
width === "auto" ? "min-w-full whitespace-nowrap" : width
|
||||||
} ${input ? "max-h-48" : ""} ${
|
} ${input ? "max-h-48" : ""} ${
|
||||||
maxHeight === "lg"
|
maxHeight === "lg"
|
||||||
? "max-h-60"
|
? "max-h-60"
|
||||||
@ -97,9 +101,9 @@ const Option: React.FC<OptionProps> = ({ children, value, className }) => (
|
|||||||
<Listbox.Option
|
<Listbox.Option
|
||||||
value={value}
|
value={value}
|
||||||
className={({ active, selected }) =>
|
className={({ active, selected }) =>
|
||||||
`${active || selected ? "bg-indigo-50" : ""} ${
|
`${className} ${active || selected ? "bg-indigo-50" : ""} ${
|
||||||
selected ? "font-medium" : ""
|
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}
|
{children}
|
||||||
|
@ -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 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) => {
|
export const createSimilarString = (str: string) => {
|
||||||
const shuffled = str
|
const shuffled = str
|
||||||
.split("")
|
.split("")
|
||||||
|
@ -23,6 +23,8 @@ import projectService from "services/project.service";
|
|||||||
// ui
|
// ui
|
||||||
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
|
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
|
||||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||||
|
// helpers
|
||||||
|
import { truncateText } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { CycleIssueResponse, UserAuth } from "types";
|
import { CycleIssueResponse, UserAuth } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
@ -135,7 +137,7 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
|
|||||||
label={
|
label={
|
||||||
<>
|
<>
|
||||||
<CyclesIcon className="h-3 w-3" />
|
<CyclesIcon className="h-3 w-3" />
|
||||||
{cycleDetails?.name}
|
{cycleDetails?.name && truncateText(cycleDetails.name, 40)}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
className="ml-1.5"
|
className="ml-1.5"
|
||||||
@ -147,7 +149,7 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
|
|||||||
renderAs="a"
|
renderAs="a"
|
||||||
href={`/${workspaceSlug}/projects/${activeProject?.id}/cycles/${cycle.id}`}
|
href={`/${workspaceSlug}/projects/${activeProject?.id}/cycles/${cycle.id}`}
|
||||||
>
|
>
|
||||||
{cycle.name}
|
{truncateText(cycle.name, 40)}
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
))}
|
))}
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
|
@ -27,6 +27,8 @@ import { ModuleDetailsSidebar } from "components/modules";
|
|||||||
// ui
|
// ui
|
||||||
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
|
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
|
||||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||||
|
// helpers
|
||||||
|
import { truncateText } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { IModule, ModuleIssueResponse, UserAuth } from "types";
|
import { IModule, ModuleIssueResponse, UserAuth } from "types";
|
||||||
|
|
||||||
@ -130,7 +132,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
|
|||||||
label={
|
label={
|
||||||
<>
|
<>
|
||||||
<RectangleGroupIcon className="h-3 w-3" />
|
<RectangleGroupIcon className="h-3 w-3" />
|
||||||
{moduleDetails?.name}
|
{moduleDetails?.name && truncateText(moduleDetails.name, 40)}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
className="ml-1.5"
|
className="ml-1.5"
|
||||||
@ -142,7 +144,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
|
|||||||
renderAs="a"
|
renderAs="a"
|
||||||
href={`/${workspaceSlug}/projects/${projectId}/modules/${module.id}`}
|
href={`/${workspaceSlug}/projects/${projectId}/modules/${module.id}`}
|
||||||
>
|
>
|
||||||
{module.name}
|
{truncateText(module.name, 40)}
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
))}
|
))}
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
|
@ -13,6 +13,7 @@ import AppLayout from "layouts/app-layout";
|
|||||||
// componentss
|
// componentss
|
||||||
import OAuthPopUp from "components/popup";
|
import OAuthPopUp from "components/popup";
|
||||||
// ui
|
// ui
|
||||||
|
import { Loader } from "components/ui";
|
||||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||||
// types
|
// types
|
||||||
import type { NextPage, GetServerSideProps } from "next";
|
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>
|
<p className="mt-4 text-sm text-gray-500">Manage the workspace integrations.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{integrations?.map((integration) => (
|
{integrations ? (
|
||||||
|
integrations.map((integration) => (
|
||||||
<OAuthPopUp
|
<OAuthPopUp
|
||||||
key={integration.id}
|
key={integration.id}
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
integration={integration}
|
integration={integration}
|
||||||
/>
|
/>
|
||||||
))}
|
))
|
||||||
|
) : (
|
||||||
|
<Loader className="space-y-5">
|
||||||
|
<Loader.Item height="60px" />
|
||||||
|
<Loader.Item height="60px" />
|
||||||
|
</Loader>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
|
Loading…
Reference in New Issue
Block a user