client frontend added
This commit is contained in:
90
web_services/client_frontend/package-lock.json
generated
90
web_services/client_frontend/package-lock.json
generated
@@ -39,6 +39,7 @@
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"flatpickr": "^4.6.13",
|
||||
"ioredis": "^5.6.1",
|
||||
"lucide-react": "^0.487.0",
|
||||
"next": "^15.2.4",
|
||||
"next-crypto": "^1.0.8",
|
||||
@@ -739,6 +740,12 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
|
||||
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@isaacs/fs-minipass": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||
@@ -3700,6 +3707,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/cluster-key-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz",
|
||||
@@ -3922,7 +3938,6 @@
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
@@ -3979,6 +3994,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
@@ -5358,6 +5382,30 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ioredis": {
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz",
|
||||
"integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ioredis/commands": "^1.1.1",
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
"debug": "^4.3.4",
|
||||
"denque": "^2.1.0",
|
||||
"lodash.defaults": "^4.2.0",
|
||||
"lodash.isarguments": "^3.1.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0",
|
||||
"standard-as-callback": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.22.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ioredis"
|
||||
}
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
@@ -6181,6 +6229,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@@ -6366,7 +6426,6 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
@@ -7096,6 +7155,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"redis-errors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect.getprototypeof": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
||||
@@ -7605,6 +7685,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/standard-as-callback": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
|
||||
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"flatpickr": "^4.6.13",
|
||||
"ioredis": "^5.6.1",
|
||||
"lucide-react": "^0.487.0",
|
||||
"next": "^15.2.4",
|
||||
"next-crypto": "^1.0.8",
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
"use server";
|
||||
import NextCrypto from "next-crypto";
|
||||
import { fetchData, fetchDataWithToken } from "@/apicalls/api-fetcher";
|
||||
import { baseUrlAuth, cookieObject, tokenSecret } from "@/apicalls/basics";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
const loginEndpoint = `${baseUrlAuth}/authentication/login`;
|
||||
const loginSelectEndpoint = `${baseUrlAuth}/authentication/select`;
|
||||
const logoutEndpoint = `${baseUrlAuth}/authentication/logout`;
|
||||
|
||||
console.log("loginEndpoint", loginEndpoint);
|
||||
console.log("loginSelectEndpoint", loginSelectEndpoint);
|
||||
|
||||
interface LoginViaAccessKeys {
|
||||
accessKey: string;
|
||||
password: string;
|
||||
rememberMe: boolean;
|
||||
}
|
||||
|
||||
interface LoginSelectEmployee {
|
||||
company_uu_id: string;
|
||||
}
|
||||
|
||||
interface LoginSelectOccupant {
|
||||
build_living_space_uu_id: any;
|
||||
}
|
||||
|
||||
async function logoutActiveSession() {
|
||||
const cookieStore = await cookies();
|
||||
const response = await fetchDataWithToken(logoutEndpoint, {}, "GET", false);
|
||||
cookieStore.delete("accessToken");
|
||||
cookieStore.delete("accessObject");
|
||||
cookieStore.delete("userProfile");
|
||||
cookieStore.delete("userSelection");
|
||||
return response;
|
||||
}
|
||||
|
||||
async function loginViaAccessKeys(payload: LoginViaAccessKeys) {
|
||||
try {
|
||||
const cookieStore = await cookies();
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
|
||||
const response = await fetchData(
|
||||
loginEndpoint,
|
||||
{
|
||||
access_key: payload.accessKey,
|
||||
password: payload.password,
|
||||
remember_me: payload.rememberMe,
|
||||
},
|
||||
"POST",
|
||||
false
|
||||
);
|
||||
console.log("response", response);
|
||||
if (response.status === 200 || response.status === 202) {
|
||||
const loginRespone: any = response?.data;
|
||||
const accessToken = await nextCrypto.encrypt(loginRespone.access_token);
|
||||
const accessObject = await nextCrypto.encrypt(
|
||||
JSON.stringify({
|
||||
userType: loginRespone.user_type,
|
||||
selectionList: loginRespone.selection_list,
|
||||
})
|
||||
);
|
||||
const userProfile = await nextCrypto.encrypt(
|
||||
JSON.stringify(loginRespone.user)
|
||||
);
|
||||
|
||||
cookieStore.set({
|
||||
name: "accessToken",
|
||||
value: accessToken,
|
||||
...cookieObject,
|
||||
});
|
||||
cookieStore.set({
|
||||
name: "accessObject",
|
||||
value: accessObject,
|
||||
...cookieObject,
|
||||
});
|
||||
cookieStore.set({
|
||||
name: "userProfile",
|
||||
value: JSON.stringify(userProfile),
|
||||
...cookieObject,
|
||||
});
|
||||
try {
|
||||
return {
|
||||
completed: true,
|
||||
message: "Login successful",
|
||||
status: 200,
|
||||
data: loginRespone,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("JSON parse error:", error);
|
||||
return {
|
||||
completed: false,
|
||||
message: "Login NOT successful",
|
||||
status: 401,
|
||||
data: "{}",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
completed: false,
|
||||
// error: response.error || "Login failed",
|
||||
// message: response.message || "Authentication failed",
|
||||
status: response.status || 500,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Login error:", error);
|
||||
return {
|
||||
completed: false,
|
||||
// error: error instanceof Error ? error.message : "Login error",
|
||||
// message: "An error occurred during login",
|
||||
status: 500,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function loginSelectEmployee(payload: LoginSelectEmployee) {
|
||||
const cookieStore = await cookies();
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
const companyUUID = payload.company_uu_id;
|
||||
const selectResponse: any = await fetchDataWithToken(
|
||||
loginSelectEndpoint,
|
||||
{
|
||||
company_uu_id: companyUUID,
|
||||
},
|
||||
"POST",
|
||||
false
|
||||
);
|
||||
cookieStore.delete("userSelection");
|
||||
|
||||
if (selectResponse.status === 200 || selectResponse.status === 202) {
|
||||
const usersSelection = await nextCrypto.encrypt(
|
||||
JSON.stringify({
|
||||
selected: companyUUID,
|
||||
user_type: "employee",
|
||||
})
|
||||
);
|
||||
cookieStore.set({
|
||||
name: "userSelection",
|
||||
value: usersSelection,
|
||||
...cookieObject,
|
||||
});
|
||||
}
|
||||
return selectResponse;
|
||||
}
|
||||
|
||||
async function loginSelectOccupant(payload: LoginSelectOccupant) {
|
||||
const livingSpaceUUID = payload.build_living_space_uu_id;
|
||||
const cookieStore = await cookies();
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
const selectResponse: any = await fetchDataWithToken(
|
||||
loginSelectEndpoint,
|
||||
{
|
||||
build_living_space_uu_id: livingSpaceUUID,
|
||||
},
|
||||
"POST",
|
||||
false
|
||||
);
|
||||
cookieStore.delete("userSelection");
|
||||
|
||||
if (selectResponse.status === 200 || selectResponse.status === 202) {
|
||||
const usersSelection = await nextCrypto.encrypt(
|
||||
JSON.stringify({
|
||||
selected: livingSpaceUUID,
|
||||
user_type: "occupant",
|
||||
})
|
||||
);
|
||||
cookieStore.set({
|
||||
name: "userSelection",
|
||||
value: usersSelection,
|
||||
...cookieObject,
|
||||
});
|
||||
}
|
||||
return selectResponse;
|
||||
}
|
||||
|
||||
export {
|
||||
loginViaAccessKeys,
|
||||
loginSelectEmployee,
|
||||
loginSelectOccupant,
|
||||
logoutActiveSession,
|
||||
};
|
||||
@@ -1,148 +0,0 @@
|
||||
"use server";
|
||||
import { fetchDataWithToken } from "@/apicalls/api-fetcher";
|
||||
import { baseUrlAuth, tokenSecret } from "@/apicalls/basics";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
import NextCrypto from "next-crypto";
|
||||
|
||||
const checkToken = `${baseUrlAuth}/authentication/token/check`;
|
||||
const pageValid = `${baseUrlAuth}/authentication/page/valid`;
|
||||
const siteUrls = `${baseUrlAuth}/authentication/sites/list`;
|
||||
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
|
||||
async function checkAccessTokenIsValid() {
|
||||
const response = await fetchDataWithToken(checkToken, {}, "GET", false);
|
||||
return response?.status === 200 || response?.status === 202 ? true : false;
|
||||
}
|
||||
|
||||
async function retrievePageList() {
|
||||
const response: any = await fetchDataWithToken(siteUrls, {}, "GET", false);
|
||||
return response?.status === 200 || response?.status === 202
|
||||
? response.data?.sites
|
||||
: null;
|
||||
}
|
||||
|
||||
async function retrieveApplicationbyUrl(pageUrl: string) {
|
||||
const response: any = await fetchDataWithToken(
|
||||
pageValid,
|
||||
{
|
||||
page_url: pageUrl,
|
||||
},
|
||||
"POST",
|
||||
false
|
||||
);
|
||||
return response?.status === 200 || response?.status === 202
|
||||
? response.data?.application
|
||||
: null;
|
||||
}
|
||||
|
||||
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 : 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 retrieveUserSelection() {
|
||||
const cookieStore = await cookies();
|
||||
const encrpytUserSelection = cookieStore.get("userSelection")?.value || "";
|
||||
|
||||
let objectUserSelection = {};
|
||||
let decrpytUserSelection: any = await nextCrypto.decrypt(
|
||||
encrpytUserSelection
|
||||
);
|
||||
decrpytUserSelection = decrpytUserSelection
|
||||
? JSON.parse(decrpytUserSelection)
|
||||
: null;
|
||||
console.log("decrpytUserSelection", decrpytUserSelection);
|
||||
const userSelection = decrpytUserSelection?.selected;
|
||||
const accessObjects = (await retrieveAccessObjects()) || {};
|
||||
console.log("accessObjects", accessObjects);
|
||||
|
||||
if (decrpytUserSelection?.user_type === "employee") {
|
||||
const companyList = accessObjects?.selectionList;
|
||||
const selectedCompany = companyList.find(
|
||||
(company: any) => company.uu_id === userSelection
|
||||
);
|
||||
if (selectedCompany) {
|
||||
objectUserSelection = { userType: "employee", selected: selectedCompany };
|
||||
}
|
||||
} else if (decrpytUserSelection?.user_type === "occupant") {
|
||||
const buildingsList = accessObjects?.selectionList;
|
||||
|
||||
// Iterate through all buildings
|
||||
if (buildingsList) {
|
||||
// Loop through each building
|
||||
for (const buildKey in buildingsList) {
|
||||
const building = buildingsList[buildKey];
|
||||
|
||||
// Check if the building has occupants
|
||||
if (building.occupants && building.occupants.length > 0) {
|
||||
// Find the occupant with the matching build_living_space_uu_id
|
||||
const occupant = building.occupants.find(
|
||||
(occ: any) => occ.build_living_space_uu_id === userSelection
|
||||
);
|
||||
|
||||
if (occupant) {
|
||||
objectUserSelection = {
|
||||
userType: "occupant",
|
||||
selected: {
|
||||
...occupant,
|
||||
buildName: building.build_name,
|
||||
buildNo: building.build_no,
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
...objectUserSelection,
|
||||
};
|
||||
}
|
||||
|
||||
// const avatarInfo = await retrieveAvatarInfo();
|
||||
// lang: avatarInfo?.data?.lang
|
||||
// ? String(avatarInfo?.data?.lang).toLowerCase()
|
||||
// : undefined,
|
||||
// avatar: avatarInfo?.data?.avatar,
|
||||
// fullName: avatarInfo?.data?.full_name,
|
||||
// async function retrieveAvatarInfo() {
|
||||
// const response = await fetchDataWithToken(
|
||||
// `${baseUrlAuth}/authentication/avatar`,
|
||||
// {},
|
||||
// "POST"
|
||||
// );
|
||||
// return response;
|
||||
// }
|
||||
|
||||
export {
|
||||
checkAccessTokenIsValid,
|
||||
retrieveAccessToken,
|
||||
retrieveUserType,
|
||||
retrieveAccessObjects,
|
||||
retrieveUserSelection,
|
||||
retrieveApplicationbyUrl,
|
||||
retrievePageList,
|
||||
// retrieveavailablePages,
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
"use server";
|
||||
import { retrieveAccessToken } from "@/apicalls/mutual/cookies/token";
|
||||
import { retrieveAccessToken } from "@/apifetchers/mutual/cookies/token";
|
||||
import {
|
||||
DEFAULT_RESPONSE,
|
||||
defaultHeaders,
|
||||
@@ -27,30 +27,6 @@ const createTimeoutPromise = (
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepares a standardized API response
|
||||
* @param response The response data
|
||||
* @param statusCode HTTP status code
|
||||
* @returns Standardized API response
|
||||
*/
|
||||
const prepareResponse = <T>(
|
||||
response: T,
|
||||
statusCode: number
|
||||
): ApiResponse<T> => {
|
||||
try {
|
||||
return {
|
||||
status: statusCode,
|
||||
data: response || ({} as T),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error preparing response:", error);
|
||||
return {
|
||||
...DEFAULT_RESPONSE,
|
||||
error: "Response parsing error",
|
||||
} as ApiResponse<T>;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Core fetch function with timeout and error handling
|
||||
* @param url The URL to fetch
|
||||
@@ -66,22 +42,22 @@ async function coreFetch<T>(
|
||||
payload?: any
|
||||
): Promise<ApiResponse<T>> {
|
||||
const { method = "POST", cache = false, timeout = DEFAULT_TIMEOUT } = options;
|
||||
|
||||
|
||||
try {
|
||||
// Setup controller for timeout handling
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
|
||||
|
||||
// Prepare fetch options
|
||||
const fetchOptions: RequestInit = {
|
||||
method,
|
||||
headers,
|
||||
cache: cache ? "force-cache" : "no-cache",
|
||||
signal,
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
// Add body if needed
|
||||
// Add body for non-GET requests with payload
|
||||
if (method !== "GET" && payload) {
|
||||
fetchOptions.body = JSON.stringify(
|
||||
// Handle special case for updateDataWithToken
|
||||
payload.payload ? payload.payload : payload
|
||||
);
|
||||
}
|
||||
@@ -89,22 +65,22 @@ async function coreFetch<T>(
|
||||
// Create timeout promise
|
||||
const timeoutPromise = createTimeoutPromise(timeout, controller);
|
||||
|
||||
// Race between fetch and timeout
|
||||
const response = (await Promise.race([
|
||||
// Execute request with timeout
|
||||
const response = await Promise.race([
|
||||
fetch(url, fetchOptions),
|
||||
timeoutPromise,
|
||||
])) as Response;
|
||||
]);
|
||||
|
||||
const responseJson = await response.json();
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
console.log("Fetching:", url, fetchOptions);
|
||||
console.log("Response:", responseJson);
|
||||
}
|
||||
|
||||
return prepareResponse(responseJson, response.status);
|
||||
// Parse response
|
||||
const responseData = await response.json();
|
||||
|
||||
// Return standardized response
|
||||
return {
|
||||
status: response.status,
|
||||
data: responseData || ({} as T),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Fetch error (${url}):`, error);
|
||||
console.error(`API Error (${url}):`, error);
|
||||
return {
|
||||
...DEFAULT_RESPONSE,
|
||||
error: error instanceof Error ? error.message : "Network error",
|
||||
@@ -19,7 +19,7 @@ const baseUrlBuilding = formatServiceUrl(
|
||||
process.env.NEXT_PUBLIC_BUILDING_SERVICE_URL || "building_service:8006"
|
||||
);
|
||||
const baseUrlPeople = formatServiceUrl(
|
||||
process.env.NEXT_PUBLIC_VALIDATION_SERVICE_URL || "identity_service:8009"
|
||||
process.env.NEXT_PUBLIC_VALIDATION_SERVICE_URL || "validation_service:8009"
|
||||
);
|
||||
|
||||
// Types for better type safety
|
||||
@@ -37,7 +37,8 @@ interface FetchOptions {
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
const tokenSecret = process.env.TOKENSECRET_90 || "";
|
||||
const tokenSecret =
|
||||
process.env.TOKENSECRET_90 || "787a4a44-2a27-454d-8918-35ceab50592d";
|
||||
|
||||
const cookieObject: any = {
|
||||
httpOnly: true,
|
||||
@@ -53,7 +54,7 @@ const DEFAULT_TIMEOUT = 10000; // 10 seconds
|
||||
const defaultHeaders = {
|
||||
accept: "application/json",
|
||||
language: "tr",
|
||||
domain: "management.com.tr",
|
||||
domain: "evyos.com.tr",
|
||||
tz: "GMT+3",
|
||||
"Content-type": "application/json",
|
||||
};
|
||||
@@ -0,0 +1,218 @@
|
||||
"use server";
|
||||
import NextCrypto from "next-crypto";
|
||||
import { fetchData, fetchDataWithToken } from "@/apifetchers/api-fetcher";
|
||||
import { baseUrlAuth, cookieObject, tokenSecret } from "@/apifetchers/basics";
|
||||
import { cookies } from "next/headers";
|
||||
import { setNewCompleteToRedis, getCompleteFromRedis } from "@/apifetchers/mutual/context/complete/fetch";
|
||||
import {
|
||||
defaultClientHeader,
|
||||
defaultClientMenu,
|
||||
defaultClientOnline,
|
||||
defaultClientPageConfig,
|
||||
defaultLinkList
|
||||
} from "@/types/mutual/context/validations";
|
||||
import { retrievePageList } from "@/apifetchers/mutual/cookies/token";
|
||||
import { setMenuToRedis } from "@/apifetchers/mutual/context/page/menu/fetch";
|
||||
|
||||
const loginEndpoint = `${baseUrlAuth}/authentication/login`;
|
||||
const loginSelectEndpoint = `${baseUrlAuth}/authentication/select`;
|
||||
const logoutEndpoint = `${baseUrlAuth}/authentication/logout`;
|
||||
|
||||
interface LoginViaAccessKeys {
|
||||
accessKey: string;
|
||||
password: string;
|
||||
rememberMe: boolean;
|
||||
}
|
||||
|
||||
interface LoginSelect {
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
interface LoginSelectOccupant {
|
||||
uuid: any;
|
||||
}
|
||||
|
||||
async function logoutActiveSession() {
|
||||
const cookieStore = await cookies();
|
||||
const response = await fetchDataWithToken(logoutEndpoint, {}, "GET", false);
|
||||
cookieStore.delete("eys-zzz");
|
||||
cookieStore.delete("eys-yyy");
|
||||
cookieStore.delete("eys-sel");
|
||||
return response;
|
||||
}
|
||||
|
||||
async function initRedis(loginRespone: any, firstSelection: any, accessToken: string, redisKey: string) {
|
||||
let alreadyAtRedis = null
|
||||
try {
|
||||
const redisData = await getCompleteFromRedis();
|
||||
alreadyAtRedis = redisData;
|
||||
} catch (error) { }
|
||||
if (!alreadyAtRedis) {
|
||||
const loginObjectToRedis = {
|
||||
online: {
|
||||
...defaultClientOnline,
|
||||
lastLogin: new Date(),
|
||||
userType: `${loginRespone.user_type}`.toUpperCase(),
|
||||
lang: loginRespone.user.person.country_code.toLowerCase(),
|
||||
},
|
||||
pageConfig: defaultClientPageConfig,
|
||||
menu: defaultClientMenu,
|
||||
header: defaultClientHeader,
|
||||
selection: { selectionList: loginRespone.selection_list, activeSelection: firstSelection },
|
||||
user: loginRespone.user,
|
||||
settings: { lastOnline: new Date(), token: accessToken },
|
||||
chatRoom: defaultLinkList,
|
||||
notifications: defaultLinkList,
|
||||
messages: defaultLinkList,
|
||||
}
|
||||
await setNewCompleteToRedis(loginObjectToRedis, redisKey);
|
||||
}
|
||||
}
|
||||
|
||||
async function loginViaAccessKeys(payload: LoginViaAccessKeys) {
|
||||
const cookieStore = await cookies();
|
||||
try {
|
||||
const response = await fetchData(
|
||||
loginEndpoint,
|
||||
{
|
||||
access_key: payload.accessKey,
|
||||
password: payload.password,
|
||||
remember_me: payload.rememberMe,
|
||||
},
|
||||
"POST",
|
||||
false
|
||||
);
|
||||
if (response.status === 200 || response.status === 202) {
|
||||
|
||||
const loginRespone: any = response?.data;
|
||||
const firstSelection = loginRespone.selection_list.find((item: any) => item.uu_id === loginRespone.selection_list[0].uu_id);
|
||||
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
const accessToken = await nextCrypto.encrypt(loginRespone.access_token);
|
||||
|
||||
const redisKey = `CLIENT:${loginRespone.user_type.toUpperCase()}:${firstSelection.uu_id}`;
|
||||
const redisKeyAccess = await nextCrypto.encrypt(redisKey);
|
||||
const usersSelection = await nextCrypto.encrypt(
|
||||
JSON.stringify({
|
||||
selected: firstSelection.uu_id,
|
||||
userType: loginRespone.user_type.toUpperCase(),
|
||||
redisKey,
|
||||
})
|
||||
);
|
||||
|
||||
await initRedis(loginRespone, firstSelection, accessToken, redisKey);
|
||||
cookieStore.set({
|
||||
name: "eys-zzz",
|
||||
value: accessToken,
|
||||
...cookieObject,
|
||||
});
|
||||
cookieStore.set({
|
||||
name: "eys-yyy",
|
||||
value: redisKeyAccess,
|
||||
...cookieObject,
|
||||
});
|
||||
cookieStore.set({
|
||||
name: "eys-sel",
|
||||
value: usersSelection,
|
||||
...cookieObject,
|
||||
});
|
||||
try {
|
||||
return {
|
||||
completed: true,
|
||||
message: "Login successful",
|
||||
status: 200,
|
||||
data: loginRespone,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("JSON parse error:", error);
|
||||
return {
|
||||
completed: false,
|
||||
message: "Login NOT successful",
|
||||
status: 401,
|
||||
data: "{}",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
completed: false,
|
||||
// error: response.error || "Login failed",
|
||||
// message: response.message || "Authentication failed",
|
||||
status: response.status || 500,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Login error:", error);
|
||||
return {
|
||||
completed: false,
|
||||
// error: error instanceof Error ? error.message : "Login error",
|
||||
// message: "An error occurred during login",
|
||||
status: 500,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function loginSelectEmployee(payload: LoginSelect) {
|
||||
const cookieStore = await cookies();
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
const employeeUUID = payload.uuid;
|
||||
const redisKey = `CLIENT:EMPLOYEE:${employeeUUID}`;
|
||||
const selectResponse: any = await fetchDataWithToken(loginSelectEndpoint, { uuid: employeeUUID }, "POST", false);
|
||||
|
||||
cookieStore.delete("eys-sel");
|
||||
|
||||
if (selectResponse.status === 200 || selectResponse.status === 202) {
|
||||
const usersSelection = await nextCrypto.encrypt(
|
||||
JSON.stringify({
|
||||
selected: employeeUUID,
|
||||
userType: "employee",
|
||||
redisKey,
|
||||
})
|
||||
);
|
||||
cookieStore.set({
|
||||
name: "eys-sel",
|
||||
value: usersSelection,
|
||||
...cookieObject,
|
||||
});
|
||||
try {
|
||||
const pageList = await retrievePageList()
|
||||
await setMenuToRedis({
|
||||
selectionList: pageList,
|
||||
activeSelection: "/dashboard"
|
||||
})
|
||||
} catch (error) { }
|
||||
}
|
||||
return selectResponse;
|
||||
}
|
||||
|
||||
async function loginSelectOccupant(payload: LoginSelectOccupant) {
|
||||
const livingSpaceUUID = payload.uuid;
|
||||
const cookieStore = await cookies();
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
|
||||
const selectResponse: any = await fetchDataWithToken(loginSelectEndpoint, { uuid: livingSpaceUUID }, "POST", false);
|
||||
const redisKey = `CLIENT:OCCUPANT:${livingSpaceUUID}`;
|
||||
cookieStore.delete("eys-sel");
|
||||
|
||||
if (selectResponse.status === 200 || selectResponse.status === 202) {
|
||||
const usersSelection = await nextCrypto.encrypt(
|
||||
JSON.stringify({
|
||||
selected: livingSpaceUUID,
|
||||
userType: "OCCUPANT",
|
||||
redisKey,
|
||||
})
|
||||
);
|
||||
cookieStore.set({
|
||||
name: "eys-sel",
|
||||
value: usersSelection,
|
||||
...cookieObject,
|
||||
});
|
||||
}
|
||||
return selectResponse;
|
||||
}
|
||||
|
||||
export {
|
||||
loginViaAccessKeys,
|
||||
loginSelectEmployee,
|
||||
loginSelectOccupant,
|
||||
logoutActiveSession,
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
"use server";
|
||||
import { redis } from "@/lib/redis";
|
||||
import { functionRetrieveUserSelection } from "@/apifetchers/utils";
|
||||
import { ClientRedisToken } from "@/types/mutual/context/validations";
|
||||
|
||||
const getCompleteFromRedis = async (): Promise<ClientRedisToken> => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
const result = await redis.get(`${redisKey}`);
|
||||
if (!result) throw new Error("No data found in redis");
|
||||
return JSON.parse(result);
|
||||
}
|
||||
|
||||
const setCompleteToRedis = async (completeObject: ClientRedisToken) => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
if (!decrpytUserSelection) throw new Error("No user selection found");
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
if (!completeObject) throw new Error("No complete object provided");
|
||||
await redis.set(redisKey, JSON.stringify(completeObject));
|
||||
return true;
|
||||
}
|
||||
|
||||
const setNewCompleteToRedis = async (completeObject: ClientRedisToken, redisKey: string) => {
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
if (!completeObject) throw new Error("No complete object provided");
|
||||
await redis.set(redisKey, JSON.stringify(completeObject));
|
||||
return true;
|
||||
}
|
||||
|
||||
export { getCompleteFromRedis, setCompleteToRedis, setNewCompleteToRedis };
|
||||
@@ -0,0 +1,32 @@
|
||||
"use server";
|
||||
import { redis } from "@/lib/redis";
|
||||
import { functionRetrieveUserSelection } from "@/apifetchers/utils";
|
||||
import { ClientSelection } from "@/types/mutual/context/validations";
|
||||
import { getCompleteFromRedis, setCompleteToRedis } from "@/apifetchers/mutual/context/complete/fetch";
|
||||
|
||||
const getSelectionFromRedis = async (): Promise<ClientSelection> => {
|
||||
try {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection();
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (redisKey === "default") { return { selectionList: [], activeSelection: {} } }
|
||||
const result = await redis.get(`${redisKey}`);
|
||||
if (!result) { return { selectionList: [], activeSelection: {} } }
|
||||
const parsedResult = JSON.parse(result);
|
||||
if (!parsedResult.selection) { return { selectionList: [], activeSelection: {} } }
|
||||
return parsedResult.selection;
|
||||
} catch (error) { return { selectionList: [], activeSelection: {} } }
|
||||
}
|
||||
|
||||
const setSelectionToRedis = async (selectionObject: ClientSelection) => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
if (!decrpytUserSelection) throw new Error("No user selection found");
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
if (!selectionObject) throw new Error("No selection object provided");
|
||||
const oldData = await getCompleteFromRedis();
|
||||
if (!oldData) throw new Error("No old data found in redis");
|
||||
await setCompleteToRedis({ ...oldData, selection: selectionObject })
|
||||
return true;
|
||||
}
|
||||
|
||||
export { getSelectionFromRedis, setSelectionToRedis };
|
||||
@@ -0,0 +1,30 @@
|
||||
"use server";
|
||||
import { redis } from "@/lib/redis";
|
||||
import { functionRetrieveUserSelection } from "@/apifetchers/utils";
|
||||
import { ClientSettings } from "@/types/mutual/context/validations";
|
||||
import { getCompleteFromRedis, setCompleteToRedis } from "@/apifetchers/mutual/context/complete/fetch";
|
||||
|
||||
const getSettingsFromRedis = async (): Promise<ClientSettings> => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
const result = await redis.get(`${redisKey}`);
|
||||
if (!result) throw new Error("No data found in redis");
|
||||
const parsedResult = JSON.parse(result);
|
||||
if (!parsedResult.settings) throw new Error("No settings found in redis");
|
||||
return parsedResult.settings;
|
||||
}
|
||||
|
||||
const setSettingsToRedis = async (settingsObject: ClientSettings) => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
if (!decrpytUserSelection) throw new Error("No user selection found");
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
if (!settingsObject) throw new Error("No settings object provided");
|
||||
const oldData = await getCompleteFromRedis();
|
||||
if (!oldData) throw new Error("No old data found in redis");
|
||||
await setCompleteToRedis({ ...oldData, settings: settingsObject })
|
||||
return true;
|
||||
}
|
||||
|
||||
export { getSettingsFromRedis, setSettingsToRedis };
|
||||
@@ -0,0 +1,30 @@
|
||||
"use server";
|
||||
import { redis } from "@/lib/redis";
|
||||
import { functionRetrieveUserSelection } from "@/apifetchers/utils";
|
||||
import { ClientUser } from "@/types/mutual/context/validations";
|
||||
import { getCompleteFromRedis, setCompleteToRedis } from "@/apifetchers/mutual/context/complete/fetch";
|
||||
|
||||
const getUserFromRedis = async (): Promise<ClientUser> => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
const result = await redis.get(`${redisKey}`);
|
||||
if (!result) throw new Error("No data found in redis");
|
||||
const parsedResult = JSON.parse(result);
|
||||
if (!parsedResult.user) throw new Error("No user found in redis");
|
||||
return parsedResult.user;
|
||||
}
|
||||
|
||||
const setUserToRedis = async (userObject: ClientUser) => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
if (!decrpytUserSelection) throw new Error("No user selection found");
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
if (!userObject) throw new Error("No user object provided");
|
||||
const oldData = await getCompleteFromRedis();
|
||||
if (!oldData) throw new Error("No old data found in redis");
|
||||
await setCompleteToRedis({ ...oldData, user: userObject });
|
||||
return true;
|
||||
}
|
||||
|
||||
export { getUserFromRedis, setUserToRedis };
|
||||
@@ -0,0 +1,31 @@
|
||||
"use server";
|
||||
import { redis } from "@/lib/redis";
|
||||
import { functionRetrieveUserSelection } from "@/apifetchers/utils";
|
||||
import { ClientSettings } from "@/types/mutual/context/validations";
|
||||
import { getCompleteFromRedis, setCompleteToRedis } from "@/apifetchers/mutual/context/complete/fetch";
|
||||
|
||||
const getConfigFromRedis = async (): Promise<ClientSettings> => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
const result = await redis.get(`${redisKey}`);
|
||||
if (!result) throw new Error("No data found in redis");
|
||||
const parsedResult = JSON.parse(result);
|
||||
if (!parsedResult.settings) throw new Error("No settings found in redis");
|
||||
return parsedResult.settings;
|
||||
}
|
||||
|
||||
const setConfigToRedis = async (settingsObject: ClientSettings) => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
if (!decrpytUserSelection) throw new Error("No user selection found");
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
if (!settingsObject) throw new Error("No settings object provided");
|
||||
const oldData = await getCompleteFromRedis();
|
||||
if (!oldData) throw new Error("No old data found in redis");
|
||||
await setCompleteToRedis({ ...oldData, settings: settingsObject });
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
export { getConfigFromRedis, setConfigToRedis };
|
||||
@@ -0,0 +1,30 @@
|
||||
"use server";
|
||||
import { redis } from "@/lib/redis";
|
||||
import { functionRetrieveUserSelection } from "@/apifetchers/utils";
|
||||
import { ClientMenu } from "@/types/mutual/context/validations";
|
||||
import { getCompleteFromRedis, setCompleteToRedis } from "@/apifetchers/mutual/context/complete/fetch";
|
||||
|
||||
const getMenuFromRedis = async (): Promise<ClientMenu> => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
const result = await redis.get(`${redisKey}`);
|
||||
if (!result) throw new Error("No data found in redis");
|
||||
const parsedResult = JSON.parse(result);
|
||||
if (!parsedResult.menu) throw new Error("No menu found in redis");
|
||||
return parsedResult.menu;
|
||||
}
|
||||
|
||||
const setMenuToRedis = async (menuObject: ClientMenu) => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
if (!decrpytUserSelection) throw new Error("No user selection found");
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
if (!menuObject) throw new Error("No menu object provided");
|
||||
const oldData = await getCompleteFromRedis();
|
||||
if (!oldData) throw new Error("No old data found in redis");
|
||||
await setCompleteToRedis({ ...oldData, menu: menuObject });
|
||||
return true;
|
||||
}
|
||||
|
||||
export { getMenuFromRedis, setMenuToRedis };
|
||||
@@ -0,0 +1,82 @@
|
||||
"use server";
|
||||
import { redis } from "@/lib/redis";
|
||||
import { functionRetrieveUserSelection } from "@/apifetchers/utils";
|
||||
import { ClientOnline } from "@/types/mutual/context/validations";
|
||||
import { getCompleteFromRedis, setCompleteToRedis } from "@/apifetchers/mutual/context/complete/fetch";
|
||||
|
||||
const getOnlineFromRedis = async (): Promise<ClientOnline> => {
|
||||
try {
|
||||
// Get user selection with default fallback
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection();
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
|
||||
// If we have a default redisKey, return a default online object
|
||||
if (redisKey === "default") {
|
||||
return {
|
||||
lang: "en",
|
||||
userType: "occupant",
|
||||
lastLogin: new Date(),
|
||||
lastLogout: new Date(),
|
||||
lastAction: new Date(),
|
||||
lastPage: "/auth/login",
|
||||
timezone: "GMT+3"
|
||||
};
|
||||
}
|
||||
|
||||
// Try to get data from Redis
|
||||
const result = await redis.get(`${redisKey}`);
|
||||
if (!result) {
|
||||
return {
|
||||
lang: "en",
|
||||
userType: "occupant",
|
||||
lastLogin: new Date(),
|
||||
lastLogout: new Date(),
|
||||
lastAction: new Date(),
|
||||
lastPage: "/auth/login",
|
||||
timezone: "GMT+3"
|
||||
};
|
||||
}
|
||||
|
||||
// Parse the result
|
||||
const parsedResult = JSON.parse(result);
|
||||
if (!parsedResult.online) {
|
||||
return {
|
||||
lang: "en",
|
||||
userType: "occupant",
|
||||
lastLogin: new Date(),
|
||||
lastLogout: new Date(),
|
||||
lastAction: new Date(),
|
||||
lastPage: "/auth/login",
|
||||
timezone: "GMT+3"
|
||||
};
|
||||
}
|
||||
|
||||
return parsedResult.online;
|
||||
} catch (error) {
|
||||
console.error("Error getting online from Redis:", error);
|
||||
// Return default online object in case of any error
|
||||
return {
|
||||
lang: "en",
|
||||
userType: "occupant",
|
||||
lastLogin: new Date(),
|
||||
lastLogout: new Date(),
|
||||
lastAction: new Date(),
|
||||
lastPage: "/auth/login",
|
||||
timezone: "GMT+3"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const setOnlineToRedis = async (onlineObject: ClientOnline) => {
|
||||
const decrpytUserSelection = await functionRetrieveUserSelection()
|
||||
if (!decrpytUserSelection) throw new Error("No user selection found");
|
||||
const redisKey = decrpytUserSelection?.redisKey;
|
||||
if (!redisKey) throw new Error("No redis key found");
|
||||
if (!onlineObject) throw new Error("No online object provided");
|
||||
const oldData = await getCompleteFromRedis();
|
||||
if (!oldData) throw new Error("No old data found in redis");
|
||||
await setCompleteToRedis({ ...oldData, online: onlineObject });
|
||||
return true;
|
||||
}
|
||||
|
||||
export { getOnlineFromRedis, setOnlineToRedis };
|
||||
@@ -0,0 +1,57 @@
|
||||
"use server";
|
||||
import NextCrypto from "next-crypto";
|
||||
|
||||
import { fetchDataWithToken } from "@/apifetchers/api-fetcher";
|
||||
import { baseUrlAuth, baseUrlRestriction, tokenSecret } from "@/apifetchers/basics";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
const checkToken = `${baseUrlAuth}/authentication/token/check`;
|
||||
const pageValid = `${baseUrlRestriction}/restrictions/page/valid`;
|
||||
const siteUrls = `${baseUrlRestriction}/restrictions/sites/list`;
|
||||
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
|
||||
function fetchResponseStatus(response: any) {
|
||||
return 199 < response?.status && response?.status < 300;
|
||||
}
|
||||
|
||||
async function checkAccessTokenIsValid() {
|
||||
try {
|
||||
const response = await fetchDataWithToken(checkToken, {}, "GET", false);
|
||||
return fetchResponseStatus(response) ? true : false;
|
||||
} catch (error) {
|
||||
console.error("Error checking token validity:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function retrievePageList() {
|
||||
const response: any = await fetchDataWithToken(siteUrls, {}, "GET", false);
|
||||
return fetchResponseStatus(response) ? response.data?.sites : null;
|
||||
}
|
||||
|
||||
async function retrieveApplicationbyUrl(pageUrl: string) {
|
||||
const response: any = await fetchDataWithToken(pageValid, { page_url: pageUrl }, "POST", false);
|
||||
return fetchResponseStatus(response) ? response.data?.application : null;
|
||||
}
|
||||
|
||||
async function retrieveAccessToken() {
|
||||
const cookieStore = await cookies();
|
||||
const encrpytAccessToken = cookieStore.get("eys-zzz")?.value || "";
|
||||
return encrpytAccessToken ? await nextCrypto.decrypt(encrpytAccessToken) : null;
|
||||
}
|
||||
|
||||
async function retrieveAccessObjects() {
|
||||
const cookieStore = await cookies();
|
||||
const encrpytAccessObject = cookieStore.get("eys-yyy")?.value || "";
|
||||
const decrpytAccessObject = await nextCrypto.decrypt(encrpytAccessObject);
|
||||
return decrpytAccessObject ? JSON.parse(decrpytAccessObject) : null;
|
||||
}
|
||||
|
||||
export {
|
||||
checkAccessTokenIsValid,
|
||||
retrieveAccessToken,
|
||||
retrieveAccessObjects,
|
||||
retrieveApplicationbyUrl,
|
||||
retrievePageList,
|
||||
};
|
||||
33
web_services/client_frontend/src/apifetchers/utils.tsx
Normal file
33
web_services/client_frontend/src/apifetchers/utils.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
"use server";
|
||||
import NextCrypto from "next-crypto";
|
||||
import { cookieObject, tokenSecret } from "@/apifetchers/basics";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
const nextCrypto = new NextCrypto(tokenSecret);
|
||||
|
||||
const functionRetrieveUserSelection = async () => {
|
||||
const cookieStore = await cookies();
|
||||
const encrpytUserSelection = cookieStore.get("eys-sel")?.value || "";
|
||||
if (!encrpytUserSelection) throw new Error("No user selection found");
|
||||
const decrpytUserSelection = await nextCrypto.decrypt(encrpytUserSelection);
|
||||
if (!decrpytUserSelection) throw new Error("No user selection found");
|
||||
return JSON.parse(decrpytUserSelection);
|
||||
}
|
||||
|
||||
const functionSetUserSelection = async (userSelection: any) => {
|
||||
const cookieStore = await cookies();
|
||||
const encrpytUserSelection = await nextCrypto.encrypt(JSON.stringify(userSelection));
|
||||
if (!encrpytUserSelection) throw new Error("No user selection found");
|
||||
cookieStore.set({
|
||||
name: "eys-sel",
|
||||
value: encrpytUserSelection,
|
||||
...cookieObject,
|
||||
});
|
||||
}
|
||||
|
||||
const functionRemoveUserSelection = async () => {
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.delete("eys-sel");
|
||||
}
|
||||
|
||||
export { functionRetrieveUserSelection, functionSetUserSelection, functionRemoveUserSelection };
|
||||
@@ -1,10 +1,12 @@
|
||||
'use server';
|
||||
import { AuthLayout } from "@/layouts/auth/layout";
|
||||
import { AuthServerProps } from "@/validations/mutual/pages/props";
|
||||
import { checkContextPageOnline } from "@/components/mutual/context/online/context";
|
||||
import getPage from "@/webPages/getPage";
|
||||
|
||||
const AuthPageEn = async ({ params, searchParams }: AuthServerProps) => {
|
||||
const lang = "en";
|
||||
const online = await checkContextPageOnline();
|
||||
const lang = online?.lang || "en";
|
||||
const awaitedParams = await params;
|
||||
const awaitedSearchParams = await searchParams;
|
||||
const pageUrlFromParams = `/${awaitedParams.page?.join("/")}` || "/login";
|
||||
@@ -1,15 +0,0 @@
|
||||
'use server';
|
||||
import { AuthServerProps } from "@/validations/mutual/pages/props";
|
||||
import { AuthLayout } from "@/layouts/auth/layout";
|
||||
import getPage from "@/webPages/getPage";
|
||||
|
||||
const AuthPageTr = async ({ params, searchParams }: AuthServerProps) => {
|
||||
const lang = "tr";
|
||||
const awaitedParams = await params;
|
||||
const awaitedSearchParams = await searchParams;
|
||||
const pageUrlFromParams = `/${awaitedParams.page?.join("/")}` || "/login";
|
||||
const FoundPage = getPage(pageUrlFromParams, { language: lang, query: awaitedSearchParams });
|
||||
return <AuthLayout lang={lang} page={FoundPage} activePageUrl={pageUrlFromParams} />
|
||||
}
|
||||
|
||||
export default AuthPageTr;
|
||||
@@ -1,15 +0,0 @@
|
||||
'use server';
|
||||
import { DashboardLayout } from "@/layouts/dashboard/layout";
|
||||
import { MaindasboardPageProps } from "@/validations/mutual/dashboard/props";
|
||||
|
||||
const MainEnPage: React.FC<MaindasboardPageProps> = async ({ params, searchParams }) => {
|
||||
const parameters = await params;
|
||||
const searchParameters = await searchParams;
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<DashboardLayout lang="en" params={parameters} searchParams={searchParameters} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MainEnPage;
|
||||
@@ -1,15 +0,0 @@
|
||||
'use server';
|
||||
import { DashboardLayout } from "@/layouts/dashboard/layout";
|
||||
import { MaindasboardPageProps } from "@/validations/mutual/dashboard/props";
|
||||
|
||||
const MainTrPage: React.FC<MaindasboardPageProps> = async ({ params, searchParams }) => {
|
||||
const parameters = await params;
|
||||
const searchParameters = await searchParams;
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<DashboardLayout lang="tr" params={parameters} searchParams={searchParameters} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MainTrPage;
|
||||
@@ -0,0 +1,33 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { retrieveAccessToken } from "@/apifetchers/mutual/cookies/token";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Check if token exists
|
||||
const token = await retrieveAccessToken();
|
||||
|
||||
if (!token) {
|
||||
return NextResponse.json({
|
||||
status: 401,
|
||||
data: null,
|
||||
error: "No token found",
|
||||
});
|
||||
}
|
||||
|
||||
// In a real implementation, you would validate the token
|
||||
// For now, just return success if a token exists
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
data: {
|
||||
valid: true,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error checking token:", error);
|
||||
return NextResponse.json({
|
||||
status: 500,
|
||||
data: null,
|
||||
error: "Error validating token",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
getSelectionFromRedis,
|
||||
setSelectionToRedis,
|
||||
} from "@/apifetchers/mutual/context/dash/selection/fetch";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
const selection = await getSelectionFromRedis();
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
data: selection || null,
|
||||
});
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const selection = await request.json();
|
||||
await setSelectionToRedis(selection);
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
data: selection || null,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import {
|
||||
getSettingsFromRedis,
|
||||
setSettingsToRedis,
|
||||
} from "@/apifetchers/mutual/context/dash/settings/fetch";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
const settings = await getSettingsFromRedis();
|
||||
return NextResponse.json(settings);
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const settings = await request.json();
|
||||
await setSettingsToRedis(settings);
|
||||
return NextResponse.json(settings);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import {
|
||||
getUserFromRedis,
|
||||
setUserToRedis,
|
||||
} from "@/apifetchers/mutual/context/dash/user/fetch";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
const user = await getUserFromRedis();
|
||||
return NextResponse.json(user);
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const user = await request.json();
|
||||
await setUserToRedis(user);
|
||||
return NextResponse.json(user);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import {
|
||||
getConfigFromRedis,
|
||||
setConfigToRedis,
|
||||
} from "@/apifetchers/mutual/context/page/config/fetch";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
const config = await getConfigFromRedis();
|
||||
return NextResponse.json(config);
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const config = await request.json();
|
||||
await setConfigToRedis(config);
|
||||
return NextResponse.json(config);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
getMenuFromRedis,
|
||||
setMenuToRedis,
|
||||
} from "@/apifetchers/mutual/context/page/menu/fetch";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
const menu = await getMenuFromRedis();
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
data: menu,
|
||||
});
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const menu = await request.json();
|
||||
await setMenuToRedis(menu);
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
data: menu,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
getOnlineFromRedis,
|
||||
setOnlineToRedis,
|
||||
} from "@/apifetchers/mutual/context/page/online/fetch";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
const online = await getOnlineFromRedis();
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
data: online,
|
||||
});
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const online = await request.json();
|
||||
await setOnlineToRedis(online);
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
data: online,
|
||||
});
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { retrieveUserSelection } from "@/apicalls/cookies/token";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function POST(): Promise<NextResponse> {
|
||||
try {
|
||||
const userSelection = await retrieveUserSelection();
|
||||
console.log("userSelection", userSelection);
|
||||
if (userSelection) {
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
message: "User selection found",
|
||||
data: userSelection,
|
||||
});
|
||||
}
|
||||
} catch (error) {}
|
||||
return NextResponse.json({
|
||||
status: 500,
|
||||
message: "User selection not found",
|
||||
});
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
import { loginViaAccessKeys } from "@/apicalls/custom/login/login";
|
||||
import { NextResponse } from "next/server";
|
||||
import { loginViaAccessKeys } from "@/apifetchers/custom/login/login";
|
||||
import { loginSchemaEmail } from "@/webPages/auth/login/schemas";
|
||||
|
||||
export async function POST(req: Request): Promise<NextResponse> {
|
||||
try {
|
||||
const headers = req.headers;
|
||||
console.log("headers", Object.entries(headers));
|
||||
const body = await req.json();
|
||||
const dataValidated = {
|
||||
accessKey: body.email,
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import { API_BASE_URL } from "@/config/config";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function POST() {
|
||||
async function retrieveAvailableApplication(): Promise<string[]> {
|
||||
return new Promise((resolve) => {
|
||||
const mockList = [
|
||||
"management/account/tenant/something",
|
||||
"management/account/tenant/somethingSecond",
|
||||
"building/parts/tenant/something",
|
||||
];
|
||||
resolve(mockList);
|
||||
});
|
||||
}
|
||||
|
||||
const availableApplications = await retrieveAvailableApplication();
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
data: availableApplications,
|
||||
const result = await fetch(`${API_BASE_URL}/context/page/menu`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
try {
|
||||
const data = await result.json();
|
||||
return NextResponse.json({ status: 200, data: data });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return NextResponse.json({ status: 500, message: "No data is found" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { z } from "zod";
|
||||
import { loginSelectEmployee } from "@/apicalls/custom/login/login";
|
||||
import { loginSelectEmployee } from "@/apifetchers/custom/login/login";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
const loginSchemaEmployee = z.object({
|
||||
company_uu_id: z.string(),
|
||||
uuid: z.string(),
|
||||
});
|
||||
|
||||
export async function POST(req: Request): Promise<NextResponse> {
|
||||
@@ -12,7 +12,7 @@ export async function POST(req: Request): Promise<NextResponse> {
|
||||
console.log("headers", Object.entries(headers));
|
||||
const body = await req.json();
|
||||
const dataValidated = {
|
||||
company_uu_id: body.company_uu_id,
|
||||
uuid: body.uuid,
|
||||
};
|
||||
const validatedLoginBody = loginSchemaEmployee.safeParse(body);
|
||||
if (!validatedLoginBody.success) {
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { z } from "zod";
|
||||
import { loginSelectOccupant } from "@/apicalls/custom/login/login";
|
||||
import { loginSelectOccupant } from "@/apifetchers/custom/login/login";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
const loginSchemaOccupant = z.object({
|
||||
build_living_space_uu_id: z.string(),
|
||||
uuid: z.string(),
|
||||
});
|
||||
|
||||
export async function POST(req: Request): Promise<NextResponse> {
|
||||
try {
|
||||
const headers = req.headers;
|
||||
console.log("headers", Object.entries(headers));
|
||||
const body = await req.json();
|
||||
const dataValidated = {
|
||||
build_living_space_uu_id: body.build_living_space_uu_id,
|
||||
uuid: body.uuid,
|
||||
};
|
||||
const validatedLoginBody = loginSchemaOccupant.safeParse(body);
|
||||
if (!validatedLoginBody.success) {
|
||||
|
||||
@@ -63,7 +63,6 @@ export async function handleCreateOperation(
|
||||
}
|
||||
|
||||
if (createFunction) {
|
||||
console.log("Body:", body);
|
||||
const result = await createFunction(body);
|
||||
return createResponse(result);
|
||||
}
|
||||
@@ -90,7 +89,6 @@ export async function handleUpdateOperation(
|
||||
return errorResponse("UUID not found", 400);
|
||||
}
|
||||
if (updateFunction) {
|
||||
console.log("Body:", body);
|
||||
const result = await updateFunction(body, uuid);
|
||||
return updateResponse(result);
|
||||
}
|
||||
@@ -136,12 +134,10 @@ export function createCreateHandler(
|
||||
createFunction?: CreateFunction,
|
||||
requiredFields: string[] = []
|
||||
) {
|
||||
console.log("Required fields:", requiredFields);
|
||||
// This handler only takes the body parameter, not the request
|
||||
return withErrorHandling((body: any) => {
|
||||
// Ensure we're only passing the actual body data to the create function
|
||||
if (body && typeof body === 'object' && body.body) {
|
||||
console.log("Extracting body from request body");
|
||||
return handleCreateOperation(body.body, createFunction, requiredFields);
|
||||
}
|
||||
return handleCreateOperation(body, createFunction, requiredFields);
|
||||
|
||||
@@ -2,10 +2,11 @@ import { ContentProps } from "@/validations/mutual/dashboard/props";
|
||||
import { resolveWhichPageToRenderMulti } from "@/pages/resolver/resolver";
|
||||
import ContentToRenderNoPage from "@/pages/mutual/noContent/page";
|
||||
|
||||
const PageToBeChildrendMulti: React.FC<ContentProps> = async ({ lang, translations, activePageUrl, mode }) => {
|
||||
|
||||
const PageToBeChildrendMulti: React.FC<ContentProps> = async ({ lang, activePageUrl, mode }) => {
|
||||
const ApplicationToRender = await resolveWhichPageToRenderMulti({ activePageUrl })
|
||||
if (!ApplicationToRender) return <ContentToRenderNoPage lang={lang} />
|
||||
return <ApplicationToRender lang={lang} translations={translations} activePageUrl={activePageUrl} mode={mode} />
|
||||
return <ApplicationToRender lang={lang} activePageUrl={activePageUrl} mode={mode} />
|
||||
}
|
||||
|
||||
export default PageToBeChildrendMulti
|
||||
|
||||
@@ -2,18 +2,23 @@
|
||||
import { FC, Suspense } from "react";
|
||||
import { ContentProps, ModeTypes, ModeTypesList } from "@/validations/mutual/dashboard/props";
|
||||
import LoadingContent from "@/components/mutual/loader/component";
|
||||
import PageToBeChildrendSingle from "./PageToBeChildrendSingle";
|
||||
import PageToBeChildrendMulti from "./PageToBeChildrendMulti";
|
||||
|
||||
const ContentComponent: FC<ContentProps> = async ({ lang, translations, activePageUrl, isMulti, mode }) => {
|
||||
// const ContentComponent: FC<ContentProps> = async ({ lang, translations, activePageUrl, isMulti, mode }) => {
|
||||
// const modeFromQuery = ModeTypesList.includes(mode || '') ? mode : 'shortList'
|
||||
// const renderProps = { lang, translations, activePageUrl, mode: modeFromQuery as ModeTypes }
|
||||
// const PageToBeChildrend = isMulti ? PageToBeChildrendMulti : PageToBeChildrendSingle
|
||||
// const loadingContent = <LoadingContent height="h-16" size="w-36 h-48" plane="h-full w-full" />
|
||||
// const classNameDiv = "fixed top-24 left-80 right-0 py-10 px-15 border-emerald-150 border-l-2 overflow-y-auto h-[calc(100vh-64px)]"
|
||||
// return <div className={classNameDiv}><Suspense fallback={loadingContent}><PageToBeChildrend {...renderProps} /></Suspense></div>
|
||||
// };
|
||||
|
||||
const ContentComponent: FC<ContentProps> = async ({ lang, activePageUrl, mode }) => {
|
||||
const modeFromQuery = ModeTypesList.includes(mode || '') ? mode : 'shortList'
|
||||
const renderProps = { lang, translations, activePageUrl, mode: modeFromQuery as ModeTypes }
|
||||
const PageToBeChildrend = isMulti ? PageToBeChildrendMulti : PageToBeChildrendSingle
|
||||
const renderProps = { lang, activePageUrl, mode: modeFromQuery as ModeTypes }
|
||||
const loadingContent = <LoadingContent height="h-16" size="w-36 h-48" plane="h-full w-full" />
|
||||
const classNameDiv = "fixed top-24 left-80 right-0 py-10 px-15 border-emerald-150 border-l-2 overflow-y-auto h-[calc(100vh-64px)]"
|
||||
return (
|
||||
<div className={classNameDiv}><Suspense fallback={loadingContent}><PageToBeChildrend {...renderProps} /></Suspense></div>
|
||||
);
|
||||
return <div className={classNameDiv}><Suspense fallback={loadingContent}><PageToBeChildrendMulti {...renderProps} /></Suspense></div>
|
||||
};
|
||||
|
||||
export default ContentComponent;
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
'use server';
|
||||
import { FC } from "react";
|
||||
import { langGetKey } from "@/lib/langGet";
|
||||
import { FooterProps } from "@/validations/mutual/dashboard/props";
|
||||
import { AllProps } from "@/validations/mutual/dashboard/props";
|
||||
|
||||
const FooterComponent: FC<FooterProps> = ({ translations }) => {
|
||||
const translations = {
|
||||
en: {
|
||||
footer: "footer",
|
||||
page: "page"
|
||||
},
|
||||
tr: {
|
||||
footer: "footer",
|
||||
page: "sayfa"
|
||||
}
|
||||
}
|
||||
|
||||
const FooterComponent: FC<AllProps> = ({ lang, activePageUrl, prefix, mode }) => {
|
||||
return (
|
||||
<div className="fixed text-center bottom-0 left-0 right-0 h-16 p-4 border-t border-emerald-150 border-t-2 shadow-sm backdrop-blur-sm bg-emerald-50">
|
||||
<h1>{langGetKey(translations, "footer")}: {langGetKey(translations, "page")}</h1>
|
||||
<h1>{langGetKey(translations[lang], "footer")}: {langGetKey(translations[lang], "page")}</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,17 +1,35 @@
|
||||
'use server';
|
||||
import { FC } from "react";
|
||||
import { HeaderProps } from "@/validations/mutual/dashboard/props";
|
||||
'use client';
|
||||
import { FC, useState, useEffect } from "react";
|
||||
import { AllProps } from "@/validations/mutual/dashboard/props";
|
||||
import LanguageSelectionComponent from "@/components/mutual/languageSelection/component";
|
||||
import { langGetKey } from "@/lib/langGet";
|
||||
import { checkContextPageOnline } from "@/components/mutual/context/online/context";
|
||||
|
||||
const HeaderComponent: FC<HeaderProps> = ({ translations, lang, activePageUrl, prefix }) => {
|
||||
const translations = {
|
||||
en: {
|
||||
selectedPage: "selectedPage",
|
||||
page: "page"
|
||||
},
|
||||
tr: {
|
||||
selectedPage: "seçiliSayfa",
|
||||
page: "sayfa"
|
||||
}
|
||||
}
|
||||
|
||||
const HeaderComponent: FC<AllProps> = ({ lang, activePageUrl, prefix, mode }) => {
|
||||
const [online, setOnline] = useState(false);
|
||||
useEffect(() => {
|
||||
checkContextPageOnline().then((online) => {
|
||||
setOnline(online);
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
<div className="flex justify-between h-24 items-center p-4 border-emerald-150 border-b-2 shadow-sm backdrop-blur-sm sticky top-0 z-50 bg-emerald-50">
|
||||
<div className="flex flex-row justify-center items-center">
|
||||
<p className="text-2xl font-bold mx-3">{langGetKey(translations, 'selectedPage')} :</p>
|
||||
<p className="text-lg font-bold mx-3"> {langGetKey(translations, 'page')}</p>
|
||||
<p className="text-2xl font-bold mx-3">{langGetKey(translations[lang], 'selectedPage')} :</p>
|
||||
<p className="text-lg font-bold mx-3"> {langGetKey(translations[lang], 'page')}</p>
|
||||
</div>
|
||||
<div>{JSON.stringify(online)}</div>
|
||||
<LanguageSelectionComponent lang={lang} activePage={activePageUrl} prefix={prefix} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
'use client';
|
||||
import { FC, useState, useEffect } from "react";
|
||||
import { MenuProps } from "@/validations/mutual/dashboard/props";
|
||||
import { langGetKey } from "@/lib/langGet";
|
||||
import { AllProps } from "@/validations/mutual/dashboard/props";
|
||||
import { parseURlFormString } from "@/lib/menuGet";
|
||||
import { menuTranslation } from "@/languages/mutual/menu"
|
||||
import FirstLayerDropdown from "./firstLayerComponent";
|
||||
import SecondLayerDropdown from "./secondLayerComponent";
|
||||
import ThirdLayerDropdown from "./thirdLayerComponent";
|
||||
import { checkContextPageMenu } from "@/components/mutual/context/menu/context";
|
||||
|
||||
// Define types for menu structure
|
||||
type ThirdLayerItem = Record<string, any>;
|
||||
@@ -13,146 +14,109 @@ type SecondLayerItems = Record<string, ThirdLayerItem>;
|
||||
type FirstLayerItems = Record<string, SecondLayerItems>;
|
||||
type MenuStructure = FirstLayerItems;
|
||||
|
||||
const MenuComponent: FC<MenuProps> = ({ lang, menuItems, menuTranslationsFlatten, activePageUrl }) => {
|
||||
// State for tracking expanded menu items
|
||||
// Helper function to get translation for a URL path
|
||||
const getMenuTranslation = (translations: any, urlPath: string) => {
|
||||
if (translations[urlPath]) {
|
||||
return translations[urlPath];
|
||||
}
|
||||
const cleanPath = urlPath.startsWith('/') ? urlPath.substring(1) : urlPath;
|
||||
if (translations[cleanPath]) {
|
||||
return translations[cleanPath];
|
||||
}
|
||||
const pathWithSlash = urlPath.startsWith('/') ? urlPath : `/${urlPath}`;
|
||||
if (translations[pathWithSlash]) {
|
||||
return translations[pathWithSlash];
|
||||
}
|
||||
const keys = Object.keys(translations);
|
||||
for (const key of keys) {
|
||||
const cleanKey = key.startsWith('/') ? key.substring(1) : key;
|
||||
const cleanUrlPath = urlPath.startsWith('/') ? urlPath.substring(1) : urlPath;
|
||||
|
||||
if (cleanUrlPath.includes(cleanKey) || cleanKey.includes(cleanUrlPath)) {
|
||||
return translations[key];
|
||||
}
|
||||
}
|
||||
const parts = urlPath.split('/');
|
||||
return parts[parts.length - 1] || urlPath;
|
||||
};
|
||||
|
||||
const MenuComponent: FC<AllProps> = ({ lang, activePageUrl, prefix, mode }) => {
|
||||
const [availableApplications, setAvailableApplications] = useState<string[]>([]);
|
||||
useEffect(() => {
|
||||
checkContextPageMenu().then((apps) => {
|
||||
console.log('apps', apps);
|
||||
setAvailableApplications(apps?.selectionList || []);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const [expandedFirstLayer, setExpandedFirstLayer] = useState<string | null>(null);
|
||||
const [expandedSecondLayer, setExpandedSecondLayer] = useState<string | null>(null);
|
||||
const [menuStructure, setMenuStructure] = useState<MenuStructure>({});
|
||||
|
||||
// Parse active URL to determine which menu items should be active
|
||||
const menuTranslationWLang = menuTranslation[lang];
|
||||
const activePathLayers = parseURlFormString(activePageUrl).data;
|
||||
const activeFirstLayer = activePathLayers[0] || null;
|
||||
const activeSecondLayer = activePathLayers[1] || null;
|
||||
const activeThirdLayer = activePathLayers[2] || null;
|
||||
const activeThirdLayer = activePathLayers.slice(2, activePathLayers.length).join("/");
|
||||
|
||||
// Initialize expanded state based on active path
|
||||
useEffect(() => {
|
||||
if (activeFirstLayer) {
|
||||
setExpandedFirstLayer(activeFirstLayer);
|
||||
if (activeSecondLayer) {
|
||||
setExpandedSecondLayer(activeSecondLayer);
|
||||
const newMenuStructure: MenuStructure = {};
|
||||
availableApplications.forEach((appPath: string) => {
|
||||
const cleanPath = appPath.startsWith('/') ? appPath.substring(1) : appPath;
|
||||
const pathParts = cleanPath.split('/');
|
||||
if (pathParts.length >= 3) {
|
||||
const firstLayer = pathParts[0];
|
||||
const secondLayer = pathParts[1];
|
||||
const thirdLayer = pathParts.slice(2).join('/');
|
||||
if (!newMenuStructure[firstLayer]) { newMenuStructure[firstLayer] = {} }
|
||||
if (!newMenuStructure[firstLayer][secondLayer]) { newMenuStructure[firstLayer][secondLayer] = {} }
|
||||
newMenuStructure[firstLayer][secondLayer][thirdLayer] = true;
|
||||
}
|
||||
}
|
||||
}, [activeFirstLayer, activeSecondLayer]);
|
||||
|
||||
// Process menu items into a hierarchical structure
|
||||
useEffect(() => {
|
||||
const processedStructure: MenuStructure = {};
|
||||
|
||||
Object.entries(menuItems).forEach(([path, _]: [string, any]) => {
|
||||
const layers = parseURlFormString(path).data;
|
||||
const firstLayer = layers[0];
|
||||
const secondLayer = layers[1];
|
||||
const thirdLayer = layers[2];
|
||||
|
||||
// Create first layer if it doesn't exist
|
||||
if (!processedStructure[firstLayer]) {
|
||||
processedStructure[firstLayer] = {};
|
||||
}
|
||||
|
||||
// Create second layer if it doesn't exist
|
||||
if (!processedStructure[firstLayer][secondLayer]) {
|
||||
processedStructure[firstLayer][secondLayer] = {};
|
||||
}
|
||||
|
||||
// Add third layer
|
||||
processedStructure[firstLayer][secondLayer][thirdLayer] = {};
|
||||
});
|
||||
setMenuStructure(newMenuStructure);
|
||||
}, [availableApplications]);
|
||||
|
||||
setMenuStructure(processedStructure);
|
||||
}, [menuItems]);
|
||||
useEffect(() => { if (activeFirstLayer) { setExpandedFirstLayer(activeFirstLayer); if (activeSecondLayer) { setExpandedSecondLayer(activeSecondLayer) } } }, [activeFirstLayer, activeSecondLayer]);
|
||||
|
||||
// Handle click on first layer menu item
|
||||
const handleFirstLayerClick = (key: string) => {
|
||||
if (expandedFirstLayer === key) {
|
||||
// If already expanded, collapse it
|
||||
setExpandedFirstLayer(null);
|
||||
setExpandedSecondLayer(null);
|
||||
} else {
|
||||
// Otherwise expand it
|
||||
setExpandedFirstLayer(key);
|
||||
setExpandedSecondLayer(null);
|
||||
}
|
||||
};
|
||||
const handleFirstLayerClick = (key: string) => { if (expandedFirstLayer === key) { setExpandedFirstLayer(null); setExpandedSecondLayer(null) } else { setExpandedFirstLayer(key); setExpandedSecondLayer(null) } };
|
||||
const handleSecondLayerClick = (key: string) => { if (expandedSecondLayer === key) { setExpandedSecondLayer(null) } else { setExpandedSecondLayer(key) } };
|
||||
|
||||
// Handle click on second layer menu item
|
||||
const handleSecondLayerClick = (key: string) => {
|
||||
if (expandedSecondLayer === key) {
|
||||
// If already expanded, collapse it
|
||||
setExpandedSecondLayer(null);
|
||||
} else {
|
||||
// Otherwise expand it
|
||||
setExpandedSecondLayer(key);
|
||||
}
|
||||
};
|
||||
|
||||
// Render third layer menu items
|
||||
const renderThirdLayerItems = (firstLayerKey: string, secondLayerKey: string, thirdLayerItems: ThirdLayerItem) => {
|
||||
const baseUrl = `/${firstLayerKey}/${secondLayerKey}`;
|
||||
|
||||
return Object.keys(thirdLayerItems).map(thirdLayerKey => {
|
||||
const isActive =
|
||||
activeFirstLayer === firstLayerKey &&
|
||||
activeSecondLayer === secondLayerKey &&
|
||||
activeThirdLayer === thirdLayerKey;
|
||||
|
||||
const isActive = activeFirstLayer === firstLayerKey && activeSecondLayer === secondLayerKey && activeThirdLayer === thirdLayerKey;
|
||||
const mergeUrl = `${baseUrl}/${thirdLayerKey}`;
|
||||
const url = `/${lang}${baseUrl}/${thirdLayerKey}`;
|
||||
|
||||
return (
|
||||
<div key={`${thirdLayerKey}-item`} className="ml-2 my-1">
|
||||
<ThirdLayerDropdown
|
||||
isActive={isActive}
|
||||
innerText={langGetKey(menuTranslationsFlatten, thirdLayerKey)}
|
||||
url={url}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
console.log('mergeUrl', mergeUrl);
|
||||
return <div key={`${thirdLayerKey}-item`} className="ml-2 my-1"><ThirdLayerDropdown isActive={isActive} innerText={getMenuTranslation(menuTranslationWLang, mergeUrl)} url={url} /></div>
|
||||
});
|
||||
};
|
||||
|
||||
// Render second layer menu items
|
||||
const renderSecondLayerItems = (firstLayerKey: string, secondLayerItems: SecondLayerItems) => {
|
||||
return Object.entries(secondLayerItems).map(([secondLayerKey, thirdLayerItems]) => {
|
||||
const isActive = activeFirstLayer === firstLayerKey && activeSecondLayer === secondLayerKey;
|
||||
const isExpanded = expandedSecondLayer === secondLayerKey;
|
||||
|
||||
const mergeUrl = `/${firstLayerKey}/${secondLayerKey}`;
|
||||
console.log('mergeUrl', mergeUrl);
|
||||
return (
|
||||
<div key={`${secondLayerKey}-item`} className="ml-2 my-1">
|
||||
<SecondLayerDropdown
|
||||
isActive={isActive}
|
||||
isExpanded={isExpanded}
|
||||
innerText={langGetKey(menuTranslationsFlatten, secondLayerKey)}
|
||||
onClick={() => handleSecondLayerClick(secondLayerKey)}
|
||||
/>
|
||||
{isExpanded && (
|
||||
<div className="ml-2 mt-1">
|
||||
{renderThirdLayerItems(firstLayerKey, secondLayerKey, thirdLayerItems)}
|
||||
</div>
|
||||
)}
|
||||
<SecondLayerDropdown isActive={isActive} isExpanded={isExpanded} innerText={getMenuTranslation(menuTranslationWLang, mergeUrl)} onClick={() => handleSecondLayerClick(secondLayerKey)} />
|
||||
{isExpanded && (<div className="ml-2 mt-1">{renderThirdLayerItems(firstLayerKey, secondLayerKey, thirdLayerItems)}</div>)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// Render first layer menu items
|
||||
const renderFirstLayerItems = () => {
|
||||
return Object.entries(menuStructure).map(([firstLayerKey, secondLayerItems]) => {
|
||||
const isActive = activeFirstLayer === firstLayerKey;
|
||||
const isExpanded = expandedFirstLayer === firstLayerKey;
|
||||
|
||||
const mergeUrl = `/${firstLayerKey}`;
|
||||
console.log('mergeUrl', mergeUrl);
|
||||
return (
|
||||
<div key={`${firstLayerKey}-item`} className="mb-2">
|
||||
<FirstLayerDropdown
|
||||
isActive={isActive}
|
||||
isExpanded={isExpanded}
|
||||
innerText={langGetKey(menuTranslationsFlatten, firstLayerKey)}
|
||||
onClick={() => handleFirstLayerClick(firstLayerKey)}
|
||||
/>
|
||||
{isExpanded && (
|
||||
<div className="mt-1">
|
||||
{renderSecondLayerItems(firstLayerKey, secondLayerItems)}
|
||||
</div>
|
||||
)}
|
||||
<FirstLayerDropdown isActive={isActive} isExpanded={isExpanded} innerText={getMenuTranslation(menuTranslationWLang, mergeUrl)} onClick={() => handleFirstLayerClick(firstLayerKey)} />
|
||||
{isExpanded && (<div className="mt-1">{renderSecondLayerItems(firstLayerKey, secondLayerItems)}</div>)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ClientPageConfig } from "@/types/mutual/context/validations";
|
||||
import { API_BASE_URL } from "@/config/config";
|
||||
|
||||
async function checkContextPageConfig(): Promise<ClientPageConfig> {
|
||||
const result = await fetch(`${API_BASE_URL}/context/page/config`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
try {
|
||||
const data = await result.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw new Error("No data is found");
|
||||
}
|
||||
}
|
||||
|
||||
async function setContextPageConfig({
|
||||
pageConfig,
|
||||
}: {
|
||||
pageConfig: ClientPageConfig;
|
||||
}) {
|
||||
const result = await fetch(`${API_BASE_URL}/context/page/config`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(pageConfig),
|
||||
});
|
||||
try {
|
||||
const data = await result.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw new Error("No data is set");
|
||||
}
|
||||
}
|
||||
|
||||
export { checkContextPageConfig, setContextPageConfig };
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ClientMenu } from "@/types/mutual/context/validations";
|
||||
import { API_BASE_URL } from "@/config/config";
|
||||
|
||||
async function checkContextPageMenu() {
|
||||
const result = await fetch(`${API_BASE_URL}/context/page/menu`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
try {
|
||||
const data = await result.json();
|
||||
console.log("checkContextPageMenu ", data);
|
||||
if (data.status == 200) return data.data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new Error("No data is found");
|
||||
}
|
||||
}
|
||||
|
||||
async function setContextPageMenu(setMenu: ClientMenu) {
|
||||
const result = await fetch(`${API_BASE_URL}/context/page/menu`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(setMenu),
|
||||
});
|
||||
try {
|
||||
const data = await result.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new Error("No data is set");
|
||||
}
|
||||
}
|
||||
|
||||
export { checkContextPageMenu, setContextPageMenu };
|
||||
@@ -0,0 +1,29 @@
|
||||
import { ClientOnline } from "@/types/mutual/context/validations";
|
||||
import { API_BASE_URL } from "@/config/config";
|
||||
|
||||
async function checkContextPageOnline() {
|
||||
const result = await fetch(`${API_BASE_URL}/context/page/online`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
try {
|
||||
const data = await result.json();
|
||||
if (data.status == 200) return data.data;
|
||||
} catch (error) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function setContextPageOnline(setOnline: ClientOnline) {
|
||||
const result = await fetch(`${API_BASE_URL}/context/page/online`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(setOnline),
|
||||
});
|
||||
try {
|
||||
const data = await result.json();
|
||||
if (data.status == 200) return data.data;
|
||||
} catch (error) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
export { checkContextPageOnline, setContextPageOnline };
|
||||
@@ -0,0 +1,37 @@
|
||||
import { ClientSelection } from "@/types/mutual/context/validations";
|
||||
import { API_BASE_URL } from "@/config/config";
|
||||
|
||||
async function checkContextDashSelection() {
|
||||
try {
|
||||
const result = await fetch(`${API_BASE_URL}/context/dash/selection`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
const data = await result.json();
|
||||
if (data.status === 200) return data.data;
|
||||
} catch (error) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function setContextDashUserSelection({
|
||||
userSet,
|
||||
}: {
|
||||
userSet: ClientSelection;
|
||||
}) {
|
||||
try {
|
||||
const result = await fetch(`${API_BASE_URL}/context/dash/selection`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(userSet),
|
||||
});
|
||||
|
||||
const data = await result.json();
|
||||
if (data.status === 200) return data.data;
|
||||
} catch (error) {
|
||||
console.error("Error setting dash selection:", error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export { checkContextDashSelection, setContextDashUserSelection };
|
||||
@@ -0,0 +1,33 @@
|
||||
import { ClientUser } from "@/types/mutual/context/validations";
|
||||
import { API_BASE_URL } from "@/config/config";
|
||||
|
||||
async function checkContextDashUserInfo(): Promise<ClientUser> {
|
||||
const result = await fetch(`${API_BASE_URL}/context/dash/user`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
try {
|
||||
const data = await result.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new Error("No data is found");
|
||||
}
|
||||
}
|
||||
|
||||
async function setContextDashUserInfo({ userSet }: { userSet: ClientUser }) {
|
||||
const result = await fetch(`${API_BASE_URL}/context/dash/user`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(userSet),
|
||||
});
|
||||
try {
|
||||
const data = await result.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new Error("No data is set");
|
||||
}
|
||||
}
|
||||
|
||||
export { checkContextDashUserInfo, setContextDashUserInfo };
|
||||
@@ -1,31 +1,25 @@
|
||||
'use server';
|
||||
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent } from "@/components/mutual/shadcnui/dropdown-menu";
|
||||
'use client';
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import { DropdownMenu, DropdownMenuTrigger } from "@/components/mutual/shadcnui/dropdown-menu";
|
||||
import { Button } from "@/components/mutual/shadcnui/button";
|
||||
import { languageSelectionTranslation } from "@/languages/mutual/languageSelection";
|
||||
import { langGetKey, langGet } from "@/lib/langGet";
|
||||
import { LanguageTypes } from "@/validations/mutual/language/validations";
|
||||
|
||||
import { checkContextPageOnline, setContextPageOnline } from "@/components/mutual/context/online/context";
|
||||
import LanguageSelectionItem from "./languageItem";
|
||||
|
||||
const LanguageSelectionComponent: React.FC<{ lang: LanguageTypes, activePage: string, prefix: string }> = ({ lang, activePage, prefix }) => {
|
||||
const translations = langGet(lang, languageSelectionTranslation);
|
||||
const getPageWithLocale = (locale: LanguageTypes): string => { return `${prefix}/${locale}/${activePage}` }
|
||||
|
||||
const englishButtonProps = {
|
||||
activeLang: lang,
|
||||
buttonsLang: "en",
|
||||
refUrl: getPageWithLocale("en"),
|
||||
innerText: langGetKey(translations, "english")
|
||||
}
|
||||
const turkishButtonProps = {
|
||||
activeLang: lang,
|
||||
buttonsLang: "tr",
|
||||
refUrl: getPageWithLocale("tr"),
|
||||
innerText: langGetKey(translations, "turkish")
|
||||
}
|
||||
const getPageWithLocale = (locale: LanguageTypes): string => { return `${prefix}/${activePage}` }
|
||||
const [online, setOnline] = useState<any>({});
|
||||
useEffect(() => { const online = checkContextPageOnline(); setOnline({ ...online, lang: lang }) }, []);
|
||||
const englishButtonProps = { activeLang: lang, buttonsLang: "en", refUrl: getPageWithLocale("en"), innerText: langGetKey(translations, "english") }
|
||||
const turkishButtonProps = { activeLang: lang, buttonsLang: "tr", refUrl: getPageWithLocale("tr"), innerText: langGetKey(translations, "turkish") }
|
||||
|
||||
return (
|
||||
<div className="flex items-end justify-end">
|
||||
<div>{JSON.stringify(online)}</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button className="w-48 h-12 text-center text-md">{langGetKey(translations, "title")}</Button>
|
||||
|
||||
@@ -3,10 +3,21 @@ import { useState, FC } from "react";
|
||||
import { DropdownMenuContent, DropdownMenuLabel } from "@/components/mutual/shadcnui/dropdown-menu";
|
||||
import Link from "next/link";
|
||||
import LoadingContent from "@/components/mutual/loader/component";
|
||||
import { setContextPageOnline, checkContextPageOnline } from "@/components/mutual/context/online/context";
|
||||
|
||||
const RenderLinkComponent: FC<{ refUrl: string, innerText: string, setisL: (isLoading: boolean) => void }> = ({ refUrl, innerText, setisL }) => {
|
||||
const RenderLinkComponent: FC<{ refUrl: string, innerText: string, setisL: (isLoading: boolean) => void, buttonsLang: string }> = (
|
||||
{ refUrl, innerText, setisL, buttonsLang }) => {
|
||||
const setOnline = async () => {
|
||||
setisL(true);
|
||||
const oldOnline = await checkContextPageOnline();
|
||||
await setContextPageOnline({
|
||||
...oldOnline,
|
||||
lang: buttonsLang,
|
||||
});
|
||||
setisL(false);
|
||||
}
|
||||
return (
|
||||
<Link replace href={refUrl} onClick={() => setisL(true)}>
|
||||
<Link replace href={refUrl} onClick={() => { setOnline() }}>
|
||||
<DropdownMenuContent className="flex w-48 h-12 align-center justify-center text-center text-md overflow-y-hidden">
|
||||
<DropdownMenuLabel className="flex items-center justify-center">{innerText}</DropdownMenuLabel>
|
||||
</DropdownMenuContent>
|
||||
@@ -29,7 +40,7 @@ const LanguageSelectionItem: React.FC<{
|
||||
}> = ({ activeLang, buttonsLang, refUrl, innerText }) => {
|
||||
const [isL, setisL] = useState<boolean>(false);
|
||||
const isC = buttonsLang !== activeLang
|
||||
const RenderLinkProp = { refUrl, innerText, setisL }
|
||||
const RenderLinkProp = { refUrl, innerText, setisL, buttonsLang }
|
||||
return (
|
||||
<>{isC && <>{isL ? <RenderLoadingComponent setisL={setisL} /> : <RenderLinkComponent {...RenderLinkProp} />}</>}</>
|
||||
)
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
import { LanguageTypes } from "@/validations/mutual/language/validations";
|
||||
import { DynamicPage } from "@/validations/mutual/menu/menu";
|
||||
import { managementAccountTenantMain } from "./management/account/tenantSomething/index";
|
||||
import { managementAccountTenantMainSecond } from "./management/account/tenantSomethingSecond/index";
|
||||
import { buildingPartsTenantSomething } from "./building/parts/tenantSomething/index";
|
||||
// import { managementAccountTenantMainSecond } from "./management/account/tenantSomethingSecond/index";
|
||||
// import { buildingPartsTenantSomething } from "./building/parts/tenantSomething/index";
|
||||
|
||||
const dynamicPagesIndex: Record<string, Record<LanguageTypes, DynamicPage>> = {
|
||||
"main/pages/user/dashboard": managementAccountTenantMain,
|
||||
"management/account/tenant/something": managementAccountTenantMain,
|
||||
"management/account/tenant/somethingSecond": managementAccountTenantMainSecond,
|
||||
"building/parts/tenant/something": buildingPartsTenantSomething,
|
||||
"/main/pages/user/dashboard": managementAccountTenantMain,
|
||||
"/definitions/identifications/people": managementAccountTenantMain,
|
||||
"/definitions/identifications/users": managementAccountTenantMain,
|
||||
"/definitions/building/parts": managementAccountTenantMain,
|
||||
"/definitions/building/areas": managementAccountTenantMain,
|
||||
"/building/accounts/managment/accounts": managementAccountTenantMain,
|
||||
"/building/accounts/managment/budgets": managementAccountTenantMain,
|
||||
"/building/accounts/parts/accounts": managementAccountTenantMain,
|
||||
"/building/accounts/parts/budgets": managementAccountTenantMain,
|
||||
"/building/meetings/regular/actions": managementAccountTenantMain,
|
||||
"/building/meetings/regular/accounts": managementAccountTenantMain,
|
||||
"/building/meetings/ergunt/actions": managementAccountTenantMain,
|
||||
"/building/meetings/ergunt/accounts": managementAccountTenantMain,
|
||||
"/building/meetings/invited/attendance": managementAccountTenantMain,
|
||||
};
|
||||
|
||||
export { dynamicPagesIndex };
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
const menuTranslationEn = {
|
||||
"/management/account/something/something/something": "Account Third Layer",
|
||||
"/management/account": "Account Second Layer",
|
||||
"/management": "Management First Layer",
|
||||
};
|
||||
const menuIndex = [
|
||||
"/management/account/something/something/something",
|
||||
"/building/parts/something/something/something",
|
||||
];
|
||||
"/definitions": "Definitions",
|
||||
"/definitions/identifications": "Identifications",
|
||||
"/definitions/identifications/people": "People",
|
||||
"/definitions/identifications/users": "Users",
|
||||
|
||||
export { menuTranslationEn, menuIndex };
|
||||
"/definitions/building": "Building",
|
||||
"/definitions/building/parts": "Build Parts",
|
||||
"/definitions/building/areas": "Building Areas",
|
||||
|
||||
"/building": "Building",
|
||||
"/building/accounts": "Account Actions",
|
||||
"/building/accounts/managment/accounts": "Management Accounts",
|
||||
"/building/accounts/managment/budgets": "Management Budgets",
|
||||
"/building/accounts/parts/accounts": "Parts Accounts",
|
||||
"/building/accounts/parts/budgets": "Parts Budgets",
|
||||
|
||||
"/building/meetings": "Meetings",
|
||||
"/building/meetings/regular/actions": "Regular Meeting Actions",
|
||||
"/building/meetings/regular/accounts": "Regular Meeting Accounts",
|
||||
"/building/meetings/ergunt/actions": "Ergunt Meeting Actions",
|
||||
"/building/meetings/ergunt/accounts": "Ergunt Meeting Accounts",
|
||||
"/building/meetings/invited/attendance": "Meeting Invited Attendance",
|
||||
};
|
||||
|
||||
export { menuTranslationEn };
|
||||
// export { menuTranslationEn, menuIndex };
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
const menuTranslationTr = {};
|
||||
const menuTranslationTr = {
|
||||
"/definitions": "Tanımlamalar",
|
||||
"/definitions/identifications": "Tanımlamalar",
|
||||
"/definitions/identifications/people": "Kişiler",
|
||||
"/definitions/identifications/users": "Kullanıcılar",
|
||||
|
||||
"/definitions/building": "Bina",
|
||||
"/definitions/building/parts": "Daireler",
|
||||
"/definitions/building/areas": "Bina Alanları",
|
||||
|
||||
"/building": "Bina",
|
||||
"/building/accounts": "Account Eylemleri",
|
||||
"/building/accounts/managment/accounts": "Bina Hesapları",
|
||||
"/building/accounts/managment/budgets": "Bina Bütçesi",
|
||||
"/building/accounts/parts/accounts": "Daire Hesapları",
|
||||
"/building/accounts/parts/budgets": "Daire Bütçesi",
|
||||
|
||||
"/building/meetings": "Toplantılar",
|
||||
"/building/meetings/regular/actions": "Düzenli Toplantı Eylemleri",
|
||||
"/building/meetings/regular/accounts": "Düzenli Toplantı Accounts",
|
||||
"/building/meetings/ergunt/actions": "Ergunt Toplantı Eylemleri",
|
||||
"/building/meetings/ergunt/accounts": "Ergunt Toplantı Accounts",
|
||||
"/building/meetings/invited/attendance": "Toplantı Davetli Katılımlar",
|
||||
};
|
||||
|
||||
export { menuTranslationTr };
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
'use server';
|
||||
import { FC } from "react";
|
||||
import { joinPageUrlFromLayersArray, retrieveLayersOfUrlFromParams } from "@/lib/menuGet";
|
||||
import { dynamicPagesIndex } from "@/languages/custom";
|
||||
import { dynamicPageMenuWithLayersGet, dynamicRetrieveMenuFlattenGet, langDynamicPagesGet, langGet } from "@/lib/langGet";
|
||||
import { DashboardLayoutProps, ModeTypes } from "@/validations/mutual/dashboard/props";
|
||||
|
||||
import HeaderComponent from "@/components/custom/header/component";
|
||||
@@ -11,26 +8,18 @@ import ContentComponent from "@/components/custom/content/component";
|
||||
import FooterComponent from "@/components/custom/footer/component";
|
||||
|
||||
const DashboardLayout: FC<DashboardLayoutProps> = async ({ params, searchParams, lang }) => {
|
||||
const layersItems = retrieveLayersOfUrlFromParams(params.page);
|
||||
const activePageUrl = joinPageUrlFromLayersArray(layersItems.data);
|
||||
const mode = (searchParams?.mode as ModeTypes) || 'shortList';
|
||||
|
||||
const menuItems = await dynamicPageMenuWithLayersGet(lang);
|
||||
const translations = langGet(lang, langDynamicPagesGet(activePageUrl, dynamicPagesIndex));
|
||||
const menuTranslationsFlatten = dynamicRetrieveMenuFlattenGet(menuItems);
|
||||
|
||||
const headerProps = { translations: translations.header, lang, activePageUrl, prefix: "/panel" }
|
||||
const menuProps = { lang, activePageUrl, menuTranslationsFlatten, menuItems }
|
||||
const contentProps = { translations: translations.content, lang, activePageUrl, mode, isMulti: true }
|
||||
const activePageUrl = `/${params.page?.join('/')}`;
|
||||
const allProps = { lang, activePageUrl, mode, prefix: "/panel" }
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-w-screen">
|
||||
<HeaderComponent {...headerProps} />
|
||||
<MenuComponent {...menuProps} />
|
||||
<ContentComponent {...contentProps} />
|
||||
<FooterComponent translations={translations.footer} />
|
||||
<HeaderComponent {...allProps} />
|
||||
<MenuComponent {...allProps} />
|
||||
<ContentComponent {...allProps} />
|
||||
<FooterComponent {...allProps} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { DashboardLayout };
|
||||
export { DashboardLayout };
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { ContentProps } from "@/validations/mutual/dashboard/props";
|
||||
import superUserTenantSomething from "./management/account/tenantSomething/page";
|
||||
import superUserTenantSomethingSecond from "./management/account/tenantSomethingSecond/page";
|
||||
import superUserBuildingPartsTenantSomething from "./building/parts/tenantSomething/page";
|
||||
|
||||
const pageIndexMulti: Record<string, Record<string, React.FC<ContentProps>>> = {
|
||||
"main/pages/user/dashboard": {
|
||||
superUserTenantSomething: superUserTenantSomething,
|
||||
},
|
||||
"management/account/tenant/something": {
|
||||
superUserTenantSomething: superUserTenantSomething,
|
||||
},
|
||||
"management/account/tenant/somethingSecond": {
|
||||
superUserTenantSomething: superUserTenantSomethingSecond,
|
||||
},
|
||||
"building/parts/tenant/something": {
|
||||
superUserTenantSomething: superUserBuildingPartsTenantSomething,
|
||||
},
|
||||
"/main/pages/user/dashboard": { superUserTenantSomething },
|
||||
"/definitions/identifications/people": { superUserTenantSomething },
|
||||
"/definitions/identifications/users": { superUserTenantSomething },
|
||||
"/definitions/building/parts": { superUserTenantSomething },
|
||||
"/definitions/building/areas": { superUserTenantSomething },
|
||||
"/building/accounts/managment/accounts": { superUserTenantSomething },
|
||||
"/building/accounts/managment/budgets": { superUserTenantSomething },
|
||||
"/building/accounts/parts/accounts": { superUserTenantSomething },
|
||||
"/building/accounts/parts/budgets": { superUserTenantSomething },
|
||||
"/building/meetings/regular/actions": { superUserTenantSomething },
|
||||
"/building/meetings/regular/accounts": { superUserTenantSomething },
|
||||
"/building/meetings/ergunt/actions": { superUserTenantSomething },
|
||||
"/building/meetings/ergunt/accounts": { superUserTenantSomething },
|
||||
"/building/meetings/invited/attendance": { superUserTenantSomething },
|
||||
};
|
||||
|
||||
export default pageIndexMulti;
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
// From Redis objects
|
||||
|
||||
interface ClientOnline {
|
||||
lastLogin: Date;
|
||||
lastLogout: Date;
|
||||
lastAction: Date;
|
||||
lastPage: string;
|
||||
userType: string;
|
||||
lang: string;
|
||||
timezone: string;
|
||||
}
|
||||
|
||||
interface ClientPageConfig {
|
||||
mode: string;
|
||||
textFont: number;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface ClientHeader {
|
||||
header: any[];
|
||||
activeDomain: string;
|
||||
listOfDomains: string[];
|
||||
connections: any[];
|
||||
}
|
||||
|
||||
interface ClientMenu {
|
||||
selectionList: string[];
|
||||
activeSelection: string;
|
||||
}
|
||||
|
||||
interface ClientSelection {
|
||||
selectionList: any[];
|
||||
activeSelection: Record<string, any>;
|
||||
}
|
||||
|
||||
interface ClientUser {
|
||||
uuid: string;
|
||||
avatar: string;
|
||||
email: string;
|
||||
phone_number: string;
|
||||
user_tag: string;
|
||||
password_expiry_begins: string;
|
||||
person: {
|
||||
uuid: string;
|
||||
firstname: string;
|
||||
surname: string;
|
||||
middle_name: string;
|
||||
sex_code: string;
|
||||
person_tag: string;
|
||||
country_code: string;
|
||||
birth_date: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ClientSettings {
|
||||
lastOnline: Date;
|
||||
token: string;
|
||||
}
|
||||
|
||||
interface LinkList {
|
||||
linkList: any[];
|
||||
}
|
||||
|
||||
interface ClientRedisToken {
|
||||
online: ClientOnline;
|
||||
pageConfig: ClientPageConfig;
|
||||
menu: ClientMenu;
|
||||
header: ClientHeader;
|
||||
selection: ClientSelection;
|
||||
user: ClientUser;
|
||||
settings: ClientSettings;
|
||||
chatRoom: LinkList;
|
||||
notifications: LinkList;
|
||||
messages: LinkList;
|
||||
}
|
||||
|
||||
const defaultClientOnline: ClientOnline = {
|
||||
lastLogin: new Date(),
|
||||
lastLogout: new Date(),
|
||||
lastAction: new Date(),
|
||||
lastPage: "/dashboard",
|
||||
userType: "employee",
|
||||
lang: "tr",
|
||||
timezone: "GMT+3",
|
||||
};
|
||||
|
||||
const defaultClientPageConfig: ClientPageConfig = {
|
||||
mode: "light",
|
||||
textFont: 14,
|
||||
theme: "default",
|
||||
};
|
||||
|
||||
const defaultClientMenu: ClientMenu = {
|
||||
selectionList: [],
|
||||
activeSelection: "/dashboard",
|
||||
};
|
||||
|
||||
const defaultClientHeader: ClientHeader = {
|
||||
header: [],
|
||||
activeDomain: "",
|
||||
listOfDomains: [],
|
||||
connections: [],
|
||||
};
|
||||
|
||||
const defaultClientSelection: ClientSelection = {
|
||||
selectionList: [],
|
||||
activeSelection: {},
|
||||
};
|
||||
|
||||
const defaultClientUser: ClientUser = {
|
||||
uuid: "",
|
||||
avatar: "",
|
||||
email: "",
|
||||
phone_number: "",
|
||||
user_tag: "",
|
||||
password_expiry_begins: new Date().toISOString(),
|
||||
person: {
|
||||
uuid: "",
|
||||
firstname: "",
|
||||
surname: "",
|
||||
middle_name: "",
|
||||
sex_code: "",
|
||||
person_tag: "",
|
||||
country_code: "",
|
||||
birth_date: "",
|
||||
},
|
||||
};
|
||||
|
||||
const defaultClientSettings: ClientSettings = {
|
||||
lastOnline: new Date(),
|
||||
token: "",
|
||||
};
|
||||
|
||||
const defaultLinkList: LinkList = {
|
||||
linkList: [],
|
||||
};
|
||||
|
||||
const defaultClientRedisToken: ClientRedisToken = {
|
||||
online: defaultClientOnline,
|
||||
pageConfig: defaultClientPageConfig,
|
||||
menu: defaultClientMenu,
|
||||
header: defaultClientHeader,
|
||||
selection: defaultClientSelection,
|
||||
user: defaultClientUser,
|
||||
settings: defaultClientSettings,
|
||||
chatRoom: defaultLinkList,
|
||||
notifications: defaultLinkList,
|
||||
messages: defaultLinkList,
|
||||
};
|
||||
|
||||
const generateRedisKey = (userType: string, uuId: string) => {
|
||||
const userTypeToUpper = userType.toUpperCase();
|
||||
if (!userTypeToUpper || !uuId) throw new Error("Invalid user type or uuId");
|
||||
return `CLIENT:${userTypeToUpper}:${uuId}`;
|
||||
};
|
||||
|
||||
const readRedisKey = (redisKey: string) => {
|
||||
if (redisKey.split(":").length !== 2) throw new Error("Invalid redis key");
|
||||
const userTypeToUpper = redisKey.split(":")[1];
|
||||
const uuId = redisKey.split(":")[2];
|
||||
if (!userTypeToUpper || !uuId) throw new Error("Invalid user type or uuId");
|
||||
return { userType: userTypeToUpper.toLowerCase(), uuId };
|
||||
};
|
||||
|
||||
export {
|
||||
defaultClientOnline,
|
||||
defaultClientPageConfig,
|
||||
defaultClientMenu,
|
||||
defaultClientHeader,
|
||||
defaultClientSelection,
|
||||
defaultClientUser,
|
||||
defaultClientSettings,
|
||||
defaultLinkList,
|
||||
defaultClientRedisToken,
|
||||
};
|
||||
export type {
|
||||
ClientOnline,
|
||||
ClientPageConfig,
|
||||
ClientMenu,
|
||||
ClientHeader,
|
||||
ClientSelection,
|
||||
ClientUser,
|
||||
ClientSettings,
|
||||
LinkList,
|
||||
ClientRedisToken,
|
||||
};
|
||||
export { generateRedisKey, readRedisKey };
|
||||
@@ -0,0 +1,44 @@
|
||||
interface FetcherRequest {
|
||||
url: string;
|
||||
isNoCache: boolean;
|
||||
}
|
||||
|
||||
interface PostFetcherRequest<T> extends FetcherRequest {
|
||||
body: Record<string, T>;
|
||||
}
|
||||
|
||||
interface GetFetcherRequest extends FetcherRequest {
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface DeleteFetcherRequest extends GetFetcherRequest {}
|
||||
interface PutFetcherRequest<T> extends PostFetcherRequest<T> {}
|
||||
interface PatchFetcherRequest<T> extends PostFetcherRequest<T> {}
|
||||
|
||||
interface FetcherRespose {
|
||||
success: boolean;
|
||||
}
|
||||
interface PaginationResponse {
|
||||
onPage: number;
|
||||
onPageCount: number;
|
||||
totalPage: number;
|
||||
totalCount: number;
|
||||
next: boolean;
|
||||
back: boolean;
|
||||
}
|
||||
|
||||
interface FetcherDataResponse<T> extends FetcherRespose {
|
||||
data: Record<string, T> | null;
|
||||
pagination?: PaginationResponse;
|
||||
}
|
||||
|
||||
export type {
|
||||
FetcherRequest,
|
||||
PostFetcherRequest,
|
||||
GetFetcherRequest,
|
||||
DeleteFetcherRequest,
|
||||
PutFetcherRequest,
|
||||
PatchFetcherRequest,
|
||||
FetcherRespose,
|
||||
FetcherDataResponse,
|
||||
};
|
||||
@@ -17,28 +17,24 @@ const ModeTypesList = ["shortList", "fullList", "create", "update", "view"];
|
||||
|
||||
interface ContentProps {
|
||||
lang: LanguageTypes;
|
||||
translations: Record<string, string>;
|
||||
activePageUrl: string;
|
||||
isMulti?: boolean;
|
||||
mode?: ModeTypes;
|
||||
}
|
||||
|
||||
interface MenuProps {
|
||||
lang: LanguageTypes;
|
||||
menuItems: Record<string, Record<string, any>>;
|
||||
availableApplications: string[];
|
||||
activePageUrl: string;
|
||||
menuTranslationsFlatten: Record<string, Record<string, any>>;
|
||||
}
|
||||
|
||||
interface FooterProps {
|
||||
translations: Record<string, string>;
|
||||
}
|
||||
interface FooterProps {}
|
||||
|
||||
interface HeaderProps {
|
||||
translations: Record<string, string>;
|
||||
interface AllProps {
|
||||
lang: LanguageTypes;
|
||||
activePageUrl: string;
|
||||
prefix: string;
|
||||
mode?: ModeTypes;
|
||||
|
||||
}
|
||||
|
||||
export type {
|
||||
@@ -47,7 +43,7 @@ export type {
|
||||
ContentProps,
|
||||
MenuProps,
|
||||
FooterProps,
|
||||
HeaderProps,
|
||||
AllProps,
|
||||
ModeTypes,
|
||||
};
|
||||
|
||||
|
||||
@@ -19,14 +19,12 @@ export function loginHook(
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
response.json().then((data) => {
|
||||
console.log("data", data); // setJsonText(JSON.stringify(data));
|
||||
setTimeout(() => {
|
||||
const userType =
|
||||
data?.data?.user_type.toLowerCase() === "employee"
|
||||
? "employee"
|
||||
: "occupant";
|
||||
const rediretUrl = `/auth/${lang}/select?type=${userType}`;
|
||||
console.log("rediretUrl", rediretUrl);
|
||||
const rediretUrl = `/auth/select?type=${userType}`;
|
||||
Router.push(rediretUrl);
|
||||
}, 100);
|
||||
});
|
||||
|
||||
@@ -1,30 +1,57 @@
|
||||
"use client";
|
||||
import React, { useTransition, useState } from "react";
|
||||
import React, { useTransition, useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Company } from "./types";
|
||||
import { LoginEmployeeProps } from "./types";
|
||||
import { selectEmployeeHook } from "./hook";
|
||||
import { checkContextDashSelection } from "@/components/mutual/context/selection/context";
|
||||
import { ClientSelection } from "@/types/mutual/context/validations";
|
||||
|
||||
const translation = {
|
||||
en: {
|
||||
companySelection: "Company Selection",
|
||||
loggedInAs: "Logged in as",
|
||||
duty: "Duty",
|
||||
id: "ID",
|
||||
noSelections: "No selections",
|
||||
continue: "Continue",
|
||||
select: "Select"
|
||||
},
|
||||
tr: {
|
||||
companySelection: "Şirket Seçimi",
|
||||
loggedInAs: "Giriş Yapan",
|
||||
duty: "Görev",
|
||||
id: "ID",
|
||||
noSelections: "Seçim Yok",
|
||||
continue: "Devam Et",
|
||||
select: "Seç"
|
||||
}
|
||||
}
|
||||
|
||||
function LoginEmployee({
|
||||
selectionList,
|
||||
translation,
|
||||
lang
|
||||
}: LoginEmployeeProps) {
|
||||
const isArrayLengthOne = Array.isArray(selectionList) && selectionList.length === 1;
|
||||
const isArrayLengthZero = Array.isArray(selectionList) && selectionList.length === 0;
|
||||
const isArrayMoreThanOne = Array.isArray(selectionList) && selectionList.length > 1;
|
||||
const [selectionListCopy, setSelectionListCopy] = useState<ClientSelection>({ selectionList: [], activeSelection: {} });
|
||||
useEffect(() => {
|
||||
checkContextDashSelection().then((selectionList) => {
|
||||
if (!selectionList) throw new Error("No selection list found");
|
||||
setSelectionListCopy(selectionList)
|
||||
})
|
||||
}, [])
|
||||
|
||||
const isArrayLengthOne = Array.isArray(selectionListCopy.selectionList) && selectionListCopy.selectionList.length === 1;
|
||||
const isArrayLengthZero = Array.isArray(selectionListCopy.selectionList) && selectionListCopy.selectionList.length === 0;
|
||||
const isArrayMoreThanOne = Array.isArray(selectionListCopy.selectionList) && selectionListCopy.selectionList.length > 1;
|
||||
const Router = useRouter();
|
||||
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [jsonText, setJsonText] = useState<string | null>(null);
|
||||
|
||||
const onSubmitEmployee = async (uu_id: string) => {
|
||||
selectEmployeeHook(startTransition, { company_uu_id: uu_id }, setError, setJsonText, Router, lang)
|
||||
};
|
||||
const onSubmitEmployee = async (uu_id: string) => { selectEmployeeHook(startTransition, { uuid: uu_id }, setError, setJsonText, Router, lang) };
|
||||
|
||||
// Render a company card with consistent styling
|
||||
const CompanyCard = ({ company, showButton = false }: { company: Company, showButton?: boolean }) => (
|
||||
const CompanyCard = ({ company }: { company: Company }) => (
|
||||
<div className="w-full p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md transition-all">
|
||||
<div className="flex flex-col" onClick={() => onSubmitEmployee(company.uu_id)}>
|
||||
{/* Company name and type */}
|
||||
@@ -40,13 +67,13 @@ function LoginEmployee({
|
||||
{/* Duty information */}
|
||||
{company.duty && (
|
||||
<div className="mb-2 text-sm text-gray-600">
|
||||
<span className="font-medium">{translation.duty}:</span> {company.duty}
|
||||
<span className="font-medium">{translation[lang].duty}:</span> {company.duty}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ID information */}
|
||||
<div className="text-xs text-gray-500 mb-3">
|
||||
<span className="font-medium">{translation.id}:</span> {company.uu_id}
|
||||
<span className="font-medium">{translation[lang].id}:</span> {company.uu_id}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -55,30 +82,26 @@ function LoginEmployee({
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-md mx-auto">
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-2">{translation.companySelection}</h1>
|
||||
<p className="text-sm text-gray-500 mb-6">{translation.loggedInAs}</p>
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-2">{translation[lang].companySelection}</h1>
|
||||
<p className="text-sm text-gray-500 mb-6">{translation[lang].loggedInAs}</p>
|
||||
|
||||
{/* No companies available */}
|
||||
{isArrayLengthZero && (
|
||||
<div className="text-center p-6 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<p className="text-gray-600">{translation.noSelections}</p>
|
||||
<p className="text-gray-600">{translation[lang].noSelections}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Single company */}
|
||||
{isArrayLengthOne && <CompanyCard company={selectionList[0]} showButton={true} />}
|
||||
{isArrayLengthOne && <CompanyCard company={selectionListCopy.activeSelection as Company} />}
|
||||
|
||||
{/* Multiple companies */}
|
||||
{isArrayMoreThanOne && (
|
||||
<div className="space-y-3">
|
||||
{selectionList.map((company, index) => (
|
||||
<div
|
||||
key={company.uu_id || index}
|
||||
onClick={() => onSubmitEmployee(company.uu_id)}
|
||||
{selectionListCopy.selectionList.map((company, index) => (
|
||||
<div key={company.uu_id || index} onClick={() => onSubmitEmployee(company.uu_id)}
|
||||
className="cursor-pointer hover:translate-x-1 transition-transform"
|
||||
>
|
||||
<CompanyCard company={company} />
|
||||
</div>
|
||||
><CompanyCard company={company as Company} /></div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -3,24 +3,38 @@ import React, { useState, useTransition, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { LoginOccupantProps } from "./types";
|
||||
import { selectOccupantHook } from "./hook";
|
||||
import { checkContextDashSelection } from "@/components/mutual/context/selection/context";
|
||||
import { ClientSelection } from "@/types/mutual/context/validations";
|
||||
|
||||
function LoginOccupant({
|
||||
selectionList,
|
||||
translation,
|
||||
lang
|
||||
}: LoginOccupantProps) {
|
||||
const translation = {
|
||||
en: {
|
||||
occupantSelection: "Occupant Selection",
|
||||
loggedInAs: "Logged in as",
|
||||
level: "Level",
|
||||
noSelections: "No selections"
|
||||
},
|
||||
tr: {
|
||||
occupantSelection: "İşçi Seçimi",
|
||||
loggedInAs: "Giriş Yapan",
|
||||
level: "Seviye",
|
||||
noSelections: "Seçim Yok"
|
||||
}
|
||||
}
|
||||
|
||||
function LoginOccupant({ lang }: LoginOccupantProps) {
|
||||
const Router = useRouter();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [jsonText, setJsonText] = useState<string | null>(null);
|
||||
const [selectionList, setSelectionList] = useState<ClientSelection>({ selectionList: [], activeSelection: {} });
|
||||
|
||||
const onSubmitOccupant = async (data: any) => {
|
||||
selectOccupantHook(startTransition, data, setError, setJsonText, Router, lang)
|
||||
};
|
||||
const isArrayLengthZero = Array.isArray(selectionList) && selectionList.length === 0;
|
||||
|
||||
// Render an occupant card with consistent styling
|
||||
useEffect(() => {
|
||||
checkContextDashSelection().then((selectionList) => {
|
||||
if (!selectionList) throw new Error("No selection list found"); setSelectionList(selectionList);
|
||||
})
|
||||
}, [])
|
||||
const onSubmitOccupant = async (data: any) => { selectOccupantHook(startTransition, data, setError, setJsonText, Router, lang) };
|
||||
const isArrayLengthZero = Array.isArray(selectionList.selectionList) && selectionList.selectionList.length === 0;
|
||||
const OccupantCard = ({ occupant, buildKey, idx }: { occupant: any, buildKey: string, idx: number }) => (
|
||||
<div
|
||||
key={`${buildKey}-${idx}`}
|
||||
@@ -28,28 +42,21 @@ function LoginOccupant({
|
||||
onClick={() => onSubmitOccupant({ build_living_space_uu_id: occupant.build_living_space_uu_id })}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
{/* Occupant description and code */}
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h3 className="text-lg font-semibold text-gray-800">{occupant.description}</h3>
|
||||
<span className="px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 rounded-full">
|
||||
{occupant.code}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Part name */}
|
||||
<div className="mb-1 text-sm text-gray-700 font-medium">
|
||||
{occupant.part_name}
|
||||
</div>
|
||||
|
||||
{/* Level information */}
|
||||
<div className="text-xs text-gray-500">
|
||||
<span className="font-medium">{translation.level}:</span> {occupant.part_level}
|
||||
<span className="font-medium">{translation[lang].level}:</span> {occupant.part_level}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Render a building section with its occupants
|
||||
const BuildingSection = ({ building, buildKey }: { building: any, buildKey: string }) => (
|
||||
<div key={buildKey} className="mb-6">
|
||||
<div className="p-3 bg-gray-50 border border-gray-200 rounded-lg mb-3">
|
||||
@@ -72,31 +79,25 @@ function LoginOccupant({
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-md mx-auto">
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-2">{translation.occupantSelection}</h1>
|
||||
<p className="text-sm text-gray-500 mb-6">{translation.loggedInAs}</p>
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-2">{translation[lang].occupantSelection}</h1>
|
||||
<p className="text-sm text-gray-500 mb-6">{translation[lang].loggedInAs}</p>
|
||||
|
||||
{/* No occupants available */}
|
||||
{!isArrayLengthZero ? (
|
||||
<div className="text-center p-6 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<p className="text-gray-600">{translation.noSelections}</p>
|
||||
<p className="text-gray-600">{translation[lang].noSelections}</p>
|
||||
</div>
|
||||
) : (
|
||||
/* Building sections with occupants */
|
||||
<div>
|
||||
{Object.keys(selectionList).map((buildKey: string) => (
|
||||
{Object.keys(selectionList.selectionList).map((buildKey: string) => (
|
||||
<BuildingSection
|
||||
key={buildKey}
|
||||
building={selectionList[buildKey]}
|
||||
building={selectionList.activeSelection}
|
||||
buildKey={buildKey}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Show error if any */}
|
||||
{error && <p className="mt-4 text-sm text-red-600">{error}</p>}
|
||||
|
||||
{/* Loading indicator */}
|
||||
{isPending && (
|
||||
<div className="mt-4 flex justify-center">
|
||||
<div className="animate-spin h-5 w-5 border-2 border-blue-600 rounded-full border-t-transparent"></div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { LanguageTypes } from "@/validations/mutual/language/validations";
|
||||
|
||||
const afterLoginDirectUrl = (lang: LanguageTypes) => {
|
||||
return `/panel/${lang}/main/pages/user/dashboard`;
|
||||
const afterLoginDirectUrl = () => {
|
||||
return `/panel/dashboard`;
|
||||
};
|
||||
|
||||
function selectEmployeeHook(
|
||||
@@ -25,7 +25,7 @@ function selectEmployeeHook(
|
||||
response.json().then((data) => {
|
||||
console.log("data", data);
|
||||
setTimeout(() => {
|
||||
Router.push(afterLoginDirectUrl(lang));
|
||||
Router.push(afterLoginDirectUrl());
|
||||
}, 100);
|
||||
});
|
||||
} else {
|
||||
@@ -62,7 +62,7 @@ function selectOccupantHook(
|
||||
response.json().then((data) => {
|
||||
console.log("data", data);
|
||||
setTimeout(() => {
|
||||
Router.push(afterLoginDirectUrl(lang));
|
||||
Router.push(afterLoginDirectUrl());
|
||||
}, 100);
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -1,39 +1,18 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import LoginOccupant from "./LoginOccupant";
|
||||
import LoginEmployee from "./LoginEmployee";
|
||||
|
||||
import { Company, SelectListProps, BuildingMap } from "./types";
|
||||
import { selectEmployeeTranslation, selectOccupantTranslation } from "./language";
|
||||
|
||||
const Select: React.FC<SelectListProps> = ({ selectionList, isEmployee, isOccupant, language, query }) => {
|
||||
const isEmployeee = query?.isEmployee == "true";
|
||||
const isOccupante = query?.isOccupant == "true";
|
||||
const userType = isEmployeee || isOccupante ? isEmployeee ? "employee" : "occupant" : "employee";
|
||||
|
||||
const isEmployeeTrue = userType == "employee" && Array.isArray(selectionList)
|
||||
const isOccupantTrue = userType == "occupant" && !Array.isArray(selectionList)
|
||||
const initTranslation = userType == "employee" ? selectEmployeeTranslation[language] : selectOccupantTranslation[language]
|
||||
|
||||
const [translation, setTranslation] = useState(initTranslation);
|
||||
const [listEmployeeSelection, setListEmployeeSelection] = useState<Company[]>(selectionList as Company[]);
|
||||
const [listOccupantSelection, setListOccupantSelection] = useState<BuildingMap>(selectionList as BuildingMap);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEmployee) { setListEmployeeSelection(selectionList as Company[]) }
|
||||
else if (isOccupant) { setListOccupantSelection(selectionList as BuildingMap) }
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setTranslation(isEmployee ? selectEmployeeTranslation[language] : selectOccupantTranslation[language]);
|
||||
}, [language]);
|
||||
import { SelectListProps } from "./types";
|
||||
|
||||
const Select: React.FC<SelectListProps> = ({ language, query }) => {
|
||||
const isEmployeee = query?.type == "employee";
|
||||
const isOccupante = query?.type == "occupant";
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-full min-h-[inherit] flex-col items-center justify-center gap-4">
|
||||
<div className="w-full max-w-md rounded-lg bg-white p-8 shadow-md">
|
||||
{isEmployeeTrue && <LoginEmployee translation={translation} selectionList={listEmployeeSelection} lang={language} />}
|
||||
{isOccupantTrue && <LoginOccupant translation={translation} selectionList={listOccupantSelection} lang={language} />}
|
||||
{isEmployeee && <LoginEmployee lang={language} />}
|
||||
{isOccupante && <LoginOccupant lang={language} />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -3,18 +3,15 @@ import React, { FC } from "react";
|
||||
import Select from "./page";
|
||||
|
||||
import { redirect } from "next/navigation";
|
||||
import { checkAccessTokenIsValid, retrieveUserType } from "@/apicalls/mutual/cookies/token";
|
||||
import { AuthPageProps } from "@/validations/mutual/auth/props";
|
||||
|
||||
const SelectPage: FC<AuthPageProps> = async ({ query, language }) => {
|
||||
const token_is_valid = await checkAccessTokenIsValid();
|
||||
const selection = await retrieveUserType();
|
||||
const isEmployee = selection?.userType == "employee";
|
||||
const isOccupant = selection?.userType == "occupant";
|
||||
const selectionList = selection?.selectionList;
|
||||
|
||||
if (!selectionList || !token_is_valid) { redirect("/auth/en/login") }
|
||||
return <Select selectionList={selectionList} isEmployee={isEmployee} isOccupant={isOccupant} language={language} query={query} />
|
||||
try {
|
||||
return <Select language={language} query={query} />;
|
||||
} catch (error) {
|
||||
redirect("/auth/login");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default SelectPage;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ClientSelection } from "@/types/mutual/context/validations";
|
||||
import { LanguageTypes } from "@/validations/mutual/language/validations";
|
||||
|
||||
interface Company {
|
||||
@@ -30,22 +31,15 @@ interface BuildingMap {
|
||||
}
|
||||
|
||||
interface SelectListProps {
|
||||
selectionList: Company[] | BuildingMap;
|
||||
isEmployee: boolean;
|
||||
isOccupant: boolean;
|
||||
language: LanguageTypes;
|
||||
query?: { [key: string]: string | string[] | undefined };
|
||||
}
|
||||
|
||||
interface LoginOccupantProps {
|
||||
selectionList: BuildingMap;
|
||||
translation: any;
|
||||
lang: LanguageTypes;
|
||||
}
|
||||
|
||||
interface LoginEmployeeProps {
|
||||
selectionList: Company[];
|
||||
translation: any;
|
||||
lang: LanguageTypes;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user