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
|
return str(email).split("@")[1] == api_config.ACCESS_EMAIL_EXT
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
# headers: CommonHeaders
|
||||||
def do_employee_login(cls, headers: CommonHeaders, data: Any, db_session):
|
def do_employee_login(cls, headers: CommonHeaders, data: Any, db_session):
|
||||||
"""Handle employee login."""
|
"""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(), [], ""
|
user_handler, other_domains_list, main_domain = 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)
|
||||||
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:
|
||||||
|
|
@ -78,7 +84,7 @@ class LoginHandler:
|
||||||
raise ValueError("EYS_00087")
|
raise ValueError("EYS_00087")
|
||||||
other_domains_list = result.get("other_domains_list", [])
|
other_domains_list = result.get("other_domains_list", [])
|
||||||
main_domain = result.get("main_domain", None)
|
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")
|
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):
|
||||||
|
|
@ -115,24 +121,24 @@ class LoginHandler:
|
||||||
duty_uu_id_list.append(str(list_employee["Duty.uu_id"]))
|
duty_uu_id_list.append(str(list_employee["Duty.uu_id"]))
|
||||||
duty_id_list.append(int(list_employee["Duty.id"]))
|
duty_id_list.append(int(list_employee["Duty.id"]))
|
||||||
companies_list.append({
|
companies_list.append({
|
||||||
"uu_id": str(list_employee["Companies.uu_id"]), "public_name": list_employee["Companies.public_name"],
|
"uu_id": str(list_employee["Employees.uu_id"]),
|
||||||
"company_type": list_employee["Companies.company_type"], "company_address": list_employee["Addresses.letter_address"],
|
"public_name": list_employee["Companies.public_name"],
|
||||||
"duty": list_employee["Duty.duty_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(
|
model_value = EmployeeTokenObject(
|
||||||
user_type=UserType.employee.value,
|
user_type=UserType.employee.value, user_uu_id=str(found_user.uu_id), user_id=found_user.id, person_id=found_user.person_id,
|
||||||
user_uu_id=str(found_user.uu_id),
|
person_uu_id=str(list_employees[0]["People.uu_id"]), request=dict(headers.request.headers), domain_list=other_domains_list,
|
||||||
user_id=found_user.id,
|
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,
|
||||||
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()
|
).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()
|
user_dict = found_user.get_dict()
|
||||||
person_dict = found_user.person.get_dict()
|
person_dict = found_user.person.get_dict()
|
||||||
if access_token := RedisHandlers().set_object_to_redis(**set_to_redis_dict):
|
if access_token := RedisHandlers().set_object_to_redis(**set_to_redis_dict):
|
||||||
|
|
@ -140,21 +146,12 @@ class LoginHandler:
|
||||||
"access_token": access_token,
|
"access_token": access_token,
|
||||||
"user_type": UserType.employee.name,
|
"user_type": UserType.employee.name,
|
||||||
"user": {
|
"user": {
|
||||||
"uuid": user_dict["uu_id"],
|
"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"],
|
||||||
"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"]))),
|
"password_expiry_begins": str(arrow.get(user_dict["password_expiry_begins"]).shift(days=int(user_dict["password_expires_day"]))),
|
||||||
"person": {
|
"person": {
|
||||||
"uuid": person_dict["uu_id"],
|
"uuid": person_dict["uu_id"], "firstname": person_dict["firstname"], "surname": person_dict["surname"],
|
||||||
"firstname": person_dict["firstname"],
|
"middle_name": person_dict["middle_name"], "sex_code": person_dict["sex_code"], "person_tag": person_dict["person_tag"],
|
||||||
"surname": person_dict["surname"],
|
"country_code": person_dict["country_code"], "birth_date": person_dict["birth_date"],
|
||||||
"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,
|
"selection_list": companies_list,
|
||||||
|
|
@ -162,22 +159,21 @@ class LoginHandler:
|
||||||
raise ValueError("Something went wrong")
|
raise ValueError("Something went wrong")
|
||||||
|
|
||||||
@classmethod
|
@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.
|
Handle occupant login.
|
||||||
"""
|
"""
|
||||||
language = extra_dict.get("language", "tr")
|
language = headers.request.headers.get("language", "tr")
|
||||||
domain = extra_dict.get("domain", None)
|
domain = headers.request.headers.get("domain", None)
|
||||||
timezone = extra_dict.get("tz", None) or "GMT+3"
|
timezone = headers.request.headers.get("tz", None) or "GMT+3"
|
||||||
|
BuildParts.set_session(db_session)
|
||||||
|
OccupantTypes.set_session(db_session)
|
||||||
|
|
||||||
user_handler = UserHandlers()
|
user_handler = UserHandlers()
|
||||||
found_user = user_handler.check_user_exists(
|
found_user = user_handler.check_user_exists(access_key=data.access_key, db_session=db_session)
|
||||||
access_key=data.access_key, db_session=db_session
|
|
||||||
)
|
|
||||||
other_domains_list, main_domain = [], ""
|
other_domains_list, main_domain = [], ""
|
||||||
with mongo_handler.collection(
|
with mongo_handler.collection(f"{str(found_user.related_company)}*Domain") as collection:
|
||||||
f"{str(found_user.related_company)}*Domain"
|
|
||||||
) as collection:
|
|
||||||
result = collection.find_one({"user_uu_id": str(found_user.uu_id)})
|
result = collection.find_one({"user_uu_id": str(found_user.uu_id)})
|
||||||
if not result:
|
if not result:
|
||||||
raise ValueError("EYS_00087")
|
raise ValueError("EYS_00087")
|
||||||
|
|
@ -186,78 +182,43 @@ class LoginHandler:
|
||||||
if 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")
|
raise ValueError("EYS_00088")
|
||||||
|
|
||||||
if not user_handler.check_password_valid(
|
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):
|
||||||
domain=main_domain,
|
|
||||||
id_=str(found_user.uu_id),
|
|
||||||
password=data.password,
|
|
||||||
password_hashed=found_user.hash_password,
|
|
||||||
):
|
|
||||||
raise ValueError("EYS_0005")
|
raise ValueError("EYS_0005")
|
||||||
|
|
||||||
occupants_selection_dict: Dict[str, Any] = {}
|
occupants_selection_dict: Dict[str, Any] = {}
|
||||||
living_spaces: list[BuildLivingSpace] = BuildLivingSpace.filter_all(
|
living_spaces: list[BuildLivingSpace] = BuildLivingSpace.query.filter(BuildLivingSpace.person_id == found_user.person_id).all()
|
||||||
BuildLivingSpace.person_id == found_user.person_id, db=db_session
|
|
||||||
).data
|
|
||||||
|
|
||||||
if not living_spaces:
|
if not living_spaces:
|
||||||
raise ValueError("EYS_0006")
|
raise ValueError("EYS_0006")
|
||||||
for living_space in living_spaces:
|
for living_space in living_spaces:
|
||||||
build_part = BuildParts.filter_one(
|
build_part = BuildParts.query.filter(BuildParts.id == living_space.build_parts_id).first()
|
||||||
BuildParts.id == living_space.build_parts_id,
|
|
||||||
db=db_session,
|
|
||||||
).data
|
|
||||||
if not build_part:
|
if not build_part:
|
||||||
raise ValueError("EYS_0007")
|
raise ValueError("EYS_0007")
|
||||||
|
|
||||||
build = build_part.buildings
|
build = build_part.buildings
|
||||||
occupant_type = OccupantTypes.filter_by_one(
|
occupant_type = OccupantTypes.query.filter(OccupantTypes.id == living_space.occupant_type_id).first()
|
||||||
id=living_space.occupant_type_id,
|
|
||||||
db=db_session,
|
|
||||||
system=True,
|
|
||||||
).data
|
|
||||||
occupant_data = {
|
occupant_data = {
|
||||||
"build_living_space_uu_id": str(living_space.uu_id),
|
"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,
|
||||||
"part_uu_id": str(build_part.uu_id),
|
"occupant_uu_id": str(occupant_type.uu_id), "description": occupant_type.occupant_description, "code": occupant_type.occupant_code,
|
||||||
"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_key = str(build.uu_id)
|
build_key = str(build.uu_id)
|
||||||
if build_key not in occupants_selection_dict:
|
if build_key not in occupants_selection_dict:
|
||||||
occupants_selection_dict[build_key] = {
|
occupants_selection_dict[build_key] = {"build_uu_id": build_key, "build_name": build.build_name, "build_no": build.build_no, "occupants": [occupant_data],}
|
||||||
"build_uu_id": build_key,
|
|
||||||
"build_name": build.build_name,
|
|
||||||
"build_no": build.build_no,
|
|
||||||
"occupants": [occupant_data],
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
occupants_selection_dict[build_key]["occupants"].append(occupant_data)
|
occupants_selection_dict[build_key]["occupants"].append(occupant_data)
|
||||||
|
|
||||||
person = found_user.person
|
person = found_user.person
|
||||||
model_value = OccupantTokenObject(
|
model_value = OccupantTokenObject(
|
||||||
user_type=UserType.occupant.value,
|
user_type=UserType.occupant.value, user_uu_id=str(found_user.uu_id), user_id=found_user.id, person_id=person.id,
|
||||||
user_uu_id=str(found_user.uu_id),
|
person_uu_id=str(person.uu_id), domain_list=other_domains_list, request=dict(request.headers), available_occupants=occupants_selection_dict,
|
||||||
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()
|
).model_dump()
|
||||||
redis_handler = RedisHandlers()
|
redis_handler = RedisHandlers()
|
||||||
if access_token := redis_handler.set_object_to_redis(
|
if access_token := redis_handler.set_object_to_redis(
|
||||||
user=found_user,
|
user=found_user, token=model_value, add_uuid=living_space.uu_id,
|
||||||
token=model_value,
|
header_info=dict(language=language, domain=domain, timezone=timezone)
|
||||||
header_info=dict(language=language, domain=domain, timezone=timezone),
|
|
||||||
):
|
):
|
||||||
return {
|
return {"access_token": access_token, "user_type": UserType.occupant.name, "selection_list": occupants_selection_dict}
|
||||||
"access_token": access_token,
|
|
||||||
"user_type": UserType.occupant.name,
|
|
||||||
"selection_list": occupants_selection_dict,
|
|
||||||
}
|
|
||||||
raise ValueError("Something went wrong")
|
raise ValueError("Something went wrong")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -302,7 +263,7 @@ class LoginHandler:
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_employee_selection(cls, access_token: str, data: Any, token_dict: TokenDictType):
|
def handle_employee_selection(cls, access_token: str, data: Any, token_dict: TokenDictType):
|
||||||
with Users.new_session() as db_session:
|
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")
|
ValueError("EYS_0011")
|
||||||
list_of_returns = (
|
list_of_returns = (
|
||||||
Employees.id, Employees.uu_id, People.id, People.uu_id, Users.id, Users.uu_id, Companies.id, Companies.uu_id,
|
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(Companies, Companies.id == Departments.company_id
|
||||||
).join(Users, Users.person_id == People.id
|
).join(Users, Users.person_id == People.id
|
||||||
).outerjoin(Addresses, Addresses.id == Companies.official_address_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()
|
selected_company_first = selected_company_query.first()
|
||||||
if not selected_company_first:
|
if not selected_company_first:
|
||||||
|
|
@ -357,8 +318,10 @@ class LoginHandler:
|
||||||
reachable_app_codes=reachable_app_codes,
|
reachable_app_codes=reachable_app_codes,
|
||||||
)
|
)
|
||||||
redis_handler = RedisHandlers()
|
redis_handler = RedisHandlers()
|
||||||
redis_result = redis_handler.update_token_at_redis(token=access_token, add_payload=company_token)
|
redis_result = redis_handler.update_token_at_redis(
|
||||||
return {"selected_uu_id": data.company_uu_id}
|
token=access_token, add_payload=company_token, add_uuid=str(result_with_keys_dict['Employees.uu_id'])
|
||||||
|
)
|
||||||
|
return {"selected_uu_id": data.uuid}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_occupant_selection(cls, access_token: str, data: Any, token_dict: TokenDictType):
|
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,
|
reachable_app_codes=reachable_app_codes,
|
||||||
)
|
)
|
||||||
redis_handler = RedisHandlers()
|
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}
|
return {"selected_uu_id": occupant_token.living_space_uu_id}
|
||||||
|
|
||||||
@classmethod # Requires auth context
|
@classmethod # Requires auth context
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,7 @@ class RequestVerifyOTP(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class RequestSelectEmployee(BaseModel):
|
class RequestSelectEmployee(BaseModel):
|
||||||
|
uuid: str
|
||||||
company_uu_id: str
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_employee(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_occupant(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class RequestResetPassword(BaseModel):
|
class RequestResetPassword(BaseModel):
|
||||||
|
|
@ -34,17 +25,7 @@ class RequestResetPassword(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class RequestSelectLiving(BaseModel):
|
class RequestSelectLiving(BaseModel):
|
||||||
|
uuid: str
|
||||||
build_living_space_uu_id: str
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_employee(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_occupant(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class RequestCreatePassword(BaseModel):
|
class RequestCreatePassword(BaseModel):
|
||||||
password_token: str
|
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
|
Verify if page is valid returns application that can user reach
|
||||||
page: { url = /building/create} | result: { "application": "4c11f5ef-0bbd-41ac-925e-f79d9aac2b0e" }
|
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"
|
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'] }
|
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")
|
raise ValueError("Invalid user type")
|
||||||
|
|
||||||
@classmethod
|
@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
|
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 "*"
|
token_to_use, user_uu_id_to_use = token or "*", user_uu_id or "*"
|
||||||
list_of_token_dict, auth_key_list = [], [
|
list_of_token_dict, auth_key_list = [], [cls.AUTH_TOKEN, token_to_use, user_uu_id_to_use, "*"]
|
||||||
cls.AUTH_TOKEN,
|
|
||||||
token_to_use,
|
|
||||||
user_uu_id_to_use,
|
|
||||||
]
|
|
||||||
if token:
|
if token:
|
||||||
result = RedisActions.get_json(list_keys=auth_key_list, limit=1)
|
result = RedisActions.get_json(list_keys=auth_key_list, limit=1)
|
||||||
if first_record := result.first:
|
if first_record := result.first:
|
||||||
|
|
@ -47,9 +45,7 @@ class TokenProvider:
|
||||||
for all_record in all_records:
|
for all_record in all_records:
|
||||||
list_of_token_dict.append(cls.convert_redis_object_to_token(all_record))
|
list_of_token_dict.append(cls.convert_redis_object_to_token(all_record))
|
||||||
return list_of_token_dict
|
return list_of_token_dict
|
||||||
raise ValueError(
|
raise ValueError("Token not found in Redis. Please check the token or user_uu_id.")
|
||||||
"Token not found in Redis. Please check the token or user_uu_id."
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def retrieve_application_codes(cls, page_url: str, token: TokenDictType):
|
def retrieve_application_codes(cls, page_url: str, token: TokenDictType):
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class RedisHandlers:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_object_from_redis(cls, access_token: str) -> TokenDictType:
|
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:
|
if not redis_response.status:
|
||||||
raise ValueError("EYS_0001")
|
raise ValueError("EYS_0001")
|
||||||
if redis_object := redis_response.first:
|
if redis_object := redis_response.first:
|
||||||
|
|
@ -41,22 +41,30 @@ class RedisHandlers:
|
||||||
raise ValueError("EYS_0002")
|
raise ValueError("EYS_0002")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_object_to_redis(cls, user: Users, token, header_info):
|
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)])
|
result_delete = RedisActions.delete(list_keys=[cls.AUTH_TOKEN, "*", str(user.uu_id), add_uuid])
|
||||||
generated_access_token = PasswordModule.generate_access_token()
|
generated_access_token = PasswordModule.generate_access_token()
|
||||||
keys = [cls.AUTH_TOKEN, generated_access_token, str(user.uu_id)]
|
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})
|
RedisActions.set_json(list_keys=keys, value={**token, **header_info}, expires={"hours": 1, "minutes": 30})
|
||||||
return generated_access_token
|
return generated_access_token
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_token_at_redis(cls, token: str, add_payload: Union[CompanyToken, OccupantToken]):
|
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, "*"]).first:
|
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)
|
already_token = cls.process_redis_object(already_token_data)
|
||||||
if already_token.is_employee and isinstance(add_payload, CompanyToken):
|
if already_token.is_employee and isinstance(add_payload, CompanyToken):
|
||||||
already_token.selected_company = add_payload
|
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):
|
elif already_token.is_occupant and isinstance(add_payload, OccupantToken):
|
||||||
already_token.selected_occupant = add_payload
|
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})
|
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
|
return result.first
|
||||||
raise ValueError("Something went wrong")
|
raise ValueError("Something went wrong")
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
services:
|
services:
|
||||||
|
client_frontend:
|
||||||
# client_frontend:
|
container_name: client_frontend
|
||||||
# container_name: client_frontend
|
build:
|
||||||
# build:
|
context: .
|
||||||
# context: .
|
dockerfile: web_services/client_frontend/Dockerfile
|
||||||
# dockerfile: web_services/client_frontend/Dockerfile
|
networks:
|
||||||
# networks:
|
- wag-services
|
||||||
# - wag-services
|
ports:
|
||||||
# ports:
|
- "3000:3000"
|
||||||
# - "3000:3000"
|
environment:
|
||||||
# environment:
|
- NODE_ENV=development
|
||||||
# - NODE_ENV=development
|
- WEB_BASE_URL=http://localhost:3000
|
||||||
# - WEB_BASE_URL=http://localhost:3000
|
- API_BASE_URL=http://localhost:3000/api
|
||||||
# - API_BASE_URL=http://localhost:3000/api
|
cpus: 1.5
|
||||||
# cpus: 1.5
|
mem_limit: 2048m
|
||||||
# mem_limit: 2048m
|
|
||||||
|
|
||||||
# management_frontend:
|
# management_frontend:
|
||||||
# container_name: management_frontend
|
# container_name: management_frontend
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"flatpickr": "^4.6.13",
|
"flatpickr": "^4.6.13",
|
||||||
|
"ioredis": "^5.6.1",
|
||||||
"lucide-react": "^0.487.0",
|
"lucide-react": "^0.487.0",
|
||||||
"next": "^15.2.4",
|
"next": "^15.2.4",
|
||||||
"next-crypto": "^1.0.8",
|
"next-crypto": "^1.0.8",
|
||||||
|
|
@ -739,6 +740,12 @@
|
||||||
"url": "https://opencollective.com/libvips"
|
"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": {
|
"node_modules/@isaacs/fs-minipass": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||||
|
|
@ -3700,6 +3707,15 @@
|
||||||
"node": ">=6"
|
"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": {
|
"node_modules/cmdk": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz",
|
||||||
|
|
@ -3922,7 +3938,6 @@
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "^2.1.3"
|
"ms": "^2.1.3"
|
||||||
|
|
@ -3979,6 +3994,15 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
|
|
@ -5358,6 +5382,30 @@
|
||||||
"node": ">= 0.4"
|
"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": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
|
|
@ -6181,6 +6229,18 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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": {
|
"node_modules/lodash.merge": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||||
|
|
@ -6366,7 +6426,6 @@
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"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": {
|
"node_modules/reflect.getprototypeof": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
||||||
|
|
@ -7605,6 +7685,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/statuses": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"flatpickr": "^4.6.13",
|
"flatpickr": "^4.6.13",
|
||||||
|
"ioredis": "^5.6.1",
|
||||||
"lucide-react": "^0.487.0",
|
"lucide-react": "^0.487.0",
|
||||||
"next": "^15.2.4",
|
"next": "^15.2.4",
|
||||||
"next-crypto": "^1.0.8",
|
"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 { NextResponse } from "next/server";
|
||||||
|
import { loginViaAccessKeys } from "@/apifetchers/custom/login/login";
|
||||||
import { loginSchemaEmail } from "@/webPages/auth/login/schemas";
|
import { loginSchemaEmail } from "@/webPages/auth/login/schemas";
|
||||||
|
|
||||||
export async function POST(req: Request): Promise<NextResponse> {
|
export async function POST(req: Request): Promise<NextResponse> {
|
||||||
try {
|
try {
|
||||||
const headers = req.headers;
|
const headers = req.headers;
|
||||||
console.log("headers", Object.entries(headers));
|
|
||||||
const body = await req.json();
|
const body = await req.json();
|
||||||
const dataValidated = {
|
const dataValidated = {
|
||||||
accessKey: body.email,
|
accessKey: body.email,
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,16 @@
|
||||||
|
import { API_BASE_URL } from "@/config/config";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
export async function POST() {
|
export async function POST() {
|
||||||
async function retrieveAvailableApplication(): Promise<string[]> {
|
const result = await fetch(`${API_BASE_URL}/context/page/menu`, {
|
||||||
return new Promise((resolve) => {
|
method: "GET",
|
||||||
const mockList = [
|
headers: { "Content-Type": "application/json" },
|
||||||
"management/account/tenant/something",
|
|
||||||
"management/account/tenant/somethingSecond",
|
|
||||||
"building/parts/tenant/something",
|
|
||||||
];
|
|
||||||
resolve(mockList);
|
|
||||||
});
|
});
|
||||||
|
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" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const availableApplications = await retrieveAvailableApplication();
|
|
||||||
return NextResponse.json({
|
|
||||||
status: 200,
|
|
||||||
data: availableApplications,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { loginSelectEmployee } from "@/apicalls/custom/login/login";
|
import { loginSelectEmployee } from "@/apifetchers/custom/login/login";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
const loginSchemaEmployee = z.object({
|
const loginSchemaEmployee = z.object({
|
||||||
company_uu_id: z.string(),
|
uuid: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function POST(req: Request): Promise<NextResponse> {
|
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));
|
console.log("headers", Object.entries(headers));
|
||||||
const body = await req.json();
|
const body = await req.json();
|
||||||
const dataValidated = {
|
const dataValidated = {
|
||||||
company_uu_id: body.company_uu_id,
|
uuid: body.uuid,
|
||||||
};
|
};
|
||||||
const validatedLoginBody = loginSchemaEmployee.safeParse(body);
|
const validatedLoginBody = loginSchemaEmployee.safeParse(body);
|
||||||
if (!validatedLoginBody.success) {
|
if (!validatedLoginBody.success) {
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { loginSelectOccupant } from "@/apicalls/custom/login/login";
|
import { loginSelectOccupant } from "@/apifetchers/custom/login/login";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
const loginSchemaOccupant = z.object({
|
const loginSchemaOccupant = z.object({
|
||||||
build_living_space_uu_id: z.string(),
|
uuid: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function POST(req: Request): Promise<NextResponse> {
|
export async function POST(req: Request): Promise<NextResponse> {
|
||||||
try {
|
try {
|
||||||
const headers = req.headers;
|
const headers = req.headers;
|
||||||
console.log("headers", Object.entries(headers));
|
|
||||||
const body = await req.json();
|
const body = await req.json();
|
||||||
const dataValidated = {
|
const dataValidated = {
|
||||||
build_living_space_uu_id: body.build_living_space_uu_id,
|
uuid: body.uuid,
|
||||||
};
|
};
|
||||||
const validatedLoginBody = loginSchemaOccupant.safeParse(body);
|
const validatedLoginBody = loginSchemaOccupant.safeParse(body);
|
||||||
if (!validatedLoginBody.success) {
|
if (!validatedLoginBody.success) {
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ export async function handleCreateOperation(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (createFunction) {
|
if (createFunction) {
|
||||||
console.log("Body:", body);
|
|
||||||
const result = await createFunction(body);
|
const result = await createFunction(body);
|
||||||
return createResponse(result);
|
return createResponse(result);
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +89,6 @@ export async function handleUpdateOperation(
|
||||||
return errorResponse("UUID not found", 400);
|
return errorResponse("UUID not found", 400);
|
||||||
}
|
}
|
||||||
if (updateFunction) {
|
if (updateFunction) {
|
||||||
console.log("Body:", body);
|
|
||||||
const result = await updateFunction(body, uuid);
|
const result = await updateFunction(body, uuid);
|
||||||
return updateResponse(result);
|
return updateResponse(result);
|
||||||
}
|
}
|
||||||
|
|
@ -136,12 +134,10 @@ export function createCreateHandler(
|
||||||
createFunction?: CreateFunction,
|
createFunction?: CreateFunction,
|
||||||
requiredFields: string[] = []
|
requiredFields: string[] = []
|
||||||
) {
|
) {
|
||||||
console.log("Required fields:", requiredFields);
|
|
||||||
// This handler only takes the body parameter, not the request
|
// This handler only takes the body parameter, not the request
|
||||||
return withErrorHandling((body: any) => {
|
return withErrorHandling((body: any) => {
|
||||||
// Ensure we're only passing the actual body data to the create function
|
// Ensure we're only passing the actual body data to the create function
|
||||||
if (body && typeof body === 'object' && body.body) {
|
if (body && typeof body === 'object' && body.body) {
|
||||||
console.log("Extracting body from request body");
|
|
||||||
return handleCreateOperation(body.body, createFunction, requiredFields);
|
return handleCreateOperation(body.body, createFunction, requiredFields);
|
||||||
}
|
}
|
||||||
return handleCreateOperation(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 { resolveWhichPageToRenderMulti } from "@/pages/resolver/resolver";
|
||||||
import ContentToRenderNoPage from "@/pages/mutual/noContent/page";
|
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 })
|
const ApplicationToRender = await resolveWhichPageToRenderMulti({ activePageUrl })
|
||||||
if (!ApplicationToRender) return <ContentToRenderNoPage lang={lang} />
|
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
|
export default PageToBeChildrendMulti
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,23 @@
|
||||||
import { FC, Suspense } from "react";
|
import { FC, Suspense } from "react";
|
||||||
import { ContentProps, ModeTypes, ModeTypesList } from "@/validations/mutual/dashboard/props";
|
import { ContentProps, ModeTypes, ModeTypesList } from "@/validations/mutual/dashboard/props";
|
||||||
import LoadingContent from "@/components/mutual/loader/component";
|
import LoadingContent from "@/components/mutual/loader/component";
|
||||||
import PageToBeChildrendSingle from "./PageToBeChildrendSingle";
|
|
||||||
import PageToBeChildrendMulti from "./PageToBeChildrendMulti";
|
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 modeFromQuery = ModeTypesList.includes(mode || '') ? mode : 'shortList'
|
||||||
const renderProps = { lang, translations, activePageUrl, mode: modeFromQuery as ModeTypes }
|
const renderProps = { lang, 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 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)]"
|
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 (
|
return <div className={classNameDiv}><Suspense fallback={loadingContent}><PageToBeChildrendMulti {...renderProps} /></Suspense></div>
|
||||||
<div className={classNameDiv}><Suspense fallback={loadingContent}><PageToBeChildrend {...renderProps} /></Suspense></div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ContentComponent;
|
export default ContentComponent;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,23 @@
|
||||||
'use server';
|
'use server';
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { langGetKey } from "@/lib/langGet";
|
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 (
|
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">
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,35 @@
|
||||||
'use server';
|
'use client';
|
||||||
import { FC } from "react";
|
import { FC, useState, useEffect } from "react";
|
||||||
import { HeaderProps } from "@/validations/mutual/dashboard/props";
|
import { AllProps } from "@/validations/mutual/dashboard/props";
|
||||||
import LanguageSelectionComponent from "@/components/mutual/languageSelection/component";
|
import LanguageSelectionComponent from "@/components/mutual/languageSelection/component";
|
||||||
import { langGetKey } from "@/lib/langGet";
|
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 (
|
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 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">
|
<div className="flex flex-row justify-center items-center">
|
||||||
<p className="text-2xl font-bold mx-3">{langGetKey(translations, 'selectedPage')} :</p>
|
<p className="text-2xl font-bold mx-3">{langGetKey(translations[lang], 'selectedPage')} :</p>
|
||||||
<p className="text-lg font-bold mx-3"> {langGetKey(translations, 'page')}</p>
|
<p className="text-lg font-bold mx-3"> {langGetKey(translations[lang], 'page')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div>{JSON.stringify(online)}</div>
|
||||||
<LanguageSelectionComponent lang={lang} activePage={activePageUrl} prefix={prefix} />
|
<LanguageSelectionComponent lang={lang} activePage={activePageUrl} prefix={prefix} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
'use client';
|
'use client';
|
||||||
import { FC, useState, useEffect } from "react";
|
import { FC, useState, useEffect } from "react";
|
||||||
import { MenuProps } from "@/validations/mutual/dashboard/props";
|
import { AllProps } from "@/validations/mutual/dashboard/props";
|
||||||
import { langGetKey } from "@/lib/langGet";
|
|
||||||
import { parseURlFormString } from "@/lib/menuGet";
|
import { parseURlFormString } from "@/lib/menuGet";
|
||||||
|
import { menuTranslation } from "@/languages/mutual/menu"
|
||||||
import FirstLayerDropdown from "./firstLayerComponent";
|
import FirstLayerDropdown from "./firstLayerComponent";
|
||||||
import SecondLayerDropdown from "./secondLayerComponent";
|
import SecondLayerDropdown from "./secondLayerComponent";
|
||||||
import ThirdLayerDropdown from "./thirdLayerComponent";
|
import ThirdLayerDropdown from "./thirdLayerComponent";
|
||||||
|
import { checkContextPageMenu } from "@/components/mutual/context/menu/context";
|
||||||
|
|
||||||
// Define types for menu structure
|
// Define types for menu structure
|
||||||
type ThirdLayerItem = Record<string, any>;
|
type ThirdLayerItem = Record<string, any>;
|
||||||
|
|
@ -13,146 +14,109 @@ type SecondLayerItems = Record<string, ThirdLayerItem>;
|
||||||
type FirstLayerItems = Record<string, SecondLayerItems>;
|
type FirstLayerItems = Record<string, SecondLayerItems>;
|
||||||
type MenuStructure = FirstLayerItems;
|
type MenuStructure = FirstLayerItems;
|
||||||
|
|
||||||
const MenuComponent: FC<MenuProps> = ({ lang, menuItems, menuTranslationsFlatten, activePageUrl }) => {
|
// Helper function to get translation for a URL path
|
||||||
// State for tracking expanded menu items
|
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 [expandedFirstLayer, setExpandedFirstLayer] = useState<string | null>(null);
|
||||||
const [expandedSecondLayer, setExpandedSecondLayer] = useState<string | null>(null);
|
const [expandedSecondLayer, setExpandedSecondLayer] = useState<string | null>(null);
|
||||||
const [menuStructure, setMenuStructure] = useState<MenuStructure>({});
|
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 activePathLayers = parseURlFormString(activePageUrl).data;
|
||||||
const activeFirstLayer = activePathLayers[0] || null;
|
const activeFirstLayer = activePathLayers[0] || null;
|
||||||
const activeSecondLayer = activePathLayers[1] || 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(() => {
|
useEffect(() => {
|
||||||
if (activeFirstLayer) {
|
const newMenuStructure: MenuStructure = {};
|
||||||
setExpandedFirstLayer(activeFirstLayer);
|
availableApplications.forEach((appPath: string) => {
|
||||||
if (activeSecondLayer) {
|
const cleanPath = appPath.startsWith('/') ? appPath.substring(1) : appPath;
|
||||||
setExpandedSecondLayer(activeSecondLayer);
|
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);
|
useEffect(() => { if (activeFirstLayer) { setExpandedFirstLayer(activeFirstLayer); if (activeSecondLayer) { setExpandedSecondLayer(activeSecondLayer) } } }, [activeFirstLayer, activeSecondLayer]);
|
||||||
}, [menuItems]);
|
|
||||||
|
|
||||||
// Handle click on first layer menu item
|
const handleFirstLayerClick = (key: string) => { if (expandedFirstLayer === key) { setExpandedFirstLayer(null); setExpandedSecondLayer(null) } else { setExpandedFirstLayer(key); setExpandedSecondLayer(null) } };
|
||||||
const handleFirstLayerClick = (key: string) => {
|
const handleSecondLayerClick = (key: string) => { if (expandedSecondLayer === key) { setExpandedSecondLayer(null) } else { setExpandedSecondLayer(key) } };
|
||||||
if (expandedFirstLayer === key) {
|
|
||||||
// If already expanded, collapse it
|
|
||||||
setExpandedFirstLayer(null);
|
|
||||||
setExpandedSecondLayer(null);
|
|
||||||
} else {
|
|
||||||
// Otherwise expand it
|
|
||||||
setExpandedFirstLayer(key);
|
|
||||||
setExpandedSecondLayer(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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 renderThirdLayerItems = (firstLayerKey: string, secondLayerKey: string, thirdLayerItems: ThirdLayerItem) => {
|
||||||
const baseUrl = `/${firstLayerKey}/${secondLayerKey}`;
|
const baseUrl = `/${firstLayerKey}/${secondLayerKey}`;
|
||||||
|
|
||||||
return Object.keys(thirdLayerItems).map(thirdLayerKey => {
|
return Object.keys(thirdLayerItems).map(thirdLayerKey => {
|
||||||
const isActive =
|
const isActive = activeFirstLayer === firstLayerKey && activeSecondLayer === secondLayerKey && activeThirdLayer === thirdLayerKey;
|
||||||
activeFirstLayer === firstLayerKey &&
|
const mergeUrl = `${baseUrl}/${thirdLayerKey}`;
|
||||||
activeSecondLayer === secondLayerKey &&
|
|
||||||
activeThirdLayer === thirdLayerKey;
|
|
||||||
|
|
||||||
const url = `/${lang}${baseUrl}/${thirdLayerKey}`;
|
const url = `/${lang}${baseUrl}/${thirdLayerKey}`;
|
||||||
|
console.log('mergeUrl', mergeUrl);
|
||||||
return (
|
return <div key={`${thirdLayerKey}-item`} className="ml-2 my-1"><ThirdLayerDropdown isActive={isActive} innerText={getMenuTranslation(menuTranslationWLang, mergeUrl)} url={url} /></div>
|
||||||
<div key={`${thirdLayerKey}-item`} className="ml-2 my-1">
|
|
||||||
<ThirdLayerDropdown
|
|
||||||
isActive={isActive}
|
|
||||||
innerText={langGetKey(menuTranslationsFlatten, thirdLayerKey)}
|
|
||||||
url={url}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render second layer menu items
|
|
||||||
const renderSecondLayerItems = (firstLayerKey: string, secondLayerItems: SecondLayerItems) => {
|
const renderSecondLayerItems = (firstLayerKey: string, secondLayerItems: SecondLayerItems) => {
|
||||||
return Object.entries(secondLayerItems).map(([secondLayerKey, thirdLayerItems]) => {
|
return Object.entries(secondLayerItems).map(([secondLayerKey, thirdLayerItems]) => {
|
||||||
const isActive = activeFirstLayer === firstLayerKey && activeSecondLayer === secondLayerKey;
|
const isActive = activeFirstLayer === firstLayerKey && activeSecondLayer === secondLayerKey;
|
||||||
const isExpanded = expandedSecondLayer === secondLayerKey;
|
const isExpanded = expandedSecondLayer === secondLayerKey;
|
||||||
|
const mergeUrl = `/${firstLayerKey}/${secondLayerKey}`;
|
||||||
|
console.log('mergeUrl', mergeUrl);
|
||||||
return (
|
return (
|
||||||
<div key={`${secondLayerKey}-item`} className="ml-2 my-1">
|
<div key={`${secondLayerKey}-item`} className="ml-2 my-1">
|
||||||
<SecondLayerDropdown
|
<SecondLayerDropdown isActive={isActive} isExpanded={isExpanded} innerText={getMenuTranslation(menuTranslationWLang, mergeUrl)} onClick={() => handleSecondLayerClick(secondLayerKey)} />
|
||||||
isActive={isActive}
|
{isExpanded && (<div className="ml-2 mt-1">{renderThirdLayerItems(firstLayerKey, secondLayerKey, thirdLayerItems)}</div>)}
|
||||||
isExpanded={isExpanded}
|
|
||||||
innerText={langGetKey(menuTranslationsFlatten, secondLayerKey)}
|
|
||||||
onClick={() => handleSecondLayerClick(secondLayerKey)}
|
|
||||||
/>
|
|
||||||
{isExpanded && (
|
|
||||||
<div className="ml-2 mt-1">
|
|
||||||
{renderThirdLayerItems(firstLayerKey, secondLayerKey, thirdLayerItems)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render first layer menu items
|
|
||||||
const renderFirstLayerItems = () => {
|
const renderFirstLayerItems = () => {
|
||||||
return Object.entries(menuStructure).map(([firstLayerKey, secondLayerItems]) => {
|
return Object.entries(menuStructure).map(([firstLayerKey, secondLayerItems]) => {
|
||||||
const isActive = activeFirstLayer === firstLayerKey;
|
const isActive = activeFirstLayer === firstLayerKey;
|
||||||
const isExpanded = expandedFirstLayer === firstLayerKey;
|
const isExpanded = expandedFirstLayer === firstLayerKey;
|
||||||
|
const mergeUrl = `/${firstLayerKey}`;
|
||||||
|
console.log('mergeUrl', mergeUrl);
|
||||||
return (
|
return (
|
||||||
<div key={`${firstLayerKey}-item`} className="mb-2">
|
<div key={`${firstLayerKey}-item`} className="mb-2">
|
||||||
<FirstLayerDropdown
|
<FirstLayerDropdown isActive={isActive} isExpanded={isExpanded} innerText={getMenuTranslation(menuTranslationWLang, mergeUrl)} onClick={() => handleFirstLayerClick(firstLayerKey)} />
|
||||||
isActive={isActive}
|
{isExpanded && (<div className="mt-1">{renderSecondLayerItems(firstLayerKey, secondLayerItems)}</div>)}
|
||||||
isExpanded={isExpanded}
|
|
||||||
innerText={langGetKey(menuTranslationsFlatten, firstLayerKey)}
|
|
||||||
onClick={() => handleFirstLayerClick(firstLayerKey)}
|
|
||||||
/>
|
|
||||||
{isExpanded && (
|
|
||||||
<div className="mt-1">
|
|
||||||
{renderSecondLayerItems(firstLayerKey, secondLayerItems)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</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';
|
'use client';
|
||||||
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent } from "@/components/mutual/shadcnui/dropdown-menu";
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
import { DropdownMenu, DropdownMenuTrigger } from "@/components/mutual/shadcnui/dropdown-menu";
|
||||||
import { Button } from "@/components/mutual/shadcnui/button";
|
import { Button } from "@/components/mutual/shadcnui/button";
|
||||||
import { languageSelectionTranslation } from "@/languages/mutual/languageSelection";
|
import { languageSelectionTranslation } from "@/languages/mutual/languageSelection";
|
||||||
import { langGetKey, langGet } from "@/lib/langGet";
|
import { langGetKey, langGet } from "@/lib/langGet";
|
||||||
import { LanguageTypes } from "@/validations/mutual/language/validations";
|
import { LanguageTypes } from "@/validations/mutual/language/validations";
|
||||||
|
import { checkContextPageOnline, setContextPageOnline } from "@/components/mutual/context/online/context";
|
||||||
import LanguageSelectionItem from "./languageItem";
|
import LanguageSelectionItem from "./languageItem";
|
||||||
|
|
||||||
const LanguageSelectionComponent: React.FC<{ lang: LanguageTypes, activePage: string, prefix: string }> = ({ lang, activePage, prefix }) => {
|
const LanguageSelectionComponent: React.FC<{ lang: LanguageTypes, activePage: string, prefix: string }> = ({ lang, activePage, prefix }) => {
|
||||||
const translations = langGet(lang, languageSelectionTranslation);
|
const translations = langGet(lang, languageSelectionTranslation);
|
||||||
const getPageWithLocale = (locale: LanguageTypes): string => { return `${prefix}/${locale}/${activePage}` }
|
const getPageWithLocale = (locale: LanguageTypes): string => { return `${prefix}/${activePage}` }
|
||||||
|
const [online, setOnline] = useState<any>({});
|
||||||
const englishButtonProps = {
|
useEffect(() => { const online = checkContextPageOnline(); setOnline({ ...online, lang: lang }) }, []);
|
||||||
activeLang: lang,
|
const englishButtonProps = { activeLang: lang, buttonsLang: "en", refUrl: getPageWithLocale("en"), innerText: langGetKey(translations, "english") }
|
||||||
buttonsLang: "en",
|
const turkishButtonProps = { activeLang: lang, buttonsLang: "tr", refUrl: getPageWithLocale("tr"), innerText: langGetKey(translations, "turkish") }
|
||||||
refUrl: getPageWithLocale("en"),
|
|
||||||
innerText: langGetKey(translations, "english")
|
|
||||||
}
|
|
||||||
const turkishButtonProps = {
|
|
||||||
activeLang: lang,
|
|
||||||
buttonsLang: "tr",
|
|
||||||
refUrl: getPageWithLocale("tr"),
|
|
||||||
innerText: langGetKey(translations, "turkish")
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-end justify-end">
|
<div className="flex items-end justify-end">
|
||||||
|
<div>{JSON.stringify(online)}</div>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button className="w-48 h-12 text-center text-md">{langGetKey(translations, "title")}</Button>
|
<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 { DropdownMenuContent, DropdownMenuLabel } from "@/components/mutual/shadcnui/dropdown-menu";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import LoadingContent from "@/components/mutual/loader/component";
|
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 (
|
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">
|
<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>
|
<DropdownMenuLabel className="flex items-center justify-center">{innerText}</DropdownMenuLabel>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|
@ -29,7 +40,7 @@ const LanguageSelectionItem: React.FC<{
|
||||||
}> = ({ activeLang, buttonsLang, refUrl, innerText }) => {
|
}> = ({ activeLang, buttonsLang, refUrl, innerText }) => {
|
||||||
const [isL, setisL] = useState<boolean>(false);
|
const [isL, setisL] = useState<boolean>(false);
|
||||||
const isC = buttonsLang !== activeLang
|
const isC = buttonsLang !== activeLang
|
||||||
const RenderLinkProp = { refUrl, innerText, setisL }
|
const RenderLinkProp = { refUrl, innerText, setisL, buttonsLang }
|
||||||
return (
|
return (
|
||||||
<>{isC && <>{isL ? <RenderLoadingComponent setisL={setisL} /> : <RenderLinkComponent {...RenderLinkProp} />}</>}</>
|
<>{isC && <>{isL ? <RenderLoadingComponent setisL={setisL} /> : <RenderLinkComponent {...RenderLinkProp} />}</>}</>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,24 @@
|
||||||
import { LanguageTypes } from "@/validations/mutual/language/validations";
|
import { LanguageTypes } from "@/validations/mutual/language/validations";
|
||||||
import { DynamicPage } from "@/validations/mutual/menu/menu";
|
import { DynamicPage } from "@/validations/mutual/menu/menu";
|
||||||
import { managementAccountTenantMain } from "./management/account/tenantSomething/index";
|
import { managementAccountTenantMain } from "./management/account/tenantSomething/index";
|
||||||
import { managementAccountTenantMainSecond } from "./management/account/tenantSomethingSecond/index";
|
// import { managementAccountTenantMainSecond } from "./management/account/tenantSomethingSecond/index";
|
||||||
import { buildingPartsTenantSomething } from "./building/parts/tenantSomething/index";
|
// import { buildingPartsTenantSomething } from "./building/parts/tenantSomething/index";
|
||||||
|
|
||||||
const dynamicPagesIndex: Record<string, Record<LanguageTypes, DynamicPage>> = {
|
const dynamicPagesIndex: Record<string, Record<LanguageTypes, DynamicPage>> = {
|
||||||
"main/pages/user/dashboard": managementAccountTenantMain,
|
"/main/pages/user/dashboard": managementAccountTenantMain,
|
||||||
"management/account/tenant/something": managementAccountTenantMain,
|
"/definitions/identifications/people": managementAccountTenantMain,
|
||||||
"management/account/tenant/somethingSecond": managementAccountTenantMainSecond,
|
"/definitions/identifications/users": managementAccountTenantMain,
|
||||||
"building/parts/tenant/something": buildingPartsTenantSomething,
|
"/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 };
|
export { dynamicPagesIndex };
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,27 @@
|
||||||
const menuTranslationEn = {
|
const menuTranslationEn = {
|
||||||
"/management/account/something/something/something": "Account Third Layer",
|
"/definitions": "Definitions",
|
||||||
"/management/account": "Account Second Layer",
|
"/definitions/identifications": "Identifications",
|
||||||
"/management": "Management First Layer",
|
"/definitions/identifications/people": "People",
|
||||||
};
|
"/definitions/identifications/users": "Users",
|
||||||
const menuIndex = [
|
|
||||||
"/management/account/something/something/something",
|
|
||||||
"/building/parts/something/something/something",
|
|
||||||
];
|
|
||||||
|
|
||||||
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 };
|
export { menuTranslationTr };
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
'use server';
|
'use server';
|
||||||
import { FC } from "react";
|
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 { DashboardLayoutProps, ModeTypes } from "@/validations/mutual/dashboard/props";
|
||||||
|
|
||||||
import HeaderComponent from "@/components/custom/header/component";
|
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";
|
import FooterComponent from "@/components/custom/footer/component";
|
||||||
|
|
||||||
const DashboardLayout: FC<DashboardLayoutProps> = async ({ params, searchParams, lang }) => {
|
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 mode = (searchParams?.mode as ModeTypes) || 'shortList';
|
||||||
|
const activePageUrl = `/${params.page?.join('/')}`;
|
||||||
const menuItems = await dynamicPageMenuWithLayersGet(lang);
|
const allProps = { lang, activePageUrl, mode, prefix: "/panel" }
|
||||||
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 }
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-w-screen">
|
<div className="flex flex-col min-w-screen">
|
||||||
<HeaderComponent {...headerProps} />
|
<HeaderComponent {...allProps} />
|
||||||
<MenuComponent {...menuProps} />
|
<MenuComponent {...allProps} />
|
||||||
<ContentComponent {...contentProps} />
|
<ContentComponent {...allProps} />
|
||||||
<FooterComponent translations={translations.footer} />
|
<FooterComponent {...allProps} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
import { ContentProps } from "@/validations/mutual/dashboard/props";
|
import { ContentProps } from "@/validations/mutual/dashboard/props";
|
||||||
import superUserTenantSomething from "./management/account/tenantSomething/page";
|
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>>> = {
|
const pageIndexMulti: Record<string, Record<string, React.FC<ContentProps>>> = {
|
||||||
"main/pages/user/dashboard": {
|
"/main/pages/user/dashboard": { superUserTenantSomething },
|
||||||
superUserTenantSomething: superUserTenantSomething,
|
"/definitions/identifications/people": { superUserTenantSomething },
|
||||||
},
|
"/definitions/identifications/users": { superUserTenantSomething },
|
||||||
"management/account/tenant/something": {
|
"/definitions/building/parts": { superUserTenantSomething },
|
||||||
superUserTenantSomething: superUserTenantSomething,
|
"/definitions/building/areas": { superUserTenantSomething },
|
||||||
},
|
"/building/accounts/managment/accounts": { superUserTenantSomething },
|
||||||
"management/account/tenant/somethingSecond": {
|
"/building/accounts/managment/budgets": { superUserTenantSomething },
|
||||||
superUserTenantSomething: superUserTenantSomethingSecond,
|
"/building/accounts/parts/accounts": { superUserTenantSomething },
|
||||||
},
|
"/building/accounts/parts/budgets": { superUserTenantSomething },
|
||||||
"building/parts/tenant/something": {
|
"/building/meetings/regular/actions": { superUserTenantSomething },
|
||||||
superUserTenantSomething: superUserBuildingPartsTenantSomething,
|
"/building/meetings/regular/accounts": { superUserTenantSomething },
|
||||||
},
|
"/building/meetings/ergunt/actions": { superUserTenantSomething },
|
||||||
|
"/building/meetings/ergunt/accounts": { superUserTenantSomething },
|
||||||
|
"/building/meetings/invited/attendance": { superUserTenantSomething },
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pageIndexMulti;
|
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 {
|
interface ContentProps {
|
||||||
lang: LanguageTypes;
|
lang: LanguageTypes;
|
||||||
translations: Record<string, string>;
|
|
||||||
activePageUrl: string;
|
activePageUrl: string;
|
||||||
isMulti?: boolean;
|
|
||||||
mode?: ModeTypes;
|
mode?: ModeTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MenuProps {
|
interface MenuProps {
|
||||||
lang: LanguageTypes;
|
lang: LanguageTypes;
|
||||||
menuItems: Record<string, Record<string, any>>;
|
availableApplications: string[];
|
||||||
activePageUrl: string;
|
activePageUrl: string;
|
||||||
menuTranslationsFlatten: Record<string, Record<string, any>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FooterProps {
|
interface FooterProps {}
|
||||||
translations: Record<string, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface HeaderProps {
|
interface AllProps {
|
||||||
translations: Record<string, string>;
|
|
||||||
lang: LanguageTypes;
|
lang: LanguageTypes;
|
||||||
activePageUrl: string;
|
activePageUrl: string;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
|
mode?: ModeTypes;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
|
|
@ -47,7 +43,7 @@ export type {
|
||||||
ContentProps,
|
ContentProps,
|
||||||
MenuProps,
|
MenuProps,
|
||||||
FooterProps,
|
FooterProps,
|
||||||
HeaderProps,
|
AllProps,
|
||||||
ModeTypes,
|
ModeTypes,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,12 @@ export function loginHook(
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
response.json().then((data) => {
|
response.json().then((data) => {
|
||||||
console.log("data", data); // setJsonText(JSON.stringify(data));
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const userType =
|
const userType =
|
||||||
data?.data?.user_type.toLowerCase() === "employee"
|
data?.data?.user_type.toLowerCase() === "employee"
|
||||||
? "employee"
|
? "employee"
|
||||||
: "occupant";
|
: "occupant";
|
||||||
const rediretUrl = `/auth/${lang}/select?type=${userType}`;
|
const rediretUrl = `/auth/select?type=${userType}`;
|
||||||
console.log("rediretUrl", rediretUrl);
|
|
||||||
Router.push(rediretUrl);
|
Router.push(rediretUrl);
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,57 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React, { useTransition, useState } from "react";
|
import React, { useTransition, useState, useEffect } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Company } from "./types";
|
import { Company } from "./types";
|
||||||
import { LoginEmployeeProps } from "./types";
|
import { LoginEmployeeProps } from "./types";
|
||||||
import { selectEmployeeHook } from "./hook";
|
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({
|
function LoginEmployee({
|
||||||
selectionList,
|
|
||||||
translation,
|
|
||||||
lang
|
lang
|
||||||
}: LoginEmployeeProps) {
|
}: LoginEmployeeProps) {
|
||||||
const isArrayLengthOne = Array.isArray(selectionList) && selectionList.length === 1;
|
const [selectionListCopy, setSelectionListCopy] = useState<ClientSelection>({ selectionList: [], activeSelection: {} });
|
||||||
const isArrayLengthZero = Array.isArray(selectionList) && selectionList.length === 0;
|
useEffect(() => {
|
||||||
const isArrayMoreThanOne = Array.isArray(selectionList) && selectionList.length > 1;
|
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 Router = useRouter();
|
||||||
|
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [jsonText, setJsonText] = useState<string | null>(null);
|
const [jsonText, setJsonText] = useState<string | null>(null);
|
||||||
|
|
||||||
const onSubmitEmployee = async (uu_id: string) => {
|
const onSubmitEmployee = async (uu_id: string) => { selectEmployeeHook(startTransition, { uuid: uu_id }, setError, setJsonText, Router, lang) };
|
||||||
selectEmployeeHook(startTransition, { company_uu_id: uu_id }, setError, setJsonText, Router, lang)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render a company card with consistent styling
|
// 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="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)}>
|
<div className="flex flex-col" onClick={() => onSubmitEmployee(company.uu_id)}>
|
||||||
{/* Company name and type */}
|
{/* Company name and type */}
|
||||||
|
|
@ -40,13 +67,13 @@ function LoginEmployee({
|
||||||
{/* Duty information */}
|
{/* Duty information */}
|
||||||
{company.duty && (
|
{company.duty && (
|
||||||
<div className="mb-2 text-sm text-gray-600">
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ID information */}
|
{/* ID information */}
|
||||||
<div className="text-xs text-gray-500 mb-3">
|
<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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -55,30 +82,26 @@ function LoginEmployee({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-w-md mx-auto">
|
<div className="w-full max-w-md mx-auto">
|
||||||
<h1 className="text-2xl font-bold text-gray-900 mb-2">{translation.companySelection}</h1>
|
<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.loggedInAs}</p>
|
<p className="text-sm text-gray-500 mb-6">{translation[lang].loggedInAs}</p>
|
||||||
|
|
||||||
{/* No companies available */}
|
{/* No companies available */}
|
||||||
{isArrayLengthZero && (
|
{isArrayLengthZero && (
|
||||||
<div className="text-center p-6 bg-gray-50 rounded-lg border border-gray-200">
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Single company */}
|
{/* Single company */}
|
||||||
{isArrayLengthOne && <CompanyCard company={selectionList[0]} showButton={true} />}
|
{isArrayLengthOne && <CompanyCard company={selectionListCopy.activeSelection as Company} />}
|
||||||
|
|
||||||
{/* Multiple companies */}
|
{/* Multiple companies */}
|
||||||
{isArrayMoreThanOne && (
|
{isArrayMoreThanOne && (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{selectionList.map((company, index) => (
|
{selectionListCopy.selectionList.map((company, index) => (
|
||||||
<div
|
<div key={company.uu_id || index} onClick={() => onSubmitEmployee(company.uu_id)}
|
||||||
key={company.uu_id || index}
|
|
||||||
onClick={() => onSubmitEmployee(company.uu_id)}
|
|
||||||
className="cursor-pointer hover:translate-x-1 transition-transform"
|
className="cursor-pointer hover:translate-x-1 transition-transform"
|
||||||
>
|
><CompanyCard company={company as Company} /></div>
|
||||||
<CompanyCard company={company} />
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,38 @@ import React, { useState, useTransition, useEffect } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { LoginOccupantProps } from "./types";
|
import { LoginOccupantProps } from "./types";
|
||||||
import { selectOccupantHook } from "./hook";
|
import { selectOccupantHook } from "./hook";
|
||||||
|
import { checkContextDashSelection } from "@/components/mutual/context/selection/context";
|
||||||
|
import { ClientSelection } from "@/types/mutual/context/validations";
|
||||||
|
|
||||||
function LoginOccupant({
|
const translation = {
|
||||||
selectionList,
|
en: {
|
||||||
translation,
|
occupantSelection: "Occupant Selection",
|
||||||
lang
|
loggedInAs: "Logged in as",
|
||||||
}: LoginOccupantProps) {
|
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 Router = useRouter();
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [jsonText, setJsonText] = useState<string | null>(null);
|
const [jsonText, setJsonText] = useState<string | null>(null);
|
||||||
|
const [selectionList, setSelectionList] = useState<ClientSelection>({ selectionList: [], activeSelection: {} });
|
||||||
|
|
||||||
const onSubmitOccupant = async (data: any) => {
|
useEffect(() => {
|
||||||
selectOccupantHook(startTransition, data, setError, setJsonText, Router, lang)
|
checkContextDashSelection().then((selectionList) => {
|
||||||
};
|
if (!selectionList) throw new Error("No selection list found"); setSelectionList(selectionList);
|
||||||
const isArrayLengthZero = Array.isArray(selectionList) && selectionList.length === 0;
|
})
|
||||||
|
}, [])
|
||||||
// Render an occupant card with consistent styling
|
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 }) => (
|
const OccupantCard = ({ occupant, buildKey, idx }: { occupant: any, buildKey: string, idx: number }) => (
|
||||||
<div
|
<div
|
||||||
key={`${buildKey}-${idx}`}
|
key={`${buildKey}-${idx}`}
|
||||||
|
|
@ -28,28 +42,21 @@ function LoginOccupant({
|
||||||
onClick={() => onSubmitOccupant({ build_living_space_uu_id: occupant.build_living_space_uu_id })}
|
onClick={() => onSubmitOccupant({ build_living_space_uu_id: occupant.build_living_space_uu_id })}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{/* Occupant description and code */}
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<h3 className="text-lg font-semibold text-gray-800">{occupant.description}</h3>
|
<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">
|
<span className="px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 rounded-full">
|
||||||
{occupant.code}
|
{occupant.code}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Part name */}
|
|
||||||
<div className="mb-1 text-sm text-gray-700 font-medium">
|
<div className="mb-1 text-sm text-gray-700 font-medium">
|
||||||
{occupant.part_name}
|
{occupant.part_name}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Level information */}
|
|
||||||
<div className="text-xs text-gray-500">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Render a building section with its occupants
|
|
||||||
const BuildingSection = ({ building, buildKey }: { building: any, buildKey: string }) => (
|
const BuildingSection = ({ building, buildKey }: { building: any, buildKey: string }) => (
|
||||||
<div key={buildKey} className="mb-6">
|
<div key={buildKey} className="mb-6">
|
||||||
<div className="p-3 bg-gray-50 border border-gray-200 rounded-lg mb-3">
|
<div className="p-3 bg-gray-50 border border-gray-200 rounded-lg mb-3">
|
||||||
|
|
@ -72,31 +79,25 @@ function LoginOccupant({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-w-md mx-auto">
|
<div className="w-full max-w-md mx-auto">
|
||||||
<h1 className="text-2xl font-bold text-gray-900 mb-2">{translation.occupantSelection}</h1>
|
<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.loggedInAs}</p>
|
<p className="text-sm text-gray-500 mb-6">{translation[lang].loggedInAs}</p>
|
||||||
|
|
||||||
{/* No occupants available */}
|
|
||||||
{!isArrayLengthZero ? (
|
{!isArrayLengthZero ? (
|
||||||
<div className="text-center p-6 bg-gray-50 rounded-lg border border-gray-200">
|
<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>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
/* Building sections with occupants */
|
|
||||||
<div>
|
<div>
|
||||||
{Object.keys(selectionList).map((buildKey: string) => (
|
{Object.keys(selectionList.selectionList).map((buildKey: string) => (
|
||||||
<BuildingSection
|
<BuildingSection
|
||||||
key={buildKey}
|
key={buildKey}
|
||||||
building={selectionList[buildKey]}
|
building={selectionList.activeSelection}
|
||||||
buildKey={buildKey}
|
buildKey={buildKey}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Show error if any */}
|
|
||||||
{error && <p className="mt-4 text-sm text-red-600">{error}</p>}
|
{error && <p className="mt-4 text-sm text-red-600">{error}</p>}
|
||||||
|
|
||||||
{/* Loading indicator */}
|
|
||||||
{isPending && (
|
{isPending && (
|
||||||
<div className="mt-4 flex justify-center">
|
<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>
|
<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";
|
import { LanguageTypes } from "@/validations/mutual/language/validations";
|
||||||
|
|
||||||
const afterLoginDirectUrl = (lang: LanguageTypes) => {
|
const afterLoginDirectUrl = () => {
|
||||||
return `/panel/${lang}/main/pages/user/dashboard`;
|
return `/panel/dashboard`;
|
||||||
};
|
};
|
||||||
|
|
||||||
function selectEmployeeHook(
|
function selectEmployeeHook(
|
||||||
|
|
@ -25,7 +25,7 @@ function selectEmployeeHook(
|
||||||
response.json().then((data) => {
|
response.json().then((data) => {
|
||||||
console.log("data", data);
|
console.log("data", data);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
Router.push(afterLoginDirectUrl(lang));
|
Router.push(afterLoginDirectUrl());
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -62,7 +62,7 @@ function selectOccupantHook(
|
||||||
response.json().then((data) => {
|
response.json().then((data) => {
|
||||||
console.log("data", data);
|
console.log("data", data);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
Router.push(afterLoginDirectUrl(lang));
|
Router.push(afterLoginDirectUrl());
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,18 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import LoginOccupant from "./LoginOccupant";
|
import LoginOccupant from "./LoginOccupant";
|
||||||
import LoginEmployee from "./LoginEmployee";
|
import LoginEmployee from "./LoginEmployee";
|
||||||
|
|
||||||
import { Company, SelectListProps, BuildingMap } from "./types";
|
import { SelectListProps } 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]);
|
|
||||||
|
|
||||||
|
const Select: React.FC<SelectListProps> = ({ language, query }) => {
|
||||||
|
const isEmployeee = query?.type == "employee";
|
||||||
|
const isOccupante = query?.type == "occupant";
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex h-full min-h-[inherit] flex-col items-center justify-center gap-4">
|
<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">
|
<div className="w-full max-w-md rounded-lg bg-white p-8 shadow-md">
|
||||||
{isEmployeeTrue && <LoginEmployee translation={translation} selectionList={listEmployeeSelection} lang={language} />}
|
{isEmployeee && <LoginEmployee lang={language} />}
|
||||||
{isOccupantTrue && <LoginOccupant translation={translation} selectionList={listOccupantSelection} lang={language} />}
|
{isOccupante && <LoginOccupant lang={language} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,15 @@ import React, { FC } from "react";
|
||||||
import Select from "./page";
|
import Select from "./page";
|
||||||
|
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { checkAccessTokenIsValid, retrieveUserType } from "@/apicalls/mutual/cookies/token";
|
|
||||||
import { AuthPageProps } from "@/validations/mutual/auth/props";
|
import { AuthPageProps } from "@/validations/mutual/auth/props";
|
||||||
|
|
||||||
const SelectPage: FC<AuthPageProps> = async ({ query, language }) => {
|
const SelectPage: FC<AuthPageProps> = async ({ query, language }) => {
|
||||||
const token_is_valid = await checkAccessTokenIsValid();
|
try {
|
||||||
const selection = await retrieveUserType();
|
return <Select language={language} query={query} />;
|
||||||
const isEmployee = selection?.userType == "employee";
|
} catch (error) {
|
||||||
const isOccupant = selection?.userType == "occupant";
|
redirect("/auth/login");
|
||||||
const selectionList = selection?.selectionList;
|
return null;
|
||||||
|
}
|
||||||
if (!selectionList || !token_is_valid) { redirect("/auth/en/login") }
|
|
||||||
return <Select selectionList={selectionList} isEmployee={isEmployee} isOccupant={isOccupant} language={language} query={query} />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectPage;
|
export default SelectPage;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { ClientSelection } from "@/types/mutual/context/validations";
|
||||||
import { LanguageTypes } from "@/validations/mutual/language/validations";
|
import { LanguageTypes } from "@/validations/mutual/language/validations";
|
||||||
|
|
||||||
interface Company {
|
interface Company {
|
||||||
|
|
@ -30,22 +31,15 @@ interface BuildingMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SelectListProps {
|
interface SelectListProps {
|
||||||
selectionList: Company[] | BuildingMap;
|
|
||||||
isEmployee: boolean;
|
|
||||||
isOccupant: boolean;
|
|
||||||
language: LanguageTypes;
|
language: LanguageTypes;
|
||||||
query?: { [key: string]: string | string[] | undefined };
|
query?: { [key: string]: string | string[] | undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoginOccupantProps {
|
interface LoginOccupantProps {
|
||||||
selectionList: BuildingMap;
|
|
||||||
translation: any;
|
|
||||||
lang: LanguageTypes;
|
lang: LanguageTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoginEmployeeProps {
|
interface LoginEmployeeProps {
|
||||||
selectionList: Company[];
|
|
||||||
translation: any;
|
|
||||||
lang: LanguageTypes;
|
lang: LanguageTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,14 +97,8 @@ async function coreFetch<T>(
|
||||||
|
|
||||||
const responseJson = await response.json();
|
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);
|
return prepareResponse(responseJson, response.status);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Fetch error (${url}):`, error);
|
|
||||||
return {
|
return {
|
||||||
...DEFAULT_RESPONSE,
|
...DEFAULT_RESPONSE,
|
||||||
error: error instanceof Error ? error.message : "Network error",
|
error: error instanceof Error ? error.message : "Network error",
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,6 @@ async function listApplicationsAvailable(payload: PaginationParams): Promise<Pag
|
||||||
query: payload.query,
|
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(
|
const response = await fetchDataWithToken(
|
||||||
applicationListAvailableEndpoint,
|
applicationListAvailableEndpoint,
|
||||||
requestBody,
|
requestBody,
|
||||||
|
|
@ -56,7 +53,6 @@ async function listApplicationsAvailable(payload: PaginationParams): Promise<Pag
|
||||||
|
|
||||||
if (response?.status === 200 || response?.status === 202) {
|
if (response?.status === 200 || response?.status === 202) {
|
||||||
const responseData = response.data as PaginatedApiResponse<any>;
|
const responseData = response.data as PaginatedApiResponse<any>;
|
||||||
console.log('list_events_available responseData:', JSON.stringify(responseData, null, 2));
|
|
||||||
return {
|
return {
|
||||||
data: responseData.data || [],
|
data: responseData.data || [],
|
||||||
pagination: collectPaginationFromApiResponse(responseData)
|
pagination: collectPaginationFromApiResponse(responseData)
|
||||||
|
|
@ -93,9 +89,6 @@ async function listApplicationsAppended(payload: PaginationParams): Promise<Pagi
|
||||||
query: payload.query,
|
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(
|
const response = await fetchDataWithToken(
|
||||||
applicationListAppendedEndpoint,
|
applicationListAppendedEndpoint,
|
||||||
requestBody,
|
requestBody,
|
||||||
|
|
@ -105,7 +98,6 @@ async function listApplicationsAppended(payload: PaginationParams): Promise<Pagi
|
||||||
|
|
||||||
if (response?.status === 200 || response?.status === 202) {
|
if (response?.status === 200 || response?.status === 202) {
|
||||||
const responseData = response.data as PaginatedApiResponse<any>;
|
const responseData = response.data as PaginatedApiResponse<any>;
|
||||||
console.log('list_events_available responseData:', JSON.stringify(responseData, null, 2));
|
|
||||||
return {
|
return {
|
||||||
data: responseData.data || [],
|
data: responseData.data || [],
|
||||||
pagination: collectPaginationFromApiResponse(responseData)
|
pagination: collectPaginationFromApiResponse(responseData)
|
||||||
|
|
@ -116,7 +108,6 @@ async function listApplicationsAppended(payload: PaginationParams): Promise<Pagi
|
||||||
pagination: defaultPaginationResponse,
|
pagination: defaultPaginationResponse,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching events list:", error);
|
|
||||||
return {
|
return {
|
||||||
data: [],
|
data: [],
|
||||||
pagination: defaultPaginationResponse,
|
pagination: defaultPaginationResponse,
|
||||||
|
|
@ -134,8 +125,6 @@ async function listAllApplications(payload: PaginationParams): Promise<Paginated
|
||||||
query: payload.query,
|
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(
|
const response = await fetchDataWithToken(
|
||||||
applicationListEndpoint,
|
applicationListEndpoint,
|
||||||
|
|
@ -156,7 +145,6 @@ async function listAllApplications(payload: PaginationParams): Promise<Paginated
|
||||||
pagination: defaultPaginationResponse,
|
pagination: defaultPaginationResponse,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching events list:", error);
|
|
||||||
return {
|
return {
|
||||||
data: [],
|
data: [],
|
||||||
pagination: defaultPaginationResponse,
|
pagination: defaultPaginationResponse,
|
||||||
|
|
@ -174,7 +162,7 @@ async function appendApplicationToService(payload: AppendApplicationToService) {
|
||||||
);
|
);
|
||||||
return response?.status === 200 || response?.status === 202 ? response.data : null;
|
return response?.status === 200 || response?.status === 202 ? response.data : null;
|
||||||
} catch (error) {
|
} 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;
|
return response?.status === 200 || response?.status === 202 ? response.data : null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error removing event from service:", error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createApplication(payload: any) {
|
async function createApplication(payload: any) {
|
||||||
console.log("Creating application with payload:", payload);
|
|
||||||
try {
|
try {
|
||||||
const response = await fetchDataWithToken(
|
const response = await fetchDataWithToken(
|
||||||
applicationCreateEndpoint,
|
applicationCreateEndpoint,
|
||||||
|
|
@ -211,7 +197,6 @@ async function createApplication(payload: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateApplication(payload: any, uuId: string) {
|
async function updateApplication(payload: any, uuId: string) {
|
||||||
console.log("Updating application with payload:", payload, 'uuId:', uuId);
|
|
||||||
try {
|
try {
|
||||||
const response = await fetchDataWithToken(
|
const response = await fetchDataWithToken(
|
||||||
`${applicationUpdateEndpoint}/${uuId}`,
|
`${applicationUpdateEndpoint}/${uuId}`,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { fetchDataWithToken } from "../api-fetcher";
|
import { fetchDataWithToken } from "@/apicalls/api-fetcher";
|
||||||
import { baseUrlApplication } from "../basics";
|
import { baseUrlApplication } from "@/apicalls/basics";
|
||||||
import { PaginationParams } from "../schemas/list";
|
import { PaginationParams } from "@/apicalls/schemas/list";
|
||||||
import { PaginatedApiResponse, collectPaginationFromApiResponse, defaultPaginationResponse } from "@/app/api/utils/types";
|
import { PaginatedApiResponse, collectPaginationFromApiResponse, defaultPaginationResponse } from "@/app/api/utils/types";
|
||||||
|
|
||||||
const eventsListAvailableEndpoint = `${baseUrlApplication}/events/list/available`;
|
const eventsListAvailableEndpoint = `${baseUrlApplication}/events/list/available`;
|
||||||
|
|
@ -36,9 +36,6 @@ async function list_events_available(payload: PaginationParams): Promise<Paginat
|
||||||
query: payload.query,
|
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(
|
const response = await fetchDataWithToken(
|
||||||
eventsListAvailableEndpoint,
|
eventsListAvailableEndpoint,
|
||||||
requestBody,
|
requestBody,
|
||||||
|
|
@ -48,7 +45,6 @@ async function list_events_available(payload: PaginationParams): Promise<Paginat
|
||||||
|
|
||||||
if (response?.status === 200 || response?.status === 202) {
|
if (response?.status === 200 || response?.status === 202) {
|
||||||
const responseData = response.data as PaginatedApiResponse<any>;
|
const responseData = response.data as PaginatedApiResponse<any>;
|
||||||
console.log('list_events_available responseData:', JSON.stringify(responseData, null, 2));
|
|
||||||
return {
|
return {
|
||||||
data: responseData.data || [],
|
data: responseData.data || [],
|
||||||
pagination: collectPaginationFromApiResponse(responseData)
|
pagination: collectPaginationFromApiResponse(responseData)
|
||||||
|
|
@ -59,7 +55,6 @@ async function list_events_available(payload: PaginationParams): Promise<Paginat
|
||||||
pagination: defaultPaginationResponse,
|
pagination: defaultPaginationResponse,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching events list:", error);
|
|
||||||
return {
|
return {
|
||||||
data: [],
|
data: [],
|
||||||
pagination: defaultPaginationResponse,
|
pagination: defaultPaginationResponse,
|
||||||
|
|
|
||||||
|
|
@ -72,10 +72,8 @@ async function retrieveUserSelection() {
|
||||||
decrpytUserSelection = decrpytUserSelection
|
decrpytUserSelection = decrpytUserSelection
|
||||||
? JSON.parse(decrpytUserSelection)
|
? JSON.parse(decrpytUserSelection)
|
||||||
: null;
|
: null;
|
||||||
console.log("decrpytUserSelection", decrpytUserSelection);
|
|
||||||
const userSelection = decrpytUserSelection?.selected;
|
const userSelection = decrpytUserSelection?.selected;
|
||||||
const accessObjects = (await retrieveAccessObjects()) || {};
|
const accessObjects = (await retrieveAccessObjects()) || {};
|
||||||
console.log("accessObjects", accessObjects);
|
|
||||||
|
|
||||||
if (decrpytUserSelection?.user_type === "employee") {
|
if (decrpytUserSelection?.user_type === "employee") {
|
||||||
const companyList = accessObjects?.selectionList;
|
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