old version placed

This commit is contained in:
berkay 2024-12-31 12:22:36 +03:00
parent 08b7ad5c00
commit 00acc8c320
93 changed files with 7481 additions and 1 deletions

41
.gitignore vendored Normal file
View File

@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@ -1,3 +1,12 @@
# wag-frontend-version-3
Hi. This is a server-side next-js implemented for wag frontend.
Install Next js & shadcn ->
> npx create-next-app@latest .
> npx shadcn@latest init
> npx shadcn@latest add form switch pagination table popover scroll-area calendar input button
--legacy-peer-deps

View File

@ -0,0 +1,18 @@
"use server";
import { fetchData, fetchDataWithToken } from "../api-fetcher";
import { baseUrl, FilterList, FilterListInterface } from "../basics";
const accountsListEndpoint = `${baseUrl}/account/records/list`;
async function retrieveaccountsList(payload: FilterListInterface) {
const feedObject = new FilterList(payload).filter();
const tokenResponse: any = await fetchDataWithToken(
accountsListEndpoint,
feedObject,
"POST",
false
);
return tokenResponse;
}
export { retrieveaccountsList };

132
apicalls/api-fetcher.tsx Normal file
View File

@ -0,0 +1,132 @@
"use server";
import { retrieveAccessToken } from "@/apicalls/cookies/token";
const defaultHeaders = {
accept: "application/json",
"Content-type": "application/json",
};
const DefaultResponse = {
completed: false,
error: "Hata tipi belirtilmedi",
message: "Hata oluştu, lütfen tekrar deneyin",
status: "500",
data: {},
};
interface HeadersObject {
cache: string;
method: string;
headers: Object;
body?: string;
}
const cacheList = ["no-cache", "no-store", "force-cache", "only-if-cached"];
const prepareResponse = async (response: any) => {
try {
const responseJson = await response.json();
const statusResponse = response?.status;
const errorResponse = responseJson?.error || responseJson?.Error;
const messageResponse = (responseJson?.message || "").toString();
const completeResponse = responseJson?.completed || false;
const preparedResponse = {
completed: completeResponse,
message: messageResponse,
status: statusResponse,
error: errorResponse || "",
...responseJson,
};
return preparedResponse;
} catch (error) {}
return DefaultResponse;
};
const fetchData = async (
endpoint: string,
payload: any,
method: string = "POST",
cache: boolean = false
) => {
let headersObject: any = {
cache: cache ? "force-cache" : "no-cache",
method: method,
headers: defaultHeaders,
};
if (method !== "GET") {
headersObject = {
...headersObject,
body: JSON.stringify(payload),
};
}
console.log("headersObject", headersObject);
try {
const response = await fetch(endpoint, headersObject);
return await prepareResponse(response);
} catch (error) {}
return DefaultResponse;
};
const updateDataWithToken = async (
endpoint: string,
uuid: string,
payload: any,
method: string = "POST",
cache: boolean = false
) => {
const accessToken = (await retrieveAccessToken()) || "";
let headersObject: any = {
cache: cache ? "force-cache" : "no-cache",
method: method,
headers: {
...defaultHeaders,
"evyos-session-key": accessToken,
},
};
if (method !== "GET") {
headersObject = {
...headersObject,
body: JSON.stringify(payload.payload),
};
}
try {
const response = await fetch(`${endpoint}/${uuid}`, headersObject);
return await prepareResponse(response);
} catch (error) {}
return DefaultResponse;
};
const fetchDataWithToken = async (
endpoint: string,
payload: any,
method: string = "POST",
cache: boolean = false
) => {
const accessToken = (await retrieveAccessToken()) || "";
let headersObject: any = {
cache: cache ? "force-cache" : "no-cache",
method: method,
headers: {
...defaultHeaders,
"evyos-session-key": accessToken,
},
};
if (method !== "GET") {
headersObject = {
...headersObject,
body: JSON.stringify(payload),
};
}
try {
const response = await fetch(endpoint, headersObject);
return await prepareResponse(response);
} catch (error) {}
return DefaultResponse;
};
export { fetchData, fetchDataWithToken, updateDataWithToken };

59
apicalls/basics.ts Normal file
View File

@ -0,0 +1,59 @@
export const baseUrl = process.env.BASICURL || "";
export const tokenSecret = process.env.TOKENSECRET || "";
export const cookieObject: any = {
httpOnly: true,
path: "/",
sameSite: "lax",
secure: true,
maxAge: 3600,
};
interface FilterListInterface {
page?: number | null | undefined;
size?: number | null | undefined;
orderField?: string | null | undefined;
orderType?: string | null | undefined;
includeJoins?: any[] | null | undefined;
query?: any | null | undefined;
}
class FilterList {
page: number;
size: number;
orderField: string;
orderType: string;
includeJoins: any[];
query: any;
constructor({
page = 1,
size = 5,
orderField = "id",
orderType = "asc",
includeJoins = [],
query = {},
}: FilterListInterface = {}) {
this.page = page ?? 1;
this.size = size ?? 5;
this.orderField = orderField ?? "id";
this.orderType = orderType ?? "asc";
this.orderType = this.orderType.startsWith("a") ? "asc" : "desc";
this.includeJoins = includeJoins ?? [];
this.query = query ?? {};
}
filter() {
return {
page: this.page,
size: this.size,
order_field: this.orderField,
order_type: this.orderType,
include_joins: this.includeJoins,
query: this.query,
};
}
}
const defaultFilterList = new FilterList({});
export { FilterList, defaultFilterList };
export type { FilterListInterface };

View File

@ -0,0 +1,52 @@
"use server";
import { fetchDataWithToken, updateDataWithToken } from "../api-fetcher";
import {
baseUrl,
FilterList,
FilterListInterface,
defaultFilterList,
} from "../basics";
const buildListEndpoint = `${baseUrl}/building/build/list`;
const buildCreateEndpoint = `${baseUrl}/building/build/create`;
const buildUpdateEndpoint = `${baseUrl}/building/build/update`;
async function retrieveBuildList(payload: FilterListInterface) {
const feedObject = new FilterList(payload).filter();
const tokenResponse: any = await fetchDataWithToken(
buildListEndpoint,
feedObject,
"POST",
false
);
return tokenResponse;
}
interface BuildUpdateInterface {
uuid: string;
payload: any;
}
async function updateBuild(payload: any) {
const { uu_id: extractedField, ...payloadBody } = payload;
const tokenResponse: any = await updateDataWithToken(
buildUpdateEndpoint,
extractedField,
payloadBody,
"POST",
false
);
return tokenResponse;
}
async function createBuild(payload: any) {
const tokenResponse: any = await fetchDataWithToken(
buildCreateEndpoint,
payload,
"POST",
false
);
return tokenResponse;
}
export { retrieveBuildList, updateBuild, createBuild };

View File

@ -0,0 +1,40 @@
"use server";
import { fetchData, fetchDataWithToken } from "../api-fetcher";
import { baseUrl, FilterList, FilterListInterface } from "../basics";
const livingSpaceListEndpoint = `${baseUrl}/building/living_space/list`;
const livingSpaceCreateEndpoint = `${baseUrl}/building/living_space/create`;
const livingSpaceUpdateEndpoint = `${baseUrl}/building/living_space/update`;
async function retrievelivingSpaceList(payload: FilterListInterface) {
const feedObject = new FilterList(payload).filter();
const tokenResponse: any = await fetchDataWithToken(
livingSpaceListEndpoint,
feedObject,
"POST",
false
);
return tokenResponse;
}
async function createLivingSpace(payload: any) {
const tokenResponse: any = await fetchDataWithToken(
livingSpaceCreateEndpoint,
payload,
"POST",
true
);
return tokenResponse;
}
async function updateLivingSpace(payload: any) {
const tokenResponse: any = await fetchDataWithToken(
livingSpaceUpdateEndpoint,
payload,
"POST",
true
);
return tokenResponse;
}
export { retrievelivingSpaceList, createLivingSpace, updateLivingSpace };

View File

@ -0,0 +1,52 @@
"use server";
import { fetchDataWithToken, updateDataWithToken } from "../api-fetcher";
import {
baseUrl,
FilterList,
FilterListInterface,
defaultFilterList,
} from "../basics";
const partsListEndpoint = `${baseUrl}/building/parts/list`;
const partsCreateEndpoint = `${baseUrl}/building/parts/create`;
const partsUpdateEndpoint = `${baseUrl}/building/parts/update`;
interface BuildUpdateInterface {
uuid: string;
payload: any;
}
async function retrievePartsList(payload: FilterListInterface) {
const feedObject = new FilterList(payload).filter();
const tokenResponse: any = await fetchDataWithToken(
partsListEndpoint,
feedObject,
"POST",
false
);
return tokenResponse;
}
async function updateParts(payload: any) {
const { uu_id: extractedField, ...payloadBody } = payload;
const tokenResponse: any = await updateDataWithToken(
partsUpdateEndpoint,
extractedField,
payloadBody,
"POST",
false
);
return tokenResponse;
}
async function createParts(payload: any) {
const tokenResponse: any = await fetchDataWithToken(
partsCreateEndpoint,
payload,
"POST",
false
);
return tokenResponse;
}
export { retrievePartsList, updateParts, createParts };

View File

@ -0,0 +1,52 @@
"use server";
import { fetchDataWithToken, updateDataWithToken } from "../api-fetcher";
import {
baseUrl,
FilterList,
FilterListInterface,
defaultFilterList,
} from "../basics";
const CompanyListEndpoint = `${baseUrl}/company/list`;
const CompanyCreateEndpoint = `${baseUrl}/company/create`;
const CompanyUpdateEndpoint = `${baseUrl}/company/update`;
interface CompanyUpdateInterface {
uuid: string;
payload: any;
}
async function retrieveCompanyList(payload: FilterListInterface) {
const feedObject = new FilterList(payload).filter();
const tokenResponse: any = await fetchDataWithToken(
CompanyListEndpoint,
feedObject,
"POST",
false
);
return tokenResponse;
}
async function updateCompany(payload: any) {
const { uu_id: extractedField, ...payloadBody } = payload;
const tokenResponse: any = await updateDataWithToken(
CompanyUpdateEndpoint,
extractedField,
payloadBody,
"POST",
false
);
return tokenResponse;
}
async function createCompany(payload: any) {
const tokenResponse: any = await fetchDataWithToken(
CompanyCreateEndpoint,
payload,
"POST",
false
);
return tokenResponse;
}
export { retrieveCompanyList, updateCompany, createCompany };

121
apicalls/cookies/token.tsx Normal file
View File

@ -0,0 +1,121 @@
"use server";
import { fetchDataWithToken, fetchData } from "../api-fetcher";
import { baseUrl, tokenSecret } from "../basics";
import { cookies } from "next/headers";
import NextCrypto from "next-crypto";
const checkToken = `${baseUrl}/authentication/valid`;
const nextCrypto = new NextCrypto(tokenSecret);
async function checkAccessTokenIsValid() {
const response = await fetchDataWithToken(checkToken, {}, "GET", false);
return response?.status === 200 ? true : false;
}
async function retrieveAccessToken() {
const cookieStore = await cookies();
const encrpytAccessToken = cookieStore.get("accessToken")?.value || "";
return encrpytAccessToken
? await nextCrypto.decrypt(encrpytAccessToken)
: null;
}
async function retrieveUserType() {
const cookieStore = await cookies();
const encrpytaccessObject = cookieStore.get("accessObject")?.value || "{}";
const decrpytUserType = JSON.parse(
(await nextCrypto.decrypt(encrpytaccessObject)) || "{}"
);
return decrpytUserType ? decrpytUserType?.user_type : null;
}
async function retrieveAccessObjects() {
const cookieStore = await cookies();
const encrpytAccessObject = cookieStore.get("accessObject")?.value || "";
const decrpytAccessObject = await nextCrypto.decrypt(encrpytAccessObject);
return decrpytAccessObject ? JSON.parse(decrpytAccessObject) : null;
}
async function retrieveAvailableEvents() {
const cookieStore = await cookies();
const encrpytAccessObject = cookieStore.get("availableEvents")?.value || "";
const decrpytAccessObject = await nextCrypto.decrypt(encrpytAccessObject);
return decrpytAccessObject ? JSON.parse(decrpytAccessObject) : null;
}
async function retrieveAvatarInfo() {
const response = await fetchDataWithToken(
`${baseUrl}/authentication/avatar`,
{},
"POST"
);
return response;
}
async function retrieveUserSelection() {
const cookieStore = await cookies();
const encrpytUserSelection = cookieStore.get("userSelection")?.value || "";
let decrpytUserSelection: any = await nextCrypto.decrypt(
encrpytUserSelection
);
decrpytUserSelection = decrpytUserSelection
? JSON.parse(decrpytUserSelection)
: null;
const userSelection = decrpytUserSelection?.company_uu_id;
let objectUserSelection = {};
if (decrpytUserSelection?.user_type === "employee") {
const accessObjects = (await retrieveAccessObjects()) || {};
const companyList = accessObjects?.companies_list;
const selectedCompany = companyList.find(
(company: any) => company.uu_id === userSelection
);
if (selectedCompany) {
objectUserSelection = {
occupantName: `${selectedCompany?.public_name}`,
};
}
} else if (decrpytUserSelection?.user_type === "occupant") {
const buildPartUUID = userSelection?.build_part_uu_id;
const occupantUUID = userSelection?.occupant_uu_id;
const build_id = userSelection?.build_id;
const accessObjects = (await retrieveAccessObjects()) || {};
const availableOccupants = accessObjects?.available_occupants[build_id];
const buildName = availableOccupants?.build_name;
const buildNo = availableOccupants?.build_no;
let selectedOccupant: any = null;
const occupants = availableOccupants?.occupants;
console.log("occupants", occupants);
if (occupants) {
selectedOccupant = occupants.find(
(occupant: any) =>
occupant.part_uu_id === buildPartUUID &&
occupant.uu_id === occupantUUID
);
}
if (selectedOccupant) {
objectUserSelection = {
buildName: `${buildName} - No:${buildNo}`,
occupantName: `${selectedOccupant?.description} ${selectedOccupant?.part_name}`,
};
}
}
const avatarInfo = await retrieveAvatarInfo();
return {
...objectUserSelection,
lang: String(avatarInfo?.data?.lang).toLowerCase(),
avatar: avatarInfo?.data?.avatar,
fullName: avatarInfo?.data?.full_name,
};
}
export {
checkAccessTokenIsValid,
retrieveAccessToken,
retrieveUserType,
retrieveAccessObjects,
retrieveAvailableEvents,
retrieveUserSelection,
};

View File

@ -0,0 +1,11 @@
"use server";
import { fetchDataWithToken } from "../api-fetcher";
import { baseUrl } from "../basics";
const eventList = `${baseUrl}/event/list`;
async function retrieveEventList() {
return await fetchDataWithToken(eventList, {}, "GET", false);
}
export { retrieveEventList };

View File

@ -0,0 +1,33 @@
"use server";
import { fetchDataWithToken } from "../api-fetcher";
import { cookies } from "next/headers";
import { baseUrl, cookieObject, tokenSecret } from "../basics";
import NextCrypto from "next-crypto";
const availableEventsURL = `${baseUrl}/access/endpoints/available`;
async function setAvailableEvents() {
const cookieStore = await cookies();
const nextCrypto = new NextCrypto(tokenSecret);
const availableResponse: any = await fetchDataWithToken(
availableEventsURL,
{},
"POST",
false
);
if (availableResponse.status === 200) {
const availableEventData = Array.from(availableResponse?.result) || [];
const availableEvents = await nextCrypto.encrypt(
JSON.stringify({ availableEvents: availableEventData })
);
cookieStore.set({
name: "availableEvents",
value: availableEvents,
...cookieObject,
});
}
}
export { setAvailableEvents };

151
apicalls/login/login.tsx Normal file
View File

@ -0,0 +1,151 @@
"use server";
import { fetchData, fetchDataWithToken } from "../api-fetcher";
import { cookies } from "next/headers";
import { baseUrl, cookieObject, tokenSecret } from "../basics";
import NextCrypto from "next-crypto";
import { setAvailableEvents } from "../events/available";
const loginEndpoint = `${baseUrl}/authentication/login`;
const loginSelectEndpoint = `${baseUrl}/authentication/select`;
interface LoginViaAccessKeys {
domain: string;
accessKey: string;
password: string;
rememberMe: boolean;
}
interface LoginSelectEmployee {
company_uu_id: string;
}
interface LoginSelectOccupant {
selectedBuilding: any;
build_part_uu_id: string;
occupant_uu_id: string;
}
async function loginViaAccessKeys(payload: LoginViaAccessKeys) {
const cookieStore = await cookies();
const nextCrypto = new NextCrypto(tokenSecret);
const tokenResponse: any = await fetchData(
loginEndpoint,
{
domain: payload.domain,
access_key: payload.accessKey,
password: payload.password,
remember_me: payload.rememberMe,
},
"POST",
false
);
if (tokenResponse.status === 200) {
const accessToken = await nextCrypto.encrypt(tokenResponse.access_token);
const accessObject = await nextCrypto.encrypt(
JSON.stringify(tokenResponse.access_object)
);
const userProfile = await nextCrypto.encrypt(
JSON.stringify(tokenResponse.user)
);
const refreshToken = await nextCrypto.encrypt(tokenResponse.refresh_token);
// const userType = await nextCrypto.encrypt(responseData.user_type);
// cookieStore.set({
// name: "refreshToken",
// value: refreshToken,
// httpOnly: true,
// path: "/",
// });
cookieStore.set({
name: "accessToken",
value: accessToken,
...cookieObject,
});
cookieStore.set({
name: "accessObject",
value: accessObject,
...cookieObject,
});
cookieStore.set({
name: "userProfile",
value: JSON.stringify(userProfile),
...cookieObject,
});
// cookieStore.set({
// name: "userType",
// value: userType,
// ...cookieObject,
// });
}
return tokenResponse;
}
async function loginSelectEmployee(payload: LoginSelectEmployee) {
const cookieStore = await cookies();
const nextCrypto = new NextCrypto(tokenSecret);
const selectResponse: any = await fetchDataWithToken(
loginSelectEndpoint,
{
company_uu_id: payload.company_uu_id,
},
"POST",
false
);
if (selectResponse.status === 200) {
const usersSelection = await nextCrypto.encrypt(
JSON.stringify({
company_uu_id: payload.company_uu_id,
user_type: "employee",
})
);
cookieStore.set({
name: "userSelection",
value: usersSelection,
...cookieObject,
});
await setAvailableEvents();
}
return selectResponse;
}
async function loginSelectOccupant(payload: LoginSelectOccupant) {
const selectedBuilding = payload.selectedBuilding;
const cookieStore = await cookies();
const nextCrypto = new NextCrypto(tokenSecret);
const selectResponse: any = await fetchDataWithToken(
loginSelectEndpoint,
{
build_part_uu_id: payload.build_part_uu_id,
occupant_uu_id: payload.occupant_uu_id,
},
"POST",
false
);
if (selectResponse.status === 200) {
const usersSelection = await nextCrypto.encrypt(
JSON.stringify({
company_uu_id: {
build_part_uu_id: payload.build_part_uu_id,
occupant_uu_id: payload.occupant_uu_id,
build_id: selectedBuilding,
},
user_type: "occupant",
})
);
cookieStore.set({
name: "userSelection",
value: usersSelection,
...cookieObject,
});
await setAvailableEvents();
}
return selectResponse;
}
export { loginViaAccessKeys, loginSelectEmployee, loginSelectOccupant };

54
apicalls/login/logout.tsx Normal file
View File

@ -0,0 +1,54 @@
"use server";
import { fetchDataWithToken } from "../api-fetcher";
import { cookies } from "next/headers";
import { baseUrl } from "../basics";
const logOutEndpoint = `${baseUrl}/authentication/logout`;
const logOutAllEndpoint = `${baseUrl}/authentication/disconnect`;
interface LoginOutUser {
domain: string;
}
async function logoutActiveSession(payload: LoginOutUser) {
const cookieStore = await cookies();
cookieStore.delete("accessToken");
cookieStore.delete("accessObject");
cookieStore.delete("userProfile");
cookieStore.delete("userSelection");
const tokenResponse: any = await fetchDataWithToken(
logOutEndpoint,
{
domain: payload.domain,
},
"POST",
false
);
if (tokenResponse.status === 200) {
return true;
}
return false;
}
async function logoutAllSessions(payload: LoginOutUser) {
const cookieStore = await cookies();
cookieStore.delete("accessToken");
cookieStore.delete("accessObject");
cookieStore.delete("userProfile");
cookieStore.delete("userSelection");
const tokenResponse: any = await fetchDataWithToken(
logOutAllEndpoint,
{
domain: payload.domain,
},
"POST",
false
);
if (tokenResponse.status === 200) {
return true;
}
return false;
}
export { logoutActiveSession, logoutAllSessions };

View File

@ -0,0 +1,69 @@
"use server";
import { fetchData, fetchDataWithToken } from "../api-fetcher";
import { baseUrl } from "../basics";
const createPasswordEndpoint = `${baseUrl}/authentication/create_password`;
const changePasswordEndpoint = `${baseUrl}/authentication/change_password`;
const forgotPasswordEndpoint = `${baseUrl}/authentication/reset_password`;
interface createPasswordViaToken {
token: string;
password: string;
rePassword: string;
}
interface changePasswordViaToken {
oldPassword: string;
newPassword: string;
}
interface sendForgotPasswordEmail {
domain: string;
accessKey: string;
}
async function create_password_via_token(payload: createPasswordViaToken) {
const createPasswordResponse: any = await fetchData(
createPasswordEndpoint,
{
password_token: payload.token,
password: payload.password,
re_password: payload.rePassword,
},
"POST",
false
);
return createPasswordResponse;
}
async function change_password_via_token(payload: changePasswordViaToken) {
const changePasswordResponse: any = await fetchDataWithToken(
changePasswordEndpoint,
{
old_password: payload.oldPassword,
new_password: payload.newPassword,
},
"POST",
false
);
return changePasswordResponse;
}
async function send_forgot_password_email(payload: sendForgotPasswordEmail) {
const response: any = await fetchData(
forgotPasswordEndpoint,
{
domain: payload.domain,
access_key: payload.accessKey,
},
"POST",
false
);
return response;
}
export {
create_password_via_token,
change_password_via_token,
send_forgot_password_email,
};

View File

@ -0,0 +1,52 @@
"use server";
import { fetchDataWithToken, updateDataWithToken } from "../api-fetcher";
import {
baseUrl,
FilterList,
FilterListInterface,
defaultFilterList,
} from "../basics";
const peopleListEndpoint = `${baseUrl}/people/list`;
const peopleCreateEndpoint = `${baseUrl}/people/create`;
const peopleUpdateEndpoint = `${baseUrl}/people/update`;
interface PeopleUpdateInterface {
uuid: string;
payload: any;
}
async function retrievePeopleList(payload: FilterListInterface) {
const feedObject = new FilterList(payload).filter();
const tokenResponse: any = await fetchDataWithToken(
peopleListEndpoint,
feedObject,
"POST",
false
);
return tokenResponse;
}
async function updatePeople(payload: any) {
const { uu_id: extractedField, ...payloadBody } = payload;
const tokenResponse: any = await updateDataWithToken(
peopleUpdateEndpoint,
extractedField,
payloadBody,
"POST",
false
);
return tokenResponse;
}
async function createPeople(payload: any) {
const tokenResponse: any = await fetchDataWithToken(
peopleCreateEndpoint,
payload,
"POST",
false
);
return tokenResponse;
}
export { retrievePeopleList, updatePeople, createPeople };

View File

@ -0,0 +1,43 @@
import { console } from "inspector";
interface ValidationInterface {
required: string[];
title: string;
type: string;
}
interface HeadersAndValidationsInterface {
headers: Object;
validation: ValidationInterface;
language: string;
properties: Object;
}
class HeadersAndValidations {
headers: Object;
validation: ValidationInterface;
language: string;
validated: any = {};
constructor({
headers,
validation,
language,
}: HeadersAndValidationsInterface) {
this.headers = headers;
this.validation = validation;
this.language = language;
this.parseProcesser();
}
parseProcesser() {
Object.entries(this.validation).map(([key, value]) => {
this.validated[key] = {
required: !value.required,
fieldType: value?.type,
};
});
}
}
export { HeadersAndValidations };

View File

@ -0,0 +1,67 @@
"use server";
import { fetchData, fetchDataWithToken } from "@/apicalls/api-fetcher";
import { baseUrl, cookieObject, tokenSecret } from "@/apicalls/basics";
import { HeadersAndValidations } from "@/apicalls/validations/validationProcesser";
const headersAndValidationEndpoint = `${baseUrl}/validations/endpoint`;
interface EndpointInterface {
endpoint: string;
}
async function retrieveHeadersEndpoint({ endpoint }: EndpointInterface) {
const selectResponse: any = await fetchDataWithToken(
headersAndValidationEndpoint,
{
endpoint: endpoint,
},
"POST",
false
);
if (selectResponse.status === 200) {
return {
status: selectResponse.status,
headers: selectResponse?.headers,
message: selectResponse.message,
};
}
return {
status: selectResponse.status,
headers: {},
message: selectResponse.message,
};
}
async function retrieveHeadersAndValidationByEndpoint({
endpoint,
}: EndpointInterface) {
console.log("endpoint", endpoint);
const selectResponse: any = await fetchDataWithToken(
headersAndValidationEndpoint,
{
endpoint: endpoint,
},
"POST",
false
);
if (selectResponse.status === 200) {
const responseParsed = new HeadersAndValidations(selectResponse);
return {
status: selectResponse.status,
headers: responseParsed.headers,
validated: responseParsed.validated,
language: responseParsed.language,
message: selectResponse.message,
};
}
return {
status: selectResponse.status,
message: selectResponse.message,
headers: null,
validated: null,
};
}
export { retrieveHeadersAndValidationByEndpoint, retrieveHeadersEndpoint };

View File

@ -0,0 +1,9 @@
const AccesibleAllEndpoints = [
"/event/list",
"/bind/services/occupant",
"/bind/services/employee",
];
const AccesibleSelfEndpoints = ["/event/list"];
export { AccesibleAllEndpoints, AccesibleSelfEndpoints };

View File

@ -0,0 +1,18 @@
const AccesibleCategories = [
{
title: {
tr: "Yetkiler",
en: "Permissions",
},
icon: "PackageCheck",
component: null,
selfEndpoints: [
"/event/list",
"/bind/services/occupant",
"/bind/services/employee",
],
allEndpoints: [],
subCategories: [],
},
];
export { AccesibleCategories };

View File

View File

@ -0,0 +1,24 @@
const AccountAllEndpoints = ["/account/records/list"];
const AccountPageInfo = {
tr: [
{
title: "Hesaplar",
endpoint: "/account/records/list",
description: "Hesaplarınızı listeyebilirsiniz",
component: "Table",
icon: null,
},
],
en: [
{
title: "Accounts",
endpoint: "/account/records/list",
description: "You can list your accounts",
component: "Table",
icon: null,
},
],
};
export { AccountAllEndpoints, AccountPageInfo };

View File

@ -0,0 +1,15 @@
const AccountSubCategories = [
{
title: {
tr: "Bakiye Sorgulama",
en: "Balance Inquiry",
},
icon: "LucideLandmark",
component: null,
selfEndpoints: ["/account/records/list"],
allEndpoints: [],
subCategories: [],
},
];
export { AccountSubCategories };

View File

View File

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,71 @@
const BuildPageInfo = {
tr: [
{
title: "Bina Listesi",
icon: null,
description: "Bina listeyebilirsiniz",
endpoint: "/building/build/list",
component: "Table",
},
{
title: "Bina Ekle",
icon: "BadgePlus",
description: "Bina oluşturma sayfasına hoş geldiniz",
endpoint: "/building/build/create",
component: "AddCreate2Table",
},
{
title: null,
icon: "Pencil",
description: "Bina güncelleme sayfasına hoş geldiniz",
endpoint: "/building/build/update/{build_uu_id}",
component: "AddUpdate2Table",
},
],
en: [
{
title: "Building List",
icon: null,
description: "Welcome to the building update page",
endpoint: "/building/build/list",
component: "Table",
},
{
title: "Create Building",
icon: "BadgePlus",
description: "Welcome to the building creation page",
endpoint: "/building/build/create",
component: "AddCreate2Table",
},
{
title: "Update Building",
icon: "Pencil",
description: "Welcome to the building update page",
endpoint: "/building/build/update/{build_uu_id}",
component: "AddUpdate2Table",
},
],
};
const BuildAllEndpoints = [
"/building/build/list",
"/building/build/create",
"/building/build/update/{build_uu_id}",
"/building/parts/list",
"/building/parts/create",
"/building/parts/update/{build_uu_id}",
"/building/area/list",
"/building/area/create",
"/building/area/update/{build_uu_id}",
"/building/living_space/list",
"/building/living_space/create",
"/building/living_space/update/{build_uu_id}",
];
const BuildSelfEndpoints = [
"/building/build/list",
"/building/build/create",
"/building/build/update/{build_uu_id}",
];
export { BuildPageInfo, BuildSelfEndpoints, BuildAllEndpoints };

View File

@ -0,0 +1,45 @@
import { LivingSpaceInfo } from "../livingSpace/pageInfo";
import { PartsPageInfo } from "../parts/pageInfo";
const BuildCategories = [
{
title: {
tr: "Daireler",
en: "Flats",
},
icon: "DoorOpen",
component: "PartsPage",
pageInfo: PartsPageInfo,
allEndpoints: [],
subCategories: [],
},
{
title: {
tr: "Kullanılabilir Alanlar",
en: "Building Areas",
},
icon: "TreePine",
component: null,
selfEndpoints: [
"/building/area/list",
"/building/area/create",
"/building/area/update/{build_uu_id}",
],
pageInfo: null,
allEndpoints: [],
subCategories: [],
},
{
title: {
tr: "Yaşayan Kişiler",
en: "Living People",
},
icon: "UsersRound",
component: "LivingSpacePage",
pageInfo: LivingSpaceInfo,
allEndpoints: [],
subCategories: [],
},
];
export { BuildCategories };

View File

View File

@ -0,0 +1,76 @@
const CompanySelfEndpoints = [
"/company/list",
"/company/create",
"/company/update/{company_uu_id}",
];
const CompanyAllEndpoints = [
"/company/list",
"/company/create",
"/company/update/{company_uu_id}",
"/department/list",
"/department/create",
"/department/update/{department_uu_id}",
"/duties/list",
"/duties/create",
"/duties/update",
"/employee/list",
"/employee/create",
"/employee/update/{employee_uu_id}",
"/employee/employ",
"/employee/fire",
"/staff/list",
"/staff/create",
"/staff/update/{staff_uu_id}",
];
const CompanyPageInfo = {
tr: [
{
title: "Şirket Listesi",
icon: null,
description: "Şirketleri listeyebilirsiniz",
endpoint: "/company/list",
component: "Table",
},
{
title: "Şirket Ekle",
icon: "BadgePlus",
description: "Şirket oluşturma sayfasına hoş geldiniz",
endpoint: "/company/create",
component: "AddCreate2Table",
},
{
title: null,
icon: "Pencil",
description: "Şirket güncelleme sayfasına hoş geldiniz",
endpoint: "/company/update/{company_uu_id}",
component: "AddUpdate2Table",
},
],
en: [
{
title: "Company List",
icon: null,
description: "Welcome to the company update page",
endpoint: "/company/list",
component: "Table",
},
{
title: "Create Company",
icon: "BadgePlus",
description: "Welcome to the company creation page",
endpoint: "/company/create",
component: "AddCreate2Table",
},
{
title: "Update Company",
icon: "Pencil",
description: "Welcome to the company update page",
endpoint: "/company/update/{company_uu_id}",
component: "AddUpdate2Table",
},
],
};
export { CompanyAllEndpoints, CompanySelfEndpoints, CompanyPageInfo };

View File

@ -0,0 +1,62 @@
const CompanyCategories = [
{
title: {
tr: "Departmanlar",
en: "Departments",
},
icon: "FolderOpenDot",
component: null,
selfEndpoints: [
"/department/list",
"/department/create",
"/department/update/{department_uu_id}",
],
allEndpoints: [],
subCategories: [],
},
{
title: {
tr: "Görevler",
en: "Duties",
},
icon: "BriefcaseMedical",
component: null,
selfEndpoints: ["/duties/list", "/duties/create", "/duties/update"],
allEndpoints: [],
subCategories: [],
},
{
title: {
tr: "Çalışanlar",
en: "Employees",
},
icon: "Pickaxe",
component: null,
selfEndpoints: [
"/employee/list",
"/employee/create",
"/employee/update/{employee_uu_id}",
"/employee/employ",
"/employee/fire",
],
allEndpoints: [],
subCategories: [],
},
{
title: {
tr: "Personel",
en: "Staff",
},
icon: "BicepsFlexed",
component: null,
selfEndpoints: [
"/staff/list",
"/staff/create",
"/staff/update/{staff_uu_id}",
],
allEndpoints: [],
subCategories: [],
},
];
export { CompanyCategories };

View File

View File

@ -0,0 +1,101 @@
const DecisionBookAllEndpoints = [
"/build/decision_book/list",
"/build/decision_book/create",
"/build/decision_book/update/{decision_book_uu_id}",
"/build/decision_book/approval",
"/build/decision_book/items/list",
"/build/decision_book/items/create",
"/build/decision_book/items/update/{decision_book_item_uu_id}",
"/build/decision_book/people/list",
"/build/decision_book/people/add",
"/build/decision_book/people/remove",
"/build/decision_book/project/list",
"/build/decision_book/project/create",
"/build/decision_book/project/update/{project_uu_id}",
"/build/decision_book/project/approval",
"/build/decision_book/project/items/list",
"/build/decision_book/project/items/create",
"/build/decision_book/project/items/update/{project_item_uu_id}",
"/build/decision_book/project/people/list",
"/build/decision_book/project/people/create",
"/build/decision_book/project/people/update/{project_people_uu_id}",
];
const DecisionBookSelfEndpoints = [
"/build/decision_book/list",
"/build/decision_book/create",
"/build/decision_book/update/{decision_book_uu_id}",
];
const DecisionBookItemAllEndpoints = [
"/build/decision_book/items/list",
"/build/decision_book/items/create",
"/build/decision_book/items/update/{decision_book_item_uu_id}",
"/build/decision_book/people/list",
"/build/decision_book/people/add",
"/build/decision_book/people/remove",
];
const ProjectBookItemsAllEndpoints = [
"/build/decision_book/project/items/list",
"/build/decision_book/project/items/create",
"/build/decision_book/project/items/update/{project_item_uu_id}",
"/build/decision_book/project/people/list",
"/build/decision_book/project/people/create",
"/build/decision_book/project/people/update/{project_people_uu_id}",
];
const DecisionBookPageInfo = {
tr: [
{
title: "Karar Defteri Listesi",
icon: null,
description: "Karar Defteri listeyebilirsiniz",
endpoint: "/decision_book/list",
component: "Table",
},
{
title: "Karar Defteri Ekle",
icon: "BadgePlus",
description: "Karar Defteri oluşturma sayfasına hoş geldiniz",
endpoint: "/decision_book/create",
component: "AddCreate2Table",
},
{
title: "Karar Defteri Güncelle",
icon: "Pencil",
description: "Karar Defteri güncelleme sayfasına hoş geldiniz",
endpoint: "/decision_book/update/{company_uu_id}",
component: "AddUpdate2Table",
},
],
en: [
{
title: "Decision Book List",
icon: null,
description: "Welcome to the decision book update page",
endpoint: "/decision_book/list",
component: "Table",
},
{
title: "Create Decision Book",
icon: "BadgePlus",
description: "Welcome to the decision book creation page",
endpoint: "/decision_book/create",
component: "AddCreate2Table",
},
{
title: "Update Decision Book",
icon: "Pencil",
description: "Welcome to the decision book update page",
endpoint: "/decision_book/update/{company_uu_id}",
component: "AddUpdate2Table",
},
],
};
export {
DecisionBookAllEndpoints,
DecisionBookSelfEndpoints,
DecisionBookItemAllEndpoints,
ProjectBookItemsAllEndpoints,
DecisionBookPageInfo,
};

View File

@ -0,0 +1,106 @@
import {
DecisionBookAllEndpoints,
DecisionBookItemAllEndpoints,
ProjectBookItemsAllEndpoints,
} from "./pageInfo";
const DecisionBookCategories = [
{
title: {
tr: "Kararlar",
en: "Decisions",
},
icon: "Stamp",
component: null,
selfEndpoints: [
"/build/decision_book/items/list",
"/build/decision_book/items/create",
"/build/decision_book/items/update/{decision_book_item_uu_id}",
],
allEndpoints: [],
subCategories: [],
},
{
title: {
tr: "Karar Defteri Kişiler",
en: "Decision Book People",
},
icon: "FolderCheck",
component: null,
selfEndpoints: [
"/build/decision_book/people/list",
"/build/decision_book/people/add",
"/build/decision_book/people/remove",
],
allEndpoints: [],
subCategories: [],
},
];
const ProjectBookItemsCategories = [
{
title: {
tr: "Proje Dosya İşlemleri",
en: "Project File Operations",
},
icon: "FolderKey",
component: null,
selfEndpoints: [
"/build/decision_book/project/items/list",
"/build/decision_book/project/items/create",
"/build/decision_book/project/items/update/{project_item_uu_id}",
],
allEndpoints: [],
subCategories: [],
},
{
title: {
tr: "Proje Dosya Kişiler",
en: "Project File People",
},
icon: "FolderCog",
component: null,
selfEndpoints: [
"/build/decision_book/project/people/list",
"/build/decision_book/project/people/create",
"/build/decision_book/project/people/update/{project_people_uu_id}",
],
allEndpoints: [],
subCategories: [],
},
];
const DecisionBookSubCategories = [
{
title: {
tr: "Kararlar",
en: "Decisions",
},
icon: "ScrollText",
component: "",
selfEndpoints: [
"/build/decision_book/items/list",
"/build/decision_book/items/create",
"/build/decision_book/items/update/{decision_book_item_uu_id}",
],
allEndpoints: DecisionBookItemAllEndpoints,
subCategories: DecisionBookCategories,
},
{
title: {
tr: "Proje Dosyaları",
en: "Project Files",
},
icon: "Projector",
component: "",
selfEndpoints: [
"/build/decision_book/project/list",
"/build/decision_book/project/create",
"/build/decision_book/project/update/{project_uu_id}",
],
allEndpoints: ProjectBookItemsAllEndpoints,
subCategories: ProjectBookItemsCategories,
},
];
export { DecisionBookSubCategories };

View File

View File

@ -0,0 +1,22 @@
const IdentityAllEndpoints = [
"/users/list",
"/users/create",
"/users/update/{user_uu_id}",
"/people/list",
"/people/create",
"/people/update/{people_uu_id}",
];
const IdentitySelfEndpoints = ["/users/list"];
const AddressAllEndpoints = [
"/address/list",
"/address/create",
"/address/update",
"/postcode/create",
"/postcode/update",
"/postcode/list",
"/address/search",
];
export { IdentityAllEndpoints, IdentitySelfEndpoints, AddressAllEndpoints };

View File

@ -0,0 +1,69 @@
import { AddressAllEndpoints } from "./pageInfo";
import { PeoplePageInfo } from "../people/pageInfo";
const AddressSubCategories = [
{
title: {
tr: "Posta Kodları",
en: "Postcodes",
},
icon: "Container",
component: null,
selfEndpoints: ["/postcode/list", "/postcode/create", "/postcode/update"],
allEndpoints: [],
subCategories: [],
},
{
title: {
tr: "Adres Arama",
en: "Address Search",
},
icon: "ScanSearch",
component: null,
selfEndpoints: ["/address/search", "/address/list"],
allEndpoints: [],
subCategories: [],
},
];
const IdentityCategories = [
{
title: {
tr: "Kullanıcılar",
en: "Users",
},
icon: "UserPlus",
component: null,
selfEndpoints: [
"/users/list",
"/users/create",
"/users/update/{user_uu_id}",
],
allEndpoints: [],
subCategories: [],
},
{
title: {
tr: "İnsanlar",
en: "People",
},
icon: "PersonStanding",
component: "PeoplePage",
pageInfo: PeoplePageInfo,
allEndpoints: [],
subCategories: [],
},
{
title: {
tr: "Adresler",
en: "Addresses",
},
icon: "MapPinned",
component: null,
selfEndpoints: ["/address/list", "/address/create", "/address/update"],
allEndpoints: AddressAllEndpoints,
subCategories: AddressSubCategories,
},
];
export { IdentityCategories };

View File

@ -0,0 +1,50 @@
const LivingSpaceInfo = {
tr: [
{
title: "Yaşayan Kişiler Listesi",
icon: null,
description: "Yaşayan Kişiler listeyebilirsiniz",
endpoint: "/building/living_space/list",
component: "Table",
},
{
title: "Yaşayan Kişi Ekle",
icon: "BadgePlus",
description: "Yaşayan Kişi oluşturma sayfasına hoş geldiniz",
endpoint: "/building/living_space/create",
component: "AddCreate2Table",
},
{
title: null,
icon: "Pencil",
description: "Yaşayan Kişi güncelleme sayfasına hoş geldiniz",
endpoint: "/building/living_space/update/{build_uu_id}",
component: "AddUpdate2Table",
},
],
en: [
{
title: "Living People List",
icon: null,
description: "Welcome to the living people update page",
endpoint: "/building/living_space/list",
component: "Table",
},
{
title: "Create Living People",
icon: "BadgePlus",
description: "Welcome to the living people creation page",
endpoint: "/building/living_space/create",
component: "AddCreate2Table",
},
{
title: "Update Living People",
icon: "Pencil",
description: "Welcome to the living people update page",
endpoint: "/building/living_space/update/{build_uu_id}",
component: "AddUpdate2Table",
},
],
};
export { LivingSpaceInfo };

113
apimaps/mappingApi.ts Normal file
View File

@ -0,0 +1,113 @@
import { BuildCategories } from "./building/subCategories";
import { MeetingSubCategories } from "./meeting/subCategories";
import { AccountSubCategories } from "./accounts/subCategories";
import { DecisionBookSubCategories } from "./decisionBook/subCategories";
import { AccesibleCategories } from "./accesible/subCategories";
import { CompanyCategories } from "./company/subCategories";
import { IdentityCategories } from "./identity/subCategories";
import {
DecisionBookAllEndpoints,
DecisionBookPageInfo,
} from "./decisionBook/pageInfo";
import { IdentityAllEndpoints } from "./identity/pageInfo";
import { AccesibleAllEndpoints } from "./accesible/pageInfo";
import { CompanyAllEndpoints, CompanyPageInfo } from "./company/pageInfo";
import { MeetingAllEndpoints } from "./meeting/pageInfo";
import { AccountAllEndpoints, AccountPageInfo } from "./accounts/pageInfo";
import { BuildPageInfo, BuildAllEndpoints } from "./building/pageInfo";
const PagesInfosAndEndpoints = [
{
title: {
tr: "Binalar",
en: "Buildings",
},
icon: "Hotel",
// component: "/build/page",
url: "/building?page=1",
pageInfo: BuildPageInfo,
allEndpoints: BuildAllEndpoints,
subCategories: BuildCategories,
},
{
title: {
tr: "Toplantılar",
en: "Meetings",
},
icon: "Logs",
url: "/meetings?page=1",
pageInfo: null,
component: null,
allEndpoints: MeetingAllEndpoints,
subCategories: MeetingSubCategories,
},
{
title: {
tr: "Cari Hesaplar",
en: "Accounts",
},
icon: "Landmark",
component: "AccountPage",
url: "/accounts?page=1",
pageInfo: AccountPageInfo,
allEndpoints: AccountAllEndpoints,
subCategories: AccountSubCategories,
},
{
title: {
tr: "Karar Defteri",
en: "Decision Book",
},
icon: "ScrollText",
component: "DecisionBookPage",
url: "/decisions?page=1",
pageInfo: DecisionBookPageInfo,
allEndpoints: DecisionBookAllEndpoints,
subCategories: DecisionBookSubCategories,
},
{
title: {
tr: "Kimlikler",
en: "Identities",
},
icon: "UserPlus",
component: null,
url: "/identities?page=1",
pageInfo: null,
allEndpoints: IdentityAllEndpoints,
subCategories: IdentityCategories,
},
{
title: {
tr: "Erişilebilirlik",
en: "Accessibility",
},
icon: "Cog",
component: null,
url: "/accessibilities?page=1",
pageInfo: null,
allEndpoints: AccesibleAllEndpoints,
subCategories: AccesibleCategories,
},
{
title: {
tr: "Firmalar",
en: "Companies",
},
icon: "Store",
component: "CompanyPage",
url: "/companies?page=1",
pageInfo: CompanyPageInfo,
allEndpoints: CompanyAllEndpoints,
subCategories: CompanyCategories,
},
];
const AvailableLanguages = ["tr", "en"];
interface LanguagesInterface {
tr: string;
en: string;
}
export type { LanguagesInterface };
export { PagesInfosAndEndpoints, AvailableLanguages };

View File

@ -0,0 +1,40 @@
import { PagesInfosAndEndpoints } from "./mappingApi";
export function retrieveAvailableCategories(availableCategories: any) {
const availableCategoriesList = Array.from(
availableCategories?.availableEvents || []
);
let availableMenu: Array<any> = [];
for (let i = 0; i < PagesInfosAndEndpoints.length; i++) {
const category = PagesInfosAndEndpoints[i];
if (category.allEndpoints) {
const setCategory = isCategoryAvailable(
category,
availableCategoriesList
);
if (setCategory) {
availableMenu.push(category);
}
}
}
return availableMenu;
}
function isCategoryAvailable(category: any, availableCategoriesList: any) {
const categoryList = Array.from(category.allEndpoints);
for (let j = 0; j < categoryList.length; j++) {
const endpoint = categoryList[j];
if (availableCategoriesList.includes(endpoint)) {
return true;
}
}
}
export function checkEndpointAvailability(
endpoint: string,
availableCategories: any
) {
const availableCategoriesList = Array.from(availableCategories || []);
return availableCategoriesList.includes(endpoint);
}

View File

@ -0,0 +1,8 @@
const MeetingAllEndpoints = [
"/build/decision_book/invite/list",
"/build/decision_book/invite/create",
"/build/decision_book/invite/update",
"/build/decision_book/invitations/assign",
];
export { MeetingAllEndpoints };

View File

@ -0,0 +1,31 @@
const MeetingSubCategories = [
{
title: {
tr: "Davetiyeler",
en: "Invitations",
},
icon: "ClipboardCheck",
component: null,
selfEndpoints: [
"/build/decision_book/invite/list",
"/build/decision_book/invite/create",
"/build/decision_book/invite/update",
"/build/decision_book/invitations/assign",
],
allEndpoints: [],
subCategories: [],
},
{
title: {
tr: "Görev Ata",
en: "Assign Task",
},
icon: "ClipboardList",
component: null,
selfEndpoints: [],
allEndpoints: [],
subCategories: [],
},
];
export { MeetingSubCategories };

50
apimaps/parts/pageInfo.ts Normal file
View File

@ -0,0 +1,50 @@
const PartsPageInfo = {
tr: [
{
title: "Daire Listesi",
icon: null,
description: "Daireleri listeyebilirsiniz",
endpoint: "/building/parts/list",
component: "Table",
},
{
title: "Daire Oluştur",
icon: "BadgePlus",
description: "Daire oluşturabilirsiniz",
endpoint: "/building/parts/create",
component: "Form",
},
{
title: "Daire Güncelle",
icon: "Pencil",
description: "Daire güncelleyebilirsiniz",
endpoint: "/building/parts/update/{build_uu_id}",
component: "Form",
},
],
en: [
{
title: "Flat List",
icon: null,
description: "You can list the flats",
endpoint: "/building/parts/list",
component: "Table",
},
{
title: "Create Flat",
icon: "BadgePlus",
description: "You can create a flat",
endpoint: "/building/parts/create",
component: "Form",
},
{
title: "Update Flat",
icon: "Pencil",
description: "You can update a flat",
endpoint: "/building/parts/update/{build_uu_id}",
component: "Form",
},
],
};
export { PartsPageInfo };

View File

@ -0,0 +1,50 @@
const PeoplePageInfo = {
tr: [
{
title: "Kişileri Listele",
icon: null,
description: "Kişileri listeyebilirsiniz",
endpoint: "/people/list",
component: "Table",
},
{
title: "Kişi Oluştur",
icon: "BadgePlus",
description: "Kişi oluşturabilirsiniz",
endpoint: "/people/create",
component: "Form",
},
{
title: "Kişi Güncelle",
icon: "Pencil",
description: "Kişi güncelleyebilirsiniz",
endpoint: "/people/update/{build_uu_id}",
component: "Form",
},
],
en: [
{
title: "People List",
icon: null,
description: "You can list the people",
endpoint: "/people/list",
component: "Table",
},
{
title: "Create People",
icon: "BadgePlus",
description: "You can create a people",
endpoint: "/people/create",
component: "Form",
},
{
title: "Update People",
icon: "Pencil",
description: "You can update a people",
endpoint: "/people/update/{build_uu_id}",
component: "Form",
},
],
};
export { PeoplePageInfo };

21
components.json Normal file
View File

@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

7
next.config.ts Normal file
View File

@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

3036
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

43
package.json Normal file
View File

@ -0,0 +1,43 @@
{
"name": "wag-frontend-version-3",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@hookform/resolvers": "^3.9.1",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-popover": "^1.1.4",
"@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-switch": "^1.1.2",
"@tanstack/react-table": "^8.20.6",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"flatpickr": "^4.6.13",
"lucide-react": "^0.469.0",
"next": "15.1.2",
"next-crypto": "^1.0.8",
"react": "^19.0.0",
"react-day-picker": "^8.10.1",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"sonner": "^1.7.1",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.1"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}

8
postcss.config.mjs Normal file
View File

@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

1
public/file.svg Normal file
View File

@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

1
public/globe.svg Normal file
View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

1
public/next.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

1
public/vercel.svg Normal file
View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 128 B

1
public/window.svg Normal file
View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 385 B

15
src/app/building/page.tsx Normal file
View File

@ -0,0 +1,15 @@
"use server";
import React from "react";
const Page = () => {
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-2xl font-bold mb-4">Building Management</h1>
<div className="bg-white rounded-lg shadow p-6">
<p>Building page content goes here</p>
</div>
</div>
);
};
export default Page;

View File

@ -0,0 +1,17 @@
"use server";
import React from "react";
const DashboardPage = () => {
return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">Dashboard</h1>
<div className="grid gap-4">
<div className="bg-white p-4 rounded-lg shadow">
<p>Welcome to your dashboard</p>
</div>
</div>
</div>
);
};
export default DashboardPage;

BIN
src/app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

72
src/app/globals.css Normal file
View File

@ -0,0 +1,72 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
font-family: Arial, Helvetica, sans-serif;
}
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

34
src/app/layout.tsx Normal file
View File

@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Evyos Web App",
description: "Generated by evyos app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}

View File

@ -0,0 +1,18 @@
"use server";
import { checkAccessTokenIsValid } from "@/apicalls/cookies/token";
import LoginWithEmail from "@/pages/LoginViaEmail/page";
import { redirect } from "next/navigation";
const LoginEmailPage = async () => {
if (await checkAccessTokenIsValid()) {
redirect("/login/select");
}
return (
<>
<LoginWithEmail />
</>
);
};
export default LoginEmailPage;

14
src/app/login/page.tsx Normal file
View File

@ -0,0 +1,14 @@
"use server";
import { checkAccessTokenIsValid } from "@/apicalls/cookies/token";
import { redirect } from "next/navigation";
const LoginPage = async () => {
if (await checkAccessTokenIsValid()) {
redirect("/login/select");
} else {
redirect("/login/email");
}
};
export default LoginPage;

View File

@ -0,0 +1,17 @@
"use server";
import { checkAccessTokenIsValid } from "@/apicalls/cookies/token";
import { redirect } from "next/navigation";
import LoginWithPhone from "@/pages/LoginViaPhone/page";
const LoginPhonePage = async () => {
if (await checkAccessTokenIsValid()) {
redirect("/login/select");
}
return (
<>
<LoginWithPhone />
</>
);
};
export default LoginPhonePage;

View File

@ -0,0 +1,27 @@
"use server";
import React from "react";
import { retrieveAccessObjects } from "@/apicalls/cookies/token";
import LoginSelectEmployeeCard from "@/pages/LoginSelectEmployee/page";
const LoginEmployeePage: React.FC = async () => {
const accessObject = await retrieveAccessObjects();
return accessObject ? (
<>
<div className="absolute top-0 left-0 min-w-full min-h-full">
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="flex flex-wrap items-center">
<LoginSelectEmployeeCard
companyList={accessObject.companies_list}
/>
</div>
</div>
</div>
</>
) : (
<>
<h1>No register Employeer Company has found for this user</h1>
</>
);
};
export default LoginEmployeePage;

View File

@ -0,0 +1,27 @@
"use server";
import React from "react";
import { retrieveAccessObjects } from "@/apicalls/cookies/token";
import LoginSelectOccupantCard from "@/pages/LoginSelectOccupant/page";
const LoginOccupantPage: React.FC = async () => {
const accessObject: any = await retrieveAccessObjects();
return accessObject ? (
<>
<div className="absolute top-0 left-0 min-w-full min-h-full">
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="flex flex-wrap items-center">
<LoginSelectOccupantCard
availableOccupants={accessObject?.available_occupants}
/>
</div>
</div>
</div>
</>
) : (
<>
<h1>No register Occupant has found for this user</h1>
</>
);
};
export default LoginOccupantPage;

View File

@ -0,0 +1,22 @@
"use server";
import {
checkAccessTokenIsValid,
retrieveUserType,
} from "@/apicalls/cookies/token";
import { redirect } from "next/navigation";
import LoginEmployeePage from "./LoginEmployeePage";
import LoginOccupantPage from "./LoginOccupantPage";
const SelectPage = async () => {
const token_is_valid = await checkAccessTokenIsValid();
const userType: "employee" | "occupant" = await retrieveUserType();
const isEmployee = userType === "employee";
if (!userType || !token_is_valid) {
redirect("/login/email");
}
return <>{isEmployee ? <LoginEmployeePage /> : <LoginOccupantPage />}</>;
};
export default SelectPage;

101
src/app/page.tsx Normal file
View File

@ -0,0 +1,101 @@
import Image from "next/image";
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
src/app/page.tsx
</code>
.
</li>
<li>Save and see your changes instantly.</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</main>
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</div>
);
}

View File

@ -0,0 +1,84 @@
"use client";
import React from "react";
import { useRouter } from "next/navigation";
const GoogleButton: React.FC = () => {
return (
<>
<button className="flex w-full items-center justify-center gap-3.5 rounded-lg border border-stroke bg-gray p-4 hover:bg-opacity-50 dark:border-strokedark dark:bg-meta-4 dark:hover:bg-opacity-50">
<span>
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_191_13499)">
<path
d="M19.999 10.2217C20.0111 9.53428 19.9387 8.84788 19.7834 8.17737H10.2031V11.8884H15.8266C15.7201 12.5391 15.4804 13.162 15.1219 13.7195C14.7634 14.2771 14.2935 14.7578 13.7405 15.1328L13.7209 15.2571L16.7502 17.5568L16.96 17.5774C18.8873 15.8329 19.9986 13.2661 19.9986 10.2217"
fill="#4285F4"
/>
<path
d="M10.2055 19.9999C12.9605 19.9999 15.2734 19.111 16.9629 17.5777L13.7429 15.1331C12.8813 15.7221 11.7248 16.1333 10.2055 16.1333C8.91513 16.1259 7.65991 15.7205 6.61791 14.9745C5.57592 14.2286 4.80007 13.1801 4.40044 11.9777L4.28085 11.9877L1.13101 14.3765L1.08984 14.4887C1.93817 16.1456 3.24007 17.5386 4.84997 18.5118C6.45987 19.4851 8.31429 20.0004 10.2059 19.9999"
fill="#34A853"
/>
<path
d="M4.39899 11.9777C4.1758 11.3411 4.06063 10.673 4.05807 9.99996C4.06218 9.32799 4.1731 8.66075 4.38684 8.02225L4.38115 7.88968L1.19269 5.4624L1.0884 5.51101C0.372763 6.90343 0 8.4408 0 9.99987C0 11.5589 0.372763 13.0963 1.0884 14.4887L4.39899 11.9777Z"
fill="#FBBC05"
/>
<path
d="M10.2059 3.86663C11.668 3.84438 13.0822 4.37803 14.1515 5.35558L17.0313 2.59996C15.1843 0.901848 12.7383 -0.0298855 10.2059 -3.6784e-05C8.31431 -0.000477834 6.4599 0.514732 4.85001 1.48798C3.24011 2.46124 1.9382 3.85416 1.08984 5.51101L4.38946 8.02225C4.79303 6.82005 5.57145 5.77231 6.61498 5.02675C7.65851 4.28118 8.9145 3.87541 10.2059 3.86663Z"
fill="#EB4335"
/>
</g>
<defs>
<clipPath id="clip0_191_13499">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
</span>
Google ile giriş yap
</button>
</>
);
};
const LoginButton: React.FC = () => {
return (
<div className="mb-5">
<input
type="submit"
value="Giriş Yap"
className="w-full cursor-pointer rounded-lg border border-primary bg-primary p-4 text-white transition hover:bg-opacity-90"
/>
</div>
);
};
interface InterfaceChangeSignTypeButton {
buttonType: "phone" | "email";
}
const ChangeSignTypeButton: React.FC<InterfaceChangeSignTypeButton> = ({
buttonType,
}) => {
const router = useRouter();
return (
<div className="my-5">
<button
className="w-full cursor-pointer rounded-lg border border-primary bg-primary p-4 text-white transition hover:bg-opacity-90"
onClick={() =>
router.push(buttonType === "phone" ? "/login/phone" : "/login/email")
}
>
{buttonType === "phone"
? "Telefon ile Giriş Yap"
: "Email ile Giriş Yap"}
</button>
</div>
);
};
export { GoogleButton, LoginButton, ChangeSignTypeButton };

View File

@ -0,0 +1,33 @@
import React from "react";
import Image from "next/image";
interface InterfaceLeftSidePanel {
textLabel: string;
}
const LeftSidePanel: React.FC<InterfaceLeftSidePanel> = ({
textLabel
}) => {
return (
<>
<div className="hidden w-full xl:block xl:w-1/2">
<div className="text-center">
<p className="text-xl 2xl:px-20 my-5 text-black dark:text-white">
{textLabel}
</p>
<span className="mt-15 inline-block">
<Image
src="/green-house.webp"
alt="login-image"
width={480}
height={480}
className="w-108 h-108 mt-5"
/>
</span>
</div>
</div>
</>
);
};
export default LeftSidePanel;

View File

@ -0,0 +1,57 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }

View File

@ -0,0 +1,76 @@
"use client"
import * as React from "react"
import { ChevronLeft, ChevronRight } from "lucide-react"
import { DayPicker } from "react-day-picker"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
export type CalendarProps = React.ComponentProps<typeof DayPicker>
function Calendar({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
classNames={{
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
month: "space-y-4",
caption: "flex justify-center pt-1 relative items-center",
caption_label: "text-sm font-medium",
nav: "space-x-1 flex items-center",
nav_button: cn(
buttonVariants({ variant: "outline" }),
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
),
nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1",
table: "w-full border-collapse space-y-1",
head_row: "flex",
head_cell:
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: cn(
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
props.mode === "range"
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
: "[&:has([aria-selected])]:rounded-md"
),
day: cn(
buttonVariants({ variant: "ghost" }),
"h-8 w-8 p-0 font-normal aria-selected:opacity-100"
),
day_range_start: "day-range-start",
day_range_end: "day-range-end",
day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground",
day_outside:
"day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
day_disabled: "text-muted-foreground opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible",
...classNames,
}}
components={{
IconLeft: ({ className, ...props }) => (
<ChevronLeft className={cn("h-4 w-4", className)} {...props} />
),
IconRight: ({ className, ...props }) => (
<ChevronRight className={cn("h-4 w-4", className)} {...props} />
),
}}
{...props}
/>
)
}
Calendar.displayName = "Calendar"
export { Calendar }

178
src/components/ui/form.tsx Normal file
View File

@ -0,0 +1,178 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot"
import {
Controller,
ControllerProps,
FieldPath,
FieldValues,
FormProvider,
useFormContext,
} from "react-hook-form"
import { cn } from "@/lib/utils"
import { Label } from "@/components/ui/label"
const Form = FormProvider
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
name: TName
}
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue
)
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
)
}
const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext)
const itemContext = React.useContext(FormItemContext)
const { getFieldState, formState } = useFormContext()
const fieldState = getFieldState(fieldContext.name, formState)
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>")
}
const { id } = itemContext
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
}
}
type FormItemContextValue = {
id: string
}
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue
)
const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId()
return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider>
)
})
FormItem.displayName = "FormItem"
const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField()
return (
<Label
ref={ref}
className={cn(error && "text-destructive", className)}
htmlFor={formItemId}
{...props}
/>
)
})
FormLabel.displayName = "FormLabel"
const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
{...props}
/>
)
})
FormControl.displayName = "FormControl"
const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField()
return (
<p
ref={ref}
id={formDescriptionId}
className={cn("text-[0.8rem] text-muted-foreground", className)}
{...props}
/>
)
})
FormDescription.displayName = "FormDescription"
const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message) : children
if (!body) {
return null
}
return (
<p
ref={ref}
id={formMessageId}
className={cn("text-[0.8rem] font-medium text-destructive", className)}
{...props}
>
{body}
</p>
)
})
FormMessage.displayName = "FormMessage"
export {
useFormField,
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField,
}

View File

@ -0,0 +1,22 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
export { Input }

View File

@ -0,0 +1,26 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName
export { Label }

View File

@ -0,0 +1,117 @@
import * as React from "react"
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
import { cn } from "@/lib/utils"
import { ButtonProps, buttonVariants } from "@/components/ui/button"
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
<nav
role="navigation"
aria-label="pagination"
className={cn("mx-auto flex w-full justify-center", className)}
{...props}
/>
)
Pagination.displayName = "Pagination"
const PaginationContent = React.forwardRef<
HTMLUListElement,
React.ComponentProps<"ul">
>(({ className, ...props }, ref) => (
<ul
ref={ref}
className={cn("flex flex-row items-center gap-1", className)}
{...props}
/>
))
PaginationContent.displayName = "PaginationContent"
const PaginationItem = React.forwardRef<
HTMLLIElement,
React.ComponentProps<"li">
>(({ className, ...props }, ref) => (
<li ref={ref} className={cn("", className)} {...props} />
))
PaginationItem.displayName = "PaginationItem"
type PaginationLinkProps = {
isActive?: boolean
} & Pick<ButtonProps, "size"> &
React.ComponentProps<"a">
const PaginationLink = ({
className,
isActive,
size = "icon",
...props
}: PaginationLinkProps) => (
<a
aria-current={isActive ? "page" : undefined}
className={cn(
buttonVariants({
variant: isActive ? "outline" : "ghost",
size,
}),
className
)}
{...props}
/>
)
PaginationLink.displayName = "PaginationLink"
const PaginationPrevious = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to previous page"
size="default"
className={cn("gap-1 pl-2.5", className)}
{...props}
>
<ChevronLeft className="h-4 w-4" />
<span>Previous</span>
</PaginationLink>
)
PaginationPrevious.displayName = "PaginationPrevious"
const PaginationNext = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to next page"
size="default"
className={cn("gap-1 pr-2.5", className)}
{...props}
>
<span>Next</span>
<ChevronRight className="h-4 w-4" />
</PaginationLink>
)
PaginationNext.displayName = "PaginationNext"
const PaginationEllipsis = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">More pages</span>
</span>
)
PaginationEllipsis.displayName = "PaginationEllipsis"
export {
Pagination,
PaginationContent,
PaginationLink,
PaginationItem,
PaginationPrevious,
PaginationNext,
PaginationEllipsis,
}

View File

@ -0,0 +1,33 @@
"use client"
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
const Popover = PopoverPrimitive.Root
const PopoverTrigger = PopoverPrimitive.Trigger
const PopoverAnchor = PopoverPrimitive.Anchor
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }

View File

@ -0,0 +1,48 @@
"use client"
import * as React from "react"
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
import { cn } from "@/lib/utils"
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={cn("relative overflow-hidden", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
))
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
const ScrollBar = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = "vertical", ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
"flex touch-none select-none transition-colors",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
className
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
))
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
export { ScrollArea, ScrollBar }

View File

@ -0,0 +1,29 @@
"use client"
import * as React from "react"
import * as SwitchPrimitives from "@radix-ui/react-switch"
import { cn } from "@/lib/utils"
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName
export { Switch }

120
src/components/ui/table.tsx Normal file
View File

@ -0,0 +1,120 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className
)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn(
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className
)}
{...props}
/>
))
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}

View File

@ -0,0 +1,78 @@
"use client";
import * as z from "zod";
import { ZodDecimal } from "./zodDecimal";
function convertApiValidationToZodValidation(apiValidation: any) {
let zodValidation: any = {};
Object.entries(apiValidation).forEach(([key, value]: any) => {
const fieldType: String = value.fieldType || "string";
const required = value.required || false;
if (fieldType === "string") {
zodValidation[key] = required
? z
.string()
.min(1)
.refine((val) => val !== "" || val !== null)
: z
.string()
.min(1)
.optional()
.refine((val) => val !== "" || val !== null);
} else if (fieldType === "integer") {
zodValidation[key] = required
? z.preprocess((value) => {
try {
const parsedValue = Number(value);
return isNaN(parsedValue) ? undefined : parsedValue;
} catch (error) {
return undefined;
}
}, z.number().min(1))
: z.preprocess((value) => {
try {
const parsedValue = Number(value);
return isNaN(parsedValue) ? undefined : parsedValue;
} catch (error) {
return undefined;
}
}, z.number().min(1).optional());
} else if (fieldType === "boolean") {
zodValidation[key] = required ? z.boolean() : z.boolean().optional();
} else if (fieldType === "datetime") {
zodValidation[key] = required ? z.date() : z.date().optional();
} else if (fieldType === "float") {
zodValidation[key] = required
? ZodDecimal.create({ coerce: true })
: ZodDecimal.create({ coerce: true }).optional();
}
});
const validSchemaZod = z.object({
...zodValidation,
});
return {
validSchemaZod: validSchemaZod,
zodValidation: zodValidation,
apiValidation: apiValidation,
};
}
function retrieveDataWhichHaveValidation(data: any, apiValidation: any) {
const apiValidated = apiValidation?.validated || {};
Object.entries(apiValidated).forEach(([key, value]: any) => {
const fieldType: String = value.fieldType || "string";
const required = value.required || false;
if (fieldType === "string") {
data[key] = required ? data[key] : data[key] || "";
} else if (fieldType === "integer") {
data[key] = required ? data[key] : data[key] || 0;
} else if (fieldType === "boolean") {
data[key] = required ? data[key] : data[key] || false;
} else if (fieldType === "datetime") {
data[key] = required ? data[key] : new Date(data[key]) || "";
} else if (fieldType === "float") {
data[key] = required ? data[key] : data[key] || 0.0;
}
});
}
export { convertApiValidationToZodValidation, retrieveDataWhichHaveValidation };

6
src/lib/utils.ts Normal file
View File

@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

306
src/lib/zodDecimal.ts Normal file
View File

@ -0,0 +1,306 @@
import {
INVALID,
ParseContext,
ParseInput,
ParseReturnType,
ParseStatus,
RawCreateParams,
ZodIssueCode,
ZodParsedType,
ZodType,
ZodTypeDef,
addIssueToContext,
} from "zod";
export type ZodDecimalCheck =
| { kind: "precision"; value: number; message?: string }
| { kind: "wholeNumber"; value: number; message?: string }
| { kind: "min"; value: number; inclusive: boolean; message?: string }
| { kind: "max"; value: number; inclusive: boolean; message?: string }
| { kind: "finite"; message?: string };
const zodDecimalKind = "ZodDecimal";
export interface ZodDecimalDef extends ZodTypeDef {
checks: ZodDecimalCheck[];
typeName: typeof zodDecimalKind;
coerce: boolean;
}
const precisionRegex = /(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export class ZodDecimal extends ZodType<number, ZodDecimalDef, any> {
// eslint-disable-next-line @typescript-eslint/naming-convention
_parse(input: ParseInput): ParseReturnType<number> {
// detect decimal js object
if (
input.data !== null &&
typeof input.data === "object" &&
"toNumber" in input.data
) {
input.data = input.data.toNumber();
}
if (this._def.coerce) {
input.data = Number(input.data);
}
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.number) {
const ctx = this._getOrReturnCtx(input);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.number,
received: ctx.parsedType,
});
return INVALID;
}
let ctx: undefined | ParseContext = undefined;
const status = new ParseStatus();
for (const check of this._def.checks) {
if (check.kind === "precision") {
const parts = input.data.toString().match(precisionRegex);
const decimals = Math.max(
(parts[1] ? parts[1].length : 0) -
(parts[2] ? parseInt(parts[2], 10) : 0),
0
);
if (decimals > check.value) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.custom,
message: check.message,
params: {
precision: check.value,
},
});
status.dirty();
}
} else if (check.kind === "wholeNumber") {
const wholeNumber = input.data.toString().split(".")[0];
const tooLong = wholeNumber.length > check.value;
if (tooLong) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.custom,
message: check.message,
params: {
wholeNumber: check.value,
},
});
status.dirty();
}
} else if (check.kind === "min") {
const tooSmall = check.inclusive
? input.data < check.value
: input.data <= check.value;
if (tooSmall) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
minimum: check.value,
type: "number",
inclusive: check.inclusive,
exact: false,
message: check.message,
});
status.dirty();
}
} else if (check.kind === "max") {
const tooBig = check.inclusive
? input.data > check.value
: input.data >= check.value;
if (tooBig) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
maximum: check.value,
type: "number",
inclusive: check.inclusive,
exact: false,
message: check.message,
});
status.dirty();
}
} else if (check.kind === "finite") {
if (!Number.isFinite(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.not_finite,
message: check.message,
});
status.dirty();
}
}
}
return { status: status.value, value: input.data };
}
static create = (
params?: RawCreateParams & { coerce?: true }
): ZodDecimal => {
return new ZodDecimal({
checks: [],
typeName: zodDecimalKind,
coerce: params?.coerce ?? false,
});
};
protected setLimit(
kind: "min" | "max",
value: number,
inclusive: boolean,
message?: string
): ZodDecimal {
return new ZodDecimal({
...this._def,
checks: [
...this._def.checks,
{
kind,
value,
inclusive,
message,
},
],
});
}
_addCheck(check: ZodDecimalCheck): ZodDecimal {
return new ZodDecimal({
...this._def,
checks: [...this._def.checks, check],
});
}
lte(value: number, message?: string): ZodDecimal {
return this.setLimit("max", value, true, message);
}
lt(value: number, message?: string): ZodDecimal {
return this.setLimit("max", value, false, message);
}
max = this.lte;
gt(value: number, message?: string): ZodDecimal {
return this.setLimit("min", value, false, message);
}
gte(value: number, message?: string): ZodDecimal {
return this.setLimit("min", value, true, message);
}
min = this.gte;
precision(value: number, message?: string): ZodDecimal {
return this._addCheck({
kind: "precision",
value,
message,
});
}
wholeNumber(value: number, message?: string): ZodDecimal {
return this._addCheck({
kind: "wholeNumber",
value,
message,
});
}
get minValue() {
let min: number | null = null;
for (const ch of this._def.checks) {
if (ch.kind === "min") {
if (min === null || ch.value > min) min = ch.value;
}
}
return min;
}
get maxValue() {
let max: number | null = null;
for (const ch of this._def.checks) {
if (ch.kind === "max") {
if (max === null || ch.value < max) max = ch.value;
}
}
return max;
}
positive(message?: string) {
return this._addCheck({
kind: "min",
value: 0,
inclusive: false,
message,
});
}
negative(message?: string) {
return this._addCheck({
kind: "max",
value: 0,
inclusive: false,
message,
});
}
nonpositive(message?: string) {
return this._addCheck({
kind: "max",
value: 0,
inclusive: true,
message,
});
}
nonnegative(message?: string) {
return this._addCheck({
kind: "min",
value: 0,
inclusive: true,
message,
});
}
finite(message?: string) {
return this._addCheck({
kind: "finite",
message,
});
}
safe(message?: string) {
return this._addCheck({
kind: "min",
inclusive: true,
value: Number.MIN_SAFE_INTEGER,
message,
})._addCheck({
kind: "max",
inclusive: true,
value: Number.MAX_SAFE_INTEGER,
message,
});
}
get isFinite() {
let max: number | null = null,
min: number | null = null;
for (const ch of this._def.checks) {
if (ch.kind === "finite") {
return true;
} else if (ch.kind === "min") {
if (min === null || ch.value > min) min = ch.value;
} else if (ch.kind === "max") {
if (max === null || ch.value < max) max = ch.value;
}
}
return Number.isFinite(min) && Number.isFinite(max);
}
}
// eslint-disable-next-line @typescript-eslint/naming-convention
export const zodDecimal = ZodDecimal.create;

View File

@ -0,0 +1,34 @@
"use server";
import React from "react";
import SelectEmployeeFrom from "./selectFrom";
import { redirect } from "next/navigation";
// import LeftSidePanel from "@/components/login/leftsidepanel";
{
/* <LeftSidePanel textLabel="Evyos Yönetim Modülüne hoşgeldiniz" /> */
}
interface InterfaceLoginSelectEmployee {
companyList: any;
}
const LoginSelectEmployeeCard: React.FC<InterfaceLoginSelectEmployee> = async (
companyList: any
) => {
if (!companyList) {
redirect("/login/email");
}
return (
<>
<div className="min-h-full min-w-full">
<h1 className="mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900">
Şirket Seçimi Yapınız
</h1>
<div>
<SelectEmployeeFrom companyList={companyList?.companyList} />
</div>
</div>
</>
);
};
export default LoginSelectEmployeeCard;

View File

@ -0,0 +1,70 @@
"use client";
import React from "react";
import Image from "next/image";
import { loginSelectEmployee } from "@/apicalls/login/login";
import { useRouter } from "next/navigation";
interface InterfaceSelectEmployeeFrom {
companyList: any;
}
const SelectEmployeeFrom: React.FC<InterfaceSelectEmployeeFrom> = ({
companyList,
}) => {
const router = useRouter();
console.log("companyList", companyList);
function onClick(data: any) {
loginSelectEmployee({ company_uu_id: data?.uu_id })
.then((responseData: any) => {
if (responseData?.completed) {
router.push("/dashboard");
}
})
.catch((error) => {
console.error(error);
});
}
return (
<>
<div className="sm:grid sm:grid-cols-3 sm:gap-4">
{companyList.map((data: any) => (
<div className="flex" key={data.uu_id}>
<div
className="flex sm:h-56 hover:bg-white bg-emerald-800 m-3 ring-1 shadow-xl ring-emerald-700 p-3 h-64 rounded-2xl"
key={data.uu_id}
onClick={() => onClick(data)}
>
<div className="lg:w-1/4">
<Image
src={"/green-house.webp"}
alt={`Evyos ${data.uu_id}`}
className=" w-full h-full object-cover"
width={300}
height={300}
/>
</div>
<div className="lg:w-3/4 m-5">
<h2 className="text-lg font-bold mb-2">UUID : {data.uu_id}</h2>
<h2 className="text-lg font-bold mb-2">
Şirket Unvanı : {data.public_name}
</h2>
<h2 className="text-lg font-bold mb-2">
Şirket Tipi Name : {data.company_type}
</h2>
<h2 className="text-lg font-bold mb-2">
Adres :{" "}
{data.company_address
? data.company_address
: "Tanımlı Değil"}
</h2>
</div>
</div>
</div>
))}
</div>
</>
);
};
export default SelectEmployeeFrom;

View File

@ -0,0 +1,30 @@
"use server";
import React from "react";
import SelectOccupantFrom from "./selectFrom";
// import LeftSidePanel from "@/components/login/leftsidepanel";
{
/* <LeftSidePanel textLabel="Evyos Yönetim Modülüne hoşgeldiniz. Lütfen mail adresinizi ve şifreniz ile giriş yapınız." /> */
}
interface interfaceLoginSelectOccupant {
availableOccupants: any;
}
const LoginSelectOccupantCard: React.FC<interfaceLoginSelectOccupant> = async ({
availableOccupants,
}) => {
return (
<>
<div className="min-h-full min-w-full">
<h1 className="mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900">
Görev Seçimi Yapınız
</h1>
<div>
<SelectOccupantFrom availableOccupants={availableOccupants} />
</div>
</div>
</>
);
};
export default LoginSelectOccupantCard;

View File

@ -0,0 +1,154 @@
"use client";
import React from "react";
import { useRouter } from "next/navigation";
import { loginSelectOccupant } from "@/apicalls/login/login";
import Image from "next/image";
interface Building {
build_uu_id: string;
build_name: string;
build_no: string;
occupants: Array<any>;
}
interface InterfaceSelectOccupanyFrom {
availableOccupants: Record<string, Building>;
}
const SelectOccupantFrom: React.FC<InterfaceSelectOccupanyFrom> = ({
availableOccupants,
}) => {
const router = useRouter();
const [activeBuildingList, setActiveBuildingList] = React.useState<
Building[]
>([]);
const [activeOccupantList, setActiveOccupantList] = React.useState<any[]>([]);
const [isBuildingSelected, setIsBuildingSelected] = React.useState(false);
const [selectedBuilding, setSelectedBuilding] = React.useState("");
React.useEffect(() => {
if (!isBuildingSelected) {
const uniqueBuildings = Object.values(availableOccupants).map(
(value) => ({
build_uu_id: value.build_uu_id,
build_name: value.build_name,
build_no: value.build_no,
occupants: value.occupants,
})
);
setActiveBuildingList(uniqueBuildings);
}
}, [availableOccupants, isBuildingSelected]);
React.useEffect(() => {
if (isBuildingSelected && selectedBuilding) {
const selectedOccupants =
Object.values(availableOccupants).find(
(value) => value.build_uu_id === selectedBuilding
)?.occupants || [];
setActiveOccupantList(selectedOccupants);
}
}, [isBuildingSelected, selectedBuilding, availableOccupants]);
const onClickBuild = (data: Building) => {
setSelectedBuilding(data.build_uu_id);
setIsBuildingSelected(true);
};
const onClick = (data: any) => {
loginSelectOccupant({
build_part_uu_id: data?.part_uu_id,
occupant_uu_id: data?.uu_id,
selectedBuilding: selectedBuilding,
})
.then((responseData: { completed: boolean }) => {
console.log("responseData", responseData);
console.log("responseData.completed", responseData?.completed);
if (responseData?.completed) {
router.replace("/dashboard");
}
})
.catch((error) => {
console.error("Login error:", error);
});
};
return (
<>
{!isBuildingSelected ? (
<div className="3xl:grid 3xl:grid-cols-3 3xl:gap-4">
{activeBuildingList.map((data: any) => (
<div
className="flex sm:h-56 hover:bg-white bg-emerald-800 m-3 ring-1 shadow-xl ring-emerald-700 p-3 h-64 rounded-2xl"
onClick={() => onClickBuild(data)}
key={data.build_uu_id}
>
<div className="w-1/4">
<Image
src={"/green-house.webp"}
alt={`Evyos ${data.build_uu_id}`}
className=" w-full h-full object-cover"
width={300}
height={300}
/>
</div>
<div className="w-3/4 m-5">
<h2 className="text-lg font-bold mb-2">
UUID : {data.build_uu_id}
</h2>
<h2 className="text-lg font-bold mb-2">
Bina : {data.build_name}
</h2>
<h2 className="text-lg font-bold mb-2">
Bina No : {data.build_no}
</h2>
{/* <h2 className="text-lg font-bold mb-2">
Adres :{" "}
{data.company_address
? data.company_address
: "Tanımlı Değil"}
</h2> */}
</div>
</div>
))}
</div>
) : (
<div className="3xl:grid 3xl:grid-cols-3 3xl:gap-4">
{activeOccupantList.map((data: any) => (
<div
className="flex sm:h-56 hover:bg-white bg-emerald-800 m-3 ring-1 shadow-xl ring-emerald-700 p-3 h-64 rounded-2xl"
key={`${data.part_uu_id}-${data.uu_id}`}
onClick={() => onClick(data)}
>
<div className="w-1/4">
<Image
src={"/green-house.webp"}
alt={`Evyos ${data.part_uu_id}`}
className=" w-full h-full object-cover"
width={300}
height={300}
/>
</div>
<div className="w-3/4 m-5">
<h2 className="text-lg font-bold mb-2">
UUID : {data.part_uu_id}
</h2>
<h2 className="text-lg font-bold mb-2">
Bina : {data.part_name}
</h2>
<h2 className="text-lg font-bold mb-2">
Daire Kat : {data.part_level}
</h2>
<h2 className="text-lg font-bold mb-2">
Giriş Tipi : {data.code} - {data.description}
</h2>
</div>
</div>
))}
</div>
)}
</>
);
};
export default SelectOccupantFrom;

View File

@ -0,0 +1,23 @@
"use client";
import React from "react";
import SignInForm from "./singInForm";
import LeftSidePanel from "@/components/login/leftsidepanel";
const LoginWithEmail: React.FC = () => {
return (
<>
<div className="absolute top-0 left-0 min-w-full min-h-full">
<div className="mx-auto max-w-screen-2xl p-4 md:p-6 2xl:p-10">
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="flex flex-wrap items-center">
<LeftSidePanel textLabel="Evyos Yönetim Modülüne hoşgeldiniz. Lütfen mail adresinizi ve şifreniz ile giriş yapınız." />
<SignInForm />
</div>
</div>
</div>
</div>
</>
);
};
export default LoginWithEmail;

View File

@ -0,0 +1,17 @@
import * as z from "zod";
const loginSchema = z.object({
loginEmailInput: z
.string()
.min(4, { message: "Email adresi minimum 4 karaterden oluşmalıdır" })
.email("Geçerli bir mail adresi giriniz")
.default(""),
loginPassword: z
.string()
.min(5, { message: "Şifre 6 karakterden az olamaz" })
.default(""),
loginRememberMe: z.boolean().optional().default(false),
});
export type LoginFormSchema = z.infer<typeof loginSchema>;
export { loginSchema };

View File

@ -0,0 +1,154 @@
"use client";
import React from "react";
import { Eye, EyeOff, Mail } from "lucide-react";
import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import { loginViaAccessKeys } from "@/apicalls/login/login";
import { zodResolver } from "@hookform/resolvers/zod";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Switch } from "@/components/ui/switch";
import { loginSchema, LoginFormSchema } from "./schema";
import {
GoogleButton,
LoginButton,
ChangeSignTypeButton,
} from "@/components/login/buttons";
const SignInForm: React.FC = () => {
const router = useRouter();
const [showPassword, setShowPassword] = React.useState(false);
const form = useForm<LoginFormSchema>({
resolver: zodResolver(loginSchema),
});
function onSubmit(values: LoginFormSchema) {
loginViaAccessKeys({
domain: "evyos.com.tr",
accessKey: values.loginEmailInput,
password: values.loginPassword,
rememberMe: values.loginRememberMe,
})
.then((res: any) => {
console.log("res", res);
if (res?.completed) {
setTimeout(() => {
router.push("/login/select");
}, 1000);
}
})
.catch((error) => {
console.log("error", error);
});
}
return (
<>
<div className="w-full border-stroke dark:border-strokedark xl:w-1/2 xl:border-l-2">
<div className="w-full p-4 sm:p-12.5 xl:p-17.5">
<h2 className="my-9 text-center text-2xl font-bold text-black dark:text-white sm:text-title-xl2">
Mail ile Giriş Yapın
</h2>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-5 max-w-3xl mx-auto py-10"
>
<div className="mb-4">
<label className="mb-2.5 block font-medium text-black dark:text-white">
Email
</label>
<div className="relative">
<FormField
control={form.control}
name="loginEmailInput"
render={({ field }) => (
<FormItem>
<FormControl>
<input
type="email"
placeholder="example@example.net"
className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
{...field}
value={field.value || ""}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<span className="absolute right-4 top-4">
<Mail />
</span>
</div>
</div>
<div className="mb-6">
<label className="mb-2.5 block font-medium text-black dark:text-white">
Password
</label>
<div className="relative">
<FormField
control={form.control}
name="loginPassword"
render={({ field }) => (
<FormItem>
<FormControl>
<input
type={showPassword ? "text" : "password"}
placeholder="Şifre giriniz"
className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
{...field}
value={field.value || ""}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<span
className="absolute right-4 top-4"
onClick={() => setShowPassword(!showPassword)}
>
{!showPassword ? <Eye /> : <EyeOff />}
</span>
</div>
</div>
<FormField
control={form.control}
name="loginRememberMe"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<FormLabel className="text-lg">Beni Hatırla</FormLabel>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
aria-readonly
/>
</FormControl>
</FormItem>
)}
/>
<LoginButton />
<GoogleButton />
</form>
</Form>
<ChangeSignTypeButton buttonType="phone" />
</div>
</div>
</>
);
};
export default SignInForm;

View File

@ -0,0 +1,23 @@
"use client";
import React from "react";
import SignPhoneInForm from "./singInForm";
import LeftSidePanel from "@/components/login/leftsidepanel";
const LoginWithPhone: React.FC = () => {
return (
<>
<div className="absolute top-0 left-0 min-w-full min-h-full">
<div className="mx-auto max-w-screen-2xl p-4 md:p-6 2xl:p-10">
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="flex flex-wrap items-center">
<LeftSidePanel textLabel="Evyos Yönetim Modülüne hoşgeldiniz. Lütfen telefon ve şifreniz ile giriş yapınız." />
<SignPhoneInForm />
</div>
</div>
</div>
</div>
</>
);
};
export default LoginWithPhone;

View File

@ -0,0 +1,17 @@
import * as z from "zod";
const loginPhoneSchema = z.object({
loginPhone: z
.string()
.min(10, { message: "Telefon numarası 10 karakterden az olamaz" })
.max(14, { message: "Telefon numarası 14 karakterden fazla olamaz" })
.default(""),
loginPassword: z
.string()
.min(5, { message: "Şifre 6 karakterden az olamaz" })
.default(""),
loginRememberMe: z.boolean().optional().default(false),
});
export type LoginPhoneFormSchema = z.infer<typeof loginPhoneSchema>;
export { loginPhoneSchema };

View File

@ -0,0 +1,153 @@
"use client";
import React from "react";
import { Eye, EyeOff, Mail } from "lucide-react";
import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import { loginViaAccessKeys } from "@/apicalls/login/login";
import { zodResolver } from "@hookform/resolvers/zod";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Switch } from "@/components/ui/switch";
import { LoginPhoneFormSchema, loginPhoneSchema } from "./schema";
import {
GoogleButton,
LoginButton,
ChangeSignTypeButton,
} from "@/components/login/buttons";
const SignPhoneInForm: React.FC = () => {
const router = useRouter();
const [showPassword, setShowPassword] = React.useState(false);
const form = useForm<LoginPhoneFormSchema>({
resolver: zodResolver(loginPhoneSchema),
});
function onSubmit(values: LoginPhoneFormSchema) {
loginViaAccessKeys({
domain: "evyos.com.tr",
accessKey: values.loginPhone,
password: values.loginPassword,
rememberMe: values.loginRememberMe,
})
.then((res: any) => {
if (res?.completed) {
setTimeout(() => {
router.push("/login/select");
}, 1000);
}
})
.catch((error) => {
console.log("error", error);
});
}
return (
<>
<div className="w-full border-stroke dark:border-strokedark xl:w-1/2 xl:border-l-2">
<div className="w-full p-4 sm:p-12.5 xl:p-17.5">
<h2 className="my-9 text-center text-2xl font-bold text-black dark:text-white sm:text-title-xl2">
Mail ile Giriş Yapın
</h2>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-5 max-w-3xl mx-auto py-10"
>
<div className="mb-4">
<label className="mb-2.5 block font-medium text-black dark:text-white">
Email
</label>
<div className="relative">
<FormField
control={form.control}
name="loginPhone"
render={({ field }) => (
<FormItem>
<FormControl>
<input
type="email"
placeholder="example@example.net"
className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
{...field}
value={field.value || ""}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<span className="absolute right-4 top-4">
<Mail />
</span>
</div>
</div>
<div className="mb-6">
<label className="mb-2.5 block font-medium text-black dark:text-white">
Password
</label>
<div className="relative">
<FormField
control={form.control}
name="loginPassword"
render={({ field }) => (
<FormItem>
<FormControl>
<input
type={showPassword ? "text" : "password"}
placeholder="Şifre giriniz"
className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
{...field}
value={field.value || ""}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<span
className="absolute right-4 top-4"
onClick={() => setShowPassword(!showPassword)}
>
{!showPassword ? <Eye /> : <EyeOff />}
</span>
</div>
</div>
<FormField
control={form.control}
name="loginRememberMe"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<FormLabel className="text-lg">Beni Hatırla</FormLabel>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
aria-readonly
/>
</FormControl>
</FormItem>
)}
/>
<LoginButton />
<GoogleButton />
</form>
</Form>
<ChangeSignTypeButton buttonType="email" />
</div>
</div>
</>
);
};
export default SignPhoneInForm;

62
tailwind.config.ts Normal file
View File

@ -0,0 +1,62 @@
import type { Config } from "tailwindcss";
export default {
darkMode: ["class"],
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
chart: {
'1': 'hsl(var(--chart-1))',
'2': 'hsl(var(--chart-2))',
'3': 'hsl(var(--chart-3))',
'4': 'hsl(var(--chart-4))',
'5': 'hsl(var(--chart-5))'
}
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
}
}
},
plugins: [require("tailwindcss-animate")],
} satisfies Config;

29
tsconfig.json Normal file
View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/apicalls/*": ["./apicalls/*"],
"@/apimaps/*": ["./apimaps/*"],
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}