updated left menu and page template
This commit is contained in:
parent
dd4a8f333d
commit
da95b629ac
|
|
@ -1,11 +1,10 @@
|
|||
import React from 'react';
|
||||
import { DataType, Pagination } from './schema';
|
||||
import { getTranslation, LanguageKey } from './language';
|
||||
import { ActionButtonsComponent } from './ActionButtonsComponent';
|
||||
import { SortingComponent } from './SortingComponent';
|
||||
import { PaginationToolsComponent } from './PaginationToolsComponent';
|
||||
|
||||
|
||||
import React from "react";
|
||||
import { DataType } from "./schema";
|
||||
import { getTranslation, LanguageKey } from "./language";
|
||||
import { ActionButtonsComponent } from "./ActionButtonsComponent";
|
||||
import { SortingComponent } from "./SortingComponent";
|
||||
import { PaginationToolsComponent } from "./PaginationToolsComponent";
|
||||
import { PagePagination } from "@/components/validations/list/paginations";
|
||||
|
||||
interface DataCardProps {
|
||||
item: DataType;
|
||||
|
|
@ -14,11 +13,11 @@ interface DataCardProps {
|
|||
lang?: LanguageKey;
|
||||
}
|
||||
|
||||
export function DataCard({
|
||||
item,
|
||||
onView,
|
||||
export function DataCard({
|
||||
item,
|
||||
onView,
|
||||
onUpdate,
|
||||
lang = 'en'
|
||||
lang = "en",
|
||||
}: DataCardProps) {
|
||||
const t = getTranslation(lang);
|
||||
|
||||
|
|
@ -29,11 +28,18 @@ export function DataCard({
|
|||
<h3 className="text-lg font-semibold">{item.title}</h3>
|
||||
<p className="text-gray-600 mt-1">{item.description}</p>
|
||||
<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}
|
||||
</span>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -58,10 +64,10 @@ export function DataCard({
|
|||
|
||||
interface ListInfoComponentProps {
|
||||
data: DataType[];
|
||||
pagination: Pagination;
|
||||
pagination: PagePagination;
|
||||
loading: boolean;
|
||||
error: Error | null;
|
||||
updatePagination: (updates: Partial<Pagination>) => void;
|
||||
updatePagination: (updates: Partial<PagePagination>) => void;
|
||||
onCreateClick: () => void;
|
||||
onViewClick: (item: DataType) => void;
|
||||
onUpdateClick: (item: DataType) => void;
|
||||
|
|
@ -77,7 +83,7 @@ export function ListInfoComponent({
|
|||
onCreateClick,
|
||||
onViewClick,
|
||||
onUpdateClick,
|
||||
lang = 'en'
|
||||
lang = "en",
|
||||
}: ListInfoComponentProps) {
|
||||
const t = getTranslation(lang);
|
||||
|
||||
|
|
@ -91,30 +97,27 @@ export function ListInfoComponent({
|
|||
|
||||
return (
|
||||
<>
|
||||
<ActionButtonsComponent
|
||||
onCreateClick={onCreateClick}
|
||||
lang={lang}
|
||||
/>
|
||||
|
||||
<SortingComponent
|
||||
<ActionButtonsComponent onCreateClick={onCreateClick} lang={lang} />
|
||||
|
||||
<SortingComponent
|
||||
pagination={pagination}
|
||||
updatePagination={updatePagination}
|
||||
lang={lang}
|
||||
/>
|
||||
|
||||
<PaginationToolsComponent
|
||||
|
||||
<PaginationToolsComponent
|
||||
pagination={pagination}
|
||||
updatePagination={updatePagination}
|
||||
lang={lang}
|
||||
/>
|
||||
|
||||
|
||||
{loading ? (
|
||||
<div className="flex justify-center items-center py-12">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-4">
|
||||
{data.map(item => (
|
||||
{data.map((item) => (
|
||||
<DataCard
|
||||
key={item.id}
|
||||
item={item}
|
||||
|
|
@ -123,7 +126,7 @@ export function ListInfoComponent({
|
|||
lang={lang}
|
||||
/>
|
||||
))}
|
||||
|
||||
|
||||
{data.length === 0 && (
|
||||
<div className="text-center py-12 text-gray-500">
|
||||
{t.noItemsFound}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import React from 'react';
|
||||
import { Pagination } from './schema';
|
||||
import { getTranslation, LanguageKey } from './language';
|
||||
import React from "react";
|
||||
import { getTranslation, LanguageKey } from "./language";
|
||||
import { PagePagination } from "@/components/validations/list/paginations";
|
||||
|
||||
interface PaginationToolsComponentProps {
|
||||
pagination: Pagination;
|
||||
updatePagination: (updates: Partial<Pagination>) => void;
|
||||
pagination: PagePagination;
|
||||
updatePagination: (updates: Partial<PagePagination>) => void;
|
||||
lang?: LanguageKey;
|
||||
}
|
||||
|
||||
export function PaginationToolsComponent({
|
||||
pagination,
|
||||
updatePagination,
|
||||
lang = 'en'
|
||||
lang = "en",
|
||||
}: PaginationToolsComponentProps) {
|
||||
const t = getTranslation(lang);
|
||||
|
||||
|
|
@ -30,19 +30,19 @@ export function PaginationToolsComponent({
|
|||
<div className="flex flex-wrap justify-between items-center gap-4">
|
||||
{/* Navigation buttons */}
|
||||
<div className="flex items-center space-x-2">
|
||||
<button
|
||||
<button
|
||||
onClick={() => handlePageChange(pagination.page - 1)}
|
||||
disabled={pagination.page <= 1}
|
||||
className="px-3 py-1 bg-gray-200 rounded disabled:opacity-50"
|
||||
>
|
||||
{t.previous}
|
||||
</button>
|
||||
|
||||
|
||||
<span className="px-4 py-1">
|
||||
{t.page} {pagination.page} {t.of} {pagination.totalPages}
|
||||
</span>
|
||||
|
||||
<button
|
||||
|
||||
<button
|
||||
onClick={() => handlePageChange(pagination.page + 1)}
|
||||
disabled={pagination.page >= pagination.totalPages}
|
||||
className="px-3 py-1 bg-gray-200 rounded disabled:opacity-50"
|
||||
|
|
@ -50,26 +50,35 @@ export function PaginationToolsComponent({
|
|||
{t.next}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Items per page selector */}
|
||||
<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
|
||||
id="page-size"
|
||||
value={pagination.size}
|
||||
onChange={handleSizeChange}
|
||||
className="border rounded px-2 py-1"
|
||||
>
|
||||
{[5, 10, 20, 50].map(size => (
|
||||
<option key={size} value={size}>{size}</option>
|
||||
{[5, 10, 20, 50].map((size) => (
|
||||
<option key={size} value={size}>
|
||||
{size}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Pagination stats */}
|
||||
<div className="text-sm text-gray-600">
|
||||
<div>{t.showing} {pagination.pageCount} {t.of} {pagination.totalCount} {t.items}</div>
|
||||
<div>{t.total}: {pagination.allCount} {t.items}</div>
|
||||
<div>
|
||||
{t.showing} {pagination.pageCount} {t.of} {pagination.totalCount}{" "}
|
||||
{t.items}
|
||||
</div>
|
||||
<div>
|
||||
{t.total}: {pagination.allCount} {t.items}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { DataSchema } from './schema';
|
||||
import { getTranslation, LanguageKey } from './language';
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { DataSchema } from "./schema";
|
||||
import { getTranslation, LanguageKey } from "./language";
|
||||
|
||||
interface SearchComponentProps {
|
||||
onSearch: (query: Record<string, string>) => void;
|
||||
lang?: LanguageKey;
|
||||
}
|
||||
|
||||
export function SearchComponent({ onSearch, lang = 'en' }: SearchComponentProps) {
|
||||
export function SearchComponent({
|
||||
onSearch,
|
||||
lang = "en",
|
||||
}: SearchComponentProps) {
|
||||
const t = getTranslation(lang);
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [activeFields, setActiveFields] = useState<string[]>([]);
|
||||
const [searchQuery, setSearchQuery] = useState<Record<string, string>>({});
|
||||
|
||||
|
|
@ -19,22 +22,22 @@ export function SearchComponent({ onSearch, lang = 'en' }: SearchComponentProps)
|
|||
// or if we have no active fields (to clear the search)
|
||||
if ((activeFields.length > 0 && searchValue) || activeFields.length === 0) {
|
||||
const newQuery: Record<string, string> = {};
|
||||
|
||||
|
||||
// Only add fields if we have a search value
|
||||
if (searchValue) {
|
||||
activeFields.forEach(field => {
|
||||
activeFields.forEach((field) => {
|
||||
newQuery[field] = searchValue;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Update local state
|
||||
setSearchQuery(newQuery);
|
||||
|
||||
|
||||
// Don't call onSearch here - it creates an infinite loop
|
||||
// We'll call it in a separate effect
|
||||
}
|
||||
}, [activeFields, searchValue]);
|
||||
|
||||
|
||||
// This effect handles calling the onSearch callback
|
||||
// It runs when searchQuery changes, not when onSearch changes
|
||||
useEffect(() => {
|
||||
|
|
@ -44,7 +47,7 @@ export function SearchComponent({ onSearch, lang = 'en' }: SearchComponentProps)
|
|||
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value;
|
||||
setSearchValue(value);
|
||||
|
||||
|
||||
if (!value) {
|
||||
setSearchQuery({});
|
||||
onSearch({});
|
||||
|
|
@ -57,18 +60,18 @@ export function SearchComponent({ onSearch, lang = 'en' }: SearchComponentProps)
|
|||
}
|
||||
|
||||
const newQuery: Record<string, string> = {};
|
||||
activeFields.forEach(field => {
|
||||
activeFields.forEach((field) => {
|
||||
newQuery[field] = value;
|
||||
});
|
||||
|
||||
|
||||
setSearchQuery(newQuery);
|
||||
onSearch(newQuery);
|
||||
};
|
||||
|
||||
const toggleField = (field: string) => {
|
||||
setActiveFields(prev => {
|
||||
setActiveFields((prev) => {
|
||||
if (prev.includes(field)) {
|
||||
return prev.filter(f => f !== field);
|
||||
return prev.filter((f) => f !== field);
|
||||
} else {
|
||||
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="mb-3">
|
||||
<label htmlFor="search" className="block text-sm font-medium mb-1">
|
||||
{t.search || 'Search'}
|
||||
{t.search || "Search"}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="search"
|
||||
value={searchValue}
|
||||
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"
|
||||
/>
|
||||
</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">
|
||||
{Object.keys(DataSchema.shape).map(field => (
|
||||
{Object.keys(DataSchema.shape).map((field) => (
|
||||
<button
|
||||
key={field}
|
||||
onClick={() => toggleField(field)}
|
||||
className={`px-3 py-1 text-sm rounded ${
|
||||
activeFields.includes(field)
|
||||
? 'bg-blue-500 text-white'
|
||||
: 'bg-gray-200 text-gray-700'
|
||||
activeFields.includes(field)
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-gray-200 text-gray-700"
|
||||
}`}
|
||||
>
|
||||
{field}
|
||||
|
|
@ -109,15 +114,20 @@ export function SearchComponent({ onSearch, lang = 'en' }: SearchComponentProps)
|
|||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{Object.keys(searchQuery).length > 0 && (
|
||||
<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">
|
||||
{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}
|
||||
<button
|
||||
<button
|
||||
onClick={() => toggleField(field)}
|
||||
className="ml-2 text-blue-500 hover:text-blue-700"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,41 +1,42 @@
|
|||
import React from 'react';
|
||||
import { Pagination, DataSchema } from './schema';
|
||||
import { getTranslation, LanguageKey } from './language';
|
||||
import React from "react";
|
||||
import { DataSchema } from "./schema";
|
||||
import { getTranslation, LanguageKey } from "./language";
|
||||
import { PagePagination } from "@/components/validations/list/paginations";
|
||||
|
||||
interface SortingComponentProps {
|
||||
pagination: Pagination;
|
||||
updatePagination: (updates: Partial<Pagination>) => void;
|
||||
pagination: PagePagination;
|
||||
updatePagination: (updates: Partial<PagePagination>) => void;
|
||||
lang?: LanguageKey;
|
||||
}
|
||||
|
||||
export function SortingComponent({
|
||||
pagination,
|
||||
updatePagination,
|
||||
lang = 'en'
|
||||
lang = "en",
|
||||
}: SortingComponentProps) {
|
||||
const t = getTranslation(lang);
|
||||
|
||||
const handleSortChange = (field: string) => {
|
||||
// Find if the field is already in the orderFields array
|
||||
const fieldIndex = pagination.orderFields.indexOf(field);
|
||||
|
||||
|
||||
// Create copies of the arrays to modify
|
||||
const newOrderFields = [...pagination.orderFields];
|
||||
const newOrderTypes = [...pagination.orderTypes];
|
||||
|
||||
|
||||
if (fieldIndex === -1) {
|
||||
// Field is not being sorted yet - add it with 'asc' direction
|
||||
newOrderFields.push(field);
|
||||
newOrderTypes.push('asc');
|
||||
} else if (pagination.orderTypes[fieldIndex] === 'asc') {
|
||||
newOrderTypes.push("asc");
|
||||
} else if (pagination.orderTypes[fieldIndex] === "asc") {
|
||||
// Field is being sorted ascending - change to descending
|
||||
newOrderTypes[fieldIndex] = 'desc';
|
||||
newOrderTypes[fieldIndex] = "desc";
|
||||
} else {
|
||||
// Field is being sorted descending - remove it from sorting
|
||||
newOrderFields.splice(fieldIndex, 1);
|
||||
newOrderTypes.splice(fieldIndex, 1);
|
||||
}
|
||||
|
||||
|
||||
updatePagination({
|
||||
orderFields: newOrderFields,
|
||||
orderTypes: newOrderTypes,
|
||||
|
|
@ -48,22 +49,26 @@ export function SortingComponent({
|
|||
<div className="flex items-center space-x-2">
|
||||
<label className="text-sm font-medium">{t.sortBy}</label>
|
||||
<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
|
||||
const fieldIndex = pagination.orderFields.indexOf(field);
|
||||
const isActive = fieldIndex !== -1;
|
||||
const direction = isActive ? pagination.orderTypes[fieldIndex] : null;
|
||||
|
||||
const direction = isActive
|
||||
? pagination.orderTypes[fieldIndex]
|
||||
: null;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={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}
|
||||
{isActive && (
|
||||
<span className="ml-1">
|
||||
{direction === 'asc' ? '↑' : '↓'}
|
||||
{direction === "asc" ? "↑" : "↓"}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,36 +1,24 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { DataType, Pagination, fetchData, DataSchema } from './schema';
|
||||
|
||||
// Define request parameters interface
|
||||
interface RequestParams {
|
||||
page: number;
|
||||
size: number;
|
||||
orderFields: string[];
|
||||
orderTypes: string[];
|
||||
query: Record<string, string>;
|
||||
}
|
||||
|
||||
// Define response metadata interface
|
||||
interface ResponseMetadata {
|
||||
totalCount: number;
|
||||
allCount: number;
|
||||
totalPages: number;
|
||||
pageCount: number;
|
||||
}
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { DataType, fetchData, DataSchema } from "./schema";
|
||||
import {
|
||||
PagePagination,
|
||||
RequestParams,
|
||||
ResponseMetadata,
|
||||
} from "@/components/validations/list/paginations";
|
||||
|
||||
// Custom hook for pagination and data fetching
|
||||
export function usePaginatedData() {
|
||||
const [data, setData] = useState<DataType[]>([]);
|
||||
|
||||
|
||||
// Request parameters - these are controlled by the user
|
||||
const [requestParams, setRequestParams] = useState<RequestParams>({
|
||||
page: 1,
|
||||
size: 10,
|
||||
orderFields: ['createdAt'],
|
||||
orderTypes: ['desc'],
|
||||
orderFields: ["createdAt"],
|
||||
orderTypes: ["desc"],
|
||||
query: {},
|
||||
});
|
||||
|
||||
|
||||
// Response metadata - these come from the API
|
||||
const [responseMetadata, setResponseMetadata] = useState<ResponseMetadata>({
|
||||
totalCount: 0,
|
||||
|
|
@ -38,7 +26,7 @@ export function usePaginatedData() {
|
|||
totalPages: 0,
|
||||
pageCount: 0,
|
||||
});
|
||||
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
|
|
@ -52,19 +40,21 @@ export function usePaginatedData() {
|
|||
orderTypes: requestParams.orderTypes,
|
||||
query: requestParams.query,
|
||||
});
|
||||
|
||||
|
||||
// Validate data with Zod
|
||||
const validatedData = result.data.map(item => {
|
||||
try {
|
||||
return DataSchema.parse(item);
|
||||
} catch (err) {
|
||||
console.error('Validation error for item:', item, err);
|
||||
return null;
|
||||
}
|
||||
}).filter(Boolean) as DataType[];
|
||||
|
||||
const validatedData = result.data
|
||||
.map((item) => {
|
||||
try {
|
||||
return DataSchema.parse(item);
|
||||
} catch (err) {
|
||||
console.error("Validation error for item:", item, err);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Boolean) as DataType[];
|
||||
|
||||
setData(validatedData);
|
||||
|
||||
|
||||
// Update response metadata from API response
|
||||
setResponseMetadata({
|
||||
totalCount: result.pagination.totalCount,
|
||||
|
|
@ -72,14 +62,20 @@ export function usePaginatedData() {
|
|||
totalPages: result.pagination.totalPages,
|
||||
pageCount: result.pagination.pageCount,
|
||||
});
|
||||
|
||||
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err : new Error('Unknown error'));
|
||||
setError(err instanceof Error ? err : new Error("Unknown error"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [requestParams.page, requestParams.size, requestParams.orderFields, requestParams.orderTypes, requestParams.query]);
|
||||
}, [
|
||||
requestParams.page,
|
||||
requestParams.size,
|
||||
requestParams.orderFields,
|
||||
requestParams.orderTypes,
|
||||
requestParams.query,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
|
|
@ -90,7 +86,7 @@ export function usePaginatedData() {
|
|||
}, [fetchDataFromApi]);
|
||||
|
||||
const updatePagination = (updates: Partial<RequestParams>) => {
|
||||
setRequestParams(prev => ({
|
||||
setRequestParams((prev) => ({
|
||||
...prev,
|
||||
...updates,
|
||||
}));
|
||||
|
|
@ -98,16 +94,16 @@ export function usePaginatedData() {
|
|||
|
||||
// Create a combined refetch object that includes the setQuery function
|
||||
const setQuery = (query: Record<string, string>) => {
|
||||
setRequestParams(prev => ({
|
||||
setRequestParams((prev) => ({
|
||||
...prev,
|
||||
query,
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
const refetch = Object.assign(fetchDataFromApi, { setQuery });
|
||||
|
||||
// Combine request params and response metadata for backward compatibility
|
||||
const pagination: Pagination = {
|
||||
const pagination: PagePagination = {
|
||||
...requestParams,
|
||||
...responseMetadata,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,55 +1,55 @@
|
|||
// Language dictionary for the template component
|
||||
const language = {
|
||||
en: {
|
||||
title: 'Data Management',
|
||||
create: 'Create New',
|
||||
view: 'View Item',
|
||||
update: 'Update Item',
|
||||
createNew: 'Create New Item',
|
||||
back: 'Back',
|
||||
cancel: 'Cancel',
|
||||
submit: 'Submit',
|
||||
noItemsFound: 'No items found',
|
||||
previous: 'Previous',
|
||||
next: 'Next',
|
||||
page: 'Page',
|
||||
of: 'of',
|
||||
itemsPerPage: 'Items per page:',
|
||||
sortBy: 'Sort by:',
|
||||
loading: 'Loading...',
|
||||
error: 'Error loading data:',
|
||||
showing: 'Showing',
|
||||
items: 'items',
|
||||
total: 'Total',
|
||||
title: "Data Management",
|
||||
create: "Create New",
|
||||
view: "View Item",
|
||||
update: "Update Item",
|
||||
createNew: "Create New Item",
|
||||
back: "Back",
|
||||
cancel: "Cancel",
|
||||
submit: "Submit",
|
||||
noItemsFound: "No items found",
|
||||
previous: "Previous",
|
||||
next: "Next",
|
||||
page: "Page",
|
||||
of: "of",
|
||||
itemsPerPage: "Items per page:",
|
||||
sortBy: "Sort by:",
|
||||
loading: "Loading...",
|
||||
error: "Error loading data:",
|
||||
showing: "Showing",
|
||||
items: "items",
|
||||
total: "Total",
|
||||
// Search related translations
|
||||
search: 'Search',
|
||||
searchPlaceholder: 'Enter search term...',
|
||||
searchFields: 'Search in fields',
|
||||
activeSearch: 'Active search',
|
||||
clearSearch: 'Clear',
|
||||
search: "Search",
|
||||
searchPlaceholder: "Enter search term...",
|
||||
searchFields: "Search in fields",
|
||||
activeSearch: "Active search",
|
||||
clearSearch: "Clear",
|
||||
formLabels: {
|
||||
title: 'Title',
|
||||
description: 'Description',
|
||||
status: 'Status',
|
||||
createdAt: 'Created'
|
||||
title: "Title",
|
||||
description: "Description",
|
||||
status: "Status",
|
||||
createdAt: "Created",
|
||||
},
|
||||
status: {
|
||||
active: 'Active',
|
||||
inactive: 'Inactive'
|
||||
active: "Active",
|
||||
inactive: "Inactive",
|
||||
},
|
||||
buttons: {
|
||||
view: 'View',
|
||||
update: 'Update',
|
||||
create: 'Create',
|
||||
save: 'Save'
|
||||
}
|
||||
view: "View",
|
||||
update: "Update",
|
||||
create: "Create",
|
||||
save: "Save",
|
||||
},
|
||||
},
|
||||
// Add more languages as needed
|
||||
};
|
||||
|
||||
export type LanguageKey = keyof typeof language;
|
||||
|
||||
export const getTranslation = (lang: LanguageKey = 'en') => {
|
||||
export const getTranslation = (lang: LanguageKey = "en") => {
|
||||
return language[lang] || language.en;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { z } from "zod";
|
||||
|
||||
import { PagePagination } from "@/components/validations/list/paginations";
|
||||
|
||||
// Define the data schema using Zod
|
||||
export const DataSchema = z.object({
|
||||
id: z.string(),
|
||||
|
|
@ -12,19 +14,6 @@ export const DataSchema = z.object({
|
|||
|
||||
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)
|
||||
export const fetchData = async ({
|
||||
page = 1,
|
||||
|
|
@ -49,7 +38,7 @@ export const fetchData = async ({
|
|||
});
|
||||
|
||||
// Simulated API response
|
||||
return new Promise<{ data: DataType[]; pagination: Pagination }>(
|
||||
return new Promise<{ data: DataType[]; pagination: PagePagination }>(
|
||||
(resolve) => {
|
||||
setTimeout(() => {
|
||||
// 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