tr]:last:border-b-0",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
+ return (
+
+ )
+}
+
+function TableHead({ className, ...props }: React.ComponentProps<"th">) {
+ return (
+ [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableCell({ className, ...props }: React.ComponentProps<"td">) {
+ return (
+ [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableCaption({
+ className,
+ ...props
+}: React.ComponentProps<"caption">) {
+ return (
+
+ )
+}
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}
diff --git a/WebServices/client-frontend/src/components/ui/textarea.tsx b/WebServices/client-frontend/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..7f21b5e
--- /dev/null
+++ b/WebServices/client-frontend/src/components/ui/textarea.tsx
@@ -0,0 +1,18 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
+ return (
+
+ )
+}
+
+export { Textarea }
diff --git a/WebServices/client-frontend/src/components/validations/menu/menu.tsx b/WebServices/client-frontend/src/components/validations/menu/menu.tsx
index 4531fd7..38de4a3 100644
--- a/WebServices/client-frontend/src/components/validations/menu/menu.tsx
+++ b/WebServices/client-frontend/src/components/validations/menu/menu.tsx
@@ -1,6 +1,7 @@
interface ClientMenuProps {
siteUrls: string[];
lang?: string;
+ activePage?: string;
}
interface UserSelection {
diff --git a/WebServices/client-frontend/src/eventRouters/Readme.md b/WebServices/client-frontend/src/eventRouters/Readme.md
new file mode 100644
index 0000000..e69de29
diff --git a/WebServices/client-frontend/src/eventRouters/index.tsx b/WebServices/client-frontend/src/eventRouters/index.tsx
new file mode 100644
index 0000000..9ce34d1
--- /dev/null
+++ b/WebServices/client-frontend/src/eventRouters/index.tsx
@@ -0,0 +1,6 @@
+
+
+const menuPages = {
+};
+
+export default menuPages;
diff --git a/WebServices/client-frontend/src/eventRouters/pageRetriever.tsx b/WebServices/client-frontend/src/eventRouters/pageRetriever.tsx
new file mode 100644
index 0000000..b09a45a
--- /dev/null
+++ b/WebServices/client-frontend/src/eventRouters/pageRetriever.tsx
@@ -0,0 +1,10 @@
+import { PageProps } from "../validations/translations/translation";
+import { UnAuthorizedPage } from "./unauthorizedpage";
+import menuPages from "./index";
+
+export function retrievePageByUrl(url: string): React.FC {
+ if (url in menuPages) {
+ return menuPages[url as keyof typeof menuPages];
+ }
+ return UnAuthorizedPage;
+}
diff --git a/WebServices/client-frontend/src/eventRouters/unauthorizedpage.tsx b/WebServices/client-frontend/src/eventRouters/unauthorizedpage.tsx
new file mode 100644
index 0000000..9ee42de
--- /dev/null
+++ b/WebServices/client-frontend/src/eventRouters/unauthorizedpage.tsx
@@ -0,0 +1,43 @@
+import { PageProps } from "../validations/translations/translation";
+
+// Language dictionary for internationalization
+const languageDictionary = {
+ en: {
+ title: "Unauthorized Access",
+ message1: "You do not have permission to access this page.",
+ message2: "Please contact the administrator.",
+ footer: `© ${new Date().getFullYear()} My Application`,
+ },
+ tr: {
+ title: "Yetkisiz Erişim",
+ message1: "Bu sayfaya erişim izniniz yok.",
+ message2: "Lütfen yönetici ile iletişime geçin.",
+ footer: `© ${new Date().getFullYear()} Uygulamam`,
+ },
+};
+
+export const UnAuthorizedPage: React.FC = ({
+ lang = "en",
+ queryParams,
+}) => {
+ // Use the language dictionary based on the lang prop, defaulting to English
+ const t =
+ languageDictionary[lang as keyof typeof languageDictionary] ||
+ languageDictionary.en;
+ return (
+ <>
+
+
+
+ {t.message1}
+ {t.message2}
+
+
+
+ >
+ );
+};
diff --git a/WebServices/client-frontend/src/validations/list/paginations.ts b/WebServices/client-frontend/src/validations/list/paginations.ts
new file mode 100644
index 0000000..3d2f215
--- /dev/null
+++ b/WebServices/client-frontend/src/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;
+}
diff --git a/WebServices/client-frontend/src/validations/menu/menu.tsx b/WebServices/client-frontend/src/validations/menu/menu.tsx
new file mode 100644
index 0000000..a262307
--- /dev/null
+++ b/WebServices/client-frontend/src/validations/menu/menu.tsx
@@ -0,0 +1,11 @@
+interface ClientMenuProps {
+ lang?: string;
+ activePage: string;
+}
+
+interface UserSelection {
+ userType: string;
+ selected: any;
+}
+
+export type { ClientMenuProps, UserSelection };
diff --git a/WebServices/client-frontend/src/validations/translations/translation.tsx b/WebServices/client-frontend/src/validations/translations/translation.tsx
new file mode 100644
index 0000000..c6952d7
--- /dev/null
+++ b/WebServices/client-frontend/src/validations/translations/translation.tsx
@@ -0,0 +1,61 @@
+// Define TypeScript interfaces for menu structure
+interface LanguageTranslation {
+ tr: string;
+ en: string;
+}
+
+interface MenuThirdLevel {
+ name: string;
+ lg: LanguageTranslation;
+ siteUrl: string;
+}
+
+interface MenuSecondLevel {
+ name: string;
+ lg: LanguageTranslation;
+ subList: MenuThirdLevel[];
+}
+
+interface MenuFirstLevel {
+ name: string;
+ lg: LanguageTranslation;
+ subList: MenuSecondLevel[];
+}
+
+// Define interfaces for the filtered menu structure
+interface FilteredMenuThirdLevel {
+ name: string;
+ lg: LanguageTranslation;
+ siteUrl: string;
+}
+
+interface FilteredMenuSecondLevel {
+ name: string;
+ lg: LanguageTranslation;
+ subList: FilteredMenuThirdLevel[];
+}
+
+interface FilteredMenuFirstLevel {
+ name: string;
+ lg: LanguageTranslation;
+ subList: FilteredMenuSecondLevel[];
+}
+
+interface PageProps {
+ lang: keyof LanguageTranslation;
+ queryParams: { [key: string]: string | undefined };
+}
+
+type PageComponent = React.ComponentType;
+
+export type {
+ PageComponent,
+ PageProps,
+ MenuFirstLevel,
+ MenuSecondLevel,
+ MenuThirdLevel,
+ FilteredMenuFirstLevel,
+ FilteredMenuSecondLevel,
+ FilteredMenuThirdLevel,
+ LanguageTranslation,
+};
diff --git a/WebServices/management-frontend/src/app/(DashboardLayout)/application/page.tsx b/WebServices/management-frontend/src/app/(DashboardLayout)/application/page.tsx
index 4482481..1e45d77 100644
--- a/WebServices/management-frontend/src/app/(DashboardLayout)/application/page.tsx
+++ b/WebServices/management-frontend/src/app/(DashboardLayout)/application/page.tsx
@@ -1,34 +1,22 @@
+'use server';
import React from "react";
-import Header from "@/components/header/Header";
-import ClientMenu from "@/components/menu/menu";
-import { retrievePageByUrl } from "@/eventRouters/pageRetriever";
+import DashboardLayout from "@/components/layouts/DashboardLayout";
+import { useDashboardPage } from "@/components/common/hooks/useDashboardPage";
async function DashboardPage({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | undefined }>;
}) {
- const activePage = "/application";
- const searchParamsInstance = await searchParams;
- const lang = (searchParamsInstance?.lang as "en" | "tr") || "en";
- const PageComponent = retrievePageByUrl(activePage);
+ const { activePage, searchParamsInstance, lang, PageComponent } = await useDashboardPage({
+ pageUrl: "/application",
+ searchParams,
+ });
return (
- <>
-
- {/* Sidebar */}
-
-
- {/* Main Content Area */}
-
- {/* Header Component */}
-
-
-
-
- >
+
+
+
);
}
diff --git a/WebServices/management-frontend/src/app/(DashboardLayout)/dashboard/page.tsx b/WebServices/management-frontend/src/app/(DashboardLayout)/dashboard/page.tsx
index d672533..d527347 100644
--- a/WebServices/management-frontend/src/app/(DashboardLayout)/dashboard/page.tsx
+++ b/WebServices/management-frontend/src/app/(DashboardLayout)/dashboard/page.tsx
@@ -1,34 +1,23 @@
+'use server';
import React from "react";
-import Header from "@/components/header/Header";
-import ClientMenu from "@/components/menu/menu";
-import { retrievePageByUrl } from "@/eventRouters/pageRetriever";
+import DashboardLayout from "@/components/layouts/DashboardLayout";
+import { useDashboardPage } from "@/components/common/hooks/useDashboardPage";
async function PageDashboard({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | undefined }>;
}) {
- const activePage = "/dashboard";
- const searchParamsInstance = await searchParams;
- const lang = (searchParamsInstance?.lang as "en" | "tr") || "en";
- const PageComponent = retrievePageByUrl(activePage);
+
+ const { activePage, searchParamsInstance, lang, PageComponent } = await useDashboardPage({
+ pageUrl: "/dashboard",
+ searchParams,
+ });
return (
- <>
-
- {/* Sidebar */}
-
-
- {/* Main Content Area */}
-
- {/* Header Component */}
-
-
-
-
- >
+
+
+
);
}
diff --git a/WebServices/management-frontend/src/app/(DashboardLayout)/error.tsx b/WebServices/management-frontend/src/app/(DashboardLayout)/error.tsx
new file mode 100644
index 0000000..6d69fdc
--- /dev/null
+++ b/WebServices/management-frontend/src/app/(DashboardLayout)/error.tsx
@@ -0,0 +1,108 @@
+'use client';
+
+import React, { useEffect, useState } from 'react';
+import Link from 'next/link';
+
+import { Button } from '@/components/ui/button';
+import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
+import { AlertTriangle, RefreshCw, Home } from 'lucide-react';
+import { useSearchParams } from 'next/navigation';
+
+// Translations for error messages
+const translations = {
+ en: {
+ errorTitle: 'An error occurred',
+ errorMessage: 'Sorry, something went wrong while loading this page.',
+ unknownError: 'Unknown error',
+ errorId: 'Error ID',
+ tryAgain: 'Try again',
+ goHome: 'Go to Home'
+ },
+ tr: {
+ errorTitle: 'Bir hata oluştu',
+ errorMessage: 'Üzgünüz, bu sayfa yüklenirken bir sorun oluştu.',
+ unknownError: 'Bilinmeyen hata',
+ errorId: 'Hata ID',
+ tryAgain: 'Tekrar dene',
+ goHome: 'Ana Sayfaya Git'
+ }
+};
+
+interface ErrorPageProps {
+ error: Error & { digest?: string };
+ reset: () => void;
+}
+
+/**
+ * Global error page for the Dashboard Layout
+ * This component will be automatically used by Next.js when an error occurs
+ * in any page within the (DashboardLayout) group
+ */
+export default function Error({ error, reset }: ErrorPageProps) {
+ // Get the language from URL params or default to English
+ const searchParams = useSearchParams();
+ const [lang, setLang] = useState<'en' | 'tr'>((searchParams?.get('lang') as 'en' | 'tr') || 'en');
+
+ // Ensure lang is valid
+ const validLang = lang === 'tr' ? 'tr' : 'en';
+ const t = translations[validLang];
+
+ useEffect(() => {
+ // Log the error to an error reporting service
+ console.error('Dashboard error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
+
+
+
+
+ {t.errorMessage}
+
+
+
+
+ {error.message || t.unknownError}
+
+ {error.digest && (
+
+ {t.errorId}: {error.digest}
+
+ )}
+
+
+
+
+
+ reset()}
+ >
+
+ {t.tryAgain}
+
+
+
+
+
+ {t.goHome}
+
+
+
+
+
+ );
+}
diff --git a/WebServices/management-frontend/src/app/error.tsx b/WebServices/management-frontend/src/app/error.tsx
new file mode 100644
index 0000000..6d69fdc
--- /dev/null
+++ b/WebServices/management-frontend/src/app/error.tsx
@@ -0,0 +1,108 @@
+'use client';
+
+import React, { useEffect, useState } from 'react';
+import Link from 'next/link';
+
+import { Button } from '@/components/ui/button';
+import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
+import { AlertTriangle, RefreshCw, Home } from 'lucide-react';
+import { useSearchParams } from 'next/navigation';
+
+// Translations for error messages
+const translations = {
+ en: {
+ errorTitle: 'An error occurred',
+ errorMessage: 'Sorry, something went wrong while loading this page.',
+ unknownError: 'Unknown error',
+ errorId: 'Error ID',
+ tryAgain: 'Try again',
+ goHome: 'Go to Home'
+ },
+ tr: {
+ errorTitle: 'Bir hata oluştu',
+ errorMessage: 'Üzgünüz, bu sayfa yüklenirken bir sorun oluştu.',
+ unknownError: 'Bilinmeyen hata',
+ errorId: 'Hata ID',
+ tryAgain: 'Tekrar dene',
+ goHome: 'Ana Sayfaya Git'
+ }
+};
+
+interface ErrorPageProps {
+ error: Error & { digest?: string };
+ reset: () => void;
+}
+
+/**
+ * Global error page for the Dashboard Layout
+ * This component will be automatically used by Next.js when an error occurs
+ * in any page within the (DashboardLayout) group
+ */
+export default function Error({ error, reset }: ErrorPageProps) {
+ // Get the language from URL params or default to English
+ const searchParams = useSearchParams();
+ const [lang, setLang] = useState<'en' | 'tr'>((searchParams?.get('lang') as 'en' | 'tr') || 'en');
+
+ // Ensure lang is valid
+ const validLang = lang === 'tr' ? 'tr' : 'en';
+ const t = translations[validLang];
+
+ useEffect(() => {
+ // Log the error to an error reporting service
+ console.error('Dashboard error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
+
+
+
+
+ {t.errorMessage}
+
+
+
+
+ {error.message || t.unknownError}
+
+ {error.digest && (
+
+ {t.errorId}: {error.digest}
+
+ )}
+
+
+
+
+
+ reset()}
+ >
+
+ {t.tryAgain}
+
+
+
+
+
+ {t.goHome}
+
+
+
+
+
+ );
+}
diff --git a/WebServices/management-frontend/src/components/common/FormDisplay/CreateComponent.tsx b/WebServices/management-frontend/src/components/common/FormDisplay/CreateComponent.tsx
index 77d3756..4968cc2 100644
--- a/WebServices/management-frontend/src/components/common/FormDisplay/CreateComponent.tsx
+++ b/WebServices/management-frontend/src/components/common/FormDisplay/CreateComponent.tsx
@@ -2,7 +2,7 @@
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 { CreateComponentProps, FieldDefinition } from "./types";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
@@ -13,18 +13,6 @@ 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,
@@ -140,13 +128,13 @@ export function CreateComponent({
return (
- {t[fieldName] || field.label}
+ {t[fieldName] || field.label[lang as "en" | "tr"]}
{field.required && * }
{errorMessage && (
@@ -159,13 +147,13 @@ export function CreateComponent
({
return (
- {t[fieldName] || field.label}
+ {t[fieldName] || field.label[lang as "en" | "tr"]}
{field.required && * }
@@ -179,7 +167,7 @@ export function CreateComponent
({
return (
- {t[fieldName] || field.label}
+ {t[fieldName] || field.label[lang as "en" | "tr"]}
{field.required && * }
({
disabled={field.readOnly}
>
-
+
{field.options?.map((option) => (
@@ -216,7 +204,7 @@ export function CreateComponent({
disabled={field.readOnly}
/>
- {t[fieldName] || field.label}
+ {t[fieldName] || field.label[lang as "en" | "tr"]}
{field.required && * }
{errorMessage && (
diff --git a/WebServices/management-frontend/src/components/common/FormDisplay/FormDisplay.tsx b/WebServices/management-frontend/src/components/common/FormDisplay/FormDisplay.tsx
index 7d093c3..20f7740 100644
--- a/WebServices/management-frontend/src/components/common/FormDisplay/FormDisplay.tsx
+++ b/WebServices/management-frontend/src/components/common/FormDisplay/FormDisplay.tsx
@@ -69,13 +69,14 @@ export function FormDisplay({
};
loadSchemaDefinitions();
- }, [formProps, mode]);
+ }, [formProps, mode, lang]); // Added lang as a dependency to ensure re-fetch when language changes
// Render the appropriate component based on the mode
switch (mode) {
case "create":
return (
+ key={`create-${lang}`} // Add key with lang to force re-render on language change
refetch={refetch}
setMode={setMode}
setSelectedItem={setSelectedItem}
@@ -88,6 +89,7 @@ export function FormDisplay({
case "update":
return initialData ? (
+ key={`update-${lang}`} // Add key with lang to force re-render on language change
initialData={initialData}
refetch={refetch}
setMode={setMode}
@@ -101,6 +103,7 @@ export function FormDisplay({
case "view":
return initialData ? (
+ key={`view-${lang}`} // Add key with lang to force re-render on language change
initialData={initialData}
refetch={refetch}
setMode={setMode}
@@ -108,7 +111,7 @@ export function FormDisplay({
onCancel={onCancel}
lang={lang}
translations={translations}
- formProps={formProps}
+ formProps={enhancedFormProps} // Changed from formProps to enhancedFormProps for consistency
/>
) : null;
default:
diff --git a/WebServices/management-frontend/src/components/common/FormDisplay/UpdateComponent.tsx b/WebServices/management-frontend/src/components/common/FormDisplay/UpdateComponent.tsx
index d1adfbf..f9e6d72 100644
--- a/WebServices/management-frontend/src/components/common/FormDisplay/UpdateComponent.tsx
+++ b/WebServices/management-frontend/src/components/common/FormDisplay/UpdateComponent.tsx
@@ -2,7 +2,7 @@
import React, { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
-import { UpdateComponentProps } from "./types";
+import { UpdateComponentProps, FieldDefinition } from "./types";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
@@ -13,18 +13,6 @@ 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; // Add name property for TypeScript compatibility
-}
-
export function UpdateComponent({
initialData,
refetch,
@@ -154,13 +142,13 @@ export function UpdateComponent({
return (
- {t[fieldName] || field.label}
+ {t[fieldName] || field.label[lang as "en" | "tr"]}
{field.required && * }
{errorMessage && (
@@ -173,13 +161,13 @@ export function UpdateComponent
({
return (
- {t[fieldName] || field.label}
+ {t[fieldName] || field.label[lang as "en" | "tr"]}
{field.required && * }
@@ -193,7 +181,7 @@ export function UpdateComponent
({
return (
- {t[fieldName] || field.label}
+ {t[fieldName] || field.label[lang as "en" | "tr"]}
{field.required && * }
({
disabled={field.readOnly}
>
-
+
{field.options?.map((option) => (
@@ -230,7 +218,7 @@ export function UpdateComponent({
disabled={field.readOnly}
/>
- {t[fieldName] || field.label}
+ {t[fieldName] || field.label[lang as "en" | "tr"]}
{field.required && * }
{errorMessage && (
@@ -243,7 +231,7 @@ export function UpdateComponent({
return (
- {t[fieldName] || field.label}
+ {t[fieldName] || field.label[lang as "en" | "tr"]}
{field.required && * }
@@ -103,7 +92,7 @@ const ViewFieldGroup: React.FC<{
key={fieldName}
fieldName={fieldName}
value={value}
- label={field.label}
+ label={field.label[lang as "en" | "tr"]}
lang={lang}
translations={translations}
hasError={hasError}
diff --git a/WebServices/management-frontend/src/components/common/FormDisplay/types.ts b/WebServices/management-frontend/src/components/common/FormDisplay/types.ts
index 47300ff..730d9ca 100644
--- a/WebServices/management-frontend/src/components/common/FormDisplay/types.ts
+++ b/WebServices/management-frontend/src/components/common/FormDisplay/types.ts
@@ -1,5 +1,17 @@
"use client";
+// Import field definitions type
+export interface FieldDefinition {
+ type: string;
+ group: string;
+ label: { tr: string; en: string };
+ options?: string[];
+ readOnly?: boolean;
+ required?: boolean;
+ defaultValue?: any;
+ name?: string;
+}
+
// Define the FormMode type to ensure consistency
export type FormMode = "list" | "create" | "update" | "view";
diff --git a/WebServices/management-frontend/src/components/common/hooks/useApiData.ts b/WebServices/management-frontend/src/components/common/hooks/useApiData.ts
index 3b42b83..46448eb 100644
--- a/WebServices/management-frontend/src/components/common/hooks/useApiData.ts
+++ b/WebServices/management-frontend/src/components/common/hooks/useApiData.ts
@@ -1,4 +1,5 @@
-import { useDataFetching, RequestParams, ApiResponse } from "./useDataFetching";
+import { useDataFetching, ApiResponse } from "./useDataFetching";
+import { RequestParams } from "../schemas";
/**
* Hook for fetching data from Next.js API routes
@@ -11,35 +12,37 @@ export function useApiData
(
initialParams: Partial = {}
) {
// Define the fetch function that will be passed to useDataFetching
- const fetchFromApi = async (params: RequestParams): Promise> => {
+ const fetchFromApi = async (
+ params: RequestParams
+ ): Promise> => {
try {
// Prepare the request body with action and all params
const requestBody = {
- action: 'list',
+ action: "list",
page: params.page,
size: params.size,
orderField: params.orderField,
orderType: params.orderType,
- query: params.query
+ query: params.query,
};
-
+
// Make the API request using POST
const response = await fetch(endpoint, {
- method: 'POST',
+ method: "POST",
headers: {
- 'Content-Type': 'application/json',
+ "Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
});
-
+
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}`);
}
-
+
return await response.json();
} catch (error) {
console.error("Error fetching data from API:", error);
-
+
// Return empty data with pagination info on error
return {
data: [],
diff --git a/WebServices/management-frontend/src/components/common/hooks/useDashboardPage.ts b/WebServices/management-frontend/src/components/common/hooks/useDashboardPage.ts
new file mode 100644
index 0000000..0d3b5ab
--- /dev/null
+++ b/WebServices/management-frontend/src/components/common/hooks/useDashboardPage.ts
@@ -0,0 +1,93 @@
+import { retrievePageByUrl } from "@/eventRouters/pageRetriever";
+import { PageProps } from "@/validations/translations/translation";
+import React, { ReactElement } from "react";
+
+export interface DashboardPageParams {
+ /**
+ * The active page path, e.g., "/application", "/dashboard"
+ */
+ pageUrl: string;
+
+ /**
+ * The search parameters from Next.js
+ */
+ searchParams: Promise<{ [key: string]: string | undefined }>;
+}
+
+export interface DashboardPageResult {
+ /**
+ * The active page path
+ */
+ activePage: string;
+
+ /**
+ * The resolved search parameters
+ */
+ searchParamsInstance: { [key: string]: string | undefined };
+
+ /**
+ * The current language, either from search params or default
+ */
+ lang: "en" | "tr";
+
+ /**
+ * The page component to render
+ */
+ PageComponent: React.FC;
+}
+
+/**
+ * Hook to retrieve and prepare dashboard page data
+ * Throws errors for Next.js error boundary to catch
+ *
+ * @param params The dashboard page parameters
+ * @returns The processed dashboard page data
+ * @throws Error if page URL is invalid or page component is not found
+ */
+export async function useDashboardPage({
+ pageUrl,
+ searchParams,
+}: DashboardPageParams): Promise {
+ let searchParamsInstance: { [key: string]: string | undefined } = {};
+ const defaultLang = "en";
+ // Validate pageUrl
+ if (!pageUrl || typeof pageUrl !== "string") {
+ throw new Error(`Invalid page URL: ${pageUrl}`);
+ }
+
+ // Resolve search params
+ try {
+ searchParamsInstance = await searchParams;
+ } catch (err) {
+ console.error("Error resolving search parameters:", err);
+ // Still throw the error to be caught by Next.js error boundary
+ throw err;
+ }
+
+ // Determine language
+ const lang = (searchParamsInstance?.lang as "en" | "tr") || defaultLang;
+
+ // Validate language
+ if (lang !== "en" && lang !== "tr") {
+ console.warn(
+ `Invalid language "${lang}" specified, falling back to "${defaultLang}"`
+ );
+ }
+
+ // Get page component
+ const PageComponent = retrievePageByUrl(pageUrl);
+
+ // Check if page component exists
+ if (!PageComponent) {
+ throw new Error(`Page component not found for URL: ${pageUrl}`);
+ }
+
+ return {
+ activePage: pageUrl,
+ searchParamsInstance,
+ lang,
+ PageComponent,
+ };
+}
+
+export default useDashboardPage;
diff --git a/WebServices/management-frontend/src/components/common/hooks/useDataFetching.ts b/WebServices/management-frontend/src/components/common/hooks/useDataFetching.ts
index 4f733d6..372b8bf 100644
--- a/WebServices/management-frontend/src/components/common/hooks/useDataFetching.ts
+++ b/WebServices/management-frontend/src/components/common/hooks/useDataFetching.ts
@@ -1,22 +1,5 @@
import { useState, useEffect, useCallback, useRef } from "react";
-
-export interface RequestParams {
- page: number;
- size: number;
- orderField: string[];
- orderType: string[];
- query: Record;
-}
-
-export interface ResponseMetadata {
- totalCount: number;
- totalItems: number;
- totalPages: number;
- pageCount: number;
- allCount?: number;
- next: boolean;
- back: boolean;
-}
+import { RequestParams, ResponseMetadata } from "../schemas";
export interface PagePagination extends RequestParams, ResponseMetadata {}
@@ -104,19 +87,21 @@ export function useDataFetching(
// Track if this is the initial mount
const initialMountRef = useRef(true);
-
+
// Track previous request params to avoid unnecessary fetches
const prevRequestParamsRef = useRef(requestParams);
useEffect(() => {
// Only fetch on mount or when request params actually change
- const paramsChanged = JSON.stringify(prevRequestParamsRef.current) !== JSON.stringify(requestParams);
-
+ const paramsChanged =
+ JSON.stringify(prevRequestParamsRef.current) !==
+ JSON.stringify(requestParams);
+
if (initialMountRef.current || paramsChanged) {
const timer = setTimeout(() => {
fetchDataFromApi();
initialMountRef.current = false;
- prevRequestParamsRef.current = {...requestParams};
+ prevRequestParamsRef.current = { ...requestParams };
}, 300); // Debounce
return () => clearTimeout(timer);
diff --git a/WebServices/management-frontend/src/components/common/hooks/useStandardApiFetch.ts b/WebServices/management-frontend/src/components/common/hooks/useStandardApiFetch.ts
new file mode 100644
index 0000000..a9b775b
--- /dev/null
+++ b/WebServices/management-frontend/src/components/common/hooks/useStandardApiFetch.ts
@@ -0,0 +1,181 @@
+import { useState, useEffect } from 'react';
+
+/**
+ * Cache options for the fetch request
+ */
+export type CacheOptions = {
+ /** Whether to cache the request (default: true) */
+ cache?: boolean;
+ /** Revalidate time in seconds (if not provided, uses Next.js defaults) */
+ revalidate?: number;
+ /** Force cache to be revalidated (equivalent to cache: 'no-store' in fetch) */
+ noStore?: boolean;
+};
+
+/**
+ * Request options for the fetch
+ */
+export type FetchOptions = {
+ /** HTTP method (default: 'GET') */
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
+ /** Request headers */
+ headers?: HeadersInit;
+ /** Request body (for POST, PUT, PATCH) */
+ body?: any;
+};
+
+/**
+ * A hook for fetching data from an API endpoint without pagination using Next.js fetch
+ * @param url The API endpoint URL
+ * @param initialParams Initial query parameters
+ * @param options Additional fetch options
+ * @param cacheOptions Cache control options
+ * @returns Object containing data, loading state, error state, and refetch function
+ */
+export function useStandardApiFetch(
+ url: string,
+ initialParams: Record = {},
+ options: FetchOptions = {},
+ cacheOptions: CacheOptions = { cache: true }
+) {
+ const [data, setData] = useState(null);
+ const [params, setParams] = useState>(initialParams);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ /**
+ * Builds the URL with query parameters
+ */
+ const buildUrl = () => {
+ const queryParams = new URLSearchParams();
+
+ // Add all non-null and non-empty params
+ Object.entries(params).forEach(([key, value]) => {
+ if (value !== null && value !== '') {
+ queryParams.append(key, String(value));
+ }
+ });
+
+ const queryString = queryParams.toString();
+ return queryString ? `${url}?${queryString}` : url;
+ };
+
+ /**
+ * Configure fetch options including cache settings
+ */
+ const getFetchOptions = (): RequestInit => {
+ const { method = 'GET', headers = {}, body } = options;
+
+ const fetchOptions: RequestInit = {
+ method,
+ headers: {
+ 'Content-Type': 'application/json',
+ ...headers,
+ },
+ };
+
+ // Add body for non-GET requests if provided
+ if (method !== 'GET' && body) {
+ fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
+ }
+
+ // Configure cache options
+ if (!cacheOptions.cache) {
+ fetchOptions.cache = 'no-store';
+ } else if (cacheOptions.noStore) {
+ fetchOptions.cache = 'no-store';
+ } else if (cacheOptions.revalidate !== undefined) {
+ fetchOptions.next = { revalidate: cacheOptions.revalidate };
+ }
+
+ return fetchOptions;
+ };
+
+ const fetchData = async () => {
+ setLoading(true);
+ try {
+ const fullUrl = buildUrl();
+ const fetchOptions = getFetchOptions();
+
+ const response = await fetch(fullUrl, fetchOptions);
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! Status: ${response.status}`);
+ }
+
+ const responseData = await response.json();
+ setData(responseData);
+ setError(null);
+ } catch (err) {
+ setError(err instanceof Error ? err : new Error('An unknown error occurred'));
+ setData(null);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchData();
+ }, [url, JSON.stringify(params), JSON.stringify(options), JSON.stringify(cacheOptions)]);
+
+ /**
+ * Update the query parameters and trigger a refetch
+ * @param newParams New parameters to merge with existing ones
+ */
+ const updateParams = (newParams: Record) => {
+ // Filter out null or empty string values
+ const filteredParams = Object.entries(newParams).reduce((acc, [key, value]) => {
+ if (value !== null && value !== '') {
+ acc[key] = value;
+ }
+ return acc;
+ }, {} as Record);
+
+ setParams(prev => ({
+ ...prev,
+ ...filteredParams
+ }));
+ };
+
+ /**
+ * Reset all parameters to initial values
+ */
+ const resetParams = () => {
+ setParams(initialParams);
+ };
+
+ /**
+ * Manually trigger a refetch of the data
+ */
+ const refetch = () => {
+ fetchData();
+ };
+
+ return {
+ data,
+ loading,
+ error,
+ updateParams,
+ resetParams,
+ refetch
+ };
+}
+
+// // Basic usage (with default caching)
+// const { data, loading, error, refetch } = useStandardApiFetch('/api/your-endpoint');
+
+// // With no caching (for data that changes frequently)
+// const { data, loading, error, refetch } = useStandardApiFetch(
+// '/api/your-endpoint',
+// {},
+// {},
+// { cache: false }
+// );
+
+// // With specific revalidation time
+// const { data, loading, error, refetch } = useStandardApiFetch(
+// '/api/your-endpoint',
+// {},
+// {},
+// { revalidate: 60 } // Revalidate every 60 seconds
+// );
diff --git a/WebServices/management-frontend/src/components/layouts/DashboardLayout.tsx b/WebServices/management-frontend/src/components/layouts/DashboardLayout.tsx
new file mode 100644
index 0000000..2891cdf
--- /dev/null
+++ b/WebServices/management-frontend/src/components/layouts/DashboardLayout.tsx
@@ -0,0 +1,72 @@
+"use client";
+import React, { ReactNode } from "react";
+import Header from "@/components/header/Header";
+import ClientMenu from "@/components/menu/menu";
+
+interface DashboardLayoutProps {
+ children: ReactNode;
+ lang: "en" | "tr";
+ activePage: string;
+
+ // Optional props for client-frontend application
+ sidebarContent?: ReactNode;
+ customHeader?: ReactNode;
+ pageInfo?: Record;
+ searchPlaceholder?: Record;
+}
+
+/**
+ * A reusable dashboard layout component that provides consistent structure
+ * for all dashboard pages with sidebar, header, and content area.
+ */
+export const DashboardLayout: React.FC = ({
+ children,
+ lang,
+ activePage,
+ sidebarContent,
+ customHeader,
+ pageInfo,
+ searchPlaceholder,
+}) => {
+ return (
+
+ {/* Sidebar */}
+
+ {sidebarContent ? (
+ sidebarContent
+ ) : (
+
+ )}
+
+
+ {/* Main Content Area */}
+
+ {/* Header Component - Either custom or default */}
+ {customHeader ? (
+ customHeader
+ ) : pageInfo && searchPlaceholder ? (
+
+ ) : (
+
+ )}
+
+ {/* Page Content */}
+
+ {children}
+
+
+
+ );
+};
+
+export default DashboardLayout;
diff --git a/WebServices/management-frontend/src/components/layouts/PageTemplate.tsx b/WebServices/management-frontend/src/components/layouts/PageTemplate.tsx
new file mode 100644
index 0000000..fd0daee
--- /dev/null
+++ b/WebServices/management-frontend/src/components/layouts/PageTemplate.tsx
@@ -0,0 +1,155 @@
+"use client";
+import React, { ReactNode, useState } from "react";
+import { Card, CardContent } from "@/components/ui/card";
+import { GridSelectionComponent, GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent";
+import { LanguageSelectionComponent, Language } from "@/components/common/HeaderSelections/LanguageSelectionComponent";
+import { PaginationToolsComponent } from "@/components/common/PaginationModifiers/PaginationToolsComponent";
+import { CreateButton } from "@/components/common/ActionButtonsDisplay/CreateButton";
+import type { FormMode } from "@/components/common/FormDisplay/types";
+
+interface PageTemplateProps {
+ title: string;
+ lang: "en" | "tr";
+ translations: Record;
+
+ // Search section
+ searchSection?: ReactNode;
+
+ // Data and pagination
+ data: any[];
+ pagination: any;
+ updatePagination: (params: any) => void;
+ loading: boolean;
+ error: any;
+ refetch: () => void;
+
+ // Content display
+ contentDisplay: ReactNode;
+
+ // Form handling
+ formComponent?: ReactNode;
+ mode: FormMode;
+ setMode: (mode: FormMode) => void;
+ handleCreateClick: () => void;
+ handleCancel: () => void;
+
+ // Language handling
+ setLang?: (lang: Language) => void;
+
+ // Optional components
+ headerActions?: ReactNode;
+ additionalActions?: ReactNode;
+}
+
+/**
+ * A reusable page template that follows the modular pattern established
+ * in the card-example page refactoring.
+ */
+export const PageTemplate: React.FC = ({
+ title,
+ lang,
+ translations,
+ searchSection,
+ data,
+ pagination,
+ updatePagination,
+ loading,
+ error,
+ refetch,
+ contentDisplay,
+ formComponent,
+ mode,
+ setMode,
+ handleCreateClick,
+ handleCancel,
+ setLang,
+ headerActions,
+ additionalActions,
+}) => {
+ const [gridCols, setGridCols] = useState(3);
+
+ return (
+ <>
+ {mode === "list" ? (
+ <>
+ {/* Header section with title and selection components */}
+
+
{title}
+
+ {/* Grid Selection */}
+
+
+ {/* Language Selection */}
+ {})}
+ />
+
+ {/* Additional header actions */}
+ {headerActions}
+
+
+
+ {/* Search filters */}
+ {searchSection && (
+
+
+ {searchSection}
+
+
+ )}
+
+ {/* Create Button */}
+
+
+
+ {/* Additional action buttons */}
+ {additionalActions}
+
+
+
+ {/* Pagination Tools Component */}
+
+
+
+
+
+
+ {/* Content Display */}
+
+ {contentDisplay}
+
+ >
+ ) : (
+ /* Form Display for create/update/view modes */
+ formComponent || (
+
+
Form component not provided
+
+ {translations[lang].cancel || "Cancel"}
+
+
+ )
+ )}
+ >
+ );
+};
+
+export default PageTemplate;
diff --git a/WebServices/management-frontend/src/components/layouts/README.md b/WebServices/management-frontend/src/components/layouts/README.md
new file mode 100644
index 0000000..ecb8b12
--- /dev/null
+++ b/WebServices/management-frontend/src/components/layouts/README.md
@@ -0,0 +1,271 @@
+# Modular Dashboard Layout System
+
+This directory contains reusable layout components for building dashboard pages with a consistent structure and appearance.
+
+## Components
+
+### DashboardLayout
+
+The `DashboardLayout` component provides the overall page structure with:
+- Sidebar navigation
+- Header
+- Main content area
+
+### PageTemplate
+
+The `PageTemplate` component provides a standardized structure for page content with:
+- Header section with title and selection components
+- Search filters section
+- Action buttons section
+- Pagination tools section
+- Content display section
+- Form display for create/update/view modes
+
+## Usage Example
+
+Here's an example of how to use these components to create a new dashboard page:
+
+```tsx
+"use client";
+import React, { useState } from "react";
+import { useApiData } from "@/components/common/hooks/useApiData";
+import { CardDisplay } from "@/components/common/CardDisplay";
+import { FormDisplay } from "@/components/common/FormDisplay/FormDisplay";
+import { TextQueryModifier, SelectQueryModifier, TypeQueryModifier } from "@/components/common/QueryModifiers";
+import { Card, CardContent } from "@/components/ui/card";
+import { Language } from "@/components/common/HeaderSelections/LanguageSelectionComponent";
+import { FormMode } from "@/components/common/FormDisplay/types";
+import { PageTemplate } from "@/components/layouts/PageTemplate";
+import type { GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent";
+
+// Import your schema and translations
+import * as schema from "./schema";
+import { translations } from "./language";
+
+const ExamplePage: React.FC<{ lang: "en" | "tr", queryParams: any }> = ({
+ lang = "en",
+ queryParams
+}) => {
+ // API data hook
+ const {
+ data,
+ pagination,
+ loading,
+ error,
+ updatePagination,
+ refetch
+ } = useApiData('/api/examples');
+
+ // State management
+ const [mode, setMode] = useState("list");
+ const [selectedItem, setSelectedItem] = useState(null);
+ const [gridCols, setGridCols] = useState(3);
+ const [currentLang, setCurrentLang] = useState(lang as Language);
+
+ // Fields to display in cards
+ const showFields = ["name", "type", "status"];
+
+ // Query handling
+ const handleQueryChange = (key: string, value: string | null) => {
+ const newQuery = { ...pagination.query };
+
+ if (value === null || value.trim() === "") {
+ delete newQuery[key];
+ } else {
+ newQuery[key] = value;
+ }
+
+ updatePagination({
+ page: 1,
+ query: newQuery,
+ });
+ };
+
+ // Reset all filters
+ const handleResetAllFilters = () => {
+ updatePagination({
+ page: 1,
+ query: {},
+ });
+ };
+
+ // Action handlers
+ const handleCardClick = (item: schema.ExampleData) => {
+ console.log("Card clicked:", item);
+ };
+
+ const handleViewClick = (item: schema.ExampleData) => {
+ setSelectedItem(item);
+ setMode("view");
+ };
+
+ const handleUpdateClick = (item: schema.ExampleData) => {
+ setSelectedItem(item);
+ setMode("update");
+ };
+
+ const handleCreateClick = () => {
+ setSelectedItem(null);
+ setMode("create");
+ };
+
+ const handleCancel = () => {
+ setMode("list");
+ setSelectedItem(null);
+ };
+
+ // Search section component
+ const SearchSection = (
+
+
+ {/* Type selector */}
+
+
+
+
+ {/* Text search */}
+
+
+ {/* Status dropdown */}
+
+
+
+ {/* Reset filters button */}
+
+
+ {translations[lang].resetAll || "Reset All"}
+
+
+
+ );
+
+ // Form component
+ const FormComponent = selectedItem || mode === "create" ? (
+
+ initialData={selectedItem || undefined}
+ mode={mode}
+ refetch={refetch}
+ setMode={setMode}
+ setSelectedItem={setSelectedItem}
+ onCancel={handleCancel}
+ lang={lang}
+ translations={translations}
+ formProps={{
+ fieldDefinitions: mode === 'create' ? schema.createFieldDefinitions :
+ mode === 'update' ? schema.updateFieldDefinitions :
+ schema.viewFieldDefinitions,
+ validationSchema: mode === 'create' ? schema.CreateExampleSchema :
+ mode === 'update' ? schema.UpdateExampleSchema :
+ schema.ViewExampleSchema,
+ fieldsByMode: schema.fieldsByMode
+ }}
+ />
+ ) : null;
+
+ // Content display component
+ const ContentDisplay = (
+
+ );
+
+ return (
+
+ );
+};
+
+export default ExamplePage;
+```
+
+## Integration with Next.js App Router
+
+To use these components with Next.js App Router, update your page component like this:
+
+```tsx
+// src/app/(DashboardLayout)/your-page/page.tsx
+import React from "react";
+import { retrievePageByUrl } from "@/eventRouters/pageRetriever";
+import DashboardLayout from "@/components/layouts/DashboardLayout";
+
+async function YourPage({
+ searchParams,
+}: {
+ searchParams: Promise<{ [key: string]: string | undefined }>;
+}) {
+ const activePage = "/your-page";
+ const searchParamsInstance = await searchParams;
+ const lang = (searchParamsInstance?.lang as "en" | "tr") || "en";
+ const PageComponent = retrievePageByUrl(activePage);
+
+ return (
+
+
+
+ );
+}
+
+export default YourPage;
+```
+
+This modular approach makes it easy to create new dashboard pages with consistent structure and behavior.
diff --git a/WebServices/management-frontend/src/eventRouters/application/page.tsx b/WebServices/management-frontend/src/eventRouters/application/page.tsx
index 294affa..31b996c 100644
--- a/WebServices/management-frontend/src/eventRouters/application/page.tsx
+++ b/WebServices/management-frontend/src/eventRouters/application/page.tsx
@@ -16,7 +16,10 @@ import { GridSelectionComponent, GridSize } from "@/components/common/HeaderSele
import { LanguageSelectionComponent, Language } from "@/components/common/HeaderSelections/LanguageSelectionComponent";
import type { FormMode } from "@/components/common/FormDisplay/types";
-const ApplicationPage: React.FC = ({ lang = "en" }) => {
+const ApplicationPage: React.FC = ({ lang: initialLang = "en" }) => {
+ // Add local state for language to ensure it persists when changed
+ const [lang, setLang] = useState(initialLang as Language);
+
// Use the API data hook directly
const {
data,
@@ -128,23 +131,22 @@ const ApplicationPage: React.FC = ({ lang = "en" }) => {
};
return (
-
+
{translations[lang].applicationTitle || "Applications"}
-
-
+ {/* Grid Selection */}
+
+
+ {/* Language Selection */}
+
-
- console.log("Language change not implemented", newLang)}
- translations={translations}
- />
-
@@ -174,14 +176,14 @@ const ApplicationPage: React.FC
= ({ lang = "en" }) => {
{translations[lang].filterSelection || "Filter Selection"}
-
{translations[lang].resetAll || "Reset All"}
-
+ */}
{/* Search input */}
diff --git a/WebServices/management-frontend/src/eventRouters/application/schema.ts b/WebServices/management-frontend/src/eventRouters/application/schema.ts
index 2842c33..6a540c1 100644
--- a/WebServices/management-frontend/src/eventRouters/application/schema.ts
+++ b/WebServices/management-frontend/src/eventRouters/application/schema.ts
@@ -1,4 +1,19 @@
import { z } from "zod";
+import { flattenFieldDefinitions } from "../schemas/zodSchemas";
+
+export interface ApplicationData {
+ id?: number;
+ name: string;
+ application_code: string;
+ site_url: string;
+ application_type: string;
+ application_for?: string;
+ description?: string;
+ active: boolean;
+ deleted?: boolean;
+ created_at?: string;
+ updated_at?: string;
+}
// Base schema with all possible fields
const ApplicationBaseSchema = z.object({
@@ -22,6 +37,34 @@ const ApplicationBaseSchema = z.object({
updated_at: z.string().optional(),
});
+const ApplicationBaseTranslationTr = {
+ uu_id: "UUID",
+ name: "Name",
+ application_code: "Application Code",
+ site_url: "Site URL",
+ application_type: "Application Type",
+ application_for: "Application For",
+ description: "Description",
+ active: "Active",
+ deleted: "Deleted",
+ created_at: "Created At",
+ updated_at: "Updated At",
+};
+
+const ApplicationBaseTranslationEn = {
+ uu_id: "UUID",
+ name: "Name",
+ application_code: "Application Code",
+ site_url: "Site URL",
+ application_type: "Application Type",
+ application_for: "Application For",
+ description: "Description",
+ active: "Active",
+ deleted: "Deleted",
+ created_at: "Created At",
+ updated_at: "Updated At",
+};
+
// Schema for creating a new application
export const CreateApplicationSchema = ApplicationBaseSchema.omit({
uu_id: true,
@@ -50,31 +93,6 @@ export type CreateApplicationFormData = z.infer;
export type UpdateApplicationFormData = z.infer;
export type ViewApplicationFormData = z.infer;
-// Define field definition type
-export interface FieldDefinition {
- type: string;
- group: string;
- label: string;
- options?: string[];
- readOnly?: boolean;
- required?: boolean;
- defaultValue?: any;
-}
-
-export interface ApplicationData {
- id?: number;
- name: string;
- application_code: string;
- site_url: string;
- application_type: string;
- application_for?: string;
- description?: string;
- active: boolean;
- deleted?: boolean;
- created_at?: string;
- updated_at?: string;
-}
-
// Base field definitions grouped by section
const baseFieldDefinitions = {
// Identification fields
@@ -82,11 +100,30 @@ const baseFieldDefinitions = {
title: "Identification Information",
order: 1,
fields: {
- uu_id: { type: "text", label: "UUID", readOnly: true, required: false },
- name: { type: "text", label: "Name", readOnly: false, required: true },
+ uu_id: {
+ type: "text",
+ label: {
+ tr: ApplicationBaseTranslationTr.uu_id,
+ en: ApplicationBaseTranslationEn.uu_id,
+ },
+ readOnly: true,
+ required: false,
+ },
+ name: {
+ type: "text",
+ label: {
+ tr: ApplicationBaseTranslationTr.name,
+ en: ApplicationBaseTranslationEn.name,
+ },
+ readOnly: false,
+ required: true,
+ },
application_code: {
type: "text",
- label: "Application Code",
+ label: {
+ tr: ApplicationBaseTranslationTr.application_code,
+ en: ApplicationBaseTranslationEn.application_code,
+ },
readOnly: false,
required: true,
},
@@ -100,27 +137,39 @@ const baseFieldDefinitions = {
fields: {
site_url: {
type: "text",
- label: "Site URL",
+ label: {
+ tr: ApplicationBaseTranslationTr.site_url,
+ en: ApplicationBaseTranslationEn.site_url,
+ },
readOnly: false,
required: true,
},
application_type: {
type: "select",
- label: "Application Type",
+ label: {
+ tr: ApplicationBaseTranslationTr.application_type,
+ en: ApplicationBaseTranslationEn.application_type,
+ },
options: ["info", "Dash", "Admin"],
readOnly: false,
required: true,
},
application_for: {
type: "select",
- label: "Application For",
+ label: {
+ tr: ApplicationBaseTranslationTr.application_for,
+ en: ApplicationBaseTranslationEn.application_for,
+ },
options: ["EMP", "OCC"],
readOnly: false,
required: false,
},
description: {
type: "textarea",
- label: "Description",
+ label: {
+ tr: ApplicationBaseTranslationTr.description,
+ en: ApplicationBaseTranslationEn.description,
+ },
readOnly: false,
required: false,
},
@@ -134,14 +183,20 @@ const baseFieldDefinitions = {
fields: {
active: {
type: "checkbox",
- label: "Active",
+ label: {
+ tr: ApplicationBaseTranslationTr.active,
+ en: ApplicationBaseTranslationEn.active,
+ },
readOnly: false,
required: false,
defaultValue: true,
},
deleted: {
type: "checkbox",
- label: "Deleted",
+ label: {
+ tr: ApplicationBaseTranslationTr.deleted,
+ en: ApplicationBaseTranslationEn.deleted,
+ },
readOnly: true,
required: false,
defaultValue: false,
@@ -156,13 +211,19 @@ const baseFieldDefinitions = {
fields: {
created_at: {
type: "date",
- label: "Created At",
+ label: {
+ tr: ApplicationBaseTranslationTr.created_at,
+ en: ApplicationBaseTranslationEn.created_at,
+ },
readOnly: true,
required: false,
},
updated_at: {
type: "date",
- label: "Updated At",
+ label: {
+ tr: ApplicationBaseTranslationTr.updated_at,
+ en: ApplicationBaseTranslationEn.updated_at,
+ },
readOnly: true,
required: false,
},
@@ -170,28 +231,6 @@ const baseFieldDefinitions = {
},
};
-// Helper function to flatten grouped field definitions into a flat structure
-const flattenFieldDefinitions = (
- groupedDefs: any
-): Record => {
- const result: Record = {};
-
- Object.entries(groupedDefs).forEach(
- ([groupName, groupConfig]: [string, any]) => {
- Object.entries(groupConfig.fields).forEach(
- ([fieldName, fieldConfig]: [string, any]) => {
- result[fieldName] = {
- ...fieldConfig,
- group: groupName,
- };
- }
- );
- }
- );
-
- return result;
-};
-
// Create a flat version of the field definitions for compatibility
const flatFieldDefinitions = flattenFieldDefinitions(baseFieldDefinitions);
@@ -386,6 +425,3 @@ export const fieldsByMode = {
update: Object.keys(updateFieldDefinitions),
view: Object.keys(viewFieldDefinitions),
};
-
-// Note: Direct fetch function has been removed to use the API route instead
-// Data fetching is now handled in the hooks.ts file using the POST endpoint
diff --git a/WebServices/management-frontend/src/eventRouters/schemas/zodSchemas.ts b/WebServices/management-frontend/src/eventRouters/schemas/zodSchemas.ts
new file mode 100644
index 0000000..2b89361
--- /dev/null
+++ b/WebServices/management-frontend/src/eventRouters/schemas/zodSchemas.ts
@@ -0,0 +1,35 @@
+// Define field definition type
+interface FieldDefinition {
+ type: string;
+ group: string;
+ label: string;
+ options?: string[];
+ readOnly?: boolean;
+ required?: boolean;
+ defaultValue?: any;
+}
+
+// Helper function to flatten grouped field definitions into a flat structure
+const flattenFieldDefinitions = (
+ groupedDefs: any
+): Record => {
+ const result: Record = {};
+
+ Object.entries(groupedDefs).forEach(
+ ([groupName, groupConfig]: [string, any]) => {
+ Object.entries(groupConfig.fields).forEach(
+ ([fieldName, fieldConfig]: [string, any]) => {
+ result[fieldName] = {
+ ...fieldConfig,
+ group: groupName,
+ };
+ }
+ );
+ }
+ );
+
+ return result;
+};
+
+export type { FieldDefinition };
+export { flattenFieldDefinitions };
diff --git a/docker-compose.yml b/docker-compose.yml
index 7c01b10..84cc2ed 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,17 +1,17 @@
services:
- # client_frontend:
- # container_name: client_frontend
- # build:
- # context: .
- # dockerfile: WebServices/client-frontend/Dockerfile
- # networks:
- # - wag-services
- # ports:
- # - "3000:3000"
- # environment:
- # - NODE_ENV=development
- # cpus: 1
- # mem_limit: 2048m
+ client_frontend:
+ container_name: client_frontend
+ build:
+ context: .
+ dockerfile: WebServices/client-frontend/Dockerfile
+ networks:
+ - wag-services
+ ports:
+ - "3000:3000"
+ environment:
+ - NODE_ENV=development
+ cpus: 1
+ mem_limit: 2048m
# volumes:
# - client-frontend:/WebServices/client-frontend
@@ -57,32 +57,32 @@ services:
mem_limit: 512m
cpus: 0.5
- # identity_service:
- # container_name: identity_service
- # build:
- # context: .
- # dockerfile: ApiServices/IdentityService/Dockerfile
- # networks:
- # - wag-services
- # depends_on:
- # - initializer_service
- # env_file:
- # - api_env.env
- # environment:
- # - API_PATH=app:app
- # - API_HOST=0.0.0.0
- # - API_PORT=8002
- # - API_LOG_LEVEL=info
- # - API_RELOAD=1
- # - API_APP_NAME=evyos-identity-api-gateway
- # - API_TITLE=WAG API Identity Api Gateway
- # - API_FORGOT_LINK=https://identity_service/forgot-password
- # - API_DESCRIPTION=This api is serves as web identity api gateway only to evyos web services.
- # - API_APP_URL=https://identity_service
- # ports:
- # - "8002:8002"
- # mem_limit: 512m
- # cpus: 0.5
+ identity_service:
+ container_name: identity_service
+ build:
+ context: .
+ dockerfile: ApiServices/IdentityService/Dockerfile
+ networks:
+ - wag-services
+ depends_on:
+ - initializer_service
+ env_file:
+ - api_env.env
+ environment:
+ - API_PATH=app:app
+ - API_HOST=0.0.0.0
+ - API_PORT=8002
+ - API_LOG_LEVEL=info
+ - API_RELOAD=1
+ - API_APP_NAME=evyos-identity-api-gateway
+ - API_TITLE=WAG API Identity Api Gateway
+ - API_FORGOT_LINK=https://identity_service/forgot-password
+ - API_DESCRIPTION=This api is serves as web identity api gateway only to evyos web services.
+ - API_APP_URL=https://identity_service
+ ports:
+ - "8002:8002"
+ mem_limit: 512m
+ cpus: 0.5
# building_service:
# container_name: building_service