Merge pull request 'sidebar-issues' (#35) from sidebar-issues into develop

Reviewed-on: https://repo.ravanertebat.com/TM/tm_panel/pulls/35
Reviewed-by: Nima Nakhsotin <n.nakhostin@ravanertebat.com>
This commit is contained in:
Nima Nakhsotin
2025-09-23 13:36:35 +03:30
11 changed files with 65 additions and 141 deletions
@@ -7,8 +7,8 @@ interface IProps {}
const CreateCompanyCategoryPage = ({}: IProps) => { const CreateCompanyCategoryPage = ({}: IProps) => {
const breadcrumbItems: BreadcrumbItem[] = [ const breadcrumbItems: BreadcrumbItem[] = [
{ href: "/", name: "Dashboard" }, { href: "/", name: "Dashboard" },
{ href: "/companies/categories", name: "Company Categories" }, { href: "/company-categories", name: "Company Categories" },
{ href: "/companies/categories/create", name: "Create Company Category" }, { href: "/company-categories/create", name: "Create Company Category" },
]; ];
return ( return (
<> <>
@@ -15,8 +15,8 @@ const EditCompanyCategory = ({
const { isLoading, data } = useCompanyCategoriesDetails(id); const { isLoading, data } = useCompanyCategoriesDetails(id);
const breadcrumbItems: BreadcrumbItem[] = [ const breadcrumbItems: BreadcrumbItem[] = [
{ name: "Dashboard", href: "/" }, { name: "Dashboard", href: "/" },
{ name: "Company Categories", href: "/companies/categories" }, { name: "Company Categories", href: "/company-categories" },
{ name: "Edit Company Category", href: `/companies/categories/edit/${id}` }, { name: "Edit Company Category", href: `/company-categories/edit/${id}` },
]; ];
if (isLoading) return <Loading />; if (isLoading) return <Loading />;
return ( return (
@@ -4,7 +4,7 @@ import CompanyCategoriesTable from "@/components/Tables/companies/company-catego
const CompanyCategoryList = () => { const CompanyCategoryList = () => {
const breadcrumbItems = [ const breadcrumbItems = [
{ name: "Dashboard", href: "/" }, { name: "Dashboard", href: "/" },
{ name: "Company Categories", href: "/companies/categories" }, { name: "Company Categories", href: "/company-categories" },
]; ];
return ( return (
<> <>
+12 -105
View File
@@ -1,5 +1,6 @@
import { z } from "zod"; import { z } from "zod";
import * as Icons from "../icons"; import * as Icons from "../icons";
const navSubItemSchema: z.ZodType<any> = z.lazy(() => const navSubItemSchema: z.ZodType<any> = z.lazy(() =>
z.object({ z.object({
title: z.string(), title: z.string(),
@@ -20,7 +21,7 @@ export type NavData = z.infer<typeof navDataSchema>;
export const NAV_DATA: NavData = [ export const NAV_DATA: NavData = [
{ {
label: "MAIN MENU", label: "",
items: [ items: [
{ {
title: "Dashboard", title: "Dashboard",
@@ -36,13 +37,20 @@ export const NAV_DATA: NavData = [
}, },
{ {
title: "Manage Companies", title: "Manage Companies",
url: "",
icon: Icons.Building, icon: Icons.Building,
items: [ items: [
{ Icons: Icons.Building, url: "/companies", title: "Companies" },
{ {
Icons: Icons.Building, icon: Icons.Building,
url: "/companies/categories", url: "/companies",
title: "Companies",
items: [],
},
{
icon: Icons.Building,
url: "/company-categories",
title: "Company Categories", title: "Company Categories",
items: [],
}, },
], ],
}, },
@@ -60,105 +68,4 @@ export const NAV_DATA: NavData = [
}, },
], ],
}, },
// {
// label: "MAIN MENU",
// items: [
// {
// title: "Dashboard",
// icon: Icons.HomeIcon,
// items: [
// {
// title: "eCommerce",
// url: "/",
// },
// ],
// },
// {
// title: "Calendar",
// url: "/calendar",
// icon: Icons.Calendar,
// items: [],
// },
// {
// title: "Profile",
// url: "/profile",
// icon: Icons.User,
// items: [],
// },
// {
// title: "Forms",
// icon: Icons.Alphabet,
// items: [
// {
// title: "Form Elements",
// url: "/forms/form-elements",
// },
// {
// title: "Form Layout",
// url: "/forms/form-layout",
// },
// ],
// },
// {
// title: "Tables",
// url: "/tables",
// icon: Icons.Table,
// items: [
// {
// title: "Tables",
// url: "/tables",
// },
// ],
// },
// {
// title: "Pages",
// icon: Icons.Alphabet,
// items: [
// {
// title: "Settings",
// url: "/pages/settings",
// },
// ],
// },
// ],
// },
// {
// label: "OTHERS",
// items: [
// {
// title: "Charts",
// icon: Icons.PieChart,
// items: [
// {
// title: "Basic Chart",
// url: "/charts/basic-chart",
// },
// ],
// },
// {
// title: "UI Elements",
// icon: Icons.FourCircle,
// items: [
// {
// title: "Alerts",
// url: "/ui-elements/alerts",
// },
// {
// title: "Buttons",
// url: "/ui-elements/buttons",
// },
// ],
// },
// {
// title: "Authentication",
// icon: Icons.Authentication,
// items: [
// {
// title: "Sign In",
// url: "/auth/sign-in",
// },
// ],
// },
// ],
// },
]; ];
+38 -23
View File
@@ -9,7 +9,6 @@ import { NAV_DATA, NavSubItem } from "./data";
import { ArrowLeftIcon, ChevronUp } from "./icons"; import { ArrowLeftIcon, ChevronUp } from "./icons";
import { MenuItem } from "./menu-item"; import { MenuItem } from "./menu-item";
import { useSidebarContext } from "./sidebar-context"; import { useSidebarContext } from "./sidebar-context";
import Image from "next/image";
export function Sidebar() { export function Sidebar() {
const pathname = usePathname(); const pathname = usePathname();
@@ -17,35 +16,35 @@ export function Sidebar() {
const [expandedItems, setExpandedItems] = useState<string[]>([]); const [expandedItems, setExpandedItems] = useState<string[]>([]);
const toggleExpanded = (title: string) => { const toggleExpanded = (title: string) => {
// setExpandedItems((prev) => (prev.includes(title) ? [] : [title]));
// Uncomment the following line to enable multiple expanded items
setExpandedItems((prev) => setExpandedItems((prev) =>
prev.includes(title) ? prev.filter((t) => t !== title) : [...prev, title], prev.includes(title) ? prev.filter((t) => t !== title) : [...prev, title],
); );
}; };
useEffect(() => { useEffect(() => {
// Keep collapsible open, when it's subpage is active const newExpandedItems: string[] = [];
NAV_DATA.some((section) => {
return section.items.some((item) => {
return item.items.some((subItem: NavSubItem) => {
if (subItem.url === pathname) {
if (!expandedItems.includes(item.title)) {
toggleExpanded(item.title);
}
// Break the loop NAV_DATA.forEach((section) => {
return true; section.items.forEach((item) => {
if (item.items.length > 0) {
const hasActiveSubItem = item.items.some(
(subItem: NavSubItem) =>
subItem.url === pathname ||
(pathname.startsWith(subItem.url) && subItem.url !== "/"),
);
if (hasActiveSubItem) {
newExpandedItems.push(item.title);
} }
}); }
}); });
}); });
}, [pathname, expandedItems]);
setExpandedItems(newExpandedItems);
}, [pathname]);
return ( return (
<> <>
{/* Mobile Overlay */}
{isMobile && isOpen && ( {isMobile && isOpen && (
<div <div
className="fixed inset-0 z-40 bg-black/50 transition-opacity duration-300" className="fixed inset-0 z-40 bg-black/50 transition-opacity duration-300"
@@ -86,7 +85,6 @@ export function Sidebar() {
)} )}
</div> </div>
{/* Navigation */}
<div className="custom-scrollbar mt-6 flex-1 overflow-y-auto pr-3 min-[850px]:mt-10"> <div className="custom-scrollbar mt-6 flex-1 overflow-y-auto pr-3 min-[850px]:mt-10">
{NAV_DATA.map((section) => ( {NAV_DATA.map((section) => (
<div key={section.label} className="mb-6"> <div key={section.label} className="mb-6">
@@ -101,9 +99,17 @@ export function Sidebar() {
{item.items.length ? ( {item.items.length ? (
<div> <div>
<MenuItem <MenuItem
isActive={item.items.some( isActive={
({ url }: { url: string }) => url === pathname, item.items.some(
)} ({ url }: { url: string }) =>
url === pathname,
) ||
(item.items.length > 0 &&
item.items.some(
({ url }: { url: string }) =>
pathname.startsWith(url) && url !== "/",
))
}
onClick={() => toggleExpanded(item.title)} onClick={() => toggleExpanded(item.title)}
> >
<item.icon <item.icon
@@ -133,7 +139,11 @@ export function Sidebar() {
<MenuItem <MenuItem
as="link" as="link"
href={subItem.url} href={subItem.url}
isActive={pathname === subItem.url} isActive={
pathname === subItem.url ||
(pathname.startsWith(subItem.url) &&
subItem.url !== "/")
}
> >
<span>{subItem.title}</span> <span>{subItem.title}</span>
</MenuItem> </MenuItem>
@@ -150,12 +160,17 @@ export function Sidebar() {
: "/" + : "/" +
item.title.toLowerCase().split(" ").join("-"); item.title.toLowerCase().split(" ").join("-");
const isActive =
href === "/"
? pathname === href
: pathname.startsWith(href);
return ( return (
<MenuItem <MenuItem
className="flex items-center gap-3 py-3" className="flex items-center gap-3 py-3"
as="link" as="link"
href={href} href={href}
isActive={pathname === href} isActive={isActive}
> >
<item.icon <item.icon
className="size-6 shrink-0" className="size-6 shrink-0"
@@ -1,6 +1,7 @@
"use client"; "use client";
import { ExclamationIcon } from "@/assets/icons"; import { ExclamationIcon } from "@/assets/icons";
import { ShowcaseSection } from "@/components/Layouts/showcase-section";
import { import {
Table, Table,
TableBody, TableBody,
@@ -9,10 +10,10 @@ import {
TableHeader, TableHeader,
} from "@/components/ui/table"; } from "@/components/ui/table";
import TableSkeleton from "@/components/ui/TableSkeleton"; import TableSkeleton from "@/components/ui/TableSkeleton";
import { cn } from "@/lib/utils";
import { unixToDate } from "@/utils/shared"; import { unixToDate } from "@/utils/shared";
import { TableRow } from "../../../ui/table"; import { TableRow } from "../../../ui/table";
import useTenderFeedbackPresenter from "./useTenderFeedbackPresenter"; import useTenderFeedbackPresenter from "./useTenderFeedbackPresenter";
import { cn } from "@/lib/utils";
interface IProps { interface IProps {
id: string; id: string;
@@ -22,7 +23,10 @@ const TenderFeedbackTable = ({ id }: IProps) => {
const { feedback, isLoading, columns, pathName, router } = const { feedback, isLoading, columns, pathName, router } =
useTenderFeedbackPresenter({ id }); useTenderFeedbackPresenter({ id });
return ( return (
<div className="flex flex-col rounded-[10px] bg-white px-7.5 pb-4 pt-7.5 shadow-1 dark:bg-gray-dark dark:shadow-card"> <ShowcaseSection
title={!isLoading && `Tender: ${feedback?.[0].tender?.title}`}
className="flex flex-col rounded-[10px] bg-white px-7.5 pb-4 pt-7.5 shadow-1 dark:bg-gray-dark dark:shadow-card"
>
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
@@ -52,9 +56,7 @@ const TenderFeedbackTable = ({ id }: IProps) => {
<TableCell className="uppercase" colSpan={100}> <TableCell className="uppercase" colSpan={100}>
{unixToDate(item.updated_at)} {unixToDate(item.updated_at)}
</TableCell> </TableCell>
<TableCell className="uppercase" colSpan={100}>
{item?.tender?.title ?? "-"}
</TableCell>
<TableCell className="uppercase" colSpan={100}> <TableCell className="uppercase" colSpan={100}>
{unixToDate(item.created_at)} {unixToDate(item.created_at)}
</TableCell> </TableCell>
@@ -76,7 +78,7 @@ const TenderFeedbackTable = ({ id }: IProps) => {
)} )}
</TableBody> </TableBody>
</Table> </Table>
</div> </ShowcaseSection>
); );
}; };
@@ -10,7 +10,7 @@ const useTenderFeedbackPresenter = ({ id }: { id: string }) => {
"row", "row",
"feedback type", "feedback type",
"updated at", "updated at",
"tender name",
"created at", "created at",
"actions", "actions",
]; ];
+1 -1
View File
@@ -1,6 +1,6 @@
import * as moment from "moment"; import * as moment from "moment";
export const unixToDate = (unix: number) => { export const unixToDate = (unix: number) => {
return moment.unix(unix).format("YYYY/MM/DD"); return moment.unix(unix).format("YYYY/MM/DD HH:MM");
}; };
export const msToDate = (ms: number) => { export const msToDate = (ms: number) => {
return moment.default(ms).format("YYYY/MM/DD"); return moment.default(ms).format("YYYY/MM/DD");