app: people and company @frontend added no test runned
This commit is contained in:
parent
c259ad3d99
commit
5c640ddcee
|
|
@ -71,7 +71,6 @@ def super_people_list_callable(list_options: PaginateOnly, token: TokenDictType)
|
||||||
people_list = People.query.filter(*People.convert(list_options.query))
|
people_list = People.query.filter(*People.convert(list_options.query))
|
||||||
else:
|
else:
|
||||||
people_list = People.query.filter()
|
people_list = People.query.filter()
|
||||||
|
|
||||||
pagination = Pagination(data=people_list)
|
pagination = Pagination(data=people_list)
|
||||||
pagination.change(**list_options.model_dump())
|
pagination.change(**list_options.model_dump())
|
||||||
pagination_result = PaginationResult(data=people_list, pagination=pagination)
|
pagination_result = PaginationResult(data=people_list, pagination=pagination)
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,8 @@ const getMenuFromRedis = async (): Promise<ClientMenu> => {
|
||||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||||
const redisKey = decrpytUserSelection?.redisKey;
|
const redisKey = decrpytUserSelection?.redisKey;
|
||||||
if (!redisKey) throw new AuthError("No redis key found");
|
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);
|
const result = await safeRedisGet(`${redisKey}`, REDIS_TIMEOUT);
|
||||||
if (!result) throw new AuthError("No data found in redis");
|
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 });
|
const parsedResult = safeJsonParse(result, { menu: defaultValuesMenu });
|
||||||
if (!parsedResult.menu) throw new AuthError("No menu found in redis");
|
if (!parsedResult.menu) throw new AuthError("No menu found in redis");
|
||||||
return parsedResult.menu;
|
return parsedResult.menu;
|
||||||
|
|
@ -31,12 +28,7 @@ const setMenuToRedis = async (menuObject: string[]) => {
|
||||||
if (!menuObject) throw new AuthError("No menu object provided");
|
if (!menuObject) throw new AuthError("No menu object provided");
|
||||||
const oldData = await getCompleteFromRedis();
|
const oldData = await getCompleteFromRedis();
|
||||||
if (!oldData) throw new AuthError("No old data found in redis");
|
if (!oldData) throw new AuthError("No old data found in redis");
|
||||||
await setCompleteToRedis({
|
await setCompleteToRedis({ ...oldData, menu: { ...oldData.menu, selectionList: menuObject } });
|
||||||
...oldData, menu: {
|
|
||||||
...oldData.menu,
|
|
||||||
selectionList: menuObject,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) { if (error instanceof AuthError) { throw error } else { throw new AuthError(error instanceof Error ? error.message : "Unknown error") } }
|
} catch (error) { if (error instanceof AuthError) { throw error } else { throw new AuthError(error instanceof Error ? error.message : "Unknown error") } }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,14 @@ const menuTranslationEn = {
|
||||||
{ value: "Dashboard", key: "dashboard" },
|
{ value: "Dashboard", key: "dashboard" },
|
||||||
{ value: "Dashboard", key: "dashboard" },
|
{ value: "Dashboard", key: "dashboard" },
|
||||||
],
|
],
|
||||||
"/individual": [
|
"/people": [
|
||||||
{ value: "Individual", key: "individual" },
|
|
||||||
{ value: "Individual", key: "individual" },
|
{ value: "Individual", key: "individual" },
|
||||||
{ value: "Individual", key: "individual" },
|
{ value: "Individual", key: "individual" },
|
||||||
|
{ value: "People", key: "people" },
|
||||||
],
|
],
|
||||||
"/user": [
|
"/user": [
|
||||||
{ value: "User", key: "user" },
|
{ value: "Individual", key: "individual" },
|
||||||
{ value: "User", key: "user" },
|
{ value: "Individual", key: "individual" },
|
||||||
{ value: "User", key: "user" },
|
{ value: "User", key: "user" },
|
||||||
],
|
],
|
||||||
"/build": [
|
"/build": [
|
||||||
|
|
@ -42,7 +42,7 @@ const menuTranslationEn = {
|
||||||
"/build/parts": [
|
"/build/parts": [
|
||||||
{ value: "Build", key: "build" },
|
{ value: "Build", key: "build" },
|
||||||
{ value: "Parts", key: "parts" },
|
{ value: "Parts", key: "parts" },
|
||||||
{ value: "Build", key: "build" },
|
{ value: "Build Parts", key: "build" },
|
||||||
],
|
],
|
||||||
"/management/budget/actions": [
|
"/management/budget/actions": [
|
||||||
{ value: "Management", key: "management" },
|
{ value: "Management", key: "management" },
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
const menuTranslationTr = {
|
||||||
// New menu
|
// New menu
|
||||||
"/dashboard": [
|
"/dashboard": [
|
||||||
|
|
@ -24,14 +5,14 @@ const menuTranslationTr = {
|
||||||
{ value: "Panel", key: "dashboard" },
|
{ value: "Panel", key: "dashboard" },
|
||||||
{ value: "Panel", key: "dashboard" },
|
{ value: "Panel", key: "dashboard" },
|
||||||
],
|
],
|
||||||
"/individual": [
|
"/people": [
|
||||||
{ value: "Bireysel", key: "individual" },
|
|
||||||
{ value: "Bireysel", key: "individual" },
|
{ value: "Bireysel", key: "individual" },
|
||||||
{ value: "Bireysel", key: "individual" },
|
{ value: "Bireysel", key: "individual" },
|
||||||
|
{ value: "Birey", key: "people" },
|
||||||
],
|
],
|
||||||
"/user": [
|
"/user": [
|
||||||
{ value: "Kullanıcı", key: "user" },
|
{ value: "Bireysel", key: "individual" },
|
||||||
{ value: "Kullanıcı", key: "user" },
|
{ value: "Bireysel", key: "individual" },
|
||||||
{ value: "Kullanıcı", key: "user" },
|
{ value: "Kullanıcı", key: "user" },
|
||||||
],
|
],
|
||||||
"/build": [
|
"/build": [
|
||||||
|
|
|
||||||
|
|
@ -237,54 +237,56 @@ function CreateFromComponentBase({
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
const mainComponent = <div className="mb-6 p-4 border rounded-md bg-blue-50 shadow-sm relative">
|
const mainComponent = selectedBuilding ? (
|
||||||
{/* Header with title and change button */}
|
<div className="mb-6 p-4 border rounded-md bg-blue-50 shadow-sm relative">
|
||||||
<div className="flex justify-between items-center mb-2">
|
{/* Header with title and change button */}
|
||||||
<h3 className="text-lg font-medium text-blue-800">
|
<div className="flex justify-between items-center mb-2">
|
||||||
{buildingTranslations[language].selectedBuilding}
|
<h3 className="text-lg font-medium text-blue-800">
|
||||||
</h3>
|
{buildingTranslations[language].selectedBuilding}
|
||||||
<Button
|
</h3>
|
||||||
onClick={() => router.push(`/panel/build`)}
|
<Button
|
||||||
className="bg-red-100 hover:bg-red-200 text-red-700 p-1 h-8 w-8 rounded-full flex items-center justify-center"
|
onClick={() => router.push(`/panel/build`)}
|
||||||
title={buildingTranslations[language].changeBuilding}
|
className="bg-red-100 hover:bg-red-200 text-red-700 p-1 h-8 w-8 rounded-full flex items-center justify-center"
|
||||||
>
|
title={buildingTranslations[language].changeBuilding}
|
||||||
<X size={16} />
|
>
|
||||||
</Button>
|
<X size={16} />
|
||||||
</div>
|
</Button>
|
||||||
|
|
||||||
{/* Building details grid */}
|
|
||||||
<div className="grid grid-cols-2 gap-2">
|
|
||||||
{/* UUID */}
|
|
||||||
<div>
|
|
||||||
<span className="font-semibold">UUID: </span>
|
|
||||||
{selectedBuilding.uu_id}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Building Name */}
|
{/* Building details grid */}
|
||||||
<div>
|
<div className="grid grid-cols-2 gap-2">
|
||||||
<span className="font-semibold">{buildingTranslations[language].buildName}: </span>
|
{/* UUID */}
|
||||||
{selectedBuilding.build_name}
|
<div>
|
||||||
</div>
|
<span className="font-semibold">UUID: </span>
|
||||||
|
{selectedBuilding?.uu_id || '-'}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Building Number */}
|
{/* Building Name */}
|
||||||
<div>
|
<div>
|
||||||
<span className="font-semibold">{buildingTranslations[language].buildNo}: </span>
|
<span className="font-semibold">{buildingTranslations[language].buildName}: </span>
|
||||||
{selectedBuilding.build_no}
|
{selectedBuilding?.build_name || '-'}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Address Code */}
|
{/* Building Number */}
|
||||||
<div>
|
<div>
|
||||||
<span className="font-semibold">{buildingTranslations[language].buildAddressCode}: </span>
|
<span className="font-semibold">{buildingTranslations[language].buildNo}: </span>
|
||||||
{selectedBuilding.gov_address_code}
|
{selectedBuilding?.build_no || '-'}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Max Floor */}
|
{/* Address Code */}
|
||||||
<div>
|
<div>
|
||||||
<span className="font-semibold">{buildingTranslations[language].buildMaxFloor}: </span>
|
<span className="font-semibold">{buildingTranslations[language].buildAddressCode}: </span>
|
||||||
{selectedBuilding.max_floor}
|
{selectedBuilding?.gov_address_code || '-'}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Max Floor */}
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{buildingTranslations[language].buildMaxFloor}: </span>
|
||||||
|
{selectedBuilding?.max_floor || '-'}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-w-2xl mx-auto p-6 bg-white rounded-lg shadow-md">
|
<div className="w-full max-w-2xl mx-auto p-6 bg-white rounded-lg shadow-md">
|
||||||
|
|
@ -302,4 +304,4 @@ function CreateFromComponentBase({
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CreateFromBuildComponent = withCache(CreateFromComponentBase);
|
export const CreateFromBuildComponent = withCache(CreateFromComponentBase);
|
||||||
export default withCache(CreateFromBuildComponent);
|
export default CreateFromBuildComponent;
|
||||||
|
|
|
||||||
|
|
@ -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<void>;
|
||||||
|
updateCache?: (url: string, data: any) => Promise<void>;
|
||||||
|
clearCache?: (url: string) => Promise<void>;
|
||||||
|
activePageUrl: string;
|
||||||
|
}) {
|
||||||
|
const language: LanguageTypes = onlineData?.lang || 'en';
|
||||||
|
const router = useRouter();
|
||||||
|
const listUrl = `/panel/${activePageUrl?.replace("/create", "")}`;
|
||||||
|
const [selectedBuilding, setSelectedBuilding] = useState<any>(null);
|
||||||
|
const [formError, setFormError] = useState<string>("");
|
||||||
|
const [cacheLoaded, setCacheLoaded] = useState<boolean>(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<any>({ 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 = <>
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||||
|
{/* ----- Basic Information ----- */}
|
||||||
|
{/* Address Government Code */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="address_gov_code"
|
||||||
|
label={buildPartsTranslations[language].address_gov_code}
|
||||||
|
placeholder={buildPartsTranslations[language].address_gov_code}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* ----- Part Identification ----- */}
|
||||||
|
{/* Part Number */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_no"
|
||||||
|
label={buildPartsTranslations[language].part_no}
|
||||||
|
placeholder={buildPartsTranslations[language].part_no}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Level */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_level"
|
||||||
|
label={buildPartsTranslations[language].part_level}
|
||||||
|
placeholder={buildPartsTranslations[language].part_level}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Code */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_code"
|
||||||
|
label={buildPartsTranslations[language].part_code}
|
||||||
|
placeholder={buildPartsTranslations[language].part_code}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* ----- Size Information ----- */}
|
||||||
|
{/* Part Gross Size */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_gross_size"
|
||||||
|
label={buildPartsTranslations[language].part_gross_size}
|
||||||
|
placeholder={buildPartsTranslations[language].part_gross_size}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Net Size */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_net_size"
|
||||||
|
label={buildPartsTranslations[language].part_net_size}
|
||||||
|
placeholder={buildPartsTranslations[language].part_net_size}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* ----- Additional Properties ----- */}
|
||||||
|
{/* Default Accessory */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="default_accessory"
|
||||||
|
label={buildPartsTranslations[language].default_accessory}
|
||||||
|
placeholder={buildPartsTranslations[language].default_accessory}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Human Livable */}
|
||||||
|
<CheckBoxInput
|
||||||
|
control={form.control}
|
||||||
|
name="human_livable"
|
||||||
|
label={buildPartsTranslations[language].human_livable}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Due Part Key */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="due_part_key"
|
||||||
|
label={buildPartsTranslations[language].due_part_key}
|
||||||
|
placeholder={buildPartsTranslations[language].due_part_key}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Direction UUID */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_direction_uu_id"
|
||||||
|
label={buildPartsTranslations[language].part_direction_uu_id}
|
||||||
|
placeholder={buildPartsTranslations[language].part_direction_uu_id}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* ----- Submit Button ----- */}
|
||||||
|
<Button type="submit" className="w-full mt-8">
|
||||||
|
{language === 'en' ? translationsOfPage.en.title : translationsOfPage.tr.title}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
|
||||||
|
const noSelectedBuildingComponent = <div className="mb-6 p-4 border rounded-md bg-yellow-50 text-center">
|
||||||
|
<div className="mb-4 text-gray-600">
|
||||||
|
{buildingTranslations[language].noSelectedBuilding}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
onClick={() => router.push(`/panel/build`)}
|
||||||
|
className="bg-blue-600 text-white hover:bg-blue-700"
|
||||||
|
>
|
||||||
|
{buildingTranslations[language].selectBuilding}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const mainComponent = selectedBuilding ? (
|
||||||
|
<div className="mb-6 p-4 border rounded-md bg-blue-50 shadow-sm relative">
|
||||||
|
{/* Header with title and change button */}
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<h3 className="text-lg font-medium text-blue-800">
|
||||||
|
{buildingTranslations[language].selectedBuilding}
|
||||||
|
</h3>
|
||||||
|
<Button
|
||||||
|
onClick={() => router.push(`/panel/build`)}
|
||||||
|
className="bg-red-100 hover:bg-red-200 text-red-700 p-1 h-8 w-8 rounded-full flex items-center justify-center"
|
||||||
|
title={buildingTranslations[language].changeBuilding}
|
||||||
|
>
|
||||||
|
<X size={16} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Building details grid */}
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
{/* UUID */}
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">UUID: </span>
|
||||||
|
{selectedBuilding?.uu_id || '-'}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Building Name */}
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{buildingTranslations[language].buildName}: </span>
|
||||||
|
{selectedBuilding?.build_name || '-'}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Building Number */}
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{buildingTranslations[language].buildNo}: </span>
|
||||||
|
{selectedBuilding?.build_no || '-'}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Address Code */}
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{buildingTranslations[language].buildAddressCode}: </span>
|
||||||
|
{selectedBuilding?.gov_address_code || '-'}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Max Floor */}
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{buildingTranslations[language].buildMaxFloor}: </span>
|
||||||
|
{selectedBuilding?.max_floor || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full max-w-2xl mx-auto p-6 bg-white rounded-lg shadow-md">
|
||||||
|
{/* back to list button */}
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<Button onClick={() => router.push(listUrl)}>{translationsOfPage[language].back2List}</Button>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-bold mb-6">{translationsOfPage[language].title}</h2>
|
||||||
|
{/* ===== BUILDING SELECTION SECTION ===== */}
|
||||||
|
{selectedBuilding ? mainComponent : noSelectedBuildingComponent}
|
||||||
|
{/* ===== FORM SECTION ===== */}
|
||||||
|
{selectedBuilding && formComponent}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CreateFromBuildComponent = withCache(CreateFromComponentBase);
|
||||||
|
export default CreateFromBuildComponent;
|
||||||
|
|
@ -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<void>;
|
||||||
|
updateCache?: (url: string, data: any) => Promise<void>;
|
||||||
|
clearCache?: (url: string) => Promise<void>;
|
||||||
|
activePageUrl: string;
|
||||||
|
}) {
|
||||||
|
const language: LanguageTypes = onlineData?.lang || 'en';
|
||||||
|
const [cacheLoaded, setCacheLoaded] = useState<boolean>(false);
|
||||||
|
const [partUUID, setPartUUID] = useState<string>(""); 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<any>({
|
||||||
|
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 (
|
||||||
|
<div className="w-full max-w-2xl mx-auto p-6 bg-white rounded-lg shadow-md">
|
||||||
|
{/* back to list button */}
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<Button onClick={() => router.push(listUrl)}>{translationsOfPage[language].back2List}</Button>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-bold mb-3">{translationsOfPage[language].title}</h2>
|
||||||
|
<h4 className="text-sm text-gray-500 mb-6">UUID: {partUUID}</h4>
|
||||||
|
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||||
|
{/* Address Government Code */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="address_gov_code"
|
||||||
|
label={buildPartsTranslations[language].address_gov_code}
|
||||||
|
placeholder={buildPartsTranslations[language].address_gov_code}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Number */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_no"
|
||||||
|
label={buildPartsTranslations[language].part_no}
|
||||||
|
placeholder={buildPartsTranslations[language].part_no}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Level */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_level"
|
||||||
|
label={buildPartsTranslations[language].part_level}
|
||||||
|
placeholder={buildPartsTranslations[language].part_level}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Code */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_code"
|
||||||
|
label={buildPartsTranslations[language].part_code}
|
||||||
|
placeholder={buildPartsTranslations[language].part_code}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Gross Size */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_gross_size"
|
||||||
|
label={buildPartsTranslations[language].part_gross_size}
|
||||||
|
placeholder={buildPartsTranslations[language].part_gross_size}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Net Size */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_net_size"
|
||||||
|
label={buildPartsTranslations[language].part_net_size}
|
||||||
|
placeholder={buildPartsTranslations[language].part_net_size}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Default Accessory */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="default_accessory"
|
||||||
|
label={buildPartsTranslations[language].default_accessory}
|
||||||
|
placeholder={buildPartsTranslations[language].default_accessory}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Human Livable */}
|
||||||
|
<CheckBoxInput
|
||||||
|
control={form.control}
|
||||||
|
name="human_livable"
|
||||||
|
label={buildPartsTranslations[language].human_livable}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Due Part Key */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="due_part_key"
|
||||||
|
label={buildPartsTranslations[language].due_part_key}
|
||||||
|
placeholder={buildPartsTranslations[language].due_part_key}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Direction UUID */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_direction_uu_id"
|
||||||
|
label={buildPartsTranslations[language].part_direction_uu_id}
|
||||||
|
placeholder={buildPartsTranslations[language].part_direction_uu_id}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button type="submit" className="w-full">{language === 'en' ? translationsOfPage.en.title : translationsOfPage.tr.title}</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UpdateFromComponent = withCache(UpdateFromComponentBase);
|
||||||
|
export default withCache(UpdateFromComponentBase);
|
||||||
|
|
@ -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<DashboardPageProps> = 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<any>(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<BuildPartsSchemaInterface>[];
|
||||||
|
|
||||||
|
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 = <DataTable
|
||||||
|
table={table}
|
||||||
|
tableData={tableData}
|
||||||
|
isLoading={isLoading}
|
||||||
|
handleSortingChange={handleSortingChange}
|
||||||
|
getSortingIcon={getSortingIcon}
|
||||||
|
flexRender={flexRender}
|
||||||
|
t={t}
|
||||||
|
selectedBuilding={selectedBuilding}
|
||||||
|
router={router}
|
||||||
|
/>
|
||||||
|
|
||||||
|
const noSelectedBuildingComponent = <div className="p-8 text-center">
|
||||||
|
<div className="mb-4 text-gray-600">{t.noSelectedBuilding || "No building selected. Please select a building first."}</div>
|
||||||
|
<Button onClick={() => router.push(`/panel/build`)} className="bg-blue-600 text-white hover:bg-blue-700">
|
||||||
|
{t.selectBuilding || "Select Building"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
return (
|
||||||
|
isLoading ? <LoadingContent height="h-48" size="w-36 h-36" plane="h-full w-full" /> :
|
||||||
|
<div className="bg-white rounded-lg shadow-md overflow-hidden mb-20">
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center p-4">
|
||||||
|
<TableHeader
|
||||||
|
title={t.dataTable}
|
||||||
|
description={t.tableWithApiData}
|
||||||
|
isLoading={isLoading}
|
||||||
|
error={error}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
<Button onClick={() => router.push(`/panel/${activePageUrl}/create`)} className="bg-primary text-white hover:bg-primary/90">
|
||||||
|
<span className="mr-2">{t.createNewBuildPart}</span>
|
||||||
|
<span className="sr-only">{t.createNewBuildPart}</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TableForm
|
||||||
|
form={form}
|
||||||
|
handleFormSubmit={handleFormSubmit}
|
||||||
|
handleSelectChange={handleSelectChange}
|
||||||
|
renderPageOptions={renderPageOptions}
|
||||||
|
pageSizeOptions={pageSizeOptions}
|
||||||
|
apiPagination={apiPagination}
|
||||||
|
handleFirstPage={handleFirstPage}
|
||||||
|
handlePreviousPage={handlePreviousPage}
|
||||||
|
handleNextPage={handleNextPage}
|
||||||
|
isPreviousDisabled={isPreviousDisabled}
|
||||||
|
isNextDisabled={isNextDisabled}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Mobile pagination controls - only visible on small screens */}
|
||||||
|
<MobilePaginationControls
|
||||||
|
handlePreviousPage={handlePreviousPage}
|
||||||
|
handleNextPage={handleNextPage}
|
||||||
|
isPreviousDisabled={isPreviousDisabled}
|
||||||
|
isNextDisabled={isNextDisabled}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{selectedBuilding ? dataTableComponent : noSelectedBuildingComponent}
|
||||||
|
</div >
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CompanyListPage;
|
||||||
|
|
@ -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<BuildPartsSchemaInterface>();
|
||||||
|
|
||||||
|
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) => (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log('Edit button clicked');
|
||||||
|
handleEditRow && handleEditRow(info.row.original);
|
||||||
|
}}
|
||||||
|
className="h-8 w-8 p-0"
|
||||||
|
>
|
||||||
|
<Pencil className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Edit</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
const columnsArray = [
|
||||||
|
columnHelper.display({
|
||||||
|
id: 'actions',
|
||||||
|
header: () => <span>{translationsOfPage[language].actionButtonGroup}</span>,
|
||||||
|
cell: (info) => { return (actionButtonGroup(info)) }
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('address_gov_code', {
|
||||||
|
cell: info => info.getValue(),
|
||||||
|
header: () => <span>{buildPartsTranslations[language].address_gov_code}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('part_no', {
|
||||||
|
cell: info => String(info.getValue()),
|
||||||
|
header: () => <span>{buildPartsTranslations[language].part_no}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('part_level', {
|
||||||
|
cell: info => String(info.getValue()),
|
||||||
|
header: () => <span>{buildPartsTranslations[language].part_level}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('part_code', {
|
||||||
|
cell: info => info.getValue(),
|
||||||
|
header: () => <span>{buildPartsTranslations[language].part_code}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('part_gross_size', {
|
||||||
|
cell: info => String(info.getValue()),
|
||||||
|
header: () => <span>{buildPartsTranslations[language].part_gross_size}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('part_net_size', {
|
||||||
|
cell: info => String(info.getValue()),
|
||||||
|
header: () => <span>{buildPartsTranslations[language].part_net_size}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('default_accessory', {
|
||||||
|
cell: info => info.getValue(),
|
||||||
|
header: () => <span>{buildPartsTranslations[language].default_accessory}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('human_livable', {
|
||||||
|
cell: info => info.getValue() ? 'Yes' : 'No',
|
||||||
|
header: () => <span>{buildPartsTranslations[language].human_livable}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('due_part_key', {
|
||||||
|
cell: info => info.getValue(),
|
||||||
|
header: () => <span>{buildPartsTranslations[language].due_part_key}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('part_direction_uu_id', {
|
||||||
|
cell: info => info.getValue() || '',
|
||||||
|
header: () => <span>{buildPartsTranslations[language].part_direction_uu_id}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
return columnsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
retrieveColumns,
|
||||||
|
columnHelper
|
||||||
|
}
|
||||||
|
|
@ -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<typeof useReactTable>;
|
||||||
|
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<DataTableProps> = React.memo(({
|
||||||
|
table,
|
||||||
|
tableData,
|
||||||
|
isLoading,
|
||||||
|
handleSortingChange,
|
||||||
|
getSortingIcon,
|
||||||
|
flexRender,
|
||||||
|
t,
|
||||||
|
selectedBuilding,
|
||||||
|
router,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="overflow-x-auto relative">
|
||||||
|
{/* Semi-transparent loading overlay that preserves interactivity */}
|
||||||
|
{isLoading && (
|
||||||
|
<div className="absolute inset-0 bg-white bg-opacity-60 flex items-center justify-center z-10">
|
||||||
|
{/* We don't put anything here as we already have the loading indicator in the header */}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{selectedBuilding && (
|
||||||
|
<div className="mb-4 p-4 border rounded-md bg-blue-50 shadow-sm relative">
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<h3 className="text-lg font-medium text-blue-800">{t.selectedBuilding}</h3>
|
||||||
|
<Button
|
||||||
|
onClick={() => router.push(`/panel/build`)}
|
||||||
|
className="bg-red-100 hover:bg-red-200 text-red-700 p-1 h-8 w-8 rounded-full flex items-center justify-center"
|
||||||
|
title={t.changeBuilding || "Change Building"}
|
||||||
|
>
|
||||||
|
<X size={16} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">UUID: </span>
|
||||||
|
{selectedBuilding.uu_id}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{t.buildName}: </span>
|
||||||
|
{selectedBuilding.build_name}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{t.buildNo}: </span>
|
||||||
|
{selectedBuilding.build_no}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{t.buildAddressCode}: </span>
|
||||||
|
{selectedBuilding.gov_address_code}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{t.buildMaxFloor}: </span>
|
||||||
|
{selectedBuilding.max_floor}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
|
<caption className="sr-only">{t.dataTable}</caption>
|
||||||
|
<thead className="bg-gray-50">
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<tr key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => {
|
||||||
|
return (
|
||||||
|
<th
|
||||||
|
key={header.id}
|
||||||
|
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
||||||
|
onClick={() => handleSortingChange(header.column.id)}
|
||||||
|
aria-sort={header.column.getIsSorted() ? (header.column.getIsSorted() === 'desc' ? 'descending' : 'ascending') : undefined}
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-1">
|
||||||
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
|
<span>{getSortingIcon(header.column.id)}</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
|
{tableData.length === 0 && !isLoading ? (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={table.getAllColumns().length} className="px-6 py-4 text-center text-gray-500">
|
||||||
|
{t.noDataAvailable}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<tr key={row.id} className="hover:bg-gray-50">
|
||||||
|
{row.getVisibleCells().map((cell) => {
|
||||||
|
return (
|
||||||
|
<td key={cell.id} className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default DataTable;
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { MobilePaginationControlsProps } from "@/validations/mutual/table/validations";
|
||||||
|
import { Button } from "@/components/mutual/ui/button";
|
||||||
|
|
||||||
|
const MobilePaginationControls: React.FC<MobilePaginationControlsProps> = ({
|
||||||
|
handlePreviousPage,
|
||||||
|
handleNextPage,
|
||||||
|
isPreviousDisabled,
|
||||||
|
isNextDisabled,
|
||||||
|
t
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:hidden">
|
||||||
|
<div className="flex-1 flex justify-between">
|
||||||
|
<Button
|
||||||
|
onClick={handlePreviousPage}
|
||||||
|
disabled={isPreviousDisabled()}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
aria-label="Go to previous page"
|
||||||
|
>{t.previous}</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleNextPage}
|
||||||
|
disabled={isNextDisabled()}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="ml-2"
|
||||||
|
aria-label="Go to next page"
|
||||||
|
>{t.next}</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MobilePaginationControls;
|
||||||
|
|
@ -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<any>;
|
||||||
|
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<TableFormProps> = ({
|
||||||
|
form,
|
||||||
|
handleFormSubmit,
|
||||||
|
handleSelectChange,
|
||||||
|
renderPageOptions,
|
||||||
|
pageSizeOptions,
|
||||||
|
apiPagination,
|
||||||
|
handleFirstPage,
|
||||||
|
handlePreviousPage,
|
||||||
|
handleNextPage,
|
||||||
|
isPreviousDisabled,
|
||||||
|
isNextDisabled,
|
||||||
|
t
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="p-4 border-b border-gray-200">
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={handleFormSubmit} className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||||
|
<div className="md:col-span-1">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="page"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t.page}</FormLabel>
|
||||||
|
<Select
|
||||||
|
value={field.value.toString()}
|
||||||
|
onValueChange={(value: string) => handleSelectChange(value, field)}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder={t.selectPage} />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{renderPageOptions().length > 0 ? (
|
||||||
|
renderPageOptions().map((option: { key: string | number; value: string; label: string }) => (
|
||||||
|
<SelectItem key={option.key} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<SelectItem key="1" value="1">1</SelectItem>
|
||||||
|
)}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="md:col-span-1">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="size"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t.size}</FormLabel>
|
||||||
|
<Select
|
||||||
|
value={field.value.toString()}
|
||||||
|
onValueChange={(value: string) => handleSelectChange(value, field)}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder={t.selectSize} />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{pageSizeOptions.map((size: number) => (
|
||||||
|
<SelectItem key={size} value={size.toString()}>
|
||||||
|
{size}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="md:col-span-1 flex items-end">
|
||||||
|
<p className="text-sm text-gray-700">
|
||||||
|
{t.page}: <span className="font-medium">{apiPagination.page}</span><span>{" / "}</span> <span className="font-medium">{apiPagination.totalPages}</span> ·
|
||||||
|
{t.size}: <span className="font-medium">{apiPagination.pageCount}</span><span>{" / "}</span> <span className="font-medium">{apiPagination.size}</span> ·
|
||||||
|
{t.total}: <span className="font-medium">{apiPagination.totalCount}</span> {t.items}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="md:col-span-1 flex items-end justify-end space-x-2">
|
||||||
|
<Button
|
||||||
|
onClick={handleFirstPage}
|
||||||
|
disabled={isPreviousDisabled()}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
aria-label="Go to first page"
|
||||||
|
>{t.first}</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handlePreviousPage}
|
||||||
|
disabled={isPreviousDisabled()}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
aria-label="Go to previous page"
|
||||||
|
>{t.previous}</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleNextPage}
|
||||||
|
disabled={isNextDisabled()}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
aria-label="Go to next page"
|
||||||
|
>{t.next}</Button>
|
||||||
|
{/* <Button type="submit" disabled={isLoading} size="sm">Fetch</Button> */}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableForm;
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { TableHeaderProps, ErrorDisplayProps } from "@/validations/mutual/table/validations";
|
||||||
|
|
||||||
|
const ErrorDisplay: React.FC<ErrorDisplayProps> = ({ message }) => {
|
||||||
|
return <div className="text-red-500">{message}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const TableHeader: React.FC<TableHeaderProps> = ({ title, description, isLoading, error, t }) => {
|
||||||
|
return (
|
||||||
|
|
||||||
|
<div className="p-4 border-b border-gray-200">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-lg font-semibold text-gray-800">{title}</h2>
|
||||||
|
<p className="text-sm text-gray-500">{description}</p>
|
||||||
|
</div>
|
||||||
|
{/* {isLoading && <LoadingContent height="h-16" size="w-36 h-48" plane="h-full w-full" />} */}
|
||||||
|
{error && <ErrorDisplay message={error} />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableHeader;
|
||||||
|
|
@ -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 };
|
||||||
|
|
@ -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<typeof buildPartsSchema>;
|
||||||
|
type BuildPartsCreateInterface = z.infer<typeof buildPartsSchemaCreate>;
|
||||||
|
|
||||||
|
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<LanguageTypes, any> = {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
@ -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 };
|
||||||
|
|
@ -7,6 +7,8 @@ import UpdateFromBuildComponent from "./builds/superuser/UpdatePage";
|
||||||
import BuildPartsListPage from "./buildParts/superuser/main/ListPage";
|
import BuildPartsListPage from "./buildParts/superuser/main/ListPage";
|
||||||
import CreateFromBuildPartsComponent from "./buildParts/superuser/CreatePage";
|
import CreateFromBuildPartsComponent from "./buildParts/superuser/CreatePage";
|
||||||
import UpdateFromBuildPartsComponent from "./buildParts/superuser/UpdatePage";
|
import UpdateFromBuildPartsComponent from "./buildParts/superuser/UpdatePage";
|
||||||
|
import PeopleListPage from "./people/superuser/main/ListPage";
|
||||||
|
import CompanyListPage from "./company/superuser/main/ListPage";
|
||||||
|
|
||||||
const pageIndexMulti: Record<string, Record<string, React.FC<any>>> = {
|
const pageIndexMulti: Record<string, Record<string, React.FC<any>>> = {
|
||||||
"/dashboard": { DashboardPage: TableCardComponentImproved },
|
"/dashboard": { DashboardPage: TableCardComponentImproved },
|
||||||
|
|
@ -16,9 +18,12 @@ const pageIndexMulti: Record<string, Record<string, React.FC<any>>> = {
|
||||||
"/build/parts": { DashboardPage: BuildPartsListPage },
|
"/build/parts": { DashboardPage: BuildPartsListPage },
|
||||||
"/build/parts/create": { DashboardPage: CreateFromBuildPartsComponent },
|
"/build/parts/create": { DashboardPage: CreateFromBuildPartsComponent },
|
||||||
"/build/parts/update": { DashboardPage: UpdateFromBuildPartsComponent },
|
"/build/parts/update": { DashboardPage: UpdateFromBuildPartsComponent },
|
||||||
"/people": { DashboardPage: DPage },
|
"/people": { DashboardPage: PeopleListPage },
|
||||||
"/people/create": { DashboardPage: DPage },
|
"/people/create": { DashboardPage: DPage },
|
||||||
"/people/update": { DashboardPage: DPage },
|
"/people/update": { DashboardPage: DPage },
|
||||||
|
"/company": { DashboardPage: CompanyListPage },
|
||||||
|
"/company/create": { DashboardPage: DPage },
|
||||||
|
"/company/update": { DashboardPage: DPage },
|
||||||
"/decision/book": { DashboardPage: DPage },
|
"/decision/book": { DashboardPage: DPage },
|
||||||
"/decision/book/create": { DashboardPage: DPage },
|
"/decision/book/create": { DashboardPage: DPage },
|
||||||
"/decision/book/update": { DashboardPage: DPage },
|
"/decision/book/update": { DashboardPage: DPage },
|
||||||
|
|
|
||||||
|
|
@ -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<void>;
|
||||||
|
updateCache?: (url: string, data: any) => Promise<void>;
|
||||||
|
clearCache?: (url: string) => Promise<void>;
|
||||||
|
activePageUrl: string;
|
||||||
|
}) {
|
||||||
|
const language: LanguageTypes = onlineData?.lang || 'en';
|
||||||
|
const router = useRouter();
|
||||||
|
const listUrl = `/panel/${activePageUrl?.replace("/create", "")}`;
|
||||||
|
const [selectedBuilding, setSelectedBuilding] = useState<any>(null);
|
||||||
|
const [formError, setFormError] = useState<string>("");
|
||||||
|
const [cacheLoaded, setCacheLoaded] = useState<boolean>(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<any>({ 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 = <>
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||||
|
{/* ----- Basic Information ----- */}
|
||||||
|
{/* Address Government Code */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="address_gov_code"
|
||||||
|
label={buildPartsTranslations[language].address_gov_code}
|
||||||
|
placeholder={buildPartsTranslations[language].address_gov_code}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* ----- Part Identification ----- */}
|
||||||
|
{/* Part Number */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_no"
|
||||||
|
label={buildPartsTranslations[language].part_no}
|
||||||
|
placeholder={buildPartsTranslations[language].part_no}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Level */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_level"
|
||||||
|
label={buildPartsTranslations[language].part_level}
|
||||||
|
placeholder={buildPartsTranslations[language].part_level}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Code */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_code"
|
||||||
|
label={buildPartsTranslations[language].part_code}
|
||||||
|
placeholder={buildPartsTranslations[language].part_code}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* ----- Size Information ----- */}
|
||||||
|
{/* Part Gross Size */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_gross_size"
|
||||||
|
label={buildPartsTranslations[language].part_gross_size}
|
||||||
|
placeholder={buildPartsTranslations[language].part_gross_size}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Net Size */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_net_size"
|
||||||
|
label={buildPartsTranslations[language].part_net_size}
|
||||||
|
placeholder={buildPartsTranslations[language].part_net_size}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* ----- Additional Properties ----- */}
|
||||||
|
{/* Default Accessory */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="default_accessory"
|
||||||
|
label={buildPartsTranslations[language].default_accessory}
|
||||||
|
placeholder={buildPartsTranslations[language].default_accessory}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Human Livable */}
|
||||||
|
<CheckBoxInput
|
||||||
|
control={form.control}
|
||||||
|
name="human_livable"
|
||||||
|
label={buildPartsTranslations[language].human_livable}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Due Part Key */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="due_part_key"
|
||||||
|
label={buildPartsTranslations[language].due_part_key}
|
||||||
|
placeholder={buildPartsTranslations[language].due_part_key}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Direction UUID */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_direction_uu_id"
|
||||||
|
label={buildPartsTranslations[language].part_direction_uu_id}
|
||||||
|
placeholder={buildPartsTranslations[language].part_direction_uu_id}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* ----- Submit Button ----- */}
|
||||||
|
<Button type="submit" className="w-full mt-8">
|
||||||
|
{language === 'en' ? translationsOfPage.en.title : translationsOfPage.tr.title}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
|
||||||
|
const noSelectedBuildingComponent = <div className="mb-6 p-4 border rounded-md bg-yellow-50 text-center">
|
||||||
|
<div className="mb-4 text-gray-600">
|
||||||
|
{buildingTranslations[language].noSelectedBuilding}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
onClick={() => router.push(`/panel/build`)}
|
||||||
|
className="bg-blue-600 text-white hover:bg-blue-700"
|
||||||
|
>
|
||||||
|
{buildingTranslations[language].selectBuilding}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const mainComponent = <div className="mb-6 p-4 border rounded-md bg-blue-50 shadow-sm relative">
|
||||||
|
{/* Header with title and change button */}
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<h3 className="text-lg font-medium text-blue-800">
|
||||||
|
{buildingTranslations[language].selectedBuilding}
|
||||||
|
</h3>
|
||||||
|
<Button
|
||||||
|
onClick={() => router.push(`/panel/build`)}
|
||||||
|
className="bg-red-100 hover:bg-red-200 text-red-700 p-1 h-8 w-8 rounded-full flex items-center justify-center"
|
||||||
|
title={buildingTranslations[language].changeBuilding}
|
||||||
|
>
|
||||||
|
<X size={16} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Building details grid */}
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
{/* UUID */}
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">UUID: </span>
|
||||||
|
{selectedBuilding.uu_id}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Building Name */}
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{buildingTranslations[language].buildName}: </span>
|
||||||
|
{selectedBuilding.build_name}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Building Number */}
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{buildingTranslations[language].buildNo}: </span>
|
||||||
|
{selectedBuilding.build_no}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Address Code */}
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{buildingTranslations[language].buildAddressCode}: </span>
|
||||||
|
{selectedBuilding.gov_address_code}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Max Floor */}
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{buildingTranslations[language].buildMaxFloor}: </span>
|
||||||
|
{selectedBuilding.max_floor}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full max-w-2xl mx-auto p-6 bg-white rounded-lg shadow-md">
|
||||||
|
{/* back to list button */}
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<Button onClick={() => router.push(listUrl)}>{translationsOfPage[language].back2List}</Button>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-bold mb-6">{translationsOfPage[language].title}</h2>
|
||||||
|
{/* ===== BUILDING SELECTION SECTION ===== */}
|
||||||
|
{selectedBuilding ? mainComponent : noSelectedBuildingComponent}
|
||||||
|
{/* ===== FORM SECTION ===== */}
|
||||||
|
{selectedBuilding && formComponent}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CreateFromBuildComponent = withCache(CreateFromComponentBase);
|
||||||
|
export default withCache(CreateFromBuildComponent);
|
||||||
|
|
@ -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<void>;
|
||||||
|
updateCache?: (url: string, data: any) => Promise<void>;
|
||||||
|
clearCache?: (url: string) => Promise<void>;
|
||||||
|
activePageUrl: string;
|
||||||
|
}) {
|
||||||
|
const language: LanguageTypes = onlineData?.lang || 'en';
|
||||||
|
const [cacheLoaded, setCacheLoaded] = useState<boolean>(false);
|
||||||
|
const [partUUID, setPartUUID] = useState<string>(""); 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<any>({
|
||||||
|
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 (
|
||||||
|
<div className="w-full max-w-2xl mx-auto p-6 bg-white rounded-lg shadow-md">
|
||||||
|
{/* back to list button */}
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<Button onClick={() => router.push(listUrl)}>{translationsOfPage[language].back2List}</Button>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-bold mb-3">{translationsOfPage[language].title}</h2>
|
||||||
|
<h4 className="text-sm text-gray-500 mb-6">UUID: {partUUID}</h4>
|
||||||
|
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||||
|
{/* Address Government Code */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="address_gov_code"
|
||||||
|
label={buildPartsTranslations[language].address_gov_code}
|
||||||
|
placeholder={buildPartsTranslations[language].address_gov_code}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Number */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_no"
|
||||||
|
label={buildPartsTranslations[language].part_no}
|
||||||
|
placeholder={buildPartsTranslations[language].part_no}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Level */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_level"
|
||||||
|
label={buildPartsTranslations[language].part_level}
|
||||||
|
placeholder={buildPartsTranslations[language].part_level}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Code */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_code"
|
||||||
|
label={buildPartsTranslations[language].part_code}
|
||||||
|
placeholder={buildPartsTranslations[language].part_code}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Gross Size */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_gross_size"
|
||||||
|
label={buildPartsTranslations[language].part_gross_size}
|
||||||
|
placeholder={buildPartsTranslations[language].part_gross_size}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Net Size */}
|
||||||
|
<NumberInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_net_size"
|
||||||
|
label={buildPartsTranslations[language].part_net_size}
|
||||||
|
placeholder={buildPartsTranslations[language].part_net_size}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Default Accessory */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="default_accessory"
|
||||||
|
label={buildPartsTranslations[language].default_accessory}
|
||||||
|
placeholder={buildPartsTranslations[language].default_accessory}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Human Livable */}
|
||||||
|
<CheckBoxInput
|
||||||
|
control={form.control}
|
||||||
|
name="human_livable"
|
||||||
|
label={buildPartsTranslations[language].human_livable}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Due Part Key */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="due_part_key"
|
||||||
|
label={buildPartsTranslations[language].due_part_key}
|
||||||
|
placeholder={buildPartsTranslations[language].due_part_key}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Part Direction UUID */}
|
||||||
|
<StringInput
|
||||||
|
control={form.control}
|
||||||
|
name="part_direction_uu_id"
|
||||||
|
label={buildPartsTranslations[language].part_direction_uu_id}
|
||||||
|
placeholder={buildPartsTranslations[language].part_direction_uu_id}
|
||||||
|
onBlurCallback={handleFieldBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button type="submit" className="w-full">{language === 'en' ? translationsOfPage.en.title : translationsOfPage.tr.title}</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UpdateFromComponent = withCache(UpdateFromComponentBase);
|
||||||
|
export default withCache(UpdateFromComponentBase);
|
||||||
|
|
@ -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<DashboardPageProps> = 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<any>(null);
|
||||||
|
const [cacheLoaded, setCacheLoaded] = React.useState<boolean>(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<PeopleSchemaInterface>[];
|
||||||
|
|
||||||
|
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 = <DataTable
|
||||||
|
table={table}
|
||||||
|
tableData={tableData}
|
||||||
|
isLoading={isLoading}
|
||||||
|
handleSortingChange={handleSortingChange}
|
||||||
|
getSortingIcon={getSortingIcon}
|
||||||
|
flexRender={flexRender}
|
||||||
|
t={t}
|
||||||
|
selected={selectedPerson}
|
||||||
|
router={router}
|
||||||
|
/>
|
||||||
|
|
||||||
|
const noSelectedBuildingComponent = <div className="p-8 text-center">
|
||||||
|
<div className="mb-4 text-gray-600">{t.noSelectedBuilding || "No building selected. Please select a building first."}</div>
|
||||||
|
<Button onClick={() => router.push(`/panel/build`)} className="bg-blue-600 text-white hover:bg-blue-700">
|
||||||
|
{t.selectBuilding || "Select Building"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
return (
|
||||||
|
isLoading ? <LoadingContent height="h-48" size="w-36 h-36" plane="h-full w-full" /> :
|
||||||
|
<div className="bg-white rounded-lg shadow-md overflow-hidden mb-20">
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center p-4">
|
||||||
|
<TableHeader
|
||||||
|
title={t.dataTable}
|
||||||
|
description={t.tableWithApiData}
|
||||||
|
isLoading={isLoading}
|
||||||
|
error={error}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
<Button onClick={() => router.push(`/panel/${activePageUrl}/create`)} className="bg-primary text-white hover:bg-primary/90">
|
||||||
|
<span className="mr-2">{t.createNew}</span>
|
||||||
|
<span className="sr-only">{t.createNewPeople}</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TableForm
|
||||||
|
form={form}
|
||||||
|
handleFormSubmit={handleFormSubmit}
|
||||||
|
handleSelectChange={handleSelectChange}
|
||||||
|
renderPageOptions={renderPageOptions}
|
||||||
|
pageSizeOptions={pageSizeOptions}
|
||||||
|
apiPagination={apiPagination}
|
||||||
|
handleFirstPage={handleFirstPage}
|
||||||
|
handlePreviousPage={handlePreviousPage}
|
||||||
|
handleNextPage={handleNextPage}
|
||||||
|
isPreviousDisabled={isPreviousDisabled}
|
||||||
|
isNextDisabled={isNextDisabled}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Mobile pagination controls - only visible on small screens */}
|
||||||
|
<MobilePaginationControls
|
||||||
|
handlePreviousPage={handlePreviousPage}
|
||||||
|
handleNextPage={handleNextPage}
|
||||||
|
isPreviousDisabled={isPreviousDisabled}
|
||||||
|
isNextDisabled={isNextDisabled}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{selectedPerson ? dataTableComponent : noSelectedBuildingComponent}
|
||||||
|
</div >
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PeopleListPage;
|
||||||
|
|
@ -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<PeopleSchemaInterface>();
|
||||||
|
|
||||||
|
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) => (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log('Edit button clicked');
|
||||||
|
handleEditRow && handleEditRow(info.row.original);
|
||||||
|
}}
|
||||||
|
className="h-8 w-8 p-0"
|
||||||
|
>
|
||||||
|
<Pencil className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Edit</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
const columnsArray = [
|
||||||
|
columnHelper.display({
|
||||||
|
id: 'actions',
|
||||||
|
header: () => <span>{translationsOfPage[language].actionButtonGroup}</span>,
|
||||||
|
cell: (info) => { return (actionButtonGroup(info)) }
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('firstname', {
|
||||||
|
cell: info => info.getValue(),
|
||||||
|
header: () => <span>{peopleTranslations[language].firstname}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('surname', {
|
||||||
|
cell: info => String(info.getValue()),
|
||||||
|
header: () => <span>{peopleTranslations[language].surname}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('middle_name', {
|
||||||
|
cell: info => String(info.getValue()),
|
||||||
|
header: () => <span>{peopleTranslations[language].middle_name}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('sex_code', {
|
||||||
|
cell: info => info.getValue(),
|
||||||
|
header: () => <span>{peopleTranslations[language].sex_code}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('person_ref', {
|
||||||
|
cell: info => String(info.getValue()),
|
||||||
|
header: () => <span>{peopleTranslations[language].person_ref}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('person_tag', {
|
||||||
|
cell: info => String(info.getValue()),
|
||||||
|
header: () => <span>{peopleTranslations[language].person_tag}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('father_name', {
|
||||||
|
cell: info => info.getValue(),
|
||||||
|
header: () => <span>{peopleTranslations[language].father_name}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('mother_name', {
|
||||||
|
cell: info => info.getValue() ? 'Yes' : 'No',
|
||||||
|
header: () => <span>{peopleTranslations[language].mother_name}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('country_code', {
|
||||||
|
cell: info => info.getValue(),
|
||||||
|
header: () => <span>{peopleTranslations[language].country_code}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('national_identity_id', {
|
||||||
|
cell: info => info.getValue() || '',
|
||||||
|
header: () => <span>{peopleTranslations[language].national_identity_id}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('birth_place', {
|
||||||
|
cell: info => info.getValue() || '',
|
||||||
|
header: () => <span>{peopleTranslations[language].birth_place}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('birth_date', {
|
||||||
|
cell: info => info.getValue() || '',
|
||||||
|
header: () => <span>{peopleTranslations[language].birth_date}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('tax_no', {
|
||||||
|
cell: info => info.getValue() || '',
|
||||||
|
header: () => <span>{peopleTranslations[language].tax_no}</span>,
|
||||||
|
footer: info => info.column.id
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
return columnsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
retrieveColumns,
|
||||||
|
columnHelper
|
||||||
|
}
|
||||||
|
|
@ -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<typeof useReactTable>;
|
||||||
|
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<DataTableProps> = React.memo(({
|
||||||
|
table,
|
||||||
|
tableData,
|
||||||
|
isLoading,
|
||||||
|
handleSortingChange,
|
||||||
|
getSortingIcon,
|
||||||
|
flexRender,
|
||||||
|
t,
|
||||||
|
selected,
|
||||||
|
router,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="overflow-x-auto relative">
|
||||||
|
{/* Semi-transparent loading overlay that preserves interactivity */}
|
||||||
|
{isLoading && (
|
||||||
|
<div className="absolute inset-0 bg-white bg-opacity-60 flex items-center justify-center z-10">
|
||||||
|
{/* We don't put anything here as we already have the loading indicator in the header */}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{selected && (
|
||||||
|
<div className="mb-4 p-4 border rounded-md bg-blue-50 shadow-sm relative">
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<h3 className="text-lg font-medium text-blue-800">{t.selectedPerson || "Selected Person"}</h3>
|
||||||
|
<Button
|
||||||
|
onClick={() => router.push(`/panel/people`)}
|
||||||
|
className="bg-red-100 hover:bg-red-200 text-red-700 p-1 h-8 w-8 rounded-full flex items-center justify-center"
|
||||||
|
title={t.changePerson || "Change Person"}
|
||||||
|
>
|
||||||
|
<X size={16} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">ID: </span>
|
||||||
|
{selected.national_identity_id || '-'}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{t.name || "Name"}: </span>
|
||||||
|
{selected.firstname || '-'}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{t.surname || "Surname"}: </span>
|
||||||
|
{selected.surname || '-'}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{t.birthDate || "Birth Date"}: </span>
|
||||||
|
{selected.birth_date || '-'}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold">{t.taxNo || "Tax No"}: </span>
|
||||||
|
{selected.tax_no || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<table className="min-w-full divide-y divide-gray-200">
|
||||||
|
<caption className="sr-only">{t.dataTable}</caption>
|
||||||
|
<thead className="bg-gray-50">
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<tr key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => {
|
||||||
|
return (
|
||||||
|
<th
|
||||||
|
key={header.id}
|
||||||
|
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer"
|
||||||
|
onClick={() => handleSortingChange(header.column.id)}
|
||||||
|
aria-sort={header.column.getIsSorted() ? (header.column.getIsSorted() === 'desc' ? 'descending' : 'ascending') : undefined}
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-1">
|
||||||
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
|
<span>{getSortingIcon(header.column.id)}</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
|
{tableData.length === 0 && !isLoading ? (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={table.getAllColumns().length} className="px-6 py-4 text-center text-gray-500">
|
||||||
|
{t.noDataAvailable}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<tr key={row.id} className="hover:bg-gray-50">
|
||||||
|
{row.getVisibleCells().map((cell) => {
|
||||||
|
return (
|
||||||
|
<td key={cell.id} className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default DataTable;
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { MobilePaginationControlsProps } from "@/validations/mutual/table/validations";
|
||||||
|
import { Button } from "@/components/mutual/ui/button";
|
||||||
|
|
||||||
|
const MobilePaginationControls: React.FC<MobilePaginationControlsProps> = ({
|
||||||
|
handlePreviousPage,
|
||||||
|
handleNextPage,
|
||||||
|
isPreviousDisabled,
|
||||||
|
isNextDisabled,
|
||||||
|
t
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:hidden">
|
||||||
|
<div className="flex-1 flex justify-between">
|
||||||
|
<Button
|
||||||
|
onClick={handlePreviousPage}
|
||||||
|
disabled={isPreviousDisabled()}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
aria-label="Go to previous page"
|
||||||
|
>{t.previous}</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleNextPage}
|
||||||
|
disabled={isNextDisabled()}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="ml-2"
|
||||||
|
aria-label="Go to next page"
|
||||||
|
>{t.next}</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MobilePaginationControls;
|
||||||
|
|
@ -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<any>;
|
||||||
|
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<TableFormProps> = ({
|
||||||
|
form,
|
||||||
|
handleFormSubmit,
|
||||||
|
handleSelectChange,
|
||||||
|
renderPageOptions,
|
||||||
|
pageSizeOptions,
|
||||||
|
apiPagination,
|
||||||
|
handleFirstPage,
|
||||||
|
handlePreviousPage,
|
||||||
|
handleNextPage,
|
||||||
|
isPreviousDisabled,
|
||||||
|
isNextDisabled,
|
||||||
|
t
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="p-4 border-b border-gray-200">
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={handleFormSubmit} className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||||
|
<div className="md:col-span-1">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="page"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t.page}</FormLabel>
|
||||||
|
<Select
|
||||||
|
value={field.value.toString()}
|
||||||
|
onValueChange={(value: string) => handleSelectChange(value, field)}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder={t.selectPage} />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{renderPageOptions().length > 0 ? (
|
||||||
|
renderPageOptions().map((option: { key: string | number; value: string; label: string }) => (
|
||||||
|
<SelectItem key={option.key} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<SelectItem key="1" value="1">1</SelectItem>
|
||||||
|
)}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="md:col-span-1">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="size"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t.size}</FormLabel>
|
||||||
|
<Select
|
||||||
|
value={field.value.toString()}
|
||||||
|
onValueChange={(value: string) => handleSelectChange(value, field)}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder={t.selectSize} />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{pageSizeOptions.map((size: number) => (
|
||||||
|
<SelectItem key={size} value={size.toString()}>
|
||||||
|
{size}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="md:col-span-1 flex items-end">
|
||||||
|
<p className="text-sm text-gray-700">
|
||||||
|
{t.page}: <span className="font-medium">{apiPagination.page}</span><span>{" / "}</span> <span className="font-medium">{apiPagination.totalPages}</span> ·
|
||||||
|
{t.size}: <span className="font-medium">{apiPagination.pageCount}</span><span>{" / "}</span> <span className="font-medium">{apiPagination.size}</span> ·
|
||||||
|
{t.total}: <span className="font-medium">{apiPagination.totalCount}</span> {t.items}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="md:col-span-1 flex items-end justify-end space-x-2">
|
||||||
|
<Button
|
||||||
|
onClick={handleFirstPage}
|
||||||
|
disabled={isPreviousDisabled()}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
aria-label="Go to first page"
|
||||||
|
>{t.first}</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handlePreviousPage}
|
||||||
|
disabled={isPreviousDisabled()}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
aria-label="Go to previous page"
|
||||||
|
>{t.previous}</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleNextPage}
|
||||||
|
disabled={isNextDisabled()}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
aria-label="Go to next page"
|
||||||
|
>{t.next}</Button>
|
||||||
|
{/* <Button type="submit" disabled={isLoading} size="sm">Fetch</Button> */}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableForm;
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { TableHeaderProps, ErrorDisplayProps } from "@/validations/mutual/table/validations";
|
||||||
|
|
||||||
|
const ErrorDisplay: React.FC<ErrorDisplayProps> = ({ message }) => {
|
||||||
|
return <div className="text-red-500">{message}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const TableHeader: React.FC<TableHeaderProps> = ({ title, description, isLoading, error, t }) => {
|
||||||
|
return (
|
||||||
|
|
||||||
|
<div className="p-4 border-b border-gray-200">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-lg font-semibold text-gray-800">{title}</h2>
|
||||||
|
<p className="text-sm text-gray-500">{description}</p>
|
||||||
|
</div>
|
||||||
|
{/* {isLoading && <LoadingContent height="h-16" size="w-36 h-48" plane="h-full w-full" />} */}
|
||||||
|
{error && <ErrorDisplay message={error} />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableHeader;
|
||||||
|
|
@ -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 };
|
||||||
|
|
@ -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<typeof peopleSchema>;
|
||||||
|
|
||||||
|
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<LanguageTypes, any> = {
|
||||||
|
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 };
|
||||||
|
|
@ -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 };
|
||||||
Loading…
Reference in New Issue