diff --git a/WebServices/management-frontend/package-lock.json b/WebServices/management-frontend/package-lock.json index 5745e4f..fffab93 100644 --- a/WebServices/management-frontend/package-lock.json +++ b/WebServices/management-frontend/package-lock.json @@ -1022,6 +1022,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, diff --git a/WebServices/management-frontend/setup-shadcn.sh b/WebServices/management-frontend/setup-shadcn.sh index 7897184..685b073 100755 --- a/WebServices/management-frontend/setup-shadcn.sh +++ b/WebServices/management-frontend/setup-shadcn.sh @@ -34,6 +34,7 @@ npx shadcn@latest add calendar -y npx shadcn@latest add date-picker -y npx shadcn@latest add skeleton -y npx shadcn@latest add table -y +npx shadcn@latest add textarea -y # Update any dependencies with legacy peer deps echo "🔄 Updating dependencies..." diff --git a/WebServices/management-frontend/src/app/(DashboardLayout)/application/page.tsx b/WebServices/management-frontend/src/app/(DashboardLayout)/application/page.tsx index 0bfb5f7..4482481 100644 --- a/WebServices/management-frontend/src/app/(DashboardLayout)/application/page.tsx +++ b/WebServices/management-frontend/src/app/(DashboardLayout)/application/page.tsx @@ -1,7 +1,7 @@ import React from "react"; import Header from "@/components/header/Header"; import ClientMenu from "@/components/menu/menu"; -import { retrievePageByUrl } from "@/components/Pages/pageRetriever"; +import { retrievePageByUrl } from "@/eventRouters/pageRetriever"; async function DashboardPage({ searchParams, diff --git a/WebServices/management-frontend/src/app/(DashboardLayout)/dashboard/page.tsx b/WebServices/management-frontend/src/app/(DashboardLayout)/dashboard/page.tsx index d4bc63f..d672533 100644 --- a/WebServices/management-frontend/src/app/(DashboardLayout)/dashboard/page.tsx +++ b/WebServices/management-frontend/src/app/(DashboardLayout)/dashboard/page.tsx @@ -1,7 +1,7 @@ import React from "react"; import Header from "@/components/header/Header"; import ClientMenu from "@/components/menu/menu"; -import { retrievePageByUrl } from "@/components/Pages/pageRetriever"; +import { retrievePageByUrl } from "@/eventRouters/pageRetriever"; async function PageDashboard({ searchParams, diff --git a/WebServices/management-frontend/src/app/api/applications/route.ts b/WebServices/management-frontend/src/app/api/applications/route.ts index 3a06901..e21a25f 100644 --- a/WebServices/management-frontend/src/app/api/applications/route.ts +++ b/WebServices/management-frontend/src/app/api/applications/route.ts @@ -1,81 +1,76 @@ import { NextRequest, NextResponse } from "next/server"; import { listApplications } from "@/apicalls/application/application"; -export async function GET(request: NextRequest) { +export async function POST(request: NextRequest) { try { - // Get query parameters - const searchParams = request.nextUrl.searchParams; + const requestBody = await request.json(); - // Extract pagination parameters - const page = parseInt(searchParams.get("page") || "1"); - const size = parseInt(searchParams.get("size") || "10"); - - // Extract sorting parameters - const orderField = searchParams.getAll("orderField") || ["name"]; - const orderType = searchParams.getAll("orderType") || ["asc"]; - - // Extract query filters - const query: Record = {}; - for (const [key, value] of searchParams.entries()) { - if (!["page", "size", "orderField", "orderType"].includes(key)) { - query[key] = value; - } - } + // Check if this is a list request or a create request + // If action is 'list' or if pagination parameters are present, treat as a list request + if (requestBody.action === 'list' || requestBody.page !== undefined) { + // Extract pagination parameters with defaults + const page = requestBody.page || 1; + const size = requestBody.size || 10; + + // Extract sorting parameters with defaults + const orderField = requestBody.orderField || ["name"]; + const orderType = requestBody.orderType || ["asc"]; + + // Extract query filters + const query = requestBody.query || {}; - // Call the actual API function - const response = await listApplications({ - page, - size, - orderField, - orderType, - query, - }); - - // Return the response - return NextResponse.json({ - data: response.data || [], - pagination: response.pagination || { + // Call the actual API function for listing + const response = await listApplications({ page, size, - totalCount: 0, - totalItems: 0, - totalPages: 0, - pageCount: 0, orderField, orderType, query, - next: false, - back: false, - }, - }); - } catch (error) { - console.error("API error:", error); - return NextResponse.json( - { error: "Internal Server Error" }, - { status: 500 } - ); - } -} + }); -export async function POST(request: NextRequest) { - try { - const body = await request.json(); - - // Here you would call your actual API function to create a new application - // For example: const result = await createApplication(body); - - // For now, we'll return a mock response - return NextResponse.json( - { - success: true, - data: { - id: Math.floor(Math.random() * 1000), - ...body, - createdAt: new Date().toISOString(), + // Return the list response + return NextResponse.json({ + data: response.data || [], + pagination: response.pagination || { + page, + size, + totalCount: 0, + totalItems: 0, + totalPages: 0, + pageCount: 0, + orderField, + orderType, + query, + next: false, + back: false, }, - }, - { status: 201 } - ); + }); + } + // If action is 'create' or no action is specified (assuming it's a create request) + else if (requestBody.action === 'create' || !requestBody.action) { + // Here you would call your actual API function to create a new application + // For example: const result = await createApplication(requestBody.data); + + // For now, we'll return a mock response + return NextResponse.json( + { + success: true, + data: { + id: Math.floor(Math.random() * 1000), + ...requestBody.data, + createdAt: new Date().toISOString(), + }, + }, + { status: 201 } + ); + } + // If the action is not recognized + else { + return NextResponse.json( + { error: "Invalid action specified" }, + { status: 400 } + ); + } } catch (error) { console.error("API error:", error); return NextResponse.json( diff --git a/WebServices/management-frontend/src/app/card-example/page.tsx b/WebServices/management-frontend/src/app/card-example/page.tsx index ed1988a..ccd2c57 100644 --- a/WebServices/management-frontend/src/app/card-example/page.tsx +++ b/WebServices/management-frontend/src/app/card-example/page.tsx @@ -1,11 +1,18 @@ "use client"; import React, { useState } from "react"; -import { CardDisplay } from "@/components/commons/CardDisplay"; -import { SearchComponent } from "@/components/commons/SearchComponent"; -import { ActionButtonsComponent } from "@/components/commons/ActionButtonsComponent"; -import { PaginationToolsComponent } from "@/components/commons/PaginationToolsComponent"; -import { useApiData } from "@/components/commons/hooks/useApiData"; +import { CardDisplay } from "@/components/common/CardDisplay"; +import { TextQueryModifier, SelectQueryModifier, TypeQueryModifier } from "@/components/common/QueryModifiers"; +import { Card, CardContent } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Filter } from "lucide-react"; +import { CreateButton } from "@/components/common/ActionButtonsDisplay/CreateButton"; +import { PaginationToolsComponent } from "@/components/common/PaginationModifiers/PaginationToolsComponent"; +import { useApiData } from "@/components/common/hooks/useApiData"; import { User, Building } from "lucide-react"; +import { GridSelectionComponent, GridSize } from "@/components/common/HeaderSelections/GridSelectionComponent"; +import { LanguageSelectionComponent, Language } from "@/components/common/HeaderSelections/LanguageSelectionComponent"; +import { FormDisplay } from "@/components/common/FormDisplay/FormDisplay"; +import type { FormMode } from "@/components/common/FormDisplay/types"; // Example translations const translations = { @@ -18,6 +25,7 @@ const translations = { typeSelection: "Type Selection", filterSelection: "Filter Selection", siteUrl: "Site URL", + resetAll: "Reset All", employee: "Employee", occupant: "Occupant", showing: "Showing", @@ -50,6 +58,7 @@ const translations = { typeSelection: "Tür Seçimi", filterSelection: "Filtre Seçimi", siteUrl: "Site URL", + resetAll: "Tümünü Sıfırla", employee: "Çalışan", occupant: "Sakin", showing: "Gösteriliyor", @@ -86,64 +95,27 @@ interface ApplicationData { application_type: string; } -// Form component for create/update -const FormComponent: React.FC<{ - initialData?: ApplicationData; - mode: "create" | "update"; - refetch?: () => void; - setMode: React.Dispatch>; - setSelectedItem: React.Dispatch>; - onCancel: () => void; - lang: string; -}> = ({ initialData, mode, refetch, setMode, onCancel, lang }) => { - // In a real application, you would implement form fields and submission logic here - return ( -
-

- {mode === "create" ? translations[lang as "en" | "tr"].create : translations[lang as "en" | "tr"].update} -

-

This is a placeholder for the {mode} form.

-
- - -
-
- ); -}; +// We're now using the modular FormDisplay component instead of an inline FormComponent export default function CardExamplePage() { - const [lang, setLang] = useState<"en" | "tr">("en"); - const [mode, setMode] = useState<"list" | "create" | "update">("list"); + const [lang, setLang] = useState("en"); + const [mode, setMode] = useState("list"); const [selectedItem, setSelectedItem] = useState(null); - const [gridCols, setGridCols] = useState<1 | 2 | 3 | 4 | 5 | 6>(3); - + const [gridCols, setGridCols] = useState(3); + // Use the API data hook - const { - data, - pagination, - loading, - error, - updatePagination, - refetch + const { + data, + pagination, + loading, + error, + updatePagination, + refetch } = useApiData("/api/data"); - + // Fields to display in the cards const showFields = ["application_code", "site_url", "application_type"]; - + // Search options const searchOptions = { typeOptions: [ @@ -169,7 +141,7 @@ export default function CardExamplePage() { { name: "status", label: translations[lang as "en" | "tr"].status, - type: "select", + type: "select" as const, options: [ { value: "active", label: "Active" }, { value: "inactive", label: "Inactive" }, @@ -179,11 +151,32 @@ export default function CardExamplePage() { ] }; - // Handle search - const handleSearch = (query: Record) => { + // 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: query, + query: newQuery, + }); + }; + + // Reset all filters + const handleResetAllFilters = () => { + updatePagination({ + page: 1, + query: {}, }); }; @@ -191,14 +184,13 @@ export default function CardExamplePage() { const handleCardClick = (item: ApplicationData) => { console.log("Card clicked:", item); }; - + const handleViewClick = (item: ApplicationData) => { - console.log("View clicked:", item); - // Example: Open a modal to view details + setSelectedItem(item); + setMode("view"); }; - + const handleUpdateClick = (item: ApplicationData) => { - console.log("Update clicked:", item); setSelectedItem(item); setMode("update"); }; @@ -206,66 +198,135 @@ export default function CardExamplePage() { // Handle create button click const handleCreateClick = () => { setSelectedItem(null); - setMode("create"); + setMode("create" as FormMode); }; // Handle cancel const handleCancel = () => { - setMode("list"); + setMode("list" as FormMode); setSelectedItem(null); }; - + return (

Card Example Page

-
- Grid Size: - -
+
- +
- + {mode === "list" ? ( <> - {/* Search Component */} - + {/* Search Filters */} + + +
+ {/* Type selection - vertical on the left */} +
+ +
- {/* Action Buttons Component */} - + {/* Filters on the right */} +
0 ? 'md:w-1/2 md:pl-4' : ''} flex flex-col space-y-4`}> +
+
+ + {translations[lang].filterSelection || "Filter Selection"} +
+ +
+ + {/* Search input */} + + + {/* Site URL dropdown */} + {searchOptions.urlOptions.length > 0 && ( + ({ value: url, label: url }))} + onQueryChange={handleQueryChange} + translations={translations} + lang={lang} + /> + )} + + {/* Additional fields */} + {searchOptions.additionalFields.map(field => ( + + ))} +
+
+
+
+ + {/* Create Button */} + + + + + + + {/* Pagination Tools Component */} + + + + + {/* Card Display Component */}
@@ -285,20 +346,9 @@ export default function CardExamplePage() { onUpdateClick={handleUpdateClick} />
- - {/* Pagination Tools Component */} -
- -
) : ( - initialData={selectedItem || undefined} mode={mode} refetch={refetch} @@ -306,6 +356,7 @@ export default function CardExamplePage() { setSelectedItem={setSelectedItem} onCancel={handleCancel} lang={lang} + translations={translations} /> )}
diff --git a/WebServices/management-frontend/src/app/example/page.tsx b/WebServices/management-frontend/src/app/example/page.tsx index 5e74981..7d3f808 100644 --- a/WebServices/management-frontend/src/app/example/page.tsx +++ b/WebServices/management-frontend/src/app/example/page.tsx @@ -1,6 +1,6 @@ "use client"; import React, { useState } from "react"; -import { CardDisplay, useApiData } from "@/components/commons"; +import { CardDisplay, useApiData } from "@/components/common"; import { User, Building } from "lucide-react"; // Example translations diff --git a/WebServices/management-frontend/src/components/Pages/application/ActionButtonsComponent.tsx b/WebServices/management-frontend/src/components/Pages/application/ActionButtonsComponent.tsx deleted file mode 100644 index 9cee034..0000000 --- a/WebServices/management-frontend/src/components/Pages/application/ActionButtonsComponent.tsx +++ /dev/null @@ -1,26 +0,0 @@ -"use client"; -import React from "react"; -import { Button } from "@/components/ui/button"; -import { Plus } from "lucide-react"; -import { LanguageTranslation } from "@/components/validations/translations/translation"; - -interface ActionButtonsComponentProps { - onCreateClick: () => void; - translations: Record; - lang: string; -} - -export const ActionButtonsComponent: React.FC = ({ - onCreateClick, - translations, - lang, -}) => { - return ( -
- -
- ); -}; diff --git a/WebServices/management-frontend/src/components/Pages/application/DataDisplayComponent.tsx b/WebServices/management-frontend/src/components/Pages/application/DataDisplayComponent.tsx deleted file mode 100644 index 7457bc5..0000000 --- a/WebServices/management-frontend/src/components/Pages/application/DataDisplayComponent.tsx +++ /dev/null @@ -1,85 +0,0 @@ -"use client"; -import React from "react"; -import { Card, CardContent } from "@/components/ui/card"; -import { LanguageTranslation } from "@/components/validations/translations/translation"; -import { ApplicationData } from "./types"; - -interface DataDisplayComponentProps { - data: ApplicationData[]; - loading: boolean; - error: Error | null; - onUpdateClick: (item: ApplicationData) => void; - translations: Record; - lang: string; -} - -export const DataDisplayComponent: React.FC = ({ - data, - loading, - error, - onUpdateClick, - translations, - lang, -}) => { - if (loading) { - return ( -
Loading applications...
- ); - } - - if (error) { - return ( -
- Error loading applications: {error.message} -
- ); - } - - if (data.length === 0) { - return
No applications found
; - } - - return ( -
- {data.map((app, index) => ( -
onUpdateClick(app)} - > - - -
{app.name}
-
-
- - {translations.code && - translations.code[lang as keyof LanguageTranslation]} - : - - {app.application_code} -
-
- - {translations.url && - translations.url[lang as keyof LanguageTranslation]} - : - - {app.site_url} -
-
- - {translations.type && - translations.type[lang as keyof LanguageTranslation]} - : - - {app.application_type} -
-
-
-
-
- ))} -
- ); -}; diff --git a/WebServices/management-frontend/src/components/Pages/application/FormComponent.tsx b/WebServices/management-frontend/src/components/Pages/application/FormComponent.tsx deleted file mode 100644 index dc71a54..0000000 --- a/WebServices/management-frontend/src/components/Pages/application/FormComponent.tsx +++ /dev/null @@ -1,359 +0,0 @@ -"use client"; -import React, { useState } from "react"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Input } from "@/components/ui/input"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Label } from "@/components/ui/label"; -import { Save, ArrowLeft } from "lucide-react"; -import { ApplicationData } from "./types"; -import { - ApplicationFormData, - ApplicationSchema, - CreateApplicationSchema, - UpdateApplicationSchema, - fieldDefinitions, - fieldsByMode, - FieldDefinition, -} from "./schema"; -import { getTranslation, LanguageKey } from "./language"; - -interface FormComponentProps { - initialData?: ApplicationData; - mode: "create" | "update"; - refetch: () => void; - setMode: (mode: "list" | "create" | "update") => void; - setSelectedItem: (item: ApplicationData | null) => void; - onCancel: () => void; - lang: string; -} - -interface ValidationErrors { - [key: string]: string[]; -} - -export const FormComponent: React.FC = ({ - initialData, - mode, - refetch, - setMode, - setSelectedItem, - onCancel, - lang, -}) => { - const t = getTranslation(lang as LanguageKey); - - // Convert initialData to ApplicationFormData with proper defaults - const getInitialFormData = (): ApplicationFormData => { - if (initialData) { - return { - ...initialData, - // Ensure required fields have defaults - active: initialData.active ?? true, - deleted: initialData.deleted ?? false, - }; - } else { - return { - name: "", - application_code: "", - site_url: "", - application_type: "", - application_for: "EMP", - description: "", - active: true, - deleted: false, - }; - } - }; - - const [formData, setFormData] = useState( - getInitialFormData() - ); - - const [validationErrors, setValidationErrors] = useState( - {} - ); - const [isSubmitting, setIsSubmitting] = useState(false); - - // Get field definitions for current mode - const currentFields = fieldsByMode[mode]; - const fieldDefs = fieldDefinitions.getDefinitionsByMode(mode); - - // Handle form input changes - const handleInputChange = (name: string, value: any) => { - setFormData({ - ...formData, - [name]: value, - }); - - // Clear validation error for this field when it changes - if (validationErrors[name]) { - const newErrors = { ...validationErrors }; - delete newErrors[name]; - setValidationErrors(newErrors); - } - }; - - // Validate form data based on mode - const validateForm = (): boolean => { - try { - if (mode === "create") { - CreateApplicationSchema.parse(formData); - } else if (mode === "update") { - UpdateApplicationSchema.parse(formData); - } else { - ApplicationSchema.parse(formData); - } - setValidationErrors({}); - return true; - } catch (error: any) { - if (error.errors) { - const errors: ValidationErrors = {}; - error.errors.forEach((err: any) => { - const field = err.path[0]; - if (!errors[field]) { - errors[field] = []; - } - errors[field].push(err.message); - }); - setValidationErrors(errors); - } - return false; - } - }; - - // Handle form submission - const handleSubmit = async () => { - if (isSubmitting) return; - - setIsSubmitting(true); - - // Validate form data - if (!validateForm()) { - setIsSubmitting(false); - return; - } - - try { - // TODO: Implement API call to save the data - // For now, just go back to list mode after a delay to simulate API call - await new Promise((resolve) => setTimeout(resolve, 500)); - refetch(); - setMode("list"); - } catch (error) { - console.error("Error saving application:", error); - } finally { - setIsSubmitting(false); - } - }; - - // Handle cancel button click - const handleCancel = () => { - setSelectedItem(null); - setMode("list"); - onCancel(); - }; - - const title = - mode === "create" - ? "Create Application" - : mode === "update" - ? "Update Application" - : "View Application"; - - // Render a field based on its definition - const renderField = (fieldName: string) => { - const fieldDef = fieldDefs[ - fieldName as keyof typeof fieldDefs - ] as FieldDefinition; - if (!fieldDef) return null; - - const hasError = !!validationErrors[fieldName]; - const errorMessages = validationErrors[fieldName] || []; - - return ( -
- - - {fieldDef.type === "text" && !fieldDef.readOnly && ( - <> - handleInputChange(fieldName, e.target.value)} - className={hasError ? "border-red-500" : ""} - /> - - )} - {fieldDef.type === "text" && fieldDef.readOnly && ( -

- {(formData[fieldName as keyof ApplicationFormData] as string) || ""} -

- )} - - {fieldDef.type === "textarea" && !fieldDef.readOnly && ( -