updated app reachable codes

This commit is contained in:
berkay 2025-04-09 11:51:28 +03:00
parent 7eadadbd1d
commit 7c2150a8b0
23 changed files with 1040 additions and 187 deletions

View File

@ -342,7 +342,6 @@ def authentication_token_refresh_post(
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content={},
status_code=status.HTTP_202_ACCEPTED,
@ -374,7 +373,6 @@ def authentication_password_verify_otp(
"tz": tz or "GMT+3",
"token": token,
}
print("Token&OTP : ", data.otp, data.token)
if not domain or not language:
return JSONResponse(
content={"error": "EYS_0003"},
@ -419,8 +417,17 @@ def authentication_page_valid(
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
result = AuthHandlers.PageHandlers.retrieve_valid_page_via_token(
page_url=data.page_url, access_token=token
)
if not result:
return JSONResponse(
content={"error": "EYS_0004"},
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content={},
content={"application": result},
status_code=status.HTTP_202_ACCEPTED,
headers=headers,
)

View File

@ -29,14 +29,16 @@ from Schemas import (
)
from Modules.Token.password_module import PasswordModule
from Schemas.building.build import RelationshipEmployee2Build
from Schemas.event.event import Event2Occupant
from Schemas.event.event import Event2Occupant, Application2Employee
from Controllers.Redis.database import RedisActions
from Controllers.Mongo.database import mongo_handler
TokenDictType = Union[EmployeeTokenObject, OccupantTokenObject]
class RedisHandlers:
AUTH_TOKEN: str = "AUTH_TOKEN"
@classmethod
@ -79,9 +81,7 @@ class RedisHandlers:
return generated_access_token
@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]):
if already_token_data := RedisActions.get_json(
list_keys=[RedisHandlers.AUTH_TOKEN, token, "*"]
).first:
@ -124,9 +124,7 @@ class UserHandlers:
return found_user
@staticmethod
def check_password_valid(
domain: str, id_: str, password: str, password_hashed: str
) -> bool:
def check_password_valid(domain: str, id_: str, password: str, password_hashed: str) -> bool:
"""
Check if the password is valid.
"""
@ -144,6 +142,10 @@ class UserHandlers:
return True
raise ValueError("EYS_0004")
@staticmethod
def update_password():
return
class LoginHandler:
@ -156,9 +158,7 @@ class LoginHandler:
return str(email).split("@")[1] == api_config.ACCESS_EMAIL_EXT
@classmethod
def do_employee_login(
cls, request: Any, data: Any, extra_dict: Optional[Dict[str, Any]] = None
):
def do_employee_login(cls, request: Any, data: Any, extra_dict: Optional[Dict[str, Any]] = None):
"""
Handle employee login.
"""
@ -268,9 +268,7 @@ class LoginHandler:
raise ValueError("Something went wrong")
@classmethod
def do_employee_occupant(
cls, request: Any, data: Any, extra_dict: Optional[Dict[str, Any]] = None
):
def do_employee_occupant(cls, request: Any, data: Any, extra_dict: Optional[Dict[str, Any]] = None):
"""
Handle occupant login.
"""
@ -421,9 +419,7 @@ class LoginHandler:
return request.headers.get(api_config.ACCESS_TOKEN_TAG)
@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:
if data.company_uu_id not in token_dict.companies_uu_id_list:
ValueError("EYS_0011")
@ -479,6 +475,10 @@ class LoginHandler:
db=db,
).data
reachable_app_codes = Application2Employee.get_application_codes(
employee_id=employee.id, db=db
)
# Create company token
company_token = CompanyToken(
company_uu_id=selected_company.uu_id.__str__(),
@ -493,6 +493,7 @@ class LoginHandler:
employee_id=employee.id,
employee_uu_id=employee.uu_id.__str__(),
reachable_event_codes=reachable_event_codes,
reachable_app_codes=reachable_app_codes
)
redis_handler = RedisHandlers()
redis_result = redis_handler.update_token_at_redis(
@ -503,9 +504,7 @@ class LoginHandler:
}
@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):
"""Handle occupant type selection"""
with BuildLivingSpace.new_session() as db:
# Get selected occupant type
@ -707,6 +706,22 @@ class PasswordHandler:
return found_user
class PageHandlers:
@classmethod
def retrieve_valid_page_via_token(cls, access_token: str, page_url: str):
if result := RedisHandlers.get_object_from_redis(access_token=access_token):
if result.is_employee:
if application := result.selected_company.reachable_app_codes.get(page_url, None):
return application
elif result.is_occupant:
if application := result.selected_company.reachable_app_codes.get(page_url, None):
return application
raise ValueError("EYS_0013")
class AuthHandlers:
LoginHandler: LoginHandler = LoginHandler()
PasswordHandler: PasswordHandler = PasswordHandler()
PageHandlers: PageHandlers = PageHandlers()

View File

@ -59,8 +59,8 @@ class OccupantToken(BaseModel):
responsible_employee_id: Optional[int] = None
responsible_employee_uuid: Optional[str] = None
reachable_event_codes: Optional[list[str]] = None # ID list of reachable modules
reachable_app_codes: Optional[list[str]] = None # ID list of reachable modules
reachable_event_codes: Optional[dict[str, str]] = None # ID list of reachable modules
reachable_app_codes: Optional[dict[str, str]] = None # ID list of reachable modules
class CompanyToken(BaseModel):
@ -83,8 +83,8 @@ class CompanyToken(BaseModel):
bulk_duties_id: int
reachable_event_codes: Optional[list[str]] = None # ID list of reachable modules
reachable_app_codes: Optional[list[str]] = None # ID list of reachable modules
reachable_event_codes: Optional[dict[str, str]] = None # ID list of reachable modules
reachable_app_codes: Optional[dict[str, str]] = None # ID list of reachable modules
class OccupantTokenObject(ApplicationToken):

View File

@ -15,7 +15,7 @@ class RequestVerifyOTP(BaseModel):
class RequestApplication(BaseModel):
page: str # /building/create
page_url: str # /building/create
class RequestSelectEmployee(BaseModel):

View File

@ -0,0 +1,36 @@
from Schemas import (
Applications,
Application2Employee,
Application2Occupant,
)
"""
name=Building Create
site_url=/buildings/create
application_type=CreateFrom
application_code=APP003
name=Building Create
site_url=/buildings/create
application_type=CreateFrom
application_code=APP004
name=Building Create
site_url=/buildings/create
application_type=CreateFrom
application_code=APP005
name=Building Update
site_url=/buildings/update
application_type=/building/update
application_code=APP002
name=Building List
site_url=/buildings
application_type=ListFrom
application_code=APP001
"""

View File

@ -0,0 +1,28 @@
FROM python:3.12-slim
WORKDIR /
# Install system dependencies and Poetry
RUN apt-get update && apt-get install -y --no-install-recommends gcc \
&& rm -rf /var/lib/apt/lists/* && pip install --no-cache-dir poetry
# Copy Poetry configuration
COPY /pyproject.toml ./pyproject.toml
# Configure Poetry and install dependencies with optimizations
RUN poetry config virtualenvs.create false \
&& poetry install --no-interaction --no-ansi --no-root --only main \
&& pip cache purge && rm -rf ~/.cache/pypoetry
# Copy application code
COPY /ApiServices/DealerService /ApiServices/DealerService
COPY /ApiServices/DealerService /
COPY /Controllers /Controllers
COPY /Modules /Modules
COPY /Schemas /Schemas
# Set Python path to include app directory
ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
# Run the application using the configured uvicorn server
CMD ["poetry", "run", "python", "ApiServices/DealerService/app.py"]

View File

@ -0,0 +1,24 @@
from Controllers.Postgres.database import get_db
from Schemas import (
Users,
Employees,
)
from init_service_to_events import init_service_to_event_matches_for_super_user
from init_applications import init_applications_for_super_user
if __name__ == "__main__":
"""
Create Applications to serve on Next.js | Create Events to add to Service
Set Events to service | Set Service to employee
"""
with get_db() as db_session:
if super_man := Users.filter_one(
Users.email == "karatay.berkay.sup@evyos.com.tr", db=db_session
).data:
super_employee = Employees.filter_one(
Employees.people_id == super_man.person_id, db=db_session
).data
init_service_to_event_matches_for_super_user(super_user=super_employee, db_session=db_session)
init_applications_for_super_user(super_user=super_employee, db_session=db_session)

View File

@ -0,0 +1,40 @@
from Schemas import (
Applications,
Application2Employee,
Employees
)
def init_applications_for_super_user(super_user: Employees, db_session=None) -> None:
list_of_created_apps = [
dict(
name="Dashboard1",
application_code = "app000001",
site_url = "/dashboard",
application_type = "info",
description = "Dashboard Page"
),
dict(
name="Dashboard2",
application_code = "app000002",
site_url = "/buildings/list",
application_type = "CreateFrom",
description = "Dashboard Page"
),
]
for list_of_created_app in list_of_created_apps:
dashboard_page = Applications.find_or_create(
**list_of_created_app, db=db_session,
)
print('dashboard_page', dashboard_page)
if dashboard_page.meta_data.created:
dashboard_page.save(db=db_session)
Application2Employee.find_or_create(
employee_id=super_user.id,
employee_uu_id=str(super_user.uu_id),
site_url=dashboard_page.site_url,
application_code=dashboard_page.application_code,
application_id=dashboard_page.id,
application_uu_id=str(dashboard_page.uu_id),
db=db_session,
)

View File

@ -0,0 +1,47 @@
from Schemas import (
Users,
Services,
Service2Events,
Applications,
Application2Employee,
Application2Occupant,
Employees,
Event2Employee,
)
list_of_event_codes = []
def init_service_to_event_matches_for_super_user(super_user, db_session=None) -> None:
service_match = Services.filter_one(
Services.service_name == "Super User", db=db_session,
).data
for list_of_event_code in list_of_event_codes:
created_service = Service2Events.find_or_create(
service_id=service_match.id,
service_uu_id=str(service_match.uu_id),
event_id=list_of_event_code.id,
event_uu_id=str(list_of_event_code.uu_id),
is_confirmed=True,
active=True,
db=db_session,
)
if created_service.meta_data.created:
created_service.save(db=db_session)
print(
f"UUID: {created_service.uu_id} event is saved to {service_match.uu_id}"
)
employee_added_service = Event2Employee.find_or_create(
event_service_id=created_service.id,
event_service_uu_id=str(created_service.uu_id),
employee_id=super_user.id,
employee_uu_id=str(super_user.uu_id),
is_confirmed=True,
db=db_session
)
if employee_added_service.meta_data.created:
employee_added_service.save(db=db_session)
print(
f"UUID: {employee_added_service.uu_id} event is saved to {super_user.uu_id}"
)

View File

@ -1,9 +1,11 @@
import uuid
from fastapi import APIRouter, Request, Response, Header
from ApiServices.TemplateService.config import api_config
from ApiServices.TemplateService.events.template.event import template_event_cluster
test_template_route = APIRouter(prefix="/test", tags=["Test"])

View File

@ -80,6 +80,9 @@ from Schemas.event.event import (
Event2Employee,
Event2OccupantExtra,
Event2EmployeeExtra,
Applications,
Application2Employee,
Application2Occupant,
)
from Schemas.identity.identity import (
UsersTokens,
@ -175,6 +178,9 @@ __all__ = [
"Event2Employee",
"Event2OccupantExtra",
"Event2EmployeeExtra",
"Applications",
"Application2Employee",
"Application2Occupant",
"Addresses",
"AddressCity",
"AddressStreet",

View File

@ -11,6 +11,25 @@ from sqlalchemy.orm import mapped_column, Mapped
from Controllers.Postgres.mixin import CrudCollection
class Applications(CrudCollection):
"""
Applications class based on declarative_base and BaseMixin via session
"""
__tablename__ = "applications"
__exclude__fields__ = []
name: Mapped[str] = mapped_column(
String, nullable=False, comment="Application Name"
)
site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL")
application_code: Mapped[str] = mapped_column(
String, nullable=False, comment="Application Code"
)
application_type: Mapped[str] = mapped_column(String, comment="Application Type")
description: Mapped[str] = mapped_column(String, comment="Application Description")
class Events(CrudCollection):
"""
Events class based on declarative_base and BaseMixin via session
@ -82,6 +101,42 @@ class Modules(CrudCollection):
__table_args__ = ({"comment": "Modules Information"},)
class ModulePrice(CrudCollection):
"""
ModulePrice class based on declarative_base and BaseMixin via session
"""
__tablename__ = "module_price"
__exclude__fields__ = []
campaign_code: Mapped[str] = mapped_column(
String, nullable=False, comment="Campaign Code"
)
module_id: Mapped[int] = mapped_column(ForeignKey("modules.id"), nullable=False)
module_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Module UUID"
)
service_id: Mapped[int] = mapped_column(ForeignKey("services.id"), nullable=False)
service_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Service UUID"
)
event_id: Mapped[int] = mapped_column(ForeignKey("events.id"), nullable=False)
event_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Event UUID"
)
is_counted_percentage: Mapped[float] = mapped_column(
Numeric(6, 2), server_default="0.00"
) # %22
discounted_price: Mapped[float] = mapped_column(
Numeric(20, 2), server_default="0.00"
) # Normal: 78.00 TL
calculated_price: Mapped[float] = mapped_column(
Numeric(20, 2), server_default="0.00"
) # sana düz 75.00 TL yapar
__table_args__ = ({"comment": "ModulePrice Information"},)
class Services(CrudCollection):
"""
Services class based on declarative_base and BaseMixin via session
@ -143,6 +198,10 @@ class Service2Events(CrudCollection):
__table_args__ = ({"comment": "Service2Events Information"},)
# class Service2Application(CrudCollection):
# pass
class Event2OccupantExtra(CrudCollection):
__tablename__ = "event2occupant_extra"
@ -229,7 +288,7 @@ class Event2Employee(CrudCollection):
)
@classmethod
def get_event_codes(cls, employee_id: int, db) -> list:
def get_event_codes(cls, employee_id: int, db) -> dict[str : list[str]]:
employee_events = cls.filter_all(
cls.employee_id == employee_id,
db=db,
@ -253,43 +312,13 @@ class Event2Employee(CrudCollection):
db=db,
).data
active_events.extend(events_extra)
return [event.function_code for event in active_events]
# @classmethod
# def get_event_endpoints(cls, employee_id: int) -> list:
# from Schemas import EndpointRestriction
#
# db = cls.new_session()
# employee_events = cls.filter_all(
# cls.employee_id == employee_id,
# db=db,
# ).data
# active_event_ids = Service2Events.filter_all(
# Service2Events.service_id.in_(
# [event.event_service_id for event in employee_events]
# ),
# db=db,
# system=True,
# ).data
# active_events = Events.filter_all(
# Events.id.in_([event.event_id for event in active_event_ids]),
# db=db,
# ).data
# if extra_events := Event2EmployeeExtra.filter_all(
# Event2EmployeeExtra.employee_id == employee_id,
# db=db,
# ).data:
# events_extra = Events.filter_all(
# Events.id.in_([event.event_id for event in extra_events]),
# db=db,
# ).data
# active_events.extend(events_extra)
# endpoint_restrictions = EndpointRestriction.filter_all(
# EndpointRestriction.id.in_([event.endpoint_id for event in active_events]),
# db=db,
# ).data
# return [event.endpoint_name for event in endpoint_restrictions]
#
events_dict = {}
for event in active_events:
if event.endpoint_code in events_dict:
events_dict[event.endpoint_code].append(event.function_code)
else:
events_dict[event.endpoint_code] = [event.function_code]
return events_dict
class Event2Occupant(CrudCollection):
@ -326,7 +355,7 @@ class Event2Occupant(CrudCollection):
)
@classmethod
def get_event_codes(cls, build_living_space_id, db) -> list:
def get_event_codes(cls, build_living_space_id: int, db) -> dict[str : list[str]]:
occupant_events = cls.filter_all(
cls.build_living_space_id == build_living_space_id,
db=db,
@ -350,75 +379,106 @@ class Event2Occupant(CrudCollection):
db=db,
).data
active_events.extend(events_extra)
return [event.function_code for event in active_events]
# @classmethod
# def get_event_endpoints(cls, build_living_space_id) -> list:
# from Schemas import EndpointRestriction
#
# db = cls.new_session()
# occupant_events = cls.filter_all(
# cls.build_living_space_id == build_living_space_id,
# db=db,
# ).data
# active_event_ids = Service2Events.filter_all(
# Service2Events.service_id.in_(
# [event.event_service_id for event in occupant_events]
# ),
# db=db,
# system=True,
# ).data
# active_events = Events.filter_all(
# Events.id.in_([event.event_id for event in active_event_ids]),
# db=db,
# ).data
# if extra_events := Event2OccupantExtra.filter_all(
# Event2OccupantExtra.build_living_space_id == build_living_space_id,
# db=db,
# ).data:
# events_extra = Events.filter_all(
# Events.id.in_([event.event_id for event in extra_events]),
# db=db,
# ).data
# active_events.extend(events_extra)
# endpoint_restrictions = EndpointRestriction.filter_all(
# EndpointRestriction.id.in_([event.endpoint_id for event in active_events]),
# db=db,
# ).data
# return [event.endpoint_name for event in endpoint_restrictions]
events_dict = {}
for event in active_events:
if event.endpoint_code in events_dict:
events_dict[event.endpoint_code].append(event.function_code)
else:
events_dict[event.endpoint_code] = [event.function_code]
return events_dict
class ModulePrice(CrudCollection):
class Application2Employee(CrudCollection):
"""
ModulePrice class based on declarative_base and BaseMixin via session
Application2Employee class based on declarative_base and BaseMixin via session
"""
__tablename__ = "module_price"
__tablename__ = "application2employee"
__exclude__fields__ = []
campaign_code: Mapped[str] = mapped_column(
String, nullable=False, comment="Campaign Code"
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id"), nullable=False)
employee_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Employee UUID"
)
module_id: Mapped[int] = mapped_column(ForeignKey("modules.id"), nullable=False)
module_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Module UUID"
)
service_id: Mapped[int] = mapped_column(ForeignKey("services.id"), nullable=False)
service_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Service UUID"
)
event_id: Mapped[int] = mapped_column(ForeignKey("events.id"), nullable=False)
event_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Event UUID"
)
is_counted_percentage: Mapped[float] = mapped_column(
Numeric(6, 2), server_default="0.00"
) # %22
discounted_price: Mapped[float] = mapped_column(
Numeric(20, 2), server_default="0.00"
) # Normal: 78.00 TL
calculated_price: Mapped[float] = mapped_column(
Numeric(20, 2), server_default="0.00"
) # sana düz 75.00 TL yapar
__table_args__ = ({"comment": "ModulePrice Information"},)
application_id: Mapped[int] = mapped_column(ForeignKey("applications.id"))
application_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Application UUID"
)
site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL")
application_code: Mapped[str] = mapped_column(
String, nullable=False, comment="Application Code"
)
@classmethod
def get_application_codes(cls, employee_id: int, db) -> dict[str , str]:
print('employee_id', employee_id)
employee_applications = cls.filter_all(
Application2Employee.employee_id == employee_id, db=db,
).data
applications_dict = {}
print('employee_applications', employee_applications)
for employee_application in employee_applications:
if employee_application.site_url not in applications_dict:
applications_dict[str(employee_application.site_url)] = str(employee_application.application_code)
return applications_dict
__table_args__ = (
Index(
"application_to_employee",
employee_id,
site_url,
application_id,
unique=True,
),
{"comment": "Application2Employee Information"},
)
class Application2Occupant(CrudCollection):
"""
Application2Occupant class based on declarative_base and BaseMixin via session
"""
__tablename__ = "application2occupant"
__exclude__fields__ = []
build_living_space_id: Mapped[int] = mapped_column(
ForeignKey("build_living_space.id"), nullable=False
)
build_living_space_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Build Living Space UUID"
)
application_id: Mapped[int] = mapped_column(ForeignKey("applications.id"))
application_uu_id: Mapped[str] = mapped_column(
String, nullable=False, comment="Application UUID"
)
site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL")
application_code: Mapped[str] = mapped_column(
String, nullable=False, comment="Application Code"
)
@classmethod
def get_application_codes(cls, build_living_space_id: int, db) -> dict[str , str]:
occupant_applications = cls.filter_all(
cls.build_living_space_id == build_living_space_id, db=db,
).data
applications_dict = {}
for occupant_application in occupant_applications:
if occupant_application.site_url not in applications_dict:
applications_dict[str(occupant_application.site_url)] = str(occupant_application.application_code)
return applications_dict
__table_args__ = (
Index(
"application_to_occupant",
build_living_space_id,
site_url,
application_id,
unique=True,
),
{"comment": "Application2Occupant Information"},
)

View File

@ -10,6 +10,7 @@
"dependencies": {
"@hookform/resolvers": "^5.0.1",
"flatpickr": "^4.6.13",
"lucide-react": "^0.487.0",
"next": "15.2.4",
"next-crypto": "^1.0.8",
"react": "^19.0.0",
@ -1152,6 +1153,14 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lucide-react": {
"version": "0.487.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.487.0.tgz",
"integrity": "sha512-aKqhOQ+YmFnwq8dWgGjOuLc8V1R9/c/yOd+zDY4+ohsR2Jo05lSGc3WsstYPIzcTpeosN7LoCkLReUUITvaIvw==",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",

View File

@ -11,6 +11,7 @@
"dependencies": {
"@hookform/resolvers": "^5.0.1",
"flatpickr": "^4.6.13",
"lucide-react": "^0.487.0",
"next": "15.2.4",
"next-crypto": "^1.0.8",
"react": "^19.0.0",

View File

@ -0,0 +1,104 @@
"use server";
import React from "react";
import { Home, User, Settings, Mail, Calendar } from "lucide-react";
import { transformMenu, LanguageTranslation } from "@/components/menu/runner";
import Link from "next/link";
async function LeftMenu({
searchParams,
pageUuidList,
lang,
}: {
pageUuidList: string[];
lang: keyof LanguageTranslation;
searchParams: { [key: string]: string | string[] | undefined };
}) {
const transformedMenu = transformMenu(pageUuidList);
// Get the menuContext from searchParams without setting a default value
const menuContext = searchParams?.menu;
// Only parse the indices if menuContext exists
let firstLayerIndex = -1;
let secondLayerIndex = -1;
if (menuContext) {
const indices = menuContext.toString().split("*").map(Number);
firstLayerIndex = indices[0] || 0;
secondLayerIndex = indices[1] || 0;
}
const pageSelected = searchParams?.page;
return (
<div>
<nav className="flex flex-col space-y-2">
<div className="text-xl font-bold mb-6 text-center">Dashboard</div>
{transformedMenu.map((item, firstIndex) => (
<div key={item.name} className="mb-4">
<Link
href={`/dashboard?${
pageSelected ? `page=${pageSelected}` : ""
}&menu=${firstIndex}*0`}
className={`text-xl font-semibold pl-5 my-2 py-2 block ${
firstIndex === firstLayerIndex
? "text-emerald-600"
: "text-emerald-400"
} hover:text-emerald-600`}
>
{item.lg[lang]}
</Link>
{/* Only render the second layer if menuContext exists and this first layer item is selected */}
{menuContext && firstIndex === firstLayerIndex && (
<ul className="space-y-2">
{item.subList.map((subItem, secondIndex) => (
<div key={subItem.name}>
<Link
href={`/dashboard?${
pageSelected ? `page=${pageSelected}` : ""
}&menu=${firstIndex}*${secondIndex}`}
className={`ml-5 my-4 pl-4 text-xl font-semibold block ${
secondIndex === secondLayerIndex
? "text-emerald-700"
: "text-emerald-500"
} hover:text-emerald-700`}
>
{subItem.lg[lang]}
</Link>
{/* Only render the third layer if this second layer item is selected */}
{firstIndex === firstLayerIndex &&
secondIndex === secondLayerIndex && (
<div className="ml-5">
{subItem.subList.map((subSubItem) => (
<Link
key={subSubItem.appUUID}
href={`/dashboard?page=${subSubItem.appUUID}&menu=${firstIndex}*${secondIndex}`}
className={`flex flex-row text-xl py-4 my-4 w-full space-x-2 p-2 rounded ${
pageSelected === subSubItem.appUUID
? " bg-gray-100 cursor-not-allowed"
: "hover:bg-gray-200"
}`}
>
<span className="text-gray-400">
<Home />
</span>
<span className="ml-5 text-gray-700">
{subSubItem.lg[lang]}
</span>
</Link>
))}
</div>
)}
</div>
))}
</ul>
)}
</div>
))}
</nav>
</div>
);
}
export default LeftMenu;

View File

@ -0,0 +1,32 @@
import React from "react";
import retrievePage from "@/components/NavigatePages";
function MainPage({
pageSelected,
lang,
}: {
pageSelected: string | undefined;
lang: string;
}) {
const ComponentPage = retrievePage({
pageId: pageSelected ?? "",
});
if (!ComponentPage) {
return (
<div className="flex flex-col w-full">
<h2 className="text-2xl font-semibold p-4">No Page Selected</h2>
</div>
);
}
return (
<>
{/* Main Content */}
<main className="flex-grow p-6 bg-gray-50 overflow-y-auto">
<ComponentPage lang={lang} />
</main>
</>
);
}
export default MainPage;

View File

@ -2,13 +2,56 @@ import React from "react";
import { checkAccessTokenIsValid } from "@/apicalls/cookies/token";
import { redirect } from "next/navigation";
async function DashboardPage() {
const token_is_valid = await checkAccessTokenIsValid();
import LeftMenu from "./leftMenu";
import MainPage from "./main";
export default async function DashboardLayout({
searchParams,
}: {
searchParams: { [key: string]: string | undefined };
}) {
const token_is_valid = await checkAccessTokenIsValid();
if (!token_is_valid) {
redirect("/auth/login");
}
return <div>You have arrived to Dashboard Page</div>;
}
const pageUuidList = [
"6015129b-f665-479c-a440-04fb82ea6114",
"14a98ae7-c64e-403d-9b53-32e7ea867ab4",
"e368137d-d548-4ed4-90da-337bcc5d1559",
"d3d97973-41c6-4bad-881b-6bf77d837fa5",
]; // Mock data of pageUUID list []
const lang = "tr"; // Assuming you have a way to determine the current language
const queryParams = await searchParams;
const pageSelected = queryParams?.page || undefined;
export default DashboardPage;
return (
<div className="flex h-screen overflow-hidden">
{/* Sidebar */}
<aside className="w-1/4 border-r p-4 overflow-y-auto">
<LeftMenu
pageUuidList={pageUuidList}
lang={lang}
searchParams={queryParams}
/>
</aside>
{/* Main Content Area */}
<div className="flex flex-col w-3/4">
{/* Sticky Header */}
<header className="sticky top-0 bg-white shadow-md z-10 p-4 flex justify-between items-center">
<h1 className="text-2xl font-semibold">Dashboard</h1>
<div className="flex items-center space-x-4">
<input
type="text"
placeholder="Search..."
className="border px-3 py-2 rounded-lg"
/>
<div className="w-10 h-10 bg-gray-300 rounded-full"></div>
</div>
</header>
<MainPage pageSelected={pageSelected} lang={lang} />
</div>
</div>
);
}

View File

@ -0,0 +1,41 @@
import { PageProps } from "./interFaces";
import Page0001 from "./page0001";
const PageIndexs = {
"6015129b-f665-479c-a440-04fb82ea6114": Page0001,
};
function UnAuthorizedPage({ lang }: PageProps) {
return (
<>
<div className="flex flex-col h-screen">
<header className="bg-gray-800 text-white p-4 text-center">
<h1 className="text-2xl font-bold">Unauthorized Access</h1>
</header>
<main className="flex-grow p-4 bg-gray-100">
<p className="text-gray-700">
You do not have permission to access this page.
</p>
<p className="text-gray-700">Please contact the administrator.</p>
</main>
<footer className="bg-gray-800 text-white p-4 text-center">
<p>&copy; 2023 My Application</p>
</footer>
</div>
</>
);
}
export function retrievePage({
pageId,
}: {
pageId: string;
}): React.ComponentType<PageProps> {
const PageComponent = PageIndexs[pageId as keyof typeof PageIndexs];
if (!PageComponent) {
return UnAuthorizedPage;
}
return PageComponent;
}
export default retrievePage;

View File

@ -0,0 +1,3 @@
export interface PageProps {
lang: string;
}

View File

@ -0,0 +1,36 @@
import React from "react";
import { PageProps } from "./interFaces";
const pageContext = {
tr: {
pageTitle: "Sayfa 0001",
pageDescription: "Bu, Sayfa 0001'in içeriğidir.",
},
en: {
pageTitle: "Page 0001",
pageDescription: "This is the content of Page 0001.",
},
};
function Page0001({ lang }: PageProps) {
const { pageTitle, pageDescription } =
pageContext[lang as keyof typeof pageContext];
return (
<>
<div className="flex flex-col h-screen">
<header className="bg-gray-800 text-white p-4 text-center">
<h1 className="text-2xl font-bold">{pageTitle}</h1>
</header>
<main className="flex-grow p-4 bg-gray-100">
<p className="text-gray-700">{pageDescription}</p>
</main>
<footer className="bg-gray-800 text-white p-4 text-center">
<p>&copy; 2023 My Application</p>
</footer>
</div>
</>
);
}
export default Page0001;

View File

@ -0,0 +1,125 @@
/**
* Filters the menu structure based on intersections with provided UUIDs
* @param {string[]} uuids - Array of UUIDs to check for intersection
* @param {Array} menu - The original menu structure
* @returns {Array} - Filtered menu structure with only matching items
*/
import Menu from "@/menu/store"; // Assuming you have a menu structure imported
// Define TypeScript interfaces for menu structure
interface LanguageTranslation {
tr: string;
en: string;
}
interface MenuThirdLevel {
name: string;
lg: LanguageTranslation;
appList: string[];
}
interface MenuSecondLevel {
name: string;
lg: LanguageTranslation;
subList: MenuThirdLevel[];
}
interface MenuFirstLevel {
name: string;
lg: LanguageTranslation;
subList: MenuSecondLevel[];
}
// Define interfaces for the filtered menu structure
interface FilteredMenuThirdLevel {
name: string;
lg: LanguageTranslation;
appUUID: string;
}
interface FilteredMenuSecondLevel {
name: string;
lg: LanguageTranslation;
subList: FilteredMenuThirdLevel[];
}
interface FilteredMenuFirstLevel {
name: string;
lg: LanguageTranslation;
subList: FilteredMenuSecondLevel[];
}
export type { LanguageTranslation };
function transformMenu(uuids: string[]) {
// Helper function to check if arrays have at least one common element
const hasIntersection = (array1: string[], array2: string[]): boolean => {
return array1.some((item) => array2.includes(item));
};
// Process the menu structure
const filteredMenu: FilteredMenuFirstLevel[] = Menu.reduce(
(acc: FilteredMenuFirstLevel[], firstLevel: MenuFirstLevel) => {
// Create a new first level item with empty subList
const newFirstLevel: FilteredMenuFirstLevel = {
name: firstLevel.name,
lg: { ...firstLevel.lg },
subList: [],
};
// Process second level items
firstLevel.subList.forEach((secondLevel: MenuSecondLevel) => {
// Create a new second level item with empty subList
const newSecondLevel: FilteredMenuSecondLevel = {
name: secondLevel.name,
lg: { ...secondLevel.lg },
subList: [],
};
// Process third level items
secondLevel.subList.forEach((thirdLevel: MenuThirdLevel) => {
// Check if the third level's appList has an intersection with our UUIDs
if (
thirdLevel.appList &&
hasIntersection(thirdLevel.appList, uuids)
) {
// Find the first matching UUID
const matchedUUID = thirdLevel.appList.find((uuid) =>
uuids.includes(uuid)
);
// Only proceed if we found a matching UUID (should always be true due to hasIntersection)
if (matchedUUID) {
// Create a modified third level item with the matched UUID
const newThirdLevel: FilteredMenuThirdLevel = {
name: thirdLevel.name,
lg: { ...thirdLevel.lg },
appUUID: matchedUUID,
};
// Add the modified third level to the second level's subList
newSecondLevel.subList.push(newThirdLevel);
}
}
});
// Only add the second level to the first level if it has any matching third level items
if (newSecondLevel.subList.length > 0) {
newFirstLevel.subList.push(newSecondLevel);
}
});
// Only add the first level to the result if it has any matching second level items
if (newFirstLevel.subList.length > 0) {
acc.push(newFirstLevel);
}
return acc;
},
[]
);
return filteredMenu;
}
export { transformMenu };

View File

@ -0,0 +1,180 @@
const Individual = {
name: "Individual",
lg: {
tr: "Birey",
en: "Individual",
},
appList: [
"0362071d-90d9-48db-8fa0-3528aaf450bd",
"6015129b-f665-479c-a440-04fb82ea6114",
],
};
const User = {
name: "User",
lg: {
tr: "Kullanıcı",
en: "User",
},
appList: ["14a98ae7-c64e-403d-9b53-32e7ea867ab4"],
};
const Build = {
name: "Build",
lg: {
tr: "Apartman",
en: "Build",
},
appList: ["e368137d-d548-4ed4-90da-337bcc5d1559"],
};
const BuildParts = {
name: "BuildParts",
lg: {
tr: "Daire",
en: "BuildParts",
},
appList: [],
};
const BuildArea = {
name: "BuildArea",
lg: {
tr: "Daire",
en: "BuildArea",
},
appList: [],
};
const ManagementAccounting = {
name: "ManagementAccounting",
lg: {
tr: "Yönetim Cari Hareketler",
en: "ManagementAccounting",
},
appList: [],
};
const ManagementBudget = {
name: "ManagementBudget",
lg: {
tr: "Yönetim Bütçe İşlemleri",
en: "Management Budget",
},
appList: [],
};
const BuildPartsAccounting = {
name: "BuildPartsAccounting",
lg: {
tr: "Daire Cari Hareketler",
en: "Build Parts Accounting",
},
appList: [],
};
const AnnualMeeting = {
name: "AnnualMeeting",
lg: {
tr: "Yıllık Olağan Toplantı Tanımlama ve Davet",
en: "Annual Meetings and Invitations",
},
appList: ["d3d97973-41c6-4bad-881b-6bf77d837fa5"],
};
const AnnualMeetingClose = {
name: "AnnualMeetingClose",
lg: {
tr: "Yıllık Olağan Toplantı kapatma ve Cari Yaratma",
en: "Annual Meeting Close and Accountings",
},
appList: [],
};
const EmergencyMeeting = {
name: "EmergencyMeeting",
lg: {
tr: "Acil Toplantı Tanımlama ve Davet",
en: "Emergency Meeting and Invitations",
},
appList: [],
};
const EmergencyMeetingClose = {
name: "EmergencyMeetingClose",
lg: {
tr: "Acil Olağan Toplantı kapatma ve Cari Yaratma",
en: "Emergency Meeting Close and Accountings",
},
appList: [],
};
const MeetingParticipations = {
name: "MeetingParticipations",
lg: {
tr: "Toplantı Katılım İşlemleri",
en: "Meeting Participations",
},
appList: ["SomeUUID"],
};
const Menu = [
{
name: "Definitions",
lg: {
tr: "Tanımlar",
en: "Definitions",
},
subList: [
{
name: "People",
lg: {
tr: "Kişiler",
en: "People",
},
subList: [Individual, User],
},
{
name: "Building",
lg: {
tr: "Binalar",
en: "Building",
},
subList: [Build, BuildParts, BuildArea],
},
],
},
{
name: "Building Management",
lg: {
tr: "Bina Yönetimi",
en: "Building Management",
},
subList: [
{
name: "Management Accounting",
lg: {
tr: "Cari işlemler",
en: "Management Accounting",
},
subList: [ManagementAccounting, ManagementBudget, BuildPartsAccounting],
},
{
name: "Meetings",
lg: {
tr: "Toplantılar",
en: "Meetings",
},
subList: [
AnnualMeeting,
AnnualMeetingClose,
EmergencyMeeting,
EmergencyMeetingClose,
MeetingParticipations,
],
},
],
},
];
export default Menu;

View File

@ -49,19 +49,19 @@ services:
ports:
- "11222:6379"
client_frontend:
container_name: client_frontend
build:
context: .
dockerfile: WebServices/client-frontend/Dockerfile
networks:
- wag-services
ports:
- "3000:3000"
# volumes:
# - client-frontend:/WebServices/client-frontend
environment:
- NODE_ENV=development
# client_frontend:
# container_name: client_frontend
# build:
# context: .
# dockerfile: WebServices/client-frontend/Dockerfile
# networks:
# - wag-services
# ports:
# - "3000:3000"
# # volumes:
# # - client-frontend:/WebServices/client-frontend
# environment:
# - NODE_ENV=development
# management_frontend:
# container_name: management_frontend
@ -77,47 +77,61 @@ services:
# environment:
# - NODE_ENV=development
# initializer_service:
# container_name: initializer_service
# build:
# context: .
# dockerfile: ApiServices/InitialService/Dockerfile
# networks:
# - wag-services
# env_file:
# - api_env.env
# depends_on:
# - postgres-service
# - mongo_service
# - redis_service
# initializer_service:
# container_name: initializer_service
# build:
# context: .
# dockerfile: ApiServices/InitialService/Dockerfile
# networks:
# - wag-services
# env_file:
# - api_env.env
# depends_on:
# - postgres-service
# - mongo_service
# - redis_service
# template_service:
# container_name: template_service
# build:
# context: .
# dockerfile: ApiServices/TemplateService/Dockerfile
# networks:
# - wag-services
# env_file:
# - api_env.env
# environment:
# - API_PATH=app:app
# - API_HOST=0.0.0.0
# - API_PORT=8000
# - API_LOG_LEVEL=info
# - API_RELOAD=1
# - API_ACCESS_TOKEN_TAG=1
# - API_APP_NAME=evyos-template-api-gateway
# - API_TITLE=WAG API Template Api Gateway
# - API_FORGOT_LINK=https://template_service/forgot-password
# - API_DESCRIPTION=This api is serves as web template api gateway only to evyos web services.
# - API_APP_URL=https://template_service
# ports:
# - "8000:8000"
# depends_on:
# - postgres-service
# - mongo_service
# - redis_service
dealer_service:
container_name: dealer_service
build:
context: .
dockerfile: ApiServices/DealerService/Dockerfile
networks:
- wag-services
env_file:
- api_env.env
depends_on:
- postgres-service
- mongo_service
- redis_service
# template_service:
# container_name: template_service
# build:
# context: .
# dockerfile: ApiServices/TemplateService/Dockerfile
# networks:
# - wag-services
# env_file:
# - api_env.env
# environment:
# - API_PATH=app:app
# - API_HOST=0.0.0.0
# - API_PORT=8000
# - API_LOG_LEVEL=info
# - API_RELOAD=1
# - API_ACCESS_TOKEN_TAG=1
# - API_APP_NAME=evyos-template-api-gateway
# - API_TITLE=WAG API Template Api Gateway
# - API_FORGOT_LINK=https://template_service/forgot-password
# - API_DESCRIPTION=This api is serves as web template api gateway only to evyos web services.
# - API_APP_URL=https://template_service
# ports:
# - "8000:8000"
# depends_on:
# - postgres-service
# - mongo_service
# - redis_service
auth_service:
container_name: auth_service