From b73417a625157dd9cbd70d5527052d2ab7d605ed Mon Sep 17 00:00:00 2001 From: Berkay Date: Mon, 16 Jun 2025 15:52:50 +0300 Subject: [PATCH] updated services web user selection --- ServicesApi/Builds/Auth/events/auth/events.py | 13 ++++-- .../Builds/Restriction/events/pages/events.py | 18 ++++---- ServicesApi/Schemas/event/event.py | 41 +++++-------------- .../src/app/api/context/page/menu/route.ts | 1 + .../customer/src/app/api/login/email/route.ts | 11 ++++- .../custom/menu/clientSelectionSection.tsx | 7 ++-- .../src/components/custom/menu/component.tsx | 7 ++-- .../custom/menu/menuItemsSection.tsx | 31 ++++++++++++-- .../custom/menu/renderOneClientSelection.tsx | 19 +++++++-- .../custom/menu/userProfileSection.tsx | 1 - .../components/mutual/context/menu/context.ts | 2 + .../custom/context/page/menu/fetch.tsx | 9 +++- .../src/fetchers/custom/login/login.tsx | 6 ++- .../src/fetchers/custom/restriction/fetch.tsx | 13 ++++++ 14 files changed, 117 insertions(+), 62 deletions(-) diff --git a/ServicesApi/Builds/Auth/events/auth/events.py b/ServicesApi/Builds/Auth/events/auth/events.py index 660a581..ff8c302 100644 --- a/ServicesApi/Builds/Auth/events/auth/events.py +++ b/ServicesApi/Builds/Auth/events/auth/events.py @@ -210,7 +210,7 @@ class LoginHandler: user_dict = found_user.get_dict() person_dict = found_user.person.get_dict() - + for living_space in living_spaces: build_part = BuildParts.query.filter(BuildParts.id == living_space.build_parts_id).first() if not build_part: @@ -344,7 +344,12 @@ class LoginHandler: # TODO: erase this bypass later filter_endpoints_and_events = db_session.query(EndpointRestriction.operation_uu_id, Events.function_code ).join(EndpointRestriction, EndpointRestriction.id == Events.endpoint_id).filter().all() + + # Get reachable applications + reachable_app_codes = Application2Employee.get_application_codes(employee_id=int(result_with_keys_dict['Employees.id']), db=db_session) + # Get reachable events reachable_event_codes = {endpoint_name: function_code for endpoint_name, function_code in filter_endpoints_and_events} + add_company = { "uu_id": str(result_with_keys_dict["Employees.uu_id"]), "public_name": result_with_keys_dict["Companies.public_name"], @@ -356,7 +361,7 @@ class LoginHandler: redis_handler = RedisHandlers() user_uu_id = Users.query.filter(Users.person_id == result_with_keys_dict['People.id']).first().uu_id redis_result = redis_handler.update_token_at_redis(token=access_token, add_payload=add_company, user_uuid=user_uu_id) - return {"selected_uu_id": data.uuid} + return {"selected_uu_id": data.uuid, "reachable_app_codes": reachable_app_codes} @classmethod def handle_occupant_selection(cls, access_token: str, data: Any, token_dict: TokenDictType): @@ -391,7 +396,9 @@ class LoginHandler: reachable_event_codes = {endpoint_name: function_code for endpoint_name, function_code in filter_endpoints_and_events} # Get reachable applications + print('selected_build_living_space_first', selected_build_living_space_first.id) reachable_app_codes = Application2Occupant.get_application_codes(build_living_space_id=selected_build_living_space_first.id, db=db) + print('reachable_app_codes', reachable_app_codes) build_part = BuildParts.query.filter(BuildParts.id == result_with_keys_dict['BuildParts.id']).first() if not build_part: @@ -410,7 +417,7 @@ class LoginHandler: user_uu_id = Users.query.filter(Users.person_id == result_with_keys_dict['People.id']).first().uu_id redis_handler.update_token_at_redis(token=access_token, add_payload=add_build_living_space, user_uuid=user_uu_id) - return {"selected_uu_id": data.uuid} + return {"selected_uu_id": data.uuid, "reachable_app_codes": reachable_app_codes} @classmethod def authentication_select_company_or_occupant_type(cls, request: Any, data: Any): diff --git a/ServicesApi/Builds/Restriction/events/pages/events.py b/ServicesApi/Builds/Restriction/events/pages/events.py index 1c73eed..97ac0bd 100644 --- a/ServicesApi/Builds/Restriction/events/pages/events.py +++ b/ServicesApi/Builds/Restriction/events/pages/events.py @@ -21,14 +21,14 @@ class PageHandlers: @classmethod def retrieve_valid_sites_via_token(cls, access_token: str) -> list: - """ - Retrieve valid pages via token. {"access_token": "string"} | Results: list(sites) - """ + """ Retrieve valid pages via token. {"access_token": "string"} | Results: list(sites) """ if result := RedisHandlers.get_object_from_redis(access_token=access_token): - if result.is_employee: - if result.selected_company and result.selected_company.reachable_app_codes: - return result.selected_company.reachable_app_codes.keys() - elif result.is_occupant: - if result.selected_occupant and result.selected_occupant.reachable_app_codes: - return result.selected_occupant.reachable_app_codes.keys() + if result.is_employee and result.selected_company: + employee_uuid = result.selected_company.get("employee_uu_id", "") + if reachable_app_codes_dict := result.reachable_app_codes: + return reachable_app_codes_dict.get(employee_uuid, {}).keys() + elif result.is_occupant and result.selected_occupant: + living_space_uu_id = result.selected_occupant.get("build_living_space_uu_id", "") + if reachable_app_codes_dict := result.reachable_app_codes: + return reachable_app_codes_dict.get(living_space_uu_id, {}).keys() raise ValueError("EYS_0013") diff --git a/ServicesApi/Schemas/event/event.py b/ServicesApi/Schemas/event/event.py index a91f43d..ea37bc5 100644 --- a/ServicesApi/Schemas/event/event.py +++ b/ServicesApi/Schemas/event/event.py @@ -211,19 +211,11 @@ class Service2Application(CrudCollection): __tablename__ = "services2applications" __exclude__fields__ = [] - application_id: Mapped[int] = mapped_column( - ForeignKey("applications.id"), nullable=False - ) - application_uu_id: Mapped[str] = mapped_column( - String, nullable=False, comment="Application UUID" - ) + application_id: Mapped[int] = mapped_column(ForeignKey("applications.id"), nullable=False) + application_uu_id: Mapped[str] = mapped_column(String, nullable=False, comment="Application 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" - ) - application_code: Mapped[str] = mapped_column( - String, nullable=False, comment="Application Code" - ) + service_uu_id: Mapped[str] = mapped_column(String, nullable=False, comment="Service UUID") + application_code: Mapped[str] = mapped_column(String, nullable=False, comment="Application Code") site_url: Mapped[str] = mapped_column(String, nullable=False, comment="Site URL") __table_args__ = {"comment": "Service2Applications Information"} @@ -237,13 +229,9 @@ class Event2OccupantExtra(CrudCollection): 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" - ) + build_living_space_uu_id: Mapped[str] = mapped_column(String, nullable=False, comment="Build Living Space 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" - ) + event_uu_id: Mapped[str] = mapped_column(String, nullable=False, comment="Event UUID") __table_args__ = ( Index( @@ -265,14 +253,10 @@ class Event2EmployeeExtra(CrudCollection): __exclude__fields__ = [] employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id"), nullable=False) - employee_uu_id: Mapped[str] = mapped_column( - String, nullable=False, comment="Employee UUID" - ) + employee_uu_id: Mapped[str] = mapped_column(String, nullable=False, comment="Employee 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" - ) + event_uu_id: Mapped[str] = mapped_column(String, nullable=False, comment="Event UUID") __table_args__ = ( Index( @@ -447,12 +431,8 @@ class Application2Occupant(CrudCollection): __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_column( - String, nullable=False, comment="Build Living Space UUID" - ) + build_living_space_id: Mapped[int] = mapped_column(ForeignKey("build_living_space.id"), nullable=False) + build_living_space_uu_id = mapped_column(String, nullable=False, comment="Build Living Space UUID") service_id: Mapped[int] = mapped_column(ForeignKey("services.id"), nullable=False) service_uu_id = mapped_column(String, nullable=False, comment="Service UUID") @@ -462,6 +442,7 @@ class Application2Occupant(CrudCollection): Service2Application.set_session(db) Applications.set_session(db) Application2OccupantExtra.set_session(db) + occupant_services = cls.query.filter(cls.build_living_space_id == build_living_space_id).all() service_ids = [service.service_id for service in occupant_services] active_applications = Service2Application.query.filter(Service2Application.service_id.in_(service_ids)).all() diff --git a/ServicesWeb/customer/src/app/api/context/page/menu/route.ts b/ServicesWeb/customer/src/app/api/context/page/menu/route.ts index f2f59c1..93448a5 100644 --- a/ServicesWeb/customer/src/app/api/context/page/menu/route.ts +++ b/ServicesWeb/customer/src/app/api/context/page/menu/route.ts @@ -8,6 +8,7 @@ import { NextResponse } from "next/server"; export async function GET() { try { const menu = await getMenuFromRedis(); + console.log("menu", menu); return NextResponse.json({ status: 200, data: menu, diff --git a/ServicesWeb/customer/src/app/api/login/email/route.ts b/ServicesWeb/customer/src/app/api/login/email/route.ts index ec2c013..687679d 100644 --- a/ServicesWeb/customer/src/app/api/login/email/route.ts +++ b/ServicesWeb/customer/src/app/api/login/email/route.ts @@ -1,5 +1,8 @@ import { NextResponse } from "next/server"; -import { loginViaAccessKeys, initFirstSelection } from "@/fetchers/custom/login/login"; +import { + loginViaAccessKeys, + initFirstSelection, +} from "@/fetchers/custom/login/login"; import { loginSchemaEmail } from "@/pages/single/auth/login/schemas"; export async function POST(req: Request): Promise { @@ -20,7 +23,11 @@ export async function POST(req: Request): Promise { } const userLogin = await loginViaAccessKeys(dataValidated); - await initFirstSelection(userLogin.data?.firstSelection, userLogin.data?.userType); + console.log("userLogin", userLogin); + await initFirstSelection( + userLogin.data?.firstSelection, + userLogin.data?.userType + ); if (userLogin.status === 200 || userLogin.status === 202) { return NextResponse.json({ status: 200, diff --git a/ServicesWeb/customer/src/components/custom/menu/clientSelectionSection.tsx b/ServicesWeb/customer/src/components/custom/menu/clientSelectionSection.tsx index c4556c2..7d93d74 100644 --- a/ServicesWeb/customer/src/components/custom/menu/clientSelectionSection.tsx +++ b/ServicesWeb/customer/src/components/custom/menu/clientSelectionSection.tsx @@ -6,9 +6,10 @@ interface ClientSelectionSectionProps { selectionData: any; refreshSelection: () => Promise; updateSelection: (newSelection: any) => Promise; + refreshMenu: () => Promise; } -const ClientSelectionSection: FC = ({ selectionData, refreshSelection, updateSelection }) => { +const ClientSelectionSection: FC = ({ selectionData, refreshSelection, updateSelection, refreshMenu }) => { const [expandedBuilds, setExpandedBuilds] = useState<{ [key: string]: boolean }>({}); if (!selectionData || !selectionData.selectionList) { return null } @@ -21,7 +22,7 @@ const ClientSelectionSection: FC = ({ selectionData selectionData.selectionList.map((client: any, index: number) => { return (
- {client && renderOneClientSelection({ item: client, selectedItem: selectionData.activeSelection, updateSelection, refreshSelection })} + {client && renderOneClientSelection({ item: client, selectedItem: selectionData.activeSelection, updateSelection, refreshSelection, refreshMenu })}
); }) @@ -63,7 +64,7 @@ const ClientSelectionSection: FC = ({ selectionData {buildData.occupants && Array.isArray(buildData.occupants) ? ( buildData.occupants.map((client: any, index: number) => (
- {client && renderOneClientSelection({ item: client, selectedItem: selectionData.activeSelection, updateSelection, refreshSelection })} + {client && renderOneClientSelection({ item: client, selectedItem: selectionData.activeSelection, updateSelection, refreshSelection, refreshMenu })}
)) ) : (
No occupants found
)} diff --git a/ServicesWeb/customer/src/components/custom/menu/component.tsx b/ServicesWeb/customer/src/components/custom/menu/component.tsx index abe31e6..4020055 100644 --- a/ServicesWeb/customer/src/components/custom/menu/component.tsx +++ b/ServicesWeb/customer/src/components/custom/menu/component.tsx @@ -8,7 +8,7 @@ import ClientSelectionSection from "./clientSelectionSection"; import MenuItemsSection from "./menuItemsSection"; import MenuLoadingState from "./menuLoadingState"; import MenuErrorState from "./menuErrorState"; -import MenuEmptyState from "./menuEmptyState"; + import LoadingContent from "@/components/mutual/loader/component"; const MenuComponent: FC = ({ @@ -20,8 +20,7 @@ const MenuComponent: FC = ({ }) => { if (menuLoading) { return } if (menuError) { return ; } - if (availableApplications.length === 0) { return ; } - + const lang = onlineData?.lang as LanguageTypes || 'en'; return (
@@ -32,7 +31,7 @@ const MenuComponent: FC = ({ {/* Client Selection Section */}
}> - + {/* Menu Items Section */} }> diff --git a/ServicesWeb/customer/src/components/custom/menu/menuItemsSection.tsx b/ServicesWeb/customer/src/components/custom/menu/menuItemsSection.tsx index b50bd5f..2d52f3d 100644 --- a/ServicesWeb/customer/src/components/custom/menu/menuItemsSection.tsx +++ b/ServicesWeb/customer/src/components/custom/menu/menuItemsSection.tsx @@ -11,7 +11,7 @@ import { MenuStructure, MenuItemsSectionProps, } from "./types"; - +import MenuEmptyState from "./menuEmptyState"; const menuStaticTranslation = { tr: { menu: "MenĂ¼" }, @@ -26,6 +26,7 @@ const MenuItemsSection: FC = ({ availableApplications, ac const [activeSecondLayer, setActiveSecondLayer] = useState(null); const [activeThirdLayer, setActiveThirdLayer] = useState(null); + useEffect(() => { const newMenuStructure: MenuStructure = {}; const menuTranslationWLang = menuTranslation[lang as keyof typeof menuTranslation]; @@ -82,6 +83,8 @@ const MenuItemsSection: FC = ({ availableApplications, ac }; const renderFirstLayerItems = () => { + + return Object.entries(menuStructure).map(([firstLayerKey, secondLayerItems]) => { const isActive = activeFirstLayer === firstLayerKey; const isExpanded = expandedFirstLayer === firstLayerKey; @@ -92,13 +95,35 @@ const MenuItemsSection: FC = ({ availableApplications, ac return (
- handleFirstLayerClick(firstLayerKey)} /> + { handleFirstLayerClick(firstLayerKey)} />} {isExpanded &&
{renderSecondLayerItems(firstLayerKey, secondLayerItems)}
}
); }); }; - return

{menuStaticTranslation[lang as keyof typeof menuStaticTranslation].menu}

{renderFirstLayerItems()}
; + + return ( + <> +
+

{menuStaticTranslation[lang as keyof typeof menuStaticTranslation].menu}

+
+ { + availableApplications.length !== 0 &&
{renderFirstLayerItems()}
+ } + { + availableApplications.length === 0 &&
+
+
+

No menu items available

+

Please check your permissions or contact an administrator

+
+
+
+ } +
+ + ) + }; export default MenuItemsSection; diff --git a/ServicesWeb/customer/src/components/custom/menu/renderOneClientSelection.tsx b/ServicesWeb/customer/src/components/custom/menu/renderOneClientSelection.tsx index 458e700..9c9d9a6 100644 --- a/ServicesWeb/customer/src/components/custom/menu/renderOneClientSelection.tsx +++ b/ServicesWeb/customer/src/components/custom/menu/renderOneClientSelection.tsx @@ -7,15 +7,26 @@ interface Props { selectedItem: any; refreshSelection: () => Promise; updateSelection: (newSelection: any) => Promise; + refreshMenu: () => Promise; } -const selectFromSelectionList = (item: any, selectedItem: any, updateSelection: (newSelection: any) => Promise, refreshSelection: () => Promise) => { +const selectFromSelectionList = async (item: any, selectedItem: any, updateSelection: (newSelection: any) => Promise, refreshSelection: () => Promise, refreshMenu: () => Promise) => { const selectedUUID = selectedItem?.uu_id || selectedItem?.build_living_space_uu_id; const itemUUID = item?.uu_id || item?.build_living_space_uu_id; - if (selectedUUID !== itemUUID) { updateSelection(item); refreshSelection() } + if (selectedUUID !== itemUUID) { + // First update the selection + await updateSelection(item); + // Then refresh the selection + await refreshSelection(); + // Wait a moment to ensure the selection update is processed + setTimeout(async () => { + // Finally refresh the menu + await refreshMenu(); + }, 300); + } } -const RenderOneClientSelection: FC = ({ item, selectedItem, updateSelection, refreshSelection }) => { +const RenderOneClientSelection: FC = ({ item, selectedItem, updateSelection, refreshSelection, refreshMenu }) => { const selectedUUID = selectedItem?.uu_id || selectedItem?.build_living_space_uu_id; const itemUUID = item?.uu_id || item?.build_living_space_uu_id; const getDisplayName = () => { if (item.public_name) return item.public_name; if (item.part_name) return item.part_name; return "No Name" }; @@ -24,7 +35,7 @@ const RenderOneClientSelection: FC = ({ item, selectedItem, updateSelecti if (selectedUUID !== itemUUID) { return ( -
{ selectFromSelectionList(item, selectedItem, updateSelection, refreshSelection) }} +
{ selectFromSelectionList(item, selectedItem, updateSelection, refreshSelection, refreshMenu) }} className="w-full text-xs bg-white shadow rounded-lg overflow-hidden transition-all hover:shadow-md mb-2 cursor-pointer">
diff --git a/ServicesWeb/customer/src/components/custom/menu/userProfileSection.tsx b/ServicesWeb/customer/src/components/custom/menu/userProfileSection.tsx index 95a5e98..24a5e52 100644 --- a/ServicesWeb/customer/src/components/custom/menu/userProfileSection.tsx +++ b/ServicesWeb/customer/src/components/custom/menu/userProfileSection.tsx @@ -12,7 +12,6 @@ interface UserProfileSectionProps { } const UserProfileSection: FC = ({ userData, onlineData, selectionData }) => { - console.log("selectionData", selectionData) if (!selectionData?.activeSelection) return null; let selectionDefinitionFirst = ""; let selectionDefinitionSecond = ""; diff --git a/ServicesWeb/customer/src/components/mutual/context/menu/context.ts b/ServicesWeb/customer/src/components/mutual/context/menu/context.ts index 842e494..965bd51 100644 --- a/ServicesWeb/customer/src/components/mutual/context/menu/context.ts +++ b/ServicesWeb/customer/src/components/mutual/context/menu/context.ts @@ -54,6 +54,8 @@ interface UseMenuResult { export function useMenu(): UseMenuResult { const { data, availableItems, isLoading, error, refresh, update } = useContextMenu(); + console.log("data", data); + console.log("availableItems", availableItems); return { menuData: data, diff --git a/ServicesWeb/customer/src/fetchers/custom/context/page/menu/fetch.tsx b/ServicesWeb/customer/src/fetchers/custom/context/page/menu/fetch.tsx index 491ea2a..af7ef26 100644 --- a/ServicesWeb/customer/src/fetchers/custom/context/page/menu/fetch.tsx +++ b/ServicesWeb/customer/src/fetchers/custom/context/page/menu/fetch.tsx @@ -17,7 +17,7 @@ const getMenuFromRedis = async (): Promise => { } catch (error) { if (error instanceof AuthError) { throw error } else { throw new AuthError(error instanceof Error ? error.message : "Unknown error") } } } -const setMenuToRedis = async (menuObject: ClientMenu) => { +const setMenuToRedis = async (menuObject: string[]) => { try { const decrpytUserSelection = await functionRetrieveUserSelection() if (!decrpytUserSelection) throw new AuthError("No user selection found"); @@ -26,7 +26,12 @@ const setMenuToRedis = async (menuObject: ClientMenu) => { if (!menuObject) throw new AuthError("No menu object provided"); const oldData = await getCompleteFromRedis(); if (!oldData) throw new AuthError("No old data found in redis"); - await setCompleteToRedis({ ...oldData, menu: menuObject }); + await setCompleteToRedis({ + ...oldData, menu: { + ...oldData.menu, + selectionList: menuObject, + } + }); return true; } catch (error) { if (error instanceof AuthError) { throw error } else { throw new AuthError(error instanceof Error ? error.message : "Unknown error") } } } diff --git a/ServicesWeb/customer/src/fetchers/custom/login/login.tsx b/ServicesWeb/customer/src/fetchers/custom/login/login.tsx index 42efd2d..4c08f32 100644 --- a/ServicesWeb/customer/src/fetchers/custom/login/login.tsx +++ b/ServicesWeb/customer/src/fetchers/custom/login/login.tsx @@ -16,6 +16,7 @@ import { retrievePageList } from "@/fetchers/mutual/cookies/token"; import { deleteAllCookies } from "@/fetchers/mutual/cookies/cookie-actions"; import { setMenuToRedis } from "@/fetchers/custom/context/page/menu/fetch"; import { LoginViaAccessKeys, LoginSelect } from "@/fetchers/types/login/validations"; +import { retrieveValidUrlsOfRestriction } from "../restriction/fetch"; async function logoutActiveSession() { const response = await fetchDataWithToken(urlLogoutEndpoint, {}, "GET", false); @@ -157,7 +158,8 @@ async function loginSelectEmployee(payload: LoginSelect) { if (selectResponse.status === 200 || selectResponse.status === 202) { try { console.log("selectResponse", selectResponse) // Get Menu URL's of Employee - + const validUrls = await retrieveValidUrlsOfRestriction() + setMenuToRedis(validUrls) } catch (error) { } } return selectResponse; @@ -169,6 +171,8 @@ async function loginSelectOccupant(payload: LoginSelect) { if (selectResponse.status === 200 || selectResponse.status === 202) { try { console.log("selectResponse", selectResponse) // Get Menu URL's of Occupant + const validUrls = await retrieveValidUrlsOfRestriction() + setMenuToRedis(validUrls) } catch (error) { } } return selectResponse; diff --git a/ServicesWeb/customer/src/fetchers/custom/restriction/fetch.tsx b/ServicesWeb/customer/src/fetchers/custom/restriction/fetch.tsx index e69de29..83dced5 100644 --- a/ServicesWeb/customer/src/fetchers/custom/restriction/fetch.tsx +++ b/ServicesWeb/customer/src/fetchers/custom/restriction/fetch.tsx @@ -0,0 +1,13 @@ +"use server"; +import { fetchDataWithToken } from "@/fetchers/fecther"; +import { fetchResponseStatus } from "@/fetchers/utils"; +import { urlSiteUrls } from "@/fetchers/index"; + +async function retrieveValidUrlsOfRestriction() { + const response = await fetchDataWithToken(urlSiteUrls, {}, "GET", false); + if (!fetchResponseStatus(response)) throw new Error("No valid urls found"); + const data = response.data as any; + return data.sites; +} + +export { retrieveValidUrlsOfRestriction } \ No newline at end of file