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

responsive: shiftingDetails page and Breadcrumbs added phone and desktop #8933

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
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
200 changes: 112 additions & 88 deletions src/components/Common/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
import { usePath, Link } from "raviger";
import { useState } from "react";
import { classNames } from "../../Utils/utils";
import { Button } from "@/components/ui/button";
import CareIcon from "../../CAREUI/icons/CareIcon";
import useAppHistory from "@/common/hooks/useAppHistory";
import { FaHospital } from "react-icons/fa";
import { FaFile } from "react-icons/fa6";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbList,
BreadcrumbSeparator,
BreadcrumbPage,
} from "@/components/ui/breadcrumb";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useState } from "react";

const MENU_TAGS: { [key: string]: string } = {
facility: "Facilities",
Expand All @@ -16,6 +30,11 @@ const MENU_TAGS: { [key: string]: string } = {
notice_board: "Notice Board",
};

const iconMapping: { [key: string]: JSX.Element } = {
"Critical Care": <FaHospital className="mr-2" />,
Assets: <FaFile className="mr-2" />,
};

const capitalize = (string: string) =>
string
.replace(/[_-]/g, " ")
Expand All @@ -42,106 +61,111 @@ export default function Breadcrumbs({
}: BreadcrumbsProps) {
const { goBack } = useAppHistory();
const path = usePath();
const [showFullPath, setShowFullPath] = useState(false);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);

const crumbs = path
?.slice(1)
.split("/")
.map((field, i) => ({
name: replacements[field]?.name || MENU_TAGS[field] || capitalize(field),
uri:
replacements[field]?.uri ||
path
.split("/")
.slice(0, i + 2)
.join("/"),
style: replacements[field]?.style || "",
}));
const crumbs =
path
?.slice(1)
.split("/")
.map((field, i) => ({
name:
replacements[field]?.name || MENU_TAGS[field] || capitalize(field),
uri:
replacements[field]?.uri ||
path
.split("/")
.slice(0, i + 2)
.join("/"),
})) || [];

const renderCrumb = (crumb: any, index: number) => {
const isLastItem = index === crumbs!.length - 1;
return (
<li
key={crumb.name}
className={classNames("text-sm font-normal", crumb.style)}
>
return (
<Breadcrumb className={className}>
<BreadcrumbList>
<div className="flex items-center">
<CareIcon icon="l-angle-right" className="h-4 text-gray-400" />
{isLastItem ? (
<span className="text-gray-500">{crumb.name}</span>
) : (
<Button
asChild
variant="link"
className="p-1 font-light text-gray-500 underline underline-offset-2 hover:text-gray-700"
>
<Link href={crumb.uri}>{crumb.name}</Link>
</Button>
{!hideBack && (
<BreadcrumbItem>
<Button
variant="link"
className="px-1 text-sm font-normal text-gray-500 underline underline-offset-2"
size="sm"
onClick={() => {
if (onBackClick && onBackClick() === false) return;
goBack(backUrl);
}}
>
<CareIcon
icon="l-angle-left"
className="-ml-2 h-4 text-gray-400"
/>
Back
</Button>
</BreadcrumbItem>
)}
</div>
</li>
);
};

return (
<nav className={classNames("w-full", className)} aria-label="Breadcrumb">
<ol className="flex flex-wrap items-center">
{!hideBack && (
<li className="mr-1 flex items-center">
<Button
variant="link"
className="px-1 text-sm font-normal text-gray-500 underline underline-offset-2"
size="xs"
onClick={() => {
if (onBackClick && onBackClick() === false) return;
goBack(backUrl);
}}
>
<CareIcon
icon="l-angle-left"
className="-ml-2 h-4 text-gray-400"
/>
<span className="pr-1">Back</span>
</Button>
<span className="text-xs font-light text-gray-400 no-underline">
{!hideBack && (
<span className="ml-1 mr-2 text-xs font-light text-gray-400 no-underline">
|
</span>
</li>
)}
<li>
<Button
asChild
variant="link"
className="p-1 font-light text-gray-500 underline underline-offset-2 hover:text-gray-700"
>
<Link href="/">Home</Link>
</Button>
</li>
{crumbs && crumbs.length > 1 && (
)}

<BreadcrumbItem>
<Link
href="/"
className="font-light text-gray-500 underline underline-offset-2 hover:text-gray-700"
>
Home
</Link>
{crumbs.length > 2 ? null : <BreadcrumbSeparator />}
</BreadcrumbItem>
</div>
{crumbs.length > 2 && (
<>
{!showFullPath && (
<li>
<div className="flex items-center">
<CareIcon
icon="l-angle-right"
className="h-4 text-gray-400"
/>
<BreadcrumbSeparator />
<BreadcrumbItem>
<DropdownMenu
open={isDropdownOpen}
onOpenChange={setIsDropdownOpen}
>
<DropdownMenuTrigger asChild>
<Button
variant="link"
className="h-auto p-0 font-light text-gray-500 hover:text-gray-700"
onClick={() => setShowFullPath(true)}
className="border-0 p-0 font-normal text-gray-500 ring-0 hover:text-gray-700 focus-visible:ring-0 focus-visible:ring-offset-0"
>
•••
</Button>
</div>
</li>
)}
{showFullPath && crumbs.slice(0, -1).map(renderCrumb)}
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
{crumbs.slice(1, -1).map((crumb) => (
<DropdownMenuItem key={crumb.uri}>
<Link
href={crumb.uri}
className="flex items-center text-gray-500"
>
{iconMapping[crumb.name] || null}
{crumb.name}
</Link>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</BreadcrumbItem>
<BreadcrumbSeparator />
</>
)}
{crumbs?.length &&
renderCrumb(crumbs[crumbs.length - 1], crumbs.length - 1)}
</ol>
</nav>

{crumbs.length > 2 && (
<BreadcrumbPage className="text-gray-500">
<span className="hidden md:inline">
{crumbs[crumbs.length - 1]?.name}
</span>
<span className="md:hidden">
{crumbs[crumbs.length - 1]?.name.length > 10
? `${crumbs[crumbs.length - 1]?.name.slice(0, 10)}...`
: crumbs[crumbs.length - 1]?.name}
</span>
</BreadcrumbPage>
)}
</BreadcrumbList>
</Breadcrumb>
);
}
2 changes: 1 addition & 1 deletion src/components/Shifting/ShiftDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ export default function ShiftDetails(props: { id: string }) {
title={t("shifting_details")}
backUrl="/shifting/board"
options={
<div className="flex gap-2">
<div className="flex flex-col gap-2 md:flex-row">
<ButtonV2
tooltip={
["COMPLETED", "CANCELLED"].includes(data?.status || "")
Expand Down
118 changes: 118 additions & 0 deletions src/components/ui/breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import * as React from "react";
import { ChevronRightIcon, DotsHorizontalIcon } from "@radix-ui/react-icons";
import { Slot } from "@radix-ui/react-slot";

import { cn } from "@/lib/utils";

const Breadcrumb = React.forwardRef<
HTMLElement,
React.ComponentPropsWithoutRef<"nav"> & {
separator?: React.ReactNode;
}
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />);
Breadcrumb.displayName = "Breadcrumb";

const BreadcrumbList = React.forwardRef<
HTMLOListElement,
React.ComponentPropsWithoutRef<"ol">
>(({ className, ...props }, ref) => (
<ol
ref={ref}
className={cn(
"flex flex-wrap items-center gap-0.5 break-words text-sm text-gray-500 dark:text-gray-400 sm:gap-0.5",
className,
)}
{...props}
/>
));
BreadcrumbList.displayName = "BreadcrumbList";

const BreadcrumbItem = React.forwardRef<
HTMLLIElement,
React.ComponentPropsWithoutRef<"li">
>(({ className, ...props }, ref) => (
<li
ref={ref}
className={cn("inline-flex items-center gap-1.5", className)}
{...props}
/>
));
BreadcrumbItem.displayName = "BreadcrumbItem";

const BreadcrumbLink = React.forwardRef<
HTMLAnchorElement,
React.ComponentPropsWithoutRef<"a"> & {
asChild?: boolean;
}
>(({ asChild, className, ...props }, ref) => {
const Comp = asChild ? Slot : "a";

return (
<Comp
ref={ref}
className={cn(
"transition-colors hover:text-gray-950 dark:hover:text-gray-50",
className,
)}
{...props}
/>
);
});
BreadcrumbLink.displayName = "BreadcrumbLink";

const BreadcrumbPage = React.forwardRef<
HTMLSpanElement,
React.ComponentPropsWithoutRef<"span">
>(({ className, ...props }, ref) => (
<span
ref={ref}
role="link"
aria-disabled="true"
aria-current="page"
className={cn("font-normal text-gray-950 dark:text-gray-50", className)}
{...props}
/>
));
BreadcrumbPage.displayName = "BreadcrumbPage";

const BreadcrumbSeparator = ({
children,
className,
...props
}: React.ComponentProps<"li">) => (
<li
role="presentation"
aria-hidden="true"
className={cn("[&>svg]:h-3.5 [&>svg]:w-3.5", className)}
{...props}
>
{children ?? <ChevronRightIcon />}
</li>
);
BreadcrumbSeparator.displayName = "BreadcrumbSeparator";

const BreadcrumbEllipsis = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
role="presentation"
aria-hidden="true"
className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props}
>
<DotsHorizontalIcon className="h-4 w-4" />
<span className="sr-only">More</span>
</span>
);
BreadcrumbEllipsis.displayName = "BreadcrumbElipssis";

export {
Breadcrumb,
BreadcrumbList,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbPage,
BreadcrumbSeparator,
BreadcrumbEllipsis,
};