From ac8c3fe1c319431388eb4cd63409c6f24962bb5d Mon Sep 17 00:00:00 2001 From: Berkay Date: Sat, 3 May 2025 18:38:50 +0300 Subject: [PATCH] updated management service bind --- .../append/{event => application}/page.tsx | 0 .../common/CardDisplay/CardDisplay.tsx | 2 +- .../common/FormDisplay/FormDisplay.tsx | 22 +- .../components/common/FormDisplay/types.ts | 13 +- .../QueryModifiers/TextQueryModifier.tsx | 25 +- .../src/components/menu/language.ts | 10 +- .../src/components/ui/select.tsx | 6 +- .../eventRouters/appendersService/language.ts | 143 ++++++ .../appendersService/listComponent.tsx | 86 ++++ .../eventRouters/appendersService/page.tsx | 214 +++++++- .../appendersService/schemaList/schema.ts | 476 ++++++++++++++++++ .../src/eventRouters/appendersService/type.ts | 17 + .../src/eventRouters/application/language.ts | 41 +- .../src/eventRouters/application/page.tsx | 247 +++------ .../src/eventRouters/application/schema.ts | 76 +-- 15 files changed, 1085 insertions(+), 293 deletions(-) rename WebServices/management-frontend/src/app/(DashboardLayout)/append/{event => application}/page.tsx (100%) create mode 100644 WebServices/management-frontend/src/eventRouters/appendersService/language.ts create mode 100644 WebServices/management-frontend/src/eventRouters/appendersService/listComponent.tsx create mode 100644 WebServices/management-frontend/src/eventRouters/appendersService/schemaList/schema.ts create mode 100644 WebServices/management-frontend/src/eventRouters/appendersService/type.ts diff --git a/WebServices/management-frontend/src/app/(DashboardLayout)/append/event/page.tsx b/WebServices/management-frontend/src/app/(DashboardLayout)/append/application/page.tsx similarity index 100% rename from WebServices/management-frontend/src/app/(DashboardLayout)/append/event/page.tsx rename to WebServices/management-frontend/src/app/(DashboardLayout)/append/application/page.tsx diff --git a/WebServices/management-frontend/src/components/common/CardDisplay/CardDisplay.tsx b/WebServices/management-frontend/src/components/common/CardDisplay/CardDisplay.tsx index 584e098..9d2a8a0 100644 --- a/WebServices/management-frontend/src/components/common/CardDisplay/CardDisplay.tsx +++ b/WebServices/management-frontend/src/components/common/CardDisplay/CardDisplay.tsx @@ -47,7 +47,7 @@ export function CardDisplay({ {(translations[lang] || {}).noData || "No data found"} ) : ( - data.map((item, index) => ( + data.map((item: T, index: number) => ( ({ }: FormDisplayProps) { const [enhancedFormProps, setEnhancedFormProps] = useState(formProps); - // Update form props when language or mode changes useEffect(() => { const updateFormProps = async () => { try { - // Check if schemaPath is provided in formProps if (formProps.schemaPath) { - // Dynamic import of the schema module const schemaModule = await import(formProps.schemaPath); - // Get the appropriate field definitions based on mode let fieldDefs; if (schemaModule.fieldDefinitions?.getDefinitionsByMode) { fieldDefs = schemaModule.fieldDefinitions.getDefinitionsByMode(mode); @@ -40,13 +36,10 @@ export function FormDisplay({ fieldDefs = schemaModule.viewFieldDefinitions; } - // Get the appropriate validation schema based on mode and language let validationSchema; if (mode === "create" && schemaModule.getCreateApplicationSchema) { - // Use language-aware schema factory function validationSchema = schemaModule.getCreateApplicationSchema(lang as "en" | "tr"); } else if (mode === "update" && schemaModule.getUpdateApplicationSchema) { - // Use language-aware schema factory function validationSchema = schemaModule.getUpdateApplicationSchema(lang as "en" | "tr"); } else if (mode === "view" && schemaModule.ViewApplicationSchema) { validationSchema = schemaModule.ViewApplicationSchema; @@ -54,23 +47,17 @@ export function FormDisplay({ validationSchema = schemaModule.ApplicationSchema; } - // Get the grouped field definitions structure if available const groupedFieldDefs = schemaModule.baseFieldDefinitions || {}; - - // Update form props with schema information and current language setEnhancedFormProps({ ...formProps, fieldDefinitions: fieldDefs || {}, validationSchema, fieldsByMode: schemaModule.fieldsByMode || {}, groupedFieldDefinitions: groupedFieldDefs, - // Add current language to force child components to recognize changes currentLang: lang, - // Add schema path for dynamic imports in child components schemaPath: formProps.schemaPath }); } else { - // If no schema path, just update with current language setEnhancedFormProps({ ...formProps, currentLang: lang @@ -78,7 +65,6 @@ export function FormDisplay({ } } catch (error) { console.error("Error loading schema definitions:", error); - // Even on error, update the language setEnhancedFormProps({ ...formProps, currentLang: lang @@ -87,17 +73,13 @@ export function FormDisplay({ }; updateFormProps(); - }, [formProps, mode, lang]); // Lang dependency ensures re-fetch when language changes + }, [formProps, mode, lang]); - // Debug the props received by FormDisplay - // FormDisplay component renders different form modes based on the mode prop - - // 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 + key={`create-${lang}`} refetch={refetch} setMode={setMode} setSelectedItem={setSelectedItem} diff --git a/WebServices/management-frontend/src/components/common/FormDisplay/types.ts b/WebServices/management-frontend/src/components/common/FormDisplay/types.ts index 9b07e2a..6dbfd44 100644 --- a/WebServices/management-frontend/src/components/common/FormDisplay/types.ts +++ b/WebServices/management-frontend/src/components/common/FormDisplay/types.ts @@ -1,6 +1,5 @@ "use client"; -// Import field definitions type export interface FieldDefinition { type: string; group: string; @@ -12,13 +11,13 @@ export interface FieldDefinition { name?: string; } -// Define the FormMode type to ensure consistency export type FormMode = "list" | "create" | "update" | "view"; +export type FormModeView = "list" | "view"; export interface BaseFormProps { initialData?: T; refetch?: () => void; - setMode: React.Dispatch>; + setMode: React.Dispatch>; setSelectedItem: React.Dispatch>; onCancel: () => void; lang: string; @@ -30,19 +29,19 @@ export interface BaseFormProps { export interface CreateComponentProps extends BaseFormProps {} export interface UpdateComponentProps extends BaseFormProps { - initialData: T; // Required for update + initialData: T; apiUrl: string; } export interface ViewComponentProps extends BaseFormProps { - initialData: T; // Required for view + initialData: T; } export interface FormDisplayProps { - mode: FormMode; + mode: FormMode | FormModeView; initialData?: T; refetch?: () => void; - setMode: React.Dispatch>; + setMode: React.Dispatch>; setSelectedItem: React.Dispatch>; onCancel: () => void; lang: string; diff --git a/WebServices/management-frontend/src/components/common/QueryModifiers/TextQueryModifier.tsx b/WebServices/management-frontend/src/components/common/QueryModifiers/TextQueryModifier.tsx index a8315e2..91068cb 100644 --- a/WebServices/management-frontend/src/components/common/QueryModifiers/TextQueryModifier.tsx +++ b/WebServices/management-frontend/src/components/common/QueryModifiers/TextQueryModifier.tsx @@ -10,26 +10,21 @@ export const TextQueryModifier: React.FC = ({ value, label, placeholder, - onQueryChange, translations, lang, + onQueryChange, }) => { const t = translations[lang] || {}; - const handleChange = useCallback((e: React.ChangeEvent) => { - const newValue = e.target.value; - onQueryChange(fieldKey, newValue); + const newValue = e.target.value; onQueryChange(fieldKey, newValue); }, [fieldKey, onQueryChange]); const handleClear = useCallback(() => { - // Clear both the regular field and the ilike filter - onQueryChange(fieldKey, null); - onQueryChange(`${fieldKey}__ilike`, null); + onQueryChange(fieldKey, null); onQueryChange(`${fieldKey}__ilike`, null); }, [fieldKey, onQueryChange]); const handleKeyUp = useCallback((e: React.KeyboardEvent) => { if (e.key === 'Enter') { - // Apply the search immediately on Enter const formattedValue = value.trim() ? `%${value.trim()}%` : null; onQueryChange(`${fieldKey}__ilike`, formattedValue); } @@ -42,9 +37,7 @@ export const TextQueryModifier: React.FC = ({ return (
- +
= ({ size="icon" className="absolute right-1 top-1 h-8 w-8" onClick={handleClear} - > - - + > )}
+ >
); diff --git a/WebServices/management-frontend/src/components/menu/language.ts b/WebServices/management-frontend/src/components/menu/language.ts index 89171ff..2ed620a 100644 --- a/WebServices/management-frontend/src/components/menu/language.ts +++ b/WebServices/management-frontend/src/components/menu/language.ts @@ -12,14 +12,14 @@ export const dashboardLanguage = { export const NavigationLanguage = { en: { "/dashboard": "Dashboard", - "/append/event": "Event Board", - "/append/service": "Service Board", + "/append/application": "Application Bind Board", + "/append/service": "Service Bind Board", "/application": "Application Board", }, tr: { "/dashboard": "Kontrol Paneli", - "/append/event": "Event Paneli", - "/append/service": "Servis Paneli", + "/append/application": "Uygulama Bağlama Paneli", + "/append/service": "Servis Bağlama Paneli", "/application": "Uygulama Paneli", }, }; @@ -70,4 +70,4 @@ export const OccupantProfileLanguage = { backToBuildings: "Back to buildings", buildNo: "Build No", }, -}; \ No newline at end of file +}; diff --git a/WebServices/management-frontend/src/components/ui/select.tsx b/WebServices/management-frontend/src/components/ui/select.tsx index dcbbc0c..9b9db5f 100644 --- a/WebServices/management-frontend/src/components/ui/select.tsx +++ b/WebServices/management-frontend/src/components/ui/select.tsx @@ -44,7 +44,7 @@ function SelectTrigger({ > {children} - + {/* */} ) @@ -63,7 +63,7 @@ function SelectContent({ className={cn( "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md", position === "popper" && - "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", + "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )} position={position} @@ -74,7 +74,7 @@ function SelectContent({ className={cn( "p-1", position === "popper" && - "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1" + "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1" )} > {children} diff --git a/WebServices/management-frontend/src/eventRouters/appendersService/language.ts b/WebServices/management-frontend/src/eventRouters/appendersService/language.ts new file mode 100644 index 0000000..832cdb0 --- /dev/null +++ b/WebServices/management-frontend/src/eventRouters/appendersService/language.ts @@ -0,0 +1,143 @@ +import { + LanguageKey, + TranslationSet, +} from "@/validations/translations/translation"; +import { + ApplicationBaseTranslationEn, + ApplicationBaseTranslationTr, +} from "./schemaList/schema"; + +// Define translations as a flat object structure to match the common components expectations +export const translations = { + en: { + ...ApplicationBaseTranslationEn, + // Page title + mainTitle: "Services", + + // Common actions + create: "Create", + update: "Update", + delete: "Delete", + view: "View", + save: "Save", + cancel: "Cancel", + + // Search and filters + search: "Search", + typeSelection: "Type Selection", + filterSelection: "Filter Selection", + siteUrl: "Site URL", + resetAll: "Reset All", + + // Type options + web: "Web", + mobile: "Mobile", + + // Status options + status: "Status", + active: "Active", + inactive: "Inactive", + pending: "Pending", + + // User types + employee: "Employee", + occupant: "Occupant", + + // Pagination + showing: "Showing", + of: "of", + items: "items", + total: "Total", + filtered: "Filtered", + previous: "Previous", + next: "Next", + page: "Page", + itemsPerPage: "Items per page", + + // Messages + noData: "No data found", + serviceSelectedTitle: "Selected Service", + serviceSelectedContent: "is selected to appende events", + + // Other + applicationType: "Application Type", + availableApplications: "Available Applications", + code: "Code", + sortBy: "Sort by:", + formErrors: "Please correct the errors in the form", + + // Form group titles + identificationInfo: "Identification Information", + applicationDetails: "Application Details", + statusInfo: "Status Information", + systemInfo: "System Information", + createDescription: "Create Application", + }, + tr: { + // Page title + ...ApplicationBaseTranslationTr, + mainTitle: "Servisler", + + // Common actions + create: "Oluştur", + update: "Güncelle", + delete: "Sil", + view: "Görüntüle", + save: "Kaydet", + cancel: "İptal", + // Search and filters + search: "Ara", + typeSelection: "Tür Seçimi", + filterSelection: "Filtre Seçimi", + siteUrl: "Site URL", + resetAll: "Tümünü Sıfırla", + + // Type options + web: "Web", + mobile: "Mobil", + + // Status options + status: "Durum", + active: "Aktif", + inactive: "Pasif", + pending: "Beklemede", + + // User types + employee: "Çalışan", + occupant: "Sakin", + + // Pagination + showing: "Gösteriliyor", + of: "of", + items: "öğeler", + total: "Toplam", + filtered: "Filtreli", + previous: "Önceki", + next: "Sonraki", + page: "Sayfa", + itemsPerPage: "Sayfa başına öğeler", + + // Messages + noData: "Veri bulunamadı", + serviceSelectedTitle: "Seçili Servis", + serviceSelectedContent: "is selected to appende events", + + // Other + applicationType: "Uygulama Türü", + availableApplications: "Mevcut Uygulamalar", + code: "Kod", + sortBy: "Sırala:", + formErrors: "Lütfen formdaki hataları düzeltiniz", + + // Form group titles + identificationInfo: "Kimlik Bilgileri", + applicationDetails: "Uygulama Detayları", + statusInfo: "Durum Bilgileri", + systemInfo: "Sistem Bilgileri", + createDescription: "Yeni bir uygulama oluşturun", + }, +}; + +export function getTranslation(lang: LanguageKey): TranslationSet { + return translations[lang]; +} diff --git a/WebServices/management-frontend/src/eventRouters/appendersService/listComponent.tsx b/WebServices/management-frontend/src/eventRouters/appendersService/listComponent.tsx new file mode 100644 index 0000000..1c07ca9 --- /dev/null +++ b/WebServices/management-frontend/src/eventRouters/appendersService/listComponent.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { translations } from './language'; +import { Filter } from 'lucide-react'; + +import { Card, CardContent } from '@/components/ui/card'; +import { TextQueryModifier } from '@/components/common/QueryModifiers/TextQueryModifier'; +import { PaginationToolsComponent } from '@/components/common/PaginationModifiers/PaginationToolsComponent'; +import { CardDisplay } from '@/components/common/CardDisplay/CardDisplay'; +import { ListComponentProps } from './type'; + +const ListComponent: React.FC = ({ + lang, + loading, + error, + data, + pagination, + showFields, + gridCols, + handleQueryChange, + updatePagination, + handleCardClick, + handleViewClick, +}) => { + + return ( + <> + {/* Search Filters */} + + +
+ {/* Filters on the right */} +
+
+
+ {translations[lang].filterSelection} +
+
+ {/* Search input */} + +
+
+
+
+ + {/* Pagination Tools Component */} + + + + + + + {/* Card Display Component */} +
+ +
+ + ); +}; + +export default ListComponent; \ No newline at end of file diff --git a/WebServices/management-frontend/src/eventRouters/appendersService/page.tsx b/WebServices/management-frontend/src/eventRouters/appendersService/page.tsx index aef7a4e..24a8a9a 100644 --- a/WebServices/management-frontend/src/eventRouters/appendersService/page.tsx +++ b/WebServices/management-frontend/src/eventRouters/appendersService/page.tsx @@ -1,9 +1,215 @@ "use client"; -import React from "react"; -import { PageProps } from "@/validations/translations/translation"; +import React, { useState, useEffect } from "react"; +import { z } from "zod"; -const AppendersServicePage: React.FC = () => { - return
AppendersServicePage
; +import * as schema from "./schemaList/schema"; +import ListComponent from "./listComponent"; +import { translations } from "./language"; + +import { PageProps } from "@/validations/translations/translation"; +import { FormModeView, FormMode } from "@/components/common/FormDisplay/types"; +import { FormDisplay } from "@/components/common/FormDisplay/FormDisplay"; +import { GridSelectionComponent, GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent"; +import { useApiData } from "@/components/common"; +import { Language } from "@/components/common/schemas"; +import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; + +const AppendersServicePage: React.FC = ({ lang }: { lang: Language }) => { + + const { + data: dataServices, + pagination: paginationServices, + loading: loadingServices, + error: errorServices, + updatePagination: updatePaginationServices, + refetch: refetchServices + } = useApiData('/api/services'); + const { + data: dataEvents, + pagination: paginationEvents, + loading: loadingEvents, + error: errorEvents, + updatePagination: updatePaginationEvents, + refetch: refetchEvents + } = useApiData('/api/events'); + const { + data: dataAppenders, + pagination: paginationAppenders, + loading: loadingAppenders, + error: errorAppenders, + updatePagination: updatePaginationAppenders, + refetch: refetchAppenders + } = useApiData('/api/appenders'); + + const [mode, setMode] = useState("list"); + const [selectedItemServices, setSelectedItemServices] = useState(null); + const [selectedItemEvents, setSelectedItemEvents] = useState(null); + const [selectedItemAppenders, setSelectedItemAppenders] = useState(null); + const [gridCols, setGridCols] = useState(3); + + const [fieldDefinitionsServices, setFieldDefinitionsServices] = useState(null); + const [validationSchemaServices, setValidationSchemaServices] = useState(null); + const [fieldDefinitionsEvents, setFieldDefinitionsEvents] = useState(null); + const [validationSchemaEvents, setValidationSchemaEvents] = useState(null); + const [fieldDefinitionsAppenders, setFieldDefinitionsAppenders] = useState(null); + const [validationSchemaAppenders, setValidationSchemaAppenders] = useState(null); + + const showFieldsServices = ["service_name", "service_code", "related_responsibility"]; + const showFieldsEvents = ["description", "marketing_layer", "cost"]; + + useEffect(() => { + setFieldDefinitionsServices(schema.viewFieldDefinitions); setValidationSchemaServices(schema.UpdateApplicationSchema); + setFieldDefinitionsEvents(schema.viewFieldDefinitions); setValidationSchemaEvents(schema.UpdateApplicationSchema); + setFieldDefinitionsAppenders(schema.viewFieldDefinitions); setValidationSchemaAppenders(schema.UpdateApplicationSchema); + }, [lang]); + + const handleQueryChange = (key: string, value: string | null) => { + const newQuery = { ...paginationServices.query }; + if (value === null) { delete newQuery[key]; } else if (value.trim() === "") { delete newQuery[key]; } else { newQuery[key] = value; } + updatePaginationServices({ page: 1, query: newQuery }); + }; + const handleServicesCardClick = (item: schema.ApplicationData) => { console.log("Services Card clicked:", item) }; + const handleServicesViewClick = (item: schema.ApplicationData) => { setSelectedItemServices(item); setMode("view"); }; + + const handleEventsCardClick = (item: schema.ApplicationData) => { console.log("Events Card clicked:", item) }; + const handleEventsViewClick = (item: schema.ApplicationData) => { setSelectedItemEvents(item); setMode("view"); }; + + const handleAppendersCardClick = (item: schema.ApplicationData) => { console.log("Appenders Card clicked:", item) }; + const handleAppendersViewClick = (item: schema.ApplicationData) => { setSelectedItemAppenders(item); setMode("view"); }; + + const cancelAllSelections = () => { + setMode("list"); + setSelectedItemServices(null); + setSelectedItemEvents(null); + setSelectedItemAppenders(null); + }; + + const seriveListProps = { + lang, + loading: loadingServices, + error: errorServices, + data: dataServices, + pagination: paginationServices, + showFields: showFieldsServices, + gridCols: gridCols, + handleQueryChange: handleQueryChange, + updatePagination: updatePaginationServices, + handleCardClick: handleServicesCardClick, + handleViewClick: handleServicesViewClick + }; + + const eventsListProps = { + lang, + loading: loadingEvents, + error: errorEvents, + data: dataEvents, + pagination: paginationEvents, + showFields: showFieldsEvents, + gridCols: gridCols, + handleQueryChange: handleQueryChange, + updatePagination: updatePaginationEvents, + handleCardClick: handleEventsCardClick, + handleViewClick: handleEventsViewClick + }; + + const appendersListProps = { + lang, + loading: loadingAppenders, + error: errorAppenders, + data: dataAppenders, + pagination: paginationAppenders, + showFields: showFieldsEvents, + gridCols: gridCols, + handleQueryChange: handleQueryChange, + updatePagination: updatePaginationAppenders, + handleCardClick: handleAppendersCardClick, + handleViewClick: handleAppendersViewClick + }; + + const serviceFormProps = { + initialData: selectedItemServices || undefined, + mode: mode, + refetch: refetchServices, + setMode: setMode, + setSelectedItem: setSelectedItemServices, + onCancel: cancelAllSelections, + lang: lang, + translations: translations, + apiUrl: '/api/services', + formProps: { + fieldDefinitions: fieldDefinitionsServices, + validationSchema: validationSchemaServices, + fieldsByMode: schema.fieldsByMode + } + }; + + const eventsFormProps = { + initialData: selectedItemEvents || undefined, + mode: mode, + refetch: refetchEvents, + setMode: setMode, + setSelectedItem: setSelectedItemEvents, + onCancel: cancelAllSelections, + lang: lang, + translations: translations, + apiUrl: '/api/events', + formProps: { + fieldDefinitions: fieldDefinitionsEvents, + validationSchema: validationSchemaEvents, + fieldsByMode: schema.fieldsByMode + } + }; + + const appendersFormProps = { + initialData: selectedItemAppenders || undefined, + mode: mode, + refetch: refetchAppenders, + setMode: setMode, + setSelectedItem: setSelectedItemAppenders, + onCancel: cancelAllSelections, + lang: lang, + translations: translations, + apiUrl: '/api/appenders', + formProps: { + fieldDefinitions: fieldDefinitionsAppenders, + validationSchema: validationSchemaAppenders, + fieldsByMode: schema.fieldsByMode + } + }; + + return ( +
+
+

{translations[lang].mainTitle}

+
+
+ + {mode === "list" ? ( +
+ {!selectedItemServices ?
: +
+ + + {translations[lang].serviceSelectedTitle} + {selectedItemServices?.name}{" "}{translations[lang].serviceSelectedContent} + +
+
+
+
+
+ } +
+ ) : ( +
+ {selectedItemServices && {...serviceFormProps} />} + {selectedItemEvents && {...eventsFormProps} />} + {selectedItemAppenders && {...appendersFormProps} />} +
+ )} +
+ ); }; export default AppendersServicePage; diff --git a/WebServices/management-frontend/src/eventRouters/appendersService/schemaList/schema.ts b/WebServices/management-frontend/src/eventRouters/appendersService/schemaList/schema.ts new file mode 100644 index 0000000..0af4fee --- /dev/null +++ b/WebServices/management-frontend/src/eventRouters/appendersService/schemaList/schema.ts @@ -0,0 +1,476 @@ +import { z } from "zod"; +import { flattenFieldDefinitions } from "@/eventRouters/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; +} + +// Validation error messages by language +const errorMessages = { + en: { + nameRequired: "Name is required", + applicationCodeRequired: "Application code is required", + siteUrlRequired: "Site URL is required", + applicationTypeRequired: "Application type is required", + }, + tr: { + nameRequired: "İsim gereklidir", + applicationCodeRequired: "Uygulama kodu gereklidir", + siteUrlRequired: "Site URL'si gereklidir", + applicationTypeRequired: "Uygulama tipi gereklidir", + }, +}; + +// Function to get schema with language-specific validation messages +const getApplicationBaseSchema = (lang: "en" | "tr" = "en") => + z.object({ + // Identification fields + uu_id: z.string().optional(), + name: z.string().min(1, errorMessages[lang].nameRequired), + application_code: z + .string() + .min(1, errorMessages[lang].applicationCodeRequired), + + // Application details + site_url: z.string().min(1, errorMessages[lang].siteUrlRequired), + application_type: z + .string() + .min(1, errorMessages[lang].applicationTypeRequired), + application_for: z.string().optional(), + description: z.string().optional(), + + // Status fields + active: z.boolean().default(true), + deleted: z.boolean().default(false), + + // System fields + created_at: z.string().optional(), + updated_at: z.string().optional(), + }); + +// For backward compatibility +const ApplicationBaseSchema = getApplicationBaseSchema("en"); + +export 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", +}; + +export 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", +}; + +// Create schema for creating new applications with language support +const getCreateApplicationSchema = (lang: "en" | "tr" = "en") => + getApplicationBaseSchema(lang).omit({ + uu_id: true, + created_at: true, + updated_at: true, + deleted: true, + }); + +// Update schema for updating existing applications with language support +const getUpdateApplicationSchema = (lang: "en" | "tr" = "en") => + getApplicationBaseSchema(lang) + .omit({ + created_at: true, + updated_at: true, + deleted: true, + }) + .required({ + uu_id: true, + }); + +// For backward compatibility +const CreateApplicationSchema = getCreateApplicationSchema("en"); +const UpdateApplicationSchema = getUpdateApplicationSchema("en"); + +// Schema for viewing an application (all fields) +const ViewApplicationSchema = ApplicationBaseSchema; + +// Default schema (used for validation) +const ApplicationSchema = ApplicationBaseSchema; + +// Export all schemas and schema generators +export { + ApplicationBaseSchema, + ApplicationSchema, + CreateApplicationSchema, + UpdateApplicationSchema, + ViewApplicationSchema, + getApplicationBaseSchema, + getCreateApplicationSchema, + getUpdateApplicationSchema, +}; + +export type ApplicationFormData = z.infer; +export type CreateApplicationFormData = z.infer; +export type UpdateApplicationFormData = z.infer; +export type ViewApplicationFormData = z.infer; + +// Base field definitions grouped by section +const baseFieldDefinitions = { + // Identification fields + identificationInfo: { + title: "Identification Information", + order: 1, + fields: { + 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: { + tr: ApplicationBaseTranslationTr.application_code, + en: ApplicationBaseTranslationEn.application_code, + }, + readOnly: false, + required: true, + }, + }, + }, + + // Application details + applicationDetails: { + title: "Application Details", + order: 2, + fields: { + site_url: { + type: "text", + label: { + tr: ApplicationBaseTranslationTr.site_url, + en: ApplicationBaseTranslationEn.site_url, + }, + readOnly: false, + required: true, + }, + application_type: { + type: "select", + label: { + tr: ApplicationBaseTranslationTr.application_type, + en: ApplicationBaseTranslationEn.application_type, + }, + options: ["info", "Dash", "Admin"], + readOnly: false, + required: true, + }, + application_for: { + type: "select", + label: { + tr: ApplicationBaseTranslationTr.application_for, + en: ApplicationBaseTranslationEn.application_for, + }, + options: ["EMP", "OCC"], + readOnly: false, + required: false, + }, + description: { + type: "textarea", + label: { + tr: ApplicationBaseTranslationTr.description, + en: ApplicationBaseTranslationEn.description, + }, + readOnly: false, + required: false, + }, + }, + }, + + // Status fields + statusInfo: { + title: "Status Information", + order: 3, + fields: { + active: { + type: "checkbox", + label: { + tr: ApplicationBaseTranslationTr.active, + en: ApplicationBaseTranslationEn.active, + }, + readOnly: false, + required: false, + defaultValue: true, + }, + deleted: { + type: "checkbox", + label: { + tr: ApplicationBaseTranslationTr.deleted, + en: ApplicationBaseTranslationEn.deleted, + }, + readOnly: true, + required: false, + defaultValue: false, + }, + }, + }, + + // System fields + systemInfo: { + title: "System Information", + order: 4, + fields: { + created_at: { + type: "date", + label: { + tr: ApplicationBaseTranslationTr.created_at, + en: ApplicationBaseTranslationEn.created_at, + }, + readOnly: true, + required: false, + }, + updated_at: { + type: "date", + label: { + tr: ApplicationBaseTranslationTr.updated_at, + en: ApplicationBaseTranslationEn.updated_at, + }, + readOnly: true, + required: false, + }, + }, + }, +}; + +// Create a flat version of the field definitions for compatibility +const flatFieldDefinitions = flattenFieldDefinitions(baseFieldDefinitions); + +// Create mode-specific field definitions using the flattened structure +export const createFieldDefinitions = { + name: { + ...flatFieldDefinitions.name, + readOnly: false, + required: true, + defaultValue: "", + }, + application_code: { + ...flatFieldDefinitions.application_code, + readOnly: false, + required: true, + defaultValue: "", + }, + site_url: { + ...flatFieldDefinitions.site_url, + readOnly: false, + required: true, + defaultValue: "", + }, + application_type: { + ...flatFieldDefinitions.application_type, + readOnly: false, + required: true, + defaultValue: "", + }, + application_for: { + ...flatFieldDefinitions.application_for, + readOnly: false, + required: false, + defaultValue: "EMP", + }, + description: { + ...flatFieldDefinitions.description, + readOnly: false, + required: false, + defaultValue: "", + }, + active: { + ...flatFieldDefinitions.active, + readOnly: false, + required: false, + defaultValue: true, + }, +}; + +// Update mode-specific field definitions +export const updateFieldDefinitions = { + uu_id: { + ...flatFieldDefinitions.uu_id, + readOnly: true, + required: false, + defaultValue: "", + }, + name: { + ...flatFieldDefinitions.name, + readOnly: false, + required: true, + defaultValue: "", + }, + application_code: { + ...flatFieldDefinitions.application_code, + readOnly: false, + required: true, + defaultValue: "", + }, + site_url: { + ...flatFieldDefinitions.site_url, + readOnly: false, + required: true, + defaultValue: "", + }, + application_type: { + ...flatFieldDefinitions.application_type, + readOnly: false, + required: true, + defaultValue: "", + }, + application_for: { + ...flatFieldDefinitions.application_for, + readOnly: false, + required: false, + defaultValue: "", + }, + description: { + ...flatFieldDefinitions.description, + readOnly: false, + required: false, + defaultValue: "", + }, + active: { + ...flatFieldDefinitions.active, + readOnly: false, + required: false, + defaultValue: true, + }, +}; + +// View mode-specific field definitions +export const viewFieldDefinitions = { + uu_id: { + ...flatFieldDefinitions.uu_id, + readOnly: true, + required: false, + defaultValue: 0, + }, + name: { + ...flatFieldDefinitions.name, + readOnly: true, + required: false, + defaultValue: "", + }, + application_code: { + ...flatFieldDefinitions.application_code, + readOnly: true, + required: false, + defaultValue: "", + }, + site_url: { + ...flatFieldDefinitions.site_url, + readOnly: true, + required: false, + defaultValue: "", + }, + application_type: { + ...flatFieldDefinitions.application_type, + readOnly: true, + required: false, + defaultValue: "", + }, + application_for: { + ...flatFieldDefinitions.application_for, + readOnly: true, + required: false, + defaultValue: "", + }, + description: { + ...flatFieldDefinitions.description, + readOnly: true, + required: false, + defaultValue: "", + }, + active: { + ...flatFieldDefinitions.active, + readOnly: true, + required: false, + defaultValue: true, + }, + deleted: { + ...flatFieldDefinitions.deleted, + readOnly: true, + required: false, + defaultValue: false, + }, + created_at: { + ...flatFieldDefinitions.created_at, + readOnly: true, + required: false, + defaultValue: "", + }, + updated_at: { + ...flatFieldDefinitions.updated_at, + readOnly: true, + required: false, + defaultValue: "", + }, +}; + +// Combined field definitions for all modes +export const fieldDefinitions = { + ...baseFieldDefinitions, + getDefinitionsByMode: (mode: "create" | "update" | "view") => { + switch (mode) { + case "create": + return createFieldDefinitions; + case "update": + return updateFieldDefinitions; + case "view": + return viewFieldDefinitions; + default: + return baseFieldDefinitions; + } + }, +}; + +// Fields to show based on mode - dynamically generated from field definitions +export const fieldsByMode = { + create: Object.keys(createFieldDefinitions), + update: Object.keys(updateFieldDefinitions), + view: Object.keys(viewFieldDefinitions), +}; + +export type FieldDefinitionsType = + | typeof createFieldDefinitions + | typeof updateFieldDefinitions + | typeof viewFieldDefinitions; diff --git a/WebServices/management-frontend/src/eventRouters/appendersService/type.ts b/WebServices/management-frontend/src/eventRouters/appendersService/type.ts new file mode 100644 index 0000000..923e28b --- /dev/null +++ b/WebServices/management-frontend/src/eventRouters/appendersService/type.ts @@ -0,0 +1,17 @@ +import { Language } from "@/components/common/schemas"; +import { GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent"; +import * as schema from "./schemaList/schema"; + +export interface ListComponentProps { + lang: Language; + loading: boolean; + error: any; + data: schema.ApplicationData[]; + pagination: any; + showFields: string[]; + gridCols: GridSize; + handleQueryChange: (key: string, value: string | null) => void; + updatePagination: (pagination: any) => void; + handleCardClick: (item: schema.ApplicationData) => void; + handleViewClick: (item: schema.ApplicationData) => void; +} diff --git a/WebServices/management-frontend/src/eventRouters/application/language.ts b/WebServices/management-frontend/src/eventRouters/application/language.ts index 7db677c..3888cd2 100644 --- a/WebServices/management-frontend/src/eventRouters/application/language.ts +++ b/WebServices/management-frontend/src/eventRouters/application/language.ts @@ -2,42 +2,17 @@ import { LanguageKey, TranslationSet, } from "@/validations/translations/translation"; - -export const fieldLanguageTranslation = { - tr: { - uu_id: "UUID", - name: "Ad", - application_code: "Kod", - site_url: "URL", - application_type: "Tür", - application_for: "Uygulama için", - description: "Açıklama", - active: "Aktif", - deleted: "Silindi", - created_at: "Oluşturulma Tarihi", - updated_at: "Güncellenme Tarihi", - }, - en: { - uu_id: "UUID", - name: "Name", - application_code: "Code", - site_url: "URL", - application_type: "Type", - application_for: "Application for", - description: "Description", - active: "Active", - deleted: "Deleted", - created_at: "Created At", - updated_at: "Updated At", - }, -}; +import { + ApplicationBaseTranslationEn, + ApplicationBaseTranslationTr, +} from "./schema"; // Define translations as a flat object structure to match the common components expectations export const translations = { en: { - ...fieldLanguageTranslation.en, + ...ApplicationBaseTranslationEn, // Page title - applicationTitle: "Applications", + title: "Applications", // Common actions create: "Create", @@ -108,8 +83,8 @@ export const translations = { }, tr: { // Page title - ...fieldLanguageTranslation.tr, - applicationTitle: "Uygulamalar", + ...ApplicationBaseTranslationTr, + title: "Uygulamalar", // Common actions create: "Oluştur", diff --git a/WebServices/management-frontend/src/eventRouters/application/page.tsx b/WebServices/management-frontend/src/eventRouters/application/page.tsx index 0dc32ad..151da07 100644 --- a/WebServices/management-frontend/src/eventRouters/application/page.tsx +++ b/WebServices/management-frontend/src/eventRouters/application/page.tsx @@ -1,6 +1,8 @@ "use client"; import React, { useState, useEffect } from "react"; import * as schema from "./schema"; +import { z } from "zod"; + import { Card, CardContent } from "@/components/ui/card"; import { Building, Filter, User } from "lucide-react"; import { TypeQueryModifier } from "@/components/common/QueryModifiers/TypeQueryModifier"; @@ -12,7 +14,6 @@ import { CardDisplay } from "@/components/common/CardDisplay/CardDisplay"; import { FormMode } from "@/components/common/FormDisplay/types"; import { FormDisplay } from "@/components/common/FormDisplay/FormDisplay"; import { GridSelectionComponent, GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent"; -import { LanguageSelectionComponent } from "@/components/common/HeaderSelections/LanguageSelectionComponent"; import { getCreateApplicationSchema, getUpdateApplicationSchema } from "./schema"; import { translations } from "./language"; import { PageProps } from "@/validations/translations/translation"; @@ -21,64 +22,34 @@ import { Language } from "@/components/common/schemas"; const ApplicationPage: React.FC = ({ lang }: { lang: Language }) => { - const { - data, - pagination, - loading, - error, - updatePagination, - refetch - } = useApiData('/api/applications'); + const { data, pagination, loading, error, updatePagination, refetch } = useApiData('/api/applications'); - // State for managing view/edit modes const [mode, setMode] = useState("list"); const [selectedItem, setSelectedItem] = useState(null); const [gridCols, setGridCols] = useState(3); + const [fieldDefinitions, setFieldDefinitions] = useState(null); + const [validationSchema, setValidationSchema] = useState(null); - // Use state to store the field definitions and validation schema - const [fieldDefinitions, setFieldDefinitions] = useState(() => - mode === 'create' ? schema.createFieldDefinitions : - mode === 'update' ? schema.updateFieldDefinitions : - schema.viewFieldDefinitions - ); - - const [validationSchema, setValidationSchema] = useState(() => - mode === 'create' ? getCreateApplicationSchema(lang) : - mode === 'update' ? getUpdateApplicationSchema(lang) : - schema.ViewApplicationSchema - ); - - // Update field definitions when mode changes useEffect(() => { - // Select the appropriate field definitions based on the current mode - const newFieldDefs = mode === 'create' ? schema.createFieldDefinitions : - mode === 'update' ? schema.updateFieldDefinitions : - schema.viewFieldDefinitions; - - setFieldDefinitions(newFieldDefs); - }, [mode]); - - // Update validation schema when mode or language changes - useEffect(() => { - setValidationSchema( - mode === 'create' ? getCreateApplicationSchema(lang) : - mode === 'update' ? getUpdateApplicationSchema(lang) : - schema.ViewApplicationSchema - ); + if (mode === 'create') { + setFieldDefinitions(schema.createFieldDefinitions); + setValidationSchema(getCreateApplicationSchema(lang)); + } else if (mode === 'update') { + setFieldDefinitions(schema.updateFieldDefinitions); + setValidationSchema(getUpdateApplicationSchema(lang)); + } else { + setFieldDefinitions(schema.viewFieldDefinitions); + setValidationSchema(schema.ViewApplicationSchema); + } }, [mode, lang]); - - // Fields to display in the cards const showFields = ["application_code", "site_url", "application_type"]; - - // Search options const searchOptions = { typeOptions: [ { value: "EMP", label: translations[lang].employee, icon: - }, { value: "OCC", @@ -107,67 +78,61 @@ const ApplicationPage: React.FC = ({ lang }: { lang: Language }) => { ] }; - // Handle query changes const handleQueryChange = (key: string, value: string | null) => { const newQuery = { ...pagination.query }; - - if (value === null) { - // Remove the key if value is null - delete newQuery[key]; - } else if (value.trim() === "") { - // Remove the key if value is empty string - delete newQuery[key]; - } else { - // Add/update the key with the value - newQuery[key] = value; - } - - updatePagination({ - page: 1, // Reset to first page on new search - query: newQuery, - }); + if (value === null) { delete newQuery[key]; } else if (value.trim() === "") { delete newQuery[key]; } else { newQuery[key] = value; } + updatePagination({ page: 1, query: newQuery }); }; + const handleCardClick = (item: schema.ApplicationData) => { console.log("Card clicked:", item) }; + const handleViewClick = (item: schema.ApplicationData) => { setSelectedItem(item); setMode("view"); }; + const handleUpdateClick = (item: schema.ApplicationData) => { setSelectedItem(item); setTimeout(() => { setMode("update") }, 0); }; + const handleCreateClick = () => { setSelectedItem(null); setMode("create"); }; - - // Handle card actions - const handleCardClick = (item: schema.ApplicationData) => { - console.log("Card clicked:", item); - }; - - const handleViewClick = (item: schema.ApplicationData) => { - setSelectedItem(item); - setMode("view"); - }; - - const handleUpdateClick = (item: schema.ApplicationData) => { - setSelectedItem(item); - setTimeout(() => { - setMode("update"); - }, 0); - }; - - // Handle create button click - const handleCreateClick = () => { + const cancelAllSelections = () => { setSelectedItem(null); - setMode("create"); - }; - - // Handle cancel - const handleCancel = () => { setMode("list"); - setSelectedItem(null); }; + const applicationCardDisplayProps = { + showFields: showFields, + data: data, + lang: lang, + translations: translations, + error: error, + loading: loading, + titleField: "name", + onCardClick: handleCardClick, + gridCols: gridCols, + showViewIcon: true, + showUpdateIcon: true, + onViewClick: handleViewClick, + onUpdateClick: handleUpdateClick + }; + + const applicationFormProps = { + initialData: selectedItem || undefined, + mode, + refetch, + setMode, + setSelectedItem, + onCancel: cancelAllSelections, + lang, + translations, + apiUrl: '/api/applications', + formProps: { + fieldDefinitions, + validationSchema, + fieldsByMode: schema.fieldsByMode + } + } + return (
-

{translations[lang].applicationTitle || "Applications"}

+

{translations[lang].title}

{/* Grid Selection */} - +
@@ -193,42 +158,23 @@ const ApplicationPage: React.FC = ({ lang }: { lang: Language }) => { {/* Filters on the right */}
0 ? 'md:w-1/2 md:pl-4' : ''} flex flex-col space-y-4`}>
-
- - {translations[lang].filterSelection || "Filter Selection"} -
- {/* */} +
{translations[lang].filterSelection}
{/* Search input */} - + {/* Site URL dropdown */} - {searchOptions.urlOptions.length > 0 && ( - 0 && ({ value: url, label: url }))} onQueryChange={handleQueryChange} translations={translations} lang={lang} /> - )} + } {/* Additional fields */} {/* {searchOptions.additionalFields && searchOptions.additionalFields?.map(field => ( @@ -249,65 +195,26 @@ const ApplicationPage: React.FC = ({ lang }: { lang: Language }) => { {/* Create Button */} - - - - - + {/* Pagination Tools Component */} - - - - - + {/* Card Display Component */} -
- -
+
) : ( - - initialData={selectedItem || undefined} - mode={mode} - refetch={refetch} - setMode={setMode} - setSelectedItem={setSelectedItem} - onCancel={handleCancel} - lang={lang} - translations={translations} - apiUrl='/api/applications' - formProps={{ - fieldDefinitions, - validationSchema, - fieldsByMode: schema.fieldsByMode - }} - /> + {...applicationFormProps} /> )}
); diff --git a/WebServices/management-frontend/src/eventRouters/application/schema.ts b/WebServices/management-frontend/src/eventRouters/application/schema.ts index be76f3e..0af4fee 100644 --- a/WebServices/management-frontend/src/eventRouters/application/schema.ts +++ b/WebServices/management-frontend/src/eventRouters/application/schema.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { flattenFieldDefinitions } from "../schemas/zodSchemas"; +import { flattenFieldDefinitions } from "@/eventRouters/schemas/zodSchemas"; export interface ApplicationData { id?: number; @@ -21,42 +21,47 @@ const errorMessages = { nameRequired: "Name is required", applicationCodeRequired: "Application code is required", siteUrlRequired: "Site URL is required", - applicationTypeRequired: "Application type is required" + applicationTypeRequired: "Application type is required", }, tr: { nameRequired: "İsim gereklidir", applicationCodeRequired: "Uygulama kodu gereklidir", siteUrlRequired: "Site URL'si gereklidir", - applicationTypeRequired: "Uygulama tipi gereklidir" - } + applicationTypeRequired: "Uygulama tipi gereklidir", + }, }; // Function to get schema with language-specific validation messages -const getApplicationBaseSchema = (lang: "en" | "tr" = "en") => z.object({ - // Identification fields - uu_id: z.string().optional(), - name: z.string().min(1, errorMessages[lang].nameRequired), - application_code: z.string().min(1, errorMessages[lang].applicationCodeRequired), +const getApplicationBaseSchema = (lang: "en" | "tr" = "en") => + z.object({ + // Identification fields + uu_id: z.string().optional(), + name: z.string().min(1, errorMessages[lang].nameRequired), + application_code: z + .string() + .min(1, errorMessages[lang].applicationCodeRequired), - // Application details - site_url: z.string().min(1, errorMessages[lang].siteUrlRequired), - application_type: z.string().min(1, errorMessages[lang].applicationTypeRequired), - application_for: z.string().optional(), - description: z.string().optional(), + // Application details + site_url: z.string().min(1, errorMessages[lang].siteUrlRequired), + application_type: z + .string() + .min(1, errorMessages[lang].applicationTypeRequired), + application_for: z.string().optional(), + description: z.string().optional(), - // Status fields - active: z.boolean().default(true), - deleted: z.boolean().default(false), + // Status fields + active: z.boolean().default(true), + deleted: z.boolean().default(false), - // System fields - created_at: z.string().optional(), - updated_at: z.string().optional(), -}); + // System fields + created_at: z.string().optional(), + updated_at: z.string().optional(), + }); // For backward compatibility const ApplicationBaseSchema = getApplicationBaseSchema("en"); -const ApplicationBaseTranslationTr = { +export const ApplicationBaseTranslationTr = { uu_id: "UUID", name: "Name", application_code: "Application Code", @@ -70,7 +75,7 @@ const ApplicationBaseTranslationTr = { updated_at: "Updated At", }; -const ApplicationBaseTranslationEn = { +export const ApplicationBaseTranslationEn = { uu_id: "UUID", name: "Name", application_code: "Application Code", @@ -85,7 +90,7 @@ const ApplicationBaseTranslationEn = { }; // Create schema for creating new applications with language support -const getCreateApplicationSchema = (lang: "en" | "tr" = "en") => +const getCreateApplicationSchema = (lang: "en" | "tr" = "en") => getApplicationBaseSchema(lang).omit({ uu_id: true, created_at: true, @@ -94,14 +99,16 @@ const getCreateApplicationSchema = (lang: "en" | "tr" = "en") => }); // Update schema for updating existing applications with language support -const getUpdateApplicationSchema = (lang: "en" | "tr" = "en") => - getApplicationBaseSchema(lang).omit({ - created_at: true, - updated_at: true, - deleted: true, - }).required({ - uu_id: true, - }); +const getUpdateApplicationSchema = (lang: "en" | "tr" = "en") => + getApplicationBaseSchema(lang) + .omit({ + created_at: true, + updated_at: true, + deleted: true, + }) + .required({ + uu_id: true, + }); // For backward compatibility const CreateApplicationSchema = getCreateApplicationSchema("en"); @@ -462,3 +469,8 @@ export const fieldsByMode = { update: Object.keys(updateFieldDefinitions), view: Object.keys(viewFieldDefinitions), }; + +export type FieldDefinitionsType = + | typeof createFieldDefinitions + | typeof updateFieldDefinitions + | typeof viewFieldDefinitions;