From 634747640a5b4f5fed497af1fb2d05c22cbafd61 Mon Sep 17 00:00:00 2001 From: ghost2023 Date: Tue, 19 May 2026 16:50:01 +0300 Subject: [PATCH 1/5] feat(data-table): Introduce comprehensive DataTable component with full feature set --- .../src/components/data-table/error.tsx | 40 +++++ .../src/components/data-table/footer.tsx | 119 ++++++++++++++ .../src/components/data-table/hooks.ts | 25 +++ .../src/components/data-table/index.ts | 12 ++ .../src/components/data-table/skeleton.tsx | 16 ++ .../src/components/data-table/table.tsx | 149 ++++++++++++++++++ .../src/components/data-table/types.ts | 38 +++++ 7 files changed, 399 insertions(+) create mode 100644 packages/ui-common/src/components/data-table/error.tsx create mode 100644 packages/ui-common/src/components/data-table/footer.tsx create mode 100644 packages/ui-common/src/components/data-table/hooks.ts create mode 100644 packages/ui-common/src/components/data-table/index.ts create mode 100644 packages/ui-common/src/components/data-table/skeleton.tsx create mode 100644 packages/ui-common/src/components/data-table/table.tsx create mode 100644 packages/ui-common/src/components/data-table/types.ts diff --git a/packages/ui-common/src/components/data-table/error.tsx b/packages/ui-common/src/components/data-table/error.tsx new file mode 100644 index 000000000..81bc860b9 --- /dev/null +++ b/packages/ui-common/src/components/data-table/error.tsx @@ -0,0 +1,40 @@ +import { Table as TanstackTable } from "@tanstack/react-table"; + +import { TableCell, TableRow } from "#components/table"; +import { Button } from "#components/button.tsx"; + +export function DataTableError({ + table, + message, + description, + onRetry, +}: { + table: TanstackTable; + message?: string; + description?: string; + onRetry?: () => void; +}) { + return ( + + +
+
+
+

+ {message ?? "No results"} +

+

+ {description ?? "No results found"} +

+
+ {onRetry && ( + + )} +
+
+
+
+ ); +} diff --git a/packages/ui-common/src/components/data-table/footer.tsx b/packages/ui-common/src/components/data-table/footer.tsx new file mode 100644 index 000000000..daa818eaa --- /dev/null +++ b/packages/ui-common/src/components/data-table/footer.tsx @@ -0,0 +1,119 @@ +import { Button } from "../button"; +import { DataTableFooterProps } from "./types"; + +export interface DataTableFooterOptions { + pageSizeOptions?: number[]; + showPageSizeSelector?: boolean; + showRowCount?: boolean; + showPagination?: boolean; + labels?: { + rowsPerPage?: string; + page?: string; + of?: string; + showing?: string; + ofLabel?: string; + items?: string; + previous?: string; + next?: string; + }; +} + +interface DataTableFooterComponentProps< + TData, +> extends DataTableFooterProps { + options?: DataTableFooterOptions; +} + +const defaultOptions: DataTableFooterOptions = { + pageSizeOptions: [5, 10, 25, 50], + showPageSizeSelector: true, + showRowCount: true, + showPagination: true, + labels: { + rowsPerPage: "Rows per page", + page: "Page", + of: "of", + showing: "Showing", + ofLabel: "of", + items: "items", + previous: "Previous", + next: "Next", + }, +}; + +export function DataTableFooter({ + table, + pagination, + options = {}, +}: DataTableFooterComponentProps) { + const opts = { ...defaultOptions, ...options }; + const labels = { ...defaultOptions.labels, ...options.labels }; + + const pageIndex = pagination.pageIndex ?? 0; + const pageSize = pagination.pageSize ?? 10; + const totalCount = pagination.totalCount ?? 0; + + const start = totalCount === 0 ? 0 : pageIndex * pageSize + 1; + const end = Math.min((pageIndex + 1) * pageSize, totalCount); + + const handlePageSizeChange = (newPageSize: number) => { + table?.setPageSize(newPageSize); + }; + + return ( +
+ {(opts.showPageSizeSelector || opts.showRowCount) && ( +
+ {opts.showPageSizeSelector && ( + <> + + + + )} + {opts.showRowCount && ( + + {labels.showing} {start}–{end} {labels.ofLabel} {totalCount}{" "} + {labels.items} + + )} +
+ )} + + {opts.showPagination && ( +
+
+ + +
+
+ )} +
+ ); +} diff --git a/packages/ui-common/src/components/data-table/hooks.ts b/packages/ui-common/src/components/data-table/hooks.ts new file mode 100644 index 000000000..0ce1ad339 --- /dev/null +++ b/packages/ui-common/src/components/data-table/hooks.ts @@ -0,0 +1,25 @@ +import { PaginationState } from "@tanstack/react-table"; +import { useState } from "react"; + +export const usePagination = ({ + pageSize, + pageIndex, +}: { + pageSize?: number; + pageIndex?: number; +} = {}) => { + const [pagination, setPagination] = useState({ + pageIndex: pageIndex ?? 0, + pageSize: pageSize ?? 10, + }); + + const setPage = (pageIndex: number) => { + setPagination((prev) => ({ ...prev, pageIndex })); + }; + + return { + pagination, + setPagination, + setPage, + }; +}; diff --git a/packages/ui-common/src/components/data-table/index.ts b/packages/ui-common/src/components/data-table/index.ts new file mode 100644 index 000000000..c07c1ab8a --- /dev/null +++ b/packages/ui-common/src/components/data-table/index.ts @@ -0,0 +1,12 @@ +export { DataTableError } from "./error"; +export { DataTableSkeleton } from "./skeleton"; +export { DataTableFooter, type DataTableFooterOptions } from "./footer"; +export type { + DataTableProps, + DataTablePagination, + DataTableFooterProps, + DataTableFooterComponent, +} from "./types"; +export { usePagination } from "./hooks"; +export { DataTable } from "./table"; +export * from "@tanstack/react-table"; diff --git a/packages/ui-common/src/components/data-table/skeleton.tsx b/packages/ui-common/src/components/data-table/skeleton.tsx new file mode 100644 index 000000000..40861a987 --- /dev/null +++ b/packages/ui-common/src/components/data-table/skeleton.tsx @@ -0,0 +1,16 @@ +import { Table as TanstackTable } from "@tanstack/react-table"; + +import { Skeleton } from "../skeleton"; +import { TableCell, TableRow } from "#components/table"; + +export function DataTableSkeleton({ table }: { table: TanstackTable }) { + return Array.from({ length: 10 }).map((_, i) => ( + + {table.getAllColumns().map((column) => ( + + + + ))} + + )); +} diff --git a/packages/ui-common/src/components/data-table/table.tsx b/packages/ui-common/src/components/data-table/table.tsx new file mode 100644 index 000000000..0dcc5c044 --- /dev/null +++ b/packages/ui-common/src/components/data-table/table.tsx @@ -0,0 +1,149 @@ +import { + flexRender, + getCoreRowModel, + getPaginationRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "#components/table"; +import { DataTableProps } from "./types"; +import { DataTableSkeleton } from "./skeleton"; +import { DataTableError } from "./error"; +import { DataTableFooter } from "./footer"; + +export function DataTable({ + columns, + data, + status, + onRowClick, + tableOptions, + pagination, + footer, + footerClassName, + containerClassName, + error, + emptyMessage, +}: DataTableProps) { + const { state, ...otherOptions } = tableOptions ?? {}; + const baseState = state ?? {}; + if (pagination) { + baseState.pagination = { + pageIndex: pagination.pageIndex ?? 0, + pageSize: pagination.pageSize ?? 1, + }; + } + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + + manualPagination: true, + ...(pagination && { + getPaginationRowModel: getPaginationRowModel(), + pageCount: pagination.pageCount, + }), + state: baseState, + ...otherOptions, + }); + + return ( + <> +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + ) + ?.headerClassName ?? "" + } + style={{ width: `${header.getSize()}px` }} + > + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {status === "loading" && } + {status === "error" && ( + + )} + {status === "success" && + (table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + onRowClick?.(row.original)} + role={onRowClick ? "button" : ""} + className={ + onRowClick + ? "cursor-pointer hover:bg-accent hover:text-foreground " + : "" + } + > + {row.getVisibleCells().map((cell) => ( + ) + ?.cellClassName ?? "" + } + > + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + )) + ) : ( + + + {emptyMessage ?? "No data"} + + + ))} + +
+
+ + {pagination && ( +
+ {footer ? ( + footer({ table, pagination }) + ) : ( + + )} +
+ )} + + ); +} diff --git a/packages/ui-common/src/components/data-table/types.ts b/packages/ui-common/src/components/data-table/types.ts new file mode 100644 index 000000000..886718b96 --- /dev/null +++ b/packages/ui-common/src/components/data-table/types.ts @@ -0,0 +1,38 @@ +import type { Table as TanstackTable, TableOptions, ColumnDef } from "@tanstack/react-table"; + +export interface DataTablePagination { + pageSize?: number; + pageIndex?: number; + pageCount?: number; + totalCount?: number; +} + +export interface DataTableFooterProps { + table: TanstackTable; + pagination: DataTablePagination; +} + +export type DataTableFooterComponent = ( + props: DataTableFooterProps +) => React.ReactNode; + +export interface DataTableProps { + columns: ColumnDef[]; + data: TData[]; + status?: "loading" | "error" | "success"; + onRowClick?: (row: TData) => void; + tableOptions?: Omit< + TableOptions, + "data" | "columns" | "getCoreRowModel" + >; + pagination?: DataTablePagination; + footer?: DataTableFooterComponent; + footerClassName?: string; + containerClassName?: string; + emptyMessage?: string; + error?: { + message: string; + description?: string; + onRetry?: () => void; + }; +} From f9b96c143ec940395d7620651db271bceb7be77e Mon Sep 17 00:00:00 2001 From: ghost2023 Date: Tue, 19 May 2026 16:50:40 +0300 Subject: [PATCH 2/5] chore(style): setup shadcn components --- packages/ui-common/src/components/badge.tsx | 48 +++++ packages/ui-common/src/components/dialog.tsx | 156 ++++++++++++++ packages/ui-common/src/components/select.tsx | 190 ++++++++++++++++++ .../ui-common/src/components/skeleton.tsx | 13 ++ packages/ui-common/src/components/table.tsx | 114 +++++++++++ 5 files changed, 521 insertions(+) create mode 100644 packages/ui-common/src/components/badge.tsx create mode 100644 packages/ui-common/src/components/dialog.tsx create mode 100644 packages/ui-common/src/components/select.tsx create mode 100644 packages/ui-common/src/components/skeleton.tsx create mode 100644 packages/ui-common/src/components/table.tsx diff --git a/packages/ui-common/src/components/badge.tsx b/packages/ui-common/src/components/badge.tsx new file mode 100644 index 000000000..bbb37ad66 --- /dev/null +++ b/packages/ui-common/src/components/badge.tsx @@ -0,0 +1,48 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from "radix-ui" + +import { cn } from "../lib/utils" + +const badgeVariants = cva( + "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90", + outline: + "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + link: "text-primary underline-offset-4 [a&]:hover:underline", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant = "default", + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot.Root : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/packages/ui-common/src/components/dialog.tsx b/packages/ui-common/src/components/dialog.tsx new file mode 100644 index 000000000..da2d5f961 --- /dev/null +++ b/packages/ui-common/src/components/dialog.tsx @@ -0,0 +1,156 @@ +import * as React from "react" +import { XIcon } from "lucide-react" +import { Dialog as DialogPrimitive } from "radix-ui" + +import { cn } from "../lib/utils" +import { Button } from "./button" + +function Dialog({ + ...props +}: React.ComponentProps) { + return +} + +function DialogTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function DialogPortal({ + ...props +}: React.ComponentProps) { + return +} + +function DialogClose({ + ...props +}: React.ComponentProps) { + return +} + +function DialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogContent({ + className, + children, + showCloseButton = true, + ...props +}: React.ComponentProps & { + showCloseButton?: boolean +}) { + return ( + + + + {children} + {showCloseButton && ( + + + Close + + )} + + + ) +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogFooter({ + className, + showCloseButton = false, + children, + ...props +}: React.ComponentProps<"div"> & { + showCloseButton?: boolean +}) { + return ( +
+ {children} + {showCloseButton && ( + + + + )} +
+ ) +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +} diff --git a/packages/ui-common/src/components/select.tsx b/packages/ui-common/src/components/select.tsx new file mode 100644 index 000000000..b7f787067 --- /dev/null +++ b/packages/ui-common/src/components/select.tsx @@ -0,0 +1,190 @@ +"use client" + +import * as React from "react" +import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react" +import { Select as SelectPrimitive } from "radix-ui" + +import { cn } from "../lib/utils" + +function Select({ + ...props +}: React.ComponentProps) { + return +} + +function SelectGroup({ + ...props +}: React.ComponentProps) { + return +} + +function SelectValue({ + ...props +}: React.ComponentProps) { + return +} + +function SelectTrigger({ + className, + size = "default", + children, + ...props +}: React.ComponentProps & { + size?: "sm" | "default" +}) { + return ( + + {children} + + + + + ) +} + +function SelectContent({ + className, + children, + position = "item-aligned", + align = "center", + ...props +}: React.ComponentProps) { + return ( + + + + + {children} + + + + + ) +} + +function SelectLabel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function SelectItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function SelectSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function SelectScrollUpButton({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +function SelectScrollDownButton({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +export { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectScrollDownButton, + SelectScrollUpButton, + SelectSeparator, + SelectTrigger, + SelectValue, +} diff --git a/packages/ui-common/src/components/skeleton.tsx b/packages/ui-common/src/components/skeleton.tsx new file mode 100644 index 000000000..fe08ad75e --- /dev/null +++ b/packages/ui-common/src/components/skeleton.tsx @@ -0,0 +1,13 @@ +import { cn } from "../lib/utils" + +function Skeleton({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { Skeleton } diff --git a/packages/ui-common/src/components/table.tsx b/packages/ui-common/src/components/table.tsx new file mode 100644 index 000000000..b91de8e32 --- /dev/null +++ b/packages/ui-common/src/components/table.tsx @@ -0,0 +1,114 @@ +import * as React from "react"; + +import { cn } from "../lib/utils"; + +function Table({ className, ...props }: React.ComponentProps<"table">) { + return ( +
+ + + ); +} + +function TableHeader({ className, ...props }: React.ComponentProps<"thead">) { + return ( + + ); +} + +function TableBody({ className, ...props }: React.ComponentProps<"tbody">) { + return ( + + ); +} + +function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) { + return ( + tr]:last:border-b-0", + className, + )} + {...props} + /> + ); +} + +function TableRow({ className, ...props }: React.ComponentProps<"tr">) { + return ( + + ); +} + +function TableHead({ className, ...props }: React.ComponentProps<"th">) { + return ( + Date: Wed, 20 May 2026 11:35:44 +0300 Subject: [PATCH 5/5] feat(portal): Integrate @edr/ui-common components and centralize styling --- apps/edr-freight-web/portal/index.html | 22 +- apps/edr-freight-web/portal/src/main.tsx | 3 +- .../src/pages/customers/CustomersPage.tsx | 395 +++++++----------- .../pages/customers/DeleteCustomerDialog.tsx | 4 +- 4 files changed, 169 insertions(+), 255 deletions(-) diff --git a/apps/edr-freight-web/portal/index.html b/apps/edr-freight-web/portal/index.html index 09f11f8dc..2233dc861 100644 --- a/apps/edr-freight-web/portal/index.html +++ b/apps/edr-freight-web/portal/index.html @@ -1,13 +1,15 @@ - - - - EDR Freight Portal - - -
- - - + + + + + EDR Freight Portal + + + +
+ + + diff --git a/apps/edr-freight-web/portal/src/main.tsx b/apps/edr-freight-web/portal/src/main.tsx index 815b33faa..1437f9d00 100644 --- a/apps/edr-freight-web/portal/src/main.tsx +++ b/apps/edr-freight-web/portal/src/main.tsx @@ -2,8 +2,9 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import "../index.css"; import "@tria-plc/iamui-common/styles.css"; -import "@edr/ui-common/styles.css" +import "@edr/ui-common/styles.css"; import App from "./App"; import { diff --git a/apps/edr-freight-web/portal/src/pages/customers/CustomersPage.tsx b/apps/edr-freight-web/portal/src/pages/customers/CustomersPage.tsx index 997dc7141..4c2ba6c4e 100644 --- a/apps/edr-freight-web/portal/src/pages/customers/CustomersPage.tsx +++ b/apps/edr-freight-web/portal/src/pages/customers/CustomersPage.tsx @@ -1,8 +1,6 @@ -import { useMemo, useState } from "react"; +import { useMemo } from "react"; import { Link } from "react-router-dom"; import { - ChevronLeft, - ChevronRight, Clock3, Eye, Filter, @@ -18,63 +16,155 @@ import { import Breadcrumbs from "@/components/Breadcrumbs"; import NewCustomerPage from "./NewCustomerPage"; import DeleteCustomerDialog from "./DeleteCustomerDialog"; -import { customers, type CustomerStatus } from "./customers.mock"; - -const PAGE_SIZE_OPTIONS = [5, 10, 25, 50]; +import { + customers, + type CustomerStatus, + type Customer, +} from "./customers.mock"; +import { + DataTable, + DataTableFooter, + type ColumnDef, + usePagination, + Button, + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, + Input, +} from "@edr/ui-common"; export default function CustomerPage() { - const [pageSize, setPageSize] = useState(10); - const [page, setPage] = useState(1); + const { pagination, setPagination } = usePagination({ pageSize: 10 }); const total = customers.length; - const totalPages = Math.max(1, Math.ceil(total / pageSize)); - const safePage = Math.min(page, totalPages); - const start = (safePage - 1) * pageSize; - const end = Math.min(start + pageSize, total); - const paginated = useMemo( + const pageCount = Math.ceil(total / pagination.pageSize); + const start = pagination.pageIndex * pagination.pageSize; + const end = Math.min(start + pagination.pageSize, total); + + const paginatedData = useMemo( () => customers.slice(start, end), [start, end], ); + const columns: ColumnDef[] = [ + { + accessorKey: "name", + header: "Customer", + cell: ({ row }) => { + const customer = row.original; + return ( +
+
+ +
+
+

{customer.name}

+

ID #{customer.id}

+
+
+ ); + }, + }, + { + accessorKey: "company", + header: "Company", + }, + { + accessorKey: "email", + header: "Email", + cell: ({ row }) => ( + {row.original.email} + ), + }, + { + accessorKey: "status", + header: "Status", + cell: ({ row }) => , + }, + { + id: "actions", + header: () =>
Actions
, + cell: ({ row }) => { + const customer = row.original; + return ( +
+ + + + + + + + + + + +
+ ); + }, + }, + ]; + return ( -
-
+
+
- {/* Header */} -
+

Customers

-

+

Manage and monitor your customer records.

- - +
- +
-
+ - {/* Stats */}
- {/* Customer Table */} -
-
+ +
-

- Customer List -

-

+ Customer List + Recent customer activities and records. -

+
- -
+ + -
-
[role=checkbox]]:translate-y-[2px]", + className, + )} + {...props} + /> + ); +} + +function TableCell({ className, ...props }: React.ComponentProps<"td">) { + return ( + [role=checkbox]]:translate-y-[2px]", + className, + )} + {...props} + /> + ); +} + +function TableCaption({ + className, + ...props +}: React.ComponentProps<"caption">) { + return ( +
+ ); +} + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +}; From 024871c127c99682e6b9b300f8bf17e6e8d2871a Mon Sep 17 00:00:00 2001 From: ghost2023 Date: Tue, 19 May 2026 16:51:41 +0300 Subject: [PATCH 3/5] chore: centralized the themeing and fix styles --- apps/edr-freight-web/portal/index.css | 41 --- apps/edr-freight-web/portal/src/main.tsx | 10 +- package.json | 2 +- packages/ui-common/package.json | 10 +- packages/ui-common/postcss.config.js | 6 + .../src/components/Layout/DashboardLayout.tsx | 7 +- packages/ui-common/src/index.ts | 3 +- packages/ui-common/src/styles/globals.css | 2 - packages/ui-common/src/styles/index.css | 233 ++++++++++++++++++ packages/ui-common/src/theme/colors.ts | 24 -- packages/ui-common/src/theme/index.ts | 2 - packages/ui-common/src/theme/typography.ts | 28 --- packages/ui-common/turbo.json | 22 ++ pnpm-lock.yaml | 179 ++++++++++++++ 14 files changed, 459 insertions(+), 110 deletions(-) create mode 100644 packages/ui-common/postcss.config.js delete mode 100644 packages/ui-common/src/styles/globals.css create mode 100644 packages/ui-common/src/styles/index.css delete mode 100644 packages/ui-common/src/theme/colors.ts delete mode 100644 packages/ui-common/src/theme/index.ts delete mode 100644 packages/ui-common/src/theme/typography.ts create mode 100644 packages/ui-common/turbo.json diff --git a/apps/edr-freight-web/portal/index.css b/apps/edr-freight-web/portal/index.css index 241330c15..f1d8c73cd 100644 --- a/apps/edr-freight-web/portal/index.css +++ b/apps/edr-freight-web/portal/index.css @@ -1,42 +1 @@ @import "tailwindcss"; - -@custom-variant dark (&:where(.dark, .dark *)); - -* { - scrollbar-width: thin; - scrollbar-color: rgb(203 213 225 / 0.6) transparent; -} - -*::-webkit-scrollbar { - width: 8px; - height: 10px; -} - -*::-webkit-scrollbar-track { - background: transparent; -} - -*::-webkit-scrollbar-thumb { - background-color: rgb(203 213 225 / 0.7); - border-radius: 9999px; -} - -*::-webkit-scrollbar-thumb:hover { - background-color: rgb(51 87 141 / 0.5); -} - -*::-webkit-scrollbar-corner { - background: transparent; -} - -.dark * { - scrollbar-color: rgb(71 85 105 / 0.6) transparent; -} - -.dark *::-webkit-scrollbar-thumb { - background-color: rgb(71 85 105 / 0.6); -} - -.dark *::-webkit-scrollbar-thumb:hover { - background-color: rgb(51 87 141 / 0.7); -} diff --git a/apps/edr-freight-web/portal/src/main.tsx b/apps/edr-freight-web/portal/src/main.tsx index d0a07ba14..815b33faa 100644 --- a/apps/edr-freight-web/portal/src/main.tsx +++ b/apps/edr-freight-web/portal/src/main.tsx @@ -3,11 +3,11 @@ import { createRoot } from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import "@tria-plc/iamui-common/styles.css"; +import "@edr/ui-common/styles.css" import App from "./App"; import { AuthProvider, - BrandingProvider, configureIam, UserProvider, axiosInstance, @@ -67,15 +67,13 @@ if (!rootElement) { createRoot(document.getElementById("root")!).render( - - + - + - - + , ); diff --git a/package.json b/package.json index 0c3b26cb8..e4d55e7e9 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "scripts": { "dev": "turbo run dev", - "dev:freight": "turbo run dev --filter=@edr/freight-api... --filter=@edr/freight-portal... --filter=@edr/freight-backoffice...", + "dev:freight": "turbo run dev --filter=@edr/freight-api... --filter=@edr/freight-portal... --filter=@edr/freight-backoffice... --filter=@edr/ui-common...", "dev:passenger": "turbo run dev --filter=@edr/passenger-api... --filter=@edr/passenger-portal... --filter=@edr/passenger-backoffice...", "build": "turbo run build", "build:freight": "turbo run build --filter=@edr/freight-api... --filter=@edr/freight-portal... --filter=@edr/freight-backoffice...", diff --git a/packages/ui-common/package.json b/packages/ui-common/package.json index 388915423..9ffe547f6 100644 --- a/packages/ui-common/package.json +++ b/packages/ui-common/package.json @@ -6,9 +6,13 @@ "main": "./src/index.ts", "types": "./src/index.ts", "exports": { - ".": "./src/index.ts" + ".": "./src/index.ts", + "./styles.css": "./dist/index.css" }, "scripts": { + "build:styles": "tailwindcss -i ./src/styles/index.css -o ./dist/index.css", + "check-types": "tsc --noEmit", + "dev:styles": "tailwindcss -i ./src/styles/index.css -o ./dist/index.css --watch", "type-check": "tsc --noEmit", "lint": "eslint src" }, @@ -18,6 +22,7 @@ }, "dependencies": { "@edr/types": "workspace:*", + "@tanstack/react-table": "^8.21.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^1.14.0", @@ -29,10 +34,13 @@ "devDependencies": { "@edr/eslint-config": "workspace:*", "@edr/tsconfig": "workspace:*", + "@tailwindcss/cli": "^4.3.0", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", + "postcss": "^8.4.47", "react": "^18.3.1", "react-dom": "^18.3.1", + "tailwindcss": "^4.3.0", "typescript": "^5.5.4" }, "imports": { diff --git a/packages/ui-common/postcss.config.js b/packages/ui-common/postcss.config.js new file mode 100644 index 000000000..1e8e9dbf9 --- /dev/null +++ b/packages/ui-common/postcss.config.js @@ -0,0 +1,6 @@ +// Optional PostCSS configuration for applications that need it +export const postcssConfig = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; diff --git a/packages/ui-common/src/components/Layout/DashboardLayout.tsx b/packages/ui-common/src/components/Layout/DashboardLayout.tsx index 5c469f0b9..c5e8b9090 100644 --- a/packages/ui-common/src/components/Layout/DashboardLayout.tsx +++ b/packages/ui-common/src/components/Layout/DashboardLayout.tsx @@ -170,9 +170,8 @@ const DashboardLayout = ({ {userName} @@ -220,7 +219,7 @@ const DashboardLayout = ({ -
+
{children}
diff --git a/packages/ui-common/src/index.ts b/packages/ui-common/src/index.ts index 9ad23067f..caba3e7cd 100644 --- a/packages/ui-common/src/index.ts +++ b/packages/ui-common/src/index.ts @@ -26,4 +26,5 @@ export type { export { Button } from "./components/button"; -export * from "./theme"; +export * from "./components/data-table"; + diff --git a/packages/ui-common/src/styles/globals.css b/packages/ui-common/src/styles/globals.css deleted file mode 100644 index bc69e207e..000000000 --- a/packages/ui-common/src/styles/globals.css +++ /dev/null @@ -1,2 +0,0 @@ -@import "tailwindcss"; -@import "tw-animate-css"; \ No newline at end of file diff --git a/packages/ui-common/src/styles/index.css b/packages/ui-common/src/styles/index.css new file mode 100644 index 000000000..f04b5a6e5 --- /dev/null +++ b/packages/ui-common/src/styles/index.css @@ -0,0 +1,233 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:where(.dark, .dark *)); + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: #0d5c2c; + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.81 0.1 252); + --chart-2: oklch(0.62 0.19 260); + --chart-3: oklch(0.55 0.22 263); + --chart-4: oklch(0.49 0.22 264); + --chart-5: oklch(0.42 0.18 266); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); + --font-sans: + ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", + Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: + ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + "Courier New", monospace; + --radius: 0.625rem; + --shadow-x: 0; + --shadow-y: 1px; + --shadow-blur: 3px; + --shadow-spread: 0px; + --shadow-opacity: 0.1; + --shadow-color: oklch(0 0 0); + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: + 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow-md: + 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1); + --shadow-lg: + 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1); + --shadow-xl: + 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.269 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: #0d5c2c; + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.371 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --destructive-foreground: oklch(0.985 0 0); + --border: oklch(0.275 0 0); + --input: oklch(0.325 0 0); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.81 0.1 252); + --chart-2: oklch(0.62 0.19 260); + --chart-3: oklch(0.55 0.22 263); + --chart-4: oklch(0.49 0.22 264); + --chart-5: oklch(0.42 0.18 266); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.275 0 0); + --sidebar-ring: oklch(0.439 0 0); + --font-sans: + ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", + Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: + ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + "Courier New", monospace; + --radius: 0.625rem; + --shadow-x: 0; + --shadow-y: 1px; + --shadow-blur: 3px; + --shadow-spread: 0px; + --shadow-opacity: 0.1; + --shadow-color: oklch(0 0 0); + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: + 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow-md: + 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1); + --shadow-lg: + 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1); + --shadow-xl: + 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); +} + +@theme { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); + + --font-sans: var(--font-sans); + --font-mono: var(--font-mono); + --font-serif: var(--font-serif); + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + + --shadow-2xs: var(--shadow-2xs); + --shadow-xs: var(--shadow-xs); + --shadow-sm: var(--shadow-sm); + --shadow: var(--shadow); + --shadow-md: var(--shadow-md); + --shadow-lg: var(--shadow-lg); + --shadow-xl: var(--shadow-xl); + --shadow-2xl: var(--shadow-2xl); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + + body { + @apply bg-background text-foreground; + } +} + +* { + scrollbar-width: thin; + scrollbar-color: rgb(203 213 225 / 0.6) transparent; + border-color: var(--color-border); +} + +*::-webkit-scrollbar { + width: 8px; + height: 10px; +} + +*::-webkit-scrollbar-track { + background: transparent; +} + +*::-webkit-scrollbar-thumb { + background-color: rgb(203 213 225 / 0.7); + border-radius: 9999px; +} + +*::-webkit-scrollbar-thumb:hover { + background-color: rgb(51 87 141 / 0.5); +} + +*::-webkit-scrollbar-corner { + background: transparent; +} + +.dark * { + scrollbar-color: rgb(71 85 105 / 0.6) transparent; +} + +.dark *::-webkit-scrollbar-thumb { + background-color: rgb(71 85 105 / 0.6); +} + +.dark *::-webkit-scrollbar-thumb:hover { + background-color: rgb(51 87 141 / 0.7); +} diff --git a/packages/ui-common/src/theme/colors.ts b/packages/ui-common/src/theme/colors.ts deleted file mode 100644 index 4db291653..000000000 --- a/packages/ui-common/src/theme/colors.ts +++ /dev/null @@ -1,24 +0,0 @@ -export const colors = { - primary: { - 50: "#eff6ff", - 100: "#dbeafe", - 500: "#3b82f6", - 600: "#2563eb", - 700: "#1d4ed8", - 900: "#1e3a8a", - }, - neutral: { - 50: "#f9fafb", - 100: "#f3f4f6", - 200: "#e5e7eb", - 400: "#9ca3af", - 600: "#4b5563", - 900: "#111827", - }, - success: "#16a34a", - warning: "#f59e0b", - danger: "#dc2626", - info: "#0ea5e9", -} as const; - -export type Colors = typeof colors; diff --git a/packages/ui-common/src/theme/index.ts b/packages/ui-common/src/theme/index.ts deleted file mode 100644 index c50436feb..000000000 --- a/packages/ui-common/src/theme/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./colors"; -export * from "./typography"; diff --git a/packages/ui-common/src/theme/typography.ts b/packages/ui-common/src/theme/typography.ts deleted file mode 100644 index 15e27ab09..000000000 --- a/packages/ui-common/src/theme/typography.ts +++ /dev/null @@ -1,28 +0,0 @@ -export const typography = { - fontFamily: { - sans: '"Inter", "Segoe UI", sans-serif', - mono: '"Fira Code", "Menlo", monospace', - }, - fontSize: { - xs: "0.75rem", - sm: "0.875rem", - base: "1rem", - lg: "1.125rem", - xl: "1.25rem", - "2xl": "1.5rem", - "3xl": "1.875rem", - }, - fontWeight: { - regular: 400, - medium: 500, - semibold: 600, - bold: 700, - }, - lineHeight: { - tight: 1.25, - normal: 1.5, - relaxed: 1.75, - }, -} as const; - -export type Typography = typeof typography; diff --git a/packages/ui-common/turbo.json b/packages/ui-common/turbo.json new file mode 100644 index 000000000..5a176f003 --- /dev/null +++ b/packages/ui-common/turbo.json @@ -0,0 +1,22 @@ +{ + "extends": ["//"], + "tasks": { + "build": { + "dependsOn": ["build:styles"] + }, + "build:styles": { + "outputs": ["dist/**"] + }, + "dev": { + "with": ["dev:styles",] + }, + "dev:styles": { + "cache": false, + "persistent": true + }, + "dev:components": { + "cache": false, + "persistent": true + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6430be107..ca17749d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -622,6 +622,9 @@ importers: '@edr/types': specifier: workspace:* version: link:../types + '@tanstack/react-table': + specifier: ^8.21.3 + version: 8.21.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -650,18 +653,27 @@ importers: '@edr/tsconfig': specifier: workspace:* version: link:../config/tsconfig + '@tailwindcss/cli': + specifier: ^4.3.0 + version: 4.3.0 '@types/react': specifier: ^18.3.11 version: 18.3.28 '@types/react-dom': specifier: ^18.3.0 version: 18.3.7(@types/react@18.3.28) + postcss: + specifier: ^8.4.47 + version: 8.5.14 react: specifier: 18.3.1 version: 18.3.1 react-dom: specifier: 18.3.1 version: 18.3.1(react@18.3.1) + tailwindcss: + specifier: ^4.3.0 + version: 4.3.0 typescript: specifier: ^5.5.4 version: 5.9.3 @@ -2041,6 +2053,88 @@ packages: '@paralleldrive/cuid2@2.3.1': resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + engines: {node: '>= 10.0.0'} + '@phc/format@1.0.0': resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==} engines: {node: '>=10'} @@ -3169,6 +3263,10 @@ packages: '@tabler/icons@3.44.0': resolution: {integrity: sha512-Wn0AOZG9sg0L+bjfMqq4eNhC6pQjIrk94LvvWYNYkY8KH8wC3YILRzQlrnVJc4FUeMxH/AK97QsYCX35H3LndA==} + '@tailwindcss/cli@4.3.0': + resolution: {integrity: sha512-X9kdlqyMopO9fewbgHsEeuy31YzMHbdZ9VsKt004tB+mxSg1CNbyhZYCzvhciN0AM4R4b5lvIprPjtNq7iQxpQ==} + hasBin: true + '@tailwindcss/node@4.3.0': resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==} @@ -8203,6 +8301,10 @@ packages: motion-utils@12.36.0: resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -8304,6 +8406,9 @@ packages: node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-addon-api@8.7.0: resolution: {integrity: sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==} engines: {node: ^18 || ^20 || >= 21} @@ -12994,6 +13099,66 @@ snapshots: dependencies: '@noble/hashes': 1.8.0 + '@parcel/watcher-android-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-x64@2.5.6': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.6': + optional: true + + '@parcel/watcher-win32-arm64@2.5.6': + optional: true + + '@parcel/watcher-win32-ia32@2.5.6': + optional: true + + '@parcel/watcher-win32-x64@2.5.6': + optional: true + + '@parcel/watcher@2.5.6': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 + node-addon-api: 7.1.1 + picomatch: 4.0.4 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.6 + '@parcel/watcher-darwin-arm64': 2.5.6 + '@parcel/watcher-darwin-x64': 2.5.6 + '@parcel/watcher-freebsd-x64': 2.5.6 + '@parcel/watcher-linux-arm-glibc': 2.5.6 + '@parcel/watcher-linux-arm-musl': 2.5.6 + '@parcel/watcher-linux-arm64-glibc': 2.5.6 + '@parcel/watcher-linux-arm64-musl': 2.5.6 + '@parcel/watcher-linux-x64-glibc': 2.5.6 + '@parcel/watcher-linux-x64-musl': 2.5.6 + '@parcel/watcher-win32-arm64': 2.5.6 + '@parcel/watcher-win32-ia32': 2.5.6 + '@parcel/watcher-win32-x64': 2.5.6 + '@phc/format@1.0.0': {} '@pkgjs/parseargs@0.11.0': @@ -14203,6 +14368,16 @@ snapshots: '@tabler/icons@3.44.0': {} + '@tailwindcss/cli@4.3.0': + dependencies: + '@parcel/watcher': 2.5.6 + '@tailwindcss/node': 4.3.0 + '@tailwindcss/oxide': 4.3.0 + enhanced-resolve: 5.21.3 + mri: 1.2.0 + picocolors: 1.1.1 + tailwindcss: 4.3.0 + '@tailwindcss/node@4.3.0': dependencies: '@jridgewell/remapping': 2.3.5 @@ -20734,6 +20909,8 @@ snapshots: motion-utils@12.36.0: {} + mri@1.2.0: {} + ms@2.0.0: {} ms@2.1.3: {} @@ -20884,6 +21061,8 @@ snapshots: node-abort-controller@3.1.1: {} + node-addon-api@7.1.1: {} + node-addon-api@8.7.0: {} node-domexception@1.0.0: {} From b9ef5a2b46078fdd81343bb7e37553678e6e8c36 Mon Sep 17 00:00:00 2001 From: ghost2023 Date: Wed, 20 May 2026 11:35:00 +0300 Subject: [PATCH 4/5] style: finish based global them --- .../src/components/Button/Button.tsx | 57 ------------ .../ui-common/src/components/Button/index.ts | 2 - .../src/components/Layout/DashboardLayout.tsx | 12 +-- .../src/components/Layout/Sidebar.tsx | 13 ++- packages/ui-common/src/components/card.tsx | 92 +++++++++++++++++++ packages/ui-common/src/components/input.tsx | 21 +++++ packages/ui-common/src/components/table.tsx | 2 +- packages/ui-common/src/index.ts | 13 +-- packages/ui-common/src/styles/index.css | 26 +++--- 9 files changed, 143 insertions(+), 95 deletions(-) delete mode 100644 packages/ui-common/src/components/Button/Button.tsx delete mode 100644 packages/ui-common/src/components/Button/index.ts create mode 100644 packages/ui-common/src/components/card.tsx create mode 100644 packages/ui-common/src/components/input.tsx diff --git a/packages/ui-common/src/components/Button/Button.tsx b/packages/ui-common/src/components/Button/Button.tsx deleted file mode 100644 index 04ddd10ad..000000000 --- a/packages/ui-common/src/components/Button/Button.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { ButtonHTMLAttributes, forwardRef } from "react"; -import clsx from "clsx"; - -export type ButtonVariant = "primary" | "secondary" | "ghost" | "danger"; -export type ButtonSize = "sm" | "md" | "lg"; - -export interface ButtonProps extends ButtonHTMLAttributes { - variant?: ButtonVariant; - size?: ButtonSize; - isLoading?: boolean; -} - -const variantClasses: Record = { - primary: "bg-blue-600 text-white hover:bg-blue-700 disabled:bg-blue-300", - secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 disabled:bg-gray-50", - ghost: "bg-transparent text-gray-900 hover:bg-gray-100", - danger: "bg-red-600 text-white hover:bg-red-700 disabled:bg-red-300", -}; - -const sizeClasses: Record = { - sm: "px-3 py-1.5 text-sm", - md: "px-4 py-2 text-base", - lg: "px-6 py-3 text-lg", -}; - -const Button = forwardRef( - ( - { - variant = "primary", - size = "md", - isLoading, - disabled, - className, - children, - ...rest - }, - ref, - ) => ( - - ), -); - -Button.displayName = "Button"; - -export default Button; diff --git a/packages/ui-common/src/components/Button/index.ts b/packages/ui-common/src/components/Button/index.ts deleted file mode 100644 index 55968f63d..000000000 --- a/packages/ui-common/src/components/Button/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./Button"; -export type { ButtonProps, ButtonVariant, ButtonSize } from "./Button"; diff --git a/packages/ui-common/src/components/Layout/DashboardLayout.tsx b/packages/ui-common/src/components/Layout/DashboardLayout.tsx index c5e8b9090..2dc2eb2fe 100644 --- a/packages/ui-common/src/components/Layout/DashboardLayout.tsx +++ b/packages/ui-common/src/components/Layout/DashboardLayout.tsx @@ -123,7 +123,7 @@ const DashboardLayout = ({ ) : null; return ( -
+
-
-
- {title} -
+
+
{title}
-
- {children} -
+
{children}
); diff --git a/packages/ui-common/src/components/Layout/Sidebar.tsx b/packages/ui-common/src/components/Layout/Sidebar.tsx index 7653b8c10..96121cbb2 100644 --- a/packages/ui-common/src/components/Layout/Sidebar.tsx +++ b/packages/ui-common/src/components/Layout/Sidebar.tsx @@ -22,11 +22,11 @@ const Sidebar = ({ onNavigate, headerExtra, }: SidebarProps) => ( -
- - - - - - - - - - - - {paginated.map((customer) => ( - - - - - - - - - - - - ))} - -
CustomerCompanyEmailStatusActions
-
-
- -
- -
-

- {customer.name} -

-

- ID #{customer.id} -

-
-
-
- {customer.company} - - {customer.email} - - - -
- - - - - - - - - - - -
-
-
- - { - setPageSize(size); - setPage(1); - }} - /> -
-
- - ); -} - -function Pagination({ - page, - pageSize, - total, - totalPages, - start, - end, - onPageChange, - onPageSizeChange, -}: { - page: number; - pageSize: number; - total: number; - totalPages: number; - start: number; - end: number; - onPageChange: (page: number) => void; - onPageSizeChange: (size: number) => void; -}) { - const showingFrom = total === 0 ? 0 : start + 1; - - return ( -
-
- - - - Showing {showingFrom}–{end} of {total} - -
- -
- - - {Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => { - const isActive = p === page; - return ( - - ); - })} - - + + { }} + pagination={{ + pageIndex: pagination.pageIndex, + pageSize: pagination.pageSize, + pageCount: pageCount, + totalCount: total, + }} + tableOptions={{ + state: { pagination }, + onPaginationChange: setPagination, + }} + containerClassName="border-b shadow-none" + footer={DataTableFooter} + /> + +
); @@ -325,8 +236,8 @@ function StatCard({ icon: React.ReactNode; }) { return ( -
-
+ +

{title}

{value}

@@ -335,8 +246,8 @@ function StatCard({
{icon}
-
-
+ + ); } diff --git a/apps/edr-freight-web/portal/src/pages/customers/DeleteCustomerDialog.tsx b/apps/edr-freight-web/portal/src/pages/customers/DeleteCustomerDialog.tsx index b75effc64..d0d766c0c 100644 --- a/apps/edr-freight-web/portal/src/pages/customers/DeleteCustomerDialog.tsx +++ b/apps/edr-freight-web/portal/src/pages/customers/DeleteCustomerDialog.tsx @@ -9,9 +9,9 @@ import { DialogHeader, DialogTitle, DialogTrigger, -} from "@/components/ui/dialog"; +} from "@edr/ui-common"; -import { Button } from "@/components/ui/button"; +import { Button } from "@edr/ui-common"; export interface DeleteCustomerDialogProps { customerName: string;