Buildin Page tested

This commit is contained in:
berkay 2025-01-02 16:17:30 +03:00
parent 4bf79cff55
commit 24d2169132
29 changed files with 1796 additions and 50 deletions

View File

@ -39,7 +39,6 @@ class FilterList {
this.orderType = this.orderType.startsWith("a") ? "asc" : "desc"; this.orderType = this.orderType.startsWith("a") ? "asc" : "desc";
this.includeJoins = includeJoins ?? []; this.includeJoins = includeJoins ?? [];
this.query = query ?? {}; this.query = query ?? {};
} }
filter() { filter() {
return { return {

View File

@ -13,6 +13,7 @@ const buildUpdateEndpoint = `${baseUrl}/building/build/update`;
async function retrieveBuildList(payload: FilterListInterface) { async function retrieveBuildList(payload: FilterListInterface) {
const feedObject = new FilterList(payload).filter(); const feedObject = new FilterList(payload).filter();
console.log("feedObject", feedObject);
const tokenResponse: any = await fetchDataWithToken( const tokenResponse: any = await fetchDataWithToken(
buildListEndpoint, buildListEndpoint,
feedObject, feedObject,

View File

@ -0,0 +1,22 @@
"use server";
import { fetchDataWithToken } from "./api-fetcher";
import { baseUrl } from "./basics";
const accessAvailableEndpoint = `${baseUrl}/access/endpoint/available`;
async function retrieveAvailableEndpoint(payload: string) {
const tokenResponse: any = await fetchDataWithToken(
accessAvailableEndpoint,
{
endpoint: payload,
},
"POST",
false
);
if (tokenResponse.status === 200) {
return true;
}
return false;
}
export { retrieveAvailableEndpoint };

View File

@ -105,7 +105,9 @@ async function retrieveUserSelection() {
const avatarInfo = await retrieveAvatarInfo(); const avatarInfo = await retrieveAvatarInfo();
return { return {
...objectUserSelection, ...objectUserSelection,
lang: String(avatarInfo?.data?.lang).toLowerCase(), lang: avatarInfo?.data?.lang
? String(avatarInfo?.data?.lang).toLowerCase()
: undefined,
avatar: avatarInfo?.data?.avatar, avatar: avatarInfo?.data?.avatar,
fullName: avatarInfo?.data?.full_name, fullName: avatarInfo?.data?.full_name,
}; };

View File

@ -11,6 +11,7 @@ interface LoginOutUser {
} }
async function logoutActiveSession(payload: LoginOutUser) { async function logoutActiveSession(payload: LoginOutUser) {
"use server";
const cookieStore = await cookies(); const cookieStore = await cookies();
cookieStore.delete("accessToken"); cookieStore.delete("accessToken");
cookieStore.delete("accessObject"); cookieStore.delete("accessObject");

View File

@ -52,10 +52,12 @@ export async function handleFormSubmission(formData: FormData): Promise<void> {
} else if (key.includes("section")) { } else if (key.includes("section")) {
} else if (key.includes("ACTION_ID")) { } else if (key.includes("ACTION_ID")) {
} else { } else {
inputs.query = { if (value) {
...inputs.query, inputs.query = {
[key]: value, ...inputs.query,
}; [key]: value,
};
}
} }
}); });
const queryEncrypt = await encryptQuery(inputs); const queryEncrypt = await encryptQuery(inputs);
@ -63,3 +65,25 @@ export async function handleFormSubmission(formData: FormData): Promise<void> {
`/${formData.get("section")}?q=${queryEncrypt.replaceAll(" ", "+")}` `/${formData.get("section")}?q=${queryEncrypt.replaceAll(" ", "+")}`
); );
} }
export async function handleCreateSubmission({
section,
data,
}: {
section: string;
data: any;
}) {
const queryEncrypt = await encryptQuery(data);
redirect(`/${section}/create?q=${queryEncrypt.replaceAll(" ", "+")}`);
}
export async function handleUpdateSubmission({
section,
data,
}: {
section: string;
data: any;
}) {
const queryEncrypt = await encryptQuery(data);
}

View File

@ -1,7 +1,6 @@
"use server"; "use server";
import { fetchData, fetchDataWithToken } from "@/apicalls/api-fetcher"; import { fetchData, fetchDataWithToken } from "@/apicalls/api-fetcher";
import { baseUrl, cookieObject, tokenSecret } from "@/apicalls/basics"; import { baseUrl, cookieObject, tokenSecret } from "@/apicalls/basics";
import { HeadersAndValidations } from "@/apicalls/validations/validationProcesser"; import { HeadersAndValidations } from "@/apicalls/validations/validationProcesser";
const headersAndValidationEndpoint = `${baseUrl}/validations/endpoint`; const headersAndValidationEndpoint = `${baseUrl}/validations/endpoint`;
@ -28,15 +27,14 @@ async function retrieveHeadersEndpoint({ endpoint }: EndpointInterface) {
} }
return { return {
status: selectResponse.status, status: selectResponse.status,
headers: {},
message: selectResponse.message, message: selectResponse.message,
headers: {},
}; };
} }
async function retrieveHeadersAndValidationByEndpoint({ async function retrieveHeadersAndValidationByEndpoint({
endpoint, endpoint,
}: EndpointInterface) { }: EndpointInterface) {
console.log("endpoint", endpoint);
const selectResponse: any = await fetchDataWithToken( const selectResponse: any = await fetchDataWithToken(
headersAndValidationEndpoint, headersAndValidationEndpoint,
{ {
@ -58,7 +56,6 @@ async function retrieveHeadersAndValidationByEndpoint({
return { return {
status: selectResponse.status, status: selectResponse.status,
message: selectResponse.message, message: selectResponse.message,
headers: null, headers: null,
validated: null, validated: null,
}; };

View File

@ -2,6 +2,7 @@ const BuildPageInfo = {
tr: [ tr: [
{ {
title: "Bina Listesi", title: "Bina Listesi",
name: "table",
icon: null, icon: null,
description: "Bina listeyebilirsiniz", description: "Bina listeyebilirsiniz",
endpoint: "/building/build/list", endpoint: "/building/build/list",
@ -10,6 +11,7 @@ const BuildPageInfo = {
{ {
title: "Bina Ekle", title: "Bina Ekle",
icon: "BadgePlus", icon: "BadgePlus",
name: "create",
description: "Bina oluşturma sayfasına hoş geldiniz", description: "Bina oluşturma sayfasına hoş geldiniz",
endpoint: "/building/build/create", endpoint: "/building/build/create",
component: "AddCreate2Table", component: "AddCreate2Table",
@ -17,6 +19,7 @@ const BuildPageInfo = {
{ {
title: null, title: null,
icon: "Pencil", icon: "Pencil",
name: "update",
description: "Bina güncelleme sayfasına hoş geldiniz", description: "Bina güncelleme sayfasına hoş geldiniz",
endpoint: "/building/build/update/{build_uu_id}", endpoint: "/building/build/update/{build_uu_id}",
component: "AddUpdate2Table", component: "AddUpdate2Table",

View File

@ -18,18 +18,19 @@ import { BuildPageInfo, BuildAllEndpoints } from "./building/pageInfo";
const PagesInfosAndEndpoints = [ const PagesInfosAndEndpoints = [
{ {
name: "BuildingPage",
title: { title: {
tr: "Binalar", tr: "Binalar",
en: "Buildings", en: "Buildings",
}, },
icon: "Hotel", icon: "Hotel",
// component: "/build/page", url: "/building",
url: "/building?page=1",
pageInfo: BuildPageInfo, pageInfo: BuildPageInfo,
allEndpoints: BuildAllEndpoints, allEndpoints: BuildAllEndpoints,
subCategories: BuildCategories, subCategories: BuildCategories,
}, },
{ {
name: "",
title: { title: {
tr: "Toplantılar", tr: "Toplantılar",
en: "Meetings", en: "Meetings",
@ -42,6 +43,7 @@ const PagesInfosAndEndpoints = [
subCategories: MeetingSubCategories, subCategories: MeetingSubCategories,
}, },
{ {
name: "",
title: { title: {
tr: "Cari Hesaplar", tr: "Cari Hesaplar",
en: "Accounts", en: "Accounts",
@ -54,6 +56,7 @@ const PagesInfosAndEndpoints = [
subCategories: AccountSubCategories, subCategories: AccountSubCategories,
}, },
{ {
name: "",
title: { title: {
tr: "Karar Defteri", tr: "Karar Defteri",
en: "Decision Book", en: "Decision Book",
@ -66,6 +69,7 @@ const PagesInfosAndEndpoints = [
subCategories: DecisionBookSubCategories, subCategories: DecisionBookSubCategories,
}, },
{ {
name: "",
title: { title: {
tr: "Kimlikler", tr: "Kimlikler",
en: "Identities", en: "Identities",
@ -78,6 +82,7 @@ const PagesInfosAndEndpoints = [
subCategories: IdentityCategories, subCategories: IdentityCategories,
}, },
{ {
name: "",
title: { title: {
tr: "Erişilebilirlik", tr: "Erişilebilirlik",
en: "Accessibility", en: "Accessibility",
@ -90,6 +95,7 @@ const PagesInfosAndEndpoints = [
subCategories: AccesibleCategories, subCategories: AccesibleCategories,
}, },
{ {
name: "",
title: { title: {
tr: "Firmalar", tr: "Firmalar",
en: "Companies", en: "Companies",

68
src/Icons/icons.tsx Normal file
View File

@ -0,0 +1,68 @@
"use client";
import {
Hotel,
Logs,
Landmark,
ScrollText,
UserPlus,
Cog,
Store,
BadgePlus,
Pencil,
} from "lucide-react";
import { DoorOpen, TreePine, UsersRound } from "lucide-react";
import { ClipboardList, ClipboardCheck } from "lucide-react";
import { LucideLandmark } from "lucide-react";
import {
Projector,
FolderKey,
FolderCog,
Stamp,
FolderCheck,
} from "lucide-react";
import { PersonStanding, MapPinned, ScanSearch, Container } from "lucide-react";
import { PackageCheck } from "lucide-react";
import {
FolderOpenDot,
BriefcaseMedical,
Pickaxe,
BicepsFlexed,
} from "lucide-react";
const AllIcons = {
Hotel,
Logs,
Landmark,
ScrollText,
UserPlus,
Cog,
Store,
DoorOpen,
TreePine,
UsersRound,
ClipboardList,
ClipboardCheck,
LucideLandmark,
Projector,
FolderKey,
FolderCog,
Stamp,
FolderCheck,
PersonStanding,
MapPinned,
ScanSearch,
Container,
PackageCheck,
FolderOpenDot,
BriefcaseMedical,
Pickaxe,
BicepsFlexed,
BadgePlus,
Pencil,
};
function getIconByName(name: string) {
return Object.entries(AllIcons).find(([key]) => key === name)?.[1] ?? Pencil;
}
export { AllIcons, getIconByName };

View File

@ -1,8 +1,8 @@
"use server"; "use server";
import React from "react"; import React from "react";
import { RefreshCcw } from "lucide-react"; import { RefreshCcw } from "lucide-react";
import Pagination from "./pagination"; import Pagination from "../../components/commons/pagination";
import TableComponent from "./table"; import TableComponent from "../../components/commons/table";
import { import {
decryptQuery, decryptQuery,
defaultPagination, defaultPagination,
@ -11,16 +11,16 @@ import {
import MainBodyWithHeader from "@/components/defaultLayout/MainBodyWithHeader"; import MainBodyWithHeader from "@/components/defaultLayout/MainBodyWithHeader";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
const DashboardPage = async ({ searchParams }: { searchParams: any }) => { const AccountsPage = async ({ searchParams }: { searchParams: any }) => {
const searchParamsKeys = await searchParams; const searchParamsKeys = await searchParams;
if (!searchParamsKeys?.q) { if (!searchParamsKeys?.q) {
const defaultURL = await defaultPagination(); const defaultURL = await defaultPagination();
console.log(defaultURL);
redirect(`/accounts?q=${defaultURL}`); redirect(`/accounts?q=${defaultURL}`);
} }
const queryEncrypt = await decryptQuery( const queryEncrypt = await decryptQuery(
searchParamsKeys?.q.replace(/ /g, "+") searchParamsKeys?.q.replace(/ /g, "+")
); );
const accountPage = ( const accountPage = (
<div className="p-4 overflow-hidden"> <div className="p-4 overflow-hidden">
<h1 className="text-2xl font-bold mb-4 ">Dashboard</h1> <h1 className="text-2xl font-bold mb-4 ">Dashboard</h1>
@ -54,4 +54,4 @@ const DashboardPage = async ({ searchParams }: { searchParams: any }) => {
return <MainBodyWithHeader children={accountPage} />; return <MainBodyWithHeader children={accountPage} />;
}; };
export default DashboardPage; export default AccountsPage;

View File

@ -1,27 +0,0 @@
"use client";
interface TableComponentInterFace {
inputHeaders: any;
}
const TableComponent: React.FC<TableComponentInterFace> = ({
inputHeaders,
}) => {
return (
<div className="grid grid-cols-2 gap-4 mb-4">
{Object.entries(inputHeaders).map(([key, value]) => (
<div key={key}>
<h1>{key} : </h1>
<input
type="text"
className="border p-2 rounded"
placeholder={`${key}`}
name={key}
defaultValue={String(value) || ""}
/>
</div>
))}
</div>
);
};
export default TableComponent;

View File

@ -0,0 +1,104 @@
"use client";
import { RetrieveInputByType } from "@/hooks/renderInputWithValidation";
import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
FormDescription,
} from "@/components/ui/form";
import { convertApiValidationToZodValidation } from "@/lib/renderZodValidation";
interface CreatePageComponentInterface {
validator: any;
headers: any;
}
const CreatePageComponent: React.FC<CreatePageComponentInterface> = ({
validator,
headers,
}) => {
const returnValidation = convertApiValidationToZodValidation(validator);
const { validSchemaZod, zodValidation, apiValidation } = returnValidation;
console.log("validSchemaZod", {
validSchemaZod,
zodValidation,
apiValidation,
validator,
headers,
});
const form = useForm<z.infer<typeof validSchemaZod>>({
resolver: zodResolver(validSchemaZod),
defaultValues: {},
});
function submitUpdate(formData: z.infer<typeof validSchemaZod>) {
// saveFunction({
// uu_id: updateUUID,
// payload: validDataParser(formData),
// }).then((res: any) => {
// console.log(res);
// if (res?.status === 200) {
// } else {
// alert("Güncelleme başarısız");
// }
// });
}
return (
<>
<div className="container mx-auto p-4">
<Form {...form}>
<form action="">
{Object.entries(validator).map(([key, value]: [string, any]) => (
<FormField
key={key}
control={form.control}
name={String(key)}
render={({ field }) => {
return (
<FormItem>
<FormLabel>
{headers[key] || `Header not found ${key}`}
</FormLabel>
<FormControl>
{RetrieveInputByType({
type: value?.fieldType || "string",
props: {
className: "",
field: field,
placeholder: headers[key],
required: value?.required || false,
},
})}
</FormControl>
{String(form.formState.errors[key]?.type) ===
"invalid_type" ? (
<span id={key} className="text-red-700">
"Lütfen metinsel bir değer giriniz"
</span>
) : (
<></>
)}
</FormItem>
);
}}
/>
))}
<button type="submit" className="mt-4">
Submit
</button>
</form>
</Form>
</div>
</>
);
};
export default CreatePageComponent;

View File

@ -0,0 +1,40 @@
"use server";
import { retrieveAvailableEndpoint } from "@/apicalls/checkEndpoint";
import { checkAccessTokenIsValid } from "@/apicalls/cookies/token";
import { decryptQuery, defaultPagination } from "@/apicalls/test";
import { retrieveHeadersAndValidationByEndpoint } from "@/apicalls/validations/validations";
import { redirect } from "next/navigation";
import CreatePageComponent from "./CreatePage";
export default async function BuildingCreatePage({
searchParams,
}: {
searchParams: any;
}) {
if (!(await checkAccessTokenIsValid())) {
redirect("/login/email");
}
const buildKey = "building";
const searchParamsKeys = await searchParams;
const endpointUrl = "/building/build/create";
const queryEncrypt = await decryptQuery(searchParamsKeys?.q);
const endpointAvailable = await retrieveAvailableEndpoint(endpointUrl);
const validateAndHeaders = await retrieveHeadersAndValidationByEndpoint({
endpoint: endpointUrl,
});
const validator = validateAndHeaders?.validated || {};
const headers = validateAndHeaders?.headers || {};
console.log("validateAndHeaders", validateAndHeaders);
console.log("endpointAvailable", endpointAvailable);
console.log("queryEncrypt", queryEncrypt);
return (
<div>
<h1>Create Building</h1>
<h1>{JSON.stringify(queryEncrypt)}</h1>
<CreatePageComponent validator={validator} headers={headers} />
</div>
);
}

View File

@ -1,10 +1,154 @@
"use server"; "use server";
import React from "react"; import React, { Suspense } from "react";
import MainBodyWithHeader from "@/components/defaultLayout/MainBodyWithHeader"; import Link from "next/link";
import BuildChildComponent from "@/pages/Build/Build"; import { RefreshCcw, PlusCircle } from "lucide-react";
const Page = () => { import MainBodyWithHeader from "@/components/defaultLayout/MainBodyWithHeader";
return <MainBodyWithHeader children={<BuildChildComponent />} />; import {
decryptQuery,
defaultPagination,
handleFormSubmission,
} from "@/apicalls/test";
import { redirect } from "next/navigation";
import TableComponent from "@/components/commons/table";
import Pagination from "@/components/commons/pagination";
import {
createBuild,
retrieveBuildList,
updateBuild,
} from "@/apicalls/building/build";
import { retrieveHeadersAndValidationByEndpoint } from "@/apicalls/validations/validations";
import {
checkAccessTokenIsValid,
retrieveUserSelection,
} from "@/apicalls/cookies/token";
import { retrievePageInfoByComponentName } from "@/hooks/retrievePageInfoByComponentName";
import { retrieveAvailableEndpoint } from "@/apicalls/checkEndpoint";
import { checkPageAvaliable } from "@/hooks/checkpageAvaliable";
import { logoutActiveSession } from "@/apicalls/login/logout";
const BuildinPage = async ({ searchParams }: { searchParams: any }) => {
const buildKey = "building";
const pageName = "BuildingPage";
const searchParamsKeys = await searchParams;
if (!searchParamsKeys?.q) {
const defaultURL = await defaultPagination();
redirect(`/${buildKey}?q=${defaultURL}`);
}
const queryEncrypt = await decryptQuery(searchParamsKeys?.q);
if (!(await checkAccessTokenIsValid())) {
redirect("/login/email");
}
const tableValues = {
endpoint: "building/build/list",
name: "table",
url: "/building",
function: retrieveBuildList,
data: [],
headers: {},
validation: {},
};
const createValues = {
endpoint: "building/build/create",
name: "create",
url: "/building/create",
function: createBuild,
data: [],
headers: {},
validation: {},
};
const updateValues = {
endpoint: "building/build/update/{build_uu_id}",
function: updateBuild,
name: "update",
url: "/building/update",
data: [],
headers: {},
validation: {},
};
let restrictions: any = {
update: updateValues,
create: createValues,
table: tableValues,
};
const user = await retrieveUserSelection();
if (!user?.lang) {
await logoutActiveSession({ domain: "evyos.com.tr" });
redirect("/login/email");
}
const pageContent = await retrievePageInfoByComponentName(
pageName,
user?.lang
);
const restrictionsChecked = await checkPageAvaliable({
pageContent,
restrictions,
queryEncrypt,
});
if (!restrictionsChecked || !restrictionsChecked?.table) {
redirect("/home");
}
const BuildingPage = (
<div className="p-4 overflow-hidden">
<h1 className="text-2xl font-bold mb-4 ">Dashboard</h1>
<form
action={handleFormSubmission}
className="bg-white p-4 rounded-lg shadow"
>
<div className="grid gap-4">
<p>Welcome to your dashboard</p>
{restrictionsChecked?.create && (
<Link
href={"/building/create"}
className="flex items-center justify-center gap-2 px-4 py-2 bg-slate-500 text-white rounded hover:bg-slate-700"
>
<PlusCircle size={16} />
Create
</Link>
)}
<h1>{JSON.stringify(queryEncrypt)}</h1>
{restrictionsChecked && (
<>
<input type="hidden" name="section" value={buildKey} readOnly />
<TableComponent
restrictions={restrictionsChecked}
query={queryEncrypt?.query || {}}
/>
</>
)}
<button
type="submit"
className="flex items-center justify-center gap-2 px-4 py-2 bg-slate-500 text-white rounded hover:bg-slate-700"
>
<RefreshCcw size={16} />
Search
</button>
<Pagination
size={parseInt(queryEncrypt?.size || "10")}
page={parseInt(queryEncrypt?.page || "1")}
orderBy={queryEncrypt?.orderBy || "id"}
orderType={queryEncrypt?.orderType || "asc"}
totalPage={3}
/>
</div>
</form>
</div>
);
return (
<>
<Suspense fallback={<div>Loading...</div>}>
<MainBodyWithHeader children={BuildingPage} />
</Suspense>
</>
);
}; };
export default Page; export default BuildinPage;

View File

@ -0,0 +1,110 @@
"use client";
import { RetrieveInputByType } from "@/hooks/renderInputWithValidation";
import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
FormDescription,
} from "@/components/ui/form";
import { convertApiValidationToZodValidation } from "@/lib/renderZodValidation";
interface UpdatePageInterface {
validator: any;
headers: any;
queryEncrypt: any;
}
const UpdatePageComponent: React.FC<UpdatePageInterface> = ({
validator,
headers,
queryEncrypt,
}) => {
const returnValidation = convertApiValidationToZodValidation(validator);
const { validSchemaZod, zodValidation, apiValidation } = returnValidation;
console.log("validSchemaZod", {
validSchemaZod,
zodValidation,
apiValidation,
validator,
headers,
queryEncrypt,
});
const form = useForm<z.infer<typeof validSchemaZod>>({
resolver: zodResolver(validSchemaZod),
defaultValues: {
...queryEncrypt,
},
});
function submitUpdate(formData: z.infer<typeof validSchemaZod>) {
const updateUUID = queryEncrypt?.uu_id;
// saveFunction({
// uu_id: updateUUID,
// payload: validDataParser(formData),
// }).then((res: any) => {
// console.log(res);
// if (res?.status === 200) {
// } else {
// alert("Güncelleme başarısız");
// }
// });
}
return (
<>
<div className="container mx-auto p-4">
<Form {...form}>
<form action="">
{Object.entries(validator).map(([key, value]: [string, any]) => (
<FormField
key={key}
control={form.control}
name={String(key)}
render={({ field }) => {
return (
<FormItem>
<FormLabel>
{headers[key] || `Header not found ${key}`}
</FormLabel>
<FormControl>
{RetrieveInputByType({
type: value?.fieldType || "string",
props: {
className: "",
field: field,
placeholder: headers[key],
required: value?.required || false,
},
})}
</FormControl>
{String(form.formState.errors[key]?.type) ===
"invalid_type" ? (
<span id={key} className="text-red-700">
"Lütfen metinsel bir değer giriniz"
</span>
) : (
<></>
)}
</FormItem>
);
}}
/>
))}
<button type="submit" className="mt-4">
Submit
</button>
</form>
</Form>
</div>
</>
);
};
export default UpdatePageComponent;

View File

@ -0,0 +1,63 @@
"use server";
import { updateBuild } from "@/apicalls/building/build";
import { retrieveAvailableEndpoint } from "@/apicalls/checkEndpoint";
import { checkAccessTokenIsValid } from "@/apicalls/cookies/token";
import { decryptQuery, defaultPagination } from "@/apicalls/test";
import { retrieveHeadersAndValidationByEndpoint } from "@/apicalls/validations/validations";
import { redirect } from "next/navigation";
import { RetrieveInputByType } from "@/hooks/renderInputWithValidation";
import React from "react";
import UpdatePageComponent from "./UpdatePage";
export default async function BuildingUpdatePage({
searchParams,
}: {
searchParams: any;
}) {
if (!(await checkAccessTokenIsValid())) {
redirect("/login/email");
}
const buildKey = "building/update";
const searchParamsKeys = await searchParams;
const endpointUrl = "building/build/update/{build_uu_id}";
if (!searchParamsKeys?.q) {
redirect(`/${buildKey}`);
}
const queryEncrypt = await decryptQuery(searchParamsKeys?.q);
const updateValues = {
endpoint: "building/build/update/{build_uu_id}",
function: updateBuild,
name: "update",
url: "/building/update",
data: [],
headers: {},
validation: {},
};
const endpointAvailable = await retrieveAvailableEndpoint(endpointUrl);
const validateAndHeaders = await retrieveHeadersAndValidationByEndpoint({
endpoint: endpointUrl,
});
const validator = validateAndHeaders?.validated || {};
const headers = validateAndHeaders?.headers || {};
console.log("endpointAvailable", endpointAvailable);
console.log("validator", validator);
console.log("headers", headers);
console.log("queryEncrypt", queryEncrypt);
return (
<div>
<h1>Update Building</h1>
<h1>{JSON.stringify(queryEncrypt)}</h1>
<UpdatePageComponent
validator={validator}
headers={headers}
queryEncrypt={queryEncrypt}
/>
</div>
);
}

View File

@ -62,6 +62,29 @@ body {
} }
} }
.loading-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f5f5f5;
}
.spinner {
width: 50px;
height: 50px;
border: 5px solid rgba(0, 0, 0, 0.1);
border-top: 5px solid #0070f3;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@layer base { @layer base {
* { * {
@apply border-border; @apply border-border;

View File

@ -0,0 +1,192 @@
"use client";
import React from "react";
import {
useReactTable,
flexRender,
getCoreRowModel,
createColumnHelper,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
TableFooter,
} from "@/components/ui/table";
import { ArrowUpDown, ArrowUp, ArrowDown } from "lucide-react";
import { getIconByName } from "@/Icons/icons";
import { useRouter } from "next/navigation";
import { encryptQuery, handleUpdateSubmission } from "@/apicalls/test";
interface TableComponentInterFace {
restrictions: any;
query: any;
}
const TableComponent: React.FC<TableComponentInterFace> = ({
restrictions,
query,
}) => {
const router = useRouter();
const [updateRow, setUpdateRow] = React.useState<any>(null);
const [columns, setColumns] = React.useState<any[]>([]);
const columnHelper = createColumnHelper();
const table = useReactTable({
data: restrictions.table?.data?.data || [],
columns,
getCoreRowModel: getCoreRowModel(),
});
React.useEffect(() => {
if (restrictions?.table?.headers) {
setColumns(createColumnsFromValidations(restrictions.table.headers));
}
}, [restrictions.table.headers]);
React.useEffect(() => {
if (updateRow) {
encryptQuery(updateRow).then((encryptData) => {
router.push(`/building/update?q=${encryptData.replaceAll(" ", "+")}`);
});
}
}, [updateRow]);
function createColumnsFromValidations(headers: any) {
const columns = Object.entries(headers).map(([key]: [string, any]) => {
return columnHelper.accessor(key, {
id: key,
footer: headers[key],
header: () => <span>{headers[key]}</span>,
cell: (info) => <span>{info.getValue()}</span>,
});
});
if (restrictions?.update) {
columns.push(
columnHelper.accessor("update", {
id: "update",
footer: "Update",
header: () => <span>Update</span>,
cell: () => (
<div className="w-8 h-8 cursor-pointer">
{restrictions?.update.icon &&
React.createElement(getIconByName(restrictions?.update.icon))}
</div>
),
})
);
}
return columns;
}
return (
<>
<div className="w-full min-w-full gap-4 mb-4">
<h1>{JSON.stringify(updateRow)}</h1>
<Table className="px-8">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id} className="relative">
<div className="flex items-center gap-2 w-full">
{/* {header.id !== "update" && (
<ArrowUpDown
className="h-4 w-4 cursor-pointer"
onClick={() => changeOrderState(header.id)}
/>
)} */}
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
{/* {tableInfo.field === header.id &&
header.id !== "update" &&
(tableInfo.type.startsWith("a") ? (
<ArrowUp className="absolute right-0 h-4 w-4 cursor-pointer" />
) : (
<ArrowDown className="absolute right-0 h-4 w-4 cursor-pointer" />
))} */}
</div>
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) =>
cell.column.id !== "update" ? (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
) : (
<TableCell
key={cell.id}
onClick={() => setUpdateRow(row.original)}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
)
)}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
<TableFooter>
{table.getFooterGroups().map((footerGroup) => (
<TableRow key={footerGroup.id}>
{footerGroup.headers.map(
(footer) =>
footer.id !== "update" && (
<TableCell key={footer.id}>
{footer.isPlaceholder ? null : (
<input
key={footer.id}
name={footer.id}
type="text"
className="w-full text-center border p-1 text-sm h-8"
placeholder={`${String(
footer.column.columnDef.footer
)}`}
defaultValue={query[footer.id] || ""}
/>
)}
</TableCell>
)
)}
</TableRow>
))}
</TableFooter>
</Table>
</div>
</>
);
};
export default TableComponent;

View File

@ -0,0 +1,551 @@
"use client";
import React from "react";
import { parseDate } from "chrono-node";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ActiveModifiers } from "react-day-picker";
import { Calendar, CalendarProps } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import { Button, buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { Calendar as CalendarIcon, LucideTextCursorInput } from "lucide-react";
import { ScrollArea } from "@/components/ui/scroll-area";
import { format } from "date-fns";
/* -------------------------------------------------------------------------- */
/* Inspired By: */
/* @steventey */
/* ------------------https://dub.co/blog/smart-datetime-picker--------------- */
/* -------------------------------------------------------------------------- */
/**
* Utility function that parses dates.
* Parses a given date string using the `chrono-node` library.
*
* @param str - A string representation of a date and time.
* @returns A `Date` object representing the parsed date and time, or `null` if the string could not be parsed.
*/
export const parseDateTime = (str: Date | string) => {
if (str instanceof Date) return str;
return parseDate(str);
};
/**
* Converts a given timestamp or the current date and time to a string representation in the local time zone.
* format: `HH:mm`, adjusted for the local time zone.
*
* @param timestamp {Date | string}
* @returns A string representation of the timestamp
*/
export const getDateTimeLocal = (timestamp?: Date): string => {
const d = timestamp ? new Date(timestamp) : new Date();
if (d.toString() === "Invalid Date") return "";
return new Date(d.getTime() - d.getTimezoneOffset() * 60000)
.toISOString()
.split(":")
.slice(0, 2)
.join(":");
};
/**
* Formats a given date and time object or string into a human-readable string representation.
* "MMM D, YYYY h:mm A" (e.g. "Jan 1, 2023 12:00 PM").
*
* @param datetime - {Date | string}
* @returns A string representation of the date and time
*/
export const formatDateTime = (datetime: Date | string) => {
return new Date(datetime).toLocaleTimeString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "numeric",
hour12: true,
});
};
const inputBase =
"bg-transparent focus:outline-none focus:ring-0 focus-within:outline-none focus-within:ring-0 sm:text-sm disabled:cursor-not-allowed disabled:opacity-50";
// @source: https://www.perplexity.ai/search/in-javascript-how-RfI7fMtITxKr5c.V9Lv5KA#1
// use this pattern to validate the transformed date string for the natural language input
const naturalInputValidationPattern =
"^[A-Z][a-z]{2}sd{1,2},sd{4},sd{1,2}:d{2}s[AP]M$";
const DEFAULT_SIZE = 96;
/**
* Smart time input Docs: {@link: https://shadcn-extension.vercel.app/docs/smart-time-input}
*/
interface SmartDatetimeInputProps {
value?: Date;
onValueChange: (date: Date) => void;
}
interface SmartDatetimeInputContextProps extends SmartDatetimeInputProps {
Time: string;
onTimeChange: (time: string) => void;
}
const SmartDatetimeInputContext =
React.createContext<SmartDatetimeInputContextProps | null>(null);
const useSmartDateInput = () => {
const context = React.useContext(SmartDatetimeInputContext);
if (!context) {
throw new Error(
"useSmartDateInput must be used within SmartDateInputProvider"
);
}
return context;
};
export const SmartDatetimeInput = React.forwardRef<
HTMLInputElement,
Omit<
React.InputHTMLAttributes<HTMLInputElement>,
"type" | "ref" | "value" | "defaultValue" | "onBlur"
> &
SmartDatetimeInputProps
>(({ className, value, onValueChange, placeholder, disabled }, ref) => {
// ? refactor to be only used with controlled input
/* const [dateTime, setDateTime] = React.useState<Date | undefined>(
value ?? undefined
); */
const [Time, setTime] = React.useState<string>("");
const onTimeChange = React.useCallback((time: string) => {
setTime(time);
}, []);
return (
<SmartDatetimeInputContext.Provider
value={{ value, onValueChange, Time, onTimeChange }}
>
<div className="flex items-center justify-center">
<div
className={cn(
"flex gap-1 w-full p-1 items-center justify-between rounded-md border transition-all",
"focus-within:outline-0 focus:outline-0 focus:ring-0",
"placeholder:text-muted-foreground focus-visible:outline-0 ",
className
)}
>
<DateTimeLocalInput />
<NaturalLanguageInput
placeholder={placeholder}
disabled={disabled}
ref={ref}
/>
</div>
</div>
</SmartDatetimeInputContext.Provider>
);
});
SmartDatetimeInput.displayName = "DatetimeInput";
// Make it a standalone component
const TimePicker = () => {
const { value, onValueChange, Time, onTimeChange } = useSmartDateInput();
const [activeIndex, setActiveIndex] = React.useState(-1);
const timestamp = 15;
const formateSelectedTime = React.useCallback(
(time: string, hour: number, partStamp: number) => {
onTimeChange(time);
const newVal = parseDateTime(value ?? new Date());
if (!newVal) return;
newVal.setHours(
hour,
partStamp === 0 ? parseInt("00") : timestamp * partStamp
);
// ? refactor needed check if we want to use the new date
onValueChange(newVal);
},
[value]
);
const handleKeydown = React.useCallback(
(e: React.KeyboardEvent<HTMLDivElement>) => {
e.stopPropagation();
if (!document) return;
const moveNext = () => {
const nextIndex =
activeIndex + 1 > DEFAULT_SIZE - 1 ? 0 : activeIndex + 1;
const currentElm = document.getElementById(`time-${nextIndex}`);
currentElm?.focus();
setActiveIndex(nextIndex);
};
const movePrev = () => {
const prevIndex =
activeIndex - 1 < 0 ? DEFAULT_SIZE - 1 : activeIndex - 1;
const currentElm = document.getElementById(`time-${prevIndex}`);
currentElm?.focus();
setActiveIndex(prevIndex);
};
const setElement = () => {
const currentElm = document.getElementById(`time-${activeIndex}`);
if (!currentElm) return;
currentElm.focus();
const timeValue = currentElm.textContent ?? "";
// this should work now haha that hour is what does the trick
const PM_AM = timeValue.split(" ")[1];
const PM_AM_hour = parseInt(timeValue.split(" ")[0].split(":")[0]);
const hour =
PM_AM === "AM"
? PM_AM_hour === 12
? 0
: PM_AM_hour
: PM_AM_hour === 12
? 12
: PM_AM_hour + 12;
const part = Math.floor(
parseInt(timeValue.split(" ")[0].split(":")[1]) / 15
);
formateSelectedTime(timeValue, hour, part);
};
const reset = () => {
const currentElm = document.getElementById(`time-${activeIndex}`);
currentElm?.blur();
setActiveIndex(-1);
};
switch (e.key) {
case "ArrowUp":
movePrev();
break;
case "ArrowDown":
moveNext();
break;
case "Escape":
reset();
break;
case "Enter":
setElement();
break;
}
},
[activeIndex, formateSelectedTime]
);
const handleClick = React.useCallback(
(hour: number, part: number, PM_AM: string, currentIndex: number) => {
formateSelectedTime(
`${hour}:${part === 0 ? "00" : timestamp * part} ${PM_AM}`,
hour,
part
);
setActiveIndex(currentIndex);
},
[formateSelectedTime]
);
const currentTime = React.useMemo(() => {
const timeVal = Time.split(" ")[0];
return {
hours: parseInt(timeVal.split(":")[0]),
minutes: parseInt(timeVal.split(":")[1]),
};
}, [Time]);
React.useEffect(() => {
const getCurrentElementTime = () => {
const timeVal = Time.split(" ")[0];
const hours = parseInt(timeVal.split(":")[0]);
const minutes = parseInt(timeVal.split(":")[1]);
const PM_AM = Time.split(" ")[1];
const formatIndex =
PM_AM === "AM" ? hours : hours === 12 ? hours : hours + 12;
const formattedHours = formatIndex;
console.log(formatIndex);
for (let j = 0; j <= 3; j++) {
const diff = Math.abs(j * timestamp - minutes);
const selected =
PM_AM === (formattedHours >= 12 ? "PM" : "AM") &&
(minutes <= 53 ? diff < Math.ceil(timestamp / 2) : diff < timestamp);
if (selected) {
const trueIndex =
activeIndex === -1 ? formattedHours * 4 + j : activeIndex;
setActiveIndex(trueIndex);
const currentElm = document.getElementById(`time-${trueIndex}`);
currentElm?.scrollIntoView({
block: "center",
behavior: "smooth",
});
}
}
};
getCurrentElementTime();
}, [Time, activeIndex]);
const height = React.useMemo(() => {
if (!document) return;
const calendarElm = document.getElementById("calendar");
if (!calendarElm) return;
return calendarElm.style.height;
}, []);
return (
<div className="space-y-2 pr-3 py-3 relative ">
<h3 className="text-sm font-medium ">Time</h3>
<ScrollArea
onKeyDown={handleKeydown}
className="h-[90%] w-full focus-visible:outline-0 focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:border-0 py-0.5"
style={{
height,
}}
>
<ul
className={cn(
"flex items-center flex-col gap-1 h-full max-h-56 w-28 px-1 py-0.5"
)}
>
{Array.from({ length: 24 }).map((_, i) => {
const PM_AM = i >= 12 ? "PM" : "AM";
const formatIndex = i > 12 ? i % 12 : i === 0 || i === 12 ? 12 : i;
return Array.from({ length: 4 }).map((_, part) => {
const diff = Math.abs(part * timestamp - currentTime.minutes);
const trueIndex = i * 4 + part;
// ? refactor : add the select of the default time on the current device (H:MM)
const isSelected =
(currentTime.hours === i ||
currentTime.hours === formatIndex) &&
Time.split(" ")[1] === PM_AM &&
(currentTime.minutes <= 53
? diff < Math.ceil(timestamp / 2)
: diff < timestamp);
const isSuggested = !value && isSelected;
const currentValue = `${formatIndex}:${
part === 0 ? "00" : timestamp * part
} ${PM_AM}`;
return (
<li
tabIndex={isSelected ? 0 : -1}
id={`time-${trueIndex}`}
key={`time-${trueIndex}`}
aria-label="currentTime"
className={cn(
buttonVariants({
variant: isSuggested
? "secondary"
: isSelected
? "default"
: "outline",
}),
"h-8 px-3 w-full text-sm focus-visible:outline-0 outline-0 focus-visible:border-0 cursor-default ring-0"
)}
onClick={() => handleClick(i, part, PM_AM, trueIndex)}
onFocus={() => isSuggested && setActiveIndex(trueIndex)}
>
{currentValue}
</li>
);
});
})}
</ul>
</ScrollArea>
</div>
);
};
const NaturalLanguageInput = React.forwardRef<
HTMLInputElement,
{
placeholder?: string;
disabled?: boolean;
}
>(({ placeholder, ...props }, ref) => {
const { value, onValueChange, Time, onTimeChange } = useSmartDateInput();
const _placeholder = placeholder ?? 'e.g. "tomorrow at 5pm" or "in 2 hours"';
const [inputValue, setInputValue] = React.useState<string>("");
React.useEffect(() => {
const hour = new Date().getHours();
const timeVal = `${
hour >= 12 ? hour % 12 : hour
}:${new Date().getMinutes()} ${hour >= 12 ? "PM" : "AM"}`;
setInputValue(value ? formatDateTime(value) : "");
onTimeChange(value ? Time : timeVal);
}, [value, Time]);
const handleParse = React.useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
// parse the date string when the input field loses focus
const parsedDateTime = parseDateTime(e.currentTarget.value);
if (parsedDateTime) {
const PM_AM = parsedDateTime.getHours() >= 12 ? "PM" : "AM";
//fix the time format for this value
const PM_AM_hour = parsedDateTime.getHours();
const hour =
PM_AM_hour > 12
? PM_AM_hour % 12
: PM_AM_hour === 0 || PM_AM_hour === 12
? 12
: PM_AM_hour;
onValueChange(parsedDateTime);
setInputValue(formatDateTime(parsedDateTime));
onTimeChange(`${hour}:${parsedDateTime.getMinutes()} ${PM_AM}`);
}
},
[value]
);
const handleKeydown = React.useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
switch (e.key) {
case "Enter":
const parsedDateTime = parseDateTime(e.currentTarget.value);
if (parsedDateTime) {
const PM_AM = parsedDateTime.getHours() >= 12 ? "PM" : "AM";
//fix the time format for this value
const PM_AM_hour = parsedDateTime.getHours();
const hour =
PM_AM_hour > 12
? PM_AM_hour % 12
: PM_AM_hour === 0 || PM_AM_hour === 12
? 12
: PM_AM_hour;
onValueChange(parsedDateTime);
setInputValue(formatDateTime(parsedDateTime));
onTimeChange(`${hour}:${parsedDateTime.getMinutes()} ${PM_AM}`);
}
break;
}
},
[value]
);
return (
<Input
ref={ref}
type="text"
placeholder={_placeholder}
value={inputValue}
onChange={(e) => setInputValue(e.currentTarget.value)}
onKeyDown={handleKeydown}
onBlur={handleParse}
className={cn("px-2 mr-0.5 flex-1 border-none h-8 rounded", inputBase)}
{...props}
/>
);
});
NaturalLanguageInput.displayName = "NaturalLanguageInput";
type DateTimeLocalInputProps = {} & CalendarProps;
const DateTimeLocalInput = ({
className,
...props
}: DateTimeLocalInputProps) => {
const { value, onValueChange, Time } = useSmartDateInput();
const formateSelectedDate = React.useCallback(
(
date: Date | undefined,
selectedDate: Date,
m: ActiveModifiers,
e: React.MouseEvent
) => {
const parsedDateTime = parseDateTime(selectedDate);
if (parsedDateTime) {
parsedDateTime.setHours(
parseInt(Time.split(":")[0]),
parseInt(Time.split(":")[1])
);
onValueChange(parsedDateTime);
}
},
[value, Time]
);
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
size={"icon"}
className={cn(
"size-9 flex items-center justify-center font-normal",
!value && "text-muted-foreground"
)}
>
<CalendarIcon className="size-4" />
<span className="sr-only">calender</span>
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" sideOffset={8}>
<div className="flex gap-1">
<Calendar
{...props}
id={"calendar"}
className={cn("peer flex justify-end", inputBase, className)}
mode="single"
selected={value}
onSelect={formateSelectedDate}
initialFocus
/>
<TimePicker />
</div>
</PopoverContent>
</Popover>
);
};
DateTimeLocalInput.displayName = "DateTimeLocalInput";

View File

@ -0,0 +1,55 @@
"use server";
import { retrieveAvailableEndpoint } from "@/apicalls/checkEndpoint";
import { retrieveHeadersAndValidationByEndpoint } from "@/apicalls/validations/validations";
async function checkPageAvaliable({
pageContent,
restrictions,
queryEncrypt,
}: {
pageContent: any;
restrictions: any;
queryEncrypt: any;
}) {
let restrictionsList: any = {
table: {
data: [],
headers: {},
validation: {},
},
update: {
data: [],
headers: {},
validation: {},
},
create: {
data: [],
headers: {},
validation: {},
},
};
await Promise.all(
pageContent?.map(async (listItem: any) => {
const { endpoint, name } = listItem;
const endpointAvailable = await retrieveAvailableEndpoint(endpoint);
if (endpointAvailable) {
if (listItem?.name === "table") {
restrictionsList[name].data = await restrictions[name].function(
queryEncrypt || {}
);
}
const validateAndHeaders = await retrieveHeadersAndValidationByEndpoint(
{
endpoint: listItem?.endpoint,
}
);
restrictionsList[name].headers = validateAndHeaders?.headers;
restrictionsList[name].validation = validateAndHeaders?.validated;
restrictionsList[name].icon = listItem?.icon;
}
}) ?? []
);
return restrictionsList;
}
export { checkPageAvaliable };

View File

@ -0,0 +1,71 @@
"use client";
import { retrieveUserSelection } from "@/apicalls/cookies/token";
import { retrieveHeadersAndValidationByEndpoint } from "@/apicalls/validations/validations";
import { AvailableLanguages } from "@/apimaps/mappingApi";
import { checkEndpointAvailability } from "@/apimaps/mappingApiFunctions";
import { retrievePageInfoByComponentName } from "./retrievePageInfoByComponentName";
async function initializePageRequirements(
endpoint: string,
pageInfoFromApi: Array<{ endpoint: string; [key: string]: any }>,
setFunction: Function,
setterFunction: Function
): Promise<void> {
try {
const validation = await retrieveHeadersAndValidationByEndpoint({
endpoint: endpoint,
});
const pageInfo = pageInfoFromApi.find(
(page: { endpoint: string }) => page.endpoint === endpoint
);
if (pageInfo && validation.status === 200) {
setFunction({
...pageInfo,
validation: validation.validated,
headers: validation.headers,
setterFunction: setterFunction,
});
}
} catch (error) {
console.error(`Error initializing endpoint ${endpoint}:`, error);
}
}
interface Mapper {
setFunction: Function;
setterFunction: Function;
}
async function initializePageContent(
pageName: string,
eventsAvailable: any,
MappingBuild: Record<string, Mapper>
): Promise<void> {
try {
const user = await retrieveUserSelection();
if (!AvailableLanguages.includes((user?.lang as string) || "")) {
new Error("Language not available");
}
const pageContent = retrievePageInfoByComponentName(pageName, user?.lang);
if (!Array.isArray(pageContent)) return;
await Promise.all(
Object.entries(MappingBuild).map(async ([endpoint, mapper]) => {
const { setFunction, setterFunction } = mapper as Mapper;
if (checkEndpointAvailability(endpoint, eventsAvailable)) {
await initializePageRequirements(
endpoint,
pageContent,
setFunction,
setterFunction
);
}
})
);
} catch (error) {
console.error("Error initializing page content:", error);
}
}
export { initializePageRequirements, initializePageContent };

View File

@ -0,0 +1,113 @@
"use client";
import React from "react";
import { Input } from "@/components/ui/input";
import { SmartDatetimeInput } from "@/components/ui/smart-datetime-input";
import { cn } from "@/lib/utils";
type ValidationTypes = "string" | "integer" | "datetime" | "boolean";
interface InputProps {
field: any;
required: boolean;
className?: string;
placeholder?: string;
}
const StringInput = ({
className,
placeholder,
field,
required,
}: InputProps) => {
return (
<>
<Input
required={required}
type="text"
className={cn(className || "", "")}
{...field}
onChange={field.onChange}
value={field.value || ""}
placeholder={placeholder}
/>
</>
);
};
const NumberInput = ({
className,
placeholder,
field,
required,
}: InputProps) => {
return (
<>
<Input
required={required}
type="number"
className={cn(className || "", "")}
{...field}
onChange={field.onChange}
value={field.value === 0 ? "" : field.value}
placeholder={placeholder}
/>
</>
);
};
const BooleanInput = ({
className,
placeholder,
field,
required,
}: InputProps) => {
return (
<>
<Input
type="checkbox"
placeholder={placeholder}
className={cn(className || "", "")}
{...field}
/>
</>
);
};
const DatetimeInput = ({
className,
placeholder,
field,
required,
}: InputProps) => {
return (
<>
<h1></h1>
<SmartDatetimeInput
required={required}
className={cn(className || "", "")}
onValueChange={field.onChange}
value={field.value ? field.value.toString() : ""}
placeholder={placeholder}
/>
</>
);
};
function RetrieveInputByType({
type,
props,
}: {
type: ValidationTypes;
props: InputProps;
}) {
switch (type) {
case "integer":
return <NumberInput {...props} />;
case "datetime":
return <DatetimeInput {...props} />;
case "boolean":
return <BooleanInput {...props} />;
default:
return <StringInput {...props} />;
}
}
export { RetrieveInputByType };

View File

@ -0,0 +1,28 @@
import {
LanguagesInterface,
PagesInfosAndEndpoints,
} from "@/apimaps/mappingApi";
const retrievePageContent = (
pageName: string,
lang: keyof LanguagesInterface
) => {
return (
PagesInfosAndEndpoints.find((page) => page.component === pageName)
?.pageInfo?.[lang] || null
);
};
const retrievepageInfoOfEndpoint = (
pageName: string,
endpoint: string,
lang: string
) => {
const pageContent = retrievePageContent(
pageName,
lang as keyof LanguagesInterface
);
return pageContent?.find((page) => page.endpoint === endpoint);
};
export { retrievePageContent, retrievepageInfoOfEndpoint };

View File

@ -0,0 +1,30 @@
import { PagesInfosAndEndpoints } from "@/apimaps/mappingApi";
const retrievePageInfoByComponentName = (
componentName: string,
lang: string
) => {
const searchInCategory = (category: any): any => {
if (category.name === componentName) {
return category.pageInfo?.[lang];
}
if (category.subCategories) {
for (const subCategory in category.subCategories) {
const result = searchInCategory(category.subCategories[subCategory]);
if (result) {
return result.pageInfo?.[lang];
}
}
}
};
for (const category in PagesInfosAndEndpoints) {
const result = searchInCategory(PagesInfosAndEndpoints[category]);
if (result) {
return result;
}
}
return null;
};
export { retrievePageInfoByComponentName };

View File

@ -0,0 +1,50 @@
import React from "react";
interface TableInfo {
totalPages: number;
currentPage: number;
size: number;
field: string;
type: string;
}
interface PaginationResult {
response: any;
setTableInfo: React.Dispatch<React.SetStateAction<TableInfoState>>;
checkPaginationState: React.Dispatch<React.SetStateAction<any>>;
}
interface TableInfoState {
size: number;
currentPage: number;
field: string;
type: string;
totalPages: number;
previousAvailable: boolean;
nextAvailable: boolean;
query: Record<string, unknown>;
}
const useRetrievePagination = ({
response,
setTableInfo,
checkPaginationState,
}: PaginationResult) => {
const paginationRes = response?.pagination;
const pagesTotal = paginationRes?.["page/total_page"];
const sizeAndPage = paginationRes?.["size/total_count"];
setTableInfo((prev) => ({
...prev,
totalPages: Number(pagesTotal[1]),
currentPage: Number(pagesTotal[0]),
size: Number(sizeAndPage[0]),
// field: String(paginationRes.order_field),
// type: String(paginationRes.order_type),
}));
checkPaginationState({
currentPage: Number(pagesTotal[0]),
totalPages: Number(pagesTotal[1]),
});
};
export { useRetrievePagination };

View File

@ -0,0 +1,41 @@
"use client";
import React from "react";
import { getPage } from "@/pages/DynamicPages/allPages";
function setComponentAsPageIfAvailable(
componentName: string,
eventsAvailable: any,
setPageFunction: React.Dispatch<React.SetStateAction<any>>
) {
const component = getPage(componentName);
if (component.props) {
const PageComponent = React.createElement<{
eventsAvailable: any;
pageSetterFunction: React.Dispatch<React.SetStateAction<any>>;
}>(component.component as any, {
eventsAvailable: eventsAvailable,
pageSetterFunction: setPageFunction,
});
setPageFunction(PageComponent);
} else {
setPageFunction(null);
}
}
function setComponentAsPage(
componentName: string,
props: any,
setPageFunction: React.Dispatch<React.SetStateAction<any>>
) {
const component = getPage(componentName);
if (props) {
const PageComponent = React.createElement(component.component, {
...props,
});
setPageFunction(PageComponent);
} else {
setPageFunction(null);
}
}
export { setComponentAsPage, setComponentAsPageIfAvailable };

View File

@ -0,0 +1,35 @@
import { useEffect, useRef } from "react";
export const useClickOutside = (handler: () => void) => {
const ref = useRef<HTMLDivElement>(null);
const isMouseDownOutside = useRef(false);
useEffect(() => {
const handleMouseDown = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) {
isMouseDownOutside.current = true;
}
};
const handleMouseUp = (e: MouseEvent) => {
if (
isMouseDownOutside.current &&
ref.current &&
!ref.current.contains(e.target as Node)
) {
handler();
}
isMouseDownOutside.current = false;
};
document.addEventListener("mousedown", handleMouseDown);
document.addEventListener("mouseup", handleMouseUp);
return () => {
document.removeEventListener("mousedown", handleMouseDown);
document.removeEventListener("mouseup", handleMouseUp);
};
}, [handler]);
return ref;
};