From 5c640ddceee7aa5bf0dc0750f0dd4adf22a871b7 Mon Sep 17 00:00:00 2001 From: Berkay Date: Wed, 25 Jun 2025 11:56:42 +0300 Subject: [PATCH] app: people and company @frontend added no test runned --- .../Identity/events/people/supers_events.py | 1 - .../customer/src/app/api/people/list/route.ts | 0 .../customer/src/app/api/user/route.ts | 0 .../custom/context/page/menu/fetch.tsx | 10 +- .../src/languages/mutual/menu/english.ts | 10 +- .../src/languages/mutual/menu/turkish.ts | 27 +- .../multi/buildParts/superuser/CreatePage.tsx | 86 ++--- .../customer/src/pages/multi/company/a.txt | 0 .../multi/company/superuser/CreatePage.tsx | 307 ++++++++++++++++++ .../multi/company/superuser/UpdatePage.tsx | 212 ++++++++++++ .../multi/company/superuser/main/ListPage.tsx | 142 ++++++++ .../multi/company/superuser/main/columns.tsx | 104 ++++++ .../company/superuser/main/dataTable.tsx | 129 ++++++++ .../main/mobilePaginationControls.tsx | 34 ++ .../company/superuser/main/tableForm.tsx | 152 +++++++++ .../company/superuser/main/tableHeader.tsx | 24 ++ .../multi/company/superuser/main/types.ts | 15 + .../pages/multi/company/superuser/schema.ts | 147 +++++++++ .../multi/company/superuser/translations.ts | 70 ++++ ServicesWeb/customer/src/pages/multi/index.ts | 7 +- .../customer/src/pages/multi/people/a.txt | 0 .../multi/people/superuser/CreatePage.tsx | 305 +++++++++++++++++ .../multi/people/superuser/UpdatePage.tsx | 212 ++++++++++++ .../multi/people/superuser/main/ListPage.tsx | 163 ++++++++++ .../multi/people/superuser/main/columns.tsx | 119 +++++++ .../multi/people/superuser/main/dataTable.tsx | 129 ++++++++ .../main/mobilePaginationControls.tsx | 34 ++ .../multi/people/superuser/main/tableForm.tsx | 152 +++++++++ .../people/superuser/main/tableHeader.tsx | 24 ++ .../multi/people/superuser/main/types.ts | 24 ++ .../pages/multi/people/superuser/schema.ts | 151 +++++++++ .../multi/people/superuser/translations.ts | 85 +++++ 32 files changed, 2794 insertions(+), 81 deletions(-) create mode 100644 ServicesWeb/customer/src/app/api/people/list/route.ts create mode 100644 ServicesWeb/customer/src/app/api/user/route.ts create mode 100644 ServicesWeb/customer/src/pages/multi/company/a.txt create mode 100644 ServicesWeb/customer/src/pages/multi/company/superuser/CreatePage.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/company/superuser/UpdatePage.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/company/superuser/main/ListPage.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/company/superuser/main/columns.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/company/superuser/main/dataTable.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/company/superuser/main/mobilePaginationControls.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/company/superuser/main/tableForm.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/company/superuser/main/tableHeader.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/company/superuser/main/types.ts create mode 100644 ServicesWeb/customer/src/pages/multi/company/superuser/schema.ts create mode 100644 ServicesWeb/customer/src/pages/multi/company/superuser/translations.ts create mode 100644 ServicesWeb/customer/src/pages/multi/people/a.txt create mode 100644 ServicesWeb/customer/src/pages/multi/people/superuser/CreatePage.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/people/superuser/UpdatePage.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/people/superuser/main/ListPage.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/people/superuser/main/columns.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/people/superuser/main/dataTable.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/people/superuser/main/mobilePaginationControls.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/people/superuser/main/tableForm.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/people/superuser/main/tableHeader.tsx create mode 100644 ServicesWeb/customer/src/pages/multi/people/superuser/main/types.ts create mode 100644 ServicesWeb/customer/src/pages/multi/people/superuser/schema.ts create mode 100644 ServicesWeb/customer/src/pages/multi/people/superuser/translations.ts diff --git a/ServicesApi/Builds/Identity/events/people/supers_events.py b/ServicesApi/Builds/Identity/events/people/supers_events.py index 0c08210..45ba35f 100644 --- a/ServicesApi/Builds/Identity/events/people/supers_events.py +++ b/ServicesApi/Builds/Identity/events/people/supers_events.py @@ -71,7 +71,6 @@ def super_people_list_callable(list_options: PaginateOnly, token: TokenDictType) people_list = People.query.filter(*People.convert(list_options.query)) else: people_list = People.query.filter() - pagination = Pagination(data=people_list) pagination.change(**list_options.model_dump()) pagination_result = PaginationResult(data=people_list, pagination=pagination) diff --git a/ServicesWeb/customer/src/app/api/people/list/route.ts b/ServicesWeb/customer/src/app/api/people/list/route.ts new file mode 100644 index 0000000..e69de29 diff --git a/ServicesWeb/customer/src/app/api/user/route.ts b/ServicesWeb/customer/src/app/api/user/route.ts new file mode 100644 index 0000000..e69de29 diff --git a/ServicesWeb/customer/src/fetchers/custom/context/page/menu/fetch.tsx b/ServicesWeb/customer/src/fetchers/custom/context/page/menu/fetch.tsx index 98a4185..bf85a35 100644 --- a/ServicesWeb/customer/src/fetchers/custom/context/page/menu/fetch.tsx +++ b/ServicesWeb/customer/src/fetchers/custom/context/page/menu/fetch.tsx @@ -11,11 +11,8 @@ const getMenuFromRedis = async (): Promise => { const decrpytUserSelection = await functionRetrieveUserSelection() const redisKey = decrpytUserSelection?.redisKey; if (!redisKey) throw new AuthError("No redis key found"); - // Use safe Redis get operation with proper connection handling const result = await safeRedisGet(`${redisKey}`, REDIS_TIMEOUT); if (!result) throw new AuthError("No data found in redis"); - - // Use safe JSON parsing with proper default object type const parsedResult = safeJsonParse(result, { menu: defaultValuesMenu }); if (!parsedResult.menu) throw new AuthError("No menu found in redis"); return parsedResult.menu; @@ -31,12 +28,7 @@ const setMenuToRedis = async (menuObject: string[]) => { if (!menuObject) throw new AuthError("No menu object provided"); const oldData = await getCompleteFromRedis(); if (!oldData) throw new AuthError("No old data found in redis"); - await setCompleteToRedis({ - ...oldData, menu: { - ...oldData.menu, - selectionList: menuObject, - } - }); + await setCompleteToRedis({ ...oldData, menu: { ...oldData.menu, selectionList: menuObject } }); return true; } catch (error) { if (error instanceof AuthError) { throw error } else { throw new AuthError(error instanceof Error ? error.message : "Unknown error") } } } diff --git a/ServicesWeb/customer/src/languages/mutual/menu/english.ts b/ServicesWeb/customer/src/languages/mutual/menu/english.ts index cf02116..96338f1 100644 --- a/ServicesWeb/customer/src/languages/mutual/menu/english.ts +++ b/ServicesWeb/customer/src/languages/mutual/menu/english.ts @@ -24,14 +24,14 @@ const menuTranslationEn = { { value: "Dashboard", key: "dashboard" }, { value: "Dashboard", key: "dashboard" }, ], - "/individual": [ - { value: "Individual", key: "individual" }, + "/people": [ { value: "Individual", key: "individual" }, { value: "Individual", key: "individual" }, + { value: "People", key: "people" }, ], "/user": [ - { value: "User", key: "user" }, - { value: "User", key: "user" }, + { value: "Individual", key: "individual" }, + { value: "Individual", key: "individual" }, { value: "User", key: "user" }, ], "/build": [ @@ -42,7 +42,7 @@ const menuTranslationEn = { "/build/parts": [ { value: "Build", key: "build" }, { value: "Parts", key: "parts" }, - { value: "Build", key: "build" }, + { value: "Build Parts", key: "build" }, ], "/management/budget/actions": [ { value: "Management", key: "management" }, diff --git a/ServicesWeb/customer/src/languages/mutual/menu/turkish.ts b/ServicesWeb/customer/src/languages/mutual/menu/turkish.ts index fd24178..08d8afd 100644 --- a/ServicesWeb/customer/src/languages/mutual/menu/turkish.ts +++ b/ServicesWeb/customer/src/languages/mutual/menu/turkish.ts @@ -1,22 +1,3 @@ -// const menuTranslationTr = { -// "/definitions/identifications/people": "Kişiler", -// "/definitions/identifications/users": "Kullanıcılar", - -// "/definitions/building/parts": "Daireler", -// "/definitions/building/areas": "Bina Alanları", - -// "/building/accounts/managment/accounts": "Bina Hesapları", -// "/building/accounts/managment/budgets": "Bina Bütçesi", -// "/building/accounts/parts/accounts": "Daire Hesapları", -// "/building/accounts/parts/budgets": "Daire Bütçesi", - -// "/building/meetings/regular/actions": "Düzenli Toplantı Eylemleri", -// "/building/meetings/regular/accounts": "Düzenli Toplantı Accounts", -// "/building/meetings/ergunt/actions": "Ergunt Toplantı Eylemleri", -// "/building/meetings/ergunt/accounts": "Ergunt Toplantı Accounts", -// "/building/meetings/invited/attendance": "Toplantı Davetli Katılımlar", -// }; - const menuTranslationTr = { // New menu "/dashboard": [ @@ -24,14 +5,14 @@ const menuTranslationTr = { { value: "Panel", key: "dashboard" }, { value: "Panel", key: "dashboard" }, ], - "/individual": [ - { value: "Bireysel", key: "individual" }, + "/people": [ { value: "Bireysel", key: "individual" }, { value: "Bireysel", key: "individual" }, + { value: "Birey", key: "people" }, ], "/user": [ - { value: "Kullanıcı", key: "user" }, - { value: "Kullanıcı", key: "user" }, + { value: "Bireysel", key: "individual" }, + { value: "Bireysel", key: "individual" }, { value: "Kullanıcı", key: "user" }, ], "/build": [ diff --git a/ServicesWeb/customer/src/pages/multi/buildParts/superuser/CreatePage.tsx b/ServicesWeb/customer/src/pages/multi/buildParts/superuser/CreatePage.tsx index 7f95166..bf7150f 100644 --- a/ServicesWeb/customer/src/pages/multi/buildParts/superuser/CreatePage.tsx +++ b/ServicesWeb/customer/src/pages/multi/buildParts/superuser/CreatePage.tsx @@ -237,54 +237,56 @@ function CreateFromComponentBase({ - const mainComponent =
- {/* Header with title and change button */} -
-

- {buildingTranslations[language].selectedBuilding} -

- -
- - {/* Building details grid */} -
- {/* UUID */} -
- UUID: - {selectedBuilding.uu_id} + const mainComponent = selectedBuilding ? ( +
+ {/* Header with title and change button */} +
+

+ {buildingTranslations[language].selectedBuilding} +

+
- {/* Building Name */} -
- {buildingTranslations[language].buildName}: - {selectedBuilding.build_name} -
+ {/* Building details grid */} +
+ {/* UUID */} +
+ UUID: + {selectedBuilding?.uu_id || '-'} +
- {/* Building Number */} -
- {buildingTranslations[language].buildNo}: - {selectedBuilding.build_no} -
+ {/* Building Name */} +
+ {buildingTranslations[language].buildName}: + {selectedBuilding?.build_name || '-'} +
- {/* Address Code */} -
- {buildingTranslations[language].buildAddressCode}: - {selectedBuilding.gov_address_code} -
+ {/* Building Number */} +
+ {buildingTranslations[language].buildNo}: + {selectedBuilding?.build_no || '-'} +
- {/* Max Floor */} -
- {buildingTranslations[language].buildMaxFloor}: - {selectedBuilding.max_floor} + {/* Address Code */} +
+ {buildingTranslations[language].buildAddressCode}: + {selectedBuilding?.gov_address_code || '-'} +
+ + {/* Max Floor */} +
+ {buildingTranslations[language].buildMaxFloor}: + {selectedBuilding?.max_floor || '-'} +
-
+ ) : null return (
@@ -302,4 +304,4 @@ function CreateFromComponentBase({ } export const CreateFromBuildComponent = withCache(CreateFromComponentBase); -export default withCache(CreateFromBuildComponent); +export default CreateFromBuildComponent; diff --git a/ServicesWeb/customer/src/pages/multi/company/a.txt b/ServicesWeb/customer/src/pages/multi/company/a.txt new file mode 100644 index 0000000..e69de29 diff --git a/ServicesWeb/customer/src/pages/multi/company/superuser/CreatePage.tsx b/ServicesWeb/customer/src/pages/multi/company/superuser/CreatePage.tsx new file mode 100644 index 0000000..bf7150f --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/company/superuser/CreatePage.tsx @@ -0,0 +1,307 @@ +import React, { useState, useEffect } from "react"; +import { apiPostFetcher } from "@/lib/fetcher"; +import { useRouter } from "next/navigation"; + +import { withCache } from "@/components/mutual/context/cache/withCache"; +import { Button } from "@/components/mutual/ui/button"; +import { Form } from "@/components/mutual/ui/form"; +import { getCacheData, setCacheData, clearCacheData } from "@/components/mutual/context/cache/context"; +import { X } from "lucide-react"; + +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { buildPartsSchemaCreate, createEmptyValues } from "./schema"; +import { buildPartsTranslations, translationsOfPage, buildingTranslations } from "./translations"; +import { LanguageTypes } from "@/validations/mutual/language/validations"; + +import { NumberInput } from "@/components/mutual/formInputs/NumberInput"; +import { StringInput } from "@/components/mutual/formInputs/StringInput"; +import { CheckBoxInput } from "@/components/mutual/formInputs/CheckBoxInput"; + +function CreateFromComponentBase({ + onlineData, + cacheData, + cacheLoading, + cacheError, + refreshCache, + updateCache, + clearCache, + activePageUrl +}: { + onlineData?: any; + cacheData?: { [url: string]: any } | null; + cacheLoading?: boolean; + cacheError?: string | null; + refreshCache?: (url?: string) => Promise; + updateCache?: (url: string, data: any) => Promise; + clearCache?: (url: string) => Promise; + activePageUrl: string; +}) { + const language: LanguageTypes = onlineData?.lang || 'en'; + const router = useRouter(); + const listUrl = `/panel/${activePageUrl?.replace("/create", "")}`; + const [selectedBuilding, setSelectedBuilding] = useState(null); + const [formError, setFormError] = useState(""); + const [cacheLoaded, setCacheLoaded] = useState(false); + const form = useForm({ resolver: zodResolver(buildPartsSchemaCreate), defaultValues: createEmptyValues }); + + useEffect(() => { + const checkCacheForSelectedBuild = async () => { + try { + const cachedData = await getCacheData("/build/list"); + if (cachedData && cachedData.build) { + setSelectedBuilding(cachedData.build); + const formValues = form.getValues(); + form.setValue("build_uu_id", cachedData.build.uu_id); + } + } catch (error) { console.error("Error checking cache for selected build:", error) } + }; + checkCacheForSelectedBuild(); + }, [form]); + + useEffect(() => { + const fetchCacheDirectly = async () => { + if (!cacheLoaded) { + try { + const cachedData = await getCacheData(activePageUrl); + if (cachedData) { + const formValues = form.getValues(); + const mergedData = { + build_uu_id: cachedData.build_uu_id || formValues.build_uu_id || null, + address_gov_code: cachedData.address_gov_code || formValues.address_gov_code || "", + part_no: cachedData.part_no || formValues.part_no || 0, + part_level: cachedData.part_level || formValues.part_level || 0, + part_code: cachedData.part_code || formValues.part_code || "", + part_gross_size: cachedData.part_gross_size || formValues.part_gross_size || 0, + part_net_size: cachedData.part_net_size || formValues.part_net_size || 0, + default_accessory: cachedData.default_accessory || formValues.default_accessory || "0", + human_livable: cachedData.human_livable || formValues.human_livable || true, + due_part_key: cachedData.due_part_key || formValues.due_part_key || "", + part_direction_uu_id: cachedData.part_direction_uu_id || formValues.part_direction_uu_id || null, + }; + form.reset(mergedData); + } + setCacheLoaded(true); + if (refreshCache) { refreshCache(activePageUrl) } + } catch (error) { console.error("Error fetching cache directly:", error) } + } + }; + fetchCacheDirectly(); + }, []); + + const handleFieldBlur = async (fieldName: string, value: any) => { + if (value) { + try { + const currentValues = form.getValues(); + const updatedData = { ...currentValues, [fieldName]: value }; + await setCacheData(activePageUrl, updatedData); + if (updateCache) { updateCache(activePageUrl, updatedData) } + } catch (error) { console.error("Error updating cache:", error) } + } + }; + + const onSubmit = async (data: any) => { + setFormError(""); + if (!data.build_uu_id && selectedBuilding) { data.build_uu_id = selectedBuilding.uu_id } + if (!data.build_uu_id) { + const errorMessage = language === 'en' ? "Missing building ID. Please select a building first." : "Bina ID'si eksik. Lütfen önce bir bina seçin."; + console.error('errorMessage', errorMessage); + setFormError(errorMessage); + return; + } + console.log("Form submitted with data:", data); + try { + const response = await apiPostFetcher({ url: `/api/parts/create`, isNoCache: true, body: data }); + await clearCacheData(activePageUrl); + if (clearCache) { clearCache(activePageUrl) } + if (response.success) { form.reset(createEmptyValues); router.push(listUrl) } + } catch (error) { + console.error("Error submitting form:", error); + setFormError(language === 'en' ? "Error submitting form" : "Form gönderilirken hata oluştu"); + } + } + + const formComponent = <> +
+ + {/* ----- Basic Information ----- */} + {/* Address Government Code */} + + + {/* ----- Part Identification ----- */} + {/* Part Number */} + + + {/* Part Level */} + + + {/* Part Code */} + + + {/* ----- Size Information ----- */} + {/* Part Gross Size */} + + + {/* Part Net Size */} + + + {/* ----- Additional Properties ----- */} + {/* Default Accessory */} + + + {/* Human Livable */} + + + {/* Due Part Key */} + + + {/* Part Direction UUID */} + + + {/* ----- Submit Button ----- */} + + + + + + const noSelectedBuildingComponent =
+
+ {buildingTranslations[language].noSelectedBuilding} +
+ +
+ + const mainComponent = selectedBuilding ? ( +
+ {/* Header with title and change button */} +
+

+ {buildingTranslations[language].selectedBuilding} +

+ +
+ + {/* Building details grid */} +
+ {/* UUID */} +
+ UUID: + {selectedBuilding?.uu_id || '-'} +
+ + {/* Building Name */} +
+ {buildingTranslations[language].buildName}: + {selectedBuilding?.build_name || '-'} +
+ + {/* Building Number */} +
+ {buildingTranslations[language].buildNo}: + {selectedBuilding?.build_no || '-'} +
+ + {/* Address Code */} +
+ {buildingTranslations[language].buildAddressCode}: + {selectedBuilding?.gov_address_code || '-'} +
+ + {/* Max Floor */} +
+ {buildingTranslations[language].buildMaxFloor}: + {selectedBuilding?.max_floor || '-'} +
+
+
+ ) : null + + return ( +
+ {/* back to list button */} +
+ +
+

{translationsOfPage[language].title}

+ {/* ===== BUILDING SELECTION SECTION ===== */} + {selectedBuilding ? mainComponent : noSelectedBuildingComponent} + {/* ===== FORM SECTION ===== */} + {selectedBuilding && formComponent} +
+ ); +} + +export const CreateFromBuildComponent = withCache(CreateFromComponentBase); +export default CreateFromBuildComponent; diff --git a/ServicesWeb/customer/src/pages/multi/company/superuser/UpdatePage.tsx b/ServicesWeb/customer/src/pages/multi/company/superuser/UpdatePage.tsx new file mode 100644 index 0000000..9615018 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/company/superuser/UpdatePage.tsx @@ -0,0 +1,212 @@ +import React, { useState, useEffect } from "react"; +import { apiPostFetcher } from "@/lib/fetcher"; +import { useRouter } from "next/navigation"; + +import { withCache } from "@/components/mutual/context/cache/withCache"; +import { Button } from "@/components/mutual/ui/button"; +import { Form } from "@/components/mutual/ui/form"; +import { getCacheData, setCacheData, clearCacheData } from "@/components/mutual/context/cache/context"; + +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { LanguageTypes } from "@/validations/mutual/language/validations"; + +import { buildPartsSchema, updateEmptyValues } from "./schema"; +import { buildPartsTranslations, translationsOfPage } from "./translations"; + +import { CheckBoxInput } from "@/components/mutual/formInputs/CheckBoxInput"; +import { NumberInput } from "@/components/mutual/formInputs/NumberInput"; +import { StringInput } from "@/components/mutual/formInputs/StringInput"; + +function UpdateFromComponentBase({ + onlineData, + cacheData, + cacheLoading, + cacheError, + refreshCache, + updateCache, + clearCache, + activePageUrl +}: { + onlineData?: any; + cacheData?: { [url: string]: any } | null; + cacheLoading?: boolean; + cacheError?: string | null; + refreshCache?: (url?: string) => Promise; + updateCache?: (url: string, data: any) => Promise; + clearCache?: (url: string) => Promise; + activePageUrl: string; +}) { + const language: LanguageTypes = onlineData?.lang || 'en'; + const [cacheLoaded, setCacheLoaded] = useState(false); + const [partUUID, setPartUUID] = useState(""); const router = useRouter(); + const listUrl = `/panel/${activePageUrl?.replace("/update", "")}`; + + const form = useForm({ resolver: zodResolver(buildPartsSchema), mode: "onSubmit", defaultValues: updateEmptyValues }); + + useEffect(() => { + const fetchData = async () => { + if (!cacheLoaded) { + try { + const cachedData = await getCacheData(activePageUrl); + if (cachedData) { + const formValues = form.getValues(); + const mergedData = { + uu_id: "", + address_gov_code: cachedData.address_gov_code || formValues.address_gov_code || "", + part_no: cachedData.part_no ?? formValues.part_no ?? 0, + part_level: cachedData.part_level ?? formValues.part_level ?? 0, + part_code: cachedData.part_code || formValues.part_code || "", + part_gross_size: cachedData.part_gross_size ?? formValues.part_gross_size ?? 0, + part_net_size: cachedData.part_net_size ?? formValues.part_net_size ?? 0, + default_accessory: cachedData.default_accessory || formValues.default_accessory || "0", + human_livable: cachedData.human_livable ?? formValues.human_livable ?? true, + due_part_key: cachedData.due_part_key || formValues.due_part_key || "", + part_direction_uu_id: cachedData.part_direction_uu_id ?? formValues.part_direction_uu_id ?? null + }; + form.reset(mergedData); + setPartUUID(cachedData.uu_id); + } + setCacheLoaded(true); + if (refreshCache) { refreshCache(activePageUrl); } + } catch (error) { console.error("Error fetching cache:", error); } + } + }; + fetchData(); + }, []); + + const handleFieldBlur = async (fieldName: string, value: any) => { + if (value !== undefined) { + try { + const currentValues = form.getValues(); + const updatedData = { ...currentValues, [fieldName]: value }; + await setCacheData(activePageUrl, updatedData); + if (updateCache) { updateCache(activePageUrl, updatedData); } + } catch (error) { console.error("Error updating cache:", error); } + } + }; + + const onSubmit = async (data: any) => { + try { + console.log('Form data received:', data); + const formattedData = { ...data, uuid: partUUID }; + const response = await apiPostFetcher({ + url: `/api/parts/update/${partUUID}`, + isNoCache: true, + body: formattedData, + }); + await clearCacheData(activePageUrl); + if (clearCache) { clearCache(activePageUrl); } + if (response.success) { form.reset(updateEmptyValues); router.push(listUrl) } + } catch (error) { console.error("Error submitting form:", error); } + } + + return ( +
+ {/* back to list button */} +
+ +
+

{translationsOfPage[language].title}

+

UUID: {partUUID}

+ +
+ + {/* Address Government Code */} + + + {/* Part Number */} + + + {/* Part Level */} + + + {/* Part Code */} + + + {/* Part Gross Size */} + + + {/* Part Net Size */} + + + {/* Default Accessory */} + + + {/* Human Livable */} + + + {/* Due Part Key */} + + + {/* Part Direction UUID */} + + + + + +
+ ); +} + +export const UpdateFromComponent = withCache(UpdateFromComponentBase); +export default withCache(UpdateFromComponentBase); diff --git a/ServicesWeb/customer/src/pages/multi/company/superuser/main/ListPage.tsx b/ServicesWeb/customer/src/pages/multi/company/superuser/main/ListPage.tsx new file mode 100644 index 0000000..c557991 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/company/superuser/main/ListPage.tsx @@ -0,0 +1,142 @@ +'use client'; +import React from "react"; +import LoadingContent from "@/components/mutual/loader/component"; + +import { useRouter } from "next/navigation"; +import { useReactTable, getCoreRowModel, getSortedRowModel, getPaginationRowModel, flexRender, ColumnDef } from "@tanstack/react-table"; +import { Button } from "@/components/mutual/ui/button"; +import { getCacheData } from "@/components/mutual/context/cache/context"; + +import { API_BASE_URL } from "@/config/config"; +import { useTableData } from "@/hooks/useTableData"; +import { LanguageTypes } from "@/validations/mutual/language/validations"; +import { DashboardPageProps } from "@/validations/mutual/table/validations"; + +import { BuildPartsSchemaInterface, listTranslations } from "../schema"; + +import DataTable from "./dataTable"; +import TableForm from "./tableForm"; +import MobilePaginationControls from "./mobilePaginationControls"; +import TableHeader from "./tableHeader"; +import { columnHelper, retrieveColumns } from "./columns"; + +const CompanyListPage: React.FC = React.memo((props) => { + const router = useRouter(); + const activePageUrl = props.activePageUrl || ''; + const language = props.onlineData?.lang as LanguageTypes || 'en'; + const t = listTranslations[language]; + const apiUrl = `${API_BASE_URL}/parts/list`; + + const [selectedBuilding, setSelectedBuilding] = React.useState(null); + + const { + tableData, isLoading, error, form, pagination, apiPagination, setSorting, handleSortingChange, handleSelectChange, + handleFirstPage, handlePreviousPage, handleNextPage, getSortingIcon, handleFormSubmit, + isPreviousDisabled, isNextDisabled, pageSizeOptions, renderPageOptions, sorting, // Disabled states + } = useTableData({ + apiUrl, + mapFormToRequestBody: (values) => { + const requestBody = { + page: values.page, size: values.size, orderField: values.orderField, orderType: values.orderType, + query: selectedBuilding ? { build_uu_id: selectedBuilding.uu_id } : {} + }; return requestBody; + } + }); + + React.useEffect(() => { + const checkCacheForSelectedBuild = async () => { + try { + const cachedData = await getCacheData("/build/list"); + if (cachedData && cachedData.build) { setSelectedBuilding(cachedData.build) } + } catch (error) { console.error("Error checking cache for selected build:", error) } + }; + checkCacheForSelectedBuild(); + }, []); + + React.useEffect(() => { + if (selectedBuilding) { form.setValue("page", 1); form.setValue("query", { build_uu_id: selectedBuilding.uu_id }) } + }, [selectedBuilding, form]); + + + const columnsArray = retrieveColumns(language, activePageUrl, router); + const columns = React.useMemo(() => columnsArray, [columnHelper]) as ColumnDef[]; + + const table = useReactTable({ + data: tableData, + columns, + state: { sorting, pagination }, + onSortingChange: setSorting, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + getPaginationRowModel: getPaginationRowModel(), + manualPagination: true, + pageCount: apiPagination.totalPages || 1, + }) + + const dataTableComponent = + + const noSelectedBuildingComponent =
+
{t.noSelectedBuilding || "No building selected. Please select a building first."}
+ +
+ + return ( + isLoading ? : +
+ +
+ + +
+ + + + {/* Mobile pagination controls - only visible on small screens */} + + + {selectedBuilding ? dataTableComponent : noSelectedBuildingComponent} +
+ ); +}); + +export default CompanyListPage; diff --git a/ServicesWeb/customer/src/pages/multi/company/superuser/main/columns.tsx b/ServicesWeb/customer/src/pages/multi/company/superuser/main/columns.tsx new file mode 100644 index 0000000..c91dd7b --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/company/superuser/main/columns.tsx @@ -0,0 +1,104 @@ +import { + useReactTable, + getCoreRowModel, + getSortedRowModel, + getPaginationRowModel, + flexRender, + createColumnHelper, + ColumnDef, +} from "@tanstack/react-table"; +import { BuildPartsSchemaInterface } from "../schema"; +import { buildPartsTranslations, translationsOfPage } from '../translations'; +import { LanguageTypes } from "@/validations/mutual/language/validations"; +import { Button } from "@/components/mutual/ui/button"; +import { Pencil } from "lucide-react"; +import { setCacheData } from "@/components/mutual/context/cache/context"; + +const columnHelper = createColumnHelper(); + +function retrieveColumns(language: LanguageTypes, activePageUrl: string, router: any) { + const handleEditRow = async (row: BuildPartsSchemaInterface) => { + try { + await setCacheData(`${activePageUrl}/update`, row); + router.push(`/panel/${activePageUrl}/update`); + } catch (error) { console.error('Error storing row data in cache:', error) } + }; + const actionButtonGroup = (info: any) => ( + + ); + const columnsArray = [ + columnHelper.display({ + id: 'actions', + header: () => {translationsOfPage[language].actionButtonGroup}, + cell: (info) => { return (actionButtonGroup(info)) } + }), + columnHelper.accessor('address_gov_code', { + cell: info => info.getValue(), + header: () => {buildPartsTranslations[language].address_gov_code}, + footer: info => info.column.id + }), + columnHelper.accessor('part_no', { + cell: info => String(info.getValue()), + header: () => {buildPartsTranslations[language].part_no}, + footer: info => info.column.id + }), + columnHelper.accessor('part_level', { + cell: info => String(info.getValue()), + header: () => {buildPartsTranslations[language].part_level}, + footer: info => info.column.id + }), + columnHelper.accessor('part_code', { + cell: info => info.getValue(), + header: () => {buildPartsTranslations[language].part_code}, + footer: info => info.column.id + }), + columnHelper.accessor('part_gross_size', { + cell: info => String(info.getValue()), + header: () => {buildPartsTranslations[language].part_gross_size}, + footer: info => info.column.id + }), + columnHelper.accessor('part_net_size', { + cell: info => String(info.getValue()), + header: () => {buildPartsTranslations[language].part_net_size}, + footer: info => info.column.id + }), + columnHelper.accessor('default_accessory', { + cell: info => info.getValue(), + header: () => {buildPartsTranslations[language].default_accessory}, + footer: info => info.column.id + }), + columnHelper.accessor('human_livable', { + cell: info => info.getValue() ? 'Yes' : 'No', + header: () => {buildPartsTranslations[language].human_livable}, + footer: info => info.column.id + }), + columnHelper.accessor('due_part_key', { + cell: info => info.getValue(), + header: () => {buildPartsTranslations[language].due_part_key}, + footer: info => info.column.id + }), + columnHelper.accessor('part_direction_uu_id', { + cell: info => info.getValue() || '', + header: () => {buildPartsTranslations[language].part_direction_uu_id}, + footer: info => info.column.id + }), + ] + return columnsArray; +} + +export { + retrieveColumns, + columnHelper +} diff --git a/ServicesWeb/customer/src/pages/multi/company/superuser/main/dataTable.tsx b/ServicesWeb/customer/src/pages/multi/company/superuser/main/dataTable.tsx new file mode 100644 index 0000000..ed26ef0 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/company/superuser/main/dataTable.tsx @@ -0,0 +1,129 @@ +'use client'; + +import React from "react"; +import { Button } from "@/components/mutual/ui/button"; +import { X } from "lucide-react"; +import { useReactTable } from "@tanstack/react-table"; +import { flexRender } from "@tanstack/react-table"; +import { BuildPartsSchemaInterface } from "../schema"; +import { Translations } from "./types"; + +interface DataTableProps { + table: ReturnType; + tableData: BuildPartsSchemaInterface[]; + isLoading: boolean; + handleSortingChange: (columnId: string) => void; + getSortingIcon: (columnId: string) => React.ReactNode; + flexRender: typeof flexRender; + t: Translations; + selectedBuilding: any | null; + router: any; +} + +const DataTable: React.FC = React.memo(({ + table, + tableData, + isLoading, + handleSortingChange, + getSortingIcon, + flexRender, + t, + selectedBuilding, + router, +}) => { + return ( +
+ {/* Semi-transparent loading overlay that preserves interactivity */} + {isLoading && ( +
+ {/* We don't put anything here as we already have the loading indicator in the header */} +
+ )} + {selectedBuilding && ( +
+
+

{t.selectedBuilding}

+ +
+
+
+ UUID: + {selectedBuilding.uu_id} +
+
+ {t.buildName}: + {selectedBuilding.build_name} +
+
+ {t.buildNo}: + {selectedBuilding.build_no} +
+
+ {t.buildAddressCode}: + {selectedBuilding.gov_address_code} +
+
+ {t.buildMaxFloor}: + {selectedBuilding.max_floor} +
+
+
+ )} + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + ); + })} + + ))} + + + {tableData.length === 0 && !isLoading ? ( + + + + ) : ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => { + return ( + + ); + })} + + )) + )} + +
{t.dataTable}
handleSortingChange(header.column.id)} + aria-sort={header.column.getIsSorted() ? (header.column.getIsSorted() === 'desc' ? 'descending' : 'ascending') : undefined} + scope="col" + > +
+ {flexRender(header.column.columnDef.header, header.getContext())} + {getSortingIcon(header.column.id)} +
+
+ {t.noDataAvailable} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+
+ ); +}); + + +export default DataTable; diff --git a/ServicesWeb/customer/src/pages/multi/company/superuser/main/mobilePaginationControls.tsx b/ServicesWeb/customer/src/pages/multi/company/superuser/main/mobilePaginationControls.tsx new file mode 100644 index 0000000..aabe650 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/company/superuser/main/mobilePaginationControls.tsx @@ -0,0 +1,34 @@ +import { MobilePaginationControlsProps } from "@/validations/mutual/table/validations"; +import { Button } from "@/components/mutual/ui/button"; + +const MobilePaginationControls: React.FC = ({ + handlePreviousPage, + handleNextPage, + isPreviousDisabled, + isNextDisabled, + t +}) => { + return ( +
+
+ + +
+
+ ); +}; + +export default MobilePaginationControls; diff --git a/ServicesWeb/customer/src/pages/multi/company/superuser/main/tableForm.tsx b/ServicesWeb/customer/src/pages/multi/company/superuser/main/tableForm.tsx new file mode 100644 index 0000000..d13c4cc --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/company/superuser/main/tableForm.tsx @@ -0,0 +1,152 @@ +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@/components/mutual/ui/form"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/mutual/ui/select"; +import { UseFormReturn } from "react-hook-form"; +import { Translations } from "./types"; +import { Button } from "@/components/mutual/ui/button"; + +interface TableFormProps { + form: UseFormReturn; + handleFormSubmit: (e: React.FormEvent) => void; + handleSelectChange: (value: string, field: { onChange: (value: number) => void }) => void; + renderPageOptions: () => { key: string | number; value: string; label: string }[]; + pageSizeOptions: number[]; + apiPagination: { + size: number; + page: number; + totalCount: number; + totalPages: number; + pageCount: number; + }; + handleFirstPage: () => void; + handlePreviousPage: () => void; + handleNextPage: () => void; + isPreviousDisabled: () => boolean; + isNextDisabled: () => boolean; + t: Translations; +} + +const TableForm: React.FC = ({ + form, + handleFormSubmit, + handleSelectChange, + renderPageOptions, + pageSizeOptions, + apiPagination, + handleFirstPage, + handlePreviousPage, + handleNextPage, + isPreviousDisabled, + isNextDisabled, + t +}) => { + return ( +
+
+ +
+ ( + + {t.page} + + + + )} + /> +
+
+ ( + + {t.size} + + + + )} + /> +
+
+

+ {t.page}: {apiPagination.page}{" / "} {apiPagination.totalPages} · + {t.size}: {apiPagination.pageCount}{" / "} {apiPagination.size} · + {t.total}: {apiPagination.totalCount} {t.items} +

+
+
+ + + + {/* */} +
+
+ +
+ ); +}; + +export default TableForm; diff --git a/ServicesWeb/customer/src/pages/multi/company/superuser/main/tableHeader.tsx b/ServicesWeb/customer/src/pages/multi/company/superuser/main/tableHeader.tsx new file mode 100644 index 0000000..a53616a --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/company/superuser/main/tableHeader.tsx @@ -0,0 +1,24 @@ +import { TableHeaderProps, ErrorDisplayProps } from "@/validations/mutual/table/validations"; + +const ErrorDisplay: React.FC = ({ message }) => { + return
{message}
; +}; + + +const TableHeader: React.FC = ({ title, description, isLoading, error, t }) => { + return ( + +
+
+
+

{title}

+

{description}

+
+ {/* {isLoading && } */} + {error && } +
+
+ ); +}; + +export default TableHeader; diff --git a/ServicesWeb/customer/src/pages/multi/company/superuser/main/types.ts b/ServicesWeb/customer/src/pages/multi/company/superuser/main/types.ts new file mode 100644 index 0000000..84671e2 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/company/superuser/main/types.ts @@ -0,0 +1,15 @@ +import { Translations as BaseTranslations } from "@/validations/mutual/table/validations"; + +// Extended Translations interface to include building-related translations +interface Translations extends BaseTranslations { + selectedBuilding: string; + buildName: string; + buildNo: string; + buildAddressCode: string; + buildMaxFloor: string; + noSelectedBuilding: string; + selectBuilding: string; + changeBuilding: string; +} + +export type { Translations }; diff --git a/ServicesWeb/customer/src/pages/multi/company/superuser/schema.ts b/ServicesWeb/customer/src/pages/multi/company/superuser/schema.ts new file mode 100644 index 0000000..aa45103 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/company/superuser/schema.ts @@ -0,0 +1,147 @@ +import { LanguageTypes } from "@/validations/mutual/language/validations"; +import * as z from "zod"; + +/** + * Zod schema for BuildParts + * Corresponds to the BuildParts class in Python + */ +const buildPartsSchema = z.object({ + uu_id: z.string(), + build_uu_id: z.string().nullable(), + address_gov_code: z.string().describe("Goverment Door Code"), + part_no: z.number().int().describe("Part Number"), + part_level: z.number().int().describe("Building Part Level"), + part_code: z.string().describe("Part Code"), + part_gross_size: z.number().int().describe("Part Gross Size"), + part_net_size: z.number().int().describe("Part Net Size"), + default_accessory: z.string().describe("Default Accessory"), + human_livable: z.boolean().describe("Human Livable"), + due_part_key: z.string().describe("Constant Payment Group"), + part_direction_uu_id: z.string().nullable().describe("Part Direction UUID"), +}); + +const buildPartsSchemaCreate = buildPartsSchema.omit({ uu_id: true }).extend({ + part_no: z.number().int().default(0), + part_level: z.number().int().default(0), + part_code: z.string().default(""), + part_gross_size: z.number().int().default(0), + part_net_size: z.number().int().default(0), + default_accessory: z.string().default("0"), + human_livable: z.boolean().default(true), + due_part_key: z.string().default(""), +}); + +type BuildPartsInterface = z.infer; +type BuildPartsCreateInterface = z.infer; + +interface BuildPartsSchemaInterface { + uu_id: string; + build_uu_id: string | null; + address_gov_code: string; + part_no: number; + part_level: number; + part_code: string; + part_gross_size: number; + part_net_size: number; + default_accessory: string; + human_livable: boolean; + due_part_key: string; + part_direction_uu_id: string | null; +} + +const createEmptyValues: BuildPartsCreateInterface = { + build_uu_id: null, + address_gov_code: "", + part_no: 0, + part_level: 0, + part_code: "", + part_gross_size: 0, + part_net_size: 0, + default_accessory: "0", + human_livable: true, + due_part_key: "", + part_direction_uu_id: null, +}; + +const updateEmptyValues: BuildPartsInterface = { + uu_id: "", + build_uu_id: null, + address_gov_code: "", + part_no: 0, + part_level: 0, + part_code: "", + part_gross_size: 0, + part_net_size: 0, + default_accessory: "0", + human_livable: true, + due_part_key: "", + part_direction_uu_id: null, +}; + +const listTranslations: Record = { + en: { + createNew: "Create New", + createNewBuildPart: "Create New Build Part", + dataTable: "Data Table", + tableWithApiData: "Table with API Data", + loading: "Loading...", + noDataAvailable: "No data available", + page: "Page", + size: "Size", + total: "Total", + items: "items", + first: "First", + previous: "Previous", + next: "Next", + selectPage: "Select page", + selectSize: "Select size", + actionButtonGroup: "Actions", + selectedBuilding: "Selected Building", + buildName: "Building Name", + buildNo: "Building No", + buildAddressCode: "Building Address Code", + buildMaxFloor: "Building Max Floor", + noSelectedBuilding: "No building selected. Please select a building first.", + selectBuilding: "Select Building", + changeBuilding: "Change Building", + }, + tr: { + createNew: "Yeni Oluştur", + createNewBuildPart: "Yeni Bina Parçası Oluştur", + dataTable: "Veri Tablosu", + tableWithApiData: "API Verili Tablo", + loading: "Yükleniyor...", + noDataAvailable: "Veri bulunamadı", + page: "Sayfa", + size: "Boyut", + total: "Toplam", + items: "öğe", + first: "İlk", + previous: "Önceki", + next: "Sonraki", + selectPage: "Sayfa seç", + selectSize: "Boyut seç", + actionButtonGroup: "Eylemler", + selectedBuilding: "Seçilen Bina", + buildName: "Bina Adı", + buildNo: "Bina No", + buildAddressCode: "Bina Adres Kodu", + buildMaxFloor: "Bina Max Kat", + noSelectedBuilding: "Bina seçilmedi. Lütfen önce bir bina seçin.", + selectBuilding: "Bina Seç", + changeBuilding: "Bina Değiştir", + }, +}; + +export type { + BuildPartsInterface, + BuildPartsSchemaInterface, + BuildPartsCreateInterface, +}; +export { + buildPartsSchema, + buildPartsSchemaCreate, + createEmptyValues, + updateEmptyValues, + listTranslations, +}; diff --git a/ServicesWeb/customer/src/pages/multi/company/superuser/translations.ts b/ServicesWeb/customer/src/pages/multi/company/superuser/translations.ts new file mode 100644 index 0000000..dc69127 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/company/superuser/translations.ts @@ -0,0 +1,70 @@ +const buildPartsTranslations = { + tr: { + uu_id: "UUID", + build_uu_id: "Bina UUID", + address_gov_code: "Devlet Adres Kodu", + part_no: "Daire Numarası", + part_level: "Daire Katı", + part_code: "Daire Kodu", + part_gross_size: "Daire Brüt Boyutu", + part_net_size: "Daire Net Boyutu", + default_accessory: "Varsayılan Aksesuar", + human_livable: "İnsan Yaşanabilir", + due_part_key: "Sabit Ödeme Grubu", + part_direction_uu_id: "Daire Yön UUID", + }, + en: { + uu_id: "UUID", + build_uu_id: "Building UUID", + address_gov_code: "Government Door Code", + part_no: "Part Number", + part_level: "Building Part Level", + part_code: "Part Code", + part_gross_size: "Part Gross Size", + part_net_size: "Part Net Size", + default_accessory: "Default Accessory", + human_livable: "Human Livable", + due_part_key: "Constant Payment Group", + part_direction_uu_id: "Part Direction UUID", + }, +}; + +const translationsOfPage = { + tr: { + title: "Binaya Daire Oluştur", + updateTitle: "Binanın Dairesini Güncelle", + back2List: "Listeye Geri Dön", + actionButtonGroup: "İşlemler", + }, + en: { + title: "Create Building Part", + updateTitle: "Update Building Part", + back2List: "Back to List", + actionButtonGroup: "Actions", + }, +}; + +const buildingTranslations = { + en: { + selectedBuilding: "Selected Building", + buildName: "Building Name", + buildNo: "Building No", + buildAddressCode: "Address Code", + buildMaxFloor: "Max Floor", + noSelectedBuilding: "No building selected. Please select a building first.", + selectBuilding: "Select Building", + changeBuilding: "Change Building", + }, + tr: { + selectedBuilding: "Seçili Bina", + buildName: "Bina Adı", + buildNo: "Bina No", + buildAddressCode: "Adres Kodu", + buildMaxFloor: "Maksimum Kat", + noSelectedBuilding: "Bina seçilmedi. Lütfen önce bir bina seçin.", + selectBuilding: "Bina Seç", + changeBuilding: "Binayı Değiştir", + }, +}; + +export { buildPartsTranslations, translationsOfPage, buildingTranslations }; diff --git a/ServicesWeb/customer/src/pages/multi/index.ts b/ServicesWeb/customer/src/pages/multi/index.ts index 4b77f75..8d472f1 100644 --- a/ServicesWeb/customer/src/pages/multi/index.ts +++ b/ServicesWeb/customer/src/pages/multi/index.ts @@ -7,6 +7,8 @@ import UpdateFromBuildComponent from "./builds/superuser/UpdatePage"; import BuildPartsListPage from "./buildParts/superuser/main/ListPage"; import CreateFromBuildPartsComponent from "./buildParts/superuser/CreatePage"; import UpdateFromBuildPartsComponent from "./buildParts/superuser/UpdatePage"; +import PeopleListPage from "./people/superuser/main/ListPage"; +import CompanyListPage from "./company/superuser/main/ListPage"; const pageIndexMulti: Record>> = { "/dashboard": { DashboardPage: TableCardComponentImproved }, @@ -16,9 +18,12 @@ const pageIndexMulti: Record>> = { "/build/parts": { DashboardPage: BuildPartsListPage }, "/build/parts/create": { DashboardPage: CreateFromBuildPartsComponent }, "/build/parts/update": { DashboardPage: UpdateFromBuildPartsComponent }, - "/people": { DashboardPage: DPage }, + "/people": { DashboardPage: PeopleListPage }, "/people/create": { DashboardPage: DPage }, "/people/update": { DashboardPage: DPage }, + "/company": { DashboardPage: CompanyListPage }, + "/company/create": { DashboardPage: DPage }, + "/company/update": { DashboardPage: DPage }, "/decision/book": { DashboardPage: DPage }, "/decision/book/create": { DashboardPage: DPage }, "/decision/book/update": { DashboardPage: DPage }, diff --git a/ServicesWeb/customer/src/pages/multi/people/a.txt b/ServicesWeb/customer/src/pages/multi/people/a.txt new file mode 100644 index 0000000..e69de29 diff --git a/ServicesWeb/customer/src/pages/multi/people/superuser/CreatePage.tsx b/ServicesWeb/customer/src/pages/multi/people/superuser/CreatePage.tsx new file mode 100644 index 0000000..7f95166 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/people/superuser/CreatePage.tsx @@ -0,0 +1,305 @@ +import React, { useState, useEffect } from "react"; +import { apiPostFetcher } from "@/lib/fetcher"; +import { useRouter } from "next/navigation"; + +import { withCache } from "@/components/mutual/context/cache/withCache"; +import { Button } from "@/components/mutual/ui/button"; +import { Form } from "@/components/mutual/ui/form"; +import { getCacheData, setCacheData, clearCacheData } from "@/components/mutual/context/cache/context"; +import { X } from "lucide-react"; + +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { buildPartsSchemaCreate, createEmptyValues } from "./schema"; +import { buildPartsTranslations, translationsOfPage, buildingTranslations } from "./translations"; +import { LanguageTypes } from "@/validations/mutual/language/validations"; + +import { NumberInput } from "@/components/mutual/formInputs/NumberInput"; +import { StringInput } from "@/components/mutual/formInputs/StringInput"; +import { CheckBoxInput } from "@/components/mutual/formInputs/CheckBoxInput"; + +function CreateFromComponentBase({ + onlineData, + cacheData, + cacheLoading, + cacheError, + refreshCache, + updateCache, + clearCache, + activePageUrl +}: { + onlineData?: any; + cacheData?: { [url: string]: any } | null; + cacheLoading?: boolean; + cacheError?: string | null; + refreshCache?: (url?: string) => Promise; + updateCache?: (url: string, data: any) => Promise; + clearCache?: (url: string) => Promise; + activePageUrl: string; +}) { + const language: LanguageTypes = onlineData?.lang || 'en'; + const router = useRouter(); + const listUrl = `/panel/${activePageUrl?.replace("/create", "")}`; + const [selectedBuilding, setSelectedBuilding] = useState(null); + const [formError, setFormError] = useState(""); + const [cacheLoaded, setCacheLoaded] = useState(false); + const form = useForm({ resolver: zodResolver(buildPartsSchemaCreate), defaultValues: createEmptyValues }); + + useEffect(() => { + const checkCacheForSelectedBuild = async () => { + try { + const cachedData = await getCacheData("/build/list"); + if (cachedData && cachedData.build) { + setSelectedBuilding(cachedData.build); + const formValues = form.getValues(); + form.setValue("build_uu_id", cachedData.build.uu_id); + } + } catch (error) { console.error("Error checking cache for selected build:", error) } + }; + checkCacheForSelectedBuild(); + }, [form]); + + useEffect(() => { + const fetchCacheDirectly = async () => { + if (!cacheLoaded) { + try { + const cachedData = await getCacheData(activePageUrl); + if (cachedData) { + const formValues = form.getValues(); + const mergedData = { + build_uu_id: cachedData.build_uu_id || formValues.build_uu_id || null, + address_gov_code: cachedData.address_gov_code || formValues.address_gov_code || "", + part_no: cachedData.part_no || formValues.part_no || 0, + part_level: cachedData.part_level || formValues.part_level || 0, + part_code: cachedData.part_code || formValues.part_code || "", + part_gross_size: cachedData.part_gross_size || formValues.part_gross_size || 0, + part_net_size: cachedData.part_net_size || formValues.part_net_size || 0, + default_accessory: cachedData.default_accessory || formValues.default_accessory || "0", + human_livable: cachedData.human_livable || formValues.human_livable || true, + due_part_key: cachedData.due_part_key || formValues.due_part_key || "", + part_direction_uu_id: cachedData.part_direction_uu_id || formValues.part_direction_uu_id || null, + }; + form.reset(mergedData); + } + setCacheLoaded(true); + if (refreshCache) { refreshCache(activePageUrl) } + } catch (error) { console.error("Error fetching cache directly:", error) } + } + }; + fetchCacheDirectly(); + }, []); + + const handleFieldBlur = async (fieldName: string, value: any) => { + if (value) { + try { + const currentValues = form.getValues(); + const updatedData = { ...currentValues, [fieldName]: value }; + await setCacheData(activePageUrl, updatedData); + if (updateCache) { updateCache(activePageUrl, updatedData) } + } catch (error) { console.error("Error updating cache:", error) } + } + }; + + const onSubmit = async (data: any) => { + setFormError(""); + if (!data.build_uu_id && selectedBuilding) { data.build_uu_id = selectedBuilding.uu_id } + if (!data.build_uu_id) { + const errorMessage = language === 'en' ? "Missing building ID. Please select a building first." : "Bina ID'si eksik. Lütfen önce bir bina seçin."; + console.error('errorMessage', errorMessage); + setFormError(errorMessage); + return; + } + console.log("Form submitted with data:", data); + try { + const response = await apiPostFetcher({ url: `/api/parts/create`, isNoCache: true, body: data }); + await clearCacheData(activePageUrl); + if (clearCache) { clearCache(activePageUrl) } + if (response.success) { form.reset(createEmptyValues); router.push(listUrl) } + } catch (error) { + console.error("Error submitting form:", error); + setFormError(language === 'en' ? "Error submitting form" : "Form gönderilirken hata oluştu"); + } + } + + const formComponent = <> +
+ + {/* ----- Basic Information ----- */} + {/* Address Government Code */} + + + {/* ----- Part Identification ----- */} + {/* Part Number */} + + + {/* Part Level */} + + + {/* Part Code */} + + + {/* ----- Size Information ----- */} + {/* Part Gross Size */} + + + {/* Part Net Size */} + + + {/* ----- Additional Properties ----- */} + {/* Default Accessory */} + + + {/* Human Livable */} + + + {/* Due Part Key */} + + + {/* Part Direction UUID */} + + + {/* ----- Submit Button ----- */} + + + + + + const noSelectedBuildingComponent =
+
+ {buildingTranslations[language].noSelectedBuilding} +
+ +
+ + const mainComponent =
+ {/* Header with title and change button */} +
+

+ {buildingTranslations[language].selectedBuilding} +

+ +
+ + {/* Building details grid */} +
+ {/* UUID */} +
+ UUID: + {selectedBuilding.uu_id} +
+ + {/* Building Name */} +
+ {buildingTranslations[language].buildName}: + {selectedBuilding.build_name} +
+ + {/* Building Number */} +
+ {buildingTranslations[language].buildNo}: + {selectedBuilding.build_no} +
+ + {/* Address Code */} +
+ {buildingTranslations[language].buildAddressCode}: + {selectedBuilding.gov_address_code} +
+ + {/* Max Floor */} +
+ {buildingTranslations[language].buildMaxFloor}: + {selectedBuilding.max_floor} +
+
+
+ + return ( +
+ {/* back to list button */} +
+ +
+

{translationsOfPage[language].title}

+ {/* ===== BUILDING SELECTION SECTION ===== */} + {selectedBuilding ? mainComponent : noSelectedBuildingComponent} + {/* ===== FORM SECTION ===== */} + {selectedBuilding && formComponent} +
+ ); +} + +export const CreateFromBuildComponent = withCache(CreateFromComponentBase); +export default withCache(CreateFromBuildComponent); diff --git a/ServicesWeb/customer/src/pages/multi/people/superuser/UpdatePage.tsx b/ServicesWeb/customer/src/pages/multi/people/superuser/UpdatePage.tsx new file mode 100644 index 0000000..9615018 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/people/superuser/UpdatePage.tsx @@ -0,0 +1,212 @@ +import React, { useState, useEffect } from "react"; +import { apiPostFetcher } from "@/lib/fetcher"; +import { useRouter } from "next/navigation"; + +import { withCache } from "@/components/mutual/context/cache/withCache"; +import { Button } from "@/components/mutual/ui/button"; +import { Form } from "@/components/mutual/ui/form"; +import { getCacheData, setCacheData, clearCacheData } from "@/components/mutual/context/cache/context"; + +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { LanguageTypes } from "@/validations/mutual/language/validations"; + +import { buildPartsSchema, updateEmptyValues } from "./schema"; +import { buildPartsTranslations, translationsOfPage } from "./translations"; + +import { CheckBoxInput } from "@/components/mutual/formInputs/CheckBoxInput"; +import { NumberInput } from "@/components/mutual/formInputs/NumberInput"; +import { StringInput } from "@/components/mutual/formInputs/StringInput"; + +function UpdateFromComponentBase({ + onlineData, + cacheData, + cacheLoading, + cacheError, + refreshCache, + updateCache, + clearCache, + activePageUrl +}: { + onlineData?: any; + cacheData?: { [url: string]: any } | null; + cacheLoading?: boolean; + cacheError?: string | null; + refreshCache?: (url?: string) => Promise; + updateCache?: (url: string, data: any) => Promise; + clearCache?: (url: string) => Promise; + activePageUrl: string; +}) { + const language: LanguageTypes = onlineData?.lang || 'en'; + const [cacheLoaded, setCacheLoaded] = useState(false); + const [partUUID, setPartUUID] = useState(""); const router = useRouter(); + const listUrl = `/panel/${activePageUrl?.replace("/update", "")}`; + + const form = useForm({ resolver: zodResolver(buildPartsSchema), mode: "onSubmit", defaultValues: updateEmptyValues }); + + useEffect(() => { + const fetchData = async () => { + if (!cacheLoaded) { + try { + const cachedData = await getCacheData(activePageUrl); + if (cachedData) { + const formValues = form.getValues(); + const mergedData = { + uu_id: "", + address_gov_code: cachedData.address_gov_code || formValues.address_gov_code || "", + part_no: cachedData.part_no ?? formValues.part_no ?? 0, + part_level: cachedData.part_level ?? formValues.part_level ?? 0, + part_code: cachedData.part_code || formValues.part_code || "", + part_gross_size: cachedData.part_gross_size ?? formValues.part_gross_size ?? 0, + part_net_size: cachedData.part_net_size ?? formValues.part_net_size ?? 0, + default_accessory: cachedData.default_accessory || formValues.default_accessory || "0", + human_livable: cachedData.human_livable ?? formValues.human_livable ?? true, + due_part_key: cachedData.due_part_key || formValues.due_part_key || "", + part_direction_uu_id: cachedData.part_direction_uu_id ?? formValues.part_direction_uu_id ?? null + }; + form.reset(mergedData); + setPartUUID(cachedData.uu_id); + } + setCacheLoaded(true); + if (refreshCache) { refreshCache(activePageUrl); } + } catch (error) { console.error("Error fetching cache:", error); } + } + }; + fetchData(); + }, []); + + const handleFieldBlur = async (fieldName: string, value: any) => { + if (value !== undefined) { + try { + const currentValues = form.getValues(); + const updatedData = { ...currentValues, [fieldName]: value }; + await setCacheData(activePageUrl, updatedData); + if (updateCache) { updateCache(activePageUrl, updatedData); } + } catch (error) { console.error("Error updating cache:", error); } + } + }; + + const onSubmit = async (data: any) => { + try { + console.log('Form data received:', data); + const formattedData = { ...data, uuid: partUUID }; + const response = await apiPostFetcher({ + url: `/api/parts/update/${partUUID}`, + isNoCache: true, + body: formattedData, + }); + await clearCacheData(activePageUrl); + if (clearCache) { clearCache(activePageUrl); } + if (response.success) { form.reset(updateEmptyValues); router.push(listUrl) } + } catch (error) { console.error("Error submitting form:", error); } + } + + return ( +
+ {/* back to list button */} +
+ +
+

{translationsOfPage[language].title}

+

UUID: {partUUID}

+ +
+ + {/* Address Government Code */} + + + {/* Part Number */} + + + {/* Part Level */} + + + {/* Part Code */} + + + {/* Part Gross Size */} + + + {/* Part Net Size */} + + + {/* Default Accessory */} + + + {/* Human Livable */} + + + {/* Due Part Key */} + + + {/* Part Direction UUID */} + + + + + +
+ ); +} + +export const UpdateFromComponent = withCache(UpdateFromComponentBase); +export default withCache(UpdateFromComponentBase); diff --git a/ServicesWeb/customer/src/pages/multi/people/superuser/main/ListPage.tsx b/ServicesWeb/customer/src/pages/multi/people/superuser/main/ListPage.tsx new file mode 100644 index 0000000..a9f1c14 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/people/superuser/main/ListPage.tsx @@ -0,0 +1,163 @@ +'use client'; +import React from "react"; +import LoadingContent from "@/components/mutual/loader/component"; + +import { useRouter } from "next/navigation"; +import { useReactTable, getCoreRowModel, getSortedRowModel, getPaginationRowModel, flexRender, ColumnDef } from "@tanstack/react-table"; +import { Button } from "@/components/mutual/ui/button"; + +import { API_BASE_URL } from "@/config/config"; +import { useTableData } from "@/hooks/useTableData"; +import { LanguageTypes } from "@/validations/mutual/language/validations"; +import { DashboardPageProps } from "@/validations/mutual/table/validations"; + +import { PeopleSchemaInterface, listTranslations } from "../schema"; + +import DataTable from "./dataTable"; +import TableForm from "./tableForm"; +import MobilePaginationControls from "./mobilePaginationControls"; +import TableHeader from "./tableHeader"; +import { columnHelper, retrieveColumns } from "./columns"; +import { setCacheData, getCacheData, clearCacheData } from "@/components/mutual/context/cache/context"; + +const PeopleListPage: React.FC = React.memo((props) => { + const router = useRouter(); + const activePageUrl = props.activePageUrl || ''; + const language = props.onlineData?.lang as LanguageTypes || 'en'; + const t = listTranslations[language]; + const apiUrl = `${API_BASE_URL}/people/list`; + + const [selectedPerson, setSelectedPerson] = React.useState(null); + const [cacheLoaded, setCacheLoaded] = React.useState(false); + + const { + tableData, isLoading, error, form, pagination, apiPagination, setSorting, handleSortingChange, handleSelectChange, + handleFirstPage, handlePreviousPage, handleNextPage, getSortingIcon, handleFormSubmit, + isPreviousDisabled, isNextDisabled, pageSizeOptions, renderPageOptions, sorting, // Disabled states + } = useTableData({ + apiUrl, + mapFormToRequestBody: (values) => { + const requestBody = { + page: values.page, size: values.size, orderField: values.orderField, orderType: values.orderType, + // query: selectedPerson ? { person_uu_id: selectedPerson.uu_id } : {} + }; return requestBody; + } + }); + + // Check for selected build in cache when component mounts + React.useEffect(() => { + const checkCacheForSelectedBuild = async () => { + if (!cacheLoaded) { + try { + const activePageUrl = props.activePageUrl || ''; + const cachedData = await getCacheData(`${activePageUrl}/list`); + if (cachedData && cachedData.person && cachedData.person.uu_id) { + setSelectedPerson(cachedData.person.uu_id); + } + setCacheLoaded(true); + } catch (error) { + console.error("Error checking cache for selected build:", error); + setCacheLoaded(true); + } + } + }; + checkCacheForSelectedBuild(); + }, [props.activePageUrl]); + + React.useEffect(() => { + const checkCacheForSelectedBuild = async () => { + try { + const cachedData = await getCacheData("/build/list"); + if (cachedData && cachedData.build) { setSelectedPerson(cachedData.build) } + } catch (error) { console.error("Error checking cache for selected build:", error) } + }; + checkCacheForSelectedBuild(); + }, []); + + // React.useEffect(() => { + // if (selectedPerson) { form.setValue("page", 1); form.setValue("query", { person_uu_id: selectedPerson.uu_id }) } + // }, [selectedPerson, form]); + + + const columnsArray = retrieveColumns(language, activePageUrl, router); + const columns = React.useMemo(() => columnsArray, [columnHelper]) as ColumnDef[]; + + const table = useReactTable({ + data: tableData, + columns, + state: { sorting, pagination }, + onSortingChange: setSorting, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + getPaginationRowModel: getPaginationRowModel(), + manualPagination: true, + pageCount: apiPagination.totalPages || 1, + }) + + const dataTableComponent = + + const noSelectedBuildingComponent =
+
{t.noSelectedBuilding || "No building selected. Please select a building first."}
+ +
+ + return ( + isLoading ? : +
+ +
+ + +
+ + + + {/* Mobile pagination controls - only visible on small screens */} + + + {selectedPerson ? dataTableComponent : noSelectedBuildingComponent} +
+ ); +}); + +export default PeopleListPage; diff --git a/ServicesWeb/customer/src/pages/multi/people/superuser/main/columns.tsx b/ServicesWeb/customer/src/pages/multi/people/superuser/main/columns.tsx new file mode 100644 index 0000000..949db50 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/people/superuser/main/columns.tsx @@ -0,0 +1,119 @@ +import { + useReactTable, + getCoreRowModel, + getSortedRowModel, + getPaginationRowModel, + flexRender, + createColumnHelper, + ColumnDef, +} from "@tanstack/react-table"; +import { PeopleSchemaInterface } from "../schema"; +import { peopleTranslations, translationsOfPage } from '../translations'; +import { LanguageTypes } from "@/validations/mutual/language/validations"; +import { Button } from "@/components/mutual/ui/button"; +import { Pencil } from "lucide-react"; +import { setCacheData } from "@/components/mutual/context/cache/context"; + +const columnHelper = createColumnHelper(); + +function retrieveColumns(language: LanguageTypes, activePageUrl: string, router: any) { + const handleEditRow = async (row: PeopleSchemaInterface) => { + try { + await setCacheData(`${activePageUrl}/update`, row); + router.push(`/panel/${activePageUrl}/update`); + } catch (error) { console.error('Error storing row data in cache:', error) } + }; + const actionButtonGroup = (info: any) => ( + + ); + const columnsArray = [ + columnHelper.display({ + id: 'actions', + header: () => {translationsOfPage[language].actionButtonGroup}, + cell: (info) => { return (actionButtonGroup(info)) } + }), + columnHelper.accessor('firstname', { + cell: info => info.getValue(), + header: () => {peopleTranslations[language].firstname}, + footer: info => info.column.id + }), + columnHelper.accessor('surname', { + cell: info => String(info.getValue()), + header: () => {peopleTranslations[language].surname}, + footer: info => info.column.id + }), + columnHelper.accessor('middle_name', { + cell: info => String(info.getValue()), + header: () => {peopleTranslations[language].middle_name}, + footer: info => info.column.id + }), + columnHelper.accessor('sex_code', { + cell: info => info.getValue(), + header: () => {peopleTranslations[language].sex_code}, + footer: info => info.column.id + }), + columnHelper.accessor('person_ref', { + cell: info => String(info.getValue()), + header: () => {peopleTranslations[language].person_ref}, + footer: info => info.column.id + }), + columnHelper.accessor('person_tag', { + cell: info => String(info.getValue()), + header: () => {peopleTranslations[language].person_tag}, + footer: info => info.column.id + }), + columnHelper.accessor('father_name', { + cell: info => info.getValue(), + header: () => {peopleTranslations[language].father_name}, + footer: info => info.column.id + }), + columnHelper.accessor('mother_name', { + cell: info => info.getValue() ? 'Yes' : 'No', + header: () => {peopleTranslations[language].mother_name}, + footer: info => info.column.id + }), + columnHelper.accessor('country_code', { + cell: info => info.getValue(), + header: () => {peopleTranslations[language].country_code}, + footer: info => info.column.id + }), + columnHelper.accessor('national_identity_id', { + cell: info => info.getValue() || '', + header: () => {peopleTranslations[language].national_identity_id}, + footer: info => info.column.id + }), + columnHelper.accessor('birth_place', { + cell: info => info.getValue() || '', + header: () => {peopleTranslations[language].birth_place}, + footer: info => info.column.id + }), + columnHelper.accessor('birth_date', { + cell: info => info.getValue() || '', + header: () => {peopleTranslations[language].birth_date}, + footer: info => info.column.id + }), + columnHelper.accessor('tax_no', { + cell: info => info.getValue() || '', + header: () => {peopleTranslations[language].tax_no}, + footer: info => info.column.id + }), + ] + return columnsArray; +} + +export { + retrieveColumns, + columnHelper +} diff --git a/ServicesWeb/customer/src/pages/multi/people/superuser/main/dataTable.tsx b/ServicesWeb/customer/src/pages/multi/people/superuser/main/dataTable.tsx new file mode 100644 index 0000000..84951d6 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/people/superuser/main/dataTable.tsx @@ -0,0 +1,129 @@ +'use client'; + +import React from "react"; +import { Button } from "@/components/mutual/ui/button"; +import { X } from "lucide-react"; +import { useReactTable } from "@tanstack/react-table"; +import { flexRender } from "@tanstack/react-table"; +import { PeopleSchemaInterface } from "../schema"; +import { Translations } from "./types"; + +interface DataTableProps { + table: ReturnType; + tableData: PeopleSchemaInterface[]; + isLoading: boolean; + handleSortingChange: (columnId: string) => void; + getSortingIcon: (columnId: string) => React.ReactNode; + flexRender: typeof flexRender; + t: Translations; + selected: any | null; + router: any; +} + +const DataTable: React.FC = React.memo(({ + table, + tableData, + isLoading, + handleSortingChange, + getSortingIcon, + flexRender, + t, + selected, + router, +}) => { + return ( +
+ {/* Semi-transparent loading overlay that preserves interactivity */} + {isLoading && ( +
+ {/* We don't put anything here as we already have the loading indicator in the header */} +
+ )} + {selected && ( +
+
+

{t.selectedPerson || "Selected Person"}

+ +
+
+
+ ID: + {selected.national_identity_id || '-'} +
+
+ {t.name || "Name"}: + {selected.firstname || '-'} +
+
+ {t.surname || "Surname"}: + {selected.surname || '-'} +
+
+ {t.birthDate || "Birth Date"}: + {selected.birth_date || '-'} +
+
+ {t.taxNo || "Tax No"}: + {selected.tax_no || '-'} +
+
+
+ )} + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + ); + })} + + ))} + + + {tableData.length === 0 && !isLoading ? ( + + + + ) : ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => { + return ( + + ); + })} + + )) + )} + +
{t.dataTable}
handleSortingChange(header.column.id)} + aria-sort={header.column.getIsSorted() ? (header.column.getIsSorted() === 'desc' ? 'descending' : 'ascending') : undefined} + scope="col" + > +
+ {flexRender(header.column.columnDef.header, header.getContext())} + {getSortingIcon(header.column.id)} +
+
+ {t.noDataAvailable} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+
+ ); +}); + + +export default DataTable; diff --git a/ServicesWeb/customer/src/pages/multi/people/superuser/main/mobilePaginationControls.tsx b/ServicesWeb/customer/src/pages/multi/people/superuser/main/mobilePaginationControls.tsx new file mode 100644 index 0000000..aabe650 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/people/superuser/main/mobilePaginationControls.tsx @@ -0,0 +1,34 @@ +import { MobilePaginationControlsProps } from "@/validations/mutual/table/validations"; +import { Button } from "@/components/mutual/ui/button"; + +const MobilePaginationControls: React.FC = ({ + handlePreviousPage, + handleNextPage, + isPreviousDisabled, + isNextDisabled, + t +}) => { + return ( +
+
+ + +
+
+ ); +}; + +export default MobilePaginationControls; diff --git a/ServicesWeb/customer/src/pages/multi/people/superuser/main/tableForm.tsx b/ServicesWeb/customer/src/pages/multi/people/superuser/main/tableForm.tsx new file mode 100644 index 0000000..d13c4cc --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/people/superuser/main/tableForm.tsx @@ -0,0 +1,152 @@ +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@/components/mutual/ui/form"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/mutual/ui/select"; +import { UseFormReturn } from "react-hook-form"; +import { Translations } from "./types"; +import { Button } from "@/components/mutual/ui/button"; + +interface TableFormProps { + form: UseFormReturn; + handleFormSubmit: (e: React.FormEvent) => void; + handleSelectChange: (value: string, field: { onChange: (value: number) => void }) => void; + renderPageOptions: () => { key: string | number; value: string; label: string }[]; + pageSizeOptions: number[]; + apiPagination: { + size: number; + page: number; + totalCount: number; + totalPages: number; + pageCount: number; + }; + handleFirstPage: () => void; + handlePreviousPage: () => void; + handleNextPage: () => void; + isPreviousDisabled: () => boolean; + isNextDisabled: () => boolean; + t: Translations; +} + +const TableForm: React.FC = ({ + form, + handleFormSubmit, + handleSelectChange, + renderPageOptions, + pageSizeOptions, + apiPagination, + handleFirstPage, + handlePreviousPage, + handleNextPage, + isPreviousDisabled, + isNextDisabled, + t +}) => { + return ( +
+
+ +
+ ( + + {t.page} + + + + )} + /> +
+
+ ( + + {t.size} + + + + )} + /> +
+
+

+ {t.page}: {apiPagination.page}{" / "} {apiPagination.totalPages} · + {t.size}: {apiPagination.pageCount}{" / "} {apiPagination.size} · + {t.total}: {apiPagination.totalCount} {t.items} +

+
+
+ + + + {/* */} +
+
+ +
+ ); +}; + +export default TableForm; diff --git a/ServicesWeb/customer/src/pages/multi/people/superuser/main/tableHeader.tsx b/ServicesWeb/customer/src/pages/multi/people/superuser/main/tableHeader.tsx new file mode 100644 index 0000000..a53616a --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/people/superuser/main/tableHeader.tsx @@ -0,0 +1,24 @@ +import { TableHeaderProps, ErrorDisplayProps } from "@/validations/mutual/table/validations"; + +const ErrorDisplay: React.FC = ({ message }) => { + return
{message}
; +}; + + +const TableHeader: React.FC = ({ title, description, isLoading, error, t }) => { + return ( + +
+
+
+

{title}

+

{description}

+
+ {/* {isLoading && } */} + {error && } +
+
+ ); +}; + +export default TableHeader; diff --git a/ServicesWeb/customer/src/pages/multi/people/superuser/main/types.ts b/ServicesWeb/customer/src/pages/multi/people/superuser/main/types.ts new file mode 100644 index 0000000..7ccc688 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/people/superuser/main/types.ts @@ -0,0 +1,24 @@ +import { Translations as BaseTranslations } from "@/validations/mutual/table/validations"; + +// Extended Translations interface to include person-related translations +interface Translations extends BaseTranslations { + // Building-related translations (for backward compatibility) + selectedBuilding?: string; + buildName?: string; + buildNo?: string; + buildAddressCode?: string; + buildMaxFloor?: string; + noSelectedBuilding?: string; + selectBuilding?: string; + changeBuilding?: string; + + // Person-related translations + selectedPerson: string; + name: string; + surname: string; + birthDate: string; + taxNo: string; + changePerson: string; +} + +export type { Translations }; diff --git a/ServicesWeb/customer/src/pages/multi/people/superuser/schema.ts b/ServicesWeb/customer/src/pages/multi/people/superuser/schema.ts new file mode 100644 index 0000000..bf279e7 --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/people/superuser/schema.ts @@ -0,0 +1,151 @@ +import { LanguageTypes } from "@/validations/mutual/language/validations"; +import * as z from "zod"; + +/** + * Zod schema for People + * Corresponds to the People class in Python + * People that are related to users in the application + */ +const peopleSchema = z.object({ + uu_id: z.string().describe("UUID of the person"), + firstname: z.string().describe("First name of the person"), + surname: z.string().max(24).describe("Surname of the person"), + middle_name: z.string().default("").describe("Middle name of the person"), + sex_code: z.string().max(1).describe("Sex code of the person (e.g., M/F)"), + person_ref: z.string().default("").describe("Reference ID for the person"), + person_tag: z.string().default("").describe("Unique tag for the person"), + father_name: z.string().default("").describe("Father's name of the person"), + mother_name: z.string().default("").describe("Mother's name of the person"), + country_code: z + .string() + .max(4) + .default("TR") + .describe("Country code of the person"), + national_identity_id: z + .string() + .default("") + .describe("National identity ID of the person"), + birth_place: z.string().default("").describe("Birth place of the person"), + birth_date: z + .string() + .datetime() + .default("1900-01-01") + .describe("Birth date of the person"), + tax_no: z.string().default("").describe("Tax number of the person"), +}); + +export type People = z.infer; + +interface PeopleSchemaInterface { + firstname: string; + surname: string; + middle_name: string; + sex_code: string; + person_ref: string; + person_tag: string; + father_name: string; + mother_name: string; + country_code: string; + national_identity_id: string; + birth_place: string; + birth_date: string; + tax_no: string; +} + +const createEmptyValues: PeopleSchemaInterface = { + firstname: "", + surname: "", + middle_name: "", + sex_code: "", + person_ref: "", + person_tag: "", + father_name: "", + mother_name: "", + country_code: "", + national_identity_id: "", + birth_place: "", + birth_date: "", + tax_no: "", +}; + +const updateEmptyValues: PeopleSchemaInterface = { + firstname: "", + surname: "", + middle_name: "", + sex_code: "", + person_ref: "", + person_tag: "", + father_name: "", + mother_name: "", + country_code: "", + national_identity_id: "", + birth_place: "", + birth_date: "", + tax_no: "", +}; + +const listTranslations: Record = { + en: { + createNew: "Create New Person", + createNewPeople: "Create New Person", + dataTable: "People Table", + tableWithApiData: "Table with API Data", + loading: "Loading...", + noDataAvailable: "No data available", + page: "Page", + size: "Size", + total: "Total", + items: "items", + first: "First", + previous: "Previous", + next: "Next", + selectPage: "Select page", + selectSize: "Select size", + actionButtonGroup: "Actions", + selectedPerson: "Selected Person", + name: "Name", + surname: "Surname", + birthDate: "Birth Date", + taxNo: "Tax No", + changePerson: "Change Person", + // Keep these for backward compatibility + selectedBuilding: "Selected Person", + buildName: "Name", + buildNo: "ID", + buildAddressCode: "Address", + buildMaxFloor: "Birth Date", + }, + tr: { + createNew: "Yeni Kişi Oluştur", + createNewPeople: "Yeni Kişi Oluştur", + dataTable: "Kişiler Tablosu", + tableWithApiData: "API Verili Tablo", + loading: "Yükleniyor...", + noDataAvailable: "Veri bulunamadı", + page: "Sayfa", + size: "Boyut", + total: "Toplam", + items: "öğe", + first: "İlk", + previous: "Önceki", + next: "Sonraki", + selectPage: "Sayfa seç", + selectSize: "Boyut seç", + actionButtonGroup: "Eylemler", + selectedPerson: "Seçilen Kişi", + name: "Ad", + surname: "Soyad", + birthDate: "Doğum Tarihi", + taxNo: "Vergi No", + changePerson: "Kişiyi Değiştir", + // Keep these for backward compatibility + selectedBuilding: "Seçilen Kişi", + buildName: "Ad", + buildNo: "Kimlik No", + buildAddressCode: "Adres", + buildMaxFloor: "Doğum Tarihi", + }, +}; + +export type { PeopleSchemaInterface }; +export { peopleSchema, createEmptyValues, updateEmptyValues, listTranslations }; diff --git a/ServicesWeb/customer/src/pages/multi/people/superuser/translations.ts b/ServicesWeb/customer/src/pages/multi/people/superuser/translations.ts new file mode 100644 index 0000000..1ac3e1f --- /dev/null +++ b/ServicesWeb/customer/src/pages/multi/people/superuser/translations.ts @@ -0,0 +1,85 @@ +interface PeopleSchemaInterface { + firstname: string; + surname: string; + middle_name: string; + sex_code: string; + person_ref: string; + person_tag: string; + father_name: string; + mother_name: string; + country_code: string; + national_identity_id: string; + birth_place: string; + birth_date: string; + tax_no: string; +} + +const peopleTranslations = { + tr: { + firstname: "Ad", + surname: "Soyad", + middle_name: "Ortadaki Ad", + sex_code: "Cinsiyet", + person_ref: "Kişi Referans", + person_tag: "Kişi Etiket", + father_name: "Baba Adı", + mother_name: "Anne Adı", + country_code: "Ülke Kodu", + national_identity_id: "Kimlik No", + birth_place: "Doğum Yeri", + birth_date: "Doğum Tarihi", + tax_no: "Vergi No", + }, + en: { + firstname: "First Name", + surname: "Surname", + middle_name: "Middle Name", + sex_code: "Sex Code", + person_ref: "Person Ref", + person_tag: "Person Tag", + father_name: "Father Name", + mother_name: "Mother Name", + country_code: "Country Code", + national_identity_id: "National Identity ID", + birth_place: "Birth Place", + birth_date: "Birth Date", + tax_no: "Tax No", + }, +}; + +const translationsOfPage = { + tr: { + title: "Kişi Oluştur", + updateTitle: "Kişiyi Güncelle", + back2List: "Listeye Geri Dön", + actionButtonGroup: "İşlemler", + selectedPerson: "Seçilen Kişi", + name: "Ad", + surname: "Soyad", + birthDate: "Doğum Tarihi", + taxNo: "Vergi No", + changePerson: "Kişiyi Değiştir", + createNew: "Yeni Kişi Oluştur", + createNewPeople: "Yeni Kişi Oluştur", + dataTable: "Kişiler Tablosu", + noDataAvailable: "Veri bulunamadı" + }, + en: { + title: "Create Person", + updateTitle: "Update Person", + back2List: "Back to List", + actionButtonGroup: "Actions", + selectedPerson: "Selected Person", + name: "Name", + surname: "Surname", + birthDate: "Birth Date", + taxNo: "Tax No", + changePerson: "Change Person", + createNew: "Create New Person", + createNewPeople: "Create New Person", + dataTable: "People Table", + noDataAvailable: "No data available" + }, +}; + +export { peopleTranslations, translationsOfPage };