client frontend added
This commit is contained in:
parent
5f7cb35ccc
commit
fdf9d2edb8
|
|
@ -68,8 +68,14 @@ class LoginHandler:
|
|||
return str(email).split("@")[1] == api_config.ACCESS_EMAIL_EXT
|
||||
|
||||
@classmethod
|
||||
# headers: CommonHeaders
|
||||
def do_employee_login(cls, headers: CommonHeaders, data: Any, db_session):
|
||||
"""Handle employee login."""
|
||||
|
||||
language = headers.request.headers.get("language", "tr")
|
||||
domain = headers.request.headers.get("domain", None)
|
||||
timezone = headers.request.headers.get("tz", None) or "GMT+3"
|
||||
|
||||
user_handler, other_domains_list, main_domain = UserHandlers(), [], ""
|
||||
found_user = user_handler.check_user_exists(access_key=data.access_key, db_session=db_session)
|
||||
with mongo_handler.collection(f"{str(found_user.related_company)}*Domain") as collection:
|
||||
|
|
@ -78,7 +84,7 @@ class LoginHandler:
|
|||
raise ValueError("EYS_00087")
|
||||
other_domains_list = result.get("other_domains_list", [])
|
||||
main_domain = result.get("main_domain", None)
|
||||
if headers.domain not in other_domains_list or not main_domain:
|
||||
if domain not in other_domains_list or not main_domain:
|
||||
raise ValueError("EYS_00088")
|
||||
|
||||
if not user_handler.check_password_valid(domain=main_domain, id_=str(found_user.uu_id), password=data.password, password_hashed=found_user.hash_password):
|
||||
|
|
@ -115,24 +121,24 @@ class LoginHandler:
|
|||
duty_uu_id_list.append(str(list_employee["Duty.uu_id"]))
|
||||
duty_id_list.append(int(list_employee["Duty.id"]))
|
||||
companies_list.append({
|
||||
"uu_id": str(list_employee["Companies.uu_id"]), "public_name": list_employee["Companies.public_name"],
|
||||
"company_type": list_employee["Companies.company_type"], "company_address": list_employee["Addresses.letter_address"],
|
||||
"duty": list_employee["Duty.duty_name"]
|
||||
"uu_id": str(list_employee["Employees.uu_id"]),
|
||||
"public_name": list_employee["Companies.public_name"],
|
||||
"company_type": list_employee["Companies.company_type"],
|
||||
"company_address": list_employee["Addresses.letter_address"],
|
||||
"duty": list_employee["Duty.duty_name"],
|
||||
"company_uuid": str(list_employee["Companies.uu_id"])
|
||||
})
|
||||
model_value = EmployeeTokenObject(
|
||||
user_type=UserType.employee.value,
|
||||
user_uu_id=str(found_user.uu_id),
|
||||
user_id=found_user.id,
|
||||
person_id=found_user.person_id,
|
||||
person_uu_id=str(list_employees[0]["People.uu_id"]),
|
||||
request=dict(headers.request.headers),
|
||||
domain_list=other_domains_list,
|
||||
companies_uu_id_list=companies_uu_id_list,
|
||||
companies_id_list=companies_id_list,
|
||||
duty_uu_id_list=duty_uu_id_list,
|
||||
duty_id_list=duty_id_list,
|
||||
user_type=UserType.employee.value, user_uu_id=str(found_user.uu_id), user_id=found_user.id, person_id=found_user.person_id,
|
||||
person_uu_id=str(list_employees[0]["People.uu_id"]), request=dict(headers.request.headers), domain_list=other_domains_list,
|
||||
companies_uu_id_list=companies_uu_id_list, companies_id_list=companies_id_list, duty_uu_id_list=duty_uu_id_list, duty_id_list=duty_id_list,
|
||||
).model_dump()
|
||||
set_to_redis_dict = dict(user=found_user, token=model_value, header_info=dict(language=headers.language, domain=headers.domain, timezone=headers.timezone))
|
||||
employee_uuid = str(companies_list[0]["uu_id"])
|
||||
print('employee_uuid', employee_uuid)
|
||||
set_to_redis_dict = dict(
|
||||
user=found_user, token=model_value, add_uuid=employee_uuid,
|
||||
header_info=dict(language=headers.language, domain=headers.domain, timezone=headers.timezone),
|
||||
)
|
||||
user_dict = found_user.get_dict()
|
||||
person_dict = found_user.person.get_dict()
|
||||
if access_token := RedisHandlers().set_object_to_redis(**set_to_redis_dict):
|
||||
|
|
@ -140,21 +146,12 @@ class LoginHandler:
|
|||
"access_token": access_token,
|
||||
"user_type": UserType.employee.name,
|
||||
"user": {
|
||||
"uuid": user_dict["uu_id"],
|
||||
"avatar": user_dict["avatar"],
|
||||
"email": user_dict["email"],
|
||||
"phone_number": user_dict["phone_number"],
|
||||
"user_tag": user_dict["user_tag"],
|
||||
"uuid": user_dict["uu_id"], "avatar": user_dict["avatar"], "email": user_dict["email"], "phone_number": user_dict["phone_number"], "user_tag": user_dict["user_tag"],
|
||||
"password_expiry_begins": str(arrow.get(user_dict["password_expiry_begins"]).shift(days=int(user_dict["password_expires_day"]))),
|
||||
"person": {
|
||||
"uuid": person_dict["uu_id"],
|
||||
"firstname": person_dict["firstname"],
|
||||
"surname": person_dict["surname"],
|
||||
"middle_name": person_dict["middle_name"],
|
||||
"sex_code": person_dict["sex_code"],
|
||||
"person_tag": person_dict["person_tag"],
|
||||
"country_code": person_dict["country_code"],
|
||||
"birth_date": person_dict["birth_date"],
|
||||
"uuid": person_dict["uu_id"], "firstname": person_dict["firstname"], "surname": person_dict["surname"],
|
||||
"middle_name": person_dict["middle_name"], "sex_code": person_dict["sex_code"], "person_tag": person_dict["person_tag"],
|
||||
"country_code": person_dict["country_code"], "birth_date": person_dict["birth_date"],
|
||||
},
|
||||
},
|
||||
"selection_list": companies_list,
|
||||
|
|
@ -162,22 +159,21 @@ class LoginHandler:
|
|||
raise ValueError("Something went wrong")
|
||||
|
||||
@classmethod
|
||||
def do_occupant_login(cls, request: Any, data: Any, db_session, extra_dict: Optional[Dict[str, Any]] = None):
|
||||
# headers=headers, data=data, db_session=db_session
|
||||
def do_occupant_login(cls, headers: CommonHeaders, data: Any, db_session):
|
||||
"""
|
||||
Handle occupant login.
|
||||
"""
|
||||
language = extra_dict.get("language", "tr")
|
||||
domain = extra_dict.get("domain", None)
|
||||
timezone = extra_dict.get("tz", None) or "GMT+3"
|
||||
language = headers.request.headers.get("language", "tr")
|
||||
domain = headers.request.headers.get("domain", None)
|
||||
timezone = headers.request.headers.get("tz", None) or "GMT+3"
|
||||
BuildParts.set_session(db_session)
|
||||
OccupantTypes.set_session(db_session)
|
||||
|
||||
user_handler = UserHandlers()
|
||||
found_user = user_handler.check_user_exists(
|
||||
access_key=data.access_key, db_session=db_session
|
||||
)
|
||||
found_user = user_handler.check_user_exists(access_key=data.access_key, db_session=db_session)
|
||||
other_domains_list, main_domain = [], ""
|
||||
with mongo_handler.collection(
|
||||
f"{str(found_user.related_company)}*Domain"
|
||||
) as collection:
|
||||
with mongo_handler.collection(f"{str(found_user.related_company)}*Domain") as collection:
|
||||
result = collection.find_one({"user_uu_id": str(found_user.uu_id)})
|
||||
if not result:
|
||||
raise ValueError("EYS_00087")
|
||||
|
|
@ -186,78 +182,43 @@ class LoginHandler:
|
|||
if domain not in other_domains_list or not main_domain:
|
||||
raise ValueError("EYS_00088")
|
||||
|
||||
if not user_handler.check_password_valid(
|
||||
domain=main_domain,
|
||||
id_=str(found_user.uu_id),
|
||||
password=data.password,
|
||||
password_hashed=found_user.hash_password,
|
||||
):
|
||||
if not user_handler.check_password_valid(domain=main_domain, id_=str(found_user.uu_id), password=data.password, password_hashed=found_user.hash_password):
|
||||
raise ValueError("EYS_0005")
|
||||
|
||||
occupants_selection_dict: Dict[str, Any] = {}
|
||||
living_spaces: list[BuildLivingSpace] = BuildLivingSpace.filter_all(
|
||||
BuildLivingSpace.person_id == found_user.person_id, db=db_session
|
||||
).data
|
||||
living_spaces: list[BuildLivingSpace] = BuildLivingSpace.query.filter(BuildLivingSpace.person_id == found_user.person_id).all()
|
||||
|
||||
if not living_spaces:
|
||||
raise ValueError("EYS_0006")
|
||||
for living_space in living_spaces:
|
||||
build_part = BuildParts.filter_one(
|
||||
BuildParts.id == living_space.build_parts_id,
|
||||
db=db_session,
|
||||
).data
|
||||
build_part = BuildParts.query.filter(BuildParts.id == living_space.build_parts_id).first()
|
||||
if not build_part:
|
||||
raise ValueError("EYS_0007")
|
||||
|
||||
build = build_part.buildings
|
||||
occupant_type = OccupantTypes.filter_by_one(
|
||||
id=living_space.occupant_type_id,
|
||||
db=db_session,
|
||||
system=True,
|
||||
).data
|
||||
occupant_type = OccupantTypes.query.filter(OccupantTypes.id == living_space.occupant_type_id).first()
|
||||
occupant_data = {
|
||||
"build_living_space_uu_id": str(living_space.uu_id),
|
||||
"part_uu_id": str(build_part.uu_id),
|
||||
"part_name": build_part.part_name(db=db_session),
|
||||
"part_level": build_part.part_level,
|
||||
"occupant_uu_id": str(occupant_type.uu_id),
|
||||
"description": occupant_type.occupant_description,
|
||||
"code": occupant_type.occupant_code,
|
||||
"build_living_space_uu_id": str(living_space.uu_id), "part_uu_id": str(build_part.uu_id), "part_name": build_part.part_name(), "part_level": build_part.part_level,
|
||||
"occupant_uu_id": str(occupant_type.uu_id), "description": occupant_type.occupant_description, "code": occupant_type.occupant_code,
|
||||
}
|
||||
|
||||
build_key = str(build.uu_id)
|
||||
if build_key not in occupants_selection_dict:
|
||||
occupants_selection_dict[build_key] = {
|
||||
"build_uu_id": build_key,
|
||||
"build_name": build.build_name,
|
||||
"build_no": build.build_no,
|
||||
"occupants": [occupant_data],
|
||||
}
|
||||
occupants_selection_dict[build_key] = {"build_uu_id": build_key, "build_name": build.build_name, "build_no": build.build_no, "occupants": [occupant_data],}
|
||||
else:
|
||||
occupants_selection_dict[build_key]["occupants"].append(occupant_data)
|
||||
|
||||
person = found_user.person
|
||||
model_value = OccupantTokenObject(
|
||||
user_type=UserType.occupant.value,
|
||||
user_uu_id=str(found_user.uu_id),
|
||||
user_id=found_user.id,
|
||||
person_id=person.id,
|
||||
person_uu_id=str(person.uu_id),
|
||||
domain_list=other_domains_list,
|
||||
request=dict(request.headers),
|
||||
available_occupants=occupants_selection_dict,
|
||||
user_type=UserType.occupant.value, user_uu_id=str(found_user.uu_id), user_id=found_user.id, person_id=person.id,
|
||||
person_uu_id=str(person.uu_id), domain_list=other_domains_list, request=dict(request.headers), available_occupants=occupants_selection_dict,
|
||||
).model_dump()
|
||||
redis_handler = RedisHandlers()
|
||||
if access_token := redis_handler.set_object_to_redis(
|
||||
user=found_user,
|
||||
token=model_value,
|
||||
header_info=dict(language=language, domain=domain, timezone=timezone),
|
||||
user=found_user, token=model_value, add_uuid=living_space.uu_id,
|
||||
header_info=dict(language=language, domain=domain, timezone=timezone)
|
||||
):
|
||||
return {
|
||||
"access_token": access_token,
|
||||
"user_type": UserType.occupant.name,
|
||||
"selection_list": occupants_selection_dict,
|
||||
}
|
||||
return {"access_token": access_token, "user_type": UserType.occupant.name, "selection_list": occupants_selection_dict}
|
||||
raise ValueError("Something went wrong")
|
||||
|
||||
@classmethod
|
||||
|
|
@ -302,7 +263,7 @@ class LoginHandler:
|
|||
@classmethod
|
||||
def handle_employee_selection(cls, access_token: str, data: Any, token_dict: TokenDictType):
|
||||
with Users.new_session() as db_session:
|
||||
if data.company_uu_id not in token_dict.companies_uu_id_list:
|
||||
if data.uuid not in token_dict.companies_uu_id_list:
|
||||
ValueError("EYS_0011")
|
||||
list_of_returns = (
|
||||
Employees.id, Employees.uu_id, People.id, People.uu_id, Users.id, Users.uu_id, Companies.id, Companies.uu_id,
|
||||
|
|
@ -319,7 +280,7 @@ class LoginHandler:
|
|||
).join(Companies, Companies.id == Departments.company_id
|
||||
).join(Users, Users.person_id == People.id
|
||||
).outerjoin(Addresses, Addresses.id == Companies.official_address_id
|
||||
).filter(Companies.uu_id == data.company_uu_id, Users.id == token_dict.user_id)
|
||||
).filter(Employees.uu_id == data.uuid, Users.id == token_dict.user_id)
|
||||
|
||||
selected_company_first = selected_company_query.first()
|
||||
if not selected_company_first:
|
||||
|
|
@ -357,8 +318,10 @@ class LoginHandler:
|
|||
reachable_app_codes=reachable_app_codes,
|
||||
)
|
||||
redis_handler = RedisHandlers()
|
||||
redis_result = redis_handler.update_token_at_redis(token=access_token, add_payload=company_token)
|
||||
return {"selected_uu_id": data.company_uu_id}
|
||||
redis_result = redis_handler.update_token_at_redis(
|
||||
token=access_token, add_payload=company_token, add_uuid=str(result_with_keys_dict['Employees.uu_id'])
|
||||
)
|
||||
return {"selected_uu_id": data.uuid}
|
||||
|
||||
@classmethod
|
||||
def handle_occupant_selection(cls, access_token: str, data: Any, token_dict: TokenDictType):
|
||||
|
|
@ -408,7 +371,9 @@ class LoginHandler:
|
|||
reachable_app_codes=reachable_app_codes,
|
||||
)
|
||||
redis_handler = RedisHandlers()
|
||||
redis_handler.update_token_at_redis(token=access_token, add_payload=occupant_token)
|
||||
redis_handler.update_token_at_redis(
|
||||
token=access_token, add_payload=occupant_token, add_uuid=occupant_token.living_space_uu_id
|
||||
)
|
||||
return {"selected_uu_id": occupant_token.living_space_uu_id}
|
||||
|
||||
@classmethod # Requires auth context
|
||||
|
|
|
|||
|
|
@ -15,16 +15,7 @@ class RequestVerifyOTP(BaseModel):
|
|||
|
||||
|
||||
class RequestSelectEmployee(BaseModel):
|
||||
|
||||
company_uu_id: str
|
||||
|
||||
@property
|
||||
def is_employee(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_occupant(self):
|
||||
return False
|
||||
uuid: str
|
||||
|
||||
|
||||
class RequestResetPassword(BaseModel):
|
||||
|
|
@ -34,17 +25,7 @@ class RequestResetPassword(BaseModel):
|
|||
|
||||
|
||||
class RequestSelectLiving(BaseModel):
|
||||
|
||||
build_living_space_uu_id: str
|
||||
|
||||
@property
|
||||
def is_employee(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_occupant(self):
|
||||
return True
|
||||
|
||||
uuid: str
|
||||
|
||||
class RequestCreatePassword(BaseModel):
|
||||
password_token: str
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ def authentication_page_valid(data: RequestApplication, headers: CommonHeaders =
|
|||
Verify if page is valid returns application that can user reach
|
||||
page: { url = /building/create} | result: { "application": "4c11f5ef-0bbd-41ac-925e-f79d9aac2b0e" }
|
||||
"""
|
||||
return PageHandlers.retrieve_valid_page_via_token(access_token=headers.token, page_url=data.page)
|
||||
list_of = PageHandlers.retrieve_valid_page_via_token(access_token=headers.token, page_url=data.page)
|
||||
print('list_of', list_of)
|
||||
return {"completed": True, "application": list_of}
|
||||
|
||||
|
||||
application_retrieve_all_sites = "ApplicationRetrieveAllSites"
|
||||
|
|
@ -36,4 +38,6 @@ def authentication_get_all_sites_list(headers: CommonHeaders = Depends(CommonHea
|
|||
"""
|
||||
Verify if page is valid returns application that can user reach result: { "sites": ['/dashboard', '/building/create'] }
|
||||
"""
|
||||
return PageHandlers.retrieve_valid_sites_via_token(access_token=headers.token)
|
||||
list_of_application_url = PageHandlers.retrieve_valid_sites_via_token(access_token=headers.token)
|
||||
print('list_of_application_url', list(list_of_application_url))
|
||||
return {"completed": True, "sites": list(list_of_application_url)}
|
||||
|
|
|
|||
|
|
@ -27,16 +27,14 @@ class TokenProvider:
|
|||
raise ValueError("Invalid user type")
|
||||
|
||||
@classmethod
|
||||
def get_dict_from_redis(cls, token: Optional[str] = None, user_uu_id: Optional[str] = None) -> Union[TokenDictType, List[TokenDictType]]:
|
||||
def get_dict_from_redis(
|
||||
cls, token: Optional[str] = None, user_uu_id: Optional[str] = None
|
||||
) -> Union[TokenDictType, List[TokenDictType]]:
|
||||
"""
|
||||
Retrieve token object from Redis using token and user_uu_id
|
||||
"""
|
||||
token_to_use, user_uu_id_to_use = token or "*", user_uu_id or "*"
|
||||
list_of_token_dict, auth_key_list = [], [
|
||||
cls.AUTH_TOKEN,
|
||||
token_to_use,
|
||||
user_uu_id_to_use,
|
||||
]
|
||||
list_of_token_dict, auth_key_list = [], [cls.AUTH_TOKEN, token_to_use, user_uu_id_to_use, "*"]
|
||||
if token:
|
||||
result = RedisActions.get_json(list_keys=auth_key_list, limit=1)
|
||||
if first_record := result.first:
|
||||
|
|
@ -47,9 +45,7 @@ class TokenProvider:
|
|||
for all_record in all_records:
|
||||
list_of_token_dict.append(cls.convert_redis_object_to_token(all_record))
|
||||
return list_of_token_dict
|
||||
raise ValueError(
|
||||
"Token not found in Redis. Please check the token or user_uu_id."
|
||||
)
|
||||
raise ValueError("Token not found in Redis. Please check the token or user_uu_id.")
|
||||
|
||||
@classmethod
|
||||
def retrieve_application_codes(cls, page_url: str, token: TokenDictType):
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class RedisHandlers:
|
|||
|
||||
@classmethod
|
||||
def get_object_from_redis(cls, access_token: str) -> TokenDictType:
|
||||
redis_response = RedisActions.get_json(list_keys=[cls.AUTH_TOKEN, access_token, "*"])
|
||||
redis_response = RedisActions.get_json(list_keys=[cls.AUTH_TOKEN, access_token, "*", "*"])
|
||||
if not redis_response.status:
|
||||
raise ValueError("EYS_0001")
|
||||
if redis_object := redis_response.first:
|
||||
|
|
@ -41,22 +41,30 @@ class RedisHandlers:
|
|||
raise ValueError("EYS_0002")
|
||||
|
||||
@classmethod
|
||||
def set_object_to_redis(cls, user: Users, token, header_info):
|
||||
result_delete = RedisActions.delete(list_keys=[cls.AUTH_TOKEN, "*", str(user.uu_id)])
|
||||
def set_object_to_redis(cls, user: Users, token, header_info, add_uuid: str):
|
||||
result_delete = RedisActions.delete(list_keys=[cls.AUTH_TOKEN, "*", str(user.uu_id), add_uuid])
|
||||
generated_access_token = PasswordModule.generate_access_token()
|
||||
keys = [cls.AUTH_TOKEN, generated_access_token, str(user.uu_id)]
|
||||
if add_uuid:
|
||||
keys.append(add_uuid)
|
||||
RedisActions.set_json(list_keys=keys, value={**token, **header_info}, expires={"hours": 1, "minutes": 30})
|
||||
return generated_access_token
|
||||
RedisActions.set_json(list_keys=keys, value={**token, **header_info}, expires={"hours": 1, "minutes": 30})
|
||||
return generated_access_token
|
||||
|
||||
@classmethod
|
||||
def update_token_at_redis(cls, token: str, add_payload: Union[CompanyToken, OccupantToken]):
|
||||
if already_token_data := RedisActions.get_json(list_keys=[cls.AUTH_TOKEN, token, "*"]).first:
|
||||
def update_token_at_redis(cls, token: str, add_payload: Union[CompanyToken, OccupantToken], add_uuid: str):
|
||||
if already_token_data := RedisActions.get_json(list_keys=[cls.AUTH_TOKEN, token, '*', add_uuid]).first:
|
||||
already_token = cls.process_redis_object(already_token_data)
|
||||
if already_token.is_employee and isinstance(add_payload, CompanyToken):
|
||||
already_token.selected_company = add_payload
|
||||
list_keys = [cls.AUTH_TOKEN, token, str(already_token.user_uu_id), str(add_uuid)]
|
||||
print('is_employee: ', list_keys)
|
||||
elif already_token.is_occupant and isinstance(add_payload, OccupantToken):
|
||||
already_token.selected_occupant = add_payload
|
||||
list_keys = [cls.AUTH_TOKEN, token, str(already_token.user_uu_id)]
|
||||
list_keys = [cls.AUTH_TOKEN, token, str(already_token.user_uu_id), str(add_uuid)]
|
||||
print('is_occupant: ', list_keys)
|
||||
result = RedisActions.set_json(list_keys=list_keys, value=already_token.model_dump(), expires={"hours": 1, "minutes": 30})
|
||||
RedisActions.delete(list_keys=[cls.AUTH_TOKEN, token, str(already_token.user_uu_id)])
|
||||
return result.first
|
||||
raise ValueError("Something went wrong")
|
||||
|
|
|
|||
|
|
@ -1,20 +1,19 @@
|
|||
services:
|
||||
|
||||
# client_frontend:
|
||||
# container_name: client_frontend
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: web_services/client_frontend/Dockerfile
|
||||
# networks:
|
||||
# - wag-services
|
||||
# ports:
|
||||
# - "3000:3000"
|
||||
# environment:
|
||||
# - NODE_ENV=development
|
||||
# - WEB_BASE_URL=http://localhost:3000
|
||||
# - API_BASE_URL=http://localhost:3000/api
|
||||
# cpus: 1.5
|
||||
# mem_limit: 2048m
|
||||
client_frontend:
|
||||
container_name: client_frontend
|
||||
build:
|
||||
context: .
|
||||
dockerfile: web_services/client_frontend/Dockerfile
|
||||
networks:
|
||||
- wag-services
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- WEB_BASE_URL=http://localhost:3000
|
||||
- API_BASE_URL=http://localhost:3000/api
|
||||
cpus: 1.5
|
||||
mem_limit: 2048m
|
||||
|
||||
# management_frontend:
|
||||
# container_name: management_frontend
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
"use server";
|
||||
import { retrieveAccessToken } from "@/apifetchers/mutual/cookies/token";
|
||||
import {
|
||||
DEFAULT_RESPONSE,
|
||||
defaultHeaders,
|
||||
FetchOptions,
|
||||
HttpMethod,
|
||||
ApiResponse,
|
||||
DEFAULT_TIMEOUT,
|
||||
} from "./basics";
|
||||
|
||||
/**
|
||||
* Creates a promise that rejects after a specified timeout
|
||||
* @param ms Timeout in milliseconds
|
||||
* @param controller AbortController to abort the fetch request
|
||||
* @returns A promise that rejects after the timeout
|
||||
*/
|
||||
const createTimeoutPromise = (
|
||||
ms: number,
|
||||
controller: AbortController
|
||||
): Promise<never> => {
|
||||
return new Promise((_, reject) => {
|
||||
setTimeout(() => {
|
||||
controller.abort();
|
||||
reject(new Error(`Request timed out after ${ms}ms`));
|
||||
}, ms);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Core fetch function with timeout and error handling
|
||||
* @param url The URL to fetch
|
||||
* @param options Fetch options
|
||||
* @param headers Request headers
|
||||
* @param payload Request payload
|
||||
* @returns API response
|
||||
*/
|
||||
async function coreFetch<T>(
|
||||
url: string,
|
||||
options: FetchOptions = {},
|
||||
headers: Record<string, string> = defaultHeaders,
|
||||
payload?: any
|
||||
): Promise<ApiResponse<T>> {
|
||||
const { method = "POST", cache = false, timeout = DEFAULT_TIMEOUT } = options;
|
||||
|
||||
try {
|
||||
// Setup controller for timeout handling
|
||||
const controller = new AbortController();
|
||||
|
||||
// Prepare fetch options
|
||||
const fetchOptions: RequestInit = {
|
||||
method,
|
||||
headers,
|
||||
cache: cache ? "force-cache" : "no-cache",
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
// Add body for non-GET requests with payload
|
||||
if (method !== "GET" && payload) {
|
||||
fetchOptions.body = JSON.stringify(
|
||||
payload.payload ? payload.payload : payload
|
||||
);
|
||||
}
|
||||
|
||||
// Create timeout promise
|
||||
const timeoutPromise = createTimeoutPromise(timeout, controller);
|
||||
|
||||
// Execute request with timeout
|
||||
const response = await Promise.race([
|
||||
fetch(url, fetchOptions),
|
||||
timeoutPromise,
|
||||
]);
|
||||
|
||||
// Parse response
|
||||
const responseData = await response.json();
|
||||
|
||||
// Return standardized response
|
||||
return {
|
||||
status: response.status,
|
||||
data: responseData || ({} as T),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`API Error (${url}):`, error);
|
||||
return {
|
||||
...DEFAULT_RESPONSE,
|
||||
error: error instanceof Error ? error.message : "Network error",
|
||||
} as ApiResponse<T>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data without authentication
|
||||
*/
|
||||
async function fetchData<T>(
|
||||
endpoint: string,
|
||||
payload?: any,
|
||||
method: HttpMethod = "POST",
|
||||
cache: boolean = false,
|
||||
timeout: number = DEFAULT_TIMEOUT
|
||||
): Promise<ApiResponse<T>> {
|
||||
return coreFetch<T>(
|
||||
endpoint,
|
||||
{ method, cache, timeout },
|
||||
defaultHeaders,
|
||||
payload
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data with authentication token
|
||||
*/
|
||||
async function fetchDataWithToken<T>(
|
||||
endpoint: string,
|
||||
payload?: any,
|
||||
method: HttpMethod = "POST",
|
||||
cache: boolean = false,
|
||||
timeout: number = DEFAULT_TIMEOUT
|
||||
): Promise<ApiResponse<T>> {
|
||||
const accessToken = (await retrieveAccessToken()) || "";
|
||||
const headers = {
|
||||
...defaultHeaders,
|
||||
"eys-acs-tkn": accessToken,
|
||||
};
|
||||
|
||||
return coreFetch<T>(endpoint, { method, cache, timeout }, headers, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data with authentication token and UUID
|
||||
*/
|
||||
async function updateDataWithToken<T>(
|
||||
endpoint: string,
|
||||
uuid: string,
|
||||
payload?: any,
|
||||
method: HttpMethod = "POST",
|
||||
cache: boolean = false,
|
||||
timeout: number = DEFAULT_TIMEOUT
|
||||
): Promise<ApiResponse<T>> {
|
||||
const accessToken = (await retrieveAccessToken()) || "";
|
||||
const headers = {
|
||||
...defaultHeaders,
|
||||
"eys-acs-tkn": accessToken,
|
||||
};
|
||||
|
||||
return coreFetch<T>(
|
||||
`${endpoint}/${uuid}`,
|
||||
{ method, cache, timeout },
|
||||
headers,
|
||||
payload
|
||||
);
|
||||
}
|
||||
|
||||
export { fetchData, fetchDataWithToken, updateDataWithToken };
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
const formatServiceUrl = (url: string) => {
|
||||
if (!url) return "";
|
||||
return url.startsWith("http") ? url : `http://${url}`;
|
||||
};
|
||||
|
||||
const baseUrlAuth = formatServiceUrl(
|
||||
process.env.NEXT_PUBLIC_AUTH_SERVICE_URL || "auth_service:8001"
|
||||
);
|
||||
const baseUrlRestriction = formatServiceUrl(
|
||||
process.env.NEXT_PUBLIC_RESTRICTION_SERVICE_URL || "restriction_service:8002"
|
||||
);
|
||||
const baseUrlApplication = formatServiceUrl(
|
||||
process.env.NEXT_PUBLIC_MANAGEMENT_SERVICE_URL || "management_service:8003"
|
||||
);
|
||||
const baseUrlAccount = formatServiceUrl(
|
||||
process.env.NEXT_PUBLIC_ACCOUNT_SERVICE_URL || "account_service:8004"
|
||||
);
|
||||
const baseUrlBuilding = formatServiceUrl(
|
||||
process.env.NEXT_PUBLIC_BUILDING_SERVICE_URL || "building_service:8006"
|
||||
);
|
||||
const baseUrlPeople = formatServiceUrl(
|
||||
process.env.NEXT_PUBLIC_VALIDATION_SERVICE_URL || "validation_service:8009"
|
||||
);
|
||||
|
||||
// Types for better type safety
|
||||
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
||||
|
||||
interface ApiResponse<T = any> {
|
||||
status: number;
|
||||
data: T;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
interface FetchOptions {
|
||||
method?: HttpMethod;
|
||||
cache?: boolean;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
const tokenSecret =
|
||||
process.env.TOKENSECRET_90 || "787a4a44-2a27-454d-8918-35ceab50592d";
|
||||
|
||||
const cookieObject: any = {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "none",
|
||||
secure: true,
|
||||
maxAge: 3600,
|
||||
priority: "high",
|
||||
};
|
||||
|
||||
// Constants
|
||||
const DEFAULT_TIMEOUT = 10000; // 10 seconds
|
||||
const defaultHeaders = {
|
||||
accept: "application/json",
|
||||
language: "tr",
|
||||
domain: "evyos.com.tr",
|
||||
tz: "GMT+3",
|
||||
"Content-type": "application/json",
|
||||
};
|
||||
const DEFAULT_RESPONSE: ApiResponse = {
|
||||
error: "Hata tipi belirtilmedi",
|
||||
status: 500,
|
||||
data: {},
|
||||
};
|
||||
|
||||
export type { HttpMethod, ApiResponse, FetchOptions };
|
||||
export {
|
||||
DEFAULT_TIMEOUT,
|
||||
DEFAULT_RESPONSE,
|
||||
defaultHeaders,
|
||||
baseUrlAuth,
|
||||
baseUrlPeople,
|
||||
baseUrlApplication,
|
||||
baseUrlAccount,
|
||||
baseUrlBuilding,
|
||||
baseUrlRestriction,
|
||||
tokenSecret,
|
||||
cookieObject,
|
||||
};
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
|
@ -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 };
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
'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 online = await checkContextPageOnline();
|
||||
const lang = online?.lang || "en";
|
||||
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 AuthPageEn;
|
||||
|
|
@ -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,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,24 +8,16 @@ 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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,14 +97,8 @@ async function coreFetch<T>(
|
|||
|
||||
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);
|
||||
} catch (error) {
|
||||
console.error(`Fetch error (${url}):`, error);
|
||||
return {
|
||||
...DEFAULT_RESPONSE,
|
||||
error: error instanceof Error ? error.message : "Network error",
|
||||
|
|
|
|||
|
|
@ -44,9 +44,6 @@ async function listApplicationsAvailable(payload: PaginationParams): Promise<Pag
|
|||
query: payload.query,
|
||||
};
|
||||
|
||||
console.log('Sending request to backend with service_uu_id:', payload.query.service_uu_id);
|
||||
console.log('Full request body:', JSON.stringify(requestBody, null, 2));
|
||||
|
||||
const response = await fetchDataWithToken(
|
||||
applicationListAvailableEndpoint,
|
||||
requestBody,
|
||||
|
|
@ -56,7 +53,6 @@ async function listApplicationsAvailable(payload: PaginationParams): Promise<Pag
|
|||
|
||||
if (response?.status === 200 || response?.status === 202) {
|
||||
const responseData = response.data as PaginatedApiResponse<any>;
|
||||
console.log('list_events_available responseData:', JSON.stringify(responseData, null, 2));
|
||||
return {
|
||||
data: responseData.data || [],
|
||||
pagination: collectPaginationFromApiResponse(responseData)
|
||||
|
|
@ -93,9 +89,6 @@ async function listApplicationsAppended(payload: PaginationParams): Promise<Pagi
|
|||
query: payload.query,
|
||||
};
|
||||
|
||||
console.log('Sending request to backend with service_uu_id:', payload.query.service_uu_id);
|
||||
console.log('Full request body:', JSON.stringify(requestBody, null, 2));
|
||||
|
||||
const response = await fetchDataWithToken(
|
||||
applicationListAppendedEndpoint,
|
||||
requestBody,
|
||||
|
|
@ -105,7 +98,6 @@ async function listApplicationsAppended(payload: PaginationParams): Promise<Pagi
|
|||
|
||||
if (response?.status === 200 || response?.status === 202) {
|
||||
const responseData = response.data as PaginatedApiResponse<any>;
|
||||
console.log('list_events_available responseData:', JSON.stringify(responseData, null, 2));
|
||||
return {
|
||||
data: responseData.data || [],
|
||||
pagination: collectPaginationFromApiResponse(responseData)
|
||||
|
|
@ -116,7 +108,6 @@ async function listApplicationsAppended(payload: PaginationParams): Promise<Pagi
|
|||
pagination: defaultPaginationResponse,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching events list:", error);
|
||||
return {
|
||||
data: [],
|
||||
pagination: defaultPaginationResponse,
|
||||
|
|
@ -134,8 +125,6 @@ async function listAllApplications(payload: PaginationParams): Promise<Paginated
|
|||
query: payload.query,
|
||||
};
|
||||
|
||||
// console.log('Sending request to backend with service_uu_id:', payload.query.service_uu_id);
|
||||
// console.log('Full request body:', JSON.stringify(requestBody, null, 2));
|
||||
|
||||
const response = await fetchDataWithToken(
|
||||
applicationListEndpoint,
|
||||
|
|
@ -156,7 +145,6 @@ async function listAllApplications(payload: PaginationParams): Promise<Paginated
|
|||
pagination: defaultPaginationResponse,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching events list:", error);
|
||||
return {
|
||||
data: [],
|
||||
pagination: defaultPaginationResponse,
|
||||
|
|
@ -174,7 +162,7 @@ async function appendApplicationToService(payload: AppendApplicationToService) {
|
|||
);
|
||||
return response?.status === 200 || response?.status === 202 ? response.data : null;
|
||||
} catch (error) {
|
||||
console.error("Error appending event to service:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -188,12 +176,10 @@ async function removeApplicationFromService(payload: RemoveApplicationFromServic
|
|||
);
|
||||
return response?.status === 200 || response?.status === 202 ? response.data : null;
|
||||
} catch (error) {
|
||||
console.error("Error removing event from service:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function createApplication(payload: any) {
|
||||
console.log("Creating application with payload:", payload);
|
||||
try {
|
||||
const response = await fetchDataWithToken(
|
||||
applicationCreateEndpoint,
|
||||
|
|
@ -211,7 +197,6 @@ async function createApplication(payload: any) {
|
|||
}
|
||||
|
||||
async function updateApplication(payload: any, uuId: string) {
|
||||
console.log("Updating application with payload:", payload, 'uuId:', uuId);
|
||||
try {
|
||||
const response = await fetchDataWithToken(
|
||||
`${applicationUpdateEndpoint}/${uuId}`,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
"use server";
|
||||
|
||||
import { fetchDataWithToken } from "../api-fetcher";
|
||||
import { baseUrlApplication } from "../basics";
|
||||
import { PaginationParams } from "../schemas/list";
|
||||
import { fetchDataWithToken } from "@/apicalls/api-fetcher";
|
||||
import { baseUrlApplication } from "@/apicalls/basics";
|
||||
import { PaginationParams } from "@/apicalls/schemas/list";
|
||||
import { PaginatedApiResponse, collectPaginationFromApiResponse, defaultPaginationResponse } from "@/app/api/utils/types";
|
||||
|
||||
const eventsListAvailableEndpoint = `${baseUrlApplication}/events/list/available`;
|
||||
|
|
@ -36,9 +36,6 @@ async function list_events_available(payload: PaginationParams): Promise<Paginat
|
|||
query: payload.query,
|
||||
};
|
||||
|
||||
console.log('Sending request to backend with service_uu_id:', payload.query.service_uu_id);
|
||||
console.log('Full request body:', JSON.stringify(requestBody, null, 2));
|
||||
|
||||
const response = await fetchDataWithToken(
|
||||
eventsListAvailableEndpoint,
|
||||
requestBody,
|
||||
|
|
@ -48,7 +45,6 @@ async function list_events_available(payload: PaginationParams): Promise<Paginat
|
|||
|
||||
if (response?.status === 200 || response?.status === 202) {
|
||||
const responseData = response.data as PaginatedApiResponse<any>;
|
||||
console.log('list_events_available responseData:', JSON.stringify(responseData, null, 2));
|
||||
return {
|
||||
data: responseData.data || [],
|
||||
pagination: collectPaginationFromApiResponse(responseData)
|
||||
|
|
@ -59,7 +55,6 @@ async function list_events_available(payload: PaginationParams): Promise<Paginat
|
|||
pagination: defaultPaginationResponse,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching events list:", error);
|
||||
return {
|
||||
data: [],
|
||||
pagination: defaultPaginationResponse,
|
||||
|
|
|
|||
|
|
@ -72,10 +72,8 @@ async function retrieveUserSelection() {
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
FROM node:23-alpine
|
||||
|
||||
WORKDIR /
|
||||
|
||||
# Copy package.json and package-lock.json
|
||||
COPY /web_services/client_frontend/package*.json ./web_services/client_frontend/
|
||||
|
||||
# Install dependencies
|
||||
RUN cd ./web_services/client_frontend && npm install --legacy-peer-deps
|
||||
|
||||
# Copy the rest of the application
|
||||
COPY /web_services/client_frontend ./web_services/client_frontend
|
||||
|
||||
## Build the Next.js app
|
||||
#RUN cd ./web_services/client_frontend && npm run dev
|
||||
|
||||
# Expose the port the app runs on
|
||||
EXPOSE 3000
|
||||
|
||||
# Command to run the app
|
||||
CMD ["sh", "-c", "cd /web_services/client_frontend && npm run dev"]
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/app/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
});
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
];
|
||||
|
||||
export default eslintConfig;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"name": "client-frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@radix-ui/react-accordion": "^1.2.10",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.13",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.6",
|
||||
"@radix-ui/react-avatar": "^1.1.9",
|
||||
"@radix-ui/react-checkbox": "^1.3.1",
|
||||
"@radix-ui/react-collapsible": "^1.1.10",
|
||||
"@radix-ui/react-context-menu": "^2.2.14",
|
||||
"@radix-ui/react-dialog": "^1.1.13",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.14",
|
||||
"@radix-ui/react-hover-card": "^1.1.13",
|
||||
"@radix-ui/react-label": "^2.1.6",
|
||||
"@radix-ui/react-menubar": "^1.1.14",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.12",
|
||||
"@radix-ui/react-popover": "^1.1.13",
|
||||
"@radix-ui/react-progress": "^1.1.6",
|
||||
"@radix-ui/react-radio-group": "^1.3.6",
|
||||
"@radix-ui/react-scroll-area": "^1.2.8",
|
||||
"@radix-ui/react-select": "^2.2.4",
|
||||
"@radix-ui/react-separator": "^1.1.6",
|
||||
"@radix-ui/react-slider": "^1.3.4",
|
||||
"@radix-ui/react-slot": "^1.2.2",
|
||||
"@radix-ui/react-switch": "^1.2.4",
|
||||
"@radix-ui/react-tabs": "^1.1.11",
|
||||
"@radix-ui/react-toggle": "^1.1.8",
|
||||
"@radix-ui/react-tooltip": "^1.2.6",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"flatpickr": "^4.6.13",
|
||||
"lucide-react": "^0.487.0",
|
||||
"next": "^15.2.4",
|
||||
"next-crypto": "^1.0.8",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.0.0",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.56.3",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"zod": "^3.24.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@tailwindcss/postcss": "^4.1.6",
|
||||
"@types/node": "^20.17.46",
|
||||
"@types/react": "^19.1.4",
|
||||
"@types/react-dom": "^19.1.5",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.3.2",
|
||||
"tailwindcss": "^4.1.6",
|
||||
"tw-animate-css": "^1.2.9",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
const config = {
|
||||
plugins: ["@tailwindcss/postcss"],
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
'use server';
|
||||
import { MaindasboardPageProps } from "@/validations/mutual/dashboard/props";
|
||||
import { DashboardLayout } from "@/layouts/dashboard/layout";
|
||||
|
||||
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 params={parameters} searchParams={searchParameters} lang="en" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MainEnPage;
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import { loginViaAccessKeys } from "@/apicalls/custom/login/login";
|
||||
import { NextResponse } from "next/server";
|
||||
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,
|
||||
password: body.password,
|
||||
rememberMe: body.rememberMe,
|
||||
};
|
||||
const validatedLoginBody = loginSchemaEmail.safeParse(body);
|
||||
if (!validatedLoginBody.success) {
|
||||
return NextResponse.json({
|
||||
status: 422,
|
||||
message: validatedLoginBody.error.message,
|
||||
});
|
||||
}
|
||||
|
||||
const userLogin = await loginViaAccessKeys(dataValidated);
|
||||
if (userLogin.status === 200 || userLogin.status === 202) {
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
message: "Login successfully completed",
|
||||
data: userLogin.data,
|
||||
});
|
||||
} else {
|
||||
return NextResponse.json({
|
||||
status: userLogin.status,
|
||||
message: userLogin.message,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
return NextResponse.json({ status: 401, message: "Invalid credentials" });
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function POST() {
|
||||
async function retrieveAvailableApplication(): Promise<string[]> {
|
||||
return new Promise((resolve) => {
|
||||
const mockList = [
|
||||
"/definitions/identifications/people",
|
||||
"/definitions/identifications/users",
|
||||
"/definitions/building/parts",
|
||||
"/definitions/building/areas",
|
||||
"/building/accounts/managment/accounts",
|
||||
"/building/accounts/managment/budgets",
|
||||
"/building/accounts/parts/accounts",
|
||||
"/building/accounts/parts/budgets",
|
||||
"/building/meetings/regular/actions",
|
||||
"/building/meetings/regular/accounts",
|
||||
"/building/meetings/ergunt/actions",
|
||||
"/building/meetings/ergunt/accounts",
|
||||
"/building/meetings/invited/attendance",
|
||||
"/main/pages/user/dashboard",
|
||||
];
|
||||
resolve(mockList);
|
||||
});
|
||||
}
|
||||
|
||||
const availableApplications = await retrieveAvailableApplication();
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
data: availableApplications,
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function POST(): Promise<NextResponse> {
|
||||
async function retrievePageToRender(): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
resolve("superUserTenantSomething");
|
||||
});
|
||||
}
|
||||
|
||||
const pageToRender = await retrievePageToRender();
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
data: pageToRender,
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { z } from "zod";
|
||||
import { loginSelectEmployee } from "@/apicalls/custom/login/login";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
const loginSchemaEmployee = z.object({
|
||||
company_uu_id: 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 = {
|
||||
company_uu_id: body.company_uu_id,
|
||||
};
|
||||
const validatedLoginBody = loginSchemaEmployee.safeParse(body);
|
||||
if (!validatedLoginBody.success) {
|
||||
return NextResponse.json({
|
||||
status: 422,
|
||||
message: validatedLoginBody.error.message,
|
||||
});
|
||||
}
|
||||
|
||||
const userLogin = await loginSelectEmployee(dataValidated);
|
||||
if (userLogin.status === 200 || userLogin.status === 202) {
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
message: "Selection successfully completed",
|
||||
data: userLogin.data,
|
||||
});
|
||||
} else {
|
||||
return NextResponse.json({
|
||||
status: userLogin.status,
|
||||
message: userLogin.message,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
return NextResponse.json({ status: 401, message: "Invalid credentials" });
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { z } from "zod";
|
||||
import { loginSelectOccupant } from "@/apicalls/custom/login/login";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
const loginSchemaOccupant = z.object({
|
||||
build_living_space_uu_id: 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,
|
||||
};
|
||||
const validatedLoginBody = loginSchemaOccupant.safeParse(body);
|
||||
if (!validatedLoginBody.success) {
|
||||
return NextResponse.json({
|
||||
status: 422,
|
||||
message: validatedLoginBody.error.message,
|
||||
});
|
||||
}
|
||||
|
||||
const userLogin = await loginSelectOccupant(dataValidated);
|
||||
if (userLogin.status === 200 || userLogin.status === 202) {
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
message: "Selection successfully completed",
|
||||
data: userLogin.data,
|
||||
});
|
||||
} else {
|
||||
return NextResponse.json({
|
||||
status: userLogin.status,
|
||||
message: userLogin.message,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
return NextResponse.json({ status: 401, message: "Invalid credentials" });
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
import { NextRequest } from "next/server";
|
||||
import {
|
||||
successResponse,
|
||||
errorResponse,
|
||||
paginationResponse,
|
||||
createResponse,
|
||||
updateResponse,
|
||||
deleteResponse,
|
||||
} from "./responseHandlers";
|
||||
import { withErrorHandling, validateRequiredFields } from "./requestHandlers";
|
||||
import {
|
||||
ApiHandler,
|
||||
PaginationParams,
|
||||
ListFunction,
|
||||
CreateFunction,
|
||||
UpdateFunction,
|
||||
DeleteFunction,
|
||||
} from "./types";
|
||||
|
||||
/**
|
||||
* Generic list operation handler
|
||||
* @param request NextRequest object
|
||||
* @param body Request body
|
||||
* @param listFunction The function to call to get the list data
|
||||
*/
|
||||
export async function handleListOperation(
|
||||
request: NextRequest,
|
||||
body: any,
|
||||
listFunction: ListFunction
|
||||
) {
|
||||
const page = body.page || 1;
|
||||
const size = body.size || 10;
|
||||
const orderField = body.orderField || ["uu_id"];
|
||||
const orderType = body.orderType || ["asc"];
|
||||
const query = body.query || {};
|
||||
const response = await listFunction({
|
||||
page,
|
||||
size,
|
||||
orderField,
|
||||
orderType,
|
||||
query,
|
||||
} as PaginationParams);
|
||||
return paginationResponse(response.data, response.pagination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic create operation handler
|
||||
* @param request NextRequest object
|
||||
* @param body Request body
|
||||
* @param createFunction The function to call to create the item
|
||||
* @param requiredFields Array of required field names
|
||||
*/
|
||||
export async function handleCreateOperation(
|
||||
body: any,
|
||||
createFunction?: CreateFunction,
|
||||
requiredFields: string[] = []
|
||||
) {
|
||||
if (requiredFields.length > 0) {
|
||||
const validation = validateRequiredFields(body, requiredFields);
|
||||
if (!validation.valid) {
|
||||
return errorResponse(validation.error as string, 400);
|
||||
}
|
||||
}
|
||||
|
||||
if (createFunction) {
|
||||
console.log("Body:", body);
|
||||
const result = await createFunction(body);
|
||||
return createResponse(result);
|
||||
}
|
||||
|
||||
return createResponse({
|
||||
uuid: Math.floor(Math.random() * 1000),
|
||||
...body,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic update operation handler
|
||||
* @param request NextRequest object
|
||||
* @param body Request body
|
||||
* @param updateFunction The function to call to update the item
|
||||
*/
|
||||
export async function handleUpdateOperation(
|
||||
request: NextRequest,
|
||||
body: any,
|
||||
updateFunction?: UpdateFunction
|
||||
) {
|
||||
const uuid = request.nextUrl.searchParams.get("uuid");
|
||||
if (!uuid) {
|
||||
return errorResponse("UUID not found", 400);
|
||||
}
|
||||
if (updateFunction) {
|
||||
console.log("Body:", body);
|
||||
const result = await updateFunction(body, uuid);
|
||||
return updateResponse(result);
|
||||
}
|
||||
return updateResponse(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic delete operation handler
|
||||
* @param request NextRequest object
|
||||
* @param deleteFunction The function to call to delete the item
|
||||
*/
|
||||
export async function handleDeleteOperation(
|
||||
request: NextRequest,
|
||||
deleteFunction?: DeleteFunction
|
||||
) {
|
||||
const uuid = request.nextUrl.searchParams.get("uuid");
|
||||
if (!uuid) {
|
||||
return errorResponse("UUID not found", 400);
|
||||
}
|
||||
|
||||
if (deleteFunction) {
|
||||
await deleteFunction(uuid);
|
||||
}
|
||||
return deleteResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapped list handler with error handling
|
||||
* @param listFunction The function to call to get the list data
|
||||
*/
|
||||
export function createListHandler(listFunction: ListFunction) {
|
||||
return withErrorHandling((request: NextRequest, body: any) =>
|
||||
handleListOperation(request, body, listFunction)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapped create handler with error handling
|
||||
* @param createFunction The function to call to create the item
|
||||
* @param requiredFields Array of required field names
|
||||
*/
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapped update handler with error handling
|
||||
* @param updateFunction The function to call to update the item
|
||||
*/
|
||||
export function createUpdateHandler(updateFunction?: UpdateFunction) {
|
||||
return withErrorHandling((request: NextRequest, body: any) =>
|
||||
handleUpdateOperation(request, body, updateFunction)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapped delete handler with error handling
|
||||
* @param deleteFunction The function to call to delete the item
|
||||
*/
|
||||
export function createDeleteHandler(deleteFunction?: DeleteFunction) {
|
||||
return withErrorHandling((request: NextRequest) =>
|
||||
handleDeleteOperation(request, deleteFunction)
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// Export all utility functions from a single entry point
|
||||
export * from './responseHandlers';
|
||||
export * from './requestHandlers';
|
||||
export * from './apiOperations';
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
import { NextRequest } from "next/server";
|
||||
import { errorResponse } from "./responseHandlers";
|
||||
import { ValidationResult, ApiHandler, ApiHandlerBodyOnly, ApiHandlerWithRequest } from "./types";
|
||||
|
||||
/**
|
||||
* Safely parse JSON request body with error handling
|
||||
* @param request NextRequest object
|
||||
* @returns Parsed request body or null if parsing fails
|
||||
*/
|
||||
export async function parseRequestBody(request: NextRequest) {
|
||||
try {
|
||||
return await request.json();
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for API route handlers with built-in error handling
|
||||
* @param handler The handler function to wrap
|
||||
*/
|
||||
export function withErrorHandling(
|
||||
handler: ApiHandler
|
||||
) {
|
||||
return async (request: NextRequest) => {
|
||||
try {
|
||||
const body = await parseRequestBody(request);
|
||||
|
||||
if (body === null) {
|
||||
return errorResponse("Invalid request body", 400);
|
||||
}
|
||||
|
||||
// Check handler parameter count to determine if it needs request object
|
||||
// If handler has only 1 parameter, it's likely a create operation that only needs body
|
||||
if (handler.length === 1) {
|
||||
// Cast to the appropriate handler type
|
||||
return await (handler as ApiHandlerBodyOnly)(body);
|
||||
} else {
|
||||
// Otherwise pass both request and body (for list, update, delete operations)
|
||||
return await (handler as ApiHandlerWithRequest)(request, body);
|
||||
}
|
||||
} catch (error: any) {
|
||||
return errorResponse(
|
||||
error.message || "Internal Server Error",
|
||||
error.status || 500
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that required fields are present in the request body
|
||||
* @param body Request body
|
||||
* @param requiredFields Array of required field names
|
||||
* @returns Object with validation result and error message if validation fails
|
||||
*/
|
||||
export function validateRequiredFields(body: any, requiredFields: string[]): ValidationResult {
|
||||
const missingFields = requiredFields.filter(field =>
|
||||
body[field] === undefined || body[field] === null || body[field] === ''
|
||||
);
|
||||
|
||||
if (missingFields.length > 0) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Missing required fields: ${missingFields.join(', ')}`
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
import { NextResponse } from "next/server";
|
||||
import { ApiResponse, PaginationResponse, PaginatedApiResponse } from "./types";
|
||||
|
||||
/**
|
||||
* Standard success response handler
|
||||
* @param data The data to return in the response
|
||||
* @param status HTTP status code (default: 200)
|
||||
*/
|
||||
export function successResponse<T>(data: T, status: number = 200) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
data,
|
||||
} as ApiResponse<T>,
|
||||
{ status }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard error response handler
|
||||
* @param message Error message
|
||||
* @param status HTTP status code (default: 500)
|
||||
*/
|
||||
export function errorResponse(message: string, status: number = 500) {
|
||||
console.error(`API error: ${message}`);
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: message
|
||||
} as ApiResponse<never>,
|
||||
{ status }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard pagination response format
|
||||
* @param data Array of items to return
|
||||
* @param pagination Pagination information
|
||||
*/
|
||||
export function paginationResponse<T>(data: T[], pagination: PaginationResponse | null) {
|
||||
return NextResponse.json({
|
||||
data: data || [],
|
||||
pagination: pagination || {
|
||||
page: 1,
|
||||
size: 10,
|
||||
totalCount: 0,
|
||||
totalItems: 0,
|
||||
totalPages: 0,
|
||||
pageCount: 0,
|
||||
orderField: ["name"],
|
||||
orderType: ["asc"],
|
||||
query: {},
|
||||
next: false,
|
||||
back: false,
|
||||
},
|
||||
} as PaginatedApiResponse<T>);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create response handler
|
||||
* @param data The created item data
|
||||
*/
|
||||
export function createResponse<T>(data: T) {
|
||||
return successResponse(
|
||||
{
|
||||
...data as any,
|
||||
createdAt: new Date().toISOString(),
|
||||
} as T,
|
||||
201
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update response handler
|
||||
* @param data The updated item data
|
||||
*/
|
||||
export function updateResponse<T>(data: T) {
|
||||
return successResponse(
|
||||
{
|
||||
...data as any,
|
||||
updatedAt: new Date().toISOString(),
|
||||
} as T
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete response handler
|
||||
*/
|
||||
export function deleteResponse() {
|
||||
return successResponse({ message: "Item deleted successfully" }, 204);
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
* Type definitions for API utilities
|
||||
*/
|
||||
|
||||
import { NextRequest } from "next/server";
|
||||
|
||||
/**
|
||||
* Validation result interface
|
||||
*/
|
||||
export interface ValidationResult {
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pagination parameters interface
|
||||
*/
|
||||
export interface PaginationParams {
|
||||
page: number;
|
||||
size: number;
|
||||
orderField: string[];
|
||||
orderType: string[];
|
||||
query: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pagination response interface
|
||||
*/
|
||||
export interface PaginationResponse {
|
||||
page: number;
|
||||
size: number;
|
||||
totalCount: number;
|
||||
totalItems: number;
|
||||
totalPages: number;
|
||||
pageCount: number;
|
||||
orderField: string[];
|
||||
orderType: string[];
|
||||
query: Record<string, any>;
|
||||
next: boolean;
|
||||
back: boolean;
|
||||
}
|
||||
|
||||
export const defaultPaginationResponse: PaginationResponse = {
|
||||
page: 1,
|
||||
size: 10,
|
||||
totalCount: 0,
|
||||
totalItems: 0,
|
||||
totalPages: 0,
|
||||
pageCount: 0,
|
||||
orderField: ["uu_id"],
|
||||
orderType: ["asc"],
|
||||
query: {},
|
||||
next: false,
|
||||
back: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* API response interface
|
||||
*/
|
||||
export interface ApiResponse<T> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paginated API response interface
|
||||
*/
|
||||
export interface PaginatedApiResponse<T> {
|
||||
data: T[];
|
||||
pagination: PaginationResponse;
|
||||
}
|
||||
|
||||
export const collectPaginationFromApiResponse = (
|
||||
response: PaginatedApiResponse<any>
|
||||
): PaginationResponse => {
|
||||
return {
|
||||
page: response.pagination?.page || 1,
|
||||
size: response.pagination?.size || 10,
|
||||
totalCount: response.pagination?.totalCount || 0,
|
||||
totalItems: response.pagination?.totalItems || 0,
|
||||
totalPages: response.pagination?.totalPages || 0,
|
||||
pageCount: response.pagination?.pageCount || 0,
|
||||
orderField: response.pagination?.orderField || ["uu_id"],
|
||||
orderType: response.pagination?.orderType || ["asc"],
|
||||
query: response.pagination?.query || {},
|
||||
next: response.pagination?.next || false,
|
||||
back: response.pagination?.back || false,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* API handler function types
|
||||
*/
|
||||
export type ApiHandlerWithRequest = (request: NextRequest, body: any) => Promise<Response>;
|
||||
export type ApiHandlerBodyOnly = (body: any) => Promise<Response>;
|
||||
export type ApiHandler = ApiHandlerWithRequest | ApiHandlerBodyOnly;
|
||||
|
||||
/**
|
||||
* List function type
|
||||
*/
|
||||
export type ListFunction = (
|
||||
params: PaginationParams
|
||||
) => Promise<PaginatedApiResponse<any>>;
|
||||
|
||||
/**
|
||||
* Create function type
|
||||
*/
|
||||
export type CreateFunction = (data: any) => Promise<any>;
|
||||
|
||||
/**
|
||||
* Update function type
|
||||
*/
|
||||
export type UpdateFunction = (id: any, data: any) => Promise<any>;
|
||||
|
||||
/**
|
||||
* Delete function type
|
||||
*/
|
||||
export type DeleteFunction = (id: any) => Promise<any>;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
|
|
@ -0,0 +1,122 @@
|
|||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
"use server";
|
||||
import Link from "next/link";
|
||||
|
||||
export default async function Home() {
|
||||
const currentDate = new Date().toLocaleString("tr-TR", { timeZone: "Europe/Istanbul" });
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-b from-blue-50 to-white p-8">
|
||||
<div className="w-full max-w-4xl bg-white rounded-xl shadow-lg overflow-hidden">
|
||||
|
||||
<div className="bg-blue-600 text-white p-8 text-center">
|
||||
<h1 className="text-4xl font-bold mb-2">Welcome to EVYOS</h1>
|
||||
<p className="text-xl">Enterprise Management System</p>
|
||||
<p className="text-sm mt-4">Server Time: {currentDate}</p>
|
||||
</div>
|
||||
|
||||
<div className="p-8">
|
||||
<div className="bg-white rounded-lg p-6 border border-gray-200">
|
||||
<Link
|
||||
href="/auth/en/login"
|
||||
className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors"
|
||||
>Go to Sign In</Link>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 text-center text-sm text-gray-600">
|
||||
<p>© {new Date().getFullYear()} EVYOS. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,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 ApplicationToRender = await resolveWhichPageToRenderMulti({ activePageUrl })
|
||||
if (!ApplicationToRender) return <ContentToRenderNoPage lang={lang} />
|
||||
return <ApplicationToRender lang={lang} translations={translations} activePageUrl={activePageUrl} mode={mode} />
|
||||
}
|
||||
|
||||
export default PageToBeChildrendMulti
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { ContentProps } from "@/validations/mutual/dashboard/props";
|
||||
import ContentToRenderNoPage from "@/pages/mutual/noContent/page";
|
||||
import { resolveWhichPageToRenderSingle } from "@/pages/resolver/resolver";
|
||||
|
||||
const PageToBeChildrendSingle: React.FC<ContentProps> = async ({ lang, translations, activePageUrl, mode }) => {
|
||||
const ApplicationToRender = await resolveWhichPageToRenderSingle({ activePageUrl })
|
||||
if (ApplicationToRender) {
|
||||
return <ApplicationToRender lang={lang} translations={translations} activePageUrl={activePageUrl} mode={mode} />
|
||||
}
|
||||
return <ContentToRenderNoPage lang={lang} />
|
||||
}
|
||||
|
||||
export default PageToBeChildrendSingle
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue