@collabdt/core 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -21
- package/dist/core/components/AppSidebarContent.d.ts.map +1 -1
- package/dist/core/components/AppSidebarContent.js +21 -27
- package/dist/core/components/AppSidebarContent.js.map +1 -1
- package/dist/core/components/authentication/AuthPage.d.ts.map +1 -1
- package/dist/core/components/authentication/AuthPage.js +32 -7
- package/dist/core/components/authentication/AuthPage.js.map +1 -1
- package/dist/core/components/authentication/SignInAuthHeader.d.ts +7 -0
- package/dist/core/components/authentication/SignInAuthHeader.d.ts.map +1 -0
- package/dist/core/components/authentication/SignInAuthHeader.js +32 -0
- package/dist/core/components/authentication/SignInAuthHeader.js.map +1 -0
- package/dist/core/components/authentication/Signin.d.ts.map +1 -1
- package/dist/core/components/authentication/Signin.js +308 -10
- package/dist/core/components/authentication/Signin.js.map +1 -1
- package/dist/core/components/settings/index.js +3 -3
- package/dist/core/components/settings/index.js.map +1 -1
- package/dist/core/components/settings/src/OrganizationSettingsPanel.d.ts.map +1 -1
- package/dist/core/components/settings/src/OrganizationSettingsPanel.js +5 -4
- package/dist/core/components/settings/src/OrganizationSettingsPanel.js.map +1 -1
- package/dist/core/components/settings/src/SettingsSidebar.d.ts.map +1 -1
- package/dist/core/components/settings/src/SettingsSidebar.js +12 -2
- package/dist/core/components/settings/src/SettingsSidebar.js.map +1 -1
- package/dist/core/components/support/BugReportDialog.d.ts.map +1 -1
- package/dist/core/components/support/BugReportDialog.js +94 -13
- package/dist/core/components/support/BugReportDialog.js.map +1 -1
- package/dist/core/components/support/FeatureRequestDialog.d.ts.map +1 -1
- package/dist/core/components/support/FeatureRequestDialog.js +33 -6
- package/dist/core/components/support/FeatureRequestDialog.js.map +1 -1
- package/dist/core/components/ui/AppSidebar.d.ts.map +1 -1
- package/dist/core/components/ui/AppSidebar.js +52 -9
- package/dist/core/components/ui/AppSidebar.js.map +1 -1
- package/dist/core/components/ui/DataTable.js +3 -3
- package/dist/core/components/ui/DataTable.js.map +1 -1
- package/dist/core/components/ui/Dialog.js +1 -1
- package/dist/core/components/ui/Dialog.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.js +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +2 -2
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js +2 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/sensorUtils.d.ts +9 -0
- package/dist/core/components/ui/Sensors/sensorUtils.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/sensorUtils.js +6 -0
- package/dist/core/components/ui/Sensors/sensorUtils.js.map +1 -1
- package/dist/core/components/ui/Sidebar.d.ts +4 -0
- package/dist/core/components/ui/Sidebar.d.ts.map +1 -1
- package/dist/core/components/ui/Sidebar.js +11 -5
- package/dist/core/components/ui/Sidebar.js.map +1 -1
- package/dist/core/components/ui/Table.js +2 -2
- package/dist/core/components/ui/Table.js.map +1 -1
- package/dist/core/components/viewers/Data/DataMenu.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/DataMenu.js +8 -8
- package/dist/core/components/viewers/Data/DataMenu.js.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.js +26 -0
- package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.js.map +1 -1
- package/dist/core/components/viewers/Data/details/DetailHeader.js +14 -14
- package/dist/core/components/viewers/Data/details/DetailHeader.js.map +1 -1
- package/dist/core/components/viewers/Data/details/TabSidebar.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/details/TabSidebar.js +22 -0
- package/dist/core/components/viewers/Data/details/TabSidebar.js.map +1 -1
- package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.js +19 -0
- package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.js.map +1 -1
- package/dist/core/components/viewers/Data/siteDetails/SiteDetails.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/siteDetails/SiteDetails.js +19 -0
- package/dist/core/components/viewers/Data/siteDetails/SiteDetails.js.map +1 -1
- package/dist/core/components/viewers/Data/utils/Columns.js +2 -2
- package/dist/core/components/viewers/Data/utils/Columns.js.map +1 -1
- package/dist/core/components/viewers/Data/utils/positionParser.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/utils/positionParser.js +6 -5
- package/dist/core/components/viewers/Data/utils/positionParser.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js +8 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js +7 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.js +6 -6
- package/dist/core/components/viewers/map/datasets/RowActions.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/data.js +1 -1
- package/dist/core/components/viewers/map/datasets/data.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.js +28 -9
- package/dist/core/components/viewers/map/datasets/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js +54 -31
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts +2 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js +5 -4
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js.map +1 -1
- package/dist/core/hooks/useCaptureScreenshot.d.ts.map +1 -1
- package/dist/core/hooks/useCaptureScreenshot.js +62 -31
- package/dist/core/hooks/useCaptureScreenshot.js.map +1 -1
- package/dist/core/hooks/useCurrentViewerPosition.d.ts +18 -0
- package/dist/core/hooks/useCurrentViewerPosition.d.ts.map +1 -0
- package/dist/core/hooks/useCurrentViewerPosition.js +66 -0
- package/dist/core/hooks/useCurrentViewerPosition.js.map +1 -0
- package/dist/core/hooks/useShareUrl.d.ts +2 -0
- package/dist/core/hooks/useShareUrl.d.ts.map +1 -0
- package/dist/core/hooks/useShareUrl.js +74 -0
- package/dist/core/hooks/useShareUrl.js.map +1 -0
- package/dist/core/styles/globals.css +46 -33
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/core/components/viewers/Data/DataMenu.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// Dependencies\r\nimport * as React from 'react'\r\nimport { toast } from 'sonner'\r\nimport { useTranslations } from 'next-intl'\r\nimport { handleApiError } from '../../../utils/errorHandler'\r\n\r\n// Utils\r\nimport { useViewerData } from './utils/useViewerData'\r\nimport { VIEWER_CONFIG } from './utils/viewerConfig'\r\n\r\nimport type { Building, Site, User, Infrastructure, Organization } from '../../../types/dbTypes'\r\nimport type { FilterState } from '../../../types/global'\r\n\r\n// Custom hooks\r\nimport { useBuildings, useCreateBuilding } from '../../../hooks/buildings/buildings'\r\nimport { useCreateSite, useSites } from '../../../hooks/sites/sites'\r\nimport { useFiles } from '../../../hooks/files/files'\r\nimport { useUsers } from '../../../hooks/users/users'\r\n\r\n// Context\r\nimport { useMenusContext } from '../../../store'\r\nimport { useBuildingsContext } from '../../../store'\r\nimport { useFilesContext } from '../../../store'\r\n\r\n// Types\r\nimport type { FileRow } from '../../../types/files'\r\nimport {\r\n Breadcrumb,\r\n BreadcrumbItem,\r\n BreadcrumbLink,\r\n BreadcrumbList,\r\n BreadcrumbPage,\r\n BreadcrumbSeparator,\r\n} from '../../ui/Breadcrumb'\r\nimport { Button, Checkbox, DataTable, Input, Separator } from '../../ui/'\r\n\r\nimport type { BuildingDetailsRef } from './buildingDetails/BuildingDetails'\r\nimport type { SiteDetailsRef } from './siteDetails/SiteDetails'\r\nimport type { InfrastructureDetailsRef } from './infrastructureDetails/InfrastructureDetails'\r\n\r\nimport BuildingDetails from './buildingDetails/BuildingDetails'\r\nimport { FileDetails } from './files/FileDetails'\r\n\r\n// Custom Components\r\nimport DetailActions from './details/DetailActions'\r\nimport DetailHeader from './details/DetailHeader'\r\nimport FilterButtons from './FilterButtons'\r\nimport HeaderButtons from './HeaderButtons'\r\nimport MoreOptions from './MoreOptions'\r\nimport SiteDetails from './siteDetails/SiteDetails'\r\nimport UserDetails from './userDetails/UserDetails'\r\n\r\n// Data\r\nimport { useColumns, useUserColumns } from './utils/Columns'\r\nimport { useBuildingHeaders, useInfrastructureHeaders, useSiteHeaders } from './utils/Headers'\r\nimport { DataTypes, DataTypesNames, ViewerNames } from '../../../types'\r\nimport { useSession } from 'next-auth/react'\r\nimport InfrastructureDetails from './infrastructureDetails/InfrastructureDetails'\r\nimport { useCreateInfrastructure, useInfrastructures } from '../../../hooks/infrastructures/infrastructures'\r\n\r\ntype DataMenuProps = {\r\n currentViewer: ViewerNames\r\n height?: string\r\n hideFrame?: boolean\r\n hideTitle?: boolean\r\n hideActions?: boolean\r\n organization?: Organization\r\n geocodeEarthApiKey?: string\r\n geocoderUrl?: string\r\n}\r\n\r\nexport function DataMenu({ currentViewer, height, hideTitle, hideActions, hideFrame, organization, geocodeEarthApiKey, geocoderUrl }: DataMenuProps) {\r\n // Translations\r\n const t = useTranslations('DataMenu')\r\n\r\n // View state\r\n const { view, setView } = useMenusContext()\r\n const { selectedItem, setSelectedItem } = useMenusContext()\r\n const { selectedSite, setSelectedSite } = useMenusContext()\r\n const { selectedInfrastructure, setSelectedInfrastructure } = useMenusContext()\r\n const { selectedFile, setSelectedFile } = useMenusContext()\r\n const { selectedUser, setSelectedUser } = useMenusContext()\r\n const { state: fileState } = useFilesContext()\r\n const { currentFile } = fileState.files\r\n\r\n // Search / filter state\r\n const [searchTerm, setSearchTerm] = React.useState('')\r\n const [activeFilters, setActiveFilters] = React.useState<FilterState[]>([])\r\n const [filters, setFilters] = React.useState([\r\n { id: 1, field: '', fieldType: null, operator: null, value: null, secondValue: null },\r\n ])\r\n\r\n const [dataType, setDataType] = React.useState<DataTypes>(DataTypesNames.building)\r\n const [editing, setEditing] = React.useState(false)\r\n const [activeChanges, setActiveChanges] = React.useState(false)\r\n\r\n // Tab headers\r\n const buildingHeaders = useBuildingHeaders()\r\n const siteHeaders = useSiteHeaders()\r\n const infrastructureHeaders = useInfrastructureHeaders()\r\n\r\n const buildingTabOptions = buildingHeaders.map(section => ({\r\n key: section.title.toLowerCase().replaceAll(/\\s/g, '-'),\r\n label: section.title,\r\n }))\r\n const siteTabOptions = siteHeaders.map(section => ({\r\n key: section.title.toLowerCase().replaceAll(/\\s/g, '-'),\r\n label: section.title,\r\n }))\r\n const infrastructureTabOptions = infrastructureHeaders.map(section => ({\r\n key: section.title.toLowerCase().replaceAll(/\\s/g, '-'),\r\n label: section.title,\r\n }))\r\n\r\n const [activeBuildingTab, setActiveBuildingTab] = React.useState(buildingTabOptions[0]?.key || '')\r\n const [activeSiteTab, setActiveSiteTab] = React.useState(siteTabOptions[0]?.key || '')\r\n const [activeInfrastructureTab, setActiveInfrastructureTab] = React.useState(infrastructureTabOptions[0]?.key || '')\r\n\r\n // Reset active tab only when selected item ID changes\r\n const prevBuildingId = React.useRef<number | undefined>(undefined)\r\n const prevInfrastructureId = React.useRef<number | undefined>(undefined)\r\n\r\n React.useEffect(() => {\r\n if (currentViewer === ViewerNames.buildings && selectedItem?.id !== prevBuildingId.current) {\r\n setActiveBuildingTab(buildingTabOptions[0]?.key || '')\r\n prevBuildingId.current = selectedItem?.id\r\n }\r\n if (currentViewer === ViewerNames.infrastructure && selectedInfrastructure?.id !== prevInfrastructureId.current) {\r\n setActiveInfrastructureTab(infrastructureTabOptions[0]?.key || '')\r\n prevInfrastructureId.current = selectedInfrastructure?.id\r\n }\r\n }, [selectedItem?.id, selectedInfrastructure?.id, currentViewer])\r\n\r\n const [newItemName, setNewItemName] = React.useState('')\r\n\r\n // Compare state\r\n const {\r\n compareItems,\r\n setCompareItems,\r\n comparing,\r\n setComparing,\r\n addItemToCompare,\r\n removeItemFromCompare,\r\n } = useBuildingsContext()\r\n\r\n // Sync dataType with current viewer. ViewerNames are plural ('sites',\r\n // 'buildings', …) while DataTypes are singular ('site', 'building', …), so a\r\n // direct index returns undefined for most viewers — map explicitly.\r\n React.useEffect(() => {\r\n const viewerToDataType: Partial<Record<string, DataTypes>> = {\r\n [ViewerNames.buildings]: DataTypesNames.building,\r\n [ViewerNames.sites]: DataTypesNames.site,\r\n [ViewerNames.infrastructure]: DataTypesNames.infrastructure,\r\n [ViewerNames.files]: DataTypesNames.file,\r\n [ViewerNames.users]: DataTypesNames.user,\r\n }\r\n setDataType(viewerToDataType[currentViewer] ?? DataTypesNames.building)\r\n }, [currentViewer])\r\n\r\n // Reset compare state when viewer changes\r\n React.useEffect(() => {\r\n setComparing(false)\r\n setCompareItems([])\r\n }, [currentViewer, setComparing, setCompareItems])\r\n\r\n // Data fetching\r\n const { buildings, isLoading: buildingsLoading, isError: buildingsError } = useBuildings()\r\n const { sites, isLoading: sitesLoading, isError: sitesError } = useSites()\r\n const { infrastructures, isLoading: infrastructuresLoading, isError: infrastructuresError } = useInfrastructures()\r\n const { files, isLoading: filesLoading, isError: filesError } = useFiles()\r\n const { users, isLoading: usersLoading, isError: usersError } = useUsers()\r\n\r\n const { createError: createSiteError } = useCreateSite()\r\n const { createError: createBuildingError } = useCreateBuilding()\r\n const { createError: createInfrastructureError } = useCreateInfrastructure()\r\n\r\n React.useEffect(() => {\r\n handleApiError(buildingsError)\r\n handleApiError(sitesError)\r\n handleApiError(filesError)\r\n handleApiError(infrastructuresError)\r\n handleApiError(usersError)\r\n handleApiError(createSiteError)\r\n handleApiError(createBuildingError)\r\n handleApiError(createInfrastructureError)\r\n }, [buildingsError, sitesError, infrastructuresError, filesError, usersError, createSiteError, createBuildingError, createInfrastructureError])\r\n\r\n const tableColumns = useColumns(buildings || [])\r\n const userColumns = useUserColumns()\r\n\r\n const { user } = useSession().data\r\n\r\n // Viewer config — resolves icon and i18n title key\r\n const viewerConfig = VIEWER_CONFIG[currentViewer]\r\n const headerTitle = t(viewerConfig.titleKey)\r\n const MenuIcon = viewerConfig.icon\r\n\r\n // Derived data, filtered data, and filter options — all per-viewer logic lives in this hook\r\n const { data, filteredData, filtersOptions, isLoading } = useViewerData({\r\n currentViewer,\r\n buildings,\r\n sites,\r\n infrastructures,\r\n files,\r\n users,\r\n loadingFlags: {\r\n buildings: buildingsLoading,\r\n sites: sitesLoading,\r\n infrastructures: infrastructuresLoading,\r\n files: filesLoading,\r\n users: usersLoading,\r\n },\r\n searchTerm,\r\n activeFilters,\r\n filters,\r\n userEmail: user?.email || '',\r\n })\r\n\r\n const openedCurrentFileIdRef = React.useRef<number | null>(null)\r\n\r\n React.useEffect(() => {\r\n if (currentViewer !== ViewerNames.files || !currentFile) return\r\n if (openedCurrentFileIdRef.current === currentFile.id) return\r\n\r\n const fileRow = data.find((item): item is FileRow => String(item.id) === String(currentFile.id))\r\n if (!fileRow) return\r\n\r\n openedCurrentFileIdRef.current = currentFile.id\r\n setSelectedFile(fileRow)\r\n setView('detail')\r\n }, [currentViewer, currentFile, data, setSelectedFile, setView])\r\n\r\n // Breadcrumb title — derives the detail page label from whichever item is selected\r\n const breadcrumbTitle = React.useMemo(() => {\r\n if (selectedItem) return selectedItem.buildingName || selectedItem.buildingAddress\r\n if (selectedSite) return selectedSite.siteName || selectedSite.siteAddress\r\n if (selectedInfrastructure) return selectedInfrastructure.infrastructureName || selectedInfrastructure.infrastructureType\r\n if (selectedFile) return selectedFile.name\r\n if (selectedUser) return selectedUser.name\r\n if (currentViewer === ViewerNames.extensions) return 'Extension Details'\r\n return undefined\r\n }, [selectedItem, selectedSite, selectedInfrastructure, selectedFile, selectedUser, currentViewer])\r\n\r\n // Row click handlers — consolidated into one function\r\n\r\n const handleRowClick = (item: Building | Site | Infrastructure | FileRow | User) => {\r\n // Files and users don't participate in compare mode\r\n if (comparing && 'id' in item) {\r\n const id = String(item.id)\r\n const isChecked = compareItems.some(i => i.id === id)\r\n if (isChecked) {\r\n removeItemFromCompare(id)\r\n } else {\r\n addItemToCompare({ ...item, id })\r\n }\r\n return\r\n }\r\n\r\n // Navigate to detail view, setting the correct selected item per viewer\r\n switch (currentViewer) {\r\n case ViewerNames.buildings:\r\n setSelectedItem(item as Building)\r\n break\r\n case ViewerNames.sites:\r\n setSelectedSite(item as Site)\r\n break\r\n case ViewerNames.infrastructure:\r\n setSelectedInfrastructure(item as Infrastructure)\r\n break\r\n case ViewerNames.files:\r\n setSelectedFile(item as FileRow)\r\n break\r\n case ViewerNames.users:\r\n setSelectedUser(item as User)\r\n break\r\n }\r\n setView('detail')\r\n }\r\n\r\nconst handleBackToTable = () => {\r\n setView('table')\r\n setSelectedItem(null)\r\n setSelectedSite(null)\r\n setSelectedInfrastructure(null)\r\n setSelectedFile(null)\r\n setSelectedUser(null)\r\n setEditing(false)\r\n setActiveChanges(false)\r\n }\r\n\r\n // Refs for imperative save on child detail components\r\n\r\n const buildingDetailsRef = React.useRef<BuildingDetailsRef>(null)\r\n const siteDetailsRef = React.useRef<SiteDetailsRef>(null)\r\n const infrastructureDetailsRef = React.useRef<InfrastructureDetailsRef>(null)\r\n const fileDetailsRef = React.useRef(null)\r\n const userDetailsRef = React.useRef(null)\r\n\r\n // Save / create handlers — consolidated into one function\r\n\r\n const handleSaveChanges = async () => {\r\n switch (currentViewer) {\r\n case ViewerNames.buildings:\r\n await buildingDetailsRef.current?.saveChanges()\r\n break\r\n case ViewerNames.sites:\r\n await siteDetailsRef.current?.saveChanges()\r\n break\r\n case ViewerNames.infrastructure:\r\n await infrastructureDetailsRef.current?.saveChanges()\r\n break\r\n case ViewerNames.files:\r\n await fileDetailsRef.current?.saveChanges()\r\n break\r\n case ViewerNames.users:\r\n await userDetailsRef.current?.saveChanges()\r\n break\r\n }\r\n }\r\n\r\n /**\r\n * Handles creation of a new entity (id < 0).\r\n * Calls saveChanges on the appropriate ref, then shows a toast.\r\n * Pass successKey as undefined for viewers that show their own success toast (e.g. buildings).\r\n */\r\n const handleCreate = async (\r\n ref: React.RefObject<{ saveChanges: () => Promise<void> }>,\r\n toastKeys: { successKey?: string; errorKey: string },\r\n ) => {\r\n try {\r\n await ref.current?.saveChanges()\r\n setActiveChanges(false)\r\n if (toastKeys.successKey) toast.success(t(toastKeys.successKey))\r\n } catch (error) {\r\n if (error?.status !== 401) toast.error(t(toastKeys.errorKey))\r\n }\r\n }\r\n\r\n // Dispatches to the right ref + toast keys based on which new item is selected\r\n const handleCreateNewItem = async () => {\r\n if (selectedSite && selectedSite.id < 0) {\r\n await handleCreate(siteDetailsRef, {\r\n successKey: 'toastSuccessCreateSite',\r\n errorKey: 'toastFailCreateSite',\r\n })\r\n } else if (selectedItem && selectedItem.id < 0) {\r\n await handleCreate(buildingDetailsRef, {\r\n // Building details shows its own success toast\r\n errorKey: 'toastFailCreateBuilding',\r\n })\r\n } else if (selectedInfrastructure && selectedInfrastructure.id < 0) {\r\n await handleCreate(infrastructureDetailsRef, {\r\n successKey: 'toastSuccessCreateInfrastructure',\r\n errorKey: 'toastFailCreateInfrastructure',\r\n })\r\n } else {\r\n // Users and existing items — standard save\r\n await handleSaveChanges()\r\n }\r\n }\r\n\r\n // Derived UI helpers\r\n\r\n const isNewItem = !!(\r\n (selectedSite && selectedSite.id < 0)\r\n || (selectedItem && selectedItem.id < 0)\r\n || (selectedInfrastructure && selectedInfrastructure.id < 0)\r\n )\r\n\r\n // Render\r\n\r\n return (\r\n <div className={`${hideFrame ? '' : 'sm:p-2'} overflow-hidden ${hideFrame ? '' : 'bg-[#fafafa]'} ${height ? height : 'h-full'}`}>\r\n <div className={`${hideFrame ? '' : 'bg-background rounded-xl shadow'} h-full min-h-0`}>\r\n <div className=\"flex flex-col h-full min-h-0\">\r\n\r\n {/* Breadcrumb Navigation */}\r\n <div className=\"\">\r\n <div className=\"flex flex-row gap-2 justify-start items-center px-3 py-4 relative\">\r\n <Breadcrumb className=\"px-3\">\r\n <BreadcrumbList>\r\n <BreadcrumbItem>\r\n {view === 'table'\r\n ? <BreadcrumbPage>{headerTitle}</BreadcrumbPage>\r\n : (\r\n <BreadcrumbLink onClick={handleBackToTable} className=\"cursor-pointer\">\r\n {headerTitle}\r\n </BreadcrumbLink>\r\n )}\r\n </BreadcrumbItem>\r\n\r\n {view === 'detail' && (\r\n <>\r\n <BreadcrumbSeparator />\r\n <BreadcrumbItem>\r\n <BreadcrumbPage>{breadcrumbTitle}</BreadcrumbPage>\r\n </BreadcrumbItem>\r\n </>\r\n )}\r\n </BreadcrumbList>\r\n </Breadcrumb>\r\n </div>\r\n\r\n {/* Header content based on view */}\r\n {view === 'table' ? (\r\n <>\r\n {/* Title Row */}\r\n {!hideTitle && (\r\n <div className=\"flex flex-row justify-between items-center px-6 py-6\">\r\n <div className=\"flex flex-col gap-1.5\">\r\n <div className=\"flex items-center gap-2\">\r\n <MenuIcon className=\"h-8 w-8\" />\r\n <h1 className=\"text-2xl text-foreground\">{headerTitle}</h1>\r\n </div>\r\n {comparing && (\r\n <h2 className=\"text-sm text-muted-foreground font-normal\">\r\n Choose up to 3 {headerTitle.toLowerCase()} to compare.\r\n </h2>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Search & Filter & Actions Row */}\r\n <div className={`flex ${hideTitle ? 'justify-between' : 'justify-end'} items-center px-6 py-4 gap-4`}>\r\n <div className={hideTitle ? 'w-64' : 'w-96'}>\r\n <Input\r\n placeholder={`${t('searchPlaceholder')} ${headerTitle}...`}\r\n value={searchTerm}\r\n onChange={e => setSearchTerm(e.target.value)}\r\n />\r\n </div>\r\n\r\n <div className=\"flex gap-2\">\r\n <FilterButtons\r\n currentViewer={currentViewer}\r\n filters={filters}\r\n activeFilters={activeFilters}\r\n filterFields={filtersOptions}\r\n data={data}\r\n setFilters={setFilters}\r\n setActiveFilters={setActiveFilters}\r\n />\r\n <HeaderButtons\r\n headerTitle={headerTitle}\r\n currentViewer={currentViewer}\r\n compareItems={compareItems}\r\n setCompareItems={setCompareItems}\r\n comparing={comparing}\r\n setComparing={setComparing}\r\n newItemName={newItemName}\r\n setNewItemName={setNewItemName}\r\n filteredData={\r\n currentViewer === ViewerNames.buildings\r\n || currentViewer === ViewerNames.sites\r\n || currentViewer === ViewerNames.infrastructure\r\n ? filteredData\r\n : undefined\r\n }\r\n users={currentViewer === ViewerNames.users ? users : undefined}\r\n geocodeEarthApiKey={geocodeEarthApiKey}\r\n geocoderUrl={geocoderUrl}\r\n />\r\n </div>\r\n </div>\r\n\r\n <Separator />\r\n </>\r\n ) : (\r\n // Detail View Header\r\n <div className=\"flex justify-between items-center px-6 py-6\">\r\n <DetailHeader\r\n selectedItem={selectedItem}\r\n selectedSite={selectedSite}\r\n selectedInfrastructure={selectedInfrastructure}\r\n selectedFile={selectedFile}\r\n selectedUser={selectedUser}\r\n countryCode={organization?.country}\r\n />\r\n\r\n <DetailActions\r\n editing={editing}\r\n activeChanges={activeChanges}\r\n currentViewer={currentViewer}\r\n selectedItem={selectedItem}\r\n selectedSite={selectedSite}\r\n selectedInfrastructure={selectedInfrastructure}\r\n selectedFile={selectedFile}\r\n selectedUser={selectedUser}\r\n activeBuildingTab={activeBuildingTab}\r\n activeSiteTab={activeSiteTab}\r\n onSave={async () => {\r\n await handleCreateNewItem()\r\n setEditing(false)\r\n }}\r\n onEdit={() => {\r\n setEditing(true)\r\n if (isNewItem) setActiveChanges(true)\r\n }}\r\n onCancel={() => {\r\n setEditing(false)\r\n if (isNewItem) handleBackToTable()\r\n }}\r\n onDeleteUser={() => {\r\n setSelectedUser(null)\r\n setView('table')\r\n }}\r\n setView={setView}\r\n />\r\n </div>\r\n )}\r\n\r\n {/* Detail view separator */}\r\n {view === 'detail' && <Separator />}\r\n </div>\r\n\r\n {/* Scrollable Content Area */}\r\n <div className=\"flex-1 overflow-hidden\">\r\n {view === 'table' ? (\r\n <div className=\"h-full flex flex-col md:p-6 overflow-auto\">\r\n <DataTable\r\n columns={currentViewer === ViewerNames.users ? userColumns : tableColumns}\r\n data={filteredData}\r\n onRowClick={handleRowClick}\r\n currentViewer={currentViewer}\r\n isLoading={isLoading}\r\n leadingCell={comparing\r\n ? ({ dataset }) => {\r\n const item = dataset\r\n const isChecked = compareItems.some(i => i?.id === String(item?.id))\r\n const maxReached = compareItems.length >= 3 && !isChecked\r\n\r\n return (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"transition-opacity duration-200 hover:bg-transparent\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n if (isChecked) {\r\n removeItemFromCompare?.(String(item.id))\r\n } else {\r\n addItemToCompare?.({ ...item, id: String(item.id) })\r\n }\r\n }}\r\n >\r\n <Checkbox\r\n checked={isChecked}\r\n disabled={maxReached}\r\n className={`opacity-70 hover:opacity-100 data-[state=checked]:opacity-100 ${maxReached && '!cursor-default'}`}\r\n />\r\n </Button>\r\n )\r\n }\r\n : undefined}\r\n trailingCell={({ row }) => (\r\n <div onClick={e => e.stopPropagation()}>\r\n <MoreOptions\r\n row={row.original}\r\n dataType={dataType}\r\n hideViewOnMap={currentViewer === ViewerNames.users}\r\n setView={setView}\r\n onDeleteUser={() => setSelectedUser(null)}\r\n />\r\n </div>\r\n )}\r\n />\r\n </div>\r\n ) : (\r\n <div className=\"h-full overflow-auto\">\r\n {selectedFile\r\n ? (\r\n <FileDetails\r\n selectedFile={selectedFile}\r\n buildings={buildings || []}\r\n editing={editing}\r\n setEditing={setEditing}\r\n setActiveChanges={setActiveChanges}\r\n ref={fileDetailsRef}\r\n />\r\n )\r\n : currentViewer === ViewerNames.buildings\r\n ? (\r\n <BuildingDetails\r\n selectedItem={selectedItem}\r\n setSelectedItem={setSelectedItem}\r\n editing={editing}\r\n setEditing={setEditing}\r\n setActiveChanges={setActiveChanges}\r\n buildings={buildings || []}\r\n ref={buildingDetailsRef}\r\n activeTab={activeBuildingTab}\r\n setActiveTab={setActiveBuildingTab}\r\n />\r\n )\r\n : currentViewer === ViewerNames.sites\r\n ? (\r\n <SiteDetails\r\n selectedSite={selectedSite}\r\n setSelectedSite={setSelectedSite}\r\n editing={editing}\r\n setEditing={setEditing}\r\n setActiveChanges={setActiveChanges}\r\n sites={sites || []}\r\n ref={siteDetailsRef}\r\n activeTab={activeSiteTab}\r\n setActiveTab={setActiveSiteTab}\r\n />\r\n )\r\n : currentViewer === ViewerNames.infrastructure\r\n ? (\r\n <InfrastructureDetails\r\n selectedInfrastructure={selectedInfrastructure}\r\n setSelectedInfrastructure={setSelectedInfrastructure}\r\n editing={editing}\r\n setEditing={setEditing}\r\n setActiveChanges={setActiveChanges}\r\n infrastructures={infrastructures || []}\r\n ref={infrastructureDetailsRef}\r\n activeTab={activeInfrastructureTab}\r\n setActiveTab={setActiveInfrastructureTab}\r\n />\r\n )\r\n : currentViewer === ViewerNames.users\r\n ? (\r\n <UserDetails\r\n selectedUser={selectedUser}\r\n setSelectedUser={setSelectedUser}\r\n editing={editing}\r\n setEditing={setEditing}\r\n setActiveChanges={setActiveChanges}\r\n users={users || []}\r\n ref={userDetailsRef}\r\n hideTitle={hideTitle}\r\n />\r\n )\r\n : null}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoYwB,SASJ,UATI,KASJ,YATI;AA9XxB,YAAY,WAAW;AACvB,SAAS,aAAa;AACtB,SAAS,uBAAuB;AAChC,SAAS,sBAAsB;AAG/B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAM9B,SAAS,cAAc,yBAAyB;AAChD,SAAS,eAAe,gBAAgB;AACxC,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AAGzB,SAAS,uBAAuB;AAChC,SAAS,2BAA2B;AACpC,SAAS,uBAAuB;AAIhC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,QAAQ,UAAU,WAAW,OAAO,iBAAiB;AAM9D,OAAO,qBAAqB;AAC5B,SAAS,mBAAmB;AAG5B,OAAO,mBAAmB;AAC1B,OAAO,kBAAkB;AACzB,OAAO,mBAAmB;AAC1B,OAAO,mBAAmB;AAC1B,OAAO,iBAAiB;AACxB,OAAO,iBAAiB;AACxB,OAAO,iBAAiB;AAGxB,SAAS,YAAY,sBAAsB;AAC3C,SAAS,oBAAoB,0BAA0B,sBAAsB;AAC7E,SAAoB,gBAAgB,mBAAmB;AACvD,SAAS,kBAAkB;AAC3B,OAAO,2BAA2B;AAClC,SAAS,yBAAyB,0BAA0B;AAarD,SAAS,SAAS,EAAE,eAAe,QAAQ,WAAW,aAAa,WAAW,cAAc,oBAAoB,YAAY,GAAkB;AA5ErJ;AA8EE,QAAM,IAAI,gBAAgB,UAAU;AAGpC,QAAM,EAAE,MAAM,QAAQ,IAAI,gBAAgB;AAC1C,QAAM,EAAE,cAAc,gBAAgB,IAAI,gBAAgB;AAC1D,QAAM,EAAE,cAAc,gBAAgB,IAAI,gBAAgB;AAC1D,QAAM,EAAE,wBAAwB,0BAA0B,IAAI,gBAAgB;AAC9E,QAAM,EAAE,cAAc,gBAAgB,IAAI,gBAAgB;AAC1D,QAAM,EAAE,cAAc,gBAAgB,IAAI,gBAAgB;AAC1D,QAAM,EAAE,OAAO,UAAU,IAAI,gBAAgB;AAC7C,QAAM,EAAE,YAAY,IAAI,UAAU;AAGlC,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,EAAE;AACrD,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAwB,CAAC,CAAC;AAC1E,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS;AAAA,IAC3C,EAAE,IAAI,GAAG,OAAO,IAAI,WAAW,MAAM,UAAU,MAAM,OAAO,MAAM,aAAa,KAAK;AAAA,EACtF,CAAC;AAED,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAoB,eAAe,QAAQ;AACjF,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAS,KAAK;AAG9D,QAAM,kBAAkB,mBAAmB;AAC3C,QAAM,cAAc,eAAe;AACnC,QAAM,wBAAwB,yBAAyB;AAEvD,QAAM,qBAAqB,gBAAgB,IAAI,cAAY;AAAA,IACzD,KAAK,QAAQ,MAAM,YAAY,EAAE,WAAW,OAAO,GAAG;AAAA,IACtD,OAAO,QAAQ;AAAA,EACjB,EAAE;AACF,QAAM,iBAAiB,YAAY,IAAI,cAAY;AAAA,IACjD,KAAK,QAAQ,MAAM,YAAY,EAAE,WAAW,OAAO,GAAG;AAAA,IACtD,OAAO,QAAQ;AAAA,EACjB,EAAE;AACF,QAAM,2BAA2B,sBAAsB,IAAI,cAAY;AAAA,IACrE,KAAK,QAAQ,MAAM,YAAY,EAAE,WAAW,OAAO,GAAG;AAAA,IACtD,OAAO,QAAQ;AAAA,EACjB,EAAE;AAEF,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,WAAS,wBAAmB,CAAC,MAApB,mBAAuB,QAAO,EAAE;AACjG,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,WAAS,oBAAe,CAAC,MAAhB,mBAAmB,QAAO,EAAE;AACrF,QAAM,CAAC,yBAAyB,0BAA0B,IAAI,MAAM,WAAS,8BAAyB,CAAC,MAA1B,mBAA6B,QAAO,EAAE;AAGnH,QAAM,iBAAiB,MAAM,OAA2B,MAAS;AACjE,QAAM,uBAAuB,MAAM,OAA2B,MAAS;AAEvE,QAAM,UAAU,MAAM;AA/HxB,QAAAA,KAAAC;AAgII,QAAI,kBAAkB,YAAY,cAAa,6CAAc,QAAO,eAAe,SAAS;AAC1F,6BAAqBD,MAAA,mBAAmB,CAAC,MAApB,gBAAAA,IAAuB,QAAO,EAAE;AACrD,qBAAe,UAAU,6CAAc;AAAA,IACzC;AACA,QAAI,kBAAkB,YAAY,mBAAkB,iEAAwB,QAAO,qBAAqB,SAAS;AAC/G,mCAA2BC,MAAA,yBAAyB,CAAC,MAA1B,gBAAAA,IAA6B,QAAO,EAAE;AACjE,2BAAqB,UAAU,iEAAwB;AAAA,IACzD;AAAA,EACF,GAAG,CAAC,6CAAc,IAAI,iEAAwB,IAAI,aAAa,CAAC;AAEhE,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,EAAE;AAGvD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,oBAAoB;AAKxB,QAAM,UAAU,MAAM;AAzJxB,QAAAD;AA0JI,UAAM,mBAAuD;AAAA,MAC3D,CAAC,YAAY,SAAS,GAAG,eAAe;AAAA,MACxC,CAAC,YAAY,KAAK,GAAG,eAAe;AAAA,MACpC,CAAC,YAAY,cAAc,GAAG,eAAe;AAAA,MAC7C,CAAC,YAAY,KAAK,GAAG,eAAe;AAAA,MACpC,CAAC,YAAY,KAAK,GAAG,eAAe;AAAA,IACtC;AACA,iBAAYA,MAAA,iBAAiB,aAAa,MAA9B,OAAAA,MAAmC,eAAe,QAAQ;AAAA,EACxE,GAAG,CAAC,aAAa,CAAC;AAGlB,QAAM,UAAU,MAAM;AACpB,iBAAa,KAAK;AAClB,oBAAgB,CAAC,CAAC;AAAA,EACpB,GAAG,CAAC,eAAe,cAAc,eAAe,CAAC;AAGjD,QAAM,EAAE,WAAW,WAAW,kBAAkB,SAAS,eAAe,IAAI,aAAa;AACzF,QAAM,EAAE,OAAO,WAAW,cAAc,SAAS,WAAW,IAAI,SAAS;AACzE,QAAM,EAAE,iBAAiB,WAAW,wBAAwB,SAAS,qBAAqB,IAAI,mBAAmB;AACjH,QAAM,EAAE,OAAO,WAAW,cAAc,SAAS,WAAW,IAAI,SAAS;AACzE,QAAM,EAAE,OAAO,WAAW,cAAc,SAAS,WAAW,IAAI,SAAS;AAEzE,QAAM,EAAE,aAAa,gBAAgB,IAAI,cAAc;AACvD,QAAM,EAAE,aAAa,oBAAoB,IAAI,kBAAkB;AAC/D,QAAM,EAAE,aAAa,0BAA0B,IAAI,wBAAwB;AAE3E,QAAM,UAAU,MAAM;AACpB,mBAAe,cAAc;AAC7B,mBAAe,UAAU;AACzB,mBAAe,UAAU;AACzB,mBAAe,oBAAoB;AACnC,mBAAe,UAAU;AACzB,mBAAe,eAAe;AAC9B,mBAAe,mBAAmB;AAClC,mBAAe,yBAAyB;AAAA,EAC1C,GAAG,CAAC,gBAAgB,YAAY,sBAAsB,YAAY,YAAY,iBAAiB,qBAAqB,yBAAyB,CAAC;AAE9I,QAAM,eAAe,WAAW,aAAa,CAAC,CAAC;AAC/C,QAAM,cAAc,eAAe;AAEnC,QAAM,EAAE,KAAK,IAAI,WAAW,EAAE;AAG9B,QAAM,eAAe,cAAc,aAAa;AAChD,QAAM,cAAc,EAAE,aAAa,QAAQ;AAC3C,QAAM,WAAW,aAAa;AAG9B,QAAM,EAAE,MAAM,cAAc,gBAAgB,UAAU,IAAI,cAAc;AAAA,IACtE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,MACZ,WAAW;AAAA,MACX,OAAO;AAAA,MACP,iBAAiB;AAAA,MACjB,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAW,6BAAM,UAAS;AAAA,EAC5B,CAAC;AAED,QAAM,yBAAyB,MAAM,OAAsB,IAAI;AAE/D,QAAM,UAAU,MAAM;AACpB,QAAI,kBAAkB,YAAY,SAAS,CAAC,YAAa;AACzD,QAAI,uBAAuB,YAAY,YAAY,GAAI;AAEvD,UAAM,UAAU,KAAK,KAAK,CAAC,SAA0B,OAAO,KAAK,EAAE,MAAM,OAAO,YAAY,EAAE,CAAC;AAC/F,QAAI,CAAC,QAAS;AAEd,2BAAuB,UAAU,YAAY;AAC7C,oBAAgB,OAAO;AACvB,YAAQ,QAAQ;AAAA,EAClB,GAAG,CAAC,eAAe,aAAa,MAAM,iBAAiB,OAAO,CAAC;AAG/D,QAAM,kBAAkB,MAAM,QAAQ,MAAM;AAC1C,QAAI,aAAc,QAAO,aAAa,gBAAgB,aAAa;AACnE,QAAI,aAAc,QAAO,aAAa,YAAY,aAAa;AAC/D,QAAI,uBAAwB,QAAO,uBAAuB,sBAAsB,uBAAuB;AACvG,QAAI,aAAc,QAAO,aAAa;AACtC,QAAI,aAAc,QAAO,aAAa;AACtC,QAAI,kBAAkB,YAAY,WAAY,QAAO;AACrD,WAAO;AAAA,EACT,GAAG,CAAC,cAAc,cAAc,wBAAwB,cAAc,cAAc,aAAa,CAAC;AAIlG,QAAM,iBAAiB,CAAC,SAA4D;AAElF,QAAI,aAAa,QAAQ,MAAM;AAC7B,YAAM,KAAK,OAAO,KAAK,EAAE;AACzB,YAAM,YAAY,aAAa,KAAK,OAAK,EAAE,OAAO,EAAE;AACpD,UAAI,WAAW;AACb,8BAAsB,EAAE;AAAA,MAC1B,OAAO;AACL,yBAAiB,iCAAK,OAAL,EAAW,GAAG,EAAC;AAAA,MAClC;AACA;AAAA,IACF;AAGA,YAAQ,eAAe;AAAA,MACrB,KAAK,YAAY;AACf,wBAAgB,IAAgB;AAChC;AAAA,MACF,KAAK,YAAY;AACf,wBAAgB,IAAY;AAC5B;AAAA,MACF,KAAK,YAAY;AACf,kCAA0B,IAAsB;AAChD;AAAA,MACF,KAAK,YAAY;AACf,wBAAgB,IAAe;AAC/B;AAAA,MACF,KAAK,YAAY;AACf,wBAAgB,IAAY;AAC5B;AAAA,IACJ;AACA,YAAQ,QAAQ;AAAA,EAClB;AAEF,QAAM,oBAAoB,MAAM;AAC5B,YAAQ,OAAO;AACf,oBAAgB,IAAI;AACpB,oBAAgB,IAAI;AACpB,8BAA0B,IAAI;AAC9B,oBAAgB,IAAI;AACpB,oBAAgB,IAAI;AACpB,eAAW,KAAK;AAChB,qBAAiB,KAAK;AAAA,EACxB;AAIA,QAAM,qBAAqB,MAAM,OAA2B,IAAI;AAChE,QAAM,iBAAiB,MAAM,OAAuB,IAAI;AACxD,QAAM,2BAA2B,MAAM,OAAiC,IAAI;AAC5E,QAAM,iBAAiB,MAAM,OAAO,IAAI;AACxC,QAAM,iBAAiB,MAAM,OAAO,IAAI;AAIxC,QAAM,oBAAoB,YAAY;AAjTxC,QAAAA,KAAAC,KAAAC,KAAA;AAkTI,YAAQ,eAAe;AAAA,MACrB,KAAK,YAAY;AACf,gBAAMF,MAAA,mBAAmB,YAAnB,gBAAAA,IAA4B;AAClC;AAAA,MACF,KAAK,YAAY;AACf,gBAAMC,MAAA,eAAe,YAAf,gBAAAA,IAAwB;AAC9B;AAAA,MACF,KAAK,YAAY;AACf,gBAAMC,MAAA,yBAAyB,YAAzB,gBAAAA,IAAkC;AACxC;AAAA,MACF,KAAK,YAAY;AACf,gBAAM,oBAAe,YAAf,mBAAwB;AAC9B;AAAA,MACF,KAAK,YAAY;AACf,gBAAM,oBAAe,YAAf,mBAAwB;AAC9B;AAAA,IACJ;AAAA,EACF;AAOA,QAAM,eAAe,OACnB,KACA,cACG;AA7UP,QAAAF;AA8UI,QAAI;AACF,cAAMA,MAAA,IAAI,YAAJ,gBAAAA,IAAa;AACnB,uBAAiB,KAAK;AACtB,UAAI,UAAU,WAAY,OAAM,QAAQ,EAAE,UAAU,UAAU,CAAC;AAAA,IACjE,SAAS,OAAO;AACd,WAAI,+BAAO,YAAW,IAAK,OAAM,MAAM,EAAE,UAAU,QAAQ,CAAC;AAAA,IAC9D;AAAA,EACF;AAGA,QAAM,sBAAsB,YAAY;AACtC,QAAI,gBAAgB,aAAa,KAAK,GAAG;AACvC,YAAM,aAAa,gBAAgB;AAAA,QACjC,YAAY;AAAA,QACZ,UAAU;AAAA,MACZ,CAAC;AAAA,IACH,WAAW,gBAAgB,aAAa,KAAK,GAAG;AAC9C,YAAM,aAAa,oBAAoB;AAAA;AAAA,QAErC,UAAU;AAAA,MACZ,CAAC;AAAA,IACH,WAAW,0BAA0B,uBAAuB,KAAK,GAAG;AAClE,YAAM,aAAa,0BAA0B;AAAA,QAC3C,YAAY;AAAA,QACZ,UAAU;AAAA,MACZ,CAAC;AAAA,IACH,OAAO;AAEL,YAAM,kBAAkB;AAAA,IAC1B;AAAA,EACF;AAIA,QAAM,YAAY,CAAC,EAChB,gBAAgB,aAAa,KAAK,KAC/B,gBAAgB,aAAa,KAAK,KAClC,0BAA0B,uBAAuB,KAAK;AAK5D,SACE,oBAAC,SAAI,WAAW,GAAG,YAAY,KAAK,QAAQ,oBAAoB,YAAY,KAAK,cAAc,IAAI,SAAS,SAAS,QAAQ,IAC3H,8BAAC,SAAI,WAAW,GAAG,YAAY,KAAK,iCAAiC,mBACnE,+BAAC,SAAI,WAAU,gCAGb;AAAA,yBAAC,SAAI,WAAU,IACb;AAAA,0BAAC,SAAI,WAAU,qEACb,8BAAC,cAAW,WAAU,QACpB,+BAAC,kBACC;AAAA,4BAAC,kBACE,mBAAS,UACN,oBAAC,kBAAgB,uBAAY,IAE7B,oBAAC,kBAAe,SAAS,mBAAmB,WAAU,kBACnD,uBACH,GAEN;AAAA,QAEC,SAAS,YACR,iCACE;AAAA,8BAAC,uBAAoB;AAAA,UACrB,oBAAC,kBACC,8BAAC,kBAAgB,2BAAgB,GACnC;AAAA,WACF;AAAA,SAEJ,GACF,GACF;AAAA,MAGC,SAAS,UACR,iCAEG;AAAA,SAAC,aACA,oBAAC,SAAI,WAAU,wDACb,+BAAC,SAAI,WAAU,yBACb;AAAA,+BAAC,SAAI,WAAU,2BACb;AAAA,gCAAC,YAAS,WAAU,WAAU;AAAA,YAC9B,oBAAC,QAAG,WAAU,4BAA4B,uBAAY;AAAA,aACxD;AAAA,UACC,aACC,qBAAC,QAAG,WAAU,6CAA4C;AAAA;AAAA,YACxC,YAAY,YAAY;AAAA,YAAE;AAAA,aAC5C;AAAA,WAEJ,GACF;AAAA,QAIF,qBAAC,SAAI,WAAW,QAAQ,YAAY,oBAAoB,aAAa,iCACnE;AAAA,8BAAC,SAAI,WAAW,YAAY,SAAS,QACnC;AAAA,YAAC;AAAA;AAAA,cACC,aAAa,GAAG,EAAE,mBAAmB,CAAC,IAAI,WAAW;AAAA,cACrD,OAAO;AAAA,cACP,UAAU,OAAK,cAAc,EAAE,OAAO,KAAK;AAAA;AAAA,UAC7C,GACF;AAAA,UAEA,qBAAC,SAAI,WAAU,cACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,cAAc;AAAA,gBACd;AAAA,gBACA;AAAA,gBACA;AAAA;AAAA,YACF;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,cACE,kBAAkB,YAAY,aAC3B,kBAAkB,YAAY,SAC9B,kBAAkB,YAAY,iBAC7B,eACA;AAAA,gBAEN,OAAO,kBAAkB,YAAY,QAAQ,QAAQ;AAAA,gBACrD;AAAA,gBACA;AAAA;AAAA,YACF;AAAA,aACF;AAAA,WACF;AAAA,QAEA,oBAAC,aAAU;AAAA,SACb;AAAA;AAAA,QAGA,qBAAC,SAAI,WAAU,+CACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,aAAa,6CAAc;AAAA;AAAA,UAC7B;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,QAAQ,YAAY;AAClB,sBAAM,oBAAoB;AAC1B,2BAAW,KAAK;AAAA,cAClB;AAAA,cACA,QAAQ,MAAM;AACZ,2BAAW,IAAI;AACf,oBAAI,UAAW,kBAAiB,IAAI;AAAA,cACtC;AAAA,cACA,UAAU,MAAM;AACd,2BAAW,KAAK;AAChB,oBAAI,UAAW,mBAAkB;AAAA,cACnC;AAAA,cACA,cAAc,MAAM;AAClB,gCAAgB,IAAI;AACpB,wBAAQ,OAAO;AAAA,cACjB;AAAA,cACA;AAAA;AAAA,UACF;AAAA,WACF;AAAA;AAAA,MAID,SAAS,YAAY,oBAAC,aAAU;AAAA,OACnC;AAAA,IAGA,oBAAC,SAAI,WAAU,0BACZ,mBAAS,UACR,oBAAC,SAAI,WAAU,6CACb;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,kBAAkB,YAAY,QAAQ,cAAc;AAAA,QAC7D,MAAM;AAAA,QACN,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA,aAAa,YACT,CAAC,EAAE,QAAQ,MAAM;AACjB,gBAAM,OAAO;AACb,gBAAM,YAAY,aAAa,KAAK,QAAK,uBAAG,QAAO,OAAO,6BAAM,EAAE,CAAC;AACnE,gBAAM,aAAa,aAAa,UAAU,KAAK,CAAC;AAEhD,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,WAAU;AAAA,cACV,SAAS,CAAC,MAAM;AACd,kBAAE,gBAAgB;AAClB,oBAAI,WAAW;AACb,iFAAwB,OAAO,KAAK,EAAE;AAAA,gBACxC,OAAO;AACL,uEAAmB,iCAAK,OAAL,EAAW,IAAI,OAAO,KAAK,EAAE,EAAE;AAAA,gBACpD;AAAA,cACF;AAAA,cAEA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS;AAAA,kBACT,UAAU;AAAA,kBACV,WAAW,iEAAiE,cAAc,iBAAiB;AAAA;AAAA,cAC7G;AAAA;AAAA,UACF;AAAA,QAEJ,IACE;AAAA,QACJ,cAAc,CAAC,EAAE,IAAI,MACnB,oBAAC,SAAI,SAAS,OAAK,EAAE,gBAAgB,GACnC;AAAA,UAAC;AAAA;AAAA,YACC,KAAK,IAAI;AAAA,YACT;AAAA,YACA,eAAe,kBAAkB,YAAY;AAAA,YAC7C;AAAA,YACA,cAAc,MAAM,gBAAgB,IAAI;AAAA;AAAA,QAC1C,GACF;AAAA;AAAA,IAEJ,GACF,IAEA,oBAAC,SAAI,WAAU,wBACZ,yBAEG;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,WAAW,aAAa,CAAC;AAAA,QACzB;AAAA,QACA;AAAA,QACA;AAAA,QACA,KAAK;AAAA;AAAA,IACP,IAEA,kBAAkB,YAAY,YAE5B;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW,aAAa,CAAC;AAAA,QACzB,KAAK;AAAA,QACL,WAAW;AAAA,QACX,cAAc;AAAA;AAAA,IAChB,IAEA,kBAAkB,YAAY,QAE5B;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,SAAS,CAAC;AAAA,QACjB,KAAK;AAAA,QACL,WAAW;AAAA,QACX,cAAc;AAAA;AAAA,IAChB,IAEA,kBAAkB,YAAY,iBAE5B;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,iBAAiB,mBAAmB,CAAC;AAAA,QACrC,KAAK;AAAA,QACL,WAAW;AAAA,QACX,cAAc;AAAA;AAAA,IAChB,IAEA,kBAAkB,YAAY,QAE5B;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,SAAS,CAAC;AAAA,QACjB,KAAK;AAAA,QACL;AAAA;AAAA,IACF,IAEA,MACd,GAEJ;AAAA,KACF,GACF,GACF;AAEJ;","names":["_a","_b","_c"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/core/components/viewers/Data/DataMenu.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// Dependencies\r\nimport * as React from 'react'\r\nimport { toast } from 'sonner'\r\nimport { useTranslations } from 'next-intl'\r\nimport { handleApiError } from '../../../utils/errorHandler'\r\n\r\n// Utils\r\nimport { useViewerData } from './utils/useViewerData'\r\nimport { VIEWER_CONFIG } from './utils/viewerConfig'\r\n\r\nimport type { Building, Site, User, Infrastructure, Organization } from '../../../types/dbTypes'\r\nimport type { FilterState } from '../../../types/global'\r\n\r\n// Custom hooks\r\nimport { useBuildings, useCreateBuilding } from '../../../hooks/buildings/buildings'\r\nimport { useCreateSite, useSites } from '../../../hooks/sites/sites'\r\nimport { useFiles } from '../../../hooks/files/files'\r\nimport { useUsers } from '../../../hooks/users/users'\r\n\r\n// Context\r\nimport { useMenusContext } from '../../../store'\r\nimport { useBuildingsContext } from '../../../store'\r\nimport { useFilesContext } from '../../../store'\r\n\r\n// Types\r\nimport type { FileRow } from '../../../types/files'\r\nimport {\r\n Breadcrumb,\r\n BreadcrumbItem,\r\n BreadcrumbLink,\r\n BreadcrumbList,\r\n BreadcrumbPage,\r\n BreadcrumbSeparator,\r\n} from '../../ui/Breadcrumb'\r\nimport { Button, Checkbox, DataTable, Input, Separator } from '../../ui/'\r\n\r\nimport type { BuildingDetailsRef } from './buildingDetails/BuildingDetails'\r\nimport type { SiteDetailsRef } from './siteDetails/SiteDetails'\r\nimport type { InfrastructureDetailsRef } from './infrastructureDetails/InfrastructureDetails'\r\n\r\nimport BuildingDetails from './buildingDetails/BuildingDetails'\r\nimport { FileDetails } from './files/FileDetails'\r\n\r\n// Custom Components\r\nimport DetailActions from './details/DetailActions'\r\nimport DetailHeader from './details/DetailHeader'\r\nimport FilterButtons from './FilterButtons'\r\nimport HeaderButtons from './HeaderButtons'\r\nimport MoreOptions from './MoreOptions'\r\nimport SiteDetails from './siteDetails/SiteDetails'\r\nimport UserDetails from './userDetails/UserDetails'\r\n\r\n// Data\r\nimport { useColumns, useUserColumns } from './utils/Columns'\r\nimport { useBuildingHeaders, useInfrastructureHeaders, useSiteHeaders } from './utils/Headers'\r\nimport { DataTypes, DataTypesNames, ViewerNames } from '../../../types'\r\nimport { useSession } from 'next-auth/react'\r\nimport InfrastructureDetails from './infrastructureDetails/InfrastructureDetails'\r\nimport { useCreateInfrastructure, useInfrastructures } from '../../../hooks/infrastructures/infrastructures'\r\n\r\ntype DataMenuProps = {\r\n currentViewer: ViewerNames\r\n height?: string\r\n hideFrame?: boolean\r\n hideTitle?: boolean\r\n hideActions?: boolean\r\n organization?: Organization\r\n geocodeEarthApiKey?: string\r\n geocoderUrl?: string\r\n}\r\n\r\nexport function DataMenu({ currentViewer, height, hideTitle, hideActions, hideFrame, organization, geocodeEarthApiKey, geocoderUrl }: DataMenuProps) {\r\n // Translations\r\n const t = useTranslations('DataMenu')\r\n\r\n // View state\r\n const { view, setView } = useMenusContext()\r\n const { selectedItem, setSelectedItem } = useMenusContext()\r\n const { selectedSite, setSelectedSite } = useMenusContext()\r\n const { selectedInfrastructure, setSelectedInfrastructure } = useMenusContext()\r\n const { selectedFile, setSelectedFile } = useMenusContext()\r\n const { selectedUser, setSelectedUser } = useMenusContext()\r\n const { state: fileState } = useFilesContext()\r\n const { currentFile } = fileState.files\r\n\r\n // Search / filter state\r\n const [searchTerm, setSearchTerm] = React.useState('')\r\n const [activeFilters, setActiveFilters] = React.useState<FilterState[]>([])\r\n const [filters, setFilters] = React.useState([\r\n { id: 1, field: '', fieldType: null, operator: null, value: null, secondValue: null },\r\n ])\r\n\r\n const [dataType, setDataType] = React.useState<DataTypes>(DataTypesNames.building)\r\n const [editing, setEditing] = React.useState(false)\r\n const [activeChanges, setActiveChanges] = React.useState(false)\r\n\r\n // Tab headers\r\n const buildingHeaders = useBuildingHeaders()\r\n const siteHeaders = useSiteHeaders()\r\n const infrastructureHeaders = useInfrastructureHeaders()\r\n\r\n const buildingTabOptions = buildingHeaders.map(section => ({\r\n key: section.title.toLowerCase().replaceAll(/\\s/g, '-'),\r\n label: section.title,\r\n }))\r\n const siteTabOptions = siteHeaders.map(section => ({\r\n key: section.title.toLowerCase().replaceAll(/\\s/g, '-'),\r\n label: section.title,\r\n }))\r\n const infrastructureTabOptions = infrastructureHeaders.map(section => ({\r\n key: section.title.toLowerCase().replaceAll(/\\s/g, '-'),\r\n label: section.title,\r\n }))\r\n\r\n const [activeBuildingTab, setActiveBuildingTab] = React.useState(buildingTabOptions[0]?.key || '')\r\n const [activeSiteTab, setActiveSiteTab] = React.useState(siteTabOptions[0]?.key || '')\r\n const [activeInfrastructureTab, setActiveInfrastructureTab] = React.useState(infrastructureTabOptions[0]?.key || '')\r\n\r\n // Reset active tab only when selected item ID changes\r\n const prevBuildingId = React.useRef<number | undefined>(undefined)\r\n const prevInfrastructureId = React.useRef<number | undefined>(undefined)\r\n\r\n React.useEffect(() => {\r\n if (currentViewer === ViewerNames.buildings && selectedItem?.id !== prevBuildingId.current) {\r\n setActiveBuildingTab(buildingTabOptions[0]?.key || '')\r\n prevBuildingId.current = selectedItem?.id\r\n }\r\n if (currentViewer === ViewerNames.infrastructure && selectedInfrastructure?.id !== prevInfrastructureId.current) {\r\n setActiveInfrastructureTab(infrastructureTabOptions[0]?.key || '')\r\n prevInfrastructureId.current = selectedInfrastructure?.id\r\n }\r\n }, [selectedItem?.id, selectedInfrastructure?.id, currentViewer])\r\n\r\n const [newItemName, setNewItemName] = React.useState('')\r\n\r\n // Compare state\r\n const {\r\n compareItems,\r\n setCompareItems,\r\n comparing,\r\n setComparing,\r\n addItemToCompare,\r\n removeItemFromCompare,\r\n } = useBuildingsContext()\r\n\r\n // Sync dataType with current viewer. ViewerNames are plural ('sites',\r\n // 'buildings', …) while DataTypes are singular ('site', 'building', …), so a\r\n // direct index returns undefined for most viewers — map explicitly.\r\n React.useEffect(() => {\r\n const viewerToDataType: Partial<Record<string, DataTypes>> = {\r\n [ViewerNames.buildings]: DataTypesNames.building,\r\n [ViewerNames.sites]: DataTypesNames.site,\r\n [ViewerNames.infrastructure]: DataTypesNames.infrastructure,\r\n [ViewerNames.files]: DataTypesNames.file,\r\n [ViewerNames.users]: DataTypesNames.user,\r\n }\r\n setDataType(viewerToDataType[currentViewer] ?? DataTypesNames.building)\r\n }, [currentViewer])\r\n\r\n // Reset compare state when viewer changes\r\n React.useEffect(() => {\r\n setComparing(false)\r\n setCompareItems([])\r\n }, [currentViewer, setComparing, setCompareItems])\r\n\r\n // Data fetching\r\n const { buildings, isLoading: buildingsLoading, isError: buildingsError } = useBuildings()\r\n const { sites, isLoading: sitesLoading, isError: sitesError } = useSites()\r\n const { infrastructures, isLoading: infrastructuresLoading, isError: infrastructuresError } = useInfrastructures()\r\n const { files, isLoading: filesLoading, isError: filesError } = useFiles()\r\n const { users, isLoading: usersLoading, isError: usersError } = useUsers()\r\n\r\n const { createError: createSiteError } = useCreateSite()\r\n const { createError: createBuildingError } = useCreateBuilding()\r\n const { createError: createInfrastructureError } = useCreateInfrastructure()\r\n\r\n React.useEffect(() => {\r\n handleApiError(buildingsError)\r\n handleApiError(sitesError)\r\n handleApiError(filesError)\r\n handleApiError(infrastructuresError)\r\n handleApiError(usersError)\r\n handleApiError(createSiteError)\r\n handleApiError(createBuildingError)\r\n handleApiError(createInfrastructureError)\r\n }, [buildingsError, sitesError, infrastructuresError, filesError, usersError, createSiteError, createBuildingError, createInfrastructureError])\r\n\r\n const tableColumns = useColumns(buildings || [])\r\n const userColumns = useUserColumns()\r\n\r\n const { user } = useSession().data\r\n\r\n // Viewer config — resolves icon and i18n title key\r\n const viewerConfig = VIEWER_CONFIG[currentViewer]\r\n const headerTitle = t(viewerConfig.titleKey)\r\n const MenuIcon = viewerConfig.icon\r\n\r\n // Derived data, filtered data, and filter options — all per-viewer logic lives in this hook\r\n const { data, filteredData, filtersOptions, isLoading } = useViewerData({\r\n currentViewer,\r\n buildings,\r\n sites,\r\n infrastructures,\r\n files,\r\n users,\r\n loadingFlags: {\r\n buildings: buildingsLoading,\r\n sites: sitesLoading,\r\n infrastructures: infrastructuresLoading,\r\n files: filesLoading,\r\n users: usersLoading,\r\n },\r\n searchTerm,\r\n activeFilters,\r\n filters,\r\n userEmail: user?.email || '',\r\n })\r\n\r\n const openedCurrentFileIdRef = React.useRef<number | null>(null)\r\n\r\n React.useEffect(() => {\r\n if (currentViewer !== ViewerNames.files || !currentFile) return\r\n if (openedCurrentFileIdRef.current === currentFile.id) return\r\n\r\n const fileRow = data.find((item): item is FileRow => String(item.id) === String(currentFile.id))\r\n if (!fileRow) return\r\n\r\n openedCurrentFileIdRef.current = currentFile.id\r\n setSelectedFile(fileRow)\r\n setView('detail')\r\n }, [currentViewer, currentFile, data, setSelectedFile, setView])\r\n\r\n // Breadcrumb title — derives the detail page label from whichever item is selected\r\n const breadcrumbTitle = React.useMemo(() => {\r\n if (selectedItem) return selectedItem.buildingName || selectedItem.buildingAddress\r\n if (selectedSite) return selectedSite.siteName || selectedSite.siteAddress\r\n if (selectedInfrastructure) return selectedInfrastructure.infrastructureName || selectedInfrastructure.infrastructureType\r\n if (selectedFile) return selectedFile.name\r\n if (selectedUser) return selectedUser.name\r\n if (currentViewer === ViewerNames.extensions) return 'Extension Details'\r\n return undefined\r\n }, [selectedItem, selectedSite, selectedInfrastructure, selectedFile, selectedUser, currentViewer])\r\n\r\n // Row click handlers — consolidated into one function\r\n\r\n const handleRowClick = (item: Building | Site | Infrastructure | FileRow | User) => {\r\n // Files and users don't participate in compare mode\r\n if (comparing && 'id' in item) {\r\n const id = String(item.id)\r\n const isChecked = compareItems.some(i => i.id === id)\r\n if (isChecked) {\r\n removeItemFromCompare(id)\r\n } else {\r\n addItemToCompare({ ...item, id })\r\n }\r\n return\r\n }\r\n\r\n // Navigate to detail view, setting the correct selected item per viewer\r\n switch (currentViewer) {\r\n case ViewerNames.buildings:\r\n setSelectedItem(item as Building)\r\n break\r\n case ViewerNames.sites:\r\n setSelectedSite(item as Site)\r\n break\r\n case ViewerNames.infrastructure:\r\n setSelectedInfrastructure(item as Infrastructure)\r\n break\r\n case ViewerNames.files:\r\n setSelectedFile(item as FileRow)\r\n break\r\n case ViewerNames.users:\r\n setSelectedUser(item as User)\r\n break\r\n }\r\n setView('detail')\r\n }\r\n\r\nconst handleBackToTable = () => {\r\n setView('table')\r\n setSelectedItem(null)\r\n setSelectedSite(null)\r\n setSelectedInfrastructure(null)\r\n setSelectedFile(null)\r\n setSelectedUser(null)\r\n setEditing(false)\r\n setActiveChanges(false)\r\n }\r\n\r\n // Refs for imperative save on child detail components\r\n\r\n const buildingDetailsRef = React.useRef<BuildingDetailsRef>(null)\r\n const siteDetailsRef = React.useRef<SiteDetailsRef>(null)\r\n const infrastructureDetailsRef = React.useRef<InfrastructureDetailsRef>(null)\r\n const fileDetailsRef = React.useRef(null)\r\n const userDetailsRef = React.useRef(null)\r\n\r\n // Save / create handlers — consolidated into one function\r\n\r\n const handleSaveChanges = async () => {\r\n switch (currentViewer) {\r\n case ViewerNames.buildings:\r\n await buildingDetailsRef.current?.saveChanges()\r\n break\r\n case ViewerNames.sites:\r\n await siteDetailsRef.current?.saveChanges()\r\n break\r\n case ViewerNames.infrastructure:\r\n await infrastructureDetailsRef.current?.saveChanges()\r\n break\r\n case ViewerNames.files:\r\n await fileDetailsRef.current?.saveChanges()\r\n break\r\n case ViewerNames.users:\r\n await userDetailsRef.current?.saveChanges()\r\n break\r\n }\r\n }\r\n\r\n /**\r\n * Handles creation of a new entity (id < 0).\r\n * Calls saveChanges on the appropriate ref, then shows a toast.\r\n * Pass successKey as undefined for viewers that show their own success toast (e.g. buildings).\r\n */\r\n const handleCreate = async (\r\n ref: React.RefObject<{ saveChanges: () => Promise<void> }>,\r\n toastKeys: { successKey?: string; errorKey: string },\r\n ) => {\r\n try {\r\n await ref.current?.saveChanges()\r\n setActiveChanges(false)\r\n if (toastKeys.successKey) toast.success(t(toastKeys.successKey))\r\n } catch (error) {\r\n if (error?.status !== 401) toast.error(t(toastKeys.errorKey))\r\n }\r\n }\r\n\r\n // Dispatches to the right ref + toast keys based on which new item is selected\r\n const handleCreateNewItem = async () => {\r\n if (selectedSite && selectedSite.id < 0) {\r\n await handleCreate(siteDetailsRef, {\r\n successKey: 'toastSuccessCreateSite',\r\n errorKey: 'toastFailCreateSite',\r\n })\r\n } else if (selectedItem && selectedItem.id < 0) {\r\n await handleCreate(buildingDetailsRef, {\r\n // Building details shows its own success toast\r\n errorKey: 'toastFailCreateBuilding',\r\n })\r\n } else if (selectedInfrastructure && selectedInfrastructure.id < 0) {\r\n await handleCreate(infrastructureDetailsRef, {\r\n successKey: 'toastSuccessCreateInfrastructure',\r\n errorKey: 'toastFailCreateInfrastructure',\r\n })\r\n } else {\r\n // Users and existing items — standard save\r\n await handleSaveChanges()\r\n }\r\n }\r\n\r\n // Derived UI helpers\r\n\r\n const isNewItem = !!(\r\n (selectedSite && selectedSite.id < 0)\r\n || (selectedItem && selectedItem.id < 0)\r\n || (selectedInfrastructure && selectedInfrastructure.id < 0)\r\n )\r\n\r\n // Render\r\n\r\n return (\r\n <div className={`${hideFrame ? '' : 'sm:p-2'} overflow-hidden ${hideFrame ? '' : 'bg-[#fafafa]'} ${height ? height : 'h-full'}`}>\r\n <div className={`${hideFrame ? '' : 'bg-background rounded-xl shadow'} h-full min-h-0`}>\r\n <div className=\"flex flex-col h-full min-h-0\">\r\n\r\n {/* Breadcrumb Navigation */}\r\n <div className=\"\">\r\n <div className=\"flex flex-row gap-2 justify-start items-center px-3 py-4 relative\">\r\n <Breadcrumb className=\"px-3\">\r\n <BreadcrumbList>\r\n <BreadcrumbItem>\r\n {view === 'table'\r\n ? <BreadcrumbPage>{headerTitle}</BreadcrumbPage>\r\n : (\r\n <BreadcrumbLink onClick={handleBackToTable} className=\"cursor-pointer\">\r\n {headerTitle}\r\n </BreadcrumbLink>\r\n )}\r\n </BreadcrumbItem>\r\n\r\n {view === 'detail' && (\r\n <>\r\n <BreadcrumbSeparator />\r\n <BreadcrumbItem>\r\n <BreadcrumbPage>{breadcrumbTitle}</BreadcrumbPage>\r\n </BreadcrumbItem>\r\n </>\r\n )}\r\n </BreadcrumbList>\r\n </Breadcrumb>\r\n </div>\r\n\r\n {/* Header content based on view */}\r\n {view === 'table' ? (\r\n <>\r\n {/* Title Row */}\r\n {!hideTitle && (\r\n <div className=\"flex flex-row justify-between items-center px-6 py-6\">\r\n <div className=\"flex flex-col gap-1.5\">\r\n <div className=\"flex items-center gap-2\">\r\n <MenuIcon className=\"h-8 w-8\" />\r\n <h1 className=\"text-2xl text-foreground\">{headerTitle}</h1>\r\n </div>\r\n {comparing && (\r\n <h2 className=\"text-sm text-muted-foreground font-normal\">\r\n Choose up to 3 {headerTitle.toLowerCase()} to compare.\r\n </h2>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Search & Filter & Actions Row */}\r\n <div className={`flex flex-col sm:flex-row ${hideTitle ? 'sm:justify-between' : 'sm:justify-end'} items-stretch sm:items-center px-3 sm:px-6 py-4 gap-3 sm:gap-4`}>\r\n <div className={`w-full ${hideTitle ? 'sm:w-64' : 'sm:w-96'}`}>\r\n <Input\r\n placeholder={`${t('searchPlaceholder')} ${headerTitle}...`}\r\n value={searchTerm}\r\n onChange={e => setSearchTerm(e.target.value)}\r\n />\r\n </div>\r\n\r\n <div className=\"flex flex-wrap gap-2\">\r\n <FilterButtons\r\n currentViewer={currentViewer}\r\n filters={filters}\r\n activeFilters={activeFilters}\r\n filterFields={filtersOptions}\r\n data={data}\r\n setFilters={setFilters}\r\n setActiveFilters={setActiveFilters}\r\n />\r\n <HeaderButtons\r\n headerTitle={headerTitle}\r\n currentViewer={currentViewer}\r\n compareItems={compareItems}\r\n setCompareItems={setCompareItems}\r\n comparing={comparing}\r\n setComparing={setComparing}\r\n newItemName={newItemName}\r\n setNewItemName={setNewItemName}\r\n filteredData={\r\n currentViewer === ViewerNames.buildings\r\n || currentViewer === ViewerNames.sites\r\n || currentViewer === ViewerNames.infrastructure\r\n ? filteredData\r\n : undefined\r\n }\r\n users={currentViewer === ViewerNames.users ? users : undefined}\r\n geocodeEarthApiKey={geocodeEarthApiKey}\r\n geocoderUrl={geocoderUrl}\r\n />\r\n </div>\r\n </div>\r\n\r\n <Separator />\r\n </>\r\n ) : (\r\n // Detail View Header\r\n <div className=\"flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 px-4 sm:px-6 py-4 sm:py-6\">\r\n <div className=\"min-w-0\">\r\n <DetailHeader\r\n selectedItem={selectedItem}\r\n selectedSite={selectedSite}\r\n selectedInfrastructure={selectedInfrastructure}\r\n selectedFile={selectedFile}\r\n selectedUser={selectedUser}\r\n countryCode={organization?.country}\r\n />\r\n </div>\r\n\r\n <div className=\"flex flex-wrap shrink-0 gap-2\">\r\n <DetailActions\r\n editing={editing}\r\n activeChanges={activeChanges}\r\n currentViewer={currentViewer}\r\n selectedItem={selectedItem}\r\n selectedSite={selectedSite}\r\n selectedInfrastructure={selectedInfrastructure}\r\n selectedFile={selectedFile}\r\n selectedUser={selectedUser}\r\n activeBuildingTab={activeBuildingTab}\r\n activeSiteTab={activeSiteTab}\r\n onSave={async () => {\r\n await handleCreateNewItem()\r\n setEditing(false)\r\n }}\r\n onEdit={() => {\r\n setEditing(true)\r\n if (isNewItem) setActiveChanges(true)\r\n }}\r\n onCancel={() => {\r\n setEditing(false)\r\n if (isNewItem) handleBackToTable()\r\n }}\r\n onDeleteUser={() => {\r\n setSelectedUser(null)\r\n setView('table')\r\n }}\r\n setView={setView}\r\n />\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Detail view separator */}\r\n {view === 'detail' && <Separator />}\r\n </div>\r\n\r\n {/* Scrollable Content Area */}\r\n <div className=\"flex-1 overflow-hidden\">\r\n {view === 'table' ? (\r\n <div className=\"h-full flex flex-col md:p-6 overflow-auto\">\r\n <DataTable\r\n columns={currentViewer === ViewerNames.users ? userColumns : tableColumns}\r\n data={filteredData}\r\n onRowClick={handleRowClick}\r\n currentViewer={currentViewer}\r\n isLoading={isLoading}\r\n leadingCell={comparing\r\n ? ({ dataset }) => {\r\n const item = dataset\r\n const isChecked = compareItems.some(i => i?.id === String(item?.id))\r\n const maxReached = compareItems.length >= 3 && !isChecked\r\n\r\n return (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"transition-opacity duration-200 hover:bg-transparent\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n if (isChecked) {\r\n removeItemFromCompare?.(String(item.id))\r\n } else {\r\n addItemToCompare?.({ ...item, id: String(item.id) })\r\n }\r\n }}\r\n >\r\n <Checkbox\r\n checked={isChecked}\r\n disabled={maxReached}\r\n className={`opacity-70 hover:opacity-100 data-[state=checked]:opacity-100 ${maxReached && '!cursor-default'}`}\r\n />\r\n </Button>\r\n )\r\n }\r\n : undefined}\r\n trailingCell={({ row }) => (\r\n <div onClick={e => e.stopPropagation()}>\r\n <MoreOptions\r\n row={row.original}\r\n dataType={dataType}\r\n hideViewOnMap={currentViewer === ViewerNames.users}\r\n setView={setView}\r\n onDeleteUser={() => setSelectedUser(null)}\r\n />\r\n </div>\r\n )}\r\n />\r\n </div>\r\n ) : (\r\n <div className=\"h-full overflow-auto\">\r\n {selectedFile\r\n ? (\r\n <FileDetails\r\n selectedFile={selectedFile}\r\n buildings={buildings || []}\r\n editing={editing}\r\n setEditing={setEditing}\r\n setActiveChanges={setActiveChanges}\r\n ref={fileDetailsRef}\r\n />\r\n )\r\n : currentViewer === ViewerNames.buildings\r\n ? (\r\n <BuildingDetails\r\n selectedItem={selectedItem}\r\n setSelectedItem={setSelectedItem}\r\n editing={editing}\r\n setEditing={setEditing}\r\n setActiveChanges={setActiveChanges}\r\n buildings={buildings || []}\r\n ref={buildingDetailsRef}\r\n activeTab={activeBuildingTab}\r\n setActiveTab={setActiveBuildingTab}\r\n />\r\n )\r\n : currentViewer === ViewerNames.sites\r\n ? (\r\n <SiteDetails\r\n selectedSite={selectedSite}\r\n setSelectedSite={setSelectedSite}\r\n editing={editing}\r\n setEditing={setEditing}\r\n setActiveChanges={setActiveChanges}\r\n sites={sites || []}\r\n ref={siteDetailsRef}\r\n activeTab={activeSiteTab}\r\n setActiveTab={setActiveSiteTab}\r\n />\r\n )\r\n : currentViewer === ViewerNames.infrastructure\r\n ? (\r\n <InfrastructureDetails\r\n selectedInfrastructure={selectedInfrastructure}\r\n setSelectedInfrastructure={setSelectedInfrastructure}\r\n editing={editing}\r\n setEditing={setEditing}\r\n setActiveChanges={setActiveChanges}\r\n infrastructures={infrastructures || []}\r\n ref={infrastructureDetailsRef}\r\n activeTab={activeInfrastructureTab}\r\n setActiveTab={setActiveInfrastructureTab}\r\n />\r\n )\r\n : currentViewer === ViewerNames.users\r\n ? (\r\n <UserDetails\r\n selectedUser={selectedUser}\r\n setSelectedUser={setSelectedUser}\r\n editing={editing}\r\n setEditing={setEditing}\r\n setActiveChanges={setActiveChanges}\r\n users={users || []}\r\n ref={userDetailsRef}\r\n hideTitle={hideTitle}\r\n />\r\n )\r\n : null}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}"],"mappings":"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","names":["_a","_b","_c"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BuildingDetails.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/buildingDetails/BuildingDetails.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;
|
|
1
|
+
{"version":3,"file":"BuildingDetails.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/Data/buildingDetails/BuildingDetails.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AA6BzD,UAAU,oBAAoB;IAC5B,YAAY,CAAC,EAAE,QAAQ,CAAA;IACvB,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAA;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACvC,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC7C,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC;AAGD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CACjC,CAAA;AAED,QAAA,MAAM,eAAe,iGA8rBnB,CAAA;AAIF,eAAe,eAAe,CAAA"}
|
|
@@ -27,6 +27,7 @@ import { usePermissions } from "../../../../store";
|
|
|
27
27
|
import { useBuilding, useCreateBuilding } from "../../../../hooks/buildings/buildings";
|
|
28
28
|
import { useFilesByBuildingId } from "../../../../hooks/files/files";
|
|
29
29
|
import { useBuildingHeaders } from "../utils/Headers";
|
|
30
|
+
import { useIsMobile } from "../../../../hooks/ui/use-mobile";
|
|
30
31
|
import { DescriptionListItem } from "../../../ui/DescriptionList";
|
|
31
32
|
import { Separator } from "../../../ui/Separator";
|
|
32
33
|
import { toast } from "sonner";
|
|
@@ -53,6 +54,7 @@ const BuildingDetails = React.forwardRef(({
|
|
|
53
54
|
const { data: session, status } = useSession();
|
|
54
55
|
const { user, isLoading, isError, updateUser } = useUser(((_a = session == null ? void 0 : session.user) == null ? void 0 : _a.id) || "");
|
|
55
56
|
const { ability } = usePermissions();
|
|
57
|
+
const isMobile = useIsMobile();
|
|
56
58
|
const buildingHeaders = useBuildingHeaders();
|
|
57
59
|
const tabOptions = buildingHeaders.map((section) => ({
|
|
58
60
|
key: section.title.toLowerCase().replaceAll(/\s/g, "-"),
|
|
@@ -620,6 +622,30 @@ const BuildingDetails = React.forwardRef(({
|
|
|
620
622
|
] }, subIdx);
|
|
621
623
|
}) });
|
|
622
624
|
};
|
|
625
|
+
if (isMobile) {
|
|
626
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 p-6 h-full overflow-hidden min-h-0", children: [
|
|
627
|
+
/* @__PURE__ */ jsx(
|
|
628
|
+
TabSidebar,
|
|
629
|
+
{
|
|
630
|
+
activeTab,
|
|
631
|
+
setActiveTab,
|
|
632
|
+
tabOptions,
|
|
633
|
+
attachedFilesCount
|
|
634
|
+
}
|
|
635
|
+
),
|
|
636
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col overflow-hidden min-h-0", children: [
|
|
637
|
+
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0 pb-2 px-6", children: /* @__PURE__ */ jsx("p", { className: "text-xl text-foreground font-semibold", children: currentTabSection == null ? void 0 : currentTabSection.title }) }),
|
|
638
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 px-6 overflow-auto", children: activeTab === "attached-files" ? /* @__PURE__ */ jsx(
|
|
639
|
+
AttachedFiles,
|
|
640
|
+
{
|
|
641
|
+
buildingId: selectedItem == null ? void 0 : selectedItem.id,
|
|
642
|
+
files: attachedFiles,
|
|
643
|
+
isLoading: filesLoading
|
|
644
|
+
}
|
|
645
|
+
) : selectedItem && renderTabContent() })
|
|
646
|
+
] })
|
|
647
|
+
] });
|
|
648
|
+
}
|
|
623
649
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-row gap-2 p-6 h-full overflow-hidden", children: [
|
|
624
650
|
/* @__PURE__ */ jsx(
|
|
625
651
|
TabSidebar,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/buildingDetails/BuildingDetails.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// Dependencies\r\nimport * as React from \"react\";\r\nimport type { Building } from '../../../../types/dbTypes'\r\nimport { mutate } from 'swr'\r\nimport { useSession } from 'next-auth/react'\r\nimport { handleApiError } from '../../../../utils/errorHandler'\r\nimport { usePermissions } from '../../../../store'\r\n\r\n// Custom hooks\r\nimport { useBuilding, useCreateBuilding } from '../../../../hooks/buildings/buildings'\r\nimport { useFilesByBuildingId } from '../../../../hooks/files/files'\r\n\r\n// Utilities\r\nimport { useBuildingHeaders } from '../utils/Headers'\r\n\r\n// Shadcn Components\r\nimport { DescriptionListItem } from '../../../ui/DescriptionList'\r\nimport { Separator } from '../../../ui/Separator'\r\nimport { toast } from 'sonner'\r\n\r\n// Custom Components\r\nimport AttachedFiles from '../details/AttachedFiles'\r\nimport TabSidebar from '../details/TabSidebar'\r\nimport FieldValue from '../details/FieldValue'\r\nimport FieldRenderer from './FieldRenderer'\r\nimport CheckboxGroup from '../details/CheckboxGroup'\r\nimport UnitsGrid from '../details/UnitsGrid'\r\nimport EnergySourcesGrid from '../details/EnergySourcesGrid'\r\nimport { useUser } from '../../../../hooks/users/users';\r\n\r\ninterface BuildingDetailsProps {\r\n selectedItem?: Building\r\n setSelectedItem?: (building: Building) => void\r\n editing?: boolean\r\n setEditing?: (editing: boolean) => void\r\n setActiveChanges?: (editing: boolean) => void\r\n buildings?: Building[]\r\n activeTab?: string\r\n setActiveTab?: (tab: string) => void\r\n}\r\n\r\n// Create a ref type for the component\r\nexport type BuildingDetailsRef = {\r\n saveChanges: () => Promise<void>\r\n}\r\n\r\nconst BuildingDetails = React.forwardRef<BuildingDetailsRef, BuildingDetailsProps>(({\r\n selectedItem,\r\n setSelectedItem,\r\n editing = false,\r\n setEditing,\r\n setActiveChanges,\r\n buildings,\r\n activeTab,\r\n setActiveTab = () => {},\r\n}, ref) => {\r\n // Session and user data\r\n const { data: session, status } = useSession()\r\n const { user, isLoading, isError, updateUser } = useUser(session?.user?.id || '')\r\n\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const buildingHeaders = useBuildingHeaders()\r\n\r\n // Generate tab keys from buildingHeaders\r\n const tabOptions = buildingHeaders.map(section => ({\r\n key: section.title.toLowerCase().replaceAll(/\\s/g, '-'),\r\n label: section.title,\r\n }))\r\n\r\n // const [activeTab, setActiveTab] = React.useState(tabOptions[0].key);\r\n const [editingValues, setEditingValues] = React.useState<Partial<Building>>({})\r\n const [isUpdating, setIsUpdating] = React.useState(false)\r\n\r\n // Find current tab section\r\n const currentTabSection = buildingHeaders.find(\r\n section => section.title.toLowerCase().replaceAll(/\\s/g, '-') === activeTab,\r\n )\r\n\r\n // Use the updated hook only if we have a building ID\r\n const { updateBuilding, building: latestBuilding, isMutating, updateError } = useBuilding(selectedItem?.id || 0)\r\n const { createBuilding } = useCreateBuilding()\r\n\r\n // Fetch attached files for the building\r\n const { files: attachedFiles, isLoading: filesLoading } = useFilesByBuildingId(selectedItem?.id || 0)\r\n\r\n // Calculate total file count\r\n const attachedFilesCount = Array.isArray(attachedFiles) ? attachedFiles.length : 0\r\n\r\n // Create a map of tag to relation property name\r\n const tagToRelationMap: Record<string, string> = {\r\n buildingCodeCompliance: 'buildingBuildingCodeCompliance',\r\n environmentalCompliance: 'buildingEnvironmentalCompliance',\r\n occupancyCertificate: 'buildingOccupancyCertificate',\r\n permitsIssued: 'buildingPermitsIssued',\r\n systemAssessments: 'buildingSystemAssessments',\r\n zoningCompliance: 'buildingZoningCompliance',\r\n certificationsFile: 'buildingCertificationsFile',\r\n energyCodeCompliance: 'buildingEnergyCodeCompliance',\r\n fireSafety: 'buildingFireSafety',\r\n maintenanceRecords: 'buildingMaintenanceRecords',\r\n proximityToServices: 'buildingProximityToServices',\r\n regionalEnergyTrends: 'buildingRegionalEnergyTrends',\r\n EnergyAssessmentReports: 'buildingEnergyAssessmentReports',\r\n AirQualityReports: 'buildingAirQualityReports',\r\n WaterQualityReports: 'buildingWaterQualityReports',\r\n DSRs: 'DSRs',\r\n BCAs: 'buildingBCAs',\r\n ContractorReports: 'buildingContractorReports',\r\n EngineeringAssessments: 'buildingEngineeringAssessments',\r\n }\r\n\r\n React.useEffect(() => {\r\n handleApiError(isError)\r\n handleApiError(updateError)\r\n }, [isError, updateError])\r\n\r\n React.useEffect(() => {\r\n if (latestBuilding && selectedItem?.id === latestBuilding.id && setSelectedItem) {\r\n // Only update if we're not currently editing to avoid overwriting user input\r\n if (!editing || Object.keys(editingValues).length === 0) {\r\n const mergedBuilding = { ...latestBuilding }\r\n \r\n // Preserve file relationships from selectedItem if they exist\r\n for (const relationProp of Object.values(tagToRelationMap)) {\r\n const existingFiles = selectedItem[relationProp as keyof Building]\r\n if (existingFiles && Array.isArray(existingFiles) && existingFiles.length > 0) {\r\n (mergedBuilding as any)[relationProp] = existingFiles\r\n }\r\n }\r\n\r\n setSelectedItem(mergedBuilding)\r\n }\r\n }\r\n }, [latestBuilding, editing, editingValues])\r\n\r\n // Update selectedItem with files organized by their relationships\r\n React.useEffect(() => {\r\n if (selectedItem && attachedFiles && Array.isArray(attachedFiles) && setSelectedItem) {\r\n const updatedBuilding = { ...selectedItem }\r\n let hasChanges = false\r\n\r\n // Initialize all file arrays as empty if they don't exist\r\n for (const relationProp of Object.values(tagToRelationMap)) {\r\n if (!updatedBuilding[relationProp as keyof Building]) {\r\n (updatedBuilding as any)[relationProp] = []\r\n }\r\n }\r\n\r\n // Group files by their tags and assign to correct relational properties\r\n for (const file of attachedFiles) {\r\n if (file.tag && tagToRelationMap[file.tag]) {\r\n const relationProp = tagToRelationMap[file.tag]\r\n const currentFiles = (updatedBuilding as any)[relationProp] || []\r\n\r\n // Only add if not already present (avoid duplicates)\r\n const fileExists = currentFiles.some((existingFile: any) =>\r\n existingFile.id === file.id,\r\n )\r\n\r\n if (!fileExists) {\r\n (updatedBuilding as any)[relationProp] = [...currentFiles, file]\r\n hasChanges = true\r\n }\r\n }\r\n }\r\n\r\n // Update the selected item only if there are actual changes\r\n if (hasChanges) {\r\n setSelectedItem(updatedBuilding)\r\n }\r\n }\r\n }, [attachedFiles, selectedItem?.id, setSelectedItem])\r\n\r\n // Handle input changes during editing\r\n const handleInputChange = (key: string, value: any) => {\r\n setEditingValues(prev => ({\r\n ...prev,\r\n [key]: value,\r\n }))\r\n setActiveChanges?.(true)\r\n\r\n if (setSelectedItem && selectedItem) {\r\n setSelectedItem({\r\n ...selectedItem,\r\n [key]: value\r\n })\r\n }\r\n\r\n // If this is a file field change, refresh the files data\r\n if (isFileField(key)) {\r\n // Refresh the files data to show newly uploaded files\r\n mutate(`/api/files/building/${selectedItem?.id}`)\r\n }\r\n }\r\n\r\n // Handle save changes\r\n const handleSaveChanges = async () => {\r\n if (!selectedItem) return\r\n\r\n setIsUpdating(true)\r\n try {\r\n // Check if this is a new building creation (ID < 0)\r\n if (selectedItem.id < 0) {\r\n const buildingData = {\r\n ...selectedItem,\r\n ...editingValues,\r\n }\r\n\r\n delete buildingData.id\r\n\r\n const result = await createBuilding({\r\n buildingData,\r\n organizationId: user?.organizationId ? String(user.organizationId) : null,\r\n })\r\n\r\n // Update the selected building with the newly created one\r\n if (result && setSelectedItem) {\r\n setSelectedItem(result)\r\n }\r\n\r\n setEditingValues({})\r\n setEditing?.(false)\r\n setActiveChanges?.(false)\r\n toast.success('Building created successfully')\r\n }\r\n else {\r\n // This is an update to an existing building\r\n // Filter out file fields from building updates\r\n const buildingUpdates = Object.keys(editingValues).reduce((acc, key) => {\r\n if (!isFileField(key)) {\r\n acc[key] = editingValues[key]\r\n }\r\n return acc\r\n }, {} as Partial<Building>)\r\n\r\n // Check if we have file field changes\r\n const hasFileChanges = Object.keys(editingValues).some(key => isFileField(key))\r\n\r\n // Update building if there are non-file changes\r\n if (Object.keys(buildingUpdates).length > 0) {\r\n const result = await updateBuilding(buildingUpdates)\r\n \r\n // Immediately update local state with the result\r\n if (result && setSelectedItem && selectedItem) {\r\n setSelectedItem({ ...selectedItem, ...result })\r\n }\r\n }\r\n\r\n // Always refresh files data if there were file changes\r\n if (hasFileChanges) {\r\n await mutate(`/api/files/building/${selectedItem.id}`)\r\n }\r\n\r\n // Clear editing state\r\n setEditingValues({})\r\n setEditing?.(false)\r\n setActiveChanges?.(false)\r\n\r\n if (Object.keys(buildingUpdates).length > 0 && hasFileChanges) {\r\n toast.success('Building and files updated successfully')\r\n }\r\n else if (Object.keys(buildingUpdates).length > 0) {\r\n toast.success('Building updated successfully')\r\n }\r\n else if (hasFileChanges) {\r\n toast.success('Files updated successfully')\r\n }\r\n }\r\n }\r\n catch (error) {\r\n console.error('Save error:', error)\r\n if (error?.status !== 401) {\r\n toast.error(selectedItem.id < 0 ? 'Failed to create building' : 'Failed to update building')\r\n }\r\n \r\n }\r\n finally {\r\n setIsUpdating(false)\r\n }\r\n }\r\n\r\n\r\n // Auto-enable editing for new buildings\r\n React.useEffect(() => {\r\n // If this is a new building (negative ID), automatically start in edit mode\r\n if (selectedItem && selectedItem.id < 0 && setEditing) {\r\n setEditing(true)\r\n setActiveChanges?.(true)\r\n }\r\n }, [selectedItem?.id, setEditing, setActiveChanges])\r\n\r\n // Expose methods to parent component\r\n React.useImperativeHandle(ref, () => ({\r\n saveChanges: handleSaveChanges,\r\n }), [selectedItem, editingValues, updateBuilding, createBuilding, setEditing, setActiveChanges])\r\n\r\n // Get current value (edited value or original value)\r\n const getFieldValue = (property: string) => {\r\n return editingValues.hasOwnProperty(property)\r\n ? editingValues[property as keyof Building]\r\n : selectedItem?.[property as keyof Building]\r\n }\r\n\r\n // Check if field is a date field\r\n const isDateField = (property: string): boolean => {\r\n const dateFields = [\r\n 'buildingAirQualityAssessmentDate',\r\n 'buildingEstimatedStartDate', 'buildingEstimatedEndDate',\r\n 'buildingStartDate', 'buildingCompletedDate', 'buildingPermitIssuedDate',\r\n 'buildingAirQualityAssessmentDate', 'conditionRatingsDate',\r\n ]\r\n\r\n return dateFields.includes(property) || property?.toLowerCase().includes('date')\r\n }\r\n // Check if field is an enum field\r\n const isEnumField = (property: string): boolean => {\r\n const enumFields = [\r\n 'buildingCountrySubdivision',\r\n 'buildingAssessmentCondition',\r\n 'buildingGeometryStyle',\r\n 'buildingProjectPhase',\r\n 'buildingProjectType',\r\n 'buildingHeatingEnergySources',\r\n 'buildingCoolingEnergySources',\r\n 'buildingHotWaterSystemsEnergySources',\r\n 'buildingOccupantType',\r\n 'buildingType',\r\n 'buildingAssessmentRatingCondition',\r\n 'buildingManagementType',\r\n 'buildingElectricityServiceSize',\r\n 'buildingElectricityServiceLocation'\r\n ]\r\n\r\n return enumFields.includes(property)\r\n }\r\n\r\n // Get enum type for a property\r\n const getEnumType = (property: string): string | null => {\r\n const enumMappings: Record<string, string> = {\r\n buildingCountrySubdivision: 'CountrySubdivisionData',\r\n buildingAssessmentCondition: 'BuildingAssessmentConditions',\r\n buildingGeometryStyle: 'BuildingGeometryStyle',\r\n buildingProjectPhase: 'BuildingProjectPhase',\r\n buildingProjectType: 'BuildingProjectType',\r\n buildingHeatingCoolingEnergySources: 'BuildingEnergySource',\r\n buildingHotWaterSystemsEnergySources: 'BuildingEnergySource',\r\n buildingOccupantType: 'BuildingOccupantType',\r\n buildingType: 'BuildingTypeEnum',\r\n buildingAssessmentRatingCondition: 'BuildingAssessmentConditions',\r\n buildingManagementType: 'BuildingManagementType',\r\n buildingElectricityServiceSize: 'BuildingElectricityServiceSize',\r\n buildingElectricityServiceLocation: 'BuildingElectricityServiceLocation',\r\n }\r\n\r\n return enumMappings[property] || null\r\n }\r\n\r\n // Check if field should be full width\r\n const isFullWidthField = (property: string): boolean => {\r\n const fullWidthFields = [\r\n 'buildingName', 'buildingAddress', 'buildingParentSiteId',\r\n 'buildingNotes', 'buildingAdditionalInformation',\r\n 'buildingUnitAdditionalInformation', 'buildingEnergyAdditionalInformation',\r\n 'buildingEnvironmentalAdditionalInformation', 'buildingAssessmentDescription',\r\n 'buildingMaintenanceRecords', 'buildingMaintenanceAdditionalInformation',\r\n 'buildingPriorityRetrofits', 'buildingProjectDescription', 'previousUpdates',\r\n 'buildingFundingAdditionalInformation', 'buildingStructuralVulnerabilities',\r\n 'buildingClimateVulnerabilities', 'buildingSafetyConcerns', 'buildingFireSafety',\r\n 'buildingSiteFireCertificate', 'buildingOccupancyCertificate',\r\n 'buildingZoningCompliance', 'buildingEnvironmentalCompliance', 'buildingPermitsIssued',\r\n 'buildingElectricityServiceUpgradeHistory'\r\n ]\r\n return fullWidthFields.includes(property)\r\n }\r\n\r\n // Check if field should use textarea input\r\n const isTextAreaField = (property: string): boolean => {\r\n const textAreaFields = [\r\n 'buildingNotes', 'buildingAdditionalInformation', 'buildingUnitAdditionalInformation',\r\n 'buildingEnergyAdditionalInformation', 'buildingEnvironmentalAdditionalInformation',\r\n 'buildingAssessmentDescription', 'buildingMaintenanceAdditionalInformation',\r\n 'buildingPriorityRetrofits', 'buildingProjectDescription', 'previousUpdates',\r\n 'buildingFundingAdditionalInformation', 'buildingStructuralVulnerabilities',\r\n 'buildingClimateVulnerabilities', 'buildingSafetyConcerns', 'buildingElectricityServiceUpgradeHistory',\r\n ]\r\n return textAreaFields.includes(property)\r\n }\r\n\r\n // Check if field is a file upload field\r\n const isFileField = (property: string): boolean => {\r\n const fileFields = [\r\n 'buildingMaintenanceRecords', 'buildingSystemAssessment', 'buildingFireSafety',\r\n 'buildingSiteFireCertificate', 'buildingOccupancyCertificate', 'buildingEnvironmentalCompliance',\r\n 'buildingZoningCompliance', 'buildingPermitsIssued', 'buildingCertificationsFile',\r\n 'buildingRegionalEnergyTrends', 'buildingEnergyCodeCompliance',\r\n 'buildingOccupancyCertificate', 'occupancyData', 'buildingUnitsMarketRentNum',\r\n 'buildingUnitsMarketRentSqft', 'buildingEnergyAssessmentReports', 'buildingAirQualityReports',\r\n 'buildingWaterQualityReports', 'buildingDSRs', 'buildingBCAs', 'buildingContractorReports',\r\n 'buildingEngineeringAssessments',\r\n ]\r\n return fileFields.includes(property)\r\n }\r\n\r\n // Check if field is a part of the 4-column layout for checkboxes\r\n const isCheckboxGroupField = (property: string): boolean => {\r\n const checkboxGroupFields = [\r\n 'buildingParkingAvailable', 'buildingElevatorAvailable',\r\n 'buildingDryerAvailable', 'buildingWasherAvailable', 'buildingCommonSpace', 'buildingHeatingIncluded',\r\n 'buildingHydroIncluded', 'buildingWaterIncluded', 'buildingUtilitiesIncluded',\r\n 'buildingUnitsModified', 'buildingUnitsMarketRent', 'buildingUnitsSubsidized',\r\n 'buildingUnitsRoom', 'buildingUnitsB', 'buildingUnits1B', 'buildingUnits2B',\r\n 'buildingUnits3B', 'buildingUnits4B', 'buildingUnits5B', 'buildingUnits6B', 'buildingUnitsBelowMarketRent',\r\n 'buildingUnitsRentGearedToIncome'\r\n ]\r\n return checkboxGroupFields.includes(property)\r\n }\r\n\r\n const isNumberField = (property: string): boolean => {\r\n const numberFields = [\r\n 'buildingNorthing', 'buildingTotalSqft', 'buildingFloors', 'buildingUnits',\r\n 'buildingGrossFloorArea', 'buildingNetFloorArea', 'buildingLotSize',\r\n 'buildingUnitsModifiedNum', 'buildingUnitsMarketRentNum', 'buildingUnitsSubsidizedNum',\r\n 'buildingUnitsRoomNum', 'buildingUnitsBNum', 'buildingUnits1BNum',\r\n 'buildingUnits2BNum', 'buildingUnits3BNum', 'buildingUnits4BNum',\r\n 'buildingUnits5BNum', 'buildingUnits6BNum', 'buildingUnitsMarketRentSqft',\r\n 'buildingUnitsBSqft', 'buildingUnits1BSqft', 'buildingUnits2BSqft',\r\n 'buildingUnits3BSqft', 'buildingUnits4BSqft', 'buildingUnits5BSqft', 'buildingUnits6BSqft',\r\n 'buildingUnitsTotal', 'buildingAirQuality', 'buildingAnnualConsumption', 'buildingAssessedValue',\r\n 'buildingOrthogonalHeight', 'buildingOsmId', 'buildingSourceEui', 'buildingWardNumber', 'buildingCensusTract', \r\n 'buildingIntensityGhgEmissions', 'buildingPermitNumber', 'buildingProgramLoan','buildingProjectNumber',\r\n 'buildingRentalMarketSurveyZone', 'buildingSiteEui', 'buildingSolarPotential', 'buildingSubsidies', \r\n 'buildingTotalContribution', 'buildingTotalGhgEmissions', 'buildingTotalRentableArea', 'buildingValueOfFundingFlowed', \r\n 'buildingWaterUseIntensity', 'buildingParentSiteId', 'buildingAssessmentRatingCondition', 'buildingStoreyNum',\r\n 'buildingElectricityServicePanelSize', 'buildingElectricityServiceSize', 'buildingElectricityServiceSize',\r\n 'buildingElectricityServicePanelSize', 'buildingUnitsBelowMarketRentNum', 'buildingUnitsBelowMarketRentSqft',\r\n 'buildingUnitsRentGearedToIncomeNum', 'buildingUnitsRentGearedToIncomeSqft',\r\n // Energy sources grid metrics/years\r\n 'buildingHeatingMetric', 'buildingHeatingInstallationYear',\r\n 'buildingCoolingMetric', 'buildingCoolingInstallationYear',\r\n 'buildingHotWaterSystemsMetric', 'buildingHotWaterSystemsInstallationYear'\r\n ]\r\n\r\n return numberFields.includes(property)\r\n }\r\n\r\n // Render the content for the current tab\r\n const renderTabContent = () => {\r\n if (!selectedItem || !currentTabSection) return null\r\n\r\n return (\r\n <div className=\"flex flex-col gap-6 pt-3\">\r\n {currentTabSection.subsections.map((subsection, subIdx) => {\r\n // Handle special layout for Units Available\r\n if (subsection.specialLayout === 'unitsGrid') {\r\n return (\r\n <div key={subIdx} className=\"\">\r\n {subIdx > 0 && <Separator className=\"mb-6\" />}\r\n <h3 className=\"font-semibold text-base mb-3 text-foreground\">{subsection.title}</h3>\r\n <UnitsGrid\r\n subsection={subsection}\r\n getFieldValue={getFieldValue}\r\n handleInputChange={handleInputChange}\r\n editing={editing}\r\n />\r\n </div>\r\n )\r\n }\r\n // Handle special layout for Energy Sources Grid\r\n if (subsection.specialLayout === 'energySourcesGrid') {\r\n return (\r\n <div key={subIdx} className=\"\">\r\n {subIdx > 0 && <Separator className=\"mb-6\" />}\r\n <h3 className=\"font-semibold text-base mb-3 text-foreground\">{subsection.title}</h3>\r\n <EnergySourcesGrid\r\n subsection={subsection}\r\n getFieldValue={getFieldValue}\r\n handleInputChange={handleInputChange}\r\n editing={editing}\r\n />\r\n </div>\r\n )\r\n }\r\n\r\n // Skip checkbox fields that are handled in groups\r\n const processedFields = new Set<number>()\r\n const fieldsToRender = subsection.fields.filter((field, idx) => {\r\n if (processedFields.has(idx)) return false\r\n\r\n if (isCheckboxGroupField(field.property)) {\r\n // Only include the first field of each checkbox group\r\n const groupStartIndex = subsection.fields.findIndex(f => f.property === field.property)\r\n if (groupStartIndex % 5 === 3 && groupStartIndex === idx) {\r\n // Mark the next 4 fields as processed\r\n for (let i = 1; i < 5 && idx + i < subsection.fields.length; i++) {\r\n processedFields.add(idx + i)\r\n }\r\n return true\r\n }\r\n return false\r\n }\r\n\r\n return true\r\n })\r\n\r\n return (\r\n <div key={subIdx} className=\"\">\r\n {/* Render separator for subsection titles (except for first) */}\r\n {subIdx > 0 && subsection.title && subsection.title !== '' && subsection.title !== 'null'\r\n && <Separator className=\"mb-6\" />}\r\n\r\n <h3 className=\"font-semibold text-base mb-3 text-foreground\">{subsection.title}</h3>\r\n\r\n <div className=\"grid grid-cols-1 [&>*:last-child]:border-b-0\">\r\n {fieldsToRender.map((field, renderIdx) => {\r\n const originalIdx = subsection.fields.findIndex(f => f.property === field.property)\r\n const value = getFieldValue(field.property)\r\n\r\n // Check if this is the start of a checkbox group\r\n if (isCheckboxGroupField(field.property)) {\r\n const checkboxFields = subsection.fields.slice(originalIdx, originalIdx + 5)\r\n return (\r\n <CheckboxGroup\r\n key={renderIdx}\r\n fields={checkboxFields}\r\n getFieldValue={getFieldValue}\r\n handleInputChange={handleInputChange}\r\n editing={editing}\r\n />\r\n )\r\n }\r\n\r\n // Handle full width fields\r\n if (isFullWidthField(field.property)) {\r\n return (\r\n <div key={renderIdx} className={`py-3 w-full ${!editing && 'border-b border-border'}`}>\r\n {editing\r\n ? (\r\n <div className=\"flex flex-col gap-2\">\r\n <div className=\"text-sm font-medium text-foreground\">{field.label}</div>\r\n <FieldRenderer\r\n property={field.property}\r\n value={value}\r\n handleInputChange={handleInputChange}\r\n isTextAreaField={isTextAreaField}\r\n isFileField={isFileField}\r\n isDateField={isDateField}\r\n isEnumField={isEnumField}\r\n getEnumType={getEnumType}\r\n isNumberField={isNumberField}\r\n isFullWidthField={isFullWidthField}\r\n buildingId={selectedItem?.id}\r\n setActiveChanges={setActiveChanges}\r\n />\r\n </div>\r\n )\r\n : (\r\n <DescriptionListItem className=\"break-words\" label={field.label}>\r\n <FieldValue\r\n value={value}\r\n label={field.label}\r\n isFile={isFileField(field.property)}\r\n property={field.property}\r\n onChange={handleInputChange}\r\n editing={editing}\r\n />\r\n </DescriptionListItem>\r\n )}\r\n </div>\r\n )\r\n }\r\n\r\n // Skip full-width fields for pairing logic\r\n if (isFullWidthField(field.property)) {\r\n return null\r\n }\r\n\r\n // Handle regular fields (in pairs)\r\n if (renderIdx % 2 === 0) {\r\n // Start a new row for every pair of fields\r\n const nextField = fieldsToRender[renderIdx + 1]\r\n const nextValue = nextField && !isFullWidthField(nextField.property) ? getFieldValue(nextField.property) : null\r\n\r\n return (\r\n <div key={renderIdx} className={`grid grid-cols-1 md:grid-cols-2 gap-3 py-3 w-full ${!editing && 'border-b border-border'} `}>\r\n {/* First field */}\r\n {editing\r\n ? (\r\n <div className=\"flex flex-col gap-2\">\r\n <div className=\"text-sm font-medium text-foreground\">{field.label}</div>\r\n <FieldRenderer\r\n property={field.property}\r\n value={value}\r\n handleInputChange={handleInputChange}\r\n isTextAreaField={isTextAreaField}\r\n isFileField={isFileField}\r\n isDateField={isDateField}\r\n isEnumField={isEnumField}\r\n getEnumType={getEnumType}\r\n isNumberField={isNumberField}\r\n isFullWidthField={isFullWidthField}\r\n buildingId={selectedItem?.id}\r\n setActiveChanges={setActiveChanges}\r\n />\r\n </div>\r\n )\r\n : (\r\n <DescriptionListItem className=\"break-words\" label={field.label}>\r\n <FieldValue\r\n value={value}\r\n label={field.label}\r\n isFile={isFileField(field.property)}\r\n property={field.property}\r\n onChange={handleInputChange}\r\n editing={editing}\r\n />\r\n </DescriptionListItem>\r\n )}\r\n\r\n <Separator className=\"md:hidden\" />\r\n\r\n {/* Second field (if exists and not full width) */}\r\n {nextField && !isFullWidthField(nextField.property) && (\r\n <>\r\n {editing\r\n ? (\r\n <div className=\"flex flex-col gap-2\">\r\n <div className=\"text-sm font-semibold text-foreground\">\r\n {nextField.label}\r\n </div>\r\n <FieldRenderer\r\n property={nextField.property}\r\n value={nextValue}\r\n handleInputChange={handleInputChange}\r\n isTextAreaField={isTextAreaField}\r\n isFileField={isFileField}\r\n isDateField={isDateField}\r\n isEnumField={isEnumField}\r\n getEnumType={getEnumType}\r\n isNumberField={isNumberField}\r\n isFullWidthField={isFullWidthField}\r\n buildingId={selectedItem?.id}\r\n setActiveChanges={setActiveChanges}\r\n />\r\n </div>\r\n )\r\n : (\r\n <DescriptionListItem\r\n className=\"break-words\"\r\n label={nextField.label}\r\n >\r\n <FieldValue\r\n value={nextValue}\r\n label={nextField.label}\r\n isFile={isFileField(nextField.property)}\r\n property={nextField.property}\r\n onChange={handleInputChange}\r\n editing={editing}\r\n />\r\n </DescriptionListItem>\r\n )}\r\n </>\r\n )}\r\n </div>\r\n )\r\n }\r\n // Skip odd indices for non-full-width fields as they're handled with the previous even index\r\n return null\r\n })}\r\n </div>\r\n </div>\r\n )\r\n })}\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div className=\"flex flex-row gap-2 p-6 h-full overflow-hidden\">\r\n {/* Sidebar */}\r\n <TabSidebar\r\n activeTab={activeTab}\r\n setActiveTab={setActiveTab}\r\n tabOptions={tabOptions}\r\n attachedFilesCount={attachedFilesCount}\r\n />\r\n\r\n <Separator orientation=\"vertical\" />\r\n\r\n {/* Content */}\r\n <div className=\"flex-1 flex flex-col overflow-hidden\">\r\n {/* Header */}\r\n <div className=\"flex-shrink-0 pb-2 px-6\">\r\n <p className=\"text-xl text-foreground font-semibold\">{currentTabSection?.title}</p>\r\n </div>\r\n <div className=\"flex-1 px-6 overflow-auto\">\r\n {activeTab === 'attached-files'\r\n ? (\r\n <AttachedFiles\r\n buildingId={selectedItem?.id}\r\n files={attachedFiles}\r\n isLoading={filesLoading}\r\n />\r\n )\r\n : (\r\n selectedItem && renderTabContent()\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n})\r\n\r\nBuildingDetails.displayName = 'BuildingDetails'\r\n\r\nexport default BuildingDetails"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/buildingDetails/BuildingDetails.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// Dependencies\r\nimport * as React from \"react\";\r\nimport type { Building } from '../../../../types/dbTypes'\r\nimport { mutate } from 'swr'\r\nimport { useSession } from 'next-auth/react'\r\nimport { handleApiError } from '../../../../utils/errorHandler'\r\nimport { usePermissions } from '../../../../store'\r\n\r\n// Custom hooks\r\nimport { useBuilding, useCreateBuilding } from '../../../../hooks/buildings/buildings'\r\nimport { useFilesByBuildingId } from '../../../../hooks/files/files'\r\n\r\n// Utilities\r\nimport { useBuildingHeaders } from '../utils/Headers'\r\nimport { useIsMobile } from '../../../../hooks/ui/use-mobile'\r\n\r\n// Shadcn Components\r\nimport { DescriptionListItem } from '../../../ui/DescriptionList'\r\nimport { Separator } from '../../../ui/Separator'\r\nimport { toast } from 'sonner'\r\n\r\n// Custom Components\r\nimport AttachedFiles from '../details/AttachedFiles'\r\nimport TabSidebar from '../details/TabSidebar'\r\nimport FieldValue from '../details/FieldValue'\r\nimport FieldRenderer from './FieldRenderer'\r\nimport CheckboxGroup from '../details/CheckboxGroup'\r\nimport UnitsGrid from '../details/UnitsGrid'\r\nimport EnergySourcesGrid from '../details/EnergySourcesGrid'\r\nimport { useUser } from '../../../../hooks/users/users';\r\n\r\ninterface BuildingDetailsProps {\r\n selectedItem?: Building\r\n setSelectedItem?: (building: Building) => void\r\n editing?: boolean\r\n setEditing?: (editing: boolean) => void\r\n setActiveChanges?: (editing: boolean) => void\r\n buildings?: Building[]\r\n activeTab?: string\r\n setActiveTab?: (tab: string) => void\r\n}\r\n\r\n// Create a ref type for the component\r\nexport type BuildingDetailsRef = {\r\n saveChanges: () => Promise<void>\r\n}\r\n\r\nconst BuildingDetails = React.forwardRef<BuildingDetailsRef, BuildingDetailsProps>(({\r\n selectedItem,\r\n setSelectedItem,\r\n editing = false,\r\n setEditing,\r\n setActiveChanges,\r\n buildings,\r\n activeTab,\r\n setActiveTab = () => {},\r\n}, ref) => {\r\n // Session and user data\r\n const { data: session, status } = useSession()\r\n const { user, isLoading, isError, updateUser } = useUser(session?.user?.id || '')\r\n\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const isMobile = useIsMobile()\r\n\r\n const buildingHeaders = useBuildingHeaders()\r\n\r\n // Generate tab keys from buildingHeaders\r\n const tabOptions = buildingHeaders.map(section => ({\r\n key: section.title.toLowerCase().replaceAll(/\\s/g, '-'),\r\n label: section.title,\r\n }))\r\n\r\n // const [activeTab, setActiveTab] = React.useState(tabOptions[0].key);\r\n const [editingValues, setEditingValues] = React.useState<Partial<Building>>({})\r\n const [isUpdating, setIsUpdating] = React.useState(false)\r\n\r\n // Find current tab section\r\n const currentTabSection = buildingHeaders.find(\r\n section => section.title.toLowerCase().replaceAll(/\\s/g, '-') === activeTab,\r\n )\r\n\r\n // Use the updated hook only if we have a building ID\r\n const { updateBuilding, building: latestBuilding, isMutating, updateError } = useBuilding(selectedItem?.id || 0)\r\n const { createBuilding } = useCreateBuilding()\r\n\r\n // Fetch attached files for the building\r\n const { files: attachedFiles, isLoading: filesLoading } = useFilesByBuildingId(selectedItem?.id || 0)\r\n\r\n // Calculate total file count\r\n const attachedFilesCount = Array.isArray(attachedFiles) ? attachedFiles.length : 0\r\n\r\n // Create a map of tag to relation property name\r\n const tagToRelationMap: Record<string, string> = {\r\n buildingCodeCompliance: 'buildingBuildingCodeCompliance',\r\n environmentalCompliance: 'buildingEnvironmentalCompliance',\r\n occupancyCertificate: 'buildingOccupancyCertificate',\r\n permitsIssued: 'buildingPermitsIssued',\r\n systemAssessments: 'buildingSystemAssessments',\r\n zoningCompliance: 'buildingZoningCompliance',\r\n certificationsFile: 'buildingCertificationsFile',\r\n energyCodeCompliance: 'buildingEnergyCodeCompliance',\r\n fireSafety: 'buildingFireSafety',\r\n maintenanceRecords: 'buildingMaintenanceRecords',\r\n proximityToServices: 'buildingProximityToServices',\r\n regionalEnergyTrends: 'buildingRegionalEnergyTrends',\r\n EnergyAssessmentReports: 'buildingEnergyAssessmentReports',\r\n AirQualityReports: 'buildingAirQualityReports',\r\n WaterQualityReports: 'buildingWaterQualityReports',\r\n DSRs: 'DSRs',\r\n BCAs: 'buildingBCAs',\r\n ContractorReports: 'buildingContractorReports',\r\n EngineeringAssessments: 'buildingEngineeringAssessments',\r\n }\r\n\r\n React.useEffect(() => {\r\n handleApiError(isError)\r\n handleApiError(updateError)\r\n }, [isError, updateError])\r\n\r\n React.useEffect(() => {\r\n if (latestBuilding && selectedItem?.id === latestBuilding.id && setSelectedItem) {\r\n // Only update if we're not currently editing to avoid overwriting user input\r\n if (!editing || Object.keys(editingValues).length === 0) {\r\n const mergedBuilding = { ...latestBuilding }\r\n \r\n // Preserve file relationships from selectedItem if they exist\r\n for (const relationProp of Object.values(tagToRelationMap)) {\r\n const existingFiles = selectedItem[relationProp as keyof Building]\r\n if (existingFiles && Array.isArray(existingFiles) && existingFiles.length > 0) {\r\n (mergedBuilding as any)[relationProp] = existingFiles\r\n }\r\n }\r\n\r\n setSelectedItem(mergedBuilding)\r\n }\r\n }\r\n }, [latestBuilding, editing, editingValues])\r\n\r\n // Update selectedItem with files organized by their relationships\r\n React.useEffect(() => {\r\n if (selectedItem && attachedFiles && Array.isArray(attachedFiles) && setSelectedItem) {\r\n const updatedBuilding = { ...selectedItem }\r\n let hasChanges = false\r\n\r\n // Initialize all file arrays as empty if they don't exist\r\n for (const relationProp of Object.values(tagToRelationMap)) {\r\n if (!updatedBuilding[relationProp as keyof Building]) {\r\n (updatedBuilding as any)[relationProp] = []\r\n }\r\n }\r\n\r\n // Group files by their tags and assign to correct relational properties\r\n for (const file of attachedFiles) {\r\n if (file.tag && tagToRelationMap[file.tag]) {\r\n const relationProp = tagToRelationMap[file.tag]\r\n const currentFiles = (updatedBuilding as any)[relationProp] || []\r\n\r\n // Only add if not already present (avoid duplicates)\r\n const fileExists = currentFiles.some((existingFile: any) =>\r\n existingFile.id === file.id,\r\n )\r\n\r\n if (!fileExists) {\r\n (updatedBuilding as any)[relationProp] = [...currentFiles, file]\r\n hasChanges = true\r\n }\r\n }\r\n }\r\n\r\n // Update the selected item only if there are actual changes\r\n if (hasChanges) {\r\n setSelectedItem(updatedBuilding)\r\n }\r\n }\r\n }, [attachedFiles, selectedItem?.id, setSelectedItem])\r\n\r\n // Handle input changes during editing\r\n const handleInputChange = (key: string, value: any) => {\r\n setEditingValues(prev => ({\r\n ...prev,\r\n [key]: value,\r\n }))\r\n setActiveChanges?.(true)\r\n\r\n if (setSelectedItem && selectedItem) {\r\n setSelectedItem({\r\n ...selectedItem,\r\n [key]: value\r\n })\r\n }\r\n\r\n // If this is a file field change, refresh the files data\r\n if (isFileField(key)) {\r\n // Refresh the files data to show newly uploaded files\r\n mutate(`/api/files/building/${selectedItem?.id}`)\r\n }\r\n }\r\n\r\n // Handle save changes\r\n const handleSaveChanges = async () => {\r\n if (!selectedItem) return\r\n\r\n setIsUpdating(true)\r\n try {\r\n // Check if this is a new building creation (ID < 0)\r\n if (selectedItem.id < 0) {\r\n const buildingData = {\r\n ...selectedItem,\r\n ...editingValues,\r\n }\r\n\r\n delete buildingData.id\r\n\r\n const result = await createBuilding({\r\n buildingData,\r\n organizationId: user?.organizationId ? String(user.organizationId) : null,\r\n })\r\n\r\n // Update the selected building with the newly created one\r\n if (result && setSelectedItem) {\r\n setSelectedItem(result)\r\n }\r\n\r\n setEditingValues({})\r\n setEditing?.(false)\r\n setActiveChanges?.(false)\r\n toast.success('Building created successfully')\r\n }\r\n else {\r\n // This is an update to an existing building\r\n // Filter out file fields from building updates\r\n const buildingUpdates = Object.keys(editingValues).reduce((acc, key) => {\r\n if (!isFileField(key)) {\r\n acc[key] = editingValues[key]\r\n }\r\n return acc\r\n }, {} as Partial<Building>)\r\n\r\n // Check if we have file field changes\r\n const hasFileChanges = Object.keys(editingValues).some(key => isFileField(key))\r\n\r\n // Update building if there are non-file changes\r\n if (Object.keys(buildingUpdates).length > 0) {\r\n const result = await updateBuilding(buildingUpdates)\r\n \r\n // Immediately update local state with the result\r\n if (result && setSelectedItem && selectedItem) {\r\n setSelectedItem({ ...selectedItem, ...result })\r\n }\r\n }\r\n\r\n // Always refresh files data if there were file changes\r\n if (hasFileChanges) {\r\n await mutate(`/api/files/building/${selectedItem.id}`)\r\n }\r\n\r\n // Clear editing state\r\n setEditingValues({})\r\n setEditing?.(false)\r\n setActiveChanges?.(false)\r\n\r\n if (Object.keys(buildingUpdates).length > 0 && hasFileChanges) {\r\n toast.success('Building and files updated successfully')\r\n }\r\n else if (Object.keys(buildingUpdates).length > 0) {\r\n toast.success('Building updated successfully')\r\n }\r\n else if (hasFileChanges) {\r\n toast.success('Files updated successfully')\r\n }\r\n }\r\n }\r\n catch (error) {\r\n console.error('Save error:', error)\r\n if (error?.status !== 401) {\r\n toast.error(selectedItem.id < 0 ? 'Failed to create building' : 'Failed to update building')\r\n }\r\n \r\n }\r\n finally {\r\n setIsUpdating(false)\r\n }\r\n }\r\n\r\n\r\n // Auto-enable editing for new buildings\r\n React.useEffect(() => {\r\n // If this is a new building (negative ID), automatically start in edit mode\r\n if (selectedItem && selectedItem.id < 0 && setEditing) {\r\n setEditing(true)\r\n setActiveChanges?.(true)\r\n }\r\n }, [selectedItem?.id, setEditing, setActiveChanges])\r\n\r\n // Expose methods to parent component\r\n React.useImperativeHandle(ref, () => ({\r\n saveChanges: handleSaveChanges,\r\n }), [selectedItem, editingValues, updateBuilding, createBuilding, setEditing, setActiveChanges])\r\n\r\n // Get current value (edited value or original value)\r\n const getFieldValue = (property: string) => {\r\n return editingValues.hasOwnProperty(property)\r\n ? editingValues[property as keyof Building]\r\n : selectedItem?.[property as keyof Building]\r\n }\r\n\r\n // Check if field is a date field\r\n const isDateField = (property: string): boolean => {\r\n const dateFields = [\r\n 'buildingAirQualityAssessmentDate',\r\n 'buildingEstimatedStartDate', 'buildingEstimatedEndDate',\r\n 'buildingStartDate', 'buildingCompletedDate', 'buildingPermitIssuedDate',\r\n 'buildingAirQualityAssessmentDate', 'conditionRatingsDate',\r\n ]\r\n\r\n return dateFields.includes(property) || property?.toLowerCase().includes('date')\r\n }\r\n // Check if field is an enum field\r\n const isEnumField = (property: string): boolean => {\r\n const enumFields = [\r\n 'buildingCountrySubdivision',\r\n 'buildingAssessmentCondition',\r\n 'buildingGeometryStyle',\r\n 'buildingProjectPhase',\r\n 'buildingProjectType',\r\n 'buildingHeatingEnergySources',\r\n 'buildingCoolingEnergySources',\r\n 'buildingHotWaterSystemsEnergySources',\r\n 'buildingOccupantType',\r\n 'buildingType',\r\n 'buildingAssessmentRatingCondition',\r\n 'buildingManagementType',\r\n 'buildingElectricityServiceSize',\r\n 'buildingElectricityServiceLocation'\r\n ]\r\n\r\n return enumFields.includes(property)\r\n }\r\n\r\n // Get enum type for a property\r\n const getEnumType = (property: string): string | null => {\r\n const enumMappings: Record<string, string> = {\r\n buildingCountrySubdivision: 'CountrySubdivisionData',\r\n buildingAssessmentCondition: 'BuildingAssessmentConditions',\r\n buildingGeometryStyle: 'BuildingGeometryStyle',\r\n buildingProjectPhase: 'BuildingProjectPhase',\r\n buildingProjectType: 'BuildingProjectType',\r\n buildingHeatingCoolingEnergySources: 'BuildingEnergySource',\r\n buildingHotWaterSystemsEnergySources: 'BuildingEnergySource',\r\n buildingOccupantType: 'BuildingOccupantType',\r\n buildingType: 'BuildingTypeEnum',\r\n buildingAssessmentRatingCondition: 'BuildingAssessmentConditions',\r\n buildingManagementType: 'BuildingManagementType',\r\n buildingElectricityServiceSize: 'BuildingElectricityServiceSize',\r\n buildingElectricityServiceLocation: 'BuildingElectricityServiceLocation',\r\n }\r\n\r\n return enumMappings[property] || null\r\n }\r\n\r\n // Check if field should be full width\r\n const isFullWidthField = (property: string): boolean => {\r\n const fullWidthFields = [\r\n 'buildingName', 'buildingAddress', 'buildingParentSiteId',\r\n 'buildingNotes', 'buildingAdditionalInformation',\r\n 'buildingUnitAdditionalInformation', 'buildingEnergyAdditionalInformation',\r\n 'buildingEnvironmentalAdditionalInformation', 'buildingAssessmentDescription',\r\n 'buildingMaintenanceRecords', 'buildingMaintenanceAdditionalInformation',\r\n 'buildingPriorityRetrofits', 'buildingProjectDescription', 'previousUpdates',\r\n 'buildingFundingAdditionalInformation', 'buildingStructuralVulnerabilities',\r\n 'buildingClimateVulnerabilities', 'buildingSafetyConcerns', 'buildingFireSafety',\r\n 'buildingSiteFireCertificate', 'buildingOccupancyCertificate',\r\n 'buildingZoningCompliance', 'buildingEnvironmentalCompliance', 'buildingPermitsIssued',\r\n 'buildingElectricityServiceUpgradeHistory'\r\n ]\r\n return fullWidthFields.includes(property)\r\n }\r\n\r\n // Check if field should use textarea input\r\n const isTextAreaField = (property: string): boolean => {\r\n const textAreaFields = [\r\n 'buildingNotes', 'buildingAdditionalInformation', 'buildingUnitAdditionalInformation',\r\n 'buildingEnergyAdditionalInformation', 'buildingEnvironmentalAdditionalInformation',\r\n 'buildingAssessmentDescription', 'buildingMaintenanceAdditionalInformation',\r\n 'buildingPriorityRetrofits', 'buildingProjectDescription', 'previousUpdates',\r\n 'buildingFundingAdditionalInformation', 'buildingStructuralVulnerabilities',\r\n 'buildingClimateVulnerabilities', 'buildingSafetyConcerns', 'buildingElectricityServiceUpgradeHistory',\r\n ]\r\n return textAreaFields.includes(property)\r\n }\r\n\r\n // Check if field is a file upload field\r\n const isFileField = (property: string): boolean => {\r\n const fileFields = [\r\n 'buildingMaintenanceRecords', 'buildingSystemAssessment', 'buildingFireSafety',\r\n 'buildingSiteFireCertificate', 'buildingOccupancyCertificate', 'buildingEnvironmentalCompliance',\r\n 'buildingZoningCompliance', 'buildingPermitsIssued', 'buildingCertificationsFile',\r\n 'buildingRegionalEnergyTrends', 'buildingEnergyCodeCompliance',\r\n 'buildingOccupancyCertificate', 'occupancyData', 'buildingUnitsMarketRentNum',\r\n 'buildingUnitsMarketRentSqft', 'buildingEnergyAssessmentReports', 'buildingAirQualityReports',\r\n 'buildingWaterQualityReports', 'buildingDSRs', 'buildingBCAs', 'buildingContractorReports',\r\n 'buildingEngineeringAssessments',\r\n ]\r\n return fileFields.includes(property)\r\n }\r\n\r\n // Check if field is a part of the 4-column layout for checkboxes\r\n const isCheckboxGroupField = (property: string): boolean => {\r\n const checkboxGroupFields = [\r\n 'buildingParkingAvailable', 'buildingElevatorAvailable',\r\n 'buildingDryerAvailable', 'buildingWasherAvailable', 'buildingCommonSpace', 'buildingHeatingIncluded',\r\n 'buildingHydroIncluded', 'buildingWaterIncluded', 'buildingUtilitiesIncluded',\r\n 'buildingUnitsModified', 'buildingUnitsMarketRent', 'buildingUnitsSubsidized',\r\n 'buildingUnitsRoom', 'buildingUnitsB', 'buildingUnits1B', 'buildingUnits2B',\r\n 'buildingUnits3B', 'buildingUnits4B', 'buildingUnits5B', 'buildingUnits6B', 'buildingUnitsBelowMarketRent',\r\n 'buildingUnitsRentGearedToIncome'\r\n ]\r\n return checkboxGroupFields.includes(property)\r\n }\r\n\r\n const isNumberField = (property: string): boolean => {\r\n const numberFields = [\r\n 'buildingNorthing', 'buildingTotalSqft', 'buildingFloors', 'buildingUnits',\r\n 'buildingGrossFloorArea', 'buildingNetFloorArea', 'buildingLotSize',\r\n 'buildingUnitsModifiedNum', 'buildingUnitsMarketRentNum', 'buildingUnitsSubsidizedNum',\r\n 'buildingUnitsRoomNum', 'buildingUnitsBNum', 'buildingUnits1BNum',\r\n 'buildingUnits2BNum', 'buildingUnits3BNum', 'buildingUnits4BNum',\r\n 'buildingUnits5BNum', 'buildingUnits6BNum', 'buildingUnitsMarketRentSqft',\r\n 'buildingUnitsBSqft', 'buildingUnits1BSqft', 'buildingUnits2BSqft',\r\n 'buildingUnits3BSqft', 'buildingUnits4BSqft', 'buildingUnits5BSqft', 'buildingUnits6BSqft',\r\n 'buildingUnitsTotal', 'buildingAirQuality', 'buildingAnnualConsumption', 'buildingAssessedValue',\r\n 'buildingOrthogonalHeight', 'buildingOsmId', 'buildingSourceEui', 'buildingWardNumber', 'buildingCensusTract', \r\n 'buildingIntensityGhgEmissions', 'buildingPermitNumber', 'buildingProgramLoan','buildingProjectNumber',\r\n 'buildingRentalMarketSurveyZone', 'buildingSiteEui', 'buildingSolarPotential', 'buildingSubsidies', \r\n 'buildingTotalContribution', 'buildingTotalGhgEmissions', 'buildingTotalRentableArea', 'buildingValueOfFundingFlowed', \r\n 'buildingWaterUseIntensity', 'buildingParentSiteId', 'buildingAssessmentRatingCondition', 'buildingStoreyNum',\r\n 'buildingElectricityServicePanelSize', 'buildingElectricityServiceSize', 'buildingElectricityServiceSize',\r\n 'buildingElectricityServicePanelSize', 'buildingUnitsBelowMarketRentNum', 'buildingUnitsBelowMarketRentSqft',\r\n 'buildingUnitsRentGearedToIncomeNum', 'buildingUnitsRentGearedToIncomeSqft',\r\n // Energy sources grid metrics/years\r\n 'buildingHeatingMetric', 'buildingHeatingInstallationYear',\r\n 'buildingCoolingMetric', 'buildingCoolingInstallationYear',\r\n 'buildingHotWaterSystemsMetric', 'buildingHotWaterSystemsInstallationYear'\r\n ]\r\n\r\n return numberFields.includes(property)\r\n }\r\n\r\n // Render the content for the current tab\r\n const renderTabContent = () => {\r\n if (!selectedItem || !currentTabSection) return null\r\n\r\n return (\r\n <div className=\"flex flex-col gap-6 pt-3\">\r\n {currentTabSection.subsections.map((subsection, subIdx) => {\r\n // Handle special layout for Units Available\r\n if (subsection.specialLayout === 'unitsGrid') {\r\n return (\r\n <div key={subIdx} className=\"\">\r\n {subIdx > 0 && <Separator className=\"mb-6\" />}\r\n <h3 className=\"font-semibold text-base mb-3 text-foreground\">{subsection.title}</h3>\r\n <UnitsGrid\r\n subsection={subsection}\r\n getFieldValue={getFieldValue}\r\n handleInputChange={handleInputChange}\r\n editing={editing}\r\n />\r\n </div>\r\n )\r\n }\r\n // Handle special layout for Energy Sources Grid\r\n if (subsection.specialLayout === 'energySourcesGrid') {\r\n return (\r\n <div key={subIdx} className=\"\">\r\n {subIdx > 0 && <Separator className=\"mb-6\" />}\r\n <h3 className=\"font-semibold text-base mb-3 text-foreground\">{subsection.title}</h3>\r\n <EnergySourcesGrid\r\n subsection={subsection}\r\n getFieldValue={getFieldValue}\r\n handleInputChange={handleInputChange}\r\n editing={editing}\r\n />\r\n </div>\r\n )\r\n }\r\n\r\n // Skip checkbox fields that are handled in groups\r\n const processedFields = new Set<number>()\r\n const fieldsToRender = subsection.fields.filter((field, idx) => {\r\n if (processedFields.has(idx)) return false\r\n\r\n if (isCheckboxGroupField(field.property)) {\r\n // Only include the first field of each checkbox group\r\n const groupStartIndex = subsection.fields.findIndex(f => f.property === field.property)\r\n if (groupStartIndex % 5 === 3 && groupStartIndex === idx) {\r\n // Mark the next 4 fields as processed\r\n for (let i = 1; i < 5 && idx + i < subsection.fields.length; i++) {\r\n processedFields.add(idx + i)\r\n }\r\n return true\r\n }\r\n return false\r\n }\r\n\r\n return true\r\n })\r\n\r\n return (\r\n <div key={subIdx} className=\"\">\r\n {/* Render separator for subsection titles (except for first) */}\r\n {subIdx > 0 && subsection.title && subsection.title !== '' && subsection.title !== 'null'\r\n && <Separator className=\"mb-6\" />}\r\n\r\n <h3 className=\"font-semibold text-base mb-3 text-foreground\">{subsection.title}</h3>\r\n\r\n <div className=\"grid grid-cols-1 [&>*:last-child]:border-b-0\">\r\n {fieldsToRender.map((field, renderIdx) => {\r\n const originalIdx = subsection.fields.findIndex(f => f.property === field.property)\r\n const value = getFieldValue(field.property)\r\n\r\n // Check if this is the start of a checkbox group\r\n if (isCheckboxGroupField(field.property)) {\r\n const checkboxFields = subsection.fields.slice(originalIdx, originalIdx + 5)\r\n return (\r\n <CheckboxGroup\r\n key={renderIdx}\r\n fields={checkboxFields}\r\n getFieldValue={getFieldValue}\r\n handleInputChange={handleInputChange}\r\n editing={editing}\r\n />\r\n )\r\n }\r\n\r\n // Handle full width fields\r\n if (isFullWidthField(field.property)) {\r\n return (\r\n <div key={renderIdx} className={`py-3 w-full ${!editing && 'border-b border-border'}`}>\r\n {editing\r\n ? (\r\n <div className=\"flex flex-col gap-2\">\r\n <div className=\"text-sm font-medium text-foreground\">{field.label}</div>\r\n <FieldRenderer\r\n property={field.property}\r\n value={value}\r\n handleInputChange={handleInputChange}\r\n isTextAreaField={isTextAreaField}\r\n isFileField={isFileField}\r\n isDateField={isDateField}\r\n isEnumField={isEnumField}\r\n getEnumType={getEnumType}\r\n isNumberField={isNumberField}\r\n isFullWidthField={isFullWidthField}\r\n buildingId={selectedItem?.id}\r\n setActiveChanges={setActiveChanges}\r\n />\r\n </div>\r\n )\r\n : (\r\n <DescriptionListItem className=\"break-words\" label={field.label}>\r\n <FieldValue\r\n value={value}\r\n label={field.label}\r\n isFile={isFileField(field.property)}\r\n property={field.property}\r\n onChange={handleInputChange}\r\n editing={editing}\r\n />\r\n </DescriptionListItem>\r\n )}\r\n </div>\r\n )\r\n }\r\n\r\n // Skip full-width fields for pairing logic\r\n if (isFullWidthField(field.property)) {\r\n return null\r\n }\r\n\r\n // Handle regular fields (in pairs)\r\n if (renderIdx % 2 === 0) {\r\n // Start a new row for every pair of fields\r\n const nextField = fieldsToRender[renderIdx + 1]\r\n const nextValue = nextField && !isFullWidthField(nextField.property) ? getFieldValue(nextField.property) : null\r\n\r\n return (\r\n <div key={renderIdx} className={`grid grid-cols-1 md:grid-cols-2 gap-3 py-3 w-full ${!editing && 'border-b border-border'} `}>\r\n {/* First field */}\r\n {editing\r\n ? (\r\n <div className=\"flex flex-col gap-2\">\r\n <div className=\"text-sm font-medium text-foreground\">{field.label}</div>\r\n <FieldRenderer\r\n property={field.property}\r\n value={value}\r\n handleInputChange={handleInputChange}\r\n isTextAreaField={isTextAreaField}\r\n isFileField={isFileField}\r\n isDateField={isDateField}\r\n isEnumField={isEnumField}\r\n getEnumType={getEnumType}\r\n isNumberField={isNumberField}\r\n isFullWidthField={isFullWidthField}\r\n buildingId={selectedItem?.id}\r\n setActiveChanges={setActiveChanges}\r\n />\r\n </div>\r\n )\r\n : (\r\n <DescriptionListItem className=\"break-words\" label={field.label}>\r\n <FieldValue\r\n value={value}\r\n label={field.label}\r\n isFile={isFileField(field.property)}\r\n property={field.property}\r\n onChange={handleInputChange}\r\n editing={editing}\r\n />\r\n </DescriptionListItem>\r\n )}\r\n\r\n <Separator className=\"md:hidden\" />\r\n\r\n {/* Second field (if exists and not full width) */}\r\n {nextField && !isFullWidthField(nextField.property) && (\r\n <>\r\n {editing\r\n ? (\r\n <div className=\"flex flex-col gap-2\">\r\n <div className=\"text-sm font-semibold text-foreground\">\r\n {nextField.label}\r\n </div>\r\n <FieldRenderer\r\n property={nextField.property}\r\n value={nextValue}\r\n handleInputChange={handleInputChange}\r\n isTextAreaField={isTextAreaField}\r\n isFileField={isFileField}\r\n isDateField={isDateField}\r\n isEnumField={isEnumField}\r\n getEnumType={getEnumType}\r\n isNumberField={isNumberField}\r\n isFullWidthField={isFullWidthField}\r\n buildingId={selectedItem?.id}\r\n setActiveChanges={setActiveChanges}\r\n />\r\n </div>\r\n )\r\n : (\r\n <DescriptionListItem\r\n className=\"break-words\"\r\n label={nextField.label}\r\n >\r\n <FieldValue\r\n value={nextValue}\r\n label={nextField.label}\r\n isFile={isFileField(nextField.property)}\r\n property={nextField.property}\r\n onChange={handleInputChange}\r\n editing={editing}\r\n />\r\n </DescriptionListItem>\r\n )}\r\n </>\r\n )}\r\n </div>\r\n )\r\n }\r\n // Skip odd indices for non-full-width fields as they're handled with the previous even index\r\n return null\r\n })}\r\n </div>\r\n </div>\r\n )\r\n })}\r\n </div>\r\n )\r\n }\r\n\r\n // Mobile: stack the tab picker above the content instead of squeezing them\r\n // into the same row (the desktop layout below is otherwise untouched).\r\n if (isMobile) {\r\n return (\r\n <div className=\"flex flex-col gap-2 p-6 h-full overflow-hidden min-h-0\">\r\n <TabSidebar\r\n activeTab={activeTab}\r\n setActiveTab={setActiveTab}\r\n tabOptions={tabOptions}\r\n attachedFilesCount={attachedFilesCount}\r\n />\r\n\r\n <div className=\"flex-1 flex flex-col overflow-hidden min-h-0\">\r\n <div className=\"flex-shrink-0 pb-2 px-6\">\r\n <p className=\"text-xl text-foreground font-semibold\">{currentTabSection?.title}</p>\r\n </div>\r\n <div className=\"flex-1 min-h-0 px-6 overflow-auto\">\r\n {activeTab === 'attached-files'\r\n ? (\r\n <AttachedFiles\r\n buildingId={selectedItem?.id}\r\n files={attachedFiles}\r\n isLoading={filesLoading}\r\n />\r\n )\r\n : (\r\n selectedItem && renderTabContent()\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div className=\"flex flex-row gap-2 p-6 h-full overflow-hidden\">\r\n {/* Sidebar */}\r\n <TabSidebar\r\n activeTab={activeTab}\r\n setActiveTab={setActiveTab}\r\n tabOptions={tabOptions}\r\n attachedFilesCount={attachedFilesCount}\r\n />\r\n\r\n <Separator orientation=\"vertical\" />\r\n\r\n {/* Content */}\r\n <div className=\"flex-1 flex flex-col overflow-hidden\">\r\n {/* Header */}\r\n <div className=\"flex-shrink-0 pb-2 px-6\">\r\n <p className=\"text-xl text-foreground font-semibold\">{currentTabSection?.title}</p>\r\n </div>\r\n <div className=\"flex-1 px-6 overflow-auto\">\r\n {activeTab === 'attached-files'\r\n ? (\r\n <AttachedFiles\r\n buildingId={selectedItem?.id}\r\n files={attachedFiles}\r\n isLoading={filesLoading}\r\n />\r\n )\r\n : (\r\n selectedItem && renderTabContent()\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n})\r\n\r\nBuildingDetails.displayName = 'BuildingDetails'\r\n\r\nexport default BuildingDetails"],"mappings":"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","names":[]}
|
|
@@ -17,9 +17,9 @@ function BuildingHeader({ building, countryName }) {
|
|
|
17
17
|
const subtitle = building.buildingName ? building.buildingAddress : parts.length ? parts.join(", ") : (_a = building.buildingAddress) != null ? _a : "";
|
|
18
18
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
19
19
|
/* @__PURE__ */ jsx(LR.Building2, { className: "h-8 w-8 shrink-0" }),
|
|
20
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
21
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold", children: title }),
|
|
22
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: subtitle })
|
|
20
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
21
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold truncate", children: title }),
|
|
22
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground truncate", children: subtitle })
|
|
23
23
|
] })
|
|
24
24
|
] });
|
|
25
25
|
}
|
|
@@ -30,9 +30,9 @@ function SiteHeader({ site, countryName }) {
|
|
|
30
30
|
const subtitle = site.siteName ? site.siteAddress : parts.length ? parts.join(", ") : (_a = site.siteAddress) != null ? _a : "";
|
|
31
31
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
32
32
|
/* @__PURE__ */ jsx(LR.BoxSelect, { className: "h-8 w-8 shrink-0" }),
|
|
33
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
34
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold", children: title }),
|
|
35
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: subtitle })
|
|
33
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
34
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold truncate", children: title }),
|
|
35
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground truncate", children: subtitle })
|
|
36
36
|
] })
|
|
37
37
|
] });
|
|
38
38
|
}
|
|
@@ -43,24 +43,24 @@ function InfrastructureHeader({ infrastructure, countryName }) {
|
|
|
43
43
|
const subtitle = infrastructure.infrastructureName ? infrastructure.infrastructureType : parts.length ? parts.join(", ") : (_a = infrastructure.infrastructureType) != null ? _a : "";
|
|
44
44
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
45
45
|
/* @__PURE__ */ jsx(LR.TrainTrack, { className: "h-8 w-8 shrink-0" }),
|
|
46
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
47
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold", children: title }),
|
|
48
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: subtitle })
|
|
46
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
47
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold truncate", children: title }),
|
|
48
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground truncate", children: subtitle })
|
|
49
49
|
] })
|
|
50
50
|
] });
|
|
51
51
|
}
|
|
52
52
|
function FileHeader({ file }) {
|
|
53
53
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
54
54
|
/* @__PURE__ */ jsx(LR.File, { className: "h-8 w-8 shrink-0" }),
|
|
55
|
-
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold", children: file.name }) })
|
|
55
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold truncate", children: file.name }) })
|
|
56
56
|
] });
|
|
57
57
|
}
|
|
58
58
|
function UserHeader({ user }) {
|
|
59
59
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60
60
|
/* @__PURE__ */ jsx(LR.Users, { className: "h-8 w-8 shrink-0" }),
|
|
61
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
62
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold", children: user.name || "New User" }),
|
|
63
|
-
user.email && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: user.email })
|
|
61
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
62
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold truncate", children: user.name || "New User" }),
|
|
63
|
+
user.email && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground truncate", children: user.email })
|
|
64
64
|
] })
|
|
65
65
|
] });
|
|
66
66
|
}
|
|
@@ -73,7 +73,7 @@ function DetailHeader({
|
|
|
73
73
|
countryCode
|
|
74
74
|
}) {
|
|
75
75
|
const countryName = getCountryName(countryCode);
|
|
76
|
-
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-4", children: selectedItem ? /* @__PURE__ */ jsx(BuildingHeader, { building: selectedItem, countryName }) : selectedSite ? /* @__PURE__ */ jsx(SiteHeader, { site: selectedSite, countryName }) : selectedInfrastructure ? /* @__PURE__ */ jsx(InfrastructureHeader, { infrastructure: selectedInfrastructure, countryName }) : selectedFile ? /* @__PURE__ */ jsx(FileHeader, { file: selectedFile }) : selectedUser ? /* @__PURE__ */ jsx(UserHeader, { user: selectedUser }) : null });
|
|
76
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-4 min-w-0", children: selectedItem ? /* @__PURE__ */ jsx(BuildingHeader, { building: selectedItem, countryName }) : selectedSite ? /* @__PURE__ */ jsx(SiteHeader, { site: selectedSite, countryName }) : selectedInfrastructure ? /* @__PURE__ */ jsx(InfrastructureHeader, { infrastructure: selectedInfrastructure, countryName }) : selectedFile ? /* @__PURE__ */ jsx(FileHeader, { file: selectedFile }) : selectedUser ? /* @__PURE__ */ jsx(UserHeader, { user: selectedUser }) : null });
|
|
77
77
|
}
|
|
78
78
|
export {
|
|
79
79
|
DetailHeader as default
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/details/DetailHeader.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport type { Building, Infrastructure, Site, User } from '../../../../types/dbTypes'\r\nimport type { FileRow } from '../../../../types/files'\r\n\r\nfunction getCountryName(code: string | null | undefined): string | null {\r\n if (!code) return null\r\n try {\r\n return new Intl.DisplayNames(['en'], { type: 'region' }).of(code.toUpperCase()) ?? null\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n// Types\r\ninterface DetailHeaderProps {\r\n selectedItem?: Building | null\r\n selectedSite?: Site | null\r\n selectedInfrastructure?: Infrastructure | null\r\n selectedFile?: FileRow | null\r\n selectedUser?: Partial<User> | null\r\n countryCode?: string | null\r\n}\r\n\r\n// Sub-components — one per entity type, all follow the same shape:\r\n// icon + primary title (name || fallback) + optional subtitle\r\n\r\nfunction BuildingHeader({ building, countryName }: { building: Building; countryName: string | null }) {\r\n const title = building.buildingName || building.buildingAddress\r\n const parts = [building.buildingCountrySubdivision, countryName].filter(Boolean)\r\n const subtitle = building.buildingName\r\n ? building.buildingAddress\r\n : parts.length ? parts.join(', ') : (building.buildingAddress ?? '')\r\n\r\n return (\r\n <>\r\n <LR.Building2 className=\"h-8 w-8 shrink-0\" />\r\n <div>\r\n <h1 className=\"text-2xl font-semibold\">{title}</h1>\r\n <p className=\"text-muted-foreground\">{subtitle}</p>\r\n </div>\r\n </>\r\n )\r\n}\r\n\r\nfunction SiteHeader({ site, countryName }: { site: Site; countryName: string | null }) {\r\n const title = site.siteName || site.siteAddress\r\n const parts = [site.siteCountrySubdivision, countryName].filter(Boolean)\r\n const subtitle = site.siteName\r\n ? site.siteAddress\r\n : parts.length ? parts.join(', ') : (site.siteAddress ?? '')\r\n\r\n return (\r\n <>\r\n <LR.BoxSelect className=\"h-8 w-8 shrink-0\" />\r\n <div>\r\n <h1 className=\"text-2xl font-semibold\">{title}</h1>\r\n <p className=\"text-muted-foreground\">{subtitle}</p>\r\n </div>\r\n </>\r\n )\r\n}\r\n\r\nfunction InfrastructureHeader({ infrastructure, countryName }: { infrastructure: Infrastructure; countryName: string | null }) {\r\n const title = infrastructure.infrastructureName || infrastructure.infrastructureType\r\n const parts = [infrastructure.infrastructureCountrySubdivision, countryName].filter(Boolean)\r\n const subtitle = infrastructure.infrastructureName\r\n ? infrastructure.infrastructureType\r\n : parts.length ? parts.join(', ') : (infrastructure.infrastructureType ?? '')\r\n\r\n return (\r\n <>\r\n <LR.TrainTrack className=\"h-8 w-8 shrink-0\" />\r\n <div>\r\n <h1 className=\"text-2xl font-semibold\">{title}</h1>\r\n <p className=\"text-muted-foreground\">{subtitle}</p>\r\n </div>\r\n </>\r\n )\r\n}\r\n\r\nfunction FileHeader({ file }: { file: FileRow }) {\r\n return (\r\n <>\r\n <LR.File className=\"h-8 w-8 shrink-0\" />\r\n <div>\r\n <h1 className=\"text-2xl font-semibold\">{file.name}</h1>\r\n </div>\r\n </>\r\n )\r\n}\r\n\r\nfunction UserHeader({ user }: { user: Partial<User> }) {\r\n return (\r\n <>\r\n <LR.Users className=\"h-8 w-8 shrink-0\" />\r\n <div>\r\n <h1 className=\"text-2xl font-semibold\">{user.name || 'New User'}</h1>\r\n {user.email && <p className=\"text-muted-foreground\">{user.email}</p>}\r\n </div>\r\n </>\r\n )\r\n}\r\n\r\n// DetailHeader — resolves which sub-component to render\r\n\r\nexport default function DetailHeader({\r\n selectedItem,\r\n selectedSite,\r\n selectedInfrastructure,\r\n selectedFile,\r\n selectedUser,\r\n countryCode,\r\n}: DetailHeaderProps) {\r\n const countryName = getCountryName(countryCode)\r\n\r\n return (\r\n <div className=\"flex items-center gap-4\">\r\n {selectedItem\r\n ? <BuildingHeader building={selectedItem} countryName={countryName} />\r\n : selectedSite\r\n ? <SiteHeader site={selectedSite} countryName={countryName} />\r\n : selectedInfrastructure\r\n ? <InfrastructureHeader infrastructure={selectedInfrastructure} countryName={countryName} />\r\n : selectedFile\r\n ? <FileHeader file={selectedFile} />\r\n : selectedUser\r\n ? <UserHeader user={selectedUser} />\r\n : null}\r\n </div>\r\n )\r\n}"],"mappings":";AAuCI,mBACE,KACA,YAFF;AAlCJ,YAAY,QAAQ;AAIpB,SAAS,eAAe,MAAgD;AATxE;AAUE,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACF,YAAO,SAAI,KAAK,aAAa,CAAC,IAAI,GAAG,EAAE,MAAM,SAAS,CAAC,EAAE,GAAG,KAAK,YAAY,CAAC,MAAvE,YAA4E;AAAA,EACrF,SAAQ;AACN,WAAO;AAAA,EACT;AACF;AAeA,SAAS,eAAe,EAAE,UAAU,YAAY,GAAuD;AA/BvG;AAgCE,QAAM,QAAQ,SAAS,gBAAgB,SAAS;AAChD,QAAM,QAAQ,CAAC,SAAS,4BAA4B,WAAW,EAAE,OAAO,OAAO;AAC/E,QAAM,WAAW,SAAS,eACtB,SAAS,kBACT,MAAM,SAAS,MAAM,KAAK,IAAI,KAAK,cAAS,oBAAT,YAA4B;AAEnE,SACE,iCACE;AAAA,wBAAC,GAAG,WAAH,EAAa,WAAU,oBAAmB;AAAA,IAC3C,qBAAC,
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/Data/details/DetailHeader.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport type { Building, Infrastructure, Site, User } from '../../../../types/dbTypes'\r\nimport type { FileRow } from '../../../../types/files'\r\n\r\nfunction getCountryName(code: string | null | undefined): string | null {\r\n if (!code) return null\r\n try {\r\n return new Intl.DisplayNames(['en'], { type: 'region' }).of(code.toUpperCase()) ?? null\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n// Types\r\ninterface DetailHeaderProps {\r\n selectedItem?: Building | null\r\n selectedSite?: Site | null\r\n selectedInfrastructure?: Infrastructure | null\r\n selectedFile?: FileRow | null\r\n selectedUser?: Partial<User> | null\r\n countryCode?: string | null\r\n}\r\n\r\n// Sub-components — one per entity type, all follow the same shape:\r\n// icon + primary title (name || fallback) + optional subtitle\r\n\r\nfunction BuildingHeader({ building, countryName }: { building: Building; countryName: string | null }) {\r\n const title = building.buildingName || building.buildingAddress\r\n const parts = [building.buildingCountrySubdivision, countryName].filter(Boolean)\r\n const subtitle = building.buildingName\r\n ? building.buildingAddress\r\n : parts.length ? parts.join(', ') : (building.buildingAddress ?? '')\r\n\r\n return (\r\n <>\r\n <LR.Building2 className=\"h-8 w-8 shrink-0\" />\r\n <div className=\"min-w-0\">\r\n <h1 className=\"text-2xl font-semibold truncate\">{title}</h1>\r\n <p className=\"text-muted-foreground truncate\">{subtitle}</p>\r\n </div>\r\n </>\r\n )\r\n}\r\n\r\nfunction SiteHeader({ site, countryName }: { site: Site; countryName: string | null }) {\r\n const title = site.siteName || site.siteAddress\r\n const parts = [site.siteCountrySubdivision, countryName].filter(Boolean)\r\n const subtitle = site.siteName\r\n ? site.siteAddress\r\n : parts.length ? parts.join(', ') : (site.siteAddress ?? '')\r\n\r\n return (\r\n <>\r\n <LR.BoxSelect className=\"h-8 w-8 shrink-0\" />\r\n <div className=\"min-w-0\">\r\n <h1 className=\"text-2xl font-semibold truncate\">{title}</h1>\r\n <p className=\"text-muted-foreground truncate\">{subtitle}</p>\r\n </div>\r\n </>\r\n )\r\n}\r\n\r\nfunction InfrastructureHeader({ infrastructure, countryName }: { infrastructure: Infrastructure; countryName: string | null }) {\r\n const title = infrastructure.infrastructureName || infrastructure.infrastructureType\r\n const parts = [infrastructure.infrastructureCountrySubdivision, countryName].filter(Boolean)\r\n const subtitle = infrastructure.infrastructureName\r\n ? infrastructure.infrastructureType\r\n : parts.length ? parts.join(', ') : (infrastructure.infrastructureType ?? '')\r\n\r\n return (\r\n <>\r\n <LR.TrainTrack className=\"h-8 w-8 shrink-0\" />\r\n <div className=\"min-w-0\">\r\n <h1 className=\"text-2xl font-semibold truncate\">{title}</h1>\r\n <p className=\"text-muted-foreground truncate\">{subtitle}</p>\r\n </div>\r\n </>\r\n )\r\n}\r\n\r\nfunction FileHeader({ file }: { file: FileRow }) {\r\n return (\r\n <>\r\n <LR.File className=\"h-8 w-8 shrink-0\" />\r\n <div className=\"min-w-0\">\r\n <h1 className=\"text-2xl font-semibold truncate\">{file.name}</h1>\r\n </div>\r\n </>\r\n )\r\n}\r\n\r\nfunction UserHeader({ user }: { user: Partial<User> }) {\r\n return (\r\n <>\r\n <LR.Users className=\"h-8 w-8 shrink-0\" />\r\n <div className=\"min-w-0\">\r\n <h1 className=\"text-2xl font-semibold truncate\">{user.name || 'New User'}</h1>\r\n {user.email && <p className=\"text-muted-foreground truncate\">{user.email}</p>}\r\n </div>\r\n </>\r\n )\r\n}\r\n\r\n// DetailHeader — resolves which sub-component to render\r\n\r\nexport default function DetailHeader({\r\n selectedItem,\r\n selectedSite,\r\n selectedInfrastructure,\r\n selectedFile,\r\n selectedUser,\r\n countryCode,\r\n}: DetailHeaderProps) {\r\n const countryName = getCountryName(countryCode)\r\n\r\n return (\r\n <div className=\"flex items-center gap-4 min-w-0\">\r\n {selectedItem\r\n ? <BuildingHeader building={selectedItem} countryName={countryName} />\r\n : selectedSite\r\n ? <SiteHeader site={selectedSite} countryName={countryName} />\r\n : selectedInfrastructure\r\n ? <InfrastructureHeader infrastructure={selectedInfrastructure} countryName={countryName} />\r\n : selectedFile\r\n ? <FileHeader file={selectedFile} />\r\n : selectedUser\r\n ? <UserHeader user={selectedUser} />\r\n : null}\r\n </div>\r\n )\r\n}"],"mappings":"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","names":[]}
|