@collabdt/core 0.0.48 → 0.0.50
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/dist/core/components/AppSidebarContent.d.ts.map +1 -1
- package/dist/core/components/AppSidebarContent.js +28 -19
- package/dist/core/components/AppSidebarContent.js.map +1 -1
- package/dist/core/components/authentication/Signin.d.ts.map +1 -1
- package/dist/core/components/authentication/Signin.js +7 -8
- package/dist/core/components/authentication/Signin.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 +13 -2
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/IfcToFragments.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/IfcToFragments.js +1 -1
- package/dist/core/components/viewers/bim/src/IfcToFragments.js.map +1 -1
- package/dist/core/components/viewers/bim/src/LoadIfcAsFragments.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/LoadIfcAsFragments.js +1 -1
- package/dist/core/components/viewers/bim/src/LoadIfcAsFragments.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppSidebarContent.d.ts","sourceRoot":"","sources":["../../../src/core/components/AppSidebarContent.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AA2BvC,OAAO,EAAE,YAAY,EAAY,MAAM,kBAAkB,CAAA;AAKzD,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,WAAW,EACnB,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,SAAS,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,GAAG,QAAQ,CAAC,CAAC,EACjE,eAAe,GAAG,SAYnB,CAAA;AAED,UAAU,eAAe;IACvB,YAAY,EAAE,YAAY,CAAA;IAC1B,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClD;AAED,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,uBAAuB,EAAE,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"AppSidebarContent.d.ts","sourceRoot":"","sources":["../../../src/core/components/AppSidebarContent.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AA2BvC,OAAO,EAAE,YAAY,EAAY,MAAM,kBAAkB,CAAA;AAKzD,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,WAAW,EACnB,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,iBAAiB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAC1D,SAAS,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,GAAG,QAAQ,CAAC,CAAC,EACjE,eAAe,GAAG,SAYnB,CAAA;AAED,UAAU,eAAe;IACvB,YAAY,EAAE,YAAY,CAAA;IAC1B,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClD;AAED,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,uBAAuB,EAAE,EAAE,eAAe,qBA0U3F"}
|
|
@@ -175,6 +175,7 @@ function AppSidebarContent({ organization, countrySubdivisionsData }) {
|
|
|
175
175
|
if (item.accessibleTo == null) return true;
|
|
176
176
|
return item.accessibleTo.some((role) => normalizedUserRoles.has(role.toLowerCase()));
|
|
177
177
|
}, [normalizedUserRoles]);
|
|
178
|
+
const visibleDatasetItems = datasetItems.filter((item) => !appContent || appContent.includes(item.id)).filter(canRenderItem);
|
|
178
179
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
179
180
|
/* @__PURE__ */ jsxs(SidebarContent, { className: "overflow-hidden", children: [
|
|
180
181
|
/* @__PURE__ */ jsx(SidebarHeader, { className: `w-full pt-7 flex items-center justify-center ${isCollapsed ? "items-center" : "pl-4 items-start"}`, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-row items-center gap-3", children: [
|
|
@@ -202,25 +203,33 @@ function AppSidebarContent({ organization, countrySubdivisionsData }) {
|
|
|
202
203
|
) }) }, item.title);
|
|
203
204
|
}) }) })
|
|
204
205
|
] }),
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
/* @__PURE__ */
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
206
|
+
visibleDatasetItems.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
207
|
+
isCollapsed && /* @__PURE__ */ jsx(Separator, { className: "z-30 w-2/3 mx-auto" }),
|
|
208
|
+
/* @__PURE__ */ jsxs(SidebarGroup, { children: [
|
|
209
|
+
/* @__PURE__ */ jsx(SidebarGroupLabel, { children: t("data") }),
|
|
210
|
+
/* @__PURE__ */ jsx(SidebarGroupContent, { children: /* @__PURE__ */ jsx(SidebarMenu, { children: visibleDatasetItems.map((item) => {
|
|
211
|
+
const active = item.id === currentViewer;
|
|
212
|
+
return /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(SidebarMenuButton, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
213
|
+
Button,
|
|
214
|
+
{
|
|
215
|
+
onClick: item.onClick,
|
|
216
|
+
title: isCollapsed ? item.title : void 0,
|
|
217
|
+
className: buildBtnClass(active),
|
|
218
|
+
variant: "ghost",
|
|
219
|
+
children: [
|
|
220
|
+
/* @__PURE__ */ jsx(item.icon, { className: buildIconClass(active) }),
|
|
221
|
+
/* @__PURE__ */ jsx(
|
|
222
|
+
"span",
|
|
223
|
+
{
|
|
224
|
+
className: `${isCollapsed ? "hidden" : "inline"} ${active ? "font-bold text-primary" : ""}`,
|
|
225
|
+
children: item.title
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
) }) }, item.title);
|
|
231
|
+
}) }) })
|
|
232
|
+
] })
|
|
224
233
|
] }),
|
|
225
234
|
isCollapsed && /* @__PURE__ */ jsx(Separator, { className: "z-30 w-2/3 mx-auto" }),
|
|
226
235
|
/* @__PURE__ */ jsxs(SidebarGroup, { children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/components/AppSidebarContent.tsx"],"sourcesContent":["'use client'\r\nimport * as React from 'react'\r\n\r\n// Dependencies\r\nimport { AppConfigContext, MapContext, useMenusContext } from '../store'\r\nimport { ViewerNames } from '../types/'\r\n\r\n// Shadcn Components\r\nimport {\r\n SidebarContent,\r\n SidebarGroup,\r\n SidebarGroupContent,\r\n SidebarGroupLabel,\r\n SidebarMenu,\r\n SidebarMenuButton,\r\n SidebarMenuItem,\r\n SidebarHeader,\r\n SidebarFooter,\r\n useSidebar,\r\n} from '../components/ui/Sidebar'\r\n\r\nimport { Button } from './ui/Button'\r\n\r\n// Custom Components\r\nimport LanguageSwitch from './LanguageSwitch'\r\n\r\n// Icons\r\nimport * as LR from 'lucide-react'\r\n\r\nimport { Logo } from './Logo'\r\nimport { Separator } from './ui/Separator'\r\nimport { useTranslations } from 'next-intl'\r\nimport { Organization, Language } from '../types/dbTypes'\r\nimport { RoleNames } from '../types/global'\r\nimport { useSession } from 'next-auth/react'\r\nimport { useUserRole } from '../hooks/users/users'\r\n\r\nexport const handleChangeViewer = (\r\n viewer: ViewerNames,\r\n setSelectedItem: React.Dispatch<React.SetStateAction<any>>,\r\n setSelectedSite: React.Dispatch<React.SetStateAction<any>>,\r\n setSelectedFile: React.Dispatch<React.SetStateAction<any>>,\r\n setView: React.Dispatch<React.SetStateAction<'table' | 'detail'>>,\r\n menusDispatch: any,\r\n) => {\r\n // Reset selected item, file, & view when changing viewer\r\n setSelectedItem(null)\r\n setSelectedSite(null)\r\n setSelectedFile(null)\r\n setView('table')\r\n\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: viewer },\r\n })\r\n}\r\n\r\ninterface AppSidebarProps {\r\n organization: Organization\r\n countrySubdivisionsData?: Record<string, string>;\r\n}\r\n\r\nexport function AppSidebarContent({ organization, countrySubdivisionsData }: AppSidebarProps) {\r\n // Translations\r\n const t = useTranslations('AppSidebar')\r\n\r\n const { dispatch: appConfigDispatch } = React.useContext(AppConfigContext)\r\n const { dispatch: mapDispatch } = React.useContext(MapContext)\r\n\r\n React.useEffect(() => {\r\n appConfigDispatch({\r\n type: 'SET_ORGANIZATION',\r\n payload: { organization },\r\n })\r\n }, [organization, appConfigDispatch])\r\n \r\n React.useEffect(() => {\r\n if (countrySubdivisionsData) {\r\n mapDispatch({\r\n type: 'SET_COUNTRY_SUBDIVISIONS',\r\n payload: { countrySubdivisionsData, organizationCountry: organization.country },\r\n })\r\n }\r\n }, [countrySubdivisionsData, mapDispatch, organization.country])\r\n\r\n const sidebarTitle = organization?.title ?? organization.name ?? 'CDT Platform'\r\n\r\n const allViewers: ViewerNames[] = [ViewerNames.extensions, ViewerNames.bim, ViewerNames.pointcloud, ViewerNames.sites, ViewerNames.infrastructure, ViewerNames.buildings, ViewerNames.files,];\r\n const appContent: ViewerNames[] = [ViewerNames.map];\r\n\r\n if (organization.appContent && organization.appContent.length > 0) {\r\n appContent.push(...organization.appContent as ViewerNames[]);\r\n } else {\r\n appContent.push(...allViewers);\r\n }\r\n\r\n const logoKey = organization?.logoKey\r\n const minioBaseUrl = process.env.NEXT_PUBLIC_MINIO_BUCKET_URL\r\n const logoUrl = minioBaseUrl && logoKey\r\n ? `${minioBaseUrl}/org-logos/${logoKey}`\r\n : '/images/cdt-logo-stroke.svg'\r\n\r\n const { dispatch: menusDispatch, state: menusState } = useMenusContext()\r\n const { setSelectedItem, setSelectedSite, setSelectedFile, setView } = useMenusContext()\r\n\r\n const { data: session } = useSession()\r\n const { userRole } = useUserRole(session?.user?.id || '')\r\n const normalizedUserRoles = React.useMemo(\r\n () => new Set(userRole?.name ? [userRole.name.trim().toLowerCase()] : []),\r\n [userRole]\r\n )\r\n\r\n const { sidebarState, setOpenInfo, isMobile, openMobile } = useSidebar()\r\n\r\n const changeViewer = (viewer: ViewerNames) => {\r\n handleChangeViewer(viewer, setSelectedItem, setSelectedSite, setSelectedFile, setView, menusDispatch)\r\n setOpenInfo(false)\r\n }\r\n\r\n const handleMapClick = () => {\r\n changeViewer(ViewerNames.map)\r\n }\r\n\r\n interface MenuItem {\r\n title: string\r\n id: ViewerNames | string\r\n icon: React.ElementType\r\n onClick?: () => void\r\n accessibleTo?: RoleNames[],\r\n url?: string\r\n tooltip?: string\r\n }\r\n\r\n // Sidebar menu items\r\n const viewerItems: MenuItem[] = [\r\n {\r\n title: t('mapTitle'),\r\n id: ViewerNames.map,\r\n icon: LR.Map,\r\n onClick: handleMapClick,\r\n },\r\n {\r\n title: t('bimTitle'),\r\n id: ViewerNames.bim,\r\n icon: LR.Box,\r\n onClick: () => changeViewer(ViewerNames.bim),\r\n },\r\n {\r\n title: t('pointCloudTitle'),\r\n id: ViewerNames.pointcloud,\r\n icon: LR.Grip,\r\n onClick: () => changeViewer(ViewerNames.pointcloud),\r\n }\r\n ]\r\n\r\n const datasetItems: MenuItem[] = [\r\n {\r\n title: t('siteTitle'),\r\n id: ViewerNames.sites,\r\n onClick: () => changeViewer(ViewerNames.sites),\r\n icon: LR.BoxSelect,\r\n },\r\n {\r\n title: t('buildingsTitle'),\r\n id: ViewerNames.buildings,\r\n icon: LR.Building2,\r\n onClick: () => changeViewer(ViewerNames.buildings),\r\n },\r\n {\r\n title: t('filesTitle'),\r\n id: ViewerNames.files,\r\n icon: LR.GalleryVerticalEnd,\r\n onClick: () => changeViewer(ViewerNames.files),\r\n },\r\n // {\r\n // title: t('landTitle'),\r\n // id: ViewerNames.land,\r\n // icon: LR.Mountain,\r\n // onClick: () => changeViewer(ViewerNames.land),\r\n // },\r\n {\r\n title: t('infrastructureTitle'),\r\n id: ViewerNames.infrastructure,\r\n icon: LR.TrainTrack,\r\n onClick: () => changeViewer(ViewerNames.infrastructure),\r\n },\r\n\r\n ]\r\n\r\n const managementItems: MenuItem[] = [\r\n // {\r\n // title: t('users'),\r\n // id: ViewerNames.users,\r\n // onClick: () => changeViewer(ViewerNames.users),\r\n // icon: LR.Users,\r\n // accessibleTo: [RoleNames.admin],\r\n // },\r\n {\r\n title: t('addExtensions'),\r\n id: ViewerNames.extensions,\r\n icon: LR.Blocks,\r\n onClick: () => changeViewer(ViewerNames.extensions),\r\n }\r\n ]\r\n\r\n // Sidebar footer items\r\n const serviceItems: MenuItem[] = [\r\n {\r\n title: t('support'),\r\n id: 'support',\r\n url: 'mailto:info@collabdt.org?subject=Support Request - CDT Platform',\r\n icon: LR.LifeBuoy,\r\n tooltip: t('contact')\r\n },\r\n // {\r\n // title: t('feedback'),\r\n // url: 'mailto:info@collabdt.org?subject=Feedback - CDT Platform',\r\n // icon: LR.Send,\r\n // },\r\n ]\r\n const { currentViewer } = menusState.menus\r\n\r\n // On mobile, treat the sheet open as expanded so labels render when the drawer is visible\r\n const isCollapsed = isMobile ? !openMobile : sidebarState === 'collapsed'\r\n\r\n const buildBtnClass = (active: boolean) => [\r\n 'text-sm flex items-center gap-2 w-full cursor-pointer transition-colors rounded-md',\r\n isCollapsed ? 'justify-center p-2' : 'justify-start p-2',\r\n active ? 'bg-primary/5 hover:bg-primary/10 border border-primary/15' : 'hover:bg-muted/50',\r\n ].join(' ')\r\n\r\n const buildIconClass = (active: boolean) => [\r\n 'h-4 w-4 shrink-0',\r\n active ? 'text-primary stroke-2' : 'text-muted-foreground'\r\n ].join(' ')\r\n\r\n const canRenderItem = React.useCallback((item: MenuItem) => {\r\n if (item.accessibleTo == null) return true\r\n return item.accessibleTo.some(role => normalizedUserRoles.has(role.toLowerCase()))\r\n }, [normalizedUserRoles])\r\n\r\n return (\r\n <>\r\n <SidebarContent className='overflow-hidden'>\r\n <SidebarHeader className={`w-full pt-7 flex items-center justify-center ${isCollapsed ? 'items-center' : 'pl-4 items-start'}`}>\r\n <div className='flex flex-row items-center gap-3'>\r\n <Logo image={logoUrl} />\r\n {sidebarState === 'expanded'\r\n && <h1 className=\"text-sm font-bold max-w-36\">{sidebarTitle}</h1>}\r\n </div>\r\n </SidebarHeader>\r\n {isCollapsed && <Separator className=\"z-30 w-2/3 mx-auto\" />}\r\n\r\n <div className='overflow-y-auto overflow-x-hidden'>\r\n <SidebarGroup>\r\n <SidebarGroupLabel>{t('3dViewer')}</SidebarGroupLabel>\r\n <SidebarGroupContent>\r\n <SidebarMenu>\r\n {viewerItems.filter(item => !appContent || appContent.includes(item.id as ViewerNames))\r\n .filter(canRenderItem)\r\n .map(item => {\r\n const active = item.id === currentViewer;\r\n return (\r\n <SidebarMenuItem key={item.title}>\r\n <SidebarMenuButton asChild>\r\n <Button\r\n onClick={item.onClick}\r\n title={isCollapsed ? item.title : undefined}\r\n className={buildBtnClass(active)}\r\n variant=\"ghost\"\r\n >\r\n <item.icon className={buildIconClass(active)} />\r\n <span className={`${isCollapsed ? 'hidden' : 'inline'} ${active ? 'font-bold text-primary' : ''}`}>{item.title}</span>\r\n </Button>\r\n </SidebarMenuButton>\r\n </SidebarMenuItem>\r\n );\r\n })\r\n }\r\n </SidebarMenu>\r\n </SidebarGroupContent>\r\n </SidebarGroup>\r\n {isCollapsed && <Separator className=\"z-30 w-2/3 mx-auto\" />}\r\n <SidebarGroup>\r\n <SidebarGroupLabel>{t('data')}</SidebarGroupLabel>\r\n <SidebarGroupContent>\r\n <SidebarMenu>\r\n {datasetItems.filter(item => !appContent || appContent.includes(item.id as ViewerNames))\r\n .filter(canRenderItem)\r\n .map(item => {\r\n const active = item.id === currentViewer\r\n return (\r\n <SidebarMenuItem key={item.title}>\r\n <SidebarMenuButton asChild>\r\n <Button\r\n onClick={item.onClick}\r\n title={isCollapsed ? item.title : undefined}\r\n className={buildBtnClass(active)}\r\n variant=\"ghost\"\r\n >\r\n <item.icon className={buildIconClass(active)} />\r\n <span className={`${isCollapsed ? 'hidden' : 'inline'} ${active ? 'font-bold text-primary' : ''}`}>{item.title}</span>\r\n </Button>\r\n </SidebarMenuButton>\r\n </SidebarMenuItem>\r\n )\r\n })}\r\n </SidebarMenu>\r\n </SidebarGroupContent>\r\n </SidebarGroup>\r\n {isCollapsed && <Separator className=\"z-30 w-2/3 mx-auto\" />}\r\n <SidebarGroup>\r\n <SidebarGroupLabel>{t('extensions')}</SidebarGroupLabel>\r\n <SidebarGroupContent>\r\n <SidebarMenu>\r\n {managementItems.filter(canRenderItem).map(item => {\r\n const active = item.id === currentViewer\r\n return (\r\n <SidebarMenuItem key={item.title}>\r\n <SidebarMenuButton asChild>\r\n <Button\r\n onClick={item.onClick}\r\n title={isCollapsed ? item.title : undefined}\r\n className={buildBtnClass(active)}\r\n variant=\"ghost\"\r\n >\r\n <item.icon className={buildIconClass(active)} />\r\n <span className={`${isCollapsed ? 'hidden' : 'inline'} ${active ? 'font-bold text-primary' : ''}`}>{item.title}</span>\r\n </Button>\r\n </SidebarMenuButton>\r\n </SidebarMenuItem>\r\n )\r\n })}\r\n </SidebarMenu>\r\n </SidebarGroupContent>\r\n </SidebarGroup>\r\n </div>\r\n </SidebarContent>\r\n {/* Footer Content - only show when not in BIM mode or when collapsed */}\r\n <SidebarFooter className={`w-full flex flex-col gap-4 ${isCollapsed ? 'items-center justify-center px-0 pb-4' : 'items-start justify-start px-2 pb-4'}`}>\r\n {/* Preferences */}\r\n {organization.languages?.length > 1 && (\r\n <SidebarGroup>\r\n <SidebarGroupLabel>{t('preferences')}</SidebarGroupLabel>\r\n <SidebarMenu>\r\n <SidebarMenuItem>\r\n <LanguageSwitch showLabel={sidebarState === 'expanded'} languages={organization.languages as Language[]} />\r\n </SidebarMenuItem>\r\n </SidebarMenu>\r\n </SidebarGroup>\r\n )}\r\n {/* Services */}\r\n <SidebarGroup>\r\n <SidebarGroupLabel>{t('service')}</SidebarGroupLabel>\r\n <SidebarMenu>\r\n {serviceItems.map((item, index) => (\r\n <SidebarMenuItem key={index}>\r\n <SidebarMenuButton asChild>\r\n <a \r\n href={item.url} \r\n className={`text-xs flex items-center gap-2 w-full ${isCollapsed ? 'justify-center p-2' : 'justify-start p-2'}`}\r\n title={item.tooltip}\r\n >\r\n <item.icon className=\"h-4 w-4\" />\r\n <span className={isCollapsed ? 'hidden' : 'inline'}>{item.title}</span>\r\n </a>\r\n </SidebarMenuButton>\r\n </SidebarMenuItem>\r\n ))}\r\n </SidebarMenu>\r\n </SidebarGroup>\r\n </SidebarFooter>\r\n </>\r\n )\r\n}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/core/components/AppSidebarContent.tsx"],"sourcesContent":["'use client'\r\nimport * as React from 'react'\r\n\r\n// Dependencies\r\nimport { AppConfigContext, MapContext, useMenusContext } from '../store'\r\nimport { ViewerNames } from '../types/'\r\n\r\n// Shadcn Components\r\nimport {\r\n SidebarContent,\r\n SidebarGroup,\r\n SidebarGroupContent,\r\n SidebarGroupLabel,\r\n SidebarMenu,\r\n SidebarMenuButton,\r\n SidebarMenuItem,\r\n SidebarHeader,\r\n SidebarFooter,\r\n useSidebar,\r\n} from '../components/ui/Sidebar'\r\n\r\nimport { Button } from './ui/Button'\r\n\r\n// Custom Components\r\nimport LanguageSwitch from './LanguageSwitch'\r\n\r\n// Icons\r\nimport * as LR from 'lucide-react'\r\n\r\nimport { Logo } from './Logo'\r\nimport { Separator } from './ui/Separator'\r\nimport { useTranslations } from 'next-intl'\r\nimport { Organization, Language } from '../types/dbTypes'\r\nimport { RoleNames } from '../types/global'\r\nimport { useSession } from 'next-auth/react'\r\nimport { useUserRole } from '../hooks/users/users'\r\n\r\nexport const handleChangeViewer = (\r\n viewer: ViewerNames,\r\n setSelectedItem: React.Dispatch<React.SetStateAction<any>>,\r\n setSelectedSite: React.Dispatch<React.SetStateAction<any>>,\r\n setSelectedFile: React.Dispatch<React.SetStateAction<any>>,\r\n setView: React.Dispatch<React.SetStateAction<'table' | 'detail'>>,\r\n menusDispatch: any,\r\n) => {\r\n // Reset selected item, file, & view when changing viewer\r\n setSelectedItem(null)\r\n setSelectedSite(null)\r\n setSelectedFile(null)\r\n setView('table')\r\n\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: viewer },\r\n })\r\n}\r\n\r\ninterface AppSidebarProps {\r\n organization: Organization\r\n countrySubdivisionsData?: Record<string, string>;\r\n}\r\n\r\nexport function AppSidebarContent({ organization, countrySubdivisionsData }: AppSidebarProps) {\r\n // Translations\r\n const t = useTranslations('AppSidebar')\r\n\r\n const { dispatch: appConfigDispatch } = React.useContext(AppConfigContext)\r\n const { dispatch: mapDispatch } = React.useContext(MapContext)\r\n\r\n React.useEffect(() => {\r\n appConfigDispatch({\r\n type: 'SET_ORGANIZATION',\r\n payload: { organization },\r\n })\r\n }, [organization, appConfigDispatch])\r\n \r\n React.useEffect(() => {\r\n if (countrySubdivisionsData) {\r\n mapDispatch({\r\n type: 'SET_COUNTRY_SUBDIVISIONS',\r\n payload: { countrySubdivisionsData, organizationCountry: organization.country },\r\n })\r\n }\r\n }, [countrySubdivisionsData, mapDispatch, organization.country])\r\n\r\n const sidebarTitle = organization?.title ?? organization.name ?? 'CDT Platform'\r\n\r\n const allViewers: ViewerNames[] = [ViewerNames.extensions, ViewerNames.bim, ViewerNames.pointcloud, ViewerNames.sites, ViewerNames.infrastructure, ViewerNames.buildings, ViewerNames.files,];\r\n const appContent: ViewerNames[] = [ViewerNames.map];\r\n\r\n if (organization.appContent && organization.appContent.length > 0) {\r\n appContent.push(...organization.appContent as ViewerNames[]);\r\n } else {\r\n appContent.push(...allViewers);\r\n }\r\n\r\n const logoKey = organization?.logoKey\r\n const minioBaseUrl = process.env.NEXT_PUBLIC_MINIO_BUCKET_URL\r\n const logoUrl = minioBaseUrl && logoKey\r\n ? `${minioBaseUrl}/org-logos/${logoKey}`\r\n : '/images/cdt-logo-stroke.svg'\r\n\r\n const { dispatch: menusDispatch, state: menusState } = useMenusContext()\r\n const { setSelectedItem, setSelectedSite, setSelectedFile, setView } = useMenusContext()\r\n\r\n const { data: session } = useSession()\r\n const { userRole } = useUserRole(session?.user?.id || '')\r\n const normalizedUserRoles = React.useMemo(\r\n () => new Set(userRole?.name ? [userRole.name.trim().toLowerCase()] : []),\r\n [userRole]\r\n )\r\n\r\n const { sidebarState, setOpenInfo, isMobile, openMobile } = useSidebar()\r\n\r\n const changeViewer = (viewer: ViewerNames) => {\r\n handleChangeViewer(viewer, setSelectedItem, setSelectedSite, setSelectedFile, setView, menusDispatch)\r\n setOpenInfo(false)\r\n }\r\n\r\n const handleMapClick = () => {\r\n changeViewer(ViewerNames.map)\r\n }\r\n\r\n interface MenuItem {\r\n title: string\r\n id: ViewerNames | string\r\n icon: React.ElementType\r\n onClick?: () => void\r\n accessibleTo?: RoleNames[],\r\n url?: string\r\n tooltip?: string\r\n }\r\n\r\n // Sidebar menu items\r\n const viewerItems: MenuItem[] = [\r\n {\r\n title: t('mapTitle'),\r\n id: ViewerNames.map,\r\n icon: LR.Map,\r\n onClick: handleMapClick,\r\n },\r\n {\r\n title: t('bimTitle'),\r\n id: ViewerNames.bim,\r\n icon: LR.Box,\r\n onClick: () => changeViewer(ViewerNames.bim),\r\n },\r\n {\r\n title: t('pointCloudTitle'),\r\n id: ViewerNames.pointcloud,\r\n icon: LR.Grip,\r\n onClick: () => changeViewer(ViewerNames.pointcloud),\r\n }\r\n ]\r\n\r\n const datasetItems: MenuItem[] = [\r\n {\r\n title: t('siteTitle'),\r\n id: ViewerNames.sites,\r\n onClick: () => changeViewer(ViewerNames.sites),\r\n icon: LR.BoxSelect,\r\n },\r\n {\r\n title: t('buildingsTitle'),\r\n id: ViewerNames.buildings,\r\n icon: LR.Building2,\r\n onClick: () => changeViewer(ViewerNames.buildings),\r\n },\r\n {\r\n title: t('filesTitle'),\r\n id: ViewerNames.files,\r\n icon: LR.GalleryVerticalEnd,\r\n onClick: () => changeViewer(ViewerNames.files),\r\n },\r\n // {\r\n // title: t('landTitle'),\r\n // id: ViewerNames.land,\r\n // icon: LR.Mountain,\r\n // onClick: () => changeViewer(ViewerNames.land),\r\n // },\r\n {\r\n title: t('infrastructureTitle'),\r\n id: ViewerNames.infrastructure,\r\n icon: LR.TrainTrack,\r\n onClick: () => changeViewer(ViewerNames.infrastructure),\r\n },\r\n\r\n ]\r\n\r\n const managementItems: MenuItem[] = [\r\n // {\r\n // title: t('users'),\r\n // id: ViewerNames.users,\r\n // onClick: () => changeViewer(ViewerNames.users),\r\n // icon: LR.Users,\r\n // accessibleTo: [RoleNames.admin],\r\n // },\r\n {\r\n title: t('addExtensions'),\r\n id: ViewerNames.extensions,\r\n icon: LR.Blocks,\r\n onClick: () => changeViewer(ViewerNames.extensions),\r\n }\r\n ]\r\n\r\n // Sidebar footer items\r\n const serviceItems: MenuItem[] = [\r\n {\r\n title: t('support'),\r\n id: 'support',\r\n url: 'mailto:info@collabdt.org?subject=Support Request - CDT Platform',\r\n icon: LR.LifeBuoy,\r\n tooltip: t('contact')\r\n },\r\n // {\r\n // title: t('feedback'),\r\n // url: 'mailto:info@collabdt.org?subject=Feedback - CDT Platform',\r\n // icon: LR.Send,\r\n // },\r\n ]\r\n const { currentViewer } = menusState.menus\r\n\r\n // On mobile, treat the sheet open as expanded so labels render when the drawer is visible\r\n const isCollapsed = isMobile ? !openMobile : sidebarState === 'collapsed'\r\n\r\n const buildBtnClass = (active: boolean) => [\r\n 'text-sm flex items-center gap-2 w-full cursor-pointer transition-colors rounded-md',\r\n isCollapsed ? 'justify-center p-2' : 'justify-start p-2',\r\n active ? 'bg-primary/5 hover:bg-primary/10 border border-primary/15' : 'hover:bg-muted/50',\r\n ].join(' ')\r\n\r\n const buildIconClass = (active: boolean) => [\r\n 'h-4 w-4 shrink-0',\r\n active ? 'text-primary stroke-2' : 'text-muted-foreground'\r\n ].join(' ')\r\n\r\n const canRenderItem = React.useCallback((item: MenuItem) => {\r\n if (item.accessibleTo == null) return true\r\n return item.accessibleTo.some(role => normalizedUserRoles.has(role.toLowerCase()))\r\n }, [normalizedUserRoles])\r\n\r\n const visibleDatasetItems = datasetItems\r\n .filter(item => !appContent || appContent.includes(item.id as ViewerNames))\r\n .filter(canRenderItem)\r\n\r\n\r\n return (\r\n <>\r\n <SidebarContent className='overflow-hidden'>\r\n <SidebarHeader className={`w-full pt-7 flex items-center justify-center ${isCollapsed ? 'items-center' : 'pl-4 items-start'}`}>\r\n <div className='flex flex-row items-center gap-3'>\r\n <Logo image={logoUrl} />\r\n {sidebarState === 'expanded'\r\n && <h1 className=\"text-sm font-bold max-w-36\">{sidebarTitle}</h1>}\r\n </div>\r\n </SidebarHeader>\r\n {isCollapsed && <Separator className=\"z-30 w-2/3 mx-auto\" />}\r\n\r\n <div className='overflow-y-auto overflow-x-hidden'>\r\n <SidebarGroup>\r\n <SidebarGroupLabel>{t('3dViewer')}</SidebarGroupLabel>\r\n <SidebarGroupContent>\r\n <SidebarMenu>\r\n {viewerItems.filter(item => !appContent || appContent.includes(item.id as ViewerNames))\r\n .filter(canRenderItem)\r\n .map(item => {\r\n const active = item.id === currentViewer;\r\n return (\r\n <SidebarMenuItem key={item.title}>\r\n <SidebarMenuButton asChild>\r\n <Button\r\n onClick={item.onClick}\r\n title={isCollapsed ? item.title : undefined}\r\n className={buildBtnClass(active)}\r\n variant=\"ghost\"\r\n >\r\n <item.icon className={buildIconClass(active)} />\r\n <span className={`${isCollapsed ? 'hidden' : 'inline'} ${active ? 'font-bold text-primary' : ''}`}>{item.title}</span>\r\n </Button>\r\n </SidebarMenuButton>\r\n </SidebarMenuItem>\r\n );\r\n })\r\n }\r\n </SidebarMenu>\r\n </SidebarGroupContent>\r\n </SidebarGroup>\r\n\r\n\r\n {visibleDatasetItems.length > 0 && (\r\n <>\r\n {isCollapsed && <Separator className=\"z-30 w-2/3 mx-auto\" />}\r\n\r\n <SidebarGroup>\r\n <SidebarGroupLabel>{t('data')}</SidebarGroupLabel>\r\n\r\n <SidebarGroupContent>\r\n <SidebarMenu>\r\n {visibleDatasetItems.map(item => {\r\n const active = item.id === currentViewer\r\n\r\n return (\r\n <SidebarMenuItem key={item.title}>\r\n <SidebarMenuButton asChild>\r\n <Button\r\n onClick={item.onClick}\r\n title={isCollapsed ? item.title : undefined}\r\n className={buildBtnClass(active)}\r\n variant=\"ghost\"\r\n >\r\n <item.icon className={buildIconClass(active)} />\r\n <span\r\n className={`${isCollapsed ? 'hidden' : 'inline'} ${\r\n active ? 'font-bold text-primary' : ''\r\n }`}\r\n >\r\n {item.title}\r\n </span>\r\n </Button>\r\n </SidebarMenuButton>\r\n </SidebarMenuItem>\r\n )\r\n })}\r\n </SidebarMenu>\r\n </SidebarGroupContent>\r\n </SidebarGroup>\r\n </>\r\n )}\r\n {isCollapsed && <Separator className=\"z-30 w-2/3 mx-auto\" />}\r\n <SidebarGroup>\r\n <SidebarGroupLabel>{t('extensions')}</SidebarGroupLabel>\r\n <SidebarGroupContent>\r\n <SidebarMenu>\r\n {managementItems.filter(canRenderItem).map(item => {\r\n const active = item.id === currentViewer\r\n return (\r\n <SidebarMenuItem key={item.title}>\r\n <SidebarMenuButton asChild>\r\n <Button\r\n onClick={item.onClick}\r\n title={isCollapsed ? item.title : undefined}\r\n className={buildBtnClass(active)}\r\n variant=\"ghost\"\r\n >\r\n <item.icon className={buildIconClass(active)} />\r\n <span className={`${isCollapsed ? 'hidden' : 'inline'} ${active ? 'font-bold text-primary' : ''}`}>{item.title}</span>\r\n </Button>\r\n </SidebarMenuButton>\r\n </SidebarMenuItem>\r\n )\r\n })}\r\n </SidebarMenu>\r\n </SidebarGroupContent>\r\n </SidebarGroup>\r\n </div>\r\n </SidebarContent>\r\n {/* Footer Content - only show when not in BIM mode or when collapsed */}\r\n <SidebarFooter className={`w-full flex flex-col gap-4 ${isCollapsed ? 'items-center justify-center px-0 pb-4' : 'items-start justify-start px-2 pb-4'}`}>\r\n {/* Preferences */}\r\n {organization.languages?.length > 1 && (\r\n <SidebarGroup>\r\n <SidebarGroupLabel>{t('preferences')}</SidebarGroupLabel>\r\n <SidebarMenu>\r\n <SidebarMenuItem>\r\n <LanguageSwitch showLabel={sidebarState === 'expanded'} languages={organization.languages as Language[]} />\r\n </SidebarMenuItem>\r\n </SidebarMenu>\r\n </SidebarGroup>\r\n )}\r\n {/* Services */}\r\n <SidebarGroup>\r\n <SidebarGroupLabel>{t('service')}</SidebarGroupLabel>\r\n <SidebarMenu>\r\n {serviceItems.map((item, index) => (\r\n <SidebarMenuItem key={index}>\r\n <SidebarMenuButton asChild>\r\n <a \r\n href={item.url} \r\n className={`text-xs flex items-center gap-2 w-full ${isCollapsed ? 'justify-center p-2' : 'justify-start p-2'}`}\r\n title={item.tooltip}\r\n >\r\n <item.icon className=\"h-4 w-4\" />\r\n <span className={isCollapsed ? 'hidden' : 'inline'}>{item.title}</span>\r\n </a>\r\n </SidebarMenuButton>\r\n </SidebarMenuItem>\r\n ))}\r\n </SidebarMenu>\r\n </SidebarGroup>\r\n </SidebarFooter>\r\n </>\r\n )\r\n}"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Signin.d.ts","sourceRoot":"","sources":["../../../../src/core/components/authentication/Signin.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"Signin.d.ts","sourceRoot":"","sources":["../../../../src/core/components/authentication/Signin.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA2R9B,wBAAgB,MAAM,sBAMrB"}
|
|
@@ -5,10 +5,7 @@ import { signIn } from "next-auth/react";
|
|
|
5
5
|
import { useTranslations } from "next-intl";
|
|
6
6
|
import * as LR from "lucide-react";
|
|
7
7
|
import { toast, Toaster } from "sonner";
|
|
8
|
-
import { Button } from "../ui
|
|
9
|
-
import { GoogleIcon } from "../ui/Icons/GoogleIcon";
|
|
10
|
-
import { Input } from "../ui/Input";
|
|
11
|
-
import { LoadingSpinner } from "../ui/LoadingSpinner";
|
|
8
|
+
import { Button, GoogleIcon, Input, LoadingSpinner } from "../ui";
|
|
12
9
|
import { AuthPage, useAuthTheme } from "./AuthPage";
|
|
13
10
|
import { useParams, useSearchParams } from "next/navigation";
|
|
14
11
|
import ReCAPTCHA from "react-google-recaptcha";
|
|
@@ -59,14 +56,16 @@ function SignInContent() {
|
|
|
59
56
|
setIsLoading(true);
|
|
60
57
|
setError("Too many failed login attempts. Please try again later.");
|
|
61
58
|
return;
|
|
62
|
-
} else if (result.code === "
|
|
59
|
+
} else if (result.code === "whitelist_invalid_credentials") {
|
|
63
60
|
setError("Invalid email or password");
|
|
64
61
|
setIsLoading(false);
|
|
65
62
|
return;
|
|
66
63
|
} else if (!captchaStatus) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
setError("Captcha Verification Failed.");
|
|
65
|
+
setIsLoading(false);
|
|
66
|
+
return;
|
|
67
|
+
} else if (result.code === "invalid_credentials") {
|
|
68
|
+
setError("Invalid email or password");
|
|
70
69
|
setIsLoading(false);
|
|
71
70
|
return;
|
|
72
71
|
} else if (result.code === "mfa_required") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/components/authentication/Signin.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport { signIn } from 'next-auth/react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as LR from 'lucide-react'\r\nimport { toast, Toaster } from 'sonner'\r\n// Direct file imports (not the ui barrel). The ui barrel\r\n// `src/core/components/ui/index.ts` re-exports dozens of components plus\r\n// `useFileUploadHandler` and `InfoSidebar`, the latter of which transitively\r\n// pulls in @thatopen and the BIM viewer tree. Importing through the barrel\r\n// would drag all of that into the auth pages; direct file imports avoid it.\r\nimport { Button } from '../ui/Button'\r\nimport { GoogleIcon } from '../ui/Icons/GoogleIcon'\r\nimport { Input } from '../ui/Input'\r\nimport { LoadingSpinner } from '../ui/LoadingSpinner'\r\nimport { AuthPage, useAuthTheme } from './AuthPage'\r\nimport { useParams, useSearchParams } from 'next/navigation'\r\nimport ReCAPTCHA from 'react-google-recaptcha'\r\n\r\nfunction SignInContent() {\r\n const [step, setStep] = React.useState<'login' | 'mfa'>('login')\r\n\r\n const [email, setEmail] = React.useState('')\r\n const [password, setPassword] = React.useState('')\r\n const [code, setCode] = React.useState('')\r\n\r\n const [captchaStatus, setCaptchaStatus] = React.useState(false)\r\n const [captchaToken, setCaptchaToken] = React.useState('')\r\n const [isLoading, setIsLoading] = React.useState(false)\r\n const [error, setError] = React.useState('')\r\n const [showPassword, setShowPassword] = React.useState(false)\r\n\r\n const searchParams = useSearchParams()\r\n const params = useParams()\r\n const orgName = params.instance ?? 'canada'\r\n const [googleError, setGoogleError] = React.useState(\r\n searchParams.get('error')\r\n )\r\n //Clean up the Google query error in URL, and have it only return /orgName/signin to clear frontend error responses \r\n React.useEffect(() => {\r\n if (googleError) {\r\n window.history.replaceState({}, '', `/${orgName}/signin`)\r\n }\r\n }, [googleError, orgName])\r\n\r\n const t = useTranslations('Signin')\r\n const tMfa = useTranslations('MFA')\r\n const authTheme = useAuthTheme()\r\n\r\n const reCaptchaSiteKey = `${process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY}`\r\n\r\n const onReCaptchaSuccess = (token) => {\r\n setCaptchaToken(token)\r\n setCaptchaStatus(true)\r\n }\r\n\r\n // Initial LOGIN\r\n const handleSubmit = async (e) => {\r\n e.preventDefault()\r\n setGoogleError(null)\r\n setIsLoading(true)\r\n setError('')\r\n\r\n try {\r\n const result = await signIn('credentials', {\r\n redirect: false,\r\n email,\r\n password,\r\n captchaToken,\r\n })\r\n\r\n if (result?.error) {\r\n\r\n if (result.code === 'rate_limit_error') {\r\n\r\n setIsLoading(true)\r\n setError('Too many failed login attempts. Please try again later.')\r\n return\r\n }\r\n else if (result.code === 'invalid_credentials') {\r\n setError('Invalid email or password')\r\n setIsLoading(false)\r\n return\r\n }\r\n //Commenting out Captcha code for Development \r\n else if (!captchaStatus) {\r\n setTimeout(() => {\r\n setError('Captcha Verification Failed.')\r\n }, 500)\r\n setIsLoading(false)\r\n return\r\n }\r\n //MFA TRIGGER\r\n else if (result.code === 'mfa_required') {\r\n setStep('mfa')\r\n setIsLoading(false)\r\n return\r\n }\r\n\r\n else {\r\n setError('Captcha Verification Expired. Please try again later.')\r\n setIsLoading(false)\r\n return\r\n }\r\n }\r\n\r\n // Completed Login + MFA - Redirect to the Platform's Organization Dashboard \r\n window.location.href = `/${orgName}`\r\n\r\n } catch (err) {\r\n setError('Unexpected error. Please try again.')\r\n setIsLoading(false)\r\n }\r\n }\r\n\r\n // VERIFY OTP for MFA\r\n const handleVerifyOTP = async () => {\r\n setIsLoading(true)\r\n setError('')\r\n\r\n try {\r\n const res = await fetch('/api/mfa', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ email, code }),\r\n })\r\n\r\n if (!res.ok) {\r\n toast.error(tMfa('invalidCode'))\r\n setIsLoading(false)\r\n return\r\n }\r\n\r\n toast.success(tMfa('success'))\r\n\r\n // FINAL LOGIN after successful MFA process\r\n await signIn('credentials', {\r\n email,\r\n password,\r\n mfaVerified: true,\r\n redirect: true,\r\n redirectTo: `/${orgName}`,\r\n })\r\n\r\n } catch (err) {\r\n toast.error(tMfa('verificationFailed'))\r\n setIsLoading(false)\r\n }\r\n }\r\n\r\n return (\r\n <>\r\n <Toaster richColors position=\"top-right\" />\r\n {/* Header */}\r\n <div className=\"space-y-2 text-left\">\r\n <h1 className=\"font-display font-bold\" style={{\r\n fontSize: '1.75rem',\r\n lineHeight: '1.1',\r\n letterSpacing: '-0.02em',\r\n color: 'var(--hp-on-surface)',\r\n }}>\r\n {step === 'login' ? t('title') : tMfa('title')}\r\n </h1>\r\n\r\n <p style={{ color: 'var(--hp-on-surface-variant)', fontSize: '0.9rem' }}>\r\n {step === 'login'\r\n ? t('message')\r\n : tMfa('subtitle', { email })}\r\n </p>\r\n </div>\r\n\r\n {/* LOGIN FORM */}\r\n {step === 'login' && (\r\n <form onSubmit={handleSubmit} className=\"space-y-4\">\r\n\r\n <Input\r\n type=\"email\"\r\n placeholder={t('placeholder1')}\r\n value={email}\r\n onChange={(e) => setEmail(e.target.value)}\r\n disabled={isLoading}\r\n required\r\n />\r\n\r\n <div className=\"relative\">\r\n <Input\r\n type={showPassword ? 'text' : 'password'}\r\n placeholder={t('placeholder2')}\r\n value={password}\r\n onChange={(e) => setPassword(e.target.value)}\r\n disabled={isLoading}\r\n required\r\n className=\"pr-10\"\r\n />\r\n <button\r\n type=\"button\"\r\n onClick={() => setShowPassword(v => !v)}\r\n tabIndex={-1}\r\n className=\"absolute right-3 top-1/2 -translate-y-1/2 transition-colors\"\r\n style={{ color: 'var(--hp-on-surface-variant)' }}\r\n >\r\n {showPassword ? <LR.EyeOff size={16} /> : <LR.Eye size={16} />}\r\n </button>\r\n </div>\r\n {/* Google Not Linked Error */}\r\n {googleError === 'google_not_linked' && (\r\n <div className=\"auth-pw-error\">\r\n Your Google account is not linked. Please sign in with your credentials first, then link your Google account.\r\n </div>\r\n )}\r\n {error && <div className=\"auth-pw-error\">{error}</div>}\r\n\r\n <div className=\"flex items-center gap-2\">\r\n <Button type=\"submit\" disabled={isLoading} className=\"flex-1 auth-btn-primary\">\r\n {isLoading ? <LoadingSpinner /> : 'Login'}\r\n </Button>\r\n\r\n {/* Google button */}\r\n {<button\r\n type=\"button\"\r\n onClick={() => signIn('google', { redirectTo: `/${orgName}` })}\r\n disabled={isLoading}\r\n aria-label=\"Sign in with Google\"\r\n className=\"auth-google-btn\"\r\n >\r\n <GoogleIcon size={20} />\r\n </button>}\r\n\r\n </div>\r\n\r\n {/* Reset Password link */}\r\n {/* <div className=\"flex justify-start\">\r\n <a\r\n href={`/${orgName}/auth/reset-password`}\r\n className=\"text-sm underline underline-offset-4 hover:opacity-80\"\r\n style={{ color: 'var(--hp-primary)' }}\r\n >\r\n Reset your password\r\n </a>\r\n </div> */}\r\n </form>\r\n )}\r\n\r\n {/* MFA FORM */}\r\n {step === 'mfa' && (\r\n <div className=\"space-y-4\">\r\n\r\n <Input\r\n placeholder={tMfa('placeholder')}\r\n value={code}\r\n onChange={(e) => setCode(e.target.value)}\r\n disabled={isLoading}\r\n />\r\n\r\n <Button onClick={handleVerifyOTP} disabled={isLoading} className=\"w-full\">\r\n {isLoading ? <LoadingSpinner /> : tMfa('verify')}\r\n </Button>\r\n\r\n {/* Back */}\r\n <Button\r\n variant=\"outline\"\r\n onClick={() => setStep('login')}\r\n className=\"w-full mt-3 inline-flex items-center justify-center gap-2\"\r\n >\r\n <LR.ArrowLeft size={16} />\r\n {tMfa('backToLogin')}\r\n </Button>\r\n </div>\r\n )}\r\n\r\n {/* CAPTCHA only on Credentials(Username and Password) Provider login */}\r\n {step === 'login' && (\r\n <div className=\"auth-captcha-wrapper\">\r\n <ReCAPTCHA\r\n key={`recaptcha-${authTheme}`}\r\n sitekey={reCaptchaSiteKey}\r\n onChange={onReCaptchaSuccess}\r\n theme={authTheme}\r\n />\r\n </div>\r\n )}\r\n </>\r\n )\r\n}\r\n\r\nexport function SignIn() {\r\n return (\r\n <AuthPage>\r\n <SignInContent />\r\n </AuthPage>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/components/authentication/Signin.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport { signIn } from 'next-auth/react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as LR from 'lucide-react'\r\nimport { toast, Toaster } from 'sonner'\r\nimport { Button, GoogleIcon, Input, LoadingSpinner } from '../ui'\r\nimport { AuthPage, useAuthTheme } from './AuthPage'\r\nimport { useParams, useSearchParams } from 'next/navigation'\r\nimport ReCAPTCHA from 'react-google-recaptcha'\r\n\r\nfunction SignInContent() {\r\n const [step, setStep] = React.useState<'login' | 'mfa'>('login')\r\n\r\n const [email, setEmail] = React.useState('')\r\n const [password, setPassword] = React.useState('')\r\n const [code, setCode] = React.useState('')\r\n\r\n const [captchaStatus, setCaptchaStatus] = React.useState(false)\r\n const [captchaToken, setCaptchaToken] = React.useState('')\r\n const [isLoading, setIsLoading] = React.useState(false)\r\n const [error, setError] = React.useState('')\r\n const [showPassword, setShowPassword] = React.useState(false)\r\n\r\n const searchParams = useSearchParams()\r\n const params = useParams()\r\n const orgName = params.instance ?? 'canada'\r\n const [googleError, setGoogleError] = React.useState(\r\n searchParams.get('error')\r\n )\r\n //Clean up the Google query error in URL, and have it only return /orgName/signin to clear frontend error responses \r\n React.useEffect(() => {\r\n if (googleError) {\r\n window.history.replaceState({}, '', `/${orgName}/signin`)\r\n }\r\n }, [googleError, orgName])\r\n\r\n const t = useTranslations('Signin')\r\n const tMfa = useTranslations('MFA')\r\n const authTheme = useAuthTheme()\r\n\r\n const reCaptchaSiteKey = `${process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY}`\r\n\r\n const onReCaptchaSuccess = (token) => {\r\n setCaptchaToken(token)\r\n setCaptchaStatus(true)\r\n }\r\n\r\n // Initial LOGIN\r\n const handleSubmit = async (e) => {\r\n e.preventDefault()\r\n setGoogleError(null)\r\n setIsLoading(true)\r\n setError('')\r\n\r\n try {\r\n const result = await signIn('credentials', {\r\n redirect: false,\r\n email,\r\n password,\r\n captchaToken,\r\n })\r\n\r\n if (result?.error) {\r\n\r\n if (result.code === 'rate_limit_error') {\r\n\r\n setIsLoading(true)\r\n setError('Too many failed login attempts. Please try again later.')\r\n return\r\n }\r\n\r\n else if (result.code === 'whitelist_invalid_credentials') {\r\n setError('Invalid email or password')\r\n setIsLoading(false)\r\n return\r\n }\r\n\r\n else if (!captchaStatus) {\r\n\r\n setError('Captcha Verification Failed.')\r\n setIsLoading(false)\r\n return\r\n }\r\n\r\n else if (result.code === 'invalid_credentials') {\r\n setError('Invalid email or password')\r\n setIsLoading(false)\r\n return\r\n }\r\n //MFA TRIGGER\r\n else if (result.code === 'mfa_required') {\r\n setStep('mfa')\r\n setIsLoading(false)\r\n return\r\n }\r\n\r\n else { //result.code === 'expired_recaptcha_token\r\n setError('Captcha Verification Expired. Please try again later.')\r\n setIsLoading(false)\r\n return\r\n }\r\n }\r\n\r\n // Completed Login + MFA - Redirect to the Platform's Organization Dashboard \r\n window.location.href = `/${orgName}`\r\n\r\n } catch (err) {\r\n setError('Unexpected error. Please try again.')\r\n setIsLoading(false)\r\n }\r\n }\r\n\r\n // VERIFY OTP for MFA\r\n const handleVerifyOTP = async () => {\r\n setIsLoading(true)\r\n setError('')\r\n\r\n try {\r\n const res = await fetch('/api/mfa', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ email, code }),\r\n })\r\n\r\n if (!res.ok) {\r\n toast.error(tMfa('invalidCode'))\r\n setIsLoading(false)\r\n return\r\n }\r\n\r\n toast.success(tMfa('success'))\r\n\r\n // FINAL LOGIN after successful MFA process\r\n await signIn('credentials', {\r\n email,\r\n password,\r\n mfaVerified: true,\r\n redirect: true,\r\n redirectTo: `/${orgName}`,\r\n })\r\n\r\n } catch (err) {\r\n toast.error(tMfa('verificationFailed'))\r\n setIsLoading(false)\r\n }\r\n }\r\n\r\n return (\r\n <>\r\n <Toaster richColors position=\"top-right\" />\r\n {/* Header */}\r\n <div className=\"space-y-2 text-left\">\r\n <h1 className=\"font-display font-bold\" style={{\r\n fontSize: '1.75rem',\r\n lineHeight: '1.1',\r\n letterSpacing: '-0.02em',\r\n color: 'var(--hp-on-surface)',\r\n }}>\r\n {step === 'login' ? t('title') : tMfa('title')}\r\n </h1>\r\n\r\n <p style={{ color: 'var(--hp-on-surface-variant)', fontSize: '0.9rem' }}>\r\n {step === 'login'\r\n ? t('message')\r\n : tMfa('subtitle', { email })}\r\n </p>\r\n </div>\r\n\r\n {/* LOGIN FORM */}\r\n {step === 'login' && (\r\n <form onSubmit={handleSubmit} className=\"space-y-4\">\r\n\r\n <Input\r\n type=\"email\"\r\n placeholder={t('placeholder1')}\r\n value={email}\r\n onChange={(e) => setEmail(e.target.value)}\r\n disabled={isLoading}\r\n required\r\n />\r\n\r\n <div className=\"relative\">\r\n <Input\r\n type={showPassword ? 'text' : 'password'}\r\n placeholder={t('placeholder2')}\r\n value={password}\r\n onChange={(e) => setPassword(e.target.value)}\r\n disabled={isLoading}\r\n required\r\n className=\"pr-10\"\r\n />\r\n <button\r\n type=\"button\"\r\n onClick={() => setShowPassword(v => !v)}\r\n tabIndex={-1}\r\n className=\"absolute right-3 top-1/2 -translate-y-1/2 transition-colors\"\r\n style={{ color: 'var(--hp-on-surface-variant)' }}\r\n >\r\n {showPassword ? <LR.EyeOff size={16} /> : <LR.Eye size={16} />}\r\n </button>\r\n </div>\r\n {/* Google Not Linked Error */}\r\n {googleError === 'google_not_linked' && (\r\n <div className=\"auth-pw-error\">\r\n Your Google account is not linked. Please sign in with your credentials first, then link your Google account.\r\n </div>\r\n )}\r\n {error && <div className=\"auth-pw-error\">{error}</div>}\r\n\r\n <div className=\"flex items-center gap-2\">\r\n <Button type=\"submit\" disabled={isLoading} className=\"flex-1 auth-btn-primary\">\r\n {isLoading ? <LoadingSpinner /> : 'Login'}\r\n </Button>\r\n\r\n {/* Google button */}\r\n {<button\r\n type=\"button\"\r\n onClick={() => signIn('google', { redirectTo: `/${orgName}` })}\r\n disabled={isLoading}\r\n aria-label=\"Sign in with Google\"\r\n className=\"auth-google-btn\"\r\n >\r\n <GoogleIcon size={20} />\r\n </button>}\r\n\r\n </div>\r\n\r\n {/*⚠️⚠️⚠️⚠️ DISABLED FOR NOW - Password Reset link, will implement in the future when we have the flow ready */}\r\n {/* Reset Password link */}\r\n {/* <div className=\"flex justify-start\">\r\n <a\r\n href={`/${orgName}/auth/reset-password`}\r\n className=\"text-sm underline underline-offset-4 hover:opacity-80\"\r\n style={{ color: 'var(--hp-primary)' }}\r\n >\r\n Reset your password\r\n </a>\r\n </div> */}\r\n </form>\r\n )}\r\n\r\n {/* MFA FORM */}\r\n {step === 'mfa' && (\r\n <div className=\"space-y-4\">\r\n\r\n <Input\r\n placeholder={tMfa('placeholder')}\r\n value={code}\r\n onChange={(e) => setCode(e.target.value)}\r\n disabled={isLoading}\r\n />\r\n\r\n <Button onClick={handleVerifyOTP} disabled={isLoading} className=\"w-full\">\r\n {isLoading ? <LoadingSpinner /> : tMfa('verify')}\r\n </Button>\r\n\r\n {/* Back */}\r\n <Button\r\n variant=\"outline\"\r\n onClick={() => setStep('login')}\r\n className=\"w-full mt-3 inline-flex items-center justify-center gap-2\"\r\n >\r\n <LR.ArrowLeft size={16} />\r\n {tMfa('backToLogin')}\r\n </Button>\r\n </div>\r\n )}\r\n\r\n {/* CAPTCHA only on Credentials(Username and Password) Provider login */}\r\n {step === 'login' && (\r\n <div className=\"auth-captcha-wrapper\">\r\n <ReCAPTCHA\r\n key={`recaptcha-${authTheme}`}\r\n sitekey={reCaptchaSiteKey}\r\n onChange={onReCaptchaSuccess}\r\n theme={authTheme}\r\n />\r\n </div>\r\n )}\r\n </>\r\n )\r\n}\r\n\r\nexport function SignIn() {\r\n return (\r\n <AuthPage>\r\n <SignInContent />\r\n </AuthPage>\r\n )\r\n}"],"mappings":"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","names":[]}
|
package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilesSection.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAmB9B,OAAO,EAAE,MAAM,IAAI,KAAK,EAAE,MAAM,uCAAuC,CAAA;AAQvE,UAAU,iBAAiB;IACzB,KAAK,EAAE,KAAK,EAAE,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAUD,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,KAAU,EAAE,EAAE,iBAAiB,qBA+
|
|
1
|
+
{"version":3,"file":"FilesSection.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAmB9B,OAAO,EAAE,MAAM,IAAI,KAAK,EAAE,MAAM,uCAAuC,CAAA;AAQvE,UAAU,iBAAiB;IACzB,KAAK,EAAE,KAAK,EAAE,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAUD,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,KAAU,EAAE,EAAE,iBAAiB,qBA+jBpE"}
|
|
@@ -74,9 +74,20 @@ function FilesSection({ files, query = "" }) {
|
|
|
74
74
|
return [f.id, (_a = f.isVisible) != null ? _a : false];
|
|
75
75
|
})
|
|
76
76
|
);
|
|
77
|
+
const isFileInScene = (file) => {
|
|
78
|
+
var _a, _b;
|
|
79
|
+
if (file.extension === "ids") return activeIDSFileId === file.id;
|
|
80
|
+
if (file.extension === "dxf") {
|
|
81
|
+
return ((_a = dxfGroupsRef.current.get(file.id.toString())) == null ? void 0 : _a.visible) === true;
|
|
82
|
+
}
|
|
83
|
+
if (is3DFile(file.extension)) {
|
|
84
|
+
const info = (_b = modelManagerRef.current) == null ? void 0 : _b.getModelByName(file.name);
|
|
85
|
+
return info ? info.model.visible !== false : false;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
};
|
|
77
89
|
return files.filter((file) => file.tag !== "user").filter((file) => file.type !== "map-file").map((file) => {
|
|
78
|
-
|
|
79
|
-
let isVisible = visibilityMap.has(file.id) ? visibilityMap.get(file.id) : (_a = file.isVisible) != null ? _a : false;
|
|
90
|
+
let isVisible = visibilityMap.has(file.id) ? visibilityMap.get(file.id) : isFileInScene(file);
|
|
80
91
|
if (file.extension === "ids" && activeIDSFileId === file.id) {
|
|
81
92
|
isVisible = true;
|
|
82
93
|
} else if (file.extension === "ids" && activeIDSFileId !== null && activeIDSFileId !== file.id) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.tsx"],"sourcesContent":["import * as React from 'react'\r\nimport * as LR from 'lucide-react'\r\nimport { BimContext, BuildingsContext, MenusContext } from '../../../../../../../../store'\r\nimport { ModelManager } from '../../../../ModelManager'\r\nimport { DXFManager } from '../../../../DXFLoader'\r\nimport * as THREE from 'three'\r\nimport { GizmoController } from '../../../../../utils/GizmoController'\r\nimport { CurrentWorld } from '../../../../CurrentWorld'\r\nimport { Cursor } from '../../../../Cursor'\r\nimport { Highlighter } from '../../../../Highlighter'\r\nimport { CollapsibleSection } from '../../../../../../../ui/CollapsibleSection'\r\nimport { useTranslations } from 'next-intl'\r\nimport ConfirmDialog from '../../../../../../../ConfirmDialog'\r\nimport { useUploadFileToBuilding, useDeleteFile, useFile } from '../../../../../../../../hooks/files/files'\r\nimport { useFileUploadHandler, useFileDeleteHandler, FileItemComponent, useFileActions, useCommonFileUpload } from '../../../../../../../ui/FilesManager'\r\nimport { IDSManager } from '../../../../IDSManager'\r\nimport { BCFTopicsManager } from '../../../../BCFTopicsManager'\r\nimport { createFileMarker, removeMarker, type AddedFile } from '../../../../tools/AddToBim/src/FileMarkerUtils'\r\nimport type { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'\r\nimport { DbFile as IFile } from '../../../../../../../../types/dbTypes'\r\n\r\n// Hoist options arrays so the array identity is stable across renders.\r\n// Inline `options={[...]}` defeats React.memo on FileItemComponent.\r\ntype FileAction = import('../../../../../../../../types/global').FileAction\r\nconst OPTIONS_3D: FileAction[] = ['download', 'view', 'move', 'info', 'delete']\r\nconst OPTIONS_NON_3D: FileAction[] = ['download', 'view', 'delete']\r\n\r\ninterface FilesSectionProps {\r\n files: IFile[]\r\n query?: string\r\n}\r\n\r\nconst is3DFile = (ext?: string | null): boolean => {\r\n if (!ext) return false\r\n return ['glb', 'gltf', 'fbx', 'obj', 'collada'].includes(ext.toLowerCase())\r\n}\r\n\r\n// Files that live in the 3D scene and can be moved/scaled (3D models + DXF drawings).\r\nconst isPlaceable = (ext?: string | null): boolean => is3DFile(ext) || ext?.toLowerCase() === 'dxf'\r\n\r\nexport function FilesSection({ files, query = '' }: FilesSectionProps) {\r\n const t = useTranslations('FileSelection')\r\n\r\n const { state: bimState, dispatch: bimDispatch } = React.useContext(BimContext)\r\n const { bimComponents, fragments } = bimState.bim\r\n const { state: buildingsState } = React.useContext(BuildingsContext)\r\n const { building } = buildingsState.buildings\r\n const buildingId = building?.id || -1\r\n const { dispatch: menusDispatch } = React.useContext(MenusContext)\r\n\r\n const { uploadFile } = useUploadFileToBuilding(buildingId)\r\n const { deleteFile } = useDeleteFile(buildingId)\r\n\r\n const { handleFileUpload } = useFileUploadHandler({\r\n buildingId,\r\n tag: 'file',\r\n uploadFile,\r\n })\r\n\r\n const { handleDeleteFile } = useFileDeleteHandler({\r\n deleteFile,\r\n onDeleteSuccess: () => {\r\n console.log('File deleted successfully')\r\n },\r\n })\r\n\r\n // Local state for file management with isVisible property\r\n const [localFiles, setLocalFiles] = React.useState<(IFile & { isVisible?: boolean })[]>([])\r\n const [activeIDSFileId, setActiveIDSFileId] = React.useState<number | null>(null)\r\n\r\n // Initialize and sync files\r\n React.useEffect(() => {\r\n setLocalFiles(prevFiles => {\r\n const visibilityMap = new Map(\r\n prevFiles.map(f => [f.id, f.isVisible ?? false])\r\n )\r\n return files\r\n .filter(file => file.tag !== 'user')\r\n .filter(file => (file as any).type !== 'map-file')\r\n .map(file => {\r\n // Preserve the user's toggle state for already-tracked files; otherwise honor\r\n // the persisted visibility so files added in-session show as added (not disabled).\r\n let isVisible = visibilityMap.has(file.id) ? visibilityMap.get(file.id)! : (file.isVisible ?? false)\r\n if (file.extension === 'ids' && activeIDSFileId === file.id) {\r\n isVisible = true\r\n } else if (file.extension === 'ids' && activeIDSFileId !== null && activeIDSFileId !== file.id) {\r\n isVisible = false\r\n }\r\n return { ...file, isVisible }\r\n })\r\n .sort((a, b) => a.name.localeCompare(b.name))\r\n })\r\n }, [files, activeIDSFileId])\r\n\r\n // Listen to IDS reset events\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n try {\r\n const idsManager = bimComponents.get(IDSManager)\r\n const handleIDSReset = () => {\r\n setActiveIDSFileId(null)\r\n setLocalFiles(prevFiles =>\r\n prevFiles.map(file =>\r\n file.extension === 'ids' ? { ...file, isVisible: false } : file\r\n )\r\n )\r\n }\r\n idsManager.onReset.add(handleIDSReset)\r\n return () => { idsManager.onReset.remove(handleIDSReset) }\r\n } catch {\r\n return\r\n }\r\n }, [bimComponents])\r\n\r\n const modelManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(ModelManager) } catch { return null }\r\n }, [bimComponents])\r\n const modelManagerRef = React.useRef(modelManager)\r\n React.useEffect(() => { modelManagerRef.current = modelManager }, [modelManager])\r\n\r\n const dxfManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(DXFManager) } catch { return null }\r\n }, [bimComponents])\r\n // Loaded DXF groups keyed by file id, so visibility toggles can show/hide them.\r\n const dxfGroupsRef = React.useRef<Map<string, THREE.Group>>(new Map())\r\n\r\n // Floating scene markers (pin + actions card) for visible placeable files.\r\n const markersRef = React.useRef<Map<string, { marker: CSS2DObject; file: IFile }>>(new Map())\r\n // Bumped after an object finishes loading so the marker reconcile effect re-runs.\r\n const [loadedTick, setLoadedTick] = React.useState(0)\r\n\r\n const toggleDxfVisibility = React.useCallback(async (file: IFile, visible: boolean) => {\r\n if (!dxfManager || !bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const key = file.id.toString()\r\n const existing = dxfGroupsRef.current.get(key)\r\n if (visible) {\r\n if (existing) { existing.visible = true; return }\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${file.id}`)\r\n if (!res.ok) throw new Error(`Failed to get download URL: ${res.status}`)\r\n const { presignedUrl } = await res.json()\r\n const group = await dxfManager.parse(presignedUrl)\r\n group.name = key\r\n const placed = file.x != null && file.y != null && file.z != null\r\n group.position.copy(placed\r\n ? new THREE.Vector3(file.x as number, file.y as number, file.z as number)\r\n : new THREE.Vector3())\r\n group.scale.setScalar(0.001)\r\n if (file.rotation != null) group.rotation.y = file.rotation as number\r\n world.scene.three.add(group)\r\n dxfGroupsRef.current.set(key, group)\r\n setLoadedTick(t => t + 1)\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load DXF \"${file.name}\":`, err)\r\n }\r\n } else if (existing) {\r\n existing.visible = false\r\n }\r\n }, [dxfManager, bimComponents])\r\n\r\n // Gizmo controllers keyed by file name (one gizmo per file at most)\r\n const gizmoControllersRef = React.useRef<Map<string, GizmoController>>(new Map())\r\n\r\n // Track the file ID currently being repositioned so useFile can provide updateFile\r\n const [moveFileId, setMoveFileId] = React.useState<number | null>(null)\r\n const { updateFile } = useFile(moveFileId)\r\n const updateFileRef = React.useRef(updateFile)\r\n React.useEffect(() => { updateFileRef.current = updateFile }, [updateFile])\r\n\r\n const fetchDbFileAsFile = async (file: IFile): Promise<globalThis.File> => {\r\n const response = await fetch(file.url)\r\n const blob = await response.blob()\r\n const mimeType = file.extension === 'ids' ? 'application/xml' : 'application/octet-stream'\r\n return new globalThis.File([blob], file.name, { type: mimeType })\r\n }\r\n\r\n // View handler: IDS, BCF, and 3D file visibility\r\n const handleBimView = React.useCallback(async (file: IFile & { isVisible?: boolean }, newVisibility: boolean) => {\r\n // IDS files\r\n if (file.extension === 'ids') {\r\n const idsManager = bimComponents?.get(IDSManager)\r\n if (!idsManager) return\r\n idsManager.enabled = newVisibility\r\n if (newVisibility) {\r\n setActiveIDSFileId(file.id)\r\n try {\r\n const dbFile = await fetchDbFileAsFile(file)\r\n await idsManager.loadFromFile(dbFile)\r\n await idsManager.loadAndTestIDS()\r\n } catch (error) {\r\n console.error('Error loading IDS file:', error)\r\n }\r\n } else {\r\n setActiveIDSFileId(null)\r\n await idsManager.reset()\r\n }\r\n }\r\n\r\n // BCF files\r\n if (file.extension === 'bcf') {\r\n menusDispatch({ type: 'SET_SIDEBAR_SELECTED_TAB', payload: { selectedTab: 'communication' } })\r\n const bcfManager = bimComponents?.get(BCFTopicsManager)\r\n if (!bcfManager) return\r\n try {\r\n const { topics } = await bcfManager.loadBCFFromUrl(file.url)\r\n if (topics?.length) {\r\n topics.forEach((topic) => {\r\n const exists = bimState.bim.bcfTopics.find(t => t.guid === topic.guid)\r\n if (!exists) {\r\n bimDispatch({ type: 'ADD_BCF_TOPIC', payload: { bcfTopic: topic } })\r\n }\r\n })\r\n }\r\n } catch (error) {\r\n console.error('Failed to load BCF from URL:', error)\r\n }\r\n }\r\n\r\n // 3D model files (GLB/GLTF/FBX/OBJ) managed by ModelManager\r\n if (is3DFile(file.extension) && modelManager) {\r\n const existingModel = modelManager.getModelByName(file.name)\r\n if (newVisibility) {\r\n if (existingModel) {\r\n // Already in scene — just show it\r\n existingModel.model.visible = true\r\n } else {\r\n // Not yet loaded — get a fresh presigned download URL then load\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${file.id}`)\r\n if (!res.ok) throw new Error(`Failed to get download URL: ${res.status}`)\r\n const { presignedUrl } = await res.json()\r\n const position = (file.x != null && file.y != null && file.z != null)\r\n ? new THREE.Vector3(file.x as number, file.y as number, file.z as number)\r\n : new THREE.Vector3(0, 0, 0)\r\n const rotation = file.rotation != null\r\n ? new THREE.Euler(0, file.rotation, 0)\r\n : undefined\r\n await modelManager.load(presignedUrl, file.id.toString(), file.name, {\r\n position,\r\n rotation,\r\n extension: file.extension ?? undefined,\r\n })\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load model \"${file.name}\":`, err)\r\n }\r\n }\r\n } else {\r\n if (existingModel) {\r\n existingModel.model.visible = false\r\n }\r\n }\r\n if (fragments) fragments.core.update(true)\r\n }\r\n\r\n // DXF files managed via DXFManager\r\n if (file.extension === 'dxf') {\r\n await toggleDxfVisibility(file, newVisibility)\r\n if (fragments) fragments.core.update(true)\r\n }\r\n setLoadedTick(t => t + 1)\r\n }, [bimComponents, menusDispatch, bimState, bimDispatch, modelManager, fragments, toggleDxfVisibility])\r\n\r\n const highlighter = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(Highlighter) } catch { return null }\r\n }, [bimComponents])\r\n\r\n // Raycast into fragments (same logic as useFilePlacement)\r\n const raycast = React.useCallback(async (data: {\r\n camera: THREE.PerspectiveCamera | THREE.OrthographicCamera\r\n mouse: THREE.Vector2\r\n dom: HTMLCanvasElement\r\n }) => {\r\n if (!fragments || !highlighter) return null\r\n const results = []\r\n for (const [modelName, model] of fragments.core.models.list) {\r\n if (highlighter.isModelDisabled(modelName)) continue\r\n const result = await model.raycast(data)\r\n if (result) results.push(result)\r\n }\r\n await Promise.all(results)\r\n if (results.length === 0) return null\r\n let closest = results[0]\r\n for (let i = 1; i < results.length; i++) {\r\n if (results[i].distance < closest.distance) closest = results[i]\r\n }\r\n return closest\r\n }, [fragments, highlighter])\r\n\r\n // Track the file currently being placed (unplaced file → click-to-place)\r\n const [placingFile, setPlacingFile] = React.useState<IFile | null>(null)\r\n\r\n // Click-to-place effect: crosshair + double-click raycast for unplaced files\r\n React.useEffect(() => {\r\n if (!placingFile || !bimComponents) return\r\n\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const cursor = bimComponents.get(Cursor)\r\n if (cursor) cursor.cursor = 'crosshair'\r\n\r\n const mouse = new THREE.Vector2()\r\n\r\n const handleDblClick = async (e: MouseEvent) => {\r\n mouse.x = e.clientX\r\n mouse.y = e.clientY\r\n\r\n const result = await raycast({\r\n camera: world.camera.three,\r\n mouse,\r\n dom: world.renderer!.three.domElement!,\r\n })\r\n\r\n if (result?.point && (result.point.x !== 0 || result.point.y !== 0 || result.point.z !== 0)) {\r\n const { x, y, z } = result.point\r\n setMoveFileId(placingFile.id)\r\n // Save coordinates to DB\r\n setTimeout(() => {\r\n updateFileRef.current({ x, y, z } as any)\r\n .catch((err: unknown) => console.error(`Failed to save placement for \"${placingFile.name}\":`, err))\r\n }, 50)\r\n // Load model into scene at the placed position (for 3D files)\r\n if (is3DFile(placingFile.extension) && modelManagerRef.current) {\r\n const existing = modelManagerRef.current.getModelByName(placingFile.name)\r\n if (!existing) {\r\n fetch(`/api/presignedUrlDownload/${placingFile.id}`)\r\n .then(res => res.ok ? res.json() : Promise.reject(res.status))\r\n .then(({ presignedUrl }) =>\r\n modelManagerRef.current!.load(\r\n presignedUrl,\r\n placingFile.id.toString(),\r\n placingFile.name,\r\n { position: new THREE.Vector3(x, y, z), extension: placingFile.extension ?? undefined },\r\n )\r\n )\r\n .catch((err: unknown) => console.error(`Failed to load model \"${placingFile.name}\":`, err))\r\n } else {\r\n existing.model.position.set(x, y, z)\r\n existing.model.visible = true\r\n }\r\n }\r\n // Update local state so it shows as placed and visible\r\n placingFile.x = x\r\n placingFile.y = y\r\n placingFile.z = z\r\n setLocalFiles(prev => prev.map(f => f.id === placingFile.id ? { ...f, x, y, z, isVisible: true } : f))\r\n setPlacingFile(null)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') {\r\n setPlacingFile(null)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }\r\n\r\n document.addEventListener('dblclick', handleDblClick)\r\n document.addEventListener('keydown', handleKeyDown)\r\n\r\n return () => {\r\n document.removeEventListener('dblclick', handleDblClick)\r\n document.removeEventListener('keydown', handleKeyDown)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }, [placingFile, bimComponents, raycast])\r\n\r\n // Resolve the scene object for a file (loaded 3D model or DXF group), if present.\r\n const getSceneObject = React.useCallback((file: IFile): THREE.Object3D | null => {\r\n if (file.extension === 'dxf') return dxfGroupsRef.current.get(file.id.toString()) ?? null\r\n return modelManager?.getModelByName(file.name)?.model ?? null\r\n }, [modelManager])\r\n\r\n // Attach a transform gizmo to a placed object (or re-set its mode); saves on accept.\r\n const editObject = React.useCallback((file: IFile, mode: 'translate' | 'rotate' | 'scale' = 'translate') => {\r\n if (!bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n const obj = getSceneObject(file)\r\n if (!obj) return\r\n\r\n const key = file.id.toString()\r\n const existing = gizmoControllersRef.current.get(key)\r\n if (existing) { existing.setMode(mode); return }\r\n\r\n const gizmo = new GizmoController(world)\r\n const cleanup = (save: boolean) => {\r\n if (save) {\r\n const { x, y, z } = obj.position\r\n const rotation = obj.rotation.y\r\n updateFileRef.current({ x, y, z, rotation } as any)\r\n .catch((err: unknown) => console.error(`Failed to save position for \"${file.name}\":`, err))\r\n file.x = x; file.y = y; file.z = z; file.rotation = rotation\r\n }\r\n gizmoControllersRef.current.delete(key)\r\n setMoveFileId(null)\r\n }\r\n gizmo.onAccept = () => cleanup(true)\r\n gizmo.onCancel = () => cleanup(false)\r\n gizmo.setMode(mode)\r\n setMoveFileId(file.id)\r\n gizmo.attach(obj)\r\n gizmoControllersRef.current.set(key, gizmo)\r\n }, [bimComponents, getSceneObject])\r\n\r\n // Move handler: unplaced files → click-to-place; placed files → gizmo (load DXF first if needed)\r\n const handleBimMove = React.useCallback((file: IFile) => {\r\n const isPlaced = file.x != null && file.y != null && file.z != null\r\n if (!isPlaced) {\r\n setPlacingFile(file)\r\n setMoveFileId(file.id)\r\n return\r\n }\r\n if (file.extension === 'dxf' && !dxfGroupsRef.current.has(file.id.toString())) {\r\n void toggleDxfVisibility(file, true).then(() => editObject(file, 'translate'))\r\n return\r\n }\r\n editObject(file, 'translate')\r\n }, [editObject, toggleDxfVisibility])\r\n\r\n const { handleAction, deleteDialog } = useFileActions({\r\n files: localFiles,\r\n setFiles: setLocalFiles,\r\n buildingId,\r\n handleDeleteFile,\r\n onView: handleBimView,\r\n onMove: handleBimMove,\r\n })\r\n\r\n const { handleAddFile } = useCommonFileUpload({\r\n buildingId,\r\n acceptedFileTypes: '*',\r\n handleFileUpload: async (domFile: globalThis.File) => {\r\n await handleFileUpload(domFile)\r\n },\r\n onUploadSuccess: () => {\r\n console.log('File uploaded successfully')\r\n },\r\n onUploadError: (error) => {\r\n console.error('Error uploading file:', error)\r\n },\r\n })\r\n\r\n const filteredFiles = React.useMemo(() => {\r\n if (!query.trim()) return localFiles\r\n return localFiles.filter(file =>\r\n file.name.toLowerCase().includes(query.toLowerCase())\r\n )\r\n }, [localFiles, query])\r\n\r\n const areAllHidden = localFiles.every(f => !f.isVisible)\r\n\r\n const handleSwitchVariant = () => ({\r\n checked: !areAllHidden,\r\n onCheckedChange: async (checked: boolean) => {\r\n setLocalFiles(prev => prev.map(f => ({ ...f, isVisible: checked })))\r\n for (const f of localFiles) {\r\n if (is3DFile(f.extension) && modelManager) {\r\n const existingModel = modelManager.getModelByName(f.name)\r\n if (checked) {\r\n if (existingModel) {\r\n existingModel.model.visible = true\r\n } else {\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${f.id}`)\r\n if (res.ok) {\r\n const { presignedUrl } = await res.json()\r\n const position = (f.x != null && f.y != null && f.z != null)\r\n ? new THREE.Vector3(f.x as number, f.y as number, f.z as number)\r\n : new THREE.Vector3(0, 0, 0)\r\n await modelManager.load(presignedUrl, f.id.toString(), f.name, {\r\n position,\r\n extension: f.extension ?? undefined,\r\n })\r\n }\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load model \"${f.name}\":`, err)\r\n }\r\n }\r\n } else if (existingModel) {\r\n existingModel.model.visible = false\r\n }\r\n } else if (f.extension === 'dxf') {\r\n await toggleDxfVisibility(f, checked)\r\n }\r\n }\r\n if (fragments) fragments.core.update(true)\r\n setLoadedTick(t => t + 1)\r\n },\r\n })\r\n\r\n // createFileMarker expects an AddedFile; build a lightweight one from the DB file.\r\n const makeMarkerInput = React.useCallback((file: IFile, position: THREE.Vector3): AddedFile => ({\r\n id: file.id.toString(),\r\n file: new File([], file.name, { type: (file as any).mimeType ?? '' }),\r\n position,\r\n }), [])\r\n\r\n // Reconcile floating markers with the visible + loaded placeable files.\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const wanted = new Map(\r\n localFiles\r\n .filter(f => f.isVisible && isPlaceable(f.extension))\r\n .map(f => [f.id.toString(), f] as const),\r\n )\r\n\r\n for (const [key, entry] of markersRef.current) {\r\n if (!wanted.has(key)) {\r\n removeMarker(entry.marker, world)\r\n markersRef.current.delete(key)\r\n }\r\n }\r\n\r\n for (const [key, file] of wanted) {\r\n if (markersRef.current.has(key)) continue\r\n const obj = getSceneObject(file)\r\n if (!obj) continue // not loaded yet — picked up once loadedTick bumps\r\n const marker = createFileMarker(makeMarkerInput(file, obj.position.clone()), obj, world, (action) => {\r\n if (action === 'delete') { handleAction('delete', file); return }\r\n editObject(file, action === 'move' ? 'translate' : action)\r\n })\r\n if (marker) markersRef.current.set(key, { marker, file })\r\n }\r\n }, [localFiles, loadedTick, bimComponents, getSceneObject, editObject, handleAction, makeMarkerInput])\r\n\r\n // Markers follow their object each frame and hide while that object's gizmo is active.\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n let raf = 0\r\n const worldPos = new THREE.Vector3()\r\n const tick = () => {\r\n markersRef.current.forEach(({ marker, file }, key) => {\r\n const obj = getSceneObject(file)\r\n if (!obj) { marker.visible = false; return }\r\n obj.getWorldPosition(worldPos)\r\n marker.position.set(worldPos.x, worldPos.y + 0.2, worldPos.z)\r\n marker.visible = !gizmoControllersRef.current.has(key)\r\n })\r\n raf = requestAnimationFrame(tick)\r\n }\r\n raf = requestAnimationFrame(tick)\r\n return () => cancelAnimationFrame(raf)\r\n }, [bimComponents, getSceneObject])\r\n\r\n // Clean up all markers on unmount so their DOM/React roots don't leak.\r\n const markersCleanupRef = React.useRef(markersRef.current)\r\n React.useEffect(() => {\r\n const markers = markersCleanupRef.current\r\n return () => {\r\n markers.forEach(({ marker }) => removeMarker(marker))\r\n markers.clear()\r\n }\r\n }, [])\r\n\r\n return (\r\n <>\r\n <CollapsibleSection\r\n title={t('filesTitle')}\r\n icon={LR.FileText}\r\n className=\"max-h-40 overflow-y-auto\"\r\n itemCount={filteredFiles.length}\r\n switchVariant={handleSwitchVariant()}\r\n onAddItem={handleAddFile}\r\n addItemTitle={t('addFileTitle')}\r\n >\r\n <div className=\"space-y-1\">\r\n {filteredFiles.map((item) => (\r\n <FileItemComponent\r\n key={item.id}\r\n file={item}\r\n onAction={handleAction}\r\n options={isPlaceable(item.extension) ? OPTIONS_3D : OPTIONS_NON_3D}\r\n translationKey=\"FileSelection\"\r\n confirmDelete={false}\r\n />\r\n ))}\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <ConfirmDialog\r\n isOpen={deleteDialog.isOpen}\r\n isDeleting={deleteDialog.isDeleting}\r\n onOpenChange={deleteDialog.onOpenChange}\r\n handleConfirm={deleteDialog.onConfirm}\r\n itemName={deleteDialog.itemName}\r\n />\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["t"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.tsx"],"sourcesContent":["import * as React from 'react'\r\nimport * as LR from 'lucide-react'\r\nimport { BimContext, BuildingsContext, MenusContext } from '../../../../../../../../store'\r\nimport { ModelManager } from '../../../../ModelManager'\r\nimport { DXFManager } from '../../../../DXFLoader'\r\nimport * as THREE from 'three'\r\nimport { GizmoController } from '../../../../../utils/GizmoController'\r\nimport { CurrentWorld } from '../../../../CurrentWorld'\r\nimport { Cursor } from '../../../../Cursor'\r\nimport { Highlighter } from '../../../../Highlighter'\r\nimport { CollapsibleSection } from '../../../../../../../ui/CollapsibleSection'\r\nimport { useTranslations } from 'next-intl'\r\nimport ConfirmDialog from '../../../../../../../ConfirmDialog'\r\nimport { useUploadFileToBuilding, useDeleteFile, useFile } from '../../../../../../../../hooks/files/files'\r\nimport { useFileUploadHandler, useFileDeleteHandler, FileItemComponent, useFileActions, useCommonFileUpload } from '../../../../../../../ui/FilesManager'\r\nimport { IDSManager } from '../../../../IDSManager'\r\nimport { BCFTopicsManager } from '../../../../BCFTopicsManager'\r\nimport { createFileMarker, removeMarker, type AddedFile } from '../../../../tools/AddToBim/src/FileMarkerUtils'\r\nimport type { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'\r\nimport { DbFile as IFile } from '../../../../../../../../types/dbTypes'\r\n\r\n// Hoist options arrays so the array identity is stable across renders.\r\n// Inline `options={[...]}` defeats React.memo on FileItemComponent.\r\ntype FileAction = import('../../../../../../../../types/global').FileAction\r\nconst OPTIONS_3D: FileAction[] = ['download', 'view', 'move', 'info', 'delete']\r\nconst OPTIONS_NON_3D: FileAction[] = ['download', 'view', 'delete']\r\n\r\ninterface FilesSectionProps {\r\n files: IFile[]\r\n query?: string\r\n}\r\n\r\nconst is3DFile = (ext?: string | null): boolean => {\r\n if (!ext) return false\r\n return ['glb', 'gltf', 'fbx', 'obj', 'collada'].includes(ext.toLowerCase())\r\n}\r\n\r\n// Files that live in the 3D scene and can be moved/scaled (3D models + DXF drawings).\r\nconst isPlaceable = (ext?: string | null): boolean => is3DFile(ext) || ext?.toLowerCase() === 'dxf'\r\n\r\nexport function FilesSection({ files, query = '' }: FilesSectionProps) {\r\n const t = useTranslations('FileSelection')\r\n\r\n const { state: bimState, dispatch: bimDispatch } = React.useContext(BimContext)\r\n const { bimComponents, fragments } = bimState.bim\r\n const { state: buildingsState } = React.useContext(BuildingsContext)\r\n const { building } = buildingsState.buildings\r\n const buildingId = building?.id || -1\r\n const { dispatch: menusDispatch } = React.useContext(MenusContext)\r\n\r\n const { uploadFile } = useUploadFileToBuilding(buildingId)\r\n const { deleteFile } = useDeleteFile(buildingId)\r\n\r\n const { handleFileUpload } = useFileUploadHandler({\r\n buildingId,\r\n tag: 'file',\r\n uploadFile,\r\n })\r\n\r\n const { handleDeleteFile } = useFileDeleteHandler({\r\n deleteFile,\r\n onDeleteSuccess: () => {\r\n console.log('File deleted successfully')\r\n },\r\n })\r\n\r\n // Local state for file management with isVisible property\r\n const [localFiles, setLocalFiles] = React.useState<(IFile & { isVisible?: boolean })[]>([])\r\n const [activeIDSFileId, setActiveIDSFileId] = React.useState<number | null>(null)\r\n\r\n // Initialize and sync files\r\n React.useEffect(() => {\r\n setLocalFiles(prevFiles => {\r\n const visibilityMap = new Map(\r\n prevFiles.map(f => [f.id, f.isVisible ?? false])\r\n )\r\n // A file is \"visible\" only if it's actually present and shown in the 3D scene\r\n // (a loaded model, a loaded DXF group, or the active IDS). This mirrors the Map\r\n // viewer, which derives visibility from on-scene state (mapFileIds) rather than\r\n // from the persisted `file.isVisible` flag — so nothing shows as visible on open\r\n // unless it has been toggled on or just placed/added into the scene.\r\n const isFileInScene = (file: IFile): boolean => {\r\n if (file.extension === 'ids') return activeIDSFileId === file.id\r\n if (file.extension === 'dxf') {\r\n return dxfGroupsRef.current.get(file.id.toString())?.visible === true\r\n }\r\n if (is3DFile(file.extension)) {\r\n const info = modelManagerRef.current?.getModelByName(file.name)\r\n return info ? info.model.visible !== false : false\r\n }\r\n return false\r\n }\r\n return files\r\n .filter(file => file.tag !== 'user')\r\n .filter(file => (file as any).type !== 'map-file')\r\n .map(file => {\r\n // Preserve the user's in-session toggle for already-tracked files; for files\r\n // seen for the first time, derive visibility from the actual scene state.\r\n let isVisible = visibilityMap.has(file.id) ? visibilityMap.get(file.id)! : isFileInScene(file)\r\n if (file.extension === 'ids' && activeIDSFileId === file.id) {\r\n isVisible = true\r\n } else if (file.extension === 'ids' && activeIDSFileId !== null && activeIDSFileId !== file.id) {\r\n isVisible = false\r\n }\r\n return { ...file, isVisible }\r\n })\r\n .sort((a, b) => a.name.localeCompare(b.name))\r\n })\r\n }, [files, activeIDSFileId])\r\n\r\n // Listen to IDS reset events\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n try {\r\n const idsManager = bimComponents.get(IDSManager)\r\n const handleIDSReset = () => {\r\n setActiveIDSFileId(null)\r\n setLocalFiles(prevFiles =>\r\n prevFiles.map(file =>\r\n file.extension === 'ids' ? { ...file, isVisible: false } : file\r\n )\r\n )\r\n }\r\n idsManager.onReset.add(handleIDSReset)\r\n return () => { idsManager.onReset.remove(handleIDSReset) }\r\n } catch {\r\n return\r\n }\r\n }, [bimComponents])\r\n\r\n const modelManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(ModelManager) } catch { return null }\r\n }, [bimComponents])\r\n const modelManagerRef = React.useRef(modelManager)\r\n React.useEffect(() => { modelManagerRef.current = modelManager }, [modelManager])\r\n\r\n const dxfManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(DXFManager) } catch { return null }\r\n }, [bimComponents])\r\n // Loaded DXF groups keyed by file id, so visibility toggles can show/hide them.\r\n const dxfGroupsRef = React.useRef<Map<string, THREE.Group>>(new Map())\r\n\r\n // Floating scene markers (pin + actions card) for visible placeable files.\r\n const markersRef = React.useRef<Map<string, { marker: CSS2DObject; file: IFile }>>(new Map())\r\n // Bumped after an object finishes loading so the marker reconcile effect re-runs.\r\n const [loadedTick, setLoadedTick] = React.useState(0)\r\n\r\n const toggleDxfVisibility = React.useCallback(async (file: IFile, visible: boolean) => {\r\n if (!dxfManager || !bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const key = file.id.toString()\r\n const existing = dxfGroupsRef.current.get(key)\r\n if (visible) {\r\n if (existing) { existing.visible = true; return }\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${file.id}`)\r\n if (!res.ok) throw new Error(`Failed to get download URL: ${res.status}`)\r\n const { presignedUrl } = await res.json()\r\n const group = await dxfManager.parse(presignedUrl)\r\n group.name = key\r\n const placed = file.x != null && file.y != null && file.z != null\r\n group.position.copy(placed\r\n ? new THREE.Vector3(file.x as number, file.y as number, file.z as number)\r\n : new THREE.Vector3())\r\n group.scale.setScalar(0.001)\r\n if (file.rotation != null) group.rotation.y = file.rotation as number\r\n world.scene.three.add(group)\r\n dxfGroupsRef.current.set(key, group)\r\n setLoadedTick(t => t + 1)\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load DXF \"${file.name}\":`, err)\r\n }\r\n } else if (existing) {\r\n existing.visible = false\r\n }\r\n }, [dxfManager, bimComponents])\r\n\r\n // Gizmo controllers keyed by file name (one gizmo per file at most)\r\n const gizmoControllersRef = React.useRef<Map<string, GizmoController>>(new Map())\r\n\r\n // Track the file ID currently being repositioned so useFile can provide updateFile\r\n const [moveFileId, setMoveFileId] = React.useState<number | null>(null)\r\n const { updateFile } = useFile(moveFileId)\r\n const updateFileRef = React.useRef(updateFile)\r\n React.useEffect(() => { updateFileRef.current = updateFile }, [updateFile])\r\n\r\n const fetchDbFileAsFile = async (file: IFile): Promise<globalThis.File> => {\r\n const response = await fetch(file.url)\r\n const blob = await response.blob()\r\n const mimeType = file.extension === 'ids' ? 'application/xml' : 'application/octet-stream'\r\n return new globalThis.File([blob], file.name, { type: mimeType })\r\n }\r\n\r\n // View handler: IDS, BCF, and 3D file visibility\r\n const handleBimView = React.useCallback(async (file: IFile & { isVisible?: boolean }, newVisibility: boolean) => {\r\n // IDS files\r\n if (file.extension === 'ids') {\r\n const idsManager = bimComponents?.get(IDSManager)\r\n if (!idsManager) return\r\n idsManager.enabled = newVisibility\r\n if (newVisibility) {\r\n setActiveIDSFileId(file.id)\r\n try {\r\n const dbFile = await fetchDbFileAsFile(file)\r\n await idsManager.loadFromFile(dbFile)\r\n await idsManager.loadAndTestIDS()\r\n } catch (error) {\r\n console.error('Error loading IDS file:', error)\r\n }\r\n } else {\r\n setActiveIDSFileId(null)\r\n await idsManager.reset()\r\n }\r\n }\r\n\r\n // BCF files\r\n if (file.extension === 'bcf') {\r\n menusDispatch({ type: 'SET_SIDEBAR_SELECTED_TAB', payload: { selectedTab: 'communication' } })\r\n const bcfManager = bimComponents?.get(BCFTopicsManager)\r\n if (!bcfManager) return\r\n try {\r\n const { topics } = await bcfManager.loadBCFFromUrl(file.url)\r\n if (topics?.length) {\r\n topics.forEach((topic) => {\r\n const exists = bimState.bim.bcfTopics.find(t => t.guid === topic.guid)\r\n if (!exists) {\r\n bimDispatch({ type: 'ADD_BCF_TOPIC', payload: { bcfTopic: topic } })\r\n }\r\n })\r\n }\r\n } catch (error) {\r\n console.error('Failed to load BCF from URL:', error)\r\n }\r\n }\r\n\r\n // 3D model files (GLB/GLTF/FBX/OBJ) managed by ModelManager\r\n if (is3DFile(file.extension) && modelManager) {\r\n const existingModel = modelManager.getModelByName(file.name)\r\n if (newVisibility) {\r\n if (existingModel) {\r\n // Already in scene — just show it\r\n existingModel.model.visible = true\r\n } else {\r\n // Not yet loaded — get a fresh presigned download URL then load\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${file.id}`)\r\n if (!res.ok) throw new Error(`Failed to get download URL: ${res.status}`)\r\n const { presignedUrl } = await res.json()\r\n const position = (file.x != null && file.y != null && file.z != null)\r\n ? new THREE.Vector3(file.x as number, file.y as number, file.z as number)\r\n : new THREE.Vector3(0, 0, 0)\r\n const rotation = file.rotation != null\r\n ? new THREE.Euler(0, file.rotation, 0)\r\n : undefined\r\n await modelManager.load(presignedUrl, file.id.toString(), file.name, {\r\n position,\r\n rotation,\r\n extension: file.extension ?? undefined,\r\n })\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load model \"${file.name}\":`, err)\r\n }\r\n }\r\n } else {\r\n if (existingModel) {\r\n existingModel.model.visible = false\r\n }\r\n }\r\n if (fragments) fragments.core.update(true)\r\n }\r\n\r\n // DXF files managed via DXFManager\r\n if (file.extension === 'dxf') {\r\n await toggleDxfVisibility(file, newVisibility)\r\n if (fragments) fragments.core.update(true)\r\n }\r\n setLoadedTick(t => t + 1)\r\n }, [bimComponents, menusDispatch, bimState, bimDispatch, modelManager, fragments, toggleDxfVisibility])\r\n\r\n const highlighter = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(Highlighter) } catch { return null }\r\n }, [bimComponents])\r\n\r\n // Raycast into fragments (same logic as useFilePlacement)\r\n const raycast = React.useCallback(async (data: {\r\n camera: THREE.PerspectiveCamera | THREE.OrthographicCamera\r\n mouse: THREE.Vector2\r\n dom: HTMLCanvasElement\r\n }) => {\r\n if (!fragments || !highlighter) return null\r\n const results = []\r\n for (const [modelName, model] of fragments.core.models.list) {\r\n if (highlighter.isModelDisabled(modelName)) continue\r\n const result = await model.raycast(data)\r\n if (result) results.push(result)\r\n }\r\n await Promise.all(results)\r\n if (results.length === 0) return null\r\n let closest = results[0]\r\n for (let i = 1; i < results.length; i++) {\r\n if (results[i].distance < closest.distance) closest = results[i]\r\n }\r\n return closest\r\n }, [fragments, highlighter])\r\n\r\n // Track the file currently being placed (unplaced file → click-to-place)\r\n const [placingFile, setPlacingFile] = React.useState<IFile | null>(null)\r\n\r\n // Click-to-place effect: crosshair + double-click raycast for unplaced files\r\n React.useEffect(() => {\r\n if (!placingFile || !bimComponents) return\r\n\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const cursor = bimComponents.get(Cursor)\r\n if (cursor) cursor.cursor = 'crosshair'\r\n\r\n const mouse = new THREE.Vector2()\r\n\r\n const handleDblClick = async (e: MouseEvent) => {\r\n mouse.x = e.clientX\r\n mouse.y = e.clientY\r\n\r\n const result = await raycast({\r\n camera: world.camera.three,\r\n mouse,\r\n dom: world.renderer!.three.domElement!,\r\n })\r\n\r\n if (result?.point && (result.point.x !== 0 || result.point.y !== 0 || result.point.z !== 0)) {\r\n const { x, y, z } = result.point\r\n setMoveFileId(placingFile.id)\r\n // Save coordinates to DB\r\n setTimeout(() => {\r\n updateFileRef.current({ x, y, z } as any)\r\n .catch((err: unknown) => console.error(`Failed to save placement for \"${placingFile.name}\":`, err))\r\n }, 50)\r\n // Load model into scene at the placed position (for 3D files)\r\n if (is3DFile(placingFile.extension) && modelManagerRef.current) {\r\n const existing = modelManagerRef.current.getModelByName(placingFile.name)\r\n if (!existing) {\r\n fetch(`/api/presignedUrlDownload/${placingFile.id}`)\r\n .then(res => res.ok ? res.json() : Promise.reject(res.status))\r\n .then(({ presignedUrl }) =>\r\n modelManagerRef.current!.load(\r\n presignedUrl,\r\n placingFile.id.toString(),\r\n placingFile.name,\r\n { position: new THREE.Vector3(x, y, z), extension: placingFile.extension ?? undefined },\r\n )\r\n )\r\n .catch((err: unknown) => console.error(`Failed to load model \"${placingFile.name}\":`, err))\r\n } else {\r\n existing.model.position.set(x, y, z)\r\n existing.model.visible = true\r\n }\r\n }\r\n // Update local state so it shows as placed and visible\r\n placingFile.x = x\r\n placingFile.y = y\r\n placingFile.z = z\r\n setLocalFiles(prev => prev.map(f => f.id === placingFile.id ? { ...f, x, y, z, isVisible: true } : f))\r\n setPlacingFile(null)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') {\r\n setPlacingFile(null)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }\r\n\r\n document.addEventListener('dblclick', handleDblClick)\r\n document.addEventListener('keydown', handleKeyDown)\r\n\r\n return () => {\r\n document.removeEventListener('dblclick', handleDblClick)\r\n document.removeEventListener('keydown', handleKeyDown)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }, [placingFile, bimComponents, raycast])\r\n\r\n // Resolve the scene object for a file (loaded 3D model or DXF group), if present.\r\n const getSceneObject = React.useCallback((file: IFile): THREE.Object3D | null => {\r\n if (file.extension === 'dxf') return dxfGroupsRef.current.get(file.id.toString()) ?? null\r\n return modelManager?.getModelByName(file.name)?.model ?? null\r\n }, [modelManager])\r\n\r\n // Attach a transform gizmo to a placed object (or re-set its mode); saves on accept.\r\n const editObject = React.useCallback((file: IFile, mode: 'translate' | 'rotate' | 'scale' = 'translate') => {\r\n if (!bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n const obj = getSceneObject(file)\r\n if (!obj) return\r\n\r\n const key = file.id.toString()\r\n const existing = gizmoControllersRef.current.get(key)\r\n if (existing) { existing.setMode(mode); return }\r\n\r\n const gizmo = new GizmoController(world)\r\n const cleanup = (save: boolean) => {\r\n if (save) {\r\n const { x, y, z } = obj.position\r\n const rotation = obj.rotation.y\r\n updateFileRef.current({ x, y, z, rotation } as any)\r\n .catch((err: unknown) => console.error(`Failed to save position for \"${file.name}\":`, err))\r\n file.x = x; file.y = y; file.z = z; file.rotation = rotation\r\n }\r\n gizmoControllersRef.current.delete(key)\r\n setMoveFileId(null)\r\n }\r\n gizmo.onAccept = () => cleanup(true)\r\n gizmo.onCancel = () => cleanup(false)\r\n gizmo.setMode(mode)\r\n setMoveFileId(file.id)\r\n gizmo.attach(obj)\r\n gizmoControllersRef.current.set(key, gizmo)\r\n }, [bimComponents, getSceneObject])\r\n\r\n // Move handler: unplaced files → click-to-place; placed files → gizmo (load DXF first if needed)\r\n const handleBimMove = React.useCallback((file: IFile) => {\r\n const isPlaced = file.x != null && file.y != null && file.z != null\r\n if (!isPlaced) {\r\n setPlacingFile(file)\r\n setMoveFileId(file.id)\r\n return\r\n }\r\n if (file.extension === 'dxf' && !dxfGroupsRef.current.has(file.id.toString())) {\r\n void toggleDxfVisibility(file, true).then(() => editObject(file, 'translate'))\r\n return\r\n }\r\n editObject(file, 'translate')\r\n }, [editObject, toggleDxfVisibility])\r\n\r\n const { handleAction, deleteDialog } = useFileActions({\r\n files: localFiles,\r\n setFiles: setLocalFiles,\r\n buildingId,\r\n handleDeleteFile,\r\n onView: handleBimView,\r\n onMove: handleBimMove,\r\n })\r\n\r\n const { handleAddFile } = useCommonFileUpload({\r\n buildingId,\r\n acceptedFileTypes: '*',\r\n handleFileUpload: async (domFile: globalThis.File) => {\r\n await handleFileUpload(domFile)\r\n },\r\n onUploadSuccess: () => {\r\n console.log('File uploaded successfully')\r\n },\r\n onUploadError: (error) => {\r\n console.error('Error uploading file:', error)\r\n },\r\n })\r\n\r\n const filteredFiles = React.useMemo(() => {\r\n if (!query.trim()) return localFiles\r\n return localFiles.filter(file =>\r\n file.name.toLowerCase().includes(query.toLowerCase())\r\n )\r\n }, [localFiles, query])\r\n\r\n const areAllHidden = localFiles.every(f => !f.isVisible)\r\n\r\n const handleSwitchVariant = () => ({\r\n checked: !areAllHidden,\r\n onCheckedChange: async (checked: boolean) => {\r\n setLocalFiles(prev => prev.map(f => ({ ...f, isVisible: checked })))\r\n for (const f of localFiles) {\r\n if (is3DFile(f.extension) && modelManager) {\r\n const existingModel = modelManager.getModelByName(f.name)\r\n if (checked) {\r\n if (existingModel) {\r\n existingModel.model.visible = true\r\n } else {\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${f.id}`)\r\n if (res.ok) {\r\n const { presignedUrl } = await res.json()\r\n const position = (f.x != null && f.y != null && f.z != null)\r\n ? new THREE.Vector3(f.x as number, f.y as number, f.z as number)\r\n : new THREE.Vector3(0, 0, 0)\r\n await modelManager.load(presignedUrl, f.id.toString(), f.name, {\r\n position,\r\n extension: f.extension ?? undefined,\r\n })\r\n }\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load model \"${f.name}\":`, err)\r\n }\r\n }\r\n } else if (existingModel) {\r\n existingModel.model.visible = false\r\n }\r\n } else if (f.extension === 'dxf') {\r\n await toggleDxfVisibility(f, checked)\r\n }\r\n }\r\n if (fragments) fragments.core.update(true)\r\n setLoadedTick(t => t + 1)\r\n },\r\n })\r\n\r\n // createFileMarker expects an AddedFile; build a lightweight one from the DB file.\r\n const makeMarkerInput = React.useCallback((file: IFile, position: THREE.Vector3): AddedFile => ({\r\n id: file.id.toString(),\r\n file: new File([], file.name, { type: (file as any).mimeType ?? '' }),\r\n position,\r\n }), [])\r\n\r\n // Reconcile floating markers with the visible + loaded placeable files.\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const wanted = new Map(\r\n localFiles\r\n .filter(f => f.isVisible && isPlaceable(f.extension))\r\n .map(f => [f.id.toString(), f] as const),\r\n )\r\n\r\n for (const [key, entry] of markersRef.current) {\r\n if (!wanted.has(key)) {\r\n removeMarker(entry.marker, world)\r\n markersRef.current.delete(key)\r\n }\r\n }\r\n\r\n for (const [key, file] of wanted) {\r\n if (markersRef.current.has(key)) continue\r\n const obj = getSceneObject(file)\r\n if (!obj) continue // not loaded yet — picked up once loadedTick bumps\r\n const marker = createFileMarker(makeMarkerInput(file, obj.position.clone()), obj, world, (action) => {\r\n if (action === 'delete') { handleAction('delete', file); return }\r\n editObject(file, action === 'move' ? 'translate' : action)\r\n })\r\n if (marker) markersRef.current.set(key, { marker, file })\r\n }\r\n }, [localFiles, loadedTick, bimComponents, getSceneObject, editObject, handleAction, makeMarkerInput])\r\n\r\n // Markers follow their object each frame and hide while that object's gizmo is active.\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n let raf = 0\r\n const worldPos = new THREE.Vector3()\r\n const tick = () => {\r\n markersRef.current.forEach(({ marker, file }, key) => {\r\n const obj = getSceneObject(file)\r\n if (!obj) { marker.visible = false; return }\r\n obj.getWorldPosition(worldPos)\r\n marker.position.set(worldPos.x, worldPos.y + 0.2, worldPos.z)\r\n marker.visible = !gizmoControllersRef.current.has(key)\r\n })\r\n raf = requestAnimationFrame(tick)\r\n }\r\n raf = requestAnimationFrame(tick)\r\n return () => cancelAnimationFrame(raf)\r\n }, [bimComponents, getSceneObject])\r\n\r\n // Clean up all markers on unmount so their DOM/React roots don't leak.\r\n const markersCleanupRef = React.useRef(markersRef.current)\r\n React.useEffect(() => {\r\n const markers = markersCleanupRef.current\r\n return () => {\r\n markers.forEach(({ marker }) => removeMarker(marker))\r\n markers.clear()\r\n }\r\n }, [])\r\n\r\n return (\r\n <>\r\n <CollapsibleSection\r\n title={t('filesTitle')}\r\n icon={LR.FileText}\r\n className=\"max-h-40 overflow-y-auto\"\r\n itemCount={filteredFiles.length}\r\n switchVariant={handleSwitchVariant()}\r\n onAddItem={handleAddFile}\r\n addItemTitle={t('addFileTitle')}\r\n >\r\n <div className=\"space-y-1\">\r\n {filteredFiles.map((item) => (\r\n <FileItemComponent\r\n key={item.id}\r\n file={item}\r\n onAction={handleAction}\r\n options={isPlaceable(item.extension) ? OPTIONS_3D : OPTIONS_NON_3D}\r\n translationKey=\"FileSelection\"\r\n confirmDelete={false}\r\n />\r\n ))}\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <ConfirmDialog\r\n isOpen={deleteDialog.isOpen}\r\n isDeleting={deleteDialog.isDeleting}\r\n onOpenChange={deleteDialog.onOpenChange}\r\n handleConfirm={deleteDialog.onConfirm}\r\n itemName={deleteDialog.itemName}\r\n />\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["t"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IfcToFragments.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/bim/src/IfcToFragments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA;AAG3C,qBAAa,cAAe,SAAQ,GAAG,CAAC,SAAS;IAC/C,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAG,sCAAsC,CAAS;IAEtE,OAAO,UAAO;IAGd,qBAAqB;mBAA8B,OAAO;iBAAW,MAAM;OAAK;IAEhF,OAAO,CAAC,UAAU,CAAiC;gBAEvC,UAAU,EAAE,GAAG,CAAC,UAAU;YAMxB,oBAAoB;
|
|
1
|
+
{"version":3,"file":"IfcToFragments.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/bim/src/IfcToFragments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA;AAG3C,qBAAa,cAAe,SAAQ,GAAG,CAAC,SAAS;IAC/C,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAG,sCAAsC,CAAS;IAEtE,OAAO,UAAO;IAGd,qBAAqB;mBAA8B,OAAO;iBAAW,MAAM;OAAK;IAEhF,OAAO,CAAC,UAAU,CAAiC;gBAEvC,UAAU,EAAE,GAAG,CAAC,UAAU;YAMxB,oBAAoB;IAY5B,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;CAqCpD"}
|
|
@@ -13,7 +13,7 @@ const _IfcToFragments = class _IfcToFragments extends OBC.Component {
|
|
|
13
13
|
async initializeSerializer() {
|
|
14
14
|
try {
|
|
15
15
|
this.serializer = new FRAGS.IfcImporter();
|
|
16
|
-
this.serializer.wasm = { absolute: true, path: "https://unpkg.com/web-ifc@0.0.
|
|
16
|
+
this.serializer.wasm = { absolute: true, path: "https://unpkg.com/web-ifc@0.0.77/" };
|
|
17
17
|
} catch (error) {
|
|
18
18
|
console.warn("Could not initialize IFC serializer:", error);
|
|
19
19
|
this.serializer = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/bim/src/IfcToFragments.ts"],"sourcesContent":["import * as OBC from '@thatopen/components'\r\nimport * as FRAGS from '@thatopen/fragments'\r\n\r\nexport class IfcToFragments extends OBC.Component {\r\n static readonly uuid = 'ef6bd7b0-0903-4621-bc35-b0c7516576f9' as const\r\n\r\n enabled = true\r\n\r\n // Events for loading states\r\n onLoadingStateChanged = new OBC.Event<{ isLoading: boolean, message: string }>()\r\n\r\n private serializer: FRAGS.IfcImporter | null = null\r\n\r\n constructor(components: OBC.Components) {\r\n super(components)\r\n components.add(IfcToFragments.uuid, this)\r\n this.initializeSerializer()\r\n }\r\n\r\n private async initializeSerializer() {\r\n try {\r\n this.serializer = new FRAGS.IfcImporter()\r\n this.serializer.wasm = { absolute: true, path: 'https://unpkg.com/web-ifc@0.0.
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/bim/src/IfcToFragments.ts"],"sourcesContent":["import * as OBC from '@thatopen/components'\r\nimport * as FRAGS from '@thatopen/fragments'\r\n\r\nexport class IfcToFragments extends OBC.Component {\r\n static readonly uuid = 'ef6bd7b0-0903-4621-bc35-b0c7516576f9' as const\r\n\r\n enabled = true\r\n\r\n // Events for loading states\r\n onLoadingStateChanged = new OBC.Event<{ isLoading: boolean, message: string }>()\r\n\r\n private serializer: FRAGS.IfcImporter | null = null\r\n\r\n constructor(components: OBC.Components) {\r\n super(components)\r\n components.add(IfcToFragments.uuid, this)\r\n this.initializeSerializer()\r\n }\r\n\r\n private async initializeSerializer() {\r\n try {\r\n this.serializer = new FRAGS.IfcImporter()\r\n // ⚠️⚠️ IMPORTANT: keep this web-ifc version in sync with the `web-ifc` version\r\n this.serializer.wasm = { absolute: true, path: 'https://unpkg.com/web-ifc@0.0.77/' }\r\n }\r\n catch (error) {\r\n console.warn('Could not initialize IFC serializer:', error)\r\n this.serializer = null\r\n }\r\n }\r\n\r\n async loadFromFile(file: File): Promise<Uint8Array> {\r\n\r\n try {\r\n const fileBuffer = await file.arrayBuffer()\r\n\r\n // Handle only IFC files - convert to fragments first\r\n this.onLoadingStateChanged.trigger({ isLoading: true, message: 'Converting IFC to fragments...' })\r\n\r\n if (this.serializer) {\r\n const ifcBytes = new Uint8Array(fileBuffer)\r\n const fragmentBytes = await this.serializer.process({ bytes: ifcBytes })\r\n\r\n if (fragmentBytes) {\r\n // Update loading message\r\n this.onLoadingStateChanged.trigger({ isLoading: true, message: 'Loading BIM Model...' })\r\n\r\n // Convert Uint8Array to ArrayBuffer\r\n const fragmentBuffer = new ArrayBuffer(fragmentBytes.byteLength)\r\n // Return the bytes so the caller can upload a .frag file\r\n return fragmentBytes\r\n }\r\n else {\r\n this.onLoadingStateChanged.trigger({ isLoading: false, message: '' })\r\n throw new Error('Failed to serialize IFC to fragments')\r\n }\r\n }\r\n else {\r\n this.onLoadingStateChanged.trigger({ isLoading: false, message: '' })\r\n throw new Error('IFC serializer not available')\r\n }\r\n }\r\n catch (error) {\r\n // Make sure to clear loading state on error\r\n this.onLoadingStateChanged.trigger({ isLoading: false, message: '' })\r\n throw new Error(`Error loading file: ${error}`)\r\n }\r\n }\r\n}\r\n"],"mappings":"AAAA,YAAY,SAAS;AACrB,YAAY,WAAW;AAEhB,MAAM,kBAAN,MAAM,wBAAuB,IAAI,UAAU;AAAA,EAUhD,YAAY,YAA4B;AACtC,UAAM,UAAU;AARlB,mBAAU;AAGV;AAAA,iCAAwB,IAAI,IAAI,MAA+C;AAE/E,SAAQ,aAAuC;AAI7C,eAAW,IAAI,gBAAe,MAAM,IAAI;AACxC,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEA,MAAc,uBAAuB;AACnC,QAAI;AACF,WAAK,aAAa,IAAI,MAAM,YAAY;AAExC,WAAK,WAAW,OAAO,EAAE,UAAU,MAAM,MAAM,oCAAoC;AAAA,IACrF,SACO,OAAO;AACZ,cAAQ,KAAK,wCAAwC,KAAK;AAC1D,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,MAAiC;AAElD,QAAI;AACF,YAAM,aAAa,MAAM,KAAK,YAAY;AAG1C,WAAK,sBAAsB,QAAQ,EAAE,WAAW,MAAM,SAAS,iCAAiC,CAAC;AAEjG,UAAI,KAAK,YAAY;AACnB,cAAM,WAAW,IAAI,WAAW,UAAU;AAC1C,cAAM,gBAAgB,MAAM,KAAK,WAAW,QAAQ,EAAE,OAAO,SAAS,CAAC;AAEvE,YAAI,eAAe;AAEjB,eAAK,sBAAsB,QAAQ,EAAE,WAAW,MAAM,SAAS,uBAAuB,CAAC;AAGvF,gBAAM,iBAAiB,IAAI,YAAY,cAAc,UAAU;AAE/D,iBAAO;AAAA,QACT,OACK;AACH,eAAK,sBAAsB,QAAQ,EAAE,WAAW,OAAO,SAAS,GAAG,CAAC;AACpE,gBAAM,IAAI,MAAM,sCAAsC;AAAA,QACxD;AAAA,MACF,OACK;AACH,aAAK,sBAAsB,QAAQ,EAAE,WAAW,OAAO,SAAS,GAAG,CAAC;AACpE,cAAM,IAAI,MAAM,8BAA8B;AAAA,MAChD;AAAA,IACF,SACO,OAAO;AAEZ,WAAK,sBAAsB,QAAQ,EAAE,WAAW,OAAO,SAAS,GAAG,CAAC;AACpE,YAAM,IAAI,MAAM,uBAAuB,KAAK,EAAE;AAAA,IAChD;AAAA,EACF;AACF;AAjEa,gBACK,OAAO;AADlB,IAAM,iBAAN;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoadIfcAsFragments.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/bim/src/LoadIfcAsFragments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA;AAK3C,qBAAa,kBAAmB,SAAQ,GAAG,CAAC,SAAS;IACnD,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAG,sCAAsC,CAAS;IAEtE,OAAO,UAAO;IAGd,qBAAqB;mBAA8B,OAAO;iBAAW,MAAM;OAAK;IAEhF,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,UAAU,CAAiC;gBAEvC,UAAU,EAAE,GAAG,CAAC,UAAU;YASxB,oBAAoB;
|
|
1
|
+
{"version":3,"file":"LoadIfcAsFragments.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/bim/src/LoadIfcAsFragments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA;AAK3C,qBAAa,kBAAmB,SAAQ,GAAG,CAAC,SAAS;IACnD,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAG,sCAAsC,CAAS;IAEtE,OAAO,UAAO;IAGd,qBAAqB;mBAA8B,OAAO;iBAAW,MAAM;OAAK;IAEhF,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,UAAU,CAAiC;gBAEvC,UAAU,EAAE,GAAG,CAAC,UAAU;YASxB,oBAAoB;IAY5B,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CA+CrE"}
|
|
@@ -21,7 +21,7 @@ const _LoadIfcAsFragments = class _LoadIfcAsFragments extends OBC.Component {
|
|
|
21
21
|
async initializeSerializer() {
|
|
22
22
|
try {
|
|
23
23
|
this.serializer = new FRAGS.IfcImporter();
|
|
24
|
-
this.serializer.wasm = { absolute: true, path: "https://unpkg.com/web-ifc@0.0.
|
|
24
|
+
this.serializer.wasm = { absolute: true, path: "https://unpkg.com/web-ifc@0.0.77/" };
|
|
25
25
|
} catch (error) {
|
|
26
26
|
console.warn("Could not initialize IFC serializer:", error);
|
|
27
27
|
this.serializer = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/bim/src/LoadIfcAsFragments.ts"],"sourcesContent":["import * as OBC from '@thatopen/components'\r\nimport * as FRAGS from '@thatopen/fragments'\r\nimport { CurrentWorld } from './CurrentWorld'\r\nimport { LoadModels } from './LoadModels'\r\n\r\nexport class LoadIfcAsFragments extends OBC.Component {\r\n static readonly uuid = 'c1007187-c47b-47a5-8472-d2d06be83973' as const\r\n\r\n enabled = true\r\n\r\n // Events for loading states\r\n onLoadingStateChanged = new OBC.Event<{ isLoading: boolean, message: string }>()\r\n\r\n private world: OBC.World | null = null\r\n private fragments: OBC.FragmentsManager | null = null\r\n private loadModels: LoadModels | null = null\r\n private serializer: FRAGS.IfcImporter | null = null\r\n\r\n constructor(components: OBC.Components) {\r\n super(components)\r\n components.add(LoadIfcAsFragments.uuid, this)\r\n this.world = components.get(CurrentWorld).world\r\n this.fragments = components.get(OBC.FragmentsManager)\r\n this.loadModels = components.get(LoadModels)\r\n this.initializeSerializer()\r\n }\r\n\r\n private async initializeSerializer() {\r\n try {\r\n this.serializer = new FRAGS.IfcImporter()\r\n this.serializer.wasm = { absolute: true, path: 'https://unpkg.com/web-ifc@0.0.
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/bim/src/LoadIfcAsFragments.ts"],"sourcesContent":["import * as OBC from '@thatopen/components'\r\nimport * as FRAGS from '@thatopen/fragments'\r\nimport { CurrentWorld } from './CurrentWorld'\r\nimport { LoadModels } from './LoadModels'\r\n\r\nexport class LoadIfcAsFragments extends OBC.Component {\r\n static readonly uuid = 'c1007187-c47b-47a5-8472-d2d06be83973' as const\r\n\r\n enabled = true\r\n\r\n // Events for loading states\r\n onLoadingStateChanged = new OBC.Event<{ isLoading: boolean, message: string }>()\r\n\r\n private world: OBC.World | null = null\r\n private fragments: OBC.FragmentsManager | null = null\r\n private loadModels: LoadModels | null = null\r\n private serializer: FRAGS.IfcImporter | null = null\r\n\r\n constructor(components: OBC.Components) {\r\n super(components)\r\n components.add(LoadIfcAsFragments.uuid, this)\r\n this.world = components.get(CurrentWorld).world\r\n this.fragments = components.get(OBC.FragmentsManager)\r\n this.loadModels = components.get(LoadModels)\r\n this.initializeSerializer()\r\n }\r\n\r\n private async initializeSerializer() {\r\n try {\r\n this.serializer = new FRAGS.IfcImporter()\r\n // ⚠️⚠️ IMPORTANT: keep this web-ifc version in sync with the `web-ifc` version\r\n this.serializer.wasm = { absolute: true, path: 'https://unpkg.com/web-ifc@0.0.77/' }\r\n }\r\n catch (error) {\r\n console.warn('Could not initialize IFC serializer:', error)\r\n this.serializer = null\r\n }\r\n }\r\n\r\n async loadFromFile(file: File, modelId: string): Promise<Uint8Array> {\r\n if (!(this.fragments && this.world && this.loadModels)) {\r\n throw new Error('Missing required components.')\r\n }\r\n\r\n try {\r\n const fileBuffer = await file.arrayBuffer()\r\n\r\n // Handle only IFC files - convert to fragments first\r\n this.onLoadingStateChanged.trigger({ isLoading: true, message: 'Converting IFC to fragments...' })\r\n\r\n if (this.serializer) {\r\n const ifcBytes = new Uint8Array(fileBuffer)\r\n const fragmentBytes = await this.serializer.process({ bytes: ifcBytes })\r\n\r\n if (fragmentBytes) {\r\n // Update loading message\r\n this.onLoadingStateChanged.trigger({ isLoading: true, message: 'Loading BIM Model...' })\r\n\r\n // Convert Uint8Array to ArrayBuffer\r\n const fragmentBuffer = new ArrayBuffer(fragmentBytes.byteLength)\r\n new Uint8Array(fragmentBuffer).set(fragmentBytes)\r\n const model = await this.fragments.core.load(fragmentBuffer, { modelId })\r\n await this.loadModels.loadModel(model, modelId)\r\n\r\n // Conversion and loading complete\r\n this.onLoadingStateChanged.trigger({ isLoading: false, message: '' })\r\n\r\n // Return the bytes so the caller can upload a .frag file\r\n return fragmentBytes\r\n }\r\n else {\r\n this.onLoadingStateChanged.trigger({ isLoading: false, message: '' })\r\n throw new Error('Failed to serialize IFC to fragments')\r\n }\r\n }\r\n else {\r\n this.onLoadingStateChanged.trigger({ isLoading: false, message: '' })\r\n throw new Error('IFC serializer not available')\r\n }\r\n }\r\n catch (error) {\r\n // Make sure to clear loading state on error\r\n this.onLoadingStateChanged.trigger({ isLoading: false, message: '' })\r\n throw new Error(`Error loading file: ${error}`)\r\n }\r\n }\r\n}\r\n"],"mappings":"AAAA,YAAY,SAAS;AACrB,YAAY,WAAW;AACvB,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAEpB,MAAM,sBAAN,MAAM,4BAA2B,IAAI,UAAU;AAAA,EAapD,YAAY,YAA4B;AACtC,UAAM,UAAU;AAXlB,mBAAU;AAGV;AAAA,iCAAwB,IAAI,IAAI,MAA+C;AAE/E,SAAQ,QAA0B;AAClC,SAAQ,YAAyC;AACjD,SAAQ,aAAgC;AACxC,SAAQ,aAAuC;AAI7C,eAAW,IAAI,oBAAmB,MAAM,IAAI;AAC5C,SAAK,QAAQ,WAAW,IAAI,YAAY,EAAE;AAC1C,SAAK,YAAY,WAAW,IAAI,IAAI,gBAAgB;AACpD,SAAK,aAAa,WAAW,IAAI,UAAU;AAC3C,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEA,MAAc,uBAAuB;AACnC,QAAI;AACF,WAAK,aAAa,IAAI,MAAM,YAAY;AAExC,WAAK,WAAW,OAAO,EAAE,UAAU,MAAM,MAAM,oCAAoC;AAAA,IACrF,SACO,OAAO;AACZ,cAAQ,KAAK,wCAAwC,KAAK;AAC1D,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,MAAY,SAAsC;AACnE,QAAI,EAAE,KAAK,aAAa,KAAK,SAAS,KAAK,aAAa;AACtD,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,QAAI;AACF,YAAM,aAAa,MAAM,KAAK,YAAY;AAG1C,WAAK,sBAAsB,QAAQ,EAAE,WAAW,MAAM,SAAS,iCAAiC,CAAC;AAEjG,UAAI,KAAK,YAAY;AACnB,cAAM,WAAW,IAAI,WAAW,UAAU;AAC1C,cAAM,gBAAgB,MAAM,KAAK,WAAW,QAAQ,EAAE,OAAO,SAAS,CAAC;AAEvE,YAAI,eAAe;AAEjB,eAAK,sBAAsB,QAAQ,EAAE,WAAW,MAAM,SAAS,uBAAuB,CAAC;AAGvF,gBAAM,iBAAiB,IAAI,YAAY,cAAc,UAAU;AAC/D,cAAI,WAAW,cAAc,EAAE,IAAI,aAAa;AAChD,gBAAM,QAAQ,MAAM,KAAK,UAAU,KAAK,KAAK,gBAAgB,EAAE,QAAQ,CAAC;AACxE,gBAAM,KAAK,WAAW,UAAU,OAAO,OAAO;AAG9C,eAAK,sBAAsB,QAAQ,EAAE,WAAW,OAAO,SAAS,GAAG,CAAC;AAGpE,iBAAO;AAAA,QACT,OACK;AACH,eAAK,sBAAsB,QAAQ,EAAE,WAAW,OAAO,SAAS,GAAG,CAAC;AACpE,gBAAM,IAAI,MAAM,sCAAsC;AAAA,QACxD;AAAA,MACF,OACK;AACH,aAAK,sBAAsB,QAAQ,EAAE,WAAW,OAAO,SAAS,GAAG,CAAC;AACpE,cAAM,IAAI,MAAM,8BAA8B;AAAA,MAChD;AAAA,IACF,SACO,OAAO;AAEZ,WAAK,sBAAsB,QAAQ,EAAE,WAAW,OAAO,SAAS,GAAG,CAAC;AACpE,YAAM,IAAI,MAAM,uBAAuB,KAAK,EAAE;AAAA,IAChD;AAAA,EACF;AACF;AAjFa,oBACK,OAAO;AADlB,IAAM,qBAAN;","names":[]}
|