updated left menu and page template
This commit is contained in:
parent
dd4a8f333d
commit
da95b629ac
|
|
@ -1,11 +1,10 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { DataType, Pagination } from './schema';
|
import { DataType } from "./schema";
|
||||||
import { getTranslation, LanguageKey } from './language';
|
import { getTranslation, LanguageKey } from "./language";
|
||||||
import { ActionButtonsComponent } from './ActionButtonsComponent';
|
import { ActionButtonsComponent } from "./ActionButtonsComponent";
|
||||||
import { SortingComponent } from './SortingComponent';
|
import { SortingComponent } from "./SortingComponent";
|
||||||
import { PaginationToolsComponent } from './PaginationToolsComponent';
|
import { PaginationToolsComponent } from "./PaginationToolsComponent";
|
||||||
|
import { PagePagination } from "@/components/validations/list/paginations";
|
||||||
|
|
||||||
|
|
||||||
interface DataCardProps {
|
interface DataCardProps {
|
||||||
item: DataType;
|
item: DataType;
|
||||||
|
|
@ -18,7 +17,7 @@ export function DataCard({
|
||||||
item,
|
item,
|
||||||
onView,
|
onView,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
lang = 'en'
|
lang = "en",
|
||||||
}: DataCardProps) {
|
}: DataCardProps) {
|
||||||
const t = getTranslation(lang);
|
const t = getTranslation(lang);
|
||||||
|
|
||||||
|
|
@ -29,11 +28,18 @@ export function DataCard({
|
||||||
<h3 className="text-lg font-semibold">{item.title}</h3>
|
<h3 className="text-lg font-semibold">{item.title}</h3>
|
||||||
<p className="text-gray-600 mt-1">{item.description}</p>
|
<p className="text-gray-600 mt-1">{item.description}</p>
|
||||||
<div className="mt-2 flex items-center">
|
<div className="mt-2 flex items-center">
|
||||||
<span className={`px-2 py-1 rounded text-xs ${item.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}`}>
|
<span
|
||||||
|
className={`px-2 py-1 rounded text-xs ${
|
||||||
|
item.status === "active"
|
||||||
|
? "bg-green-100 text-green-800"
|
||||||
|
: "bg-gray-100 text-gray-800"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{item.status}
|
{item.status}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-500 ml-2">
|
<span className="text-xs text-gray-500 ml-2">
|
||||||
{t.formLabels.createdAt}: {new Date(item.createdAt).toLocaleDateString()}
|
{t.formLabels.createdAt}:{" "}
|
||||||
|
{new Date(item.createdAt).toLocaleDateString()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -58,10 +64,10 @@ export function DataCard({
|
||||||
|
|
||||||
interface ListInfoComponentProps {
|
interface ListInfoComponentProps {
|
||||||
data: DataType[];
|
data: DataType[];
|
||||||
pagination: Pagination;
|
pagination: PagePagination;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
error: Error | null;
|
error: Error | null;
|
||||||
updatePagination: (updates: Partial<Pagination>) => void;
|
updatePagination: (updates: Partial<PagePagination>) => void;
|
||||||
onCreateClick: () => void;
|
onCreateClick: () => void;
|
||||||
onViewClick: (item: DataType) => void;
|
onViewClick: (item: DataType) => void;
|
||||||
onUpdateClick: (item: DataType) => void;
|
onUpdateClick: (item: DataType) => void;
|
||||||
|
|
@ -77,7 +83,7 @@ export function ListInfoComponent({
|
||||||
onCreateClick,
|
onCreateClick,
|
||||||
onViewClick,
|
onViewClick,
|
||||||
onUpdateClick,
|
onUpdateClick,
|
||||||
lang = 'en'
|
lang = "en",
|
||||||
}: ListInfoComponentProps) {
|
}: ListInfoComponentProps) {
|
||||||
const t = getTranslation(lang);
|
const t = getTranslation(lang);
|
||||||
|
|
||||||
|
|
@ -91,10 +97,7 @@ export function ListInfoComponent({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ActionButtonsComponent
|
<ActionButtonsComponent onCreateClick={onCreateClick} lang={lang} />
|
||||||
onCreateClick={onCreateClick}
|
|
||||||
lang={lang}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SortingComponent
|
<SortingComponent
|
||||||
pagination={pagination}
|
pagination={pagination}
|
||||||
|
|
@ -114,7 +117,7 @@ export function ListInfoComponent({
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{data.map(item => (
|
{data.map((item) => (
|
||||||
<DataCard
|
<DataCard
|
||||||
key={item.id}
|
key={item.id}
|
||||||
item={item}
|
item={item}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Pagination } from './schema';
|
import { getTranslation, LanguageKey } from "./language";
|
||||||
import { getTranslation, LanguageKey } from './language';
|
import { PagePagination } from "@/components/validations/list/paginations";
|
||||||
|
|
||||||
interface PaginationToolsComponentProps {
|
interface PaginationToolsComponentProps {
|
||||||
pagination: Pagination;
|
pagination: PagePagination;
|
||||||
updatePagination: (updates: Partial<Pagination>) => void;
|
updatePagination: (updates: Partial<PagePagination>) => void;
|
||||||
lang?: LanguageKey;
|
lang?: LanguageKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PaginationToolsComponent({
|
export function PaginationToolsComponent({
|
||||||
pagination,
|
pagination,
|
||||||
updatePagination,
|
updatePagination,
|
||||||
lang = 'en'
|
lang = "en",
|
||||||
}: PaginationToolsComponentProps) {
|
}: PaginationToolsComponentProps) {
|
||||||
const t = getTranslation(lang);
|
const t = getTranslation(lang);
|
||||||
|
|
||||||
|
|
@ -53,23 +53,32 @@ export function PaginationToolsComponent({
|
||||||
|
|
||||||
{/* Items per page selector */}
|
{/* Items per page selector */}
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<label htmlFor="page-size" className="text-sm font-medium">{t.itemsPerPage}</label>
|
<label htmlFor="page-size" className="text-sm font-medium">
|
||||||
|
{t.itemsPerPage}
|
||||||
|
</label>
|
||||||
<select
|
<select
|
||||||
id="page-size"
|
id="page-size"
|
||||||
value={pagination.size}
|
value={pagination.size}
|
||||||
onChange={handleSizeChange}
|
onChange={handleSizeChange}
|
||||||
className="border rounded px-2 py-1"
|
className="border rounded px-2 py-1"
|
||||||
>
|
>
|
||||||
{[5, 10, 20, 50].map(size => (
|
{[5, 10, 20, 50].map((size) => (
|
||||||
<option key={size} value={size}>{size}</option>
|
<option key={size} value={size}>
|
||||||
|
{size}
|
||||||
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pagination stats */}
|
{/* Pagination stats */}
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-sm text-gray-600">
|
||||||
<div>{t.showing} {pagination.pageCount} {t.of} {pagination.totalCount} {t.items}</div>
|
<div>
|
||||||
<div>{t.total}: {pagination.allCount} {t.items}</div>
|
{t.showing} {pagination.pageCount} {t.of} {pagination.totalCount}{" "}
|
||||||
|
{t.items}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{t.total}: {pagination.allCount} {t.items}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from "react";
|
||||||
import { DataSchema } from './schema';
|
import { DataSchema } from "./schema";
|
||||||
import { getTranslation, LanguageKey } from './language';
|
import { getTranslation, LanguageKey } from "./language";
|
||||||
|
|
||||||
interface SearchComponentProps {
|
interface SearchComponentProps {
|
||||||
onSearch: (query: Record<string, string>) => void;
|
onSearch: (query: Record<string, string>) => void;
|
||||||
lang?: LanguageKey;
|
lang?: LanguageKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SearchComponent({ onSearch, lang = 'en' }: SearchComponentProps) {
|
export function SearchComponent({
|
||||||
|
onSearch,
|
||||||
|
lang = "en",
|
||||||
|
}: SearchComponentProps) {
|
||||||
const t = getTranslation(lang);
|
const t = getTranslation(lang);
|
||||||
const [searchValue, setSearchValue] = useState('');
|
const [searchValue, setSearchValue] = useState("");
|
||||||
const [activeFields, setActiveFields] = useState<string[]>([]);
|
const [activeFields, setActiveFields] = useState<string[]>([]);
|
||||||
const [searchQuery, setSearchQuery] = useState<Record<string, string>>({});
|
const [searchQuery, setSearchQuery] = useState<Record<string, string>>({});
|
||||||
|
|
||||||
|
|
@ -22,7 +25,7 @@ export function SearchComponent({ onSearch, lang = 'en' }: SearchComponentProps)
|
||||||
|
|
||||||
// Only add fields if we have a search value
|
// Only add fields if we have a search value
|
||||||
if (searchValue) {
|
if (searchValue) {
|
||||||
activeFields.forEach(field => {
|
activeFields.forEach((field) => {
|
||||||
newQuery[field] = searchValue;
|
newQuery[field] = searchValue;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +60,7 @@ export function SearchComponent({ onSearch, lang = 'en' }: SearchComponentProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
const newQuery: Record<string, string> = {};
|
const newQuery: Record<string, string> = {};
|
||||||
activeFields.forEach(field => {
|
activeFields.forEach((field) => {
|
||||||
newQuery[field] = value;
|
newQuery[field] = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -66,9 +69,9 @@ export function SearchComponent({ onSearch, lang = 'en' }: SearchComponentProps)
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleField = (field: string) => {
|
const toggleField = (field: string) => {
|
||||||
setActiveFields(prev => {
|
setActiveFields((prev) => {
|
||||||
if (prev.includes(field)) {
|
if (prev.includes(field)) {
|
||||||
return prev.filter(f => f !== field);
|
return prev.filter((f) => f !== field);
|
||||||
} else {
|
} else {
|
||||||
return [...prev, field];
|
return [...prev, field];
|
||||||
}
|
}
|
||||||
|
|
@ -79,29 +82,31 @@ export function SearchComponent({ onSearch, lang = 'en' }: SearchComponentProps)
|
||||||
<div className="bg-white p-4 rounded-lg shadow mb-4">
|
<div className="bg-white p-4 rounded-lg shadow mb-4">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label htmlFor="search" className="block text-sm font-medium mb-1">
|
<label htmlFor="search" className="block text-sm font-medium mb-1">
|
||||||
{t.search || 'Search'}
|
{t.search || "Search"}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="search"
|
id="search"
|
||||||
value={searchValue}
|
value={searchValue}
|
||||||
onChange={handleSearchChange}
|
onChange={handleSearchChange}
|
||||||
placeholder={t.searchPlaceholder || 'Enter search term...'}
|
placeholder={t.searchPlaceholder || "Enter search term..."}
|
||||||
className="w-full p-2 border rounded focus:ring-blue-500 focus:border-blue-500"
|
className="w-full p-2 border rounded focus:ring-blue-500 focus:border-blue-500"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-medium mb-1">{t.searchFields || 'Search in fields'}:</div>
|
<div className="text-sm font-medium mb-1">
|
||||||
|
{t.searchFields || "Search in fields"}:
|
||||||
|
</div>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{Object.keys(DataSchema.shape).map(field => (
|
{Object.keys(DataSchema.shape).map((field) => (
|
||||||
<button
|
<button
|
||||||
key={field}
|
key={field}
|
||||||
onClick={() => toggleField(field)}
|
onClick={() => toggleField(field)}
|
||||||
className={`px-3 py-1 text-sm rounded ${
|
className={`px-3 py-1 text-sm rounded ${
|
||||||
activeFields.includes(field)
|
activeFields.includes(field)
|
||||||
? 'bg-blue-500 text-white'
|
? "bg-blue-500 text-white"
|
||||||
: 'bg-gray-200 text-gray-700'
|
: "bg-gray-200 text-gray-700"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{field}
|
{field}
|
||||||
|
|
@ -112,10 +117,15 @@ export function SearchComponent({ onSearch, lang = 'en' }: SearchComponentProps)
|
||||||
|
|
||||||
{Object.keys(searchQuery).length > 0 && (
|
{Object.keys(searchQuery).length > 0 && (
|
||||||
<div className="mt-3 p-2 bg-gray-100 rounded">
|
<div className="mt-3 p-2 bg-gray-100 rounded">
|
||||||
<div className="text-sm font-medium mb-1">{t.activeSearch || 'Active search'}:</div>
|
<div className="text-sm font-medium mb-1">
|
||||||
|
{t.activeSearch || "Active search"}:
|
||||||
|
</div>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{Object.entries(searchQuery).map(([field, value]) => (
|
{Object.entries(searchQuery).map(([field, value]) => (
|
||||||
<div key={field} className="bg-blue-100 text-blue-800 px-2 py-1 rounded text-sm">
|
<div
|
||||||
|
key={field}
|
||||||
|
className="bg-blue-100 text-blue-800 px-2 py-1 rounded text-sm"
|
||||||
|
>
|
||||||
{field}: {value}
|
{field}: {value}
|
||||||
<button
|
<button
|
||||||
onClick={() => toggleField(field)}
|
onClick={() => toggleField(field)}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Pagination, DataSchema } from './schema';
|
import { DataSchema } from "./schema";
|
||||||
import { getTranslation, LanguageKey } from './language';
|
import { getTranslation, LanguageKey } from "./language";
|
||||||
|
import { PagePagination } from "@/components/validations/list/paginations";
|
||||||
|
|
||||||
interface SortingComponentProps {
|
interface SortingComponentProps {
|
||||||
pagination: Pagination;
|
pagination: PagePagination;
|
||||||
updatePagination: (updates: Partial<Pagination>) => void;
|
updatePagination: (updates: Partial<PagePagination>) => void;
|
||||||
lang?: LanguageKey;
|
lang?: LanguageKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SortingComponent({
|
export function SortingComponent({
|
||||||
pagination,
|
pagination,
|
||||||
updatePagination,
|
updatePagination,
|
||||||
lang = 'en'
|
lang = "en",
|
||||||
}: SortingComponentProps) {
|
}: SortingComponentProps) {
|
||||||
const t = getTranslation(lang);
|
const t = getTranslation(lang);
|
||||||
|
|
||||||
|
|
@ -26,10 +27,10 @@ export function SortingComponent({
|
||||||
if (fieldIndex === -1) {
|
if (fieldIndex === -1) {
|
||||||
// Field is not being sorted yet - add it with 'asc' direction
|
// Field is not being sorted yet - add it with 'asc' direction
|
||||||
newOrderFields.push(field);
|
newOrderFields.push(field);
|
||||||
newOrderTypes.push('asc');
|
newOrderTypes.push("asc");
|
||||||
} else if (pagination.orderTypes[fieldIndex] === 'asc') {
|
} else if (pagination.orderTypes[fieldIndex] === "asc") {
|
||||||
// Field is being sorted ascending - change to descending
|
// Field is being sorted ascending - change to descending
|
||||||
newOrderTypes[fieldIndex] = 'desc';
|
newOrderTypes[fieldIndex] = "desc";
|
||||||
} else {
|
} else {
|
||||||
// Field is being sorted descending - remove it from sorting
|
// Field is being sorted descending - remove it from sorting
|
||||||
newOrderFields.splice(fieldIndex, 1);
|
newOrderFields.splice(fieldIndex, 1);
|
||||||
|
|
@ -48,22 +49,26 @@ export function SortingComponent({
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<label className="text-sm font-medium">{t.sortBy}</label>
|
<label className="text-sm font-medium">{t.sortBy}</label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{Object.keys(DataSchema.shape).map(field => {
|
{Object.keys(DataSchema.shape).map((field) => {
|
||||||
// Find if this field is in the orderFields array
|
// Find if this field is in the orderFields array
|
||||||
const fieldIndex = pagination.orderFields.indexOf(field);
|
const fieldIndex = pagination.orderFields.indexOf(field);
|
||||||
const isActive = fieldIndex !== -1;
|
const isActive = fieldIndex !== -1;
|
||||||
const direction = isActive ? pagination.orderTypes[fieldIndex] : null;
|
const direction = isActive
|
||||||
|
? pagination.orderTypes[fieldIndex]
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={field}
|
key={field}
|
||||||
onClick={() => handleSortChange(field)}
|
onClick={() => handleSortChange(field)}
|
||||||
className={`px-3 py-1 rounded ${isActive ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}
|
className={`px-3 py-1 rounded ${
|
||||||
|
isActive ? "bg-blue-500 text-white" : "bg-gray-200"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{field}
|
{field}
|
||||||
{isActive && (
|
{isActive && (
|
||||||
<span className="ml-1">
|
<span className="ml-1">
|
||||||
{direction === 'asc' ? '↑' : '↓'}
|
{direction === "asc" ? "↑" : "↓"}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,10 @@
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import { DataType, Pagination, fetchData, DataSchema } from './schema';
|
import { DataType, fetchData, DataSchema } from "./schema";
|
||||||
|
import {
|
||||||
// Define request parameters interface
|
PagePagination,
|
||||||
interface RequestParams {
|
RequestParams,
|
||||||
page: number;
|
ResponseMetadata,
|
||||||
size: number;
|
} from "@/components/validations/list/paginations";
|
||||||
orderFields: string[];
|
|
||||||
orderTypes: string[];
|
|
||||||
query: Record<string, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define response metadata interface
|
|
||||||
interface ResponseMetadata {
|
|
||||||
totalCount: number;
|
|
||||||
allCount: number;
|
|
||||||
totalPages: number;
|
|
||||||
pageCount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom hook for pagination and data fetching
|
// Custom hook for pagination and data fetching
|
||||||
export function usePaginatedData() {
|
export function usePaginatedData() {
|
||||||
|
|
@ -26,8 +14,8 @@ export function usePaginatedData() {
|
||||||
const [requestParams, setRequestParams] = useState<RequestParams>({
|
const [requestParams, setRequestParams] = useState<RequestParams>({
|
||||||
page: 1,
|
page: 1,
|
||||||
size: 10,
|
size: 10,
|
||||||
orderFields: ['createdAt'],
|
orderFields: ["createdAt"],
|
||||||
orderTypes: ['desc'],
|
orderTypes: ["desc"],
|
||||||
query: {},
|
query: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -54,14 +42,16 @@ export function usePaginatedData() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Validate data with Zod
|
// Validate data with Zod
|
||||||
const validatedData = result.data.map(item => {
|
const validatedData = result.data
|
||||||
try {
|
.map((item) => {
|
||||||
return DataSchema.parse(item);
|
try {
|
||||||
} catch (err) {
|
return DataSchema.parse(item);
|
||||||
console.error('Validation error for item:', item, err);
|
} catch (err) {
|
||||||
return null;
|
console.error("Validation error for item:", item, err);
|
||||||
}
|
return null;
|
||||||
}).filter(Boolean) as DataType[];
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean) as DataType[];
|
||||||
|
|
||||||
setData(validatedData);
|
setData(validatedData);
|
||||||
|
|
||||||
|
|
@ -75,11 +65,17 @@ export function usePaginatedData() {
|
||||||
|
|
||||||
setError(null);
|
setError(null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err : new Error('Unknown error'));
|
setError(err instanceof Error ? err : new Error("Unknown error"));
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [requestParams.page, requestParams.size, requestParams.orderFields, requestParams.orderTypes, requestParams.query]);
|
}, [
|
||||||
|
requestParams.page,
|
||||||
|
requestParams.size,
|
||||||
|
requestParams.orderFields,
|
||||||
|
requestParams.orderTypes,
|
||||||
|
requestParams.query,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
|
|
@ -90,7 +86,7 @@ export function usePaginatedData() {
|
||||||
}, [fetchDataFromApi]);
|
}, [fetchDataFromApi]);
|
||||||
|
|
||||||
const updatePagination = (updates: Partial<RequestParams>) => {
|
const updatePagination = (updates: Partial<RequestParams>) => {
|
||||||
setRequestParams(prev => ({
|
setRequestParams((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
...updates,
|
...updates,
|
||||||
}));
|
}));
|
||||||
|
|
@ -98,7 +94,7 @@ export function usePaginatedData() {
|
||||||
|
|
||||||
// Create a combined refetch object that includes the setQuery function
|
// Create a combined refetch object that includes the setQuery function
|
||||||
const setQuery = (query: Record<string, string>) => {
|
const setQuery = (query: Record<string, string>) => {
|
||||||
setRequestParams(prev => ({
|
setRequestParams((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
query,
|
query,
|
||||||
}));
|
}));
|
||||||
|
|
@ -107,7 +103,7 @@ export function usePaginatedData() {
|
||||||
const refetch = Object.assign(fetchDataFromApi, { setQuery });
|
const refetch = Object.assign(fetchDataFromApi, { setQuery });
|
||||||
|
|
||||||
// Combine request params and response metadata for backward compatibility
|
// Combine request params and response metadata for backward compatibility
|
||||||
const pagination: Pagination = {
|
const pagination: PagePagination = {
|
||||||
...requestParams,
|
...requestParams,
|
||||||
...responseMetadata,
|
...responseMetadata,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,55 @@
|
||||||
// Language dictionary for the template component
|
// Language dictionary for the template component
|
||||||
const language = {
|
const language = {
|
||||||
en: {
|
en: {
|
||||||
title: 'Data Management',
|
title: "Data Management",
|
||||||
create: 'Create New',
|
create: "Create New",
|
||||||
view: 'View Item',
|
view: "View Item",
|
||||||
update: 'Update Item',
|
update: "Update Item",
|
||||||
createNew: 'Create New Item',
|
createNew: "Create New Item",
|
||||||
back: 'Back',
|
back: "Back",
|
||||||
cancel: 'Cancel',
|
cancel: "Cancel",
|
||||||
submit: 'Submit',
|
submit: "Submit",
|
||||||
noItemsFound: 'No items found',
|
noItemsFound: "No items found",
|
||||||
previous: 'Previous',
|
previous: "Previous",
|
||||||
next: 'Next',
|
next: "Next",
|
||||||
page: 'Page',
|
page: "Page",
|
||||||
of: 'of',
|
of: "of",
|
||||||
itemsPerPage: 'Items per page:',
|
itemsPerPage: "Items per page:",
|
||||||
sortBy: 'Sort by:',
|
sortBy: "Sort by:",
|
||||||
loading: 'Loading...',
|
loading: "Loading...",
|
||||||
error: 'Error loading data:',
|
error: "Error loading data:",
|
||||||
showing: 'Showing',
|
showing: "Showing",
|
||||||
items: 'items',
|
items: "items",
|
||||||
total: 'Total',
|
total: "Total",
|
||||||
// Search related translations
|
// Search related translations
|
||||||
search: 'Search',
|
search: "Search",
|
||||||
searchPlaceholder: 'Enter search term...',
|
searchPlaceholder: "Enter search term...",
|
||||||
searchFields: 'Search in fields',
|
searchFields: "Search in fields",
|
||||||
activeSearch: 'Active search',
|
activeSearch: "Active search",
|
||||||
clearSearch: 'Clear',
|
clearSearch: "Clear",
|
||||||
formLabels: {
|
formLabels: {
|
||||||
title: 'Title',
|
title: "Title",
|
||||||
description: 'Description',
|
description: "Description",
|
||||||
status: 'Status',
|
status: "Status",
|
||||||
createdAt: 'Created'
|
createdAt: "Created",
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
active: 'Active',
|
active: "Active",
|
||||||
inactive: 'Inactive'
|
inactive: "Inactive",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
view: 'View',
|
view: "View",
|
||||||
update: 'Update',
|
update: "Update",
|
||||||
create: 'Create',
|
create: "Create",
|
||||||
save: 'Save'
|
save: "Save",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
// Add more languages as needed
|
// Add more languages as needed
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LanguageKey = keyof typeof language;
|
export type LanguageKey = keyof typeof language;
|
||||||
|
|
||||||
export const getTranslation = (lang: LanguageKey = 'en') => {
|
export const getTranslation = (lang: LanguageKey = "en") => {
|
||||||
return language[lang] || language.en;
|
return language[lang] || language.en;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { PagePagination } from "@/components/validations/list/paginations";
|
||||||
|
|
||||||
// Define the data schema using Zod
|
// Define the data schema using Zod
|
||||||
export const DataSchema = z.object({
|
export const DataSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
|
|
@ -12,19 +14,6 @@ export const DataSchema = z.object({
|
||||||
|
|
||||||
export type DataType = z.infer<typeof DataSchema>;
|
export type DataType = z.infer<typeof DataSchema>;
|
||||||
|
|
||||||
// Define pagination interface
|
|
||||||
export interface Pagination {
|
|
||||||
page: number;
|
|
||||||
size: number;
|
|
||||||
totalCount: number;
|
|
||||||
allCount: number;
|
|
||||||
totalPages: number;
|
|
||||||
orderFields: string[];
|
|
||||||
orderTypes: string[];
|
|
||||||
pageCount: number;
|
|
||||||
query: Record<string, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock API function (replace with your actual API call)
|
// Mock API function (replace with your actual API call)
|
||||||
export const fetchData = async ({
|
export const fetchData = async ({
|
||||||
page = 1,
|
page = 1,
|
||||||
|
|
@ -49,7 +38,7 @@ export const fetchData = async ({
|
||||||
});
|
});
|
||||||
|
|
||||||
// Simulated API response
|
// Simulated API response
|
||||||
return new Promise<{ data: DataType[]; pagination: Pagination }>(
|
return new Promise<{ data: DataType[]; pagination: PagePagination }>(
|
||||||
(resolve) => {
|
(resolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Generate mock data
|
// Generate mock data
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Define pagination interface
|
||||||
|
export interface PagePagination {
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
totalCount: number;
|
||||||
|
allCount: number;
|
||||||
|
totalPages: number;
|
||||||
|
orderFields: string[];
|
||||||
|
orderTypes: string[];
|
||||||
|
pageCount: number;
|
||||||
|
query: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Define request parameters interface
|
||||||
|
export interface RequestParams {
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
orderFields: string[];
|
||||||
|
orderTypes: string[];
|
||||||
|
query: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define response metadata interface
|
||||||
|
export interface ResponseMetadata {
|
||||||
|
totalCount: number;
|
||||||
|
allCount: number;
|
||||||
|
totalPages: number;
|
||||||
|
pageCount: number;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue