From da95b629ac28c9769d2eca9c1541eafd4ff63087 Mon Sep 17 00:00:00 2001 From: berkay Date: Wed, 16 Apr 2025 21:04:53 +0300 Subject: [PATCH] updated left menu and page template --- .../Pages/template/ListInfoComponent.tsx | 59 +++++++------- .../template/PaginationToolsComponent.tsx | 43 ++++++---- .../Pages/template/SearchComponent.tsx | 64 ++++++++------- .../Pages/template/SortingComponent.tsx | 39 +++++---- .../src/components/Pages/template/hooks.ts | 80 +++++++++---------- .../src/components/Pages/template/language.ts | 74 ++++++++--------- .../src/components/Pages/template/schema.ts | 17 +--- .../validations/list/paginations.ts | 30 +++++++ 8 files changed, 224 insertions(+), 182 deletions(-) create mode 100644 WebServices/client-frontend/src/components/validations/list/paginations.ts diff --git a/WebServices/client-frontend/src/components/Pages/template/ListInfoComponent.tsx b/WebServices/client-frontend/src/components/Pages/template/ListInfoComponent.tsx index 6d1fdae..169021c 100644 --- a/WebServices/client-frontend/src/components/Pages/template/ListInfoComponent.tsx +++ b/WebServices/client-frontend/src/components/Pages/template/ListInfoComponent.tsx @@ -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({

{item.title}

{item.description}

- + {item.status} - {t.formLabels.createdAt}: {new Date(item.createdAt).toLocaleDateString()} + {t.formLabels.createdAt}:{" "} + {new Date(item.createdAt).toLocaleDateString()}
@@ -58,10 +64,10 @@ export function DataCard({ interface ListInfoComponentProps { data: DataType[]; - pagination: Pagination; + pagination: PagePagination; loading: boolean; error: Error | null; - updatePagination: (updates: Partial) => void; + updatePagination: (updates: Partial) => 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 ( <> - - - + + - - - + {loading ? (
) : (
- {data.map(item => ( + {data.map((item) => ( ))} - + {data.length === 0 && (
{t.noItemsFound} diff --git a/WebServices/client-frontend/src/components/Pages/template/PaginationToolsComponent.tsx b/WebServices/client-frontend/src/components/Pages/template/PaginationToolsComponent.tsx index f5bac14..2d1a530 100644 --- a/WebServices/client-frontend/src/components/Pages/template/PaginationToolsComponent.tsx +++ b/WebServices/client-frontend/src/components/Pages/template/PaginationToolsComponent.tsx @@ -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) => void; + pagination: PagePagination; + updatePagination: (updates: Partial) => void; lang?: LanguageKey; } export function PaginationToolsComponent({ pagination, updatePagination, - lang = 'en' + lang = "en", }: PaginationToolsComponentProps) { const t = getTranslation(lang); @@ -30,19 +30,19 @@ export function PaginationToolsComponent({
{/* Navigation buttons */}
- - + {t.page} {pagination.page} {t.of} {pagination.totalPages} - -
- + {/* Items per page selector */}
- +
- + {/* Pagination stats */}
-
{t.showing} {pagination.pageCount} {t.of} {pagination.totalCount} {t.items}
-
{t.total}: {pagination.allCount} {t.items}
+
+ {t.showing} {pagination.pageCount} {t.of} {pagination.totalCount}{" "} + {t.items} +
+
+ {t.total}: {pagination.allCount} {t.items} +
diff --git a/WebServices/client-frontend/src/components/Pages/template/SearchComponent.tsx b/WebServices/client-frontend/src/components/Pages/template/SearchComponent.tsx index 9006df4..b275683 100644 --- a/WebServices/client-frontend/src/components/Pages/template/SearchComponent.tsx +++ b/WebServices/client-frontend/src/components/Pages/template/SearchComponent.tsx @@ -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) => 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([]); const [searchQuery, setSearchQuery] = useState>({}); @@ -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 = {}; - + // 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) => { 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 = {}; - 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)
- +
-
{t.searchFields || 'Search in fields'}:
+
+ {t.searchFields || "Search in fields"}: +
- {Object.keys(DataSchema.shape).map(field => ( + {Object.keys(DataSchema.shape).map((field) => (
- + {Object.keys(searchQuery).length > 0 && (
-
{t.activeSearch || 'Active search'}:
+
+ {t.activeSearch || "Active search"}: +
{Object.entries(searchQuery).map(([field, value]) => ( -
+
{field}: {value} - diff --git a/WebServices/client-frontend/src/components/Pages/template/hooks.ts b/WebServices/client-frontend/src/components/Pages/template/hooks.ts index b891a0e..6e796ad 100644 --- a/WebServices/client-frontend/src/components/Pages/template/hooks.ts +++ b/WebServices/client-frontend/src/components/Pages/template/hooks.ts @@ -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; -} - -// 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([]); - + // Request parameters - these are controlled by the user const [requestParams, setRequestParams] = useState({ page: 1, size: 10, - orderFields: ['createdAt'], - orderTypes: ['desc'], + orderFields: ["createdAt"], + orderTypes: ["desc"], query: {}, }); - + // Response metadata - these come from the API const [responseMetadata, setResponseMetadata] = useState({ totalCount: 0, @@ -38,7 +26,7 @@ export function usePaginatedData() { totalPages: 0, pageCount: 0, }); - + const [loading, setLoading] = useState(false); const [error, setError] = useState(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) => { - 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) => { - 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, }; diff --git a/WebServices/client-frontend/src/components/Pages/template/language.ts b/WebServices/client-frontend/src/components/Pages/template/language.ts index 352d4f4..ac7ec41 100644 --- a/WebServices/client-frontend/src/components/Pages/template/language.ts +++ b/WebServices/client-frontend/src/components/Pages/template/language.ts @@ -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; }; diff --git a/WebServices/client-frontend/src/components/Pages/template/schema.ts b/WebServices/client-frontend/src/components/Pages/template/schema.ts index 188cd0c..6632c30 100644 --- a/WebServices/client-frontend/src/components/Pages/template/schema.ts +++ b/WebServices/client-frontend/src/components/Pages/template/schema.ts @@ -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; -// Define pagination interface -export interface Pagination { - page: number; - size: number; - totalCount: number; - allCount: number; - totalPages: number; - orderFields: string[]; - orderTypes: string[]; - pageCount: number; - query: Record; -} - // 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 diff --git a/WebServices/client-frontend/src/components/validations/list/paginations.ts b/WebServices/client-frontend/src/components/validations/list/paginations.ts new file mode 100644 index 0000000..3d2f215 --- /dev/null +++ b/WebServices/client-frontend/src/components/validations/list/paginations.ts @@ -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; +} + + +// Define request parameters interface +export interface RequestParams { + page: number; + size: number; + orderFields: string[]; + orderTypes: string[]; + query: Record; +} + +// Define response metadata interface +export interface ResponseMetadata { + totalCount: number; + allCount: number; + totalPages: number; + pageCount: number; +}