@collabdt/core 0.0.57 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +29 -43
  2. package/dist/core/components/AppSidebarContent.d.ts.map +1 -1
  3. package/dist/core/components/AppSidebarContent.js +21 -27
  4. package/dist/core/components/AppSidebarContent.js.map +1 -1
  5. package/dist/core/components/authentication/AuthPage.d.ts.map +1 -1
  6. package/dist/core/components/authentication/AuthPage.js +32 -7
  7. package/dist/core/components/authentication/AuthPage.js.map +1 -1
  8. package/dist/core/components/authentication/SignInAuthHeader.d.ts +7 -0
  9. package/dist/core/components/authentication/SignInAuthHeader.d.ts.map +1 -0
  10. package/dist/core/components/authentication/SignInAuthHeader.js +32 -0
  11. package/dist/core/components/authentication/SignInAuthHeader.js.map +1 -0
  12. package/dist/core/components/authentication/Signin.d.ts.map +1 -1
  13. package/dist/core/components/authentication/Signin.js +308 -10
  14. package/dist/core/components/authentication/Signin.js.map +1 -1
  15. package/dist/core/components/settings/index.js +3 -3
  16. package/dist/core/components/settings/index.js.map +1 -1
  17. package/dist/core/components/settings/src/OrganizationSettingsPanel.d.ts.map +1 -1
  18. package/dist/core/components/settings/src/OrganizationSettingsPanel.js +5 -4
  19. package/dist/core/components/settings/src/OrganizationSettingsPanel.js.map +1 -1
  20. package/dist/core/components/settings/src/SettingsSidebar.d.ts.map +1 -1
  21. package/dist/core/components/settings/src/SettingsSidebar.js +12 -2
  22. package/dist/core/components/settings/src/SettingsSidebar.js.map +1 -1
  23. package/dist/core/components/support/BugReportDialog.d.ts.map +1 -1
  24. package/dist/core/components/support/BugReportDialog.js +122 -9
  25. package/dist/core/components/support/BugReportDialog.js.map +1 -1
  26. package/dist/core/components/support/FeatureRequestDialog.d.ts.map +1 -1
  27. package/dist/core/components/support/FeatureRequestDialog.js +61 -2
  28. package/dist/core/components/support/FeatureRequestDialog.js.map +1 -1
  29. package/dist/core/components/ui/AppSidebar.d.ts.map +1 -1
  30. package/dist/core/components/ui/AppSidebar.js +52 -9
  31. package/dist/core/components/ui/AppSidebar.js.map +1 -1
  32. package/dist/core/components/ui/DataTable.js +3 -3
  33. package/dist/core/components/ui/DataTable.js.map +1 -1
  34. package/dist/core/components/ui/Dialog.js +1 -1
  35. package/dist/core/components/ui/Dialog.js.map +1 -1
  36. package/dist/core/components/ui/InfoSidebar/Container.js +1 -1
  37. package/dist/core/components/ui/InfoSidebar/Container.js.map +1 -1
  38. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
  39. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +2 -2
  40. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
  41. package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
  42. package/dist/core/components/ui/Sensors/SensorsSection.js +2 -1
  43. package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
  44. package/dist/core/components/ui/Sensors/sensorUtils.d.ts +9 -0
  45. package/dist/core/components/ui/Sensors/sensorUtils.d.ts.map +1 -1
  46. package/dist/core/components/ui/Sensors/sensorUtils.js +6 -0
  47. package/dist/core/components/ui/Sensors/sensorUtils.js.map +1 -1
  48. package/dist/core/components/ui/Sidebar.d.ts +4 -0
  49. package/dist/core/components/ui/Sidebar.d.ts.map +1 -1
  50. package/dist/core/components/ui/Sidebar.js +11 -5
  51. package/dist/core/components/ui/Sidebar.js.map +1 -1
  52. package/dist/core/components/ui/Table.js +2 -2
  53. package/dist/core/components/ui/Table.js.map +1 -1
  54. package/dist/core/components/viewers/Data/DataMenu.d.ts.map +1 -1
  55. package/dist/core/components/viewers/Data/DataMenu.js +17 -9
  56. package/dist/core/components/viewers/Data/DataMenu.js.map +1 -1
  57. package/dist/core/components/viewers/Data/HeaderButtons.d.ts.map +1 -1
  58. package/dist/core/components/viewers/Data/HeaderButtons.js +5 -12
  59. package/dist/core/components/viewers/Data/HeaderButtons.js.map +1 -1
  60. package/dist/core/components/viewers/Data/MoreOptions.d.ts.map +1 -1
  61. package/dist/core/components/viewers/Data/MoreOptions.js +14 -27
  62. package/dist/core/components/viewers/Data/MoreOptions.js.map +1 -1
  63. package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.d.ts.map +1 -1
  64. package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.js +26 -0
  65. package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.js.map +1 -1
  66. package/dist/core/components/viewers/Data/buildingDetails/FieldRenderer.d.ts.map +1 -1
  67. package/dist/core/components/viewers/Data/buildingDetails/FieldRenderer.js +13 -18
  68. package/dist/core/components/viewers/Data/buildingDetails/FieldRenderer.js.map +1 -1
  69. package/dist/core/components/viewers/Data/details/DetailHeader.js +14 -14
  70. package/dist/core/components/viewers/Data/details/DetailHeader.js.map +1 -1
  71. package/dist/core/components/viewers/Data/details/FieldValue.d.ts.map +1 -1
  72. package/dist/core/components/viewers/Data/details/FieldValue.js +13 -3
  73. package/dist/core/components/viewers/Data/details/FieldValue.js.map +1 -1
  74. package/dist/core/components/viewers/Data/details/TabSidebar.d.ts.map +1 -1
  75. package/dist/core/components/viewers/Data/details/TabSidebar.js +22 -0
  76. package/dist/core/components/viewers/Data/details/TabSidebar.js.map +1 -1
  77. package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.d.ts.map +1 -1
  78. package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.js +19 -0
  79. package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.js.map +1 -1
  80. package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.d.ts +2 -0
  81. package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.d.ts.map +1 -1
  82. package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.js +84 -3
  83. package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.js.map +1 -1
  84. package/dist/core/components/viewers/Data/siteDetails/SiteDetails.d.ts.map +1 -1
  85. package/dist/core/components/viewers/Data/siteDetails/SiteDetails.js +42 -2
  86. package/dist/core/components/viewers/Data/siteDetails/SiteDetails.js.map +1 -1
  87. package/dist/core/components/viewers/Data/utils/Columns.js +2 -2
  88. package/dist/core/components/viewers/Data/utils/Columns.js.map +1 -1
  89. package/dist/core/components/viewers/Data/utils/positionParser.d.ts.map +1 -1
  90. package/dist/core/components/viewers/Data/utils/positionParser.js +6 -5
  91. package/dist/core/components/viewers/Data/utils/positionParser.js.map +1 -1
  92. package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.d.ts.map +1 -1
  93. package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js +8 -1
  94. package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js.map +1 -1
  95. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
  96. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js +7 -1
  97. package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js.map +1 -1
  98. package/dist/core/components/viewers/map/datasets/RowActions.js +6 -6
  99. package/dist/core/components/viewers/map/datasets/RowActions.js.map +1 -1
  100. package/dist/core/components/viewers/map/datasets/data.js +1 -1
  101. package/dist/core/components/viewers/map/datasets/data.js.map +1 -1
  102. package/dist/core/components/viewers/map/datasets/index.d.ts.map +1 -1
  103. package/dist/core/components/viewers/map/datasets/index.js +28 -9
  104. package/dist/core/components/viewers/map/datasets/index.js.map +1 -1
  105. package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
  106. package/dist/core/components/viewers/map/src/MapLayers/index.js +2 -0
  107. package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
  108. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteContextMenu.d.ts +19 -0
  109. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteContextMenu.d.ts.map +1 -0
  110. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteContextMenu.js +163 -0
  111. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteContextMenu.js.map +1 -0
  112. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteLayer.d.ts +3 -0
  113. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteLayer.d.ts.map +1 -0
  114. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteLayer.js +310 -0
  115. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteLayer.js.map +1 -0
  116. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/siteGeometry.d.ts +30 -0
  117. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/siteGeometry.d.ts.map +1 -0
  118. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/siteGeometry.js +122 -0
  119. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/siteGeometry.js.map +1 -0
  120. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/useShowSitesOnMap.d.ts +13 -0
  121. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/useShowSitesOnMap.d.ts.map +1 -0
  122. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/useShowSitesOnMap.js +58 -0
  123. package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/useShowSitesOnMap.js.map +1 -0
  124. package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/AssociateBuildingsDialog.d.ts +18 -0
  125. package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/AssociateBuildingsDialog.d.ts.map +1 -0
  126. package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/AssociateBuildingsDialog.js +102 -0
  127. package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/AssociateBuildingsDialog.js.map +1 -0
  128. package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/SiteAdder.d.ts.map +1 -1
  129. package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/SiteAdder.js +243 -176
  130. package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/SiteAdder.js.map +1 -1
  131. package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/persistSite.d.ts +24 -0
  132. package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/persistSite.d.ts.map +1 -0
  133. package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/persistSite.js +46 -0
  134. package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/persistSite.js.map +1 -0
  135. package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.d.ts +1 -0
  136. package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.d.ts.map +1 -1
  137. package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js +1 -0
  138. package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js.map +1 -1
  139. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.d.ts.map +1 -1
  140. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js +54 -31
  141. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js.map +1 -1
  142. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js +1 -1
  143. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js.map +1 -1
  144. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts +2 -1
  145. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts.map +1 -1
  146. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js +5 -4
  147. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js.map +1 -1
  148. package/dist/core/hooks/sites/createSiteHooks.d.ts.map +1 -1
  149. package/dist/core/hooks/sites/createSiteHooks.js +0 -1
  150. package/dist/core/hooks/sites/createSiteHooks.js.map +1 -1
  151. package/dist/core/hooks/useCaptureScreenshot.d.ts.map +1 -1
  152. package/dist/core/hooks/useCaptureScreenshot.js +111 -33
  153. package/dist/core/hooks/useCaptureScreenshot.js.map +1 -1
  154. package/dist/core/hooks/useCurrentViewerPosition.d.ts +18 -0
  155. package/dist/core/hooks/useCurrentViewerPosition.d.ts.map +1 -0
  156. package/dist/core/hooks/useCurrentViewerPosition.js +66 -0
  157. package/dist/core/hooks/useCurrentViewerPosition.js.map +1 -0
  158. package/dist/core/hooks/useShareUrl.d.ts +2 -0
  159. package/dist/core/hooks/useShareUrl.d.ts.map +1 -0
  160. package/dist/core/hooks/useShareUrl.js +74 -0
  161. package/dist/core/hooks/useShareUrl.js.map +1 -0
  162. package/dist/core/store/CombineProviders.d.ts.map +1 -1
  163. package/dist/core/store/CombineProviders.js +2 -0
  164. package/dist/core/store/CombineProviders.js.map +1 -1
  165. package/dist/core/store/MapSites/context.d.ts +16 -0
  166. package/dist/core/store/MapSites/context.d.ts.map +1 -0
  167. package/dist/core/store/MapSites/context.js +29 -0
  168. package/dist/core/store/MapSites/context.js.map +1 -0
  169. package/dist/core/store/MapSites/reducer.d.ts +52 -0
  170. package/dist/core/store/MapSites/reducer.d.ts.map +1 -0
  171. package/dist/core/store/MapSites/reducer.js +70 -0
  172. package/dist/core/store/MapSites/reducer.js.map +1 -0
  173. package/dist/core/store/index.d.ts +1 -0
  174. package/dist/core/store/index.d.ts.map +1 -1
  175. package/dist/core/store/index.js +1 -0
  176. package/dist/core/store/index.js.map +1 -1
  177. package/dist/core/styles/globals.css +46 -33
  178. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/core/components/settings/src/OrganizationSettingsPanel.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from 'react'\r\nimport { useSession } from 'next-auth/react'\r\nimport { toast } from 'sonner'\r\nimport { mutate } from 'swr'\r\nimport { useTranslations } from 'next-intl'\r\nimport { MapContext, usePermissions } from '../../../store'\r\nimport * as LR from 'lucide-react'\r\n\r\nimport type { Organization } from '../../../types/dbTypes'\r\nimport { Button } from '../../ui/Button'\r\nimport { Input } from '../../ui/Input'\r\nimport { Textarea } from '../../ui/Textarea'\r\nimport { Separator } from '../../ui/Separator'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '../../ui/Select'\r\nimport { uploadOrganizationLogoToPublicBucket } from '../../../utils/imageUtils'\r\nimport { useOrganization } from '../../../hooks/organizations/organizations'\r\nimport OrganizationSkeleton from './OrganizationSkeleton'\r\n\r\n//import { useAppConfigContext } from '../../../store/AppConfig/context'\r\n\r\ninterface OrganizationSettingsPanelProps {\r\n minioBaseUrl?: string\r\n}\r\n\r\nexport default function OrganizationSettingsPanel({\r\n minioBaseUrl,\r\n}: OrganizationSettingsPanelProps) {\r\n const t = useTranslations('OrganizationSettings')\r\n\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { data: session } = useSession()\r\n\r\n const {state: mapState} = React.useContext(MapContext)\r\n const { countrySubdivisionsData } = mapState.map;\r\n\r\n const user = session?.user\r\n const userOrganizationId = user?.organizationId\r\n const {\r\n organization: organizationData,\r\n isLoading: isOrganizationLoading,\r\n } = useOrganization(userOrganizationId ? String(userOrganizationId) : null)\r\n\r\n const [organization, setOrganization] = React.useState<Organization | null>(organizationData ?? null)\r\n const [editingValues, setEditingValues] = React.useState<Partial<Organization>>({})\r\n const [isEditing, setIsEditing] = React.useState(false)\r\n const [hasChanges, setHasChanges] = React.useState(false)\r\n const [selectedLogoFile, setSelectedLogoFile] = React.useState<File | null>(null)\r\n const [selectedFaviconFile, setSelectedFaviconFile] = React.useState<File | null>(null)\r\n\r\n React.useEffect(() => {\r\n if (organizationData) {\r\n setOrganization(organizationData)\r\n }\r\n }, [organizationData])\r\n\r\n const handleInputChange = (key: keyof Organization, value: any) => {\r\n setEditingValues(prev => ({\r\n ...prev,\r\n [key]: value,\r\n }))\r\n setHasChanges(true)\r\n }\r\n\r\n const getFieldValue = (property: keyof Organization) => {\r\n return Object.prototype.hasOwnProperty.call(editingValues, property)\r\n ? editingValues[property]\r\n : organization?.[property]\r\n }\r\n\r\n const handleSave = async () => {\r\n if (!organization) return\r\n\r\n try {\r\n const patch: Partial<Organization> = {\r\n ...editingValues,\r\n }\r\n\r\n // Upload logo if changed\r\n if (selectedLogoFile) {\r\n const logoKey = await uploadOrganizationLogoToPublicBucket(selectedLogoFile)\r\n patch.logoKey = logoKey\r\n }\r\n\r\n // Upload favicon if changed\r\n if (selectedFaviconFile) {\r\n const faviconKey = await uploadOrganizationLogoToPublicBucket(selectedFaviconFile)\r\n patch.faviconKey = faviconKey\r\n }\r\n\r\n const res = await fetch(`/api/organizations/${organization.id}`, {\r\n method: 'PATCH',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(patch),\r\n })\r\n\r\n if (!res.ok) throw new Error('Failed to update organization')\r\n\r\n const responseData = await res.json()\r\n const updatedOrg = responseData?.organization ?? responseData\r\n setOrganization(updatedOrg)\r\n mutate(['organization', String(userOrganizationId)], updatedOrg, { revalidate: false })\r\n setEditingValues({})\r\n setIsEditing(false)\r\n setHasChanges(false)\r\n setSelectedLogoFile(null)\r\n setSelectedFaviconFile(null)\r\n toast.success('Organization updated successfully')\r\n } catch (error) {\r\n toast.error('Failed to update organization')\r\n }\r\n }\r\n\r\n const handleCancel = () => {\r\n setEditingValues({})\r\n setIsEditing(false)\r\n setHasChanges(false)\r\n setSelectedLogoFile(null)\r\n setSelectedFaviconFile(null)\r\n }\r\n\r\n //const { state: { runtimeConfig: { minioUrl } } } = useAppConfigContext()\r\n\r\n const logoUrl = selectedLogoFile\r\n ? URL.createObjectURL(selectedLogoFile)\r\n : (getFieldValue('logoKey') as string | null | undefined) && minioBaseUrl\r\n ? `${minioBaseUrl}/org-logos/${getFieldValue('logoKey')}`\r\n : null\r\n\r\n const faviconUrl = selectedFaviconFile\r\n ? URL.createObjectURL(selectedFaviconFile)\r\n : (getFieldValue('faviconKey') as string | null | undefined) && minioBaseUrl\r\n ? `${minioBaseUrl}/org-logos/${getFieldValue('faviconKey')}`\r\n : null\r\n\r\n// const faviconUrl = selectedFaviconFile\r\n// ? URL.createObjectURL(selectedFaviconFile)\r\n// : organization?.faviconKey && minioUrl\r\n// ? `${minioUrl}/org-logos/${organization.faviconKey}`\r\n// : null\r\n \r\n if (isOrganizationLoading) {\r\n return (\r\n <OrganizationSkeleton />\r\n )\r\n }\r\n\r\n if (!organization) {\r\n return (\r\n <div className=\"flex items-center justify-center h-full\">\r\n <p className=\"text-muted-foreground\">{t('messages.noOrganization')}</p>\r\n </div>\r\n )\r\n }\r\n\r\n const countrySubdivisions = \r\n countrySubdivisionsData\r\n ? Object.values(countrySubdivisionsData).map((subdivision: any) => subdivision.name)\r\n : []\r\n\r\n return (\r\n <div className=\"flex flex-col h-full overflow-hidden\">\r\n {/* Header */}\r\n <div className=\"flex justify-between items-center px-6 py-4 border-b\">\r\n <div>\r\n <h2 className=\"text-2xl font-semibold\">{t('header')}</h2>\r\n <p className=\"text-sm text-muted-foreground\">{t('subheader')}</p>\r\n </div>\r\n <div className=\"flex gap-2\">\r\n {isEditing ? (\r\n <>\r\n <Button variant=\"outline\" onClick={handleCancel} disabled={!ability.can('update', \"Organization\")}>\r\n {t('cancel')}\r\n </Button>\r\n <Button onClick={handleSave} disabled={!hasChanges || !ability.can('update', \"Organization\")}>\r\n <LR.Save className=\"mr-2\" />\r\n {t('save')}\r\n </Button>\r\n </>\r\n ) : (\r\n <Button onClick={() => setIsEditing(true)} disabled={!ability.can('update', \"Organization\")}>\r\n <LR.Pencil className=\"mr-2\" />\r\n {t('edit')}\r\n </Button>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {/* Content */}\r\n <div className=\"flex-1 overflow-auto p-6\">\r\n <div className=\"max-w-4xl mx-auto space-y-8\">\r\n {/* Branding Section */}\r\n <div className=\"space-y-4\">\r\n <h3 className=\"text-lg font-semibold\">{t('sections.branding')}</h3>\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\r\n {/* Logo */}\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.logo')}</label>\r\n <div className=\"flex items-center gap-4\">\r\n <div className=\"w-32 h-32 border rounded-lg overflow-hidden bg-muted flex items-center justify-center relative group\">\r\n {logoUrl ? (\r\n <img src={logoUrl} alt=\"Organization logo\" className=\"w-full h-full object-contain\" />\r\n ) : (\r\n <LR.Building2 className=\"h-12 w-12 text-muted-foreground\" />\r\n )}\r\n {isEditing && (\r\n <label className=\"absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center cursor-pointer\">\r\n <LR.Upload className=\"h-6 w-6 text-white\" />\r\n <input\r\n type=\"file\"\r\n accept=\"image/*\"\r\n className=\"hidden\"\r\n onChange={(e) => {\r\n const file = e.target.files?.[0]\r\n if (file) {\r\n setSelectedLogoFile(file)\r\n setHasChanges(true)\r\n }\r\n }}\r\n disabled={!ability.can('update', \"Organization\")}\r\n />\r\n </label>\r\n )}\r\n </div>\r\n {isEditing && logoUrl && (\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n onClick={() => {\r\n setSelectedLogoFile(null)\r\n handleInputChange('logoKey', null)\r\n }}\r\n disabled={!ability.can('update', \"Organization\")}\r\n >\r\n Remove\r\n </Button>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {/* Favicon */}\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.favicon')}</label>\r\n <div className=\"flex items-center gap-4\">\r\n <div className=\"w-16 h-16 border rounded-lg overflow-hidden bg-muted flex items-center justify-center relative group\">\r\n {faviconUrl ? (\r\n <img src={faviconUrl} alt=\"Favicon\" className=\"w-full h-full object-contain\" />\r\n ) : (\r\n <LR.Image className=\"h-6 w-6 text-muted-foreground\" />\r\n )}\r\n {isEditing && (\r\n <label className=\"absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center cursor-pointer\">\r\n <LR.Upload className=\"h-4 w-4 text-white\" />\r\n <input\r\n type=\"file\"\r\n accept=\"image/*\"\r\n className=\"hidden\"\r\n onChange={(e) => {\r\n const file = e.target.files?.[0]\r\n if (file) {\r\n setSelectedFaviconFile(file)\r\n setHasChanges(true)\r\n }\r\n }}\r\n disabled={!ability.can('update', \"Organization\")}\r\n />\r\n </label>\r\n )}\r\n </div>\r\n {isEditing && faviconUrl && (\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n onClick={() => {\r\n setSelectedFaviconFile(null)\r\n handleInputChange('faviconKey', null)\r\n }}\r\n disabled={!ability.can('update', \"Organization\")}\r\n >\r\n Remove\r\n </Button>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Colors */}\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.mainColor')}</label>\r\n <div className=\"flex gap-2\">\r\n <Input\r\n type=\"color\"\r\n value={(getFieldValue('mainColor') as string) || '#000000'}\r\n onChange={(e) => handleInputChange('mainColor', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n className=\"w-20 h-10 p-1\"\r\n \r\n />\r\n <Input\r\n type=\"text\"\r\n value={(getFieldValue('mainColor') as string) || ''}\r\n onChange={(e) => handleInputChange('mainColor', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n placeholder=\"#000000\"\r\n />\r\n </div>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.secondaryColor')}</label>\r\n <div className=\"flex gap-2\">\r\n <Input\r\n type=\"color\"\r\n value={(getFieldValue('secondaryColor') as string) || '#000000'}\r\n onChange={(e) => handleInputChange('secondaryColor', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n className=\"w-20 h-10 p-1\"\r\n />\r\n <Input\r\n type=\"text\"\r\n value={(getFieldValue('secondaryColor') as string) || ''}\r\n onChange={(e) => handleInputChange('secondaryColor', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n placeholder=\"#000000\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <Separator />\r\n\r\n {/* Basic Information */}\r\n <div className=\"space-y-4\">\r\n <h3 className=\"text-lg font-semibold\">{t('sections.basicInfo')}</h3>\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.name')}</label>\r\n <Input\r\n value={(getFieldValue('name') as string) || ''}\r\n onChange={(e) => handleInputChange('name', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.title')}</label>\r\n <Input\r\n value={(getFieldValue('title') as string) || ''}\r\n onChange={(e) => handleInputChange('title', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\") }\r\n />\r\n </div>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.description')}</label>\r\n <Textarea\r\n value={(getFieldValue('description') as string) || ''}\r\n onChange={(e) => handleInputChange('description', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n rows={4}\r\n />\r\n </div>\r\n </div>\r\n\r\n <Separator />\r\n\r\n {/* Location */}\r\n <div className=\"space-y-4\">\r\n <h3 className=\"text-lg font-semibold\">{t('sections.location')}</h3>\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.countrySubdivision')}</label>\r\n <Select\r\n value={String(getFieldValue('countrySubdivision') || '')}\r\n onValueChange={(value) => handleInputChange('countrySubdivision', value)}\r\n disabled={!isEditing}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder={t('placeholders.selectProvince')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {countrySubdivisions.map((csName) => (\r\n <SelectItem key={csName} value={csName}>\r\n {csName}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.municipality')}</label>\r\n <Input\r\n value={(getFieldValue('municipality') as string) || ''}\r\n onChange={(e) => handleInputChange('municipality', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.postalCode')}</label>\r\n <Input\r\n value={(getFieldValue('postalCode') as string) || ''}\r\n onChange={(e) => handleInputChange('postalCode', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.locationId')}</label>\r\n <Input\r\n value={(getFieldValue('locationId') as string) || ''}\r\n onChange={(e) => handleInputChange('locationId', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <Separator />\r\n\r\n {/* Map Configuration */}\r\n <div className=\"space-y-4\">\r\n <h3 className=\"text-lg font-semibold\">{t('sections.mapConfig')}</h3>\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.latitude')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('lat') as number) ?? ''}\r\n onChange={(e) => handleInputChange('lat', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.longitude')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('long') as number) ?? ''}\r\n onChange={(e) => handleInputChange('long', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.zoom')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('zoom') as number) ?? ''}\r\n onChange={(e) => handleInputChange('zoom', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.minZoom')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('minZoom') as number) ?? ''}\r\n onChange={(e) => handleInputChange('minZoom', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.bearing')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('bearing') as number) ?? ''}\r\n onChange={(e) => handleInputChange('bearing', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.pitch')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('pitch') as number) ?? ''}\r\n onChange={(e) => handleInputChange('pitch', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a"]}
1
+ {"version":3,"sources":["../../../../../src/core/components/settings/src/OrganizationSettingsPanel.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from 'react'\r\nimport { useSession } from 'next-auth/react'\r\nimport { toast } from 'sonner'\r\nimport { mutate } from 'swr'\r\nimport { useTranslations } from 'next-intl'\r\nimport { MapContext, usePermissions } from '../../../store'\r\nimport * as LR from 'lucide-react'\r\n\r\nimport type { Organization } from '../../../types/dbTypes'\r\nimport { Button } from '../../ui/Button'\r\nimport { Input } from '../../ui/Input'\r\nimport { Textarea } from '../../ui/Textarea'\r\nimport { Separator } from '../../ui/Separator'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '../../ui/Select'\r\nimport { uploadOrganizationLogoToPublicBucket } from '../../../utils/imageUtils'\r\nimport { useOrganization } from '../../../hooks/organizations/organizations'\r\nimport OrganizationSkeleton from './OrganizationSkeleton'\r\n\r\n//import { useAppConfigContext } from '../../../store/AppConfig/context'\r\n\r\ninterface OrganizationSettingsPanelProps {\r\n minioBaseUrl?: string\r\n}\r\n\r\nexport default function OrganizationSettingsPanel({\r\n minioBaseUrl,\r\n}: OrganizationSettingsPanelProps) {\r\n const t = useTranslations('OrganizationSettings')\r\n\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { data: session } = useSession()\r\n\r\n const {state: mapState} = React.useContext(MapContext)\r\n const { countrySubdivisionsData } = mapState.map;\r\n\r\n const user = session?.user\r\n const userOrganizationId = user?.organizationId\r\n const {\r\n organization: organizationData,\r\n isLoading: isOrganizationLoading,\r\n } = useOrganization(userOrganizationId ? String(userOrganizationId) : null)\r\n\r\n const [organization, setOrganization] = React.useState<Organization | null>(organizationData ?? null)\r\n const [editingValues, setEditingValues] = React.useState<Partial<Organization>>({})\r\n const [isEditing, setIsEditing] = React.useState(false)\r\n const [hasChanges, setHasChanges] = React.useState(false)\r\n const [selectedLogoFile, setSelectedLogoFile] = React.useState<File | null>(null)\r\n const [selectedFaviconFile, setSelectedFaviconFile] = React.useState<File | null>(null)\r\n\r\n React.useEffect(() => {\r\n if (organizationData) {\r\n setOrganization(organizationData)\r\n }\r\n }, [organizationData])\r\n\r\n const handleInputChange = (key: keyof Organization, value: any) => {\r\n setEditingValues(prev => ({\r\n ...prev,\r\n [key]: value,\r\n }))\r\n setHasChanges(true)\r\n }\r\n\r\n const getFieldValue = (property: keyof Organization) => {\r\n return Object.prototype.hasOwnProperty.call(editingValues, property)\r\n ? editingValues[property]\r\n : organization?.[property]\r\n }\r\n\r\n const handleSave = async () => {\r\n if (!organization) return\r\n\r\n try {\r\n const patch: Partial<Organization> = {\r\n ...editingValues,\r\n }\r\n\r\n // Upload logo if changed\r\n if (selectedLogoFile) {\r\n const logoKey = await uploadOrganizationLogoToPublicBucket(selectedLogoFile)\r\n patch.logoKey = logoKey\r\n }\r\n\r\n // Upload favicon if changed\r\n if (selectedFaviconFile) {\r\n const faviconKey = await uploadOrganizationLogoToPublicBucket(selectedFaviconFile)\r\n patch.faviconKey = faviconKey\r\n }\r\n\r\n const res = await fetch(`/api/organizations/${organization.id}`, {\r\n method: 'PATCH',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(patch),\r\n })\r\n\r\n if (!res.ok) throw new Error('Failed to update organization')\r\n\r\n const responseData = await res.json()\r\n const updatedOrg = responseData?.organization ?? responseData\r\n setOrganization(updatedOrg)\r\n mutate(['organization', String(userOrganizationId)], updatedOrg, { revalidate: false })\r\n setEditingValues({})\r\n setIsEditing(false)\r\n setHasChanges(false)\r\n setSelectedLogoFile(null)\r\n setSelectedFaviconFile(null)\r\n toast.success('Organization updated successfully')\r\n } catch (error) {\r\n toast.error('Failed to update organization')\r\n }\r\n }\r\n\r\n const handleCancel = () => {\r\n setEditingValues({})\r\n setIsEditing(false)\r\n setHasChanges(false)\r\n setSelectedLogoFile(null)\r\n setSelectedFaviconFile(null)\r\n }\r\n\r\n //const { state: { runtimeConfig: { minioUrl } } } = useAppConfigContext()\r\n\r\n const logoUrl = selectedLogoFile\r\n ? URL.createObjectURL(selectedLogoFile)\r\n : (getFieldValue('logoKey') as string | null | undefined) && minioBaseUrl\r\n ? `${minioBaseUrl}/org-logos/${getFieldValue('logoKey')}`\r\n : null\r\n\r\n const faviconUrl = selectedFaviconFile\r\n ? URL.createObjectURL(selectedFaviconFile)\r\n : (getFieldValue('faviconKey') as string | null | undefined) && minioBaseUrl\r\n ? `${minioBaseUrl}/org-logos/${getFieldValue('faviconKey')}`\r\n : null\r\n\r\n// const faviconUrl = selectedFaviconFile\r\n// ? URL.createObjectURL(selectedFaviconFile)\r\n// : organization?.faviconKey && minioUrl\r\n// ? `${minioUrl}/org-logos/${organization.faviconKey}`\r\n// : null\r\n \r\n if (isOrganizationLoading) {\r\n return (\r\n <OrganizationSkeleton />\r\n )\r\n }\r\n\r\n if (!organization) {\r\n return (\r\n <div className=\"flex items-center justify-center h-full\">\r\n <p className=\"text-muted-foreground\">{t('messages.noOrganization')}</p>\r\n </div>\r\n )\r\n }\r\n\r\n const countrySubdivisions = \r\n countrySubdivisionsData\r\n ? Object.values(countrySubdivisionsData).map((subdivision: any) => subdivision.name)\r\n : []\r\n\r\n return (\r\n <div className=\"flex flex-col h-full overflow-hidden\">\r\n {/* Header */}\r\n <div className=\"flex justify-between items-center px-6 py-4 border-b\">\r\n <div>\r\n <h2 className=\"text-2xl font-semibold\">{t('header')}</h2>\r\n <p className=\"text-sm text-muted-foreground\">{t('subheader')}</p>\r\n </div>\r\n <div className=\"flex gap-2\">\r\n {isEditing ? (\r\n <>\r\n <Button variant=\"outline\" onClick={handleCancel} disabled={!ability.can('update', \"Organization\")}>\r\n {t('cancel')}\r\n </Button>\r\n <Button onClick={handleSave} disabled={!hasChanges || !ability.can('update', \"Organization\")}>\r\n <LR.Save className=\"mr-2\" />\r\n {t('save')}\r\n </Button>\r\n </>\r\n ) : (\r\n <Button onClick={() => setIsEditing(true)} disabled={!ability.can('update', \"Organization\")}>\r\n <LR.Pencil className=\"mr-2\" />\r\n {t('edit')}\r\n </Button>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {/* Content */}\r\n <div className=\"flex-1 overflow-auto p-6\">\r\n <div className=\"max-w-4xl mx-auto space-y-8\">\r\n {/* Branding Section */}\r\n <div className=\"space-y-4\">\r\n <h3 className=\"text-lg font-semibold\">{t('sections.branding')}</h3>\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\r\n {/* Logo */}\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.logo')}</label>\r\n <div className=\"flex items-center gap-4\">\r\n <div className=\"w-32 h-32 border rounded-lg overflow-hidden bg-muted flex items-center justify-center relative group\">\r\n {logoUrl ? (\r\n <img src={logoUrl} alt=\"Organization logo\" className=\"w-full h-full object-contain\" />\r\n ) : (\r\n <LR.Building2 className=\"h-12 w-12 text-muted-foreground\" />\r\n )}\r\n {isEditing && (\r\n <label className=\"absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center cursor-pointer\">\r\n <LR.Upload className=\"h-6 w-6 text-white\" />\r\n <input\r\n type=\"file\"\r\n accept=\"image/*\"\r\n className=\"hidden\"\r\n onChange={(e) => {\r\n const file = e.target.files?.[0]\r\n if (file) {\r\n setSelectedLogoFile(file)\r\n setHasChanges(true)\r\n }\r\n }}\r\n disabled={!ability.can('update', \"Organization\")}\r\n />\r\n </label>\r\n )}\r\n </div>\r\n {isEditing && logoUrl && (\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n onClick={() => {\r\n setSelectedLogoFile(null)\r\n handleInputChange('logoKey', null)\r\n }}\r\n disabled={!ability.can('update', \"Organization\")}\r\n >\r\n Remove\r\n </Button>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {/* Favicon */}\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.favicon')}</label>\r\n <div className=\"flex items-center gap-4\">\r\n <div className=\"w-16 h-16 border rounded-lg overflow-hidden bg-muted flex items-center justify-center relative group\">\r\n {faviconUrl ? (\r\n <img src={faviconUrl} alt=\"Favicon\" className=\"w-full h-full object-contain\" />\r\n ) : (\r\n <LR.Image className=\"h-6 w-6 text-muted-foreground\" />\r\n )}\r\n {isEditing && (\r\n <label className=\"absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center cursor-pointer\">\r\n <LR.Upload className=\"h-4 w-4 text-white\" />\r\n <input\r\n type=\"file\"\r\n accept=\"image/*\"\r\n className=\"hidden\"\r\n onChange={(e) => {\r\n const file = e.target.files?.[0]\r\n if (file) {\r\n setSelectedFaviconFile(file)\r\n setHasChanges(true)\r\n }\r\n }}\r\n disabled={!ability.can('update', \"Organization\")}\r\n />\r\n </label>\r\n )}\r\n </div>\r\n {isEditing && faviconUrl && (\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n onClick={() => {\r\n setSelectedFaviconFile(null)\r\n handleInputChange('faviconKey', null)\r\n }}\r\n disabled={!ability.can('update', \"Organization\")}\r\n >\r\n Remove\r\n </Button>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Colors */}\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.mainColor')}</label>\r\n <div className=\"flex gap-2\">\r\n <Input\r\n type=\"color\"\r\n value={(getFieldValue('mainColor') as string) || '#000000'}\r\n onChange={(e) => handleInputChange('mainColor', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n className=\"w-20 h-10 p-1\"\r\n \r\n />\r\n <Input\r\n type=\"text\"\r\n value={(getFieldValue('mainColor') as string) || ''}\r\n onChange={(e) => handleInputChange('mainColor', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n placeholder=\"#000000\"\r\n />\r\n </div>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.secondaryColor')}</label>\r\n <div className=\"flex gap-2\">\r\n <Input\r\n type=\"color\"\r\n value={(getFieldValue('secondaryColor') as string) || '#000000'}\r\n onChange={(e) => handleInputChange('secondaryColor', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n className=\"w-20 h-10 p-1\"\r\n />\r\n <Input\r\n type=\"text\"\r\n value={(getFieldValue('secondaryColor') as string) || ''}\r\n onChange={(e) => handleInputChange('secondaryColor', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n placeholder=\"#000000\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <Separator />\r\n\r\n {/* Basic Information */}\r\n <div className=\"space-y-4\">\r\n <h3 className=\"text-lg font-semibold\">{t('sections.basicInfo')}</h3>\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.name')}</label>\r\n <Input\r\n value={(organization?.name as string) || ''}\r\n disabled\r\n readOnly\r\n />\r\n <p className=\"text-xs text-muted-foreground\">{t('fields.nameLocked')}</p>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.title')}</label>\r\n <Input\r\n value={(getFieldValue('title') as string) || ''}\r\n onChange={(e) => handleInputChange('title', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\") }\r\n />\r\n </div>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.description')}</label>\r\n <Textarea\r\n value={(getFieldValue('description') as string) || ''}\r\n onChange={(e) => handleInputChange('description', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n rows={4}\r\n />\r\n </div>\r\n </div>\r\n\r\n <Separator />\r\n\r\n {/* Location */}\r\n <div className=\"space-y-4\">\r\n <h3 className=\"text-lg font-semibold\">{t('sections.location')}</h3>\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.countrySubdivision')}</label>\r\n <Select\r\n value={String(getFieldValue('countrySubdivision') || '')}\r\n onValueChange={(value) => handleInputChange('countrySubdivision', value)}\r\n disabled={!isEditing}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder={t('placeholders.selectProvince')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {countrySubdivisions.map((csName) => (\r\n <SelectItem key={csName} value={csName}>\r\n {csName}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.municipality')}</label>\r\n <Input\r\n value={(getFieldValue('municipality') as string) || ''}\r\n onChange={(e) => handleInputChange('municipality', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.postalCode')}</label>\r\n <Input\r\n value={(getFieldValue('postalCode') as string) || ''}\r\n onChange={(e) => handleInputChange('postalCode', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.locationId')}</label>\r\n <Input\r\n value={(getFieldValue('locationId') as string) || ''}\r\n onChange={(e) => handleInputChange('locationId', e.target.value)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <Separator />\r\n\r\n {/* Map Configuration */}\r\n <div className=\"space-y-4\">\r\n <h3 className=\"text-lg font-semibold\">{t('sections.mapConfig')}</h3>\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.latitude')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('lat') as number) ?? ''}\r\n onChange={(e) => handleInputChange('lat', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.longitude')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('long') as number) ?? ''}\r\n onChange={(e) => handleInputChange('long', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.zoom')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('zoom') as number) ?? ''}\r\n onChange={(e) => handleInputChange('zoom', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.minZoom')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('minZoom') as number) ?? ''}\r\n onChange={(e) => handleInputChange('minZoom', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.bearing')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('bearing') as number) ?? ''}\r\n onChange={(e) => handleInputChange('bearing', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium\">{t('fields.pitch')}</label>\r\n <Input\r\n type=\"number\"\r\n step=\"any\"\r\n value={(getFieldValue('pitch') as number) ?? ''}\r\n onChange={(e) => handleInputChange('pitch', e.target.value ? parseFloat(e.target.value) : null)}\r\n disabled={!isEditing || !ability.can('update', \"Organization\")}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a"]}
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsSidebar.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/settings/src/SettingsSidebar.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAG7C,KAAK,WAAW,GAAG;IACjB,GAAG,EAAE,cAAc,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,WAAW,EAAE,CAAA;IACnB,SAAS,EAAE,cAAc,CAAA;IACzB,WAAW,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CAC3C,CAAA;AAED,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,oBAAoB,+BAkC7F"}
1
+ {"version":3,"file":"SettingsSidebar.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/settings/src/SettingsSidebar.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAI7C,KAAK,WAAW,GAAG;IACjB,GAAG,EAAE,cAAc,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,WAAW,EAAE,CAAA;IACnB,SAAS,EAAE,cAAc,CAAA;IACzB,WAAW,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CAC3C,CAAA;AAED,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,oBAAoB,+BA8D7F"}
@@ -1,14 +1,17 @@
1
1
  "use client";
2
- import { jsx } from "react/jsx-runtime";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Button } from "../../ui/";
4
4
  import {
5
5
  SidebarMenu,
6
6
  SidebarMenuButton,
7
7
  SidebarMenuItem
8
8
  } from "../../ui/Sidebar";
9
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../ui/Select";
9
10
  import { usePermissions } from "../../../store";
11
+ import { useIsMobile } from "../../../hooks/ui/use-mobile";
10
12
  function SettingsSidebar({ tabs, activeTab, onTabChange }) {
11
13
  const { ability } = usePermissions();
14
+ const isMobile = useIsMobile();
12
15
  const isTabVisible = (tabKey) => {
13
16
  if (tabKey === "users") {
14
17
  return ability.can("update", "Role");
@@ -18,7 +21,14 @@ function SettingsSidebar({ tabs, activeTab, onTabChange }) {
18
21
  }
19
22
  return true;
20
23
  };
21
- return /* @__PURE__ */ jsx(SidebarMenu, { className: "w-full", children: tabs.filter((tab) => isTabVisible(tab.key)).map((tab) => /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(SidebarMenuButton, { asChild: true, children: /* @__PURE__ */ jsx(
24
+ const visibleTabs = tabs.filter((tab) => isTabVisible(tab.key));
25
+ if (isMobile) {
26
+ return /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsxs(Select, { value: activeTab, onValueChange: (value) => onTabChange(value), children: [
27
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
28
+ /* @__PURE__ */ jsx(SelectContent, { children: visibleTabs.map((tab) => /* @__PURE__ */ jsx(SelectItem, { value: tab.key, disabled: tab.disabled, children: tab.label }, tab.key)) })
29
+ ] }) });
30
+ }
31
+ return /* @__PURE__ */ jsx(SidebarMenu, { className: "w-full", children: visibleTabs.map((tab) => /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(SidebarMenuButton, { asChild: true, children: /* @__PURE__ */ jsx(
22
32
  Button,
23
33
  {
24
34
  className: `text-sm flex justify-start pointer-events-auto cursor-pointer ${activeTab === tab.key ? "bg-accent" : ""}`,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/core/components/settings/src/SettingsSidebar.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { Button } from '../../ui/'\r\nimport {\r\n SidebarMenu,\r\n SidebarMenuButton,\r\n SidebarMenuItem,\r\n} from '../../ui/Sidebar'\r\nimport type { SettingsTabKey } from './types'\r\nimport { usePermissions } from '../../../store'\r\n\r\ntype SettingsTab = {\r\n key: SettingsTabKey\r\n label: string\r\n disabled?: boolean\r\n}\r\n\r\ntype SettingsSidebarProps = {\r\n tabs: SettingsTab[]\r\n activeTab: SettingsTabKey\r\n onTabChange: (tab: SettingsTabKey) => void\r\n}\r\n\r\nexport default function SettingsSidebar({ tabs, activeTab, onTabChange }: SettingsSidebarProps) {\r\n \r\n // Permissions\r\n const { ability } = usePermissions()\r\n \r\n // Helper to check if tab should be visible\r\n const isTabVisible = (tabKey: SettingsTabKey) => {\r\n if (tabKey === 'users') {\r\n return ability.can('update', 'Role')\r\n }\r\n if (tabKey === 'organization') {\r\n return ability.can('update', 'Organization')\r\n }\r\n return true\r\n }\r\n \r\n return (\r\n <SidebarMenu className=\"w-full\">\r\n {tabs.filter(tab => isTabVisible(tab.key)).map(tab => (\r\n <SidebarMenuItem key={tab.key}>\r\n <SidebarMenuButton asChild>\r\n <Button\r\n className={`text-sm flex justify-start pointer-events-auto cursor-pointer ${activeTab === tab.key ? 'bg-accent' : ''}`}\r\n variant=\"ghost\"\r\n onClick={() => onTabChange(tab.key)}\r\n disabled={tab.disabled}\r\n >\r\n <span>{tab.label}</span>\r\n </Button>\r\n </SidebarMenuButton>\r\n </SidebarMenuItem>\r\n ))}\r\n </SidebarMenu>\r\n )\r\n}\r\n"],"mappings":";AAqDc;AAhDd,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,sBAAsB;AAchB,SAAR,gBAAiC,EAAE,MAAM,WAAW,YAAY,GAAyB;AAG9F,QAAM,EAAE,QAAQ,IAAI,eAAe;AAGnC,QAAM,eAAe,CAAC,WAA2B;AAC/C,QAAI,WAAW,SAAS;AACtB,aAAO,QAAQ,IAAI,UAAU,MAAM;AAAA,IACrC;AACA,QAAI,WAAW,gBAAgB;AAC7B,aAAO,QAAQ,IAAI,UAAU,cAAc;AAAA,IAC7C;AACA,WAAO;AAAA,EACT;AAEA,SACE,oBAAC,eAAY,WAAU,UACpB,eAAK,OAAO,SAAO,aAAa,IAAI,GAAG,CAAC,EAAE,IAAI,SAC7C,oBAAC,mBACC,8BAAC,qBAAkB,SAAO,MACxB;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,iEAAiE,cAAc,IAAI,MAAM,cAAc,EAAE;AAAA,MACpH,SAAQ;AAAA,MACR,SAAS,MAAM,YAAY,IAAI,GAAG;AAAA,MAClC,UAAU,IAAI;AAAA,MAEd,8BAAC,UAAM,cAAI,OAAM;AAAA;AAAA,EACnB,GACF,KAVoB,IAAI,GAW1B,CACD,GACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/core/components/settings/src/SettingsSidebar.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { Button } from '../../ui/'\r\nimport {\r\n SidebarMenu,\r\n SidebarMenuButton,\r\n SidebarMenuItem,\r\n} from '../../ui/Sidebar'\r\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../ui/Select'\r\nimport type { SettingsTabKey } from './types'\r\nimport { usePermissions } from '../../../store'\r\nimport { useIsMobile } from '../../../hooks/ui/use-mobile'\r\n\r\ntype SettingsTab = {\r\n key: SettingsTabKey\r\n label: string\r\n disabled?: boolean\r\n}\r\n\r\ntype SettingsSidebarProps = {\r\n tabs: SettingsTab[]\r\n activeTab: SettingsTabKey\r\n onTabChange: (tab: SettingsTabKey) => void\r\n}\r\n\r\nexport default function SettingsSidebar({ tabs, activeTab, onTabChange }: SettingsSidebarProps) {\r\n\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const isMobile = useIsMobile()\r\n\r\n // Helper to check if tab should be visible\r\n const isTabVisible = (tabKey: SettingsTabKey) => {\r\n if (tabKey === 'users') {\r\n return ability.can('update', 'Role')\r\n }\r\n if (tabKey === 'organization') {\r\n return ability.can('update', 'Organization')\r\n }\r\n return true\r\n }\r\n\r\n const visibleTabs = tabs.filter(tab => isTabVisible(tab.key))\r\n\r\n // Mobile: the desktop sidebar list below is `hidden` under md, with no\r\n // other way to reach it — swap to a dropdown so tabs (e.g. \"Organization\")\r\n // stay reachable on narrow viewports. Branching on isMobile (rather than\r\n // hiding via CSS classes on the desktop markup) keeps the desktop render\r\n // path byte-for-byte unchanged from before.\r\n if (isMobile) {\r\n return (\r\n <div className=\"w-full\">\r\n <Select value={activeTab} onValueChange={value => onTabChange(value as SettingsTabKey)}>\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {visibleTabs.map(tab => (\r\n <SelectItem key={tab.key} value={tab.key} disabled={tab.disabled}>\r\n {tab.label}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <SidebarMenu className=\"w-full\">\r\n {visibleTabs.map(tab => (\r\n <SidebarMenuItem key={tab.key}>\r\n <SidebarMenuButton asChild>\r\n <Button\r\n className={`text-sm flex justify-start pointer-events-auto cursor-pointer ${activeTab === tab.key ? 'bg-accent' : ''}`}\r\n variant=\"ghost\"\r\n onClick={() => onTabChange(tab.key)}\r\n disabled={tab.disabled}\r\n >\r\n <span>{tab.label}</span>\r\n </Button>\r\n </SidebarMenuButton>\r\n </SidebarMenuItem>\r\n ))}\r\n </SidebarMenu>\r\n )\r\n}\r\n"],"mappings":";AAwDQ,SAEI,KAFJ;AAnDR,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,QAAQ,eAAe,YAAY,eAAe,mBAAmB;AAE9E,SAAS,sBAAsB;AAC/B,SAAS,mBAAmB;AAcb,SAAR,gBAAiC,EAAE,MAAM,WAAW,YAAY,GAAyB;AAG9F,QAAM,EAAE,QAAQ,IAAI,eAAe;AAEnC,QAAM,WAAW,YAAY;AAG7B,QAAM,eAAe,CAAC,WAA2B;AAC/C,QAAI,WAAW,SAAS;AACtB,aAAO,QAAQ,IAAI,UAAU,MAAM;AAAA,IACrC;AACA,QAAI,WAAW,gBAAgB;AAC7B,aAAO,QAAQ,IAAI,UAAU,cAAc;AAAA,IAC7C;AACA,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,KAAK,OAAO,SAAO,aAAa,IAAI,GAAG,CAAC;AAO5D,MAAI,UAAU;AACZ,WACE,oBAAC,SAAI,WAAU,UACb,+BAAC,UAAO,OAAO,WAAW,eAAe,WAAS,YAAY,KAAuB,GACnF;AAAA,0BAAC,iBACC,8BAAC,eAAY,GACf;AAAA,MACA,oBAAC,iBACE,sBAAY,IAAI,SACf,oBAAC,cAAyB,OAAO,IAAI,KAAK,UAAU,IAAI,UACrD,cAAI,SADU,IAAI,GAErB,CACD,GACH;AAAA,OACF,GACF;AAAA,EAEJ;AAEA,SACE,oBAAC,eAAY,WAAU,UACpB,sBAAY,IAAI,SACf,oBAAC,mBACC,8BAAC,qBAAkB,SAAO,MACxB;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,iEAAiE,cAAc,IAAI,MAAM,cAAc,EAAE;AAAA,MACpH,SAAQ;AAAA,MACR,SAAS,MAAM,YAAY,IAAI,GAAG;AAAA,MAClC,UAAU,IAAI;AAAA,MAEd,8BAAC,UAAM,cAAI,OAAM;AAAA;AAAA,EACnB,GACF,KAVoB,IAAI,GAW1B,CACD,GACH;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"BugReportDialog.d.ts","sourceRoot":"","sources":["../../../../src/core/components/support/BugReportDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAkB/B,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,MAAM,GACP,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,qBAiJA"}
1
+ {"version":3,"file":"BugReportDialog.d.ts","sourceRoot":"","sources":["../../../../src/core/components/support/BugReportDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAsF/B,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,MAAM,GACP,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,qBAuNA"}
@@ -12,8 +12,61 @@ import { Button } from "../ui/Button";
12
12
  import { Textarea } from "../ui/Textarea";
13
13
  import { Input } from "../ui/Input";
14
14
  import { useCaptureScreenshot } from "../../hooks/useCaptureScreenshot";
15
+ import { useCurrentViewerPosition } from "../../hooks/useCurrentViewerPosition";
16
+ import { useShareUrl } from "../../hooks/useShareUrl";
15
17
  import * as LR from "lucide-react";
16
18
  import { toast } from "sonner";
19
+ function getBrowserInfo() {
20
+ var _a, _b, _c, _d;
21
+ const ua = navigator.userAgent;
22
+ if ("userAgentData" in navigator) {
23
+ const uaData = navigator.userAgentData;
24
+ const brands = (_a = uaData.brands) != null ? _a : [];
25
+ const brand = (_b = brands.find((b) => !b.brand.includes("Not") && b.brand !== "Chromium")) != null ? _b : brands[0];
26
+ const name = (_c = brand == null ? void 0 : brand.brand) != null ? _c : "Unknown";
27
+ const version = (_d = brand == null ? void 0 : brand.version) != null ? _d : "";
28
+ return version ? `${name} ${version}` : name;
29
+ }
30
+ const checks = [
31
+ ["Firefox", /Firefox\/(\d+)/],
32
+ ["Edge", /Edg\/(\d+)/],
33
+ ["Chrome", /Chrome\/(\d+)/],
34
+ ["Safari", /Version\/(\d+).*Safari/]
35
+ ];
36
+ for (const [name, re] of checks) {
37
+ const m = re.exec(ua);
38
+ if (m) return `${name} ${m[1]}`;
39
+ }
40
+ return "Unknown";
41
+ }
42
+ function getDeviceInfo() {
43
+ const ua = navigator.userAgent;
44
+ if (/iPhone/i.test(ua)) {
45
+ return "Mobile - iPhone";
46
+ }
47
+ if (/Android/i.test(ua) && /Mobile/i.test(ua)) {
48
+ return "Mobile - Android";
49
+ }
50
+ if (/iPad/i.test(ua)) {
51
+ return "Tablet - iPad";
52
+ }
53
+ if (/Android/i.test(ua) && !/Mobile/i.test(ua)) {
54
+ return "Tablet - Android";
55
+ }
56
+ if (/Windows NT/i.test(ua)) {
57
+ return "Desktop - Windows";
58
+ }
59
+ if (/Macintosh|Mac OS X/i.test(ua)) {
60
+ return "Desktop - macOS";
61
+ }
62
+ if (/CrOS/i.test(ua)) {
63
+ return "Desktop - ChromeOS";
64
+ }
65
+ if (/Linux/i.test(ua)) {
66
+ return "Desktop - Linux";
67
+ }
68
+ return "Unknown";
69
+ }
17
70
  function BugReportDialog({
18
71
  open,
19
72
  onOpenChange,
@@ -22,28 +75,67 @@ function BugReportDialog({
22
75
  }) {
23
76
  const t = useTranslations("supportDialog");
24
77
  const captureScreenshot = useCaptureScreenshot();
78
+ const getPosition = useCurrentViewerPosition();
79
+ const getShareUrl = useShareUrl();
25
80
  const [title, setTitle] = React.useState("");
26
81
  const [description, setDescription] = React.useState("");
27
82
  const [repro, setRepro] = React.useState("");
28
83
  const [screenshot, setScreenshot] = React.useState(null);
84
+ const [previewScreenshot, setPreviewScreenshot] = React.useState(null);
29
85
  const [loading, setLoading] = React.useState(false);
86
+ const [capturing, setCapturing] = React.useState(false);
30
87
  const [error, setError] = React.useState(null);
88
+ const previewRef = React.useRef(null);
89
+ const errorRef = React.useRef(null);
90
+ React.useEffect(() => {
91
+ var _a;
92
+ if (previewScreenshot) {
93
+ (_a = previewRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "nearest" });
94
+ }
95
+ }, [previewScreenshot]);
96
+ React.useEffect(() => {
97
+ var _a;
98
+ if (error) {
99
+ (_a = errorRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "nearest" });
100
+ }
101
+ }, [error]);
31
102
  const reset = () => {
32
103
  setTitle("");
33
104
  setDescription("");
34
105
  setRepro("");
35
106
  setScreenshot(null);
107
+ setPreviewScreenshot(null);
36
108
  setError(null);
37
109
  };
38
110
  const capture = async () => {
39
- const img = await captureScreenshot();
40
- setScreenshot(img);
111
+ setCapturing(true);
112
+ setError(null);
113
+ try {
114
+ const img = await captureScreenshot();
115
+ if (img) {
116
+ setPreviewScreenshot(img);
117
+ } else {
118
+ setError("Screenshot failed \u2014 the image came back empty.");
119
+ }
120
+ } catch (err) {
121
+ setError(`Screenshot failed: ${err instanceof Error ? err.message : String(err)}`);
122
+ } finally {
123
+ setCapturing(false);
124
+ }
125
+ };
126
+ const confirmScreenshot = () => {
127
+ setScreenshot(previewScreenshot);
128
+ setPreviewScreenshot(null);
129
+ };
130
+ const retakeScreenshot = () => {
131
+ setPreviewScreenshot(null);
41
132
  };
42
133
  const submit = async () => {
43
134
  if (!title.trim()) return;
44
135
  try {
45
136
  setLoading(true);
46
137
  setError(null);
138
+ const shareUrl = await getShareUrl();
47
139
  const res = await fetch("/api/github-issue", {
48
140
  method: "POST",
49
141
  headers: { "Content-Type": "application/json" },
@@ -55,11 +147,14 @@ function BugReportDialog({
55
147
  type: "bug",
56
148
  labels: ["users"],
57
149
  meta: {
58
- url: window.location.href,
150
+ url: shareUrl,
59
151
  userAgent: navigator.userAgent,
152
+ browser: getBrowserInfo(),
153
+ device: getDeviceInfo(),
60
154
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
61
155
  viewer,
62
- userEmail
156
+ userEmail,
157
+ position: getPosition()
63
158
  }
64
159
  })
65
160
  });
@@ -115,16 +210,34 @@ function BugReportDialog({
115
210
  }
116
211
  ),
117
212
  /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
118
- /* @__PURE__ */ jsxs(Button, { onClick: capture, variant: "secondary", disabled: loading, children: [
213
+ /* @__PURE__ */ jsxs(Button, { onClick: capture, variant: "secondary", disabled: loading || capturing, children: [
119
214
  /* @__PURE__ */ jsx(LR.Camera, { className: "h-4 w-4 mr-1" }),
120
- t("captureScreenshot")
215
+ capturing ? t("capturing") : t("captureScreenshot")
121
216
  ] }),
122
- screenshot && /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground flex items-center gap-1", children: [
217
+ screenshot && !previewScreenshot && /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground flex items-center gap-1", children: [
123
218
  /* @__PURE__ */ jsx(LR.CheckCircle, { className: "h-3 w-3 text-green-500" }),
124
219
  t("screenshotAttached")
125
220
  ] })
126
221
  ] }),
127
- screenshot && /* @__PURE__ */ jsx(
222
+ previewScreenshot && /* @__PURE__ */ jsxs("div", { ref: previewRef, className: "flex flex-col gap-2 rounded border p-2", children: [
223
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t("screenshotPreviewTitle") }),
224
+ /* @__PURE__ */ jsx(
225
+ "img",
226
+ {
227
+ src: previewScreenshot,
228
+ alt: "screenshot preview",
229
+ className: "w-full h-auto rounded border object-contain max-h-[60vh]"
230
+ }
231
+ ),
232
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
233
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: retakeScreenshot, children: t("retake") }),
234
+ /* @__PURE__ */ jsxs(Button, { onClick: confirmScreenshot, children: [
235
+ /* @__PURE__ */ jsx(LR.Check, { className: "h-4 w-4 mr-1" }),
236
+ t("attachScreenshot")
237
+ ] })
238
+ ] })
239
+ ] }),
240
+ screenshot && !previewScreenshot && /* @__PURE__ */ jsx(
128
241
  "img",
129
242
  {
130
243
  src: screenshot,
@@ -132,7 +245,7 @@ function BugReportDialog({
132
245
  className: "w-full h-auto rounded border max-h-40 object-contain"
133
246
  }
134
247
  ),
135
- error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
248
+ error && /* @__PURE__ */ jsx("p", { ref: errorRef, className: "text-sm text-destructive", children: error }),
136
249
  /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
137
250
  /* @__PURE__ */ jsx(
138
251
  Button,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/core/components/support/BugReportDialog.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport { useTranslations } from \"next-intl\";\r\n\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n} from \"../ui/Dialog\";\r\n\r\nimport { Button } from \"../ui/Button\";\r\nimport { Textarea } from \"../ui/Textarea\";\r\nimport { Input } from \"../ui/Input\";\r\nimport { useCaptureScreenshot } from \"../../hooks/useCaptureScreenshot\";\r\n\r\nimport * as LR from \"lucide-react\";\r\nimport { toast } from \"sonner\";\r\n\r\nexport function BugReportDialog({\r\n open,\r\n onOpenChange,\r\n userEmail,\r\n viewer,\r\n}: {\r\n open: boolean;\r\n onOpenChange: (v: boolean) => void;\r\n userEmail?: string;\r\n viewer?: string;\r\n}) {\r\n const t = useTranslations(\"supportDialog\");\r\n const captureScreenshot = useCaptureScreenshot();\r\n\r\n const [title, setTitle] = React.useState(\"\");\r\n const [description, setDescription] = React.useState(\"\");\r\n const [repro, setRepro] = React.useState(\"\");\r\n const [screenshot, setScreenshot] = React.useState<string | null>(null);\r\n const [loading, setLoading] = React.useState(false);\r\n const [error, setError] = React.useState<string | null>(null);\r\n\r\n const reset = () => {\r\n setTitle(\"\");\r\n setDescription(\"\");\r\n setRepro(\"\");\r\n setScreenshot(null);\r\n setError(null);\r\n };\r\n\r\n const capture = async () => {\r\n const img = await captureScreenshot();\r\n setScreenshot(img);\r\n };\r\n\r\n const submit = async () => {\r\n if (!title.trim()) return;\r\n try {\r\n setLoading(true);\r\n setError(null);\r\n\r\n const res = await fetch(\"/api/github-issue\", {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n title,\r\n description,\r\n repro,\r\n screenshot,\r\n type: \"bug\",\r\n labels: [\"users\"],\r\n meta: {\r\n url: window.location.href,\r\n userAgent: navigator.userAgent,\r\n timestamp: new Date().toISOString(),\r\n viewer,\r\n userEmail,\r\n },\r\n }),\r\n });\r\n\r\n if (!res.ok) {\r\n const data = await res.json().catch(() => ({}));\r\n throw new Error(data.error || \"Submission failed\");\r\n }\r\n\r\n reset();\r\n onOpenChange(false);\r\n toast.success(t(\"submitSuccess\"));\r\n } catch (err) {\r\n setError(err instanceof Error ? err.message : \"Something went wrong\");\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n return (\r\n <Dialog\r\n open={open}\r\n onOpenChange={(v) => {\r\n onOpenChange(v);\r\n if (!v) reset();\r\n }}\r\n >\r\n <DialogContent className=\"sm:max-w-[520px]\">\r\n <DialogHeader>\r\n <DialogTitle className=\"flex items-center gap-2\">\r\n <LR.Bug className=\"h-5 w-5\" />\r\n {t(\"bugTitle\")}\r\n </DialogTitle>\r\n </DialogHeader>\r\n\r\n <div className=\"space-y-3\">\r\n <Input\r\n placeholder={t(\"bugTitlePlaceholder\")}\r\n value={title}\r\n onChange={(e) => setTitle(e.target.value)}\r\n />\r\n\r\n <Textarea\r\n placeholder={t(\"descriptionPlaceholder\")}\r\n value={description}\r\n onChange={(e) => setDescription(e.target.value)}\r\n />\r\n\r\n <Textarea\r\n placeholder={t(\"reproPlaceholder\")}\r\n value={repro}\r\n onChange={(e) => setRepro(e.target.value)}\r\n />\r\n\r\n <div className=\"flex gap-2 items-center\">\r\n <Button onClick={capture} variant=\"secondary\" disabled={loading}>\r\n <LR.Camera className=\"h-4 w-4 mr-1\" />\r\n {t(\"captureScreenshot\")}\r\n </Button>\r\n {screenshot && (\r\n <span className=\"text-sm text-muted-foreground flex items-center gap-1\">\r\n <LR.CheckCircle className=\"h-3 w-3 text-green-500\" />\r\n {t(\"screenshotAttached\")}\r\n </span>\r\n )}\r\n </div>\r\n\r\n {screenshot && (\r\n <img\r\n src={screenshot}\r\n alt=\"screenshot preview\"\r\n className=\"w-full h-auto rounded border max-h-40 object-contain\"\r\n />\r\n )}\r\n\r\n {error && (\r\n <p className=\"text-sm text-destructive\">{error}</p>\r\n )}\r\n\r\n <div className=\"flex justify-end gap-2\">\r\n <Button\r\n variant=\"secondary\"\r\n onClick={() => onOpenChange(false)}\r\n disabled={loading}\r\n >\r\n {t(\"cancel\")}\r\n </Button>\r\n\r\n <Button\r\n onClick={submit}\r\n disabled={!title.trim() || loading}\r\n >\r\n {loading ? \"...\" : t(\"submit\")}\r\n </Button>\r\n </div>\r\n </div>\r\n </DialogContent>\r\n </Dialog>\r\n );\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/core/components/support/BugReportDialog.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport { useTranslations } from \"next-intl\";\r\n\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n} from \"../ui/Dialog\";\r\n\r\nimport { Button } from \"../ui/Button\";\r\nimport { Textarea } from \"../ui/Textarea\";\r\nimport { Input } from \"../ui/Input\";\r\nimport { useCaptureScreenshot } from \"../../hooks/useCaptureScreenshot\";\r\nimport { useCurrentViewerPosition } from \"../../hooks/useCurrentViewerPosition\";\r\nimport { useShareUrl } from \"../../hooks/useShareUrl\";\r\n\r\nimport * as LR from \"lucide-react\";\r\nimport { toast } from \"sonner\";\r\n\r\nfunction getBrowserInfo(): string {\r\n const ua = navigator.userAgent;\r\n if (\"userAgentData\" in navigator) {\r\n const uaData = (navigator as any).userAgentData;\r\n const brands: Array<{ brand: string; version: string }> = uaData.brands ?? [];\r\n const brand =\r\n brands.find((b) => !b.brand.includes(\"Not\") && b.brand !== \"Chromium\") ??\r\n brands[0];\r\n const name = brand?.brand ?? \"Unknown\";\r\n const version = brand?.version ?? \"\";\r\n return version ? `${name} ${version}` : name;\r\n }\r\n const checks: Array<[string, RegExp]> = [\r\n [\"Firefox\", /Firefox\\/(\\d+)/],\r\n [\"Edge\", /Edg\\/(\\d+)/],\r\n [\"Chrome\", /Chrome\\/(\\d+)/],\r\n [\"Safari\", /Version\\/(\\d+).*Safari/],\r\n ];\r\n for (const [name, re] of checks) {\r\n const m = re.exec(ua);\r\n if (m) return `${name} ${m[1]}`;\r\n }\r\n return \"Unknown\";\r\n}\r\n\r\nfunction getDeviceInfo(): string {\r\n const ua = navigator.userAgent;\r\n\r\n // Mobile\r\n if (/iPhone/i.test(ua)) {\r\n return \"Mobile - iPhone\";\r\n }\r\n\r\n if (/Android/i.test(ua) && /Mobile/i.test(ua)) {\r\n return \"Mobile - Android\";\r\n }\r\n\r\n // Tablets\r\n if (/iPad/i.test(ua)) {\r\n return \"Tablet - iPad\";\r\n }\r\n\r\n if (/Android/i.test(ua) && !/Mobile/i.test(ua)) {\r\n return \"Tablet - Android\";\r\n }\r\n\r\n // Desktop OS\r\n if (/Windows NT/i.test(ua)) {\r\n return \"Desktop - Windows\";\r\n }\r\n\r\n if (/Macintosh|Mac OS X/i.test(ua)) {\r\n return \"Desktop - macOS\";\r\n }\r\n\r\n if (/CrOS/i.test(ua)) {\r\n return \"Desktop - ChromeOS\";\r\n }\r\n\r\n if (/Linux/i.test(ua)) {\r\n return \"Desktop - Linux\";\r\n }\r\n\r\n return \"Unknown\";\r\n}\r\n\r\nexport function BugReportDialog({\r\n open,\r\n onOpenChange,\r\n userEmail,\r\n viewer,\r\n}: {\r\n open: boolean;\r\n onOpenChange: (v: boolean) => void;\r\n userEmail?: string;\r\n viewer?: string;\r\n}) {\r\n const t = useTranslations(\"supportDialog\");\r\n const captureScreenshot = useCaptureScreenshot();\r\n const getPosition = useCurrentViewerPosition();\r\n const getShareUrl = useShareUrl();\r\n\r\n const [title, setTitle] = React.useState(\"\");\r\n const [description, setDescription] = React.useState(\"\");\r\n const [repro, setRepro] = React.useState(\"\");\r\n const [screenshot, setScreenshot] = React.useState<string | null>(null);\r\n // Holds a just-captured image pending the user's confirmation — not\r\n // attached to the report until they explicitly accept it via `OK`.\r\n const [previewScreenshot, setPreviewScreenshot] = React.useState<string | null>(null);\r\n const [loading, setLoading] = React.useState(false);\r\n const [capturing, setCapturing] = React.useState(false);\r\n const [error, setError] = React.useState<string | null>(null);\r\n const previewRef = React.useRef<HTMLDivElement>(null);\r\n const errorRef = React.useRef<HTMLParagraphElement>(null);\r\n\r\n // The dialog body scrolls (max-h-[90vh]); on a short mobile viewport the\r\n // preview OR the error message can render below whatever's currently in\r\n // view with nothing to bring it on-screen automatically — a big desktop\r\n // viewport just happens to fit everything so this never showed up there.\r\n React.useEffect(() => {\r\n if (previewScreenshot) {\r\n previewRef.current?.scrollIntoView({ behavior: \"smooth\", block: \"nearest\" });\r\n }\r\n }, [previewScreenshot]);\r\n\r\n React.useEffect(() => {\r\n if (error) {\r\n errorRef.current?.scrollIntoView({ behavior: \"smooth\", block: \"nearest\" });\r\n }\r\n }, [error]);\r\n\r\n const reset = () => {\r\n setTitle(\"\");\r\n setDescription(\"\");\r\n setRepro(\"\");\r\n setScreenshot(null);\r\n setPreviewScreenshot(null);\r\n setError(null);\r\n };\r\n\r\n const capture = async () => {\r\n setCapturing(true);\r\n setError(null);\r\n try {\r\n const img = await captureScreenshot();\r\n if (img) {\r\n setPreviewScreenshot(img);\r\n } else {\r\n setError('Screenshot failed — the image came back empty.');\r\n }\r\n } catch (err) {\r\n setError(`Screenshot failed: ${err instanceof Error ? err.message : String(err)}`);\r\n } finally {\r\n setCapturing(false);\r\n }\r\n };\r\n\r\n const confirmScreenshot = () => {\r\n setScreenshot(previewScreenshot);\r\n setPreviewScreenshot(null);\r\n };\r\n\r\n const retakeScreenshot = () => {\r\n setPreviewScreenshot(null);\r\n };\r\n\r\n const submit = async () => {\r\n if (!title.trim()) return;\r\n try {\r\n setLoading(true);\r\n setError(null);\r\n\r\n const shareUrl = await getShareUrl();\r\n const res = await fetch(\"/api/github-issue\", {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n title,\r\n description,\r\n repro,\r\n screenshot,\r\n type: \"bug\",\r\n labels: [\"users\"],\r\n meta: {\r\n url: shareUrl,\r\n userAgent: navigator.userAgent,\r\n browser: getBrowserInfo(),\r\n device: getDeviceInfo(),\r\n timestamp: new Date().toISOString(),\r\n viewer,\r\n userEmail,\r\n position: getPosition(),\r\n },\r\n }),\r\n });\r\n\r\n if (!res.ok) {\r\n const data = await res.json().catch(() => ({}));\r\n throw new Error(data.error || \"Submission failed\");\r\n }\r\n\r\n reset();\r\n onOpenChange(false);\r\n toast.success(t(\"submitSuccess\"));\r\n } catch (err) {\r\n setError(err instanceof Error ? err.message : \"Something went wrong\");\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n return (\r\n <Dialog\r\n open={open}\r\n onOpenChange={(v) => {\r\n onOpenChange(v);\r\n if (!v) reset();\r\n }}\r\n >\r\n <DialogContent className=\"sm:max-w-[520px]\">\r\n <DialogHeader>\r\n <DialogTitle className=\"flex items-center gap-2\">\r\n <LR.Bug className=\"h-5 w-5\" />\r\n {t(\"bugTitle\")}\r\n </DialogTitle>\r\n </DialogHeader>\r\n\r\n <div className=\"space-y-3\">\r\n <Input\r\n placeholder={t(\"bugTitlePlaceholder\")}\r\n value={title}\r\n onChange={(e) => setTitle(e.target.value)}\r\n />\r\n\r\n <Textarea\r\n placeholder={t(\"descriptionPlaceholder\")}\r\n value={description}\r\n onChange={(e) => setDescription(e.target.value)}\r\n />\r\n\r\n <Textarea\r\n placeholder={t(\"reproPlaceholder\")}\r\n value={repro}\r\n onChange={(e) => setRepro(e.target.value)}\r\n />\r\n\r\n <div className=\"flex gap-2 items-center\">\r\n <Button onClick={capture} variant=\"secondary\" disabled={loading || capturing}>\r\n <LR.Camera className=\"h-4 w-4 mr-1\" />\r\n {capturing ? t(\"capturing\") : t(\"captureScreenshot\")}\r\n </Button>\r\n {screenshot && !previewScreenshot && (\r\n <span className=\"text-sm text-muted-foreground flex items-center gap-1\">\r\n <LR.CheckCircle className=\"h-3 w-3 text-green-500\" />\r\n {t(\"screenshotAttached\")}\r\n </span>\r\n )}\r\n </div>\r\n\r\n {previewScreenshot && (\r\n <div ref={previewRef} className=\"flex flex-col gap-2 rounded border p-2\">\r\n <p className=\"text-sm font-medium\">{t(\"screenshotPreviewTitle\")}</p>\r\n <img\r\n src={previewScreenshot}\r\n alt=\"screenshot preview\"\r\n className=\"w-full h-auto rounded border object-contain max-h-[60vh]\"\r\n />\r\n <div className=\"flex justify-end gap-2\">\r\n <Button variant=\"secondary\" onClick={retakeScreenshot}>\r\n {t(\"retake\")}\r\n </Button>\r\n <Button onClick={confirmScreenshot}>\r\n <LR.Check className=\"h-4 w-4 mr-1\" />\r\n {t(\"attachScreenshot\")}\r\n </Button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n {screenshot && !previewScreenshot && (\r\n <img\r\n src={screenshot}\r\n alt=\"screenshot preview\"\r\n className=\"w-full h-auto rounded border max-h-40 object-contain\"\r\n />\r\n )}\r\n\r\n {error && (\r\n <p ref={errorRef} className=\"text-sm text-destructive\">{error}</p>\r\n )}\r\n\r\n <div className=\"flex justify-end gap-2\">\r\n <Button\r\n variant=\"secondary\"\r\n onClick={() => onOpenChange(false)}\r\n disabled={loading}\r\n >\r\n {t(\"cancel\")}\r\n </Button>\r\n\r\n <Button\r\n onClick={submit}\r\n disabled={!title.trim() || loading}\r\n >\r\n {loading ? \"...\" : t(\"submit\")}\r\n </Button>\r\n </div>\r\n </div>\r\n </DialogContent>\r\n </Dialog>\r\n );\r\n}\r\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"FeatureRequestDialog.d.ts","sourceRoot":"","sources":["../../../../src/core/components/support/FeatureRequestDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAiB/B,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,KAAK,qBAsGpF"}
1
+ {"version":3,"file":"FeatureRequestDialog.d.ts","sourceRoot":"","sources":["../../../../src/core/components/support/FeatureRequestDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqF/B,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,KAAK,qBA4GpF"}
@@ -13,8 +13,63 @@ import { Input } from "../ui/Input";
13
13
  import { Textarea } from "../ui/Textarea";
14
14
  import * as LR from "lucide-react";
15
15
  import { toast } from "sonner";
16
+ import { useCurrentViewerPosition } from "../../hooks/useCurrentViewerPosition";
17
+ import { useShareUrl } from "../../hooks/useShareUrl";
18
+ function getBrowserInfo() {
19
+ var _a, _b, _c, _d;
20
+ const ua = navigator.userAgent;
21
+ if ("userAgentData" in navigator) {
22
+ const uaData = navigator.userAgentData;
23
+ const brands = (_a = uaData.brands) != null ? _a : [];
24
+ const brand = (_b = brands.find((b) => !b.brand.includes("Not") && b.brand !== "Chromium")) != null ? _b : brands[0];
25
+ const name = (_c = brand == null ? void 0 : brand.brand) != null ? _c : "Unknown";
26
+ const version = (_d = brand == null ? void 0 : brand.version) != null ? _d : "";
27
+ return version ? `${name} ${version}` : name;
28
+ }
29
+ const checks = [
30
+ ["Firefox", /Firefox\/(\d+)/],
31
+ ["Edge", /Edg\/(\d+)/],
32
+ ["Chrome", /Chrome\/(\d+)/],
33
+ ["Safari", /Version\/(\d+).*Safari/]
34
+ ];
35
+ for (const [name, re] of checks) {
36
+ const m = re.exec(ua);
37
+ if (m) return `${name} ${m[1]}`;
38
+ }
39
+ return "Unknown";
40
+ }
41
+ function getDeviceInfo() {
42
+ const ua = navigator.userAgent;
43
+ if (/iPhone/i.test(ua)) {
44
+ return "Mobile - iPhone";
45
+ }
46
+ if (/Android/i.test(ua) && /Mobile/i.test(ua)) {
47
+ return "Mobile - Android";
48
+ }
49
+ if (/iPad/i.test(ua)) {
50
+ return "Tablet - iPad";
51
+ }
52
+ if (/Android/i.test(ua) && !/Mobile/i.test(ua)) {
53
+ return "Tablet - Android";
54
+ }
55
+ if (/Windows NT/i.test(ua)) {
56
+ return "Desktop - Windows";
57
+ }
58
+ if (/Macintosh|Mac OS X/i.test(ua)) {
59
+ return "Desktop - macOS";
60
+ }
61
+ if (/CrOS/i.test(ua)) {
62
+ return "Desktop - ChromeOS";
63
+ }
64
+ if (/Linux/i.test(ua)) {
65
+ return "Desktop - Linux";
66
+ }
67
+ return "Unknown";
68
+ }
16
69
  function FeatureRequestDialog({ open, onOpenChange, userEmail, viewer }) {
17
70
  const t = useTranslations("supportDialog");
71
+ const getPosition = useCurrentViewerPosition();
72
+ const getShareUrl = useShareUrl();
18
73
  const [title, setTitle] = React.useState("");
19
74
  const [description, setDescription] = React.useState("");
20
75
  const [loading, setLoading] = React.useState(false);
@@ -26,6 +81,7 @@ function FeatureRequestDialog({ open, onOpenChange, userEmail, viewer }) {
26
81
  if (!title.trim() || !description.trim()) return;
27
82
  try {
28
83
  setLoading(true);
84
+ const shareUrl = await getShareUrl();
29
85
  await fetch("/api/github-issue", {
30
86
  method: "POST",
31
87
  headers: { "Content-Type": "application/json" },
@@ -35,11 +91,14 @@ function FeatureRequestDialog({ open, onOpenChange, userEmail, viewer }) {
35
91
  type: "feature-request",
36
92
  labels: ["users"],
37
93
  meta: {
38
- url: window.location.href,
94
+ url: shareUrl,
39
95
  userAgent: navigator.userAgent,
96
+ browser: getBrowserInfo(),
97
+ device: getDeviceInfo(),
40
98
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
41
99
  viewer,
42
- userEmail
100
+ userEmail,
101
+ position: getPosition()
43
102
  }
44
103
  })
45
104
  });