Skip to content

Commit

Permalink
Merge pull request #1441 from rockingrohit9639/refactor/sidebar
Browse files Browse the repository at this point in the history
Refactor/sidebar
  • Loading branch information
DonKoko authored Dec 10, 2024
2 parents 9214caa + 1756f7b commit 564ee85
Show file tree
Hide file tree
Showing 40 changed files with 2,235 additions and 803 deletions.
12 changes: 5 additions & 7 deletions app/components/assets/assets-index/asset-index-pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState } from "react";
import { useFetcher, useRouteLoaderData } from "@remix-run/react";
import { useFetcher } from "@remix-run/react";
import { AlertIcon, ChevronRight } from "~/components/icons/library";
import { useSidebar } from "~/components/layout/sidebar/sidebar";
import {
AlertDialog,
AlertDialogCancel,
Expand All @@ -16,7 +17,6 @@ import { useAssetIndexViewState } from "~/hooks/use-asset-index-view-state";

import { useViewportHeight } from "~/hooks/use-viewport-height";
import { useUserRoleHelper } from "~/hooks/user-user-role-helper";
import type { loader as layoutLoader } from "~/routes/_layout+/_layout";
import {
PermissionAction,
PermissionEntity,
Expand All @@ -29,11 +29,9 @@ import { ButtonGroup } from "../../shared/button-group";

export function AssetIndexPagination() {
const { roles } = useUserRoleHelper();
let minimizedSidebar = useRouteLoaderData<typeof layoutLoader>(
"routes/_layout+/_layout"
)?.minimizedSidebar;
const fetcher = useFetcher({ key: "asset-index-settings-mode" });
const { isMd } = useViewportHeight();
const { state } = useSidebar();

const { modeIsSimple, modeIsAdvanced } = useAssetIndexViewState();
const disabledButtonStyles =
Expand All @@ -57,9 +55,9 @@ export function AssetIndexPagination() {
return (
<div
className={tw(
"asset-index-pagination flex flex-col items-center justify-between border-t border-gray-200 bg-white transition-all delay-75 ease-in-out md:flex-row",
isMd ? "fixed bottom-0 right-0 z-[12]" : "",
"asset-index-pagination flex flex-col items-center justify-between border-t border-gray-200 bg-white md:flex-row ",
minimizedSidebar ? "lg:left-[82px]" : "lg:left-[312px]"
state === "collapsed" ? "lg:left-[48px]" : "lg:left-[256px]"
)}
>
<Pagination className="px-4 py-[6px]" />
Expand Down
1 change: 1 addition & 0 deletions app/components/errors/error-404-handler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default function Error404Handler({
case "asset":
case "kit":
case "location":

case "booking":
case "customField": {
const modelLabel = getModelLabelForEnumValue(additionalData.model);
Expand Down
48 changes: 41 additions & 7 deletions app/components/errors/utils.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,70 @@
import { z } from "zod";
import { isRouteError } from "~/utils/http";

/**
* Base schema for additional error data.
* Contains common fields used across different error types.
*/
const baseAdditionalDataSchema = z.object({
/** Unique identifier for the error instance */
id: z.string(),
/** Optional URL to redirect the user to after error handling */
redirectTo: z.string().optional(),
});

/**
* Schema defining organization structure used in error data
*/
const organizationSchema = z.object({
organization: z.object({
/** Organization's unique identifier */
id: z.string(),
/** Organization's display name */
name: z.string(),
}),
});

/**
* Schema for 404 error additional data.
* Uses a discriminated union to handle different model types with specific requirements.
*/
export const error404AdditionalDataSchema = z.discriminatedUnion("model", [
/* For common and general use case */
baseAdditionalDataSchema.extend({
/** Type of resource that wasn't found */
model: z.enum(["asset", "kit", "location", "booking", "customField"]),
/** Organization context where the resource wasn't found */
organization: organizationSchema,
}),
/* A team member (user) can be in multiple organization's of user so we do this. */
baseAdditionalDataSchema.extend({
/** Specific case for team member not found errors */
model: z.literal("teamMember"),
/** List of organizations the team member could belong to */
organizations: organizationSchema.array(),
}),
]);

/**
* Type definition for the 404 error additional data structure
*/
export type Error404AdditionalData = z.infer<
typeof error404AdditionalDataSchema
>;

export function parse404ErrorData(response: unknown):
/**
* Parses and validates the structure of a 404 error response.
*
* @param response - The unknown response to be parsed
* @returns An object indicating whether it's a valid 404 error and its additional data
* If it's not a valid 404 error or parsing fails, returns {isError404: false, additionalData: null}
* If it's a valid 404 error, returns {isError404: true, additionalData: Error404AdditionalData}
*/
export function parse404ErrorData(
response: unknown
):
| { isError404: false; additionalData: null }
| {
isError404: true;
additionalData: Error404AdditionalData;
} {
| { isError404: true; additionalData: Error404AdditionalData } {
if (!isRouteError(response)) {
return { isError404: false, additionalData: null };
}
Expand All @@ -51,12 +80,17 @@ export function parse404ErrorData(response: unknown):
return { isError404: true, additionalData: parsedDataResponse.data };
}

/**
* Converts a model enum value to a human-readable label.
*
* @param model - The model type from Error404AdditionalData
* @returns A string representing the human-readable label for the model
*/
export function getModelLabelForEnumValue(
model: Error404AdditionalData["model"]
) {
): string {
if (model === "customField") {
return "Custom field";
}

return model;
}
8 changes: 6 additions & 2 deletions app/components/icons/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@ import type { IconType } from "../shared/icons-map";
import iconsMap from "../shared/icons-map";

export interface IconProps {
className?: string;
icon: IconType;
disableWrap?: true;
size?: React.ComponentProps<typeof IconHug>["size"];
}
const Icon = React.forwardRef<HTMLElement, IconProps>(function Icon(
{ icon, disableWrap }: IconProps,
{ className, icon, disableWrap, size = "sm" }: IconProps,
_ref
) {
return (
icon &&
(disableWrap ? (
<div>{iconsMap[icon]}</div>
) : (
<IconHug size="sm">{iconsMap[icon]}</IconHug>
<IconHug className={className} size={size}>
{iconsMap[icon]}
</IconHug>
))
);
});
Expand Down
4 changes: 3 additions & 1 deletion app/components/icons/iconHug.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { tw } from "~/utils/tw";

interface Props {
/** Size of the hug. Defualt is sm */
size: "sm" | "md" | "lg" | "xl" | "2xl";
size: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";

children: JSX.Element | JSX.Element[];

Expand All @@ -16,6 +16,8 @@ interface Props {
const sizeClasses: {
[key in Props["size"]]: string;
} = {
/** 16 */
xs: "w-4 h-4",
/** 32px */
sm: "w-5 h-5",
/** 40px */
Expand Down
36 changes: 19 additions & 17 deletions app/components/icons/library.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ export function ItemsIcon(props: SVGProps<SVGSVGElement>) {
export function SettingsIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
width="100%"
height="100%"
width="20"
height="20"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
Expand Down Expand Up @@ -436,8 +436,9 @@ export function XIcon(props: SVGProps<SVGSVGElement>) {

export const AssetsIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
width={20}
height={22}
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
Expand Down Expand Up @@ -854,8 +855,9 @@ export const WriteIcon = (props: SVGProps<SVGSVGElement>) => (
export const TagsIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={20}
height={23}
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
{...props}
>
Expand Down Expand Up @@ -923,9 +925,9 @@ export const RemoveTagsIcon = (props: SVGProps<SVGSVGElement>) => (

export const LocationMarkerIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
width="18"
height="22"
viewBox="0 0 18 22"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
Expand Down Expand Up @@ -1454,9 +1456,9 @@ export const CalendarIcon = (props: SVGProps<SVGSVGElement>) => (
export const BookingsIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 20 22"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
{...props}
>
Expand Down Expand Up @@ -1491,8 +1493,8 @@ export const CustomFiedIcon = (props: SVGProps<SVGSVGElement>) => (

export const KitIcon = (props: SVGProps<SVGSVGElement>) => (
<svg
width="100%"
height="100%"
width="18"
height="20"
viewBox="0 0 22 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
Expand Down Expand Up @@ -1572,9 +1574,9 @@ export const AssetLabel = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
width="100%"
height="100%"
viewBox="0 0 24 24"
width="18"
height="20"
viewBox="0 0 22 20"
{...props}
>
<path
Expand Down
44 changes: 44 additions & 0 deletions app/components/layout/sidebar/app-sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ShelfSidebarLogo } from "~/components/marketing/logos";
import { useSidebarNavItems } from "~/hooks/use-sidebar-nav-items";
import { SidebarNoticeCard } from "./notice-card";
import OrganizationSelector from "./organization-selector";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarHeader,
SidebarRail,
useSidebar,
} from "./sidebar";
import SidebarNav from "./sidebar-nav";
import SidebarUserMenu from "./sidebar-user-menu";

type AppSidebarProps = React.ComponentProps<typeof Sidebar>;

export default function AppSidebar(props: AppSidebarProps) {
const { state } = useSidebar();
const { topMenuItems, bottomMenuItems } = useSidebarNavItems();

return (
<Sidebar collapsible="icon" {...props}>
<SidebarHeader className={state === "collapsed" ? "px-0" : ""}>
<div className="my-2 flex items-center">
<ShelfSidebarLogo minimized={state === "collapsed"} />
</div>

<OrganizationSelector />
</SidebarHeader>

<SidebarContent>
<SidebarNav items={topMenuItems} />
</SidebarContent>

<SidebarFooter>
<SidebarNoticeCard />
<SidebarNav className="p-0" items={bottomMenuItems} />
<SidebarUserMenu />
</SidebarFooter>
<SidebarRail />
</Sidebar>
);
}
31 changes: 0 additions & 31 deletions app/components/layout/sidebar/atoms.ts

This file was deleted.

Loading

0 comments on commit 564ee85

Please sign in to comment.