From 36e63960f86a4adada51a77e74e6a45653bd84ee Mon Sep 17 00:00:00 2001 From: Berkay Date: Wed, 30 Apr 2025 14:30:22 +0300 Subject: [PATCH] updated lang change and FormDisplay Components --- WebServices/client-frontend/setup-shadcn.sh | 1 + .../app/(DashboardLayout)/individual/page.tsx | 58 +--- .../src/app/(DashboardLayout)/layout.tsx | 2 +- .../ActionButtonsDisplay/CreateButton.tsx | 25 ++ .../CustomButtonComponent.tsx | 31 ++ .../common/ActionButtonsDisplay/index.ts | 3 + .../common/ActionButtonsDisplay/types.ts | 17 + .../common/CardDisplay/CardDisplay.tsx | 73 ++++ .../common/CardDisplay/CardItem.tsx | 130 ++++++++ .../common/CardDisplay/CardSkeleton.tsx | 46 +++ .../components/common/CardDisplay/index.tsx | 1 + .../components/common/CardDisplay/schema.ts | 117 +++++++ .../components/common/CardDisplay/utils.ts | 46 +++ .../common/FormDisplay/CreateComponent.tsx | 279 ++++++++++++++++ .../common/FormDisplay/FormDisplay.tsx | 120 +++++++ .../common/FormDisplay/UpdateComponent.tsx | 312 ++++++++++++++++++ .../common/FormDisplay/ViewComponent.tsx | 262 +++++++++++++++ .../components/common/FormDisplay/index.ts | 5 + .../components/common/FormDisplay/types.ts | 37 +++ .../GridSelectionComponent.tsx | 42 +++ .../LanguageSelectionComponent.tsx | 37 +++ .../PaginationModifiers/PageNavigation.tsx | 116 +++++++ .../PaginationModifiers/PageSizeSelector.tsx | 52 +++ .../PaginationModifiers/PaginationStats.tsx | 36 ++ .../PaginationToolsComponent.tsx | 43 +++ .../common/PaginationModifiers/index.ts | 5 + .../common/PaginationModifiers/types.ts | 19 ++ .../QueryModifiers/SelectQueryModifier.tsx | 67 ++++ .../QueryModifiers/TextQueryModifier.tsx | 80 +++++ .../QueryModifiers/TypeQueryModifier.tsx | 51 +++ .../components/common/QueryModifiers/index.ts | 4 + .../components/common/QueryModifiers/types.ts | 35 ++ .../src/components/common/ReadMe.md | 128 +++++++ .../Screenshot from 2025-04-29 19-36-45.png | Bin 0 -> 68479 bytes .../SortingComponent/SortingComponent.tsx | 49 +++ .../common/SortingComponent/index.ts | 2 + .../common/SortingComponent/types.ts | 13 + .../src/components/common/hooks/useApiData.ts | 68 ++++ .../common/hooks/useDashboardPage.ts | 117 +++++++ .../common/hooks/useDataFetching.ts | 178 ++++++++++ .../common/hooks/useStandardApiFetch.ts | 181 ++++++++++ .../src/components/common/index.ts | 26 ++ .../src/components/common/schemas.ts | 37 +++ .../src/components/header/Header.tsx | 10 +- .../components/layouts/DashboardLayout.tsx | 44 +++ .../src/components/layouts/PageTemplate.tsx | 155 +++++++++ .../src/components/layouts/README.md | 271 +++++++++++++++ .../src/components/menu/NavigationMenu.tsx | 30 +- .../src/components/menu/menu.tsx | 4 +- .../src/components/navigator/retriever.tsx | 2 +- .../src/components/ui/alert.tsx | 66 ++++ .../src/components/ui/badge.tsx | 46 +++ .../src/components/ui/card.tsx | 92 ++++++ .../src/components/ui/dialog.tsx | 135 ++++++++ .../src/components/ui/select.tsx | 185 +++++++++++ .../src/components/ui/skeleton.tsx | 13 + .../src/components/ui/sonner.tsx | 25 ++ .../src/components/ui/table.tsx | 116 +++++++ .../src/components/ui/textarea.tsx | 18 + .../src/components/validations/menu/menu.tsx | 1 + .../src/eventRouters/Readme.md | 0 .../src/eventRouters/index.tsx | 6 + .../src/eventRouters/pageRetriever.tsx | 10 + .../src/eventRouters/unauthorizedpage.tsx | 43 +++ .../src/validations/list/paginations.ts | 30 ++ .../src/validations/menu/menu.tsx | 11 + .../validations/translations/translation.tsx | 61 ++++ .../(DashboardLayout)/application/page.tsx | 32 +- .../app/(DashboardLayout)/dashboard/page.tsx | 33 +- .../src/app/(DashboardLayout)/error.tsx | 108 ++++++ .../management-frontend/src/app/error.tsx | 108 ++++++ .../common/FormDisplay/CreateComponent.tsx | 28 +- .../common/FormDisplay/FormDisplay.tsx | 7 +- .../common/FormDisplay/UpdateComponent.tsx | 30 +- .../common/FormDisplay/ViewComponent.tsx | 15 +- .../components/common/FormDisplay/types.ts | 12 + .../src/components/common/hooks/useApiData.ts | 23 +- .../common/hooks/useDashboardPage.ts | 93 ++++++ .../common/hooks/useDataFetching.ts | 29 +- .../common/hooks/useStandardApiFetch.ts | 181 ++++++++++ .../components/layouts/DashboardLayout.tsx | 72 ++++ .../src/components/layouts/PageTemplate.tsx | 155 +++++++++ .../src/components/layouts/README.md | 271 +++++++++++++++ .../src/eventRouters/application/page.tsx | 36 +- .../src/eventRouters/application/schema.ts | 158 +++++---- .../src/eventRouters/schemas/zodSchemas.ts | 35 ++ docker-compose.yml | 78 ++--- 87 files changed, 5517 insertions(+), 312 deletions(-) create mode 100644 WebServices/client-frontend/src/components/common/ActionButtonsDisplay/CreateButton.tsx create mode 100644 WebServices/client-frontend/src/components/common/ActionButtonsDisplay/CustomButtonComponent.tsx create mode 100644 WebServices/client-frontend/src/components/common/ActionButtonsDisplay/index.ts create mode 100644 WebServices/client-frontend/src/components/common/ActionButtonsDisplay/types.ts create mode 100644 WebServices/client-frontend/src/components/common/CardDisplay/CardDisplay.tsx create mode 100644 WebServices/client-frontend/src/components/common/CardDisplay/CardItem.tsx create mode 100644 WebServices/client-frontend/src/components/common/CardDisplay/CardSkeleton.tsx create mode 100644 WebServices/client-frontend/src/components/common/CardDisplay/index.tsx create mode 100644 WebServices/client-frontend/src/components/common/CardDisplay/schema.ts create mode 100644 WebServices/client-frontend/src/components/common/CardDisplay/utils.ts create mode 100644 WebServices/client-frontend/src/components/common/FormDisplay/CreateComponent.tsx create mode 100644 WebServices/client-frontend/src/components/common/FormDisplay/FormDisplay.tsx create mode 100644 WebServices/client-frontend/src/components/common/FormDisplay/UpdateComponent.tsx create mode 100644 WebServices/client-frontend/src/components/common/FormDisplay/ViewComponent.tsx create mode 100644 WebServices/client-frontend/src/components/common/FormDisplay/index.ts create mode 100644 WebServices/client-frontend/src/components/common/FormDisplay/types.ts create mode 100644 WebServices/client-frontend/src/components/common/HeaderSelections/GridSelectionComponent.tsx create mode 100644 WebServices/client-frontend/src/components/common/HeaderSelections/LanguageSelectionComponent.tsx create mode 100644 WebServices/client-frontend/src/components/common/PaginationModifiers/PageNavigation.tsx create mode 100644 WebServices/client-frontend/src/components/common/PaginationModifiers/PageSizeSelector.tsx create mode 100644 WebServices/client-frontend/src/components/common/PaginationModifiers/PaginationStats.tsx create mode 100644 WebServices/client-frontend/src/components/common/PaginationModifiers/PaginationToolsComponent.tsx create mode 100644 WebServices/client-frontend/src/components/common/PaginationModifiers/index.ts create mode 100644 WebServices/client-frontend/src/components/common/PaginationModifiers/types.ts create mode 100644 WebServices/client-frontend/src/components/common/QueryModifiers/SelectQueryModifier.tsx create mode 100644 WebServices/client-frontend/src/components/common/QueryModifiers/TextQueryModifier.tsx create mode 100644 WebServices/client-frontend/src/components/common/QueryModifiers/TypeQueryModifier.tsx create mode 100644 WebServices/client-frontend/src/components/common/QueryModifiers/index.ts create mode 100644 WebServices/client-frontend/src/components/common/QueryModifiers/types.ts create mode 100644 WebServices/client-frontend/src/components/common/ReadMe.md create mode 100644 WebServices/client-frontend/src/components/common/Screenshot from 2025-04-29 19-36-45.png create mode 100644 WebServices/client-frontend/src/components/common/SortingComponent/SortingComponent.tsx create mode 100644 WebServices/client-frontend/src/components/common/SortingComponent/index.ts create mode 100644 WebServices/client-frontend/src/components/common/SortingComponent/types.ts create mode 100644 WebServices/client-frontend/src/components/common/hooks/useApiData.ts create mode 100644 WebServices/client-frontend/src/components/common/hooks/useDashboardPage.ts create mode 100644 WebServices/client-frontend/src/components/common/hooks/useDataFetching.ts create mode 100644 WebServices/client-frontend/src/components/common/hooks/useStandardApiFetch.ts create mode 100644 WebServices/client-frontend/src/components/common/index.ts create mode 100644 WebServices/client-frontend/src/components/common/schemas.ts create mode 100644 WebServices/client-frontend/src/components/layouts/DashboardLayout.tsx create mode 100644 WebServices/client-frontend/src/components/layouts/PageTemplate.tsx create mode 100644 WebServices/client-frontend/src/components/layouts/README.md create mode 100644 WebServices/client-frontend/src/components/ui/alert.tsx create mode 100644 WebServices/client-frontend/src/components/ui/badge.tsx create mode 100644 WebServices/client-frontend/src/components/ui/card.tsx create mode 100644 WebServices/client-frontend/src/components/ui/dialog.tsx create mode 100644 WebServices/client-frontend/src/components/ui/select.tsx create mode 100644 WebServices/client-frontend/src/components/ui/skeleton.tsx create mode 100644 WebServices/client-frontend/src/components/ui/sonner.tsx create mode 100644 WebServices/client-frontend/src/components/ui/table.tsx create mode 100644 WebServices/client-frontend/src/components/ui/textarea.tsx create mode 100644 WebServices/client-frontend/src/eventRouters/Readme.md create mode 100644 WebServices/client-frontend/src/eventRouters/index.tsx create mode 100644 WebServices/client-frontend/src/eventRouters/pageRetriever.tsx create mode 100644 WebServices/client-frontend/src/eventRouters/unauthorizedpage.tsx create mode 100644 WebServices/client-frontend/src/validations/list/paginations.ts create mode 100644 WebServices/client-frontend/src/validations/menu/menu.tsx create mode 100644 WebServices/client-frontend/src/validations/translations/translation.tsx create mode 100644 WebServices/management-frontend/src/app/(DashboardLayout)/error.tsx create mode 100644 WebServices/management-frontend/src/app/error.tsx create mode 100644 WebServices/management-frontend/src/components/common/hooks/useDashboardPage.ts create mode 100644 WebServices/management-frontend/src/components/common/hooks/useStandardApiFetch.ts create mode 100644 WebServices/management-frontend/src/components/layouts/DashboardLayout.tsx create mode 100644 WebServices/management-frontend/src/components/layouts/PageTemplate.tsx create mode 100644 WebServices/management-frontend/src/components/layouts/README.md create mode 100644 WebServices/management-frontend/src/eventRouters/schemas/zodSchemas.ts diff --git a/WebServices/client-frontend/setup-shadcn.sh b/WebServices/client-frontend/setup-shadcn.sh index 52dec08..bd6dc81 100755 --- a/WebServices/client-frontend/setup-shadcn.sh +++ b/WebServices/client-frontend/setup-shadcn.sh @@ -34,6 +34,7 @@ npx shadcn@latest add calendar -y npx shadcn@latest add date-picker -y npx shadcn@latest add skeleton -y npx shadcn@latest add table -y +npx shadcn@latest add textarea -y # Update any dependencies with legacy peer deps echo "🔄 Updating dependencies..." diff --git a/WebServices/client-frontend/src/app/(DashboardLayout)/individual/page.tsx b/WebServices/client-frontend/src/app/(DashboardLayout)/individual/page.tsx index 0f4ed5b..2c99949 100644 --- a/WebServices/client-frontend/src/app/(DashboardLayout)/individual/page.tsx +++ b/WebServices/client-frontend/src/app/(DashboardLayout)/individual/page.tsx @@ -1,54 +1,28 @@ "use server"; import React from "react"; -import ClientMenu from "@/components/menu/menu"; -import { retrievePageList, retrievePagebyUrl } from "@/apicalls/cookies/token"; -import { retrievePageByUrlAndPageId } from "@/components/navigator/retriever"; -import { searchPlaceholder } from "@/app/commons/pageDefaults"; - -const pageInfo = { - tr: "Birey Sayfası", - en: "Individual Page", -}; +import DashboardLayout from "@/components/layouts/DashboardLayout"; +import { useDashboardPage } from "@/components/common/hooks/useDashboardPage"; export default async function Dashboard({ searchParams, }: { searchParams: Promise<{ [key: string]: string | undefined }>; }) { - const activePage = "/individual"; - const siteUrlsList = (await retrievePageList()) || []; - const pageToDirect = await retrievePagebyUrl(activePage); - const PageComponent = retrievePageByUrlAndPageId(pageToDirect, activePage); - const searchParamsInstance = await searchParams; - const lang = (searchParamsInstance?.lang as "en" | "tr") || "en"; + // Use the enhanced dashboard hook to get all necessary data + const { + activePage, + searchParamsInstance, + lang, + PageComponent, + siteUrlsList + } = await useDashboardPage({ + pageUrl: "/individual", + searchParams + }); return ( - <> -
- {/* Sidebar */} - - - {/* Main Content Area */} -
- {/* Sticky Header */} -
-

{pageInfo[lang]}

-
- -
-
-
-
- -
-
-
- + + + ); } diff --git a/WebServices/client-frontend/src/app/(DashboardLayout)/layout.tsx b/WebServices/client-frontend/src/app/(DashboardLayout)/layout.tsx index 521102d..ec6fb15 100644 --- a/WebServices/client-frontend/src/app/(DashboardLayout)/layout.tsx +++ b/WebServices/client-frontend/src/app/(DashboardLayout)/layout.tsx @@ -18,7 +18,7 @@ export default async function DashLayout({ } return (
-
{children}
+
{children}
); } diff --git a/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/CreateButton.tsx b/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/CreateButton.tsx new file mode 100644 index 0000000..2716947 --- /dev/null +++ b/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/CreateButton.tsx @@ -0,0 +1,25 @@ +"use client"; +import React from "react"; +import { Button } from "@/components/ui/button"; +import { Plus } from "lucide-react"; + +interface CreateButtonProps { + onClick: () => void; + translations: Record; + lang: string; +} + +export const CreateButton: React.FC = ({ + onClick, + translations, + lang, +}) => { + const t = translations[lang] || {}; + + return ( + + ); +}; diff --git a/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/CustomButtonComponent.tsx b/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/CustomButtonComponent.tsx new file mode 100644 index 0000000..2d86b8b --- /dev/null +++ b/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/CustomButtonComponent.tsx @@ -0,0 +1,31 @@ +"use client"; +import React from "react"; +import { Button } from "@/components/ui/button"; +import { CustomButton } from "./types"; +import { cn } from "@/lib/utils"; + +interface CustomButtonComponentProps { + button: CustomButton; + isSelected: boolean; + onClick: () => void; +} + +export const CustomButtonComponent: React.FC = ({ + button, + isSelected, + onClick, +}) => { + return ( + + ); +}; diff --git a/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/index.ts b/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/index.ts new file mode 100644 index 0000000..5b1e2de --- /dev/null +++ b/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/index.ts @@ -0,0 +1,3 @@ +export * from './CreateButton'; +export * from './CustomButtonComponent'; +export * from './types'; diff --git a/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/types.ts b/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/types.ts new file mode 100644 index 0000000..270f86a --- /dev/null +++ b/WebServices/client-frontend/src/components/common/ActionButtonsDisplay/types.ts @@ -0,0 +1,17 @@ +import { ReactNode } from "react"; + +export interface CustomButton { + id: string; + label: string; + onClick: () => void; + variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link"; + icon?: ReactNode; +} + +export interface ActionButtonsProps { + onCreateClick: () => void; + translations: Record; + lang: string; + customButtons?: CustomButton[]; + defaultSelectedButtonId?: string; +} diff --git a/WebServices/client-frontend/src/components/common/CardDisplay/CardDisplay.tsx b/WebServices/client-frontend/src/components/common/CardDisplay/CardDisplay.tsx new file mode 100644 index 0000000..8b4b3ce --- /dev/null +++ b/WebServices/client-frontend/src/components/common/CardDisplay/CardDisplay.tsx @@ -0,0 +1,73 @@ +"use client"; +import React from "react"; +import { CardItem } from "./CardItem"; +import { CardSkeleton } from "./CardSkeleton"; +import { getFieldValue, getGridClasses } from "./utils"; +import { CardDisplayProps } from "./schema"; + +// Interface moved to schema.ts + +export function CardDisplay({ + showFields, + data, + lang, + translations, + error, + loading, + titleField = "name", + onCardClick, + renderCustomField, + gridCols = 4, + showViewIcon = false, + showUpdateIcon = false, + onViewClick, + onUpdateClick, +}: CardDisplayProps) { + if (error) { + return ( +
+ {error.message || "An error occurred while fetching data."} +
+ ); + } + + return ( +
+ {loading ? ( + // Loading skeletons + Array.from({ length: 10 }).map((_, index) => ( + + )) + ) : data.length === 0 ? ( +
+ {(translations[lang] || {}).noData || "No data found"} +
+ ) : ( + data.map((item, index) => ( + + )) + )} +
+ ); +} diff --git a/WebServices/client-frontend/src/components/common/CardDisplay/CardItem.tsx b/WebServices/client-frontend/src/components/common/CardDisplay/CardItem.tsx new file mode 100644 index 0000000..83bd719 --- /dev/null +++ b/WebServices/client-frontend/src/components/common/CardDisplay/CardItem.tsx @@ -0,0 +1,130 @@ +"use client"; +import React from "react"; +import { + Card, + CardContent, + CardHeader, +} from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Eye, Edit } from "lucide-react"; +import { CardItemProps, CardActionsProps, CardFieldProps } from "./schema"; + +export function CardItem({ + item, + index, + showFields, + titleField, + lang, + translations, + onCardClick, + renderCustomField, + showViewIcon, + showUpdateIcon, + onViewClick, + onUpdateClick, + getFieldValue, +}: CardItemProps) { + return ( +
+ onCardClick(item) : undefined} + > + +

+ {getFieldValue(item, titleField)} +

+ +
+ +
+ {showFields.map((field) => ( + + ))} +
+
+
+
+ ); +} + +// Interface moved to schema.ts + +function CardActions({ + item, + showViewIcon, + showUpdateIcon, + onViewClick, + onUpdateClick, +}: CardActionsProps) { + if (!showViewIcon && !showUpdateIcon) return null; + + return ( +
+ {showViewIcon && ( + + )} + {showUpdateIcon && ( + + )} +
+ ); +} + +// Interface moved to schema.ts + +function CardField({ + item, + field, + lang, + translations, + renderCustomField, + getFieldValue, +}: CardFieldProps) { + return ( +
+ + {translations[field]?.[lang] || field}: + + + {renderCustomField + ? renderCustomField(item, field) + : getFieldValue(item, field)} + +
+ ); +} diff --git a/WebServices/client-frontend/src/components/common/CardDisplay/CardSkeleton.tsx b/WebServices/client-frontend/src/components/common/CardDisplay/CardSkeleton.tsx new file mode 100644 index 0000000..73c0620 --- /dev/null +++ b/WebServices/client-frontend/src/components/common/CardDisplay/CardSkeleton.tsx @@ -0,0 +1,46 @@ +"use client"; +import React from "react"; +import { + Card, + CardContent, + CardHeader, +} from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { CardSkeletonProps } from "./schema"; + +// Interface moved to schema.ts + +export function CardSkeleton({ + index, + showFields, + showViewIcon, + showUpdateIcon, +}: CardSkeletonProps) { + return ( +
+ + + +
+ {showViewIcon && ( + + )} + {showUpdateIcon && ( + + )} +
+
+ +
+ {showFields.map((field, fieldIndex) => ( +
+ + +
+ ))} +
+
+
+
+ ); +} diff --git a/WebServices/client-frontend/src/components/common/CardDisplay/index.tsx b/WebServices/client-frontend/src/components/common/CardDisplay/index.tsx new file mode 100644 index 0000000..b3dba2c --- /dev/null +++ b/WebServices/client-frontend/src/components/common/CardDisplay/index.tsx @@ -0,0 +1 @@ +export { CardDisplay } from './CardDisplay'; diff --git a/WebServices/client-frontend/src/components/common/CardDisplay/schema.ts b/WebServices/client-frontend/src/components/common/CardDisplay/schema.ts new file mode 100644 index 0000000..b1637b0 --- /dev/null +++ b/WebServices/client-frontend/src/components/common/CardDisplay/schema.ts @@ -0,0 +1,117 @@ +/** + * CardDisplay component interfaces + */ + +/** + * Main props for the CardDisplay component + */ +export interface CardDisplayProps { + /** Fields to display in each card */ + showFields: string[]; + /** Array of data items to display */ + data: T[]; + /** Current language code */ + lang: string; + /** Translations object for field labels and messages */ + translations: Record; + /** Error object if data fetching failed */ + error: Error | null; + /** Loading state indicator */ + loading: boolean; + /** Field to use as the card title (default: "name") */ + titleField?: string; + /** Handler for when a card is clicked */ + onCardClick?: (item: T) => void; + /** Custom renderer for specific fields */ + renderCustomField?: (item: T, field: string) => React.ReactNode; + /** Number of columns in the grid (1-6) */ + gridCols?: 1 | 2 | 3 | 4 | 5 | 6; + /** Whether to show the view icon */ + showViewIcon?: boolean; + /** Whether to show the update/edit icon */ + showUpdateIcon?: boolean; + /** Handler for when the view icon is clicked */ + onViewClick?: (item: T) => void; + /** Handler for when the update/edit icon is clicked */ + onUpdateClick?: (item: T) => void; +} + +/** + * Props for the CardItem component + */ +export interface CardItemProps { + /** Data item to display */ + item: T; + /** Index of the item in the data array */ + index: number; + /** Fields to display in the card */ + showFields: string[]; + /** Field to use as the card title */ + titleField: string; + /** Current language code */ + lang: string; + /** Translations object for field labels */ + translations: Record; + /** Handler for when the card is clicked */ + onCardClick?: (item: T) => void; + /** Custom renderer for specific fields */ + renderCustomField?: (item: T, field: string) => React.ReactNode; + /** Whether to show the view icon */ + showViewIcon: boolean; + /** Whether to show the update/edit icon */ + showUpdateIcon: boolean; + /** Handler for when the view icon is clicked */ + onViewClick?: (item: T) => void; + /** Handler for when the update/edit icon is clicked */ + onUpdateClick?: (item: T) => void; + /** Function to get field values from the item */ + getFieldValue: (item: any, field: string) => any; +} + +/** + * Props for the CardActions component + */ +export interface CardActionsProps { + /** Data item the actions apply to */ + item: T; + /** Whether to show the view icon */ + showViewIcon: boolean; + /** Whether to show the update/edit icon */ + showUpdateIcon: boolean; + /** Handler for when the view icon is clicked */ + onViewClick?: (item: T) => void; + /** Handler for when the update/edit icon is clicked */ + onUpdateClick?: (item: T) => void; +} + +/** + * Props for the CardField component + */ +export interface CardFieldProps { + /** Data item the field belongs to */ + item: T; + /** Field name to display */ + field: string; + /** Current language code */ + lang: string; + /** Translations object for field labels */ + translations: Record; + /** Custom renderer for specific fields */ + renderCustomField?: (item: T, field: string) => React.ReactNode; + /** Function to get field values from the item */ + getFieldValue: (item: any, field: string) => any; +} + +/** + * Props for the CardSkeleton component + */ +export interface CardSkeletonProps { + /** Index of the skeleton in the loading array */ + index: number; + /** Fields to create skeleton placeholders for */ + showFields: string[]; + /** Whether to show a skeleton for the view icon */ + showViewIcon: boolean; + /** Whether to show a skeleton for the update/edit icon */ + showUpdateIcon: boolean; +} diff --git a/WebServices/client-frontend/src/components/common/CardDisplay/utils.ts b/WebServices/client-frontend/src/components/common/CardDisplay/utils.ts new file mode 100644 index 0000000..4e6e97f --- /dev/null +++ b/WebServices/client-frontend/src/components/common/CardDisplay/utils.ts @@ -0,0 +1,46 @@ +/** + * Safely gets a field value from an item, supporting nested fields with dot notation + */ +export function getFieldValue(item: any, field: string): any { + if (!item) return ""; + + // Handle nested fields with dot notation (e.g., "user.name") + if (field.includes(".")) { + const parts = field.split("."); + let value = item; + for (const part of parts) { + if (value === null || value === undefined) return ""; + value = value[part]; + } + return value; + } + + return item[field]; +} + +/** + * Gets a field label from translations or formats the field name + */ +export function getFieldLabel(field: string, translations: Record, lang: string): string { + const t = translations[lang] || {}; + return t[field] || field.charAt(0).toUpperCase() + field.slice(1).replace(/_/g, " "); +} + +/** + * Generates responsive grid classes based on the gridCols prop + */ +export function getGridClasses(gridCols: 1 | 2 | 3 | 4 | 5 | 6): string { + const baseClass = "grid grid-cols-1 gap-4"; + + // Map gridCols to responsive classes + const colClasses: Record = { + 1: "", + 2: "sm:grid-cols-2", + 3: "sm:grid-cols-2 md:grid-cols-3", + 4: "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4", + 5: "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5", + 6: "sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6" + }; + + return `${baseClass} ${colClasses[gridCols]}`; +} diff --git a/WebServices/client-frontend/src/components/common/FormDisplay/CreateComponent.tsx b/WebServices/client-frontend/src/components/common/FormDisplay/CreateComponent.tsx new file mode 100644 index 0000000..77d3756 --- /dev/null +++ b/WebServices/client-frontend/src/components/common/FormDisplay/CreateComponent.tsx @@ -0,0 +1,279 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import { CreateComponentProps } from "./types"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Checkbox } from "@/components/ui/checkbox"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { AlertCircle } from "lucide-react"; + +// Import field definitions type +interface FieldDefinition { + type: string; + group: string; + label: string; + options?: string[]; + readOnly?: boolean; + required?: boolean; + defaultValue?: any; + name?: string; +} + +export function CreateComponent({ + refetch, + setMode, + setSelectedItem, + onCancel, + lang, + translations, + formProps = {}, +}: CreateComponentProps) { + const t = translations[lang as keyof typeof translations] || {}; + + // Get field definitions from formProps if available + const fieldDefinitions = formProps.fieldDefinitions || {}; + const validationSchema = formProps.validationSchema; + + // Group fields by their group property + const [groupedFields, setGroupedFields] = useState>({}); + + // Process field definitions to group them + useEffect(() => { + if (Object.keys(fieldDefinitions).length > 0) { + const groups: Record = {}; + + // Group fields by their group property + Object.entries(fieldDefinitions).forEach(([fieldName, definition]) => { + const def = definition as FieldDefinition; + if (!groups[def.group]) { + groups[def.group] = []; + } + groups[def.group].push({ ...def, name: fieldName }); + }); + + setGroupedFields(groups); + } + }, [fieldDefinitions]); + + // Initialize form with default values from field definitions + const defaultValues: Record = {}; + Object.entries(fieldDefinitions).forEach(([key, def]) => { + const fieldDef = def as FieldDefinition; + defaultValues[key] = fieldDef.defaultValue !== undefined ? fieldDef.defaultValue : ""; + }); + + // Setup form with validation schema if available + const { + register, + handleSubmit, + formState: { errors }, + setValue, + watch, + } = useForm({ + defaultValues, + resolver: validationSchema ? zodResolver(validationSchema) : undefined, + }); + + const formValues = watch(); + + // Handle form submission + const onSubmit = async (data: Record) => { + try { + console.log("Form data to save:", data); + + // Here you would make an API call to save the data + // For example: await createApplication(data); + + // Mock API call success + if (refetch) refetch(); + setMode("list"); + setSelectedItem(null); + } catch (error) { + console.error("Error saving form:", error); + } + }; + + // Handle select changes + const handleSelectChange = (name: string, value: string) => { + setValue(name, value); + }; + + // Handle checkbox changes + const handleCheckboxChange = (name: string, checked: boolean) => { + setValue(name, checked); + }; + + // Translate group names for display dynamically + const getGroupTitle = (groupName: string) => { + // First check if there's a translation for the exact group key + if (t[groupName]) { + return t[groupName]; + } + + // Try to format the group name in a more readable way if no translation exists + // Convert camelCase or snake_case to Title Case with spaces + const formattedName = groupName + // Insert space before capital letters and uppercase the first letter + .replace(/([A-Z])/g, ' $1') + // Replace underscores with spaces + .replace(/_/g, ' ') + // Capitalize first letter + .replace(/^./, (str) => str.toUpperCase()) + // Capitalize each word + .replace(/\b\w/g, (c) => c.toUpperCase()); + + return formattedName; + }; + + // Render a field based on its type + const renderField = (fieldName: string, field: FieldDefinition) => { + const errorMessage = errors[fieldName]?.message as string; + + switch (field.type) { + case "text": + return ( +
+ + + {errorMessage && ( +

{errorMessage}

+ )} +
+ ); + + case "textarea": + return ( +
+ +