Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Bran258/drtc-fluvial-admin/llms.txt

Use this file to discover all available pages before exploring further.

The shared/components directory contains every reusable UI building block used across the DRTC Fluvial Admin panel. Each component is a "use client" React component written in TypeScript and styled with Tailwind CSS. You import them directly into any page or feature module — no extra setup required.
All components in this library use the institutional navy color #001f3f as their primary brand token. Do not override this with ad-hoc Tailwind classes; use the component’s variant or type props instead.

Layout components

The layout shell is composed of three fixed structural components — Sidebar, Navbar, and Footer — that wrap every authenticated page. You do not pass props to any of them; they are self-contained.

Page-level utility components

These components are placed at the top of individual pages to provide consistent headers, breadcrumb navigation, and sub-tab bars.
shared/components/TitleHeader.tsxRenders a page title and optional description on the left, with optional action buttons or other content on the right. Use it as the first element inside every page’s content area.Props
interface TitleHeaderProps {
  title: string;              // required — rendered as an <h1>
  description?: string;       // optional — rendered below the title in slate-500
  rightContent?: React.ReactNode; // optional — rendered in a flex row on the right
}
Usage
import TitleHeader from "@/shared/components/TitleHeader";
import Button from "@/shared/components/Button";

export default function EmpadronamientoPage() {
  return (
    <TitleHeader
      title="Empadronamiento"
      description="Gestión de embarcaciones registradas"
      rightContent={
        <Button href="/fluvial/tramites/empadronamiento/nuevo" icon={<PlusCircle size={16} />}>
          Nuevo registro
        </Button>
      }
    />
  );
}
shared/components/BackButton.tsxA back-navigation row that includes a chevron link on the left and an optional step indicator pill on the right. Use it at the top of multi-step form pages to let users navigate back and see their progress.Props
type Props = {
  backHref: string;       // required — the href the chevron link points to
  backLabel?: string;     // default: "Regresar" — label next to the chevron
  stepText?: string;      // optional — if provided, renders an info pill on the right
};
Usage
import BackButton from "@/shared/components/BackButton";

// Basic back navigation
<BackButton backHref="/fluvial/tramites/empadronamiento/dashboard" />

// With custom label and step indicator
<BackButton
  backHref="/fluvial/tramites/empadronamiento/paso-1"
  backLabel="Volver al paso anterior"
  stepText="Paso 2 de 4"
/>
Only pass stepText when the page is part of a numbered multi-step flow. The pill uses an Info icon and is automatically hidden when stepText is undefined.

Interactive components

shared/components/Button.tsxThe primary interactive element across the panel. Supports four visual variants, three sizes, optional icons, a loading spinner, and programmatic navigation via href.Props
type Variant = "primary" | "secondary" | "danger" | "ghost";
type Size = "sm" | "md" | "lg";

type Props = {
  children: ReactNode;       // required — button label
  onClick?: () => void;      // click handler (runs after href navigation if both are set)
  href?: string;             // if provided, calls router.push(href) on click
  type?: "button" | "submit"; // default: "button"
  variant?: Variant;         // default: "primary"
  size?: Size;               // default: "md"
  icon?: ReactNode;          // icon rendered before the label; hidden when loading=true
  loading?: boolean;         // default: false — shows a spinning Loader2 icon
  disabled?: boolean;        // default: false — disables click and reduces opacity
  fullWidth?: boolean;       // default: false — sets w-full
};
Variant styles
VariantAppearance
primaryNavy (#001f3f) background, white text
secondaryWhite background, gray border, dark text
dangerRed-600 background, white text
ghostTransparent background, gray text, hover fill
Size tokens
SizePaddingFont size
smpx-3 py-1.5text-xs
mdpx-4 py-2text-sm
lgpx-5 py-3text-base
Usage
import Button from "@/shared/components/Button";
import { PlusCircle, Trash2 } from "lucide-react";

// Primary action with icon
<Button icon={<PlusCircle size={16} />} href="/fluvial/tramites/empadronamiento/nuevo">
  Nuevo registro
</Button>

// Form submit with loading state
<Button type="submit" loading={isSubmitting} fullWidth>
  Guardar
</Button>

// Danger confirmation
<Button variant="danger" size="sm" onClick={handleDelete} icon={<Trash2 size={14} />}>
  Eliminar
</Button>

// Secondary cancel
<Button variant="secondary" onClick={onClose}>
  Cancelar
</Button>
shared/components/ModalBase.tsxA centered overlay modal with a blurred backdrop, a header section, and a scrollable body. It closes when you click the backdrop, click the X button, or press Escape.Props
type Props = {
  title: string;       // required — displayed as an <h3> in the modal header
  subtitle?: string;   // optional — smaller text beneath the title
  onClose: () => void; // required — called on backdrop click, X click, or Escape key
  children: ReactNode; // required — rendered in the modal body
};
Usage
import { useState } from "react";
import ModalBase from "@/shared/components/ModalBase";
import Button from "@/shared/components/Button";

export default function EditarPropietarioModal({ propietarioId }: { propietarioId: string }) {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setOpen(true)}>Editar propietario</Button>

      {open && (
        <ModalBase
          title="Editar propietario"
          subtitle={`ID: ${propietarioId}`}
          onClose={() => setOpen(false)}
        >
          {/* form fields */}
        </ModalBase>
      )}
    </>
  );
}
The modal body is wrapped in a space-y-6 div. Place each form section or field group as a direct child to get consistent vertical spacing automatically.

Data display components

shared/components/table-pagination/DataTable.tsxA generic, fully-typed paginated data table. It renders a header with a title and record count, an optional search input with an optional type selector, a striped table body, and a smart pagination row. It is the standard table component for all list views in the panel.Type parameterDataTable<T>T is the shape of a single data record. TypeScript infers T from the data and columns props.Column type
export type Column<T> = {
  header: string;           // column heading text
  accessor?: keyof T;       // key on T to display as a string
  render?: (item: T) => ReactNode; // custom cell renderer; takes priority over accessor
  className?: string;       // optional class for the <td>
};
Props
type Props<T> = {
  title: string;             // table card title
  total: number;             // total record count (shown as "N registros encontrados")
  data: T[];                 // current page's records
  columns: Column<T>[];      // column definitions
  keyField: keyof T;         // unique key field used as React key
  loading?: boolean;         // shows "Cargando..." row when true

  page: number;              // current page number (1-indexed)
  totalPages: number;        // total number of pages
  onPageChange: (page: number) => void;

  search?: string;           // controlled search input value
  onSearchChange?: (value: string) => void;
  onSearchSubmit?: () => void; // if provided, renders a "Buscar" button

  searchType?: string;       // controlled search type select value
  onSearchTypeChange?: (value: string) => void;
  searchTypeOptions?: { label: string; value: string }[]; // populates the type dropdown

  extraActions?: ReactNode;  // additional buttons rendered in the header action row
};
Pagination behaviourThe pagination row only renders when totalPages > 1. It shows at most 5 page buttons centered around the current page, with ellipsis (...) to jump to the first and last pages when needed.Usage
import { DataTable, Column } from "@/shared/components/table-pagination/DataTable";
import { useState } from "react";

type Nave = { id: string; nombre: string; matricula: string; estado: string };

const columns: Column<Nave>[] = [
  { header: "Matrícula", accessor: "matricula" },
  { header: "Nombre", accessor: "nombre" },
  {
    header: "Estado",
    render: (nave) => (
      <span className={nave.estado === "activo" ? "text-green-600" : "text-red-600"}>
        {nave.estado}
      </span>
    ),
  },
];

export default function NavesPage() {
  const [page, setPage] = useState(1);
  const [search, setSearch] = useState("");

  const { data, total, totalPages, loading } = useNaves({ page, search });

  return (
    <DataTable<Nave>
      title="Naves registradas"
      total={total}
      data={data}
      columns={columns}
      keyField="id"
      loading={loading}
      page={page}
      totalPages={totalPages}
      onPageChange={setPage}
      search={search}
      onSearchChange={setSearch}
      onSearchSubmit={() => setPage(1)}
    />
  );
}
Always reset page to 1 inside onSearchSubmit to avoid being on a page that no longer exists after filtering.

Build docs developers (and LLMs) love