|
|
||
|---|---|---|
| .. | ||
| DashboardLayout.tsx | ||
| PageTemplate.tsx | ||
| README.md | ||
| schema.ts | ||
README.md
Modular Dashboard Layout System
This directory contains reusable layout components for building dashboard pages with a consistent structure and appearance.
Components
DashboardLayout
The DashboardLayout component provides the overall page structure with:
- Sidebar navigation
- Header
- Main content area
PageTemplate
The PageTemplate component provides a standardized structure for page content with:
- Header section with title and selection components
- Search filters section
- Action buttons section
- Pagination tools section
- Content display section
- Form display for create/update/view modes
Usage Example
Here's an example of how to use these components to create a new dashboard page:
"use client";
import React, { useState } from "react";
import { useApiData } from "@/components/common/hooks/useApiData";
import { CardDisplay } from "@/components/common/CardDisplay";
import { FormDisplay } from "@/components/common/FormDisplay/FormDisplay";
import { TextQueryModifier, SelectQueryModifier, TypeQueryModifier } from "@/components/common/QueryModifiers";
import { Card, CardContent } from "@/components/ui/card";
import { Language } from "@/components/common/HeaderSelections/LanguageSelectionComponent";
import { FormMode } from "@/components/common/FormDisplay/types";
import { PageTemplate } from "@/components/layouts/PageTemplate";
import type { GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent";
// Import your schema and translations
import * as schema from "./schema";
import { translations } from "./language";
const ExamplePage: React.FC<{ lang: "en" | "tr", queryParams: any }> = ({
lang = "en",
queryParams
}) => {
// API data hook
const {
data,
pagination,
loading,
error,
updatePagination,
refetch
} = useApiData<schema.ExampleData>('/api/examples');
// State management
const [mode, setMode] = useState<FormMode>("list");
const [selectedItem, setSelectedItem] = useState<schema.ExampleData | null>(null);
const [gridCols, setGridCols] = useState<GridSize>(3);
const [currentLang, setCurrentLang] = useState<Language>(lang as Language);
// Fields to display in cards
const showFields = ["name", "type", "status"];
// Query handling
const handleQueryChange = (key: string, value: string | null) => {
const newQuery = { ...pagination.query };
if (value === null || value.trim() === "") {
delete newQuery[key];
} else {
newQuery[key] = value;
}
updatePagination({
page: 1,
query: newQuery,
});
};
// Reset all filters
const handleResetAllFilters = () => {
updatePagination({
page: 1,
query: {},
});
};
// Action handlers
const handleCardClick = (item: schema.ExampleData) => {
console.log("Card clicked:", item);
};
const handleViewClick = (item: schema.ExampleData) => {
setSelectedItem(item);
setMode("view");
};
const handleUpdateClick = (item: schema.ExampleData) => {
setSelectedItem(item);
setMode("update");
};
const handleCreateClick = () => {
setSelectedItem(null);
setMode("create");
};
const handleCancel = () => {
setMode("list");
setSelectedItem(null);
};
// Search section component
const SearchSection = (
<div className="flex flex-col space-y-4">
<div className="flex space-x-4">
{/* Type selector */}
<TypeQueryModifier
fieldKey="type"
value={pagination.query["type"] || ""}
options={[
{ value: "type1", label: translations[lang].type1 },
{ value: "type2", label: translations[lang].type2 }
]}
onQueryChange={handleQueryChange}
translations={translations}
lang={lang}
/>
</div>
<div className="flex space-x-4">
{/* Text search */}
<TextQueryModifier
fieldKey="name"
value={pagination.query["name__ilike"] ? pagination.query["name__ilike"].replace(/%/g, "") : ""}
label={translations[lang].search || "Search"}
onQueryChange={handleQueryChange}
translations={translations}
lang={lang}
/>
{/* Status dropdown */}
<SelectQueryModifier
fieldKey="status"
value={pagination.query["status"] || ""}
label={translations[lang].status || "Status"}
options={[
{ value: "active", label: translations[lang].active || "Active" },
{ value: "inactive", label: translations[lang].inactive || "Inactive" }
]}
onQueryChange={handleQueryChange}
translations={translations}
lang={lang}
/>
</div>
{/* Reset filters button */}
<div className="flex justify-end">
<button
onClick={handleResetAllFilters}
className="px-4 py-2 bg-gray-100 rounded-md hover:bg-gray-200"
>
{translations[lang].resetAll || "Reset All"}
</button>
</div>
</div>
);
// Form component
const FormComponent = selectedItem || mode === "create" ? (
<FormDisplay<schema.ExampleData>
initialData={selectedItem || undefined}
mode={mode}
refetch={refetch}
setMode={setMode}
setSelectedItem={setSelectedItem}
onCancel={handleCancel}
lang={lang}
translations={translations}
formProps={{
fieldDefinitions: mode === 'create' ? schema.createFieldDefinitions :
mode === 'update' ? schema.updateFieldDefinitions :
schema.viewFieldDefinitions,
validationSchema: mode === 'create' ? schema.CreateExampleSchema :
mode === 'update' ? schema.UpdateExampleSchema :
schema.ViewExampleSchema,
fieldsByMode: schema.fieldsByMode
}}
/>
) : null;
// Content display component
const ContentDisplay = (
<CardDisplay
showFields={showFields}
data={data}
lang={lang}
translations={translations}
error={error}
loading={loading}
titleField="name"
onCardClick={handleCardClick}
gridCols={gridCols}
showViewIcon={true}
showUpdateIcon={true}
onViewClick={handleViewClick}
onUpdateClick={handleUpdateClick}
/>
);
return (
<PageTemplate
title={translations[lang].examplePageTitle || "Example Page"}
lang={lang}
translations={translations}
searchSection={SearchSection}
data={data}
pagination={pagination}
updatePagination={updatePagination}
loading={loading}
error={error}
refetch={refetch}
contentDisplay={ContentDisplay}
formComponent={FormComponent}
mode={mode}
setMode={setMode}
handleCreateClick={handleCreateClick}
handleCancel={handleCancel}
setLang={setCurrentLang}
/>
);
};
export default ExamplePage;
Integration with Next.js App Router
To use these components with Next.js App Router, update your page component like this:
// src/app/(DashboardLayout)/your-page/page.tsx
import React from "react";
import { retrievePageByUrl } from "@/eventRouters/pageRetriever";
import DashboardLayout from "@/components/layouts/DashboardLayout";
async function YourPage({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | undefined }>;
}) {
const activePage = "/your-page";
const searchParamsInstance = await searchParams;
const lang = (searchParamsInstance?.lang as "en" | "tr") || "en";
const PageComponent = retrievePageByUrl(activePage);
return (
<DashboardLayout lang={lang} activePage={activePage}>
<PageComponent lang={lang} queryParams={searchParamsInstance} />
</DashboardLayout>
);
}
export default YourPage;
This modular approach makes it easy to create new dashboard pages with consistent structure and behavior.