@burdenoff/microfe-assethandler 2026.826.3 → 2026.826.4
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/_virtual/_rolldown/runtime.js +1 -1
- package/dist/assethandler/AssetHandlerRoot.js.map +1 -1
- package/dist/assethandler/AssetHandlerRoutes.js.map +1 -1
- package/dist/assethandler/components/ActivityTimeline.js.map +1 -1
- package/dist/assethandler/components/AttachmentsPanel.js.map +1 -1
- package/dist/assethandler/components/ChartCard.js.map +1 -1
- package/dist/assethandler/components/CommentsPanel.js.map +1 -1
- package/dist/assethandler/components/NotFoundState.js.map +1 -1
- package/dist/assethandler/components/Pagination.js.map +1 -1
- package/dist/assethandler/components/SectionHeader.js.map +1 -1
- package/dist/assethandler/components/StatTile.js.map +1 -1
- package/dist/assethandler/components/charts/BarsChart.js.map +1 -1
- package/dist/assethandler/components/charts/ChartParts.js.map +1 -1
- package/dist/assethandler/components/charts/DonutChart.js.map +1 -1
- package/dist/assethandler/components/charts/ProgressMeter.js.map +1 -1
- package/dist/assethandler/components/charts/RadialGauge.js.map +1 -1
- package/dist/assethandler/components/charts/Sparkline.js.map +1 -1
- package/dist/assethandler/components/charts/TrendChart.js.map +1 -1
- package/dist/assethandler/components/charts/chartTheme.js.map +1 -1
- package/dist/assethandler/hooks/useAssetHandlerNavigate.js.map +1 -1
- package/dist/assethandler/hooks/useCopy.js.map +1 -1
- package/dist/assethandler/hooks/useMaintenancePlans.js.map +1 -1
- package/dist/assethandler/ops/MaintenancePlanDialog.js.map +1 -1
- package/dist/assethandler/ops/NewWorkOrderDialog.js.map +1 -1
- package/dist/assethandler/ops/ScheduleMaintenanceDialog.js.map +1 -1
- package/dist/assethandler/pages/AiPredictionsPage.js.map +1 -1
- package/dist/assethandler/pages/AnalyticsPage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetCategoriesPage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetCheckInOutPage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetCreatePage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetDetailPage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetListPage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetTrackingPage.js.map +1 -1
- package/dist/assethandler/pages/CmdbBrowserPage.js.map +1 -1
- package/dist/assethandler/pages/DashboardPage.js.map +1 -1
- package/dist/assethandler/pages/DigitalTwinPage.js.map +1 -1
- package/dist/assethandler/pages/DiscoveryJobsPage.js.map +1 -1
- package/dist/assethandler/pages/EsgDashboardPage.js.map +1 -1
- package/dist/assethandler/pages/FinanceBudgetDetailPage.js.map +1 -1
- package/dist/assethandler/pages/FinanceFinanceHubPage.js.map +1 -1
- package/dist/assethandler/pages/FinanceInvoiceDetailPage.js.map +1 -1
- package/dist/assethandler/pages/IotDevicesPage.js.map +1 -1
- package/dist/assethandler/pages/ItilChangeDetailPage.js.map +1 -1
- package/dist/assethandler/pages/ItilChangeManagementPage.js.map +1 -1
- package/dist/assethandler/pages/ItilDialogs.js.map +1 -1
- package/dist/assethandler/pages/ItilIncidentDetailPage.js.map +1 -1
- package/dist/assethandler/pages/ItilIncidentManagementPage.js.map +1 -1
- package/dist/assethandler/pages/ItilItilhubPage.js.map +1 -1
- package/dist/assethandler/pages/ItilProblemDetailPage.js.map +1 -1
- package/dist/assethandler/pages/ItilProblemManagementPage.js.map +1 -1
- package/dist/assethandler/pages/KnowledgeBasePage.js.map +1 -1
- package/dist/assethandler/pages/MaintenanceMaintenanceCalendarPage.js.map +1 -1
- package/dist/assethandler/pages/MaintenanceMaintenancePlansPage.js.map +1 -1
- package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.js.map +1 -1
- package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js.map +1 -1
- package/dist/assethandler/pages/OperationsOperationsHubPage.js.map +1 -1
- package/dist/assethandler/pages/OperationsWorkOrderDetailPage.js.map +1 -1
- package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js.map +1 -1
- package/dist/assethandler/pages/ProvenancePage.js.map +1 -1
- package/dist/assethandler/pages/ReportsDashboardPage.js.map +1 -1
- package/dist/assethandler/pages/SamLicensesPage.js.map +1 -1
- package/dist/assethandler/pages/SecurityComplianceDetailPage.js.map +1 -1
- package/dist/assethandler/pages/SecurityPolicyDetailPage.js.map +1 -1
- package/dist/assethandler/pages/SecuritySecurityEventDetailPage.js.map +1 -1
- package/dist/assethandler/pages/SecuritySecurityHubPage.js.map +1 -1
- package/dist/assethandler/pages/TransactionsTransactionDetailPage.js.map +1 -1
- package/dist/assethandler/pages/TransactionsTransactionListPage.js.map +1 -1
- package/dist/assethandler/providers/AssetHandlerProvider.js.map +1 -1
- package/dist/assethandler/store/assethandlerStore.js.map +1 -1
- package/dist/assethandler/utils/categoryPresentation.js.map +1 -1
- package/dist/assethandler/utils/cn.js.map +1 -1
- package/dist/assethandler/utils/csv.js.map +1 -1
- package/dist/assethandler/utils/i18n.js.map +1 -1
- package/dist/assethandler/utils/knowledgeBase.js.map +1 -1
- package/dist/assethandler/utils/meter.js.map +1 -1
- package/dist/assethandler/utils/money.js.map +1 -1
- package/dist/assethandler/utils/navigation.js.map +1 -1
- package/dist/assethandler/utils/printAssetTag.js.map +1 -1
- package/dist/assethandler/utils/toast.js.map +1 -1
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -1
- package/dist/node_modules/d3-array/src/ascending.js.map +1 -1
- package/dist/node_modules/d3-array/src/bisect.js.map +1 -1
- package/dist/node_modules/d3-array/src/bisector.js.map +1 -1
- package/dist/node_modules/d3-array/src/descending.js.map +1 -1
- package/dist/node_modules/d3-array/src/max.js.map +1 -1
- package/dist/node_modules/d3-array/src/min.js.map +1 -1
- package/dist/node_modules/d3-array/src/number.js.map +1 -1
- package/dist/node_modules/d3-array/src/quantile.js.map +1 -1
- package/dist/node_modules/d3-array/src/quickselect.js.map +1 -1
- package/dist/node_modules/d3-array/src/range.js.map +1 -1
- package/dist/node_modules/d3-array/src/sort.js.map +1 -1
- package/dist/node_modules/d3-array/src/ticks.js.map +1 -1
- package/dist/node_modules/d3-color/src/color.js.map +1 -1
- package/dist/node_modules/d3-color/src/define.js.map +1 -1
- package/dist/node_modules/d3-format/src/defaultLocale.js.map +1 -1
- package/dist/node_modules/d3-format/src/exponent.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatDecimal.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatGroup.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatNumerals.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatPrefixAuto.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatRounded.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatSpecifier.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatTrim.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatTypes.js.map +1 -1
- package/dist/node_modules/d3-format/src/identity.js.map +1 -1
- package/dist/node_modules/d3-format/src/locale.js.map +1 -1
- package/dist/node_modules/d3-format/src/precisionFixed.js.map +1 -1
- package/dist/node_modules/d3-format/src/precisionPrefix.js.map +1 -1
- package/dist/node_modules/d3-format/src/precisionRound.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/array.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/basis.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/basisClosed.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/color.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/date.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/number.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/numberArray.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/object.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/piecewise.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/rgb.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/round.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/string.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/value.js.map +1 -1
- package/dist/node_modules/d3-path/src/path.js.map +1 -1
- package/dist/node_modules/d3-scale/src/band.js.map +1 -1
- package/dist/node_modules/d3-scale/src/constant.js.map +1 -1
- package/dist/node_modules/d3-scale/src/continuous.js.map +1 -1
- package/dist/node_modules/d3-scale/src/diverging.js.map +1 -1
- package/dist/node_modules/d3-scale/src/identity.js.map +1 -1
- package/dist/node_modules/d3-scale/src/init.js.map +1 -1
- package/dist/node_modules/d3-scale/src/linear.js.map +1 -1
- package/dist/node_modules/d3-scale/src/log.js.map +1 -1
- package/dist/node_modules/d3-scale/src/nice.js.map +1 -1
- package/dist/node_modules/d3-scale/src/number.js.map +1 -1
- package/dist/node_modules/d3-scale/src/ordinal.js.map +1 -1
- package/dist/node_modules/d3-scale/src/pow.js.map +1 -1
- package/dist/node_modules/d3-scale/src/quantile.js.map +1 -1
- package/dist/node_modules/d3-scale/src/quantize.js.map +1 -1
- package/dist/node_modules/d3-scale/src/radial.js.map +1 -1
- package/dist/node_modules/d3-scale/src/sequential.js.map +1 -1
- package/dist/node_modules/d3-scale/src/sequentialQuantile.js.map +1 -1
- package/dist/node_modules/d3-scale/src/symlog.js.map +1 -1
- package/dist/node_modules/d3-scale/src/threshold.js.map +1 -1
- package/dist/node_modules/d3-scale/src/tickFormat.js.map +1 -1
- package/dist/node_modules/d3-scale/src/time.js.map +1 -1
- package/dist/node_modules/d3-scale/src/utcTime.js.map +1 -1
- package/dist/node_modules/d3-shape/src/area.js.map +1 -1
- package/dist/node_modules/d3-shape/src/array.js.map +1 -1
- package/dist/node_modules/d3-shape/src/constant.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/basis.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/basisClosed.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/basisOpen.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/bump.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/linear.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/linearClosed.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/monotone.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/natural.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/step.js.map +1 -1
- package/dist/node_modules/d3-shape/src/line.js.map +1 -1
- package/dist/node_modules/d3-shape/src/noop.js.map +1 -1
- package/dist/node_modules/d3-shape/src/offset/expand.js.map +1 -1
- package/dist/node_modules/d3-shape/src/offset/none.js.map +1 -1
- package/dist/node_modules/d3-shape/src/offset/silhouette.js.map +1 -1
- package/dist/node_modules/d3-shape/src/offset/wiggle.js.map +1 -1
- package/dist/node_modules/d3-shape/src/order/none.js.map +1 -1
- package/dist/node_modules/d3-shape/src/path.js.map +1 -1
- package/dist/node_modules/d3-shape/src/point.js.map +1 -1
- package/dist/node_modules/d3-shape/src/stack.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/circle.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/cross.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/diamond.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/square.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/star.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/triangle.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/wye.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol.js.map +1 -1
- package/dist/node_modules/d3-time/src/day.js.map +1 -1
- package/dist/node_modules/d3-time/src/hour.js.map +1 -1
- package/dist/node_modules/d3-time/src/interval.js.map +1 -1
- package/dist/node_modules/d3-time/src/millisecond.js.map +1 -1
- package/dist/node_modules/d3-time/src/minute.js.map +1 -1
- package/dist/node_modules/d3-time/src/month.js.map +1 -1
- package/dist/node_modules/d3-time/src/second.js.map +1 -1
- package/dist/node_modules/d3-time/src/ticks.js.map +1 -1
- package/dist/node_modules/d3-time/src/week.js.map +1 -1
- package/dist/node_modules/d3-time/src/year.js.map +1 -1
- package/dist/node_modules/d3-time-format/src/defaultLocale.js.map +1 -1
- package/dist/node_modules/d3-time-format/src/locale.js.map +1 -1
- package/dist/node_modules/decimal.js-light/decimal.js.map +1 -1
- package/dist/node_modules/dijkstrajs/dijkstra.js.map +1 -1
- package/dist/node_modules/eventemitter3/index.js.map +1 -1
- package/dist/node_modules/fast-equals/dist/es/index.js.map +1 -1
- package/dist/node_modules/internmap/src/index.js.map +1 -1
- package/dist/node_modules/lodash/_DataView.js.map +1 -1
- package/dist/node_modules/lodash/_Hash.js.map +1 -1
- package/dist/node_modules/lodash/_ListCache.js.map +1 -1
- package/dist/node_modules/lodash/_Map.js.map +1 -1
- package/dist/node_modules/lodash/_MapCache.js.map +1 -1
- package/dist/node_modules/lodash/_Promise.js.map +1 -1
- package/dist/node_modules/lodash/_Set.js.map +1 -1
- package/dist/node_modules/lodash/_SetCache.js.map +1 -1
- package/dist/node_modules/lodash/_Stack.js +2 -1
- package/dist/node_modules/lodash/_Stack.js.map +1 -1
- package/dist/node_modules/lodash/_Symbol.js.map +1 -1
- package/dist/node_modules/lodash/_Uint8Array.js.map +1 -1
- package/dist/node_modules/lodash/_WeakMap.js.map +1 -1
- package/dist/node_modules/lodash/_apply.js.map +1 -1
- package/dist/node_modules/lodash/_arrayEvery.js.map +1 -1
- package/dist/node_modules/lodash/_arrayFilter.js.map +1 -1
- package/dist/node_modules/lodash/_arrayIncludes.js.map +1 -1
- package/dist/node_modules/lodash/_arrayIncludesWith.js.map +1 -1
- package/dist/node_modules/lodash/_arrayLikeKeys.js.map +1 -1
- package/dist/node_modules/lodash/_arrayMap.js.map +1 -1
- package/dist/node_modules/lodash/_arrayPush.js.map +1 -1
- package/dist/node_modules/lodash/_arraySome.js.map +1 -1
- package/dist/node_modules/lodash/_asciiToArray.js.map +1 -1
- package/dist/node_modules/lodash/_assocIndexOf.js.map +1 -1
- package/dist/node_modules/lodash/_baseAssignValue.js.map +1 -1
- package/dist/node_modules/lodash/_baseEach.js.map +1 -1
- package/dist/node_modules/lodash/_baseEvery.js.map +1 -1
- package/dist/node_modules/lodash/_baseExtremum.js.map +1 -1
- package/dist/node_modules/lodash/_baseFindIndex.js.map +1 -1
- package/dist/node_modules/lodash/_baseFlatten.js.map +1 -1
- package/dist/node_modules/lodash/_baseFor.js.map +1 -1
- package/dist/node_modules/lodash/_baseForOwn.js.map +1 -1
- package/dist/node_modules/lodash/_baseGet.js.map +1 -1
- package/dist/node_modules/lodash/_baseGetAllKeys.js.map +1 -1
- package/dist/node_modules/lodash/_baseGetTag.js.map +1 -1
- package/dist/node_modules/lodash/_baseGt.js.map +1 -1
- package/dist/node_modules/lodash/_baseHasIn.js.map +1 -1
- package/dist/node_modules/lodash/_baseIndexOf.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsArguments.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsEqual.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsEqualDeep.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsMatch.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsNaN.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsNative.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsTypedArray.js.map +1 -1
- package/dist/node_modules/lodash/_baseIteratee.js.map +1 -1
- package/dist/node_modules/lodash/_baseKeys.js.map +1 -1
- package/dist/node_modules/lodash/_baseLt.js.map +1 -1
- package/dist/node_modules/lodash/_baseMap.js.map +1 -1
- package/dist/node_modules/lodash/_baseMatches.js.map +1 -1
- package/dist/node_modules/lodash/_baseMatchesProperty.js.map +1 -1
- package/dist/node_modules/lodash/_baseOrderBy.js.map +1 -1
- package/dist/node_modules/lodash/_baseProperty.js.map +1 -1
- package/dist/node_modules/lodash/_basePropertyDeep.js.map +1 -1
- package/dist/node_modules/lodash/_baseRange.js.map +1 -1
- package/dist/node_modules/lodash/_baseRest.js.map +1 -1
- package/dist/node_modules/lodash/_baseSetToString.js.map +1 -1
- package/dist/node_modules/lodash/_baseSlice.js.map +1 -1
- package/dist/node_modules/lodash/_baseSome.js.map +1 -1
- package/dist/node_modules/lodash/_baseSortBy.js.map +1 -1
- package/dist/node_modules/lodash/_baseTimes.js.map +1 -1
- package/dist/node_modules/lodash/_baseToString.js.map +1 -1
- package/dist/node_modules/lodash/_baseTrim.js.map +1 -1
- package/dist/node_modules/lodash/_baseUnary.js.map +1 -1
- package/dist/node_modules/lodash/_baseUniq.js.map +1 -1
- package/dist/node_modules/lodash/_cacheHas.js.map +1 -1
- package/dist/node_modules/lodash/_castPath.js.map +1 -1
- package/dist/node_modules/lodash/_castSlice.js.map +1 -1
- package/dist/node_modules/lodash/_compareAscending.js.map +1 -1
- package/dist/node_modules/lodash/_compareMultiple.js.map +1 -1
- package/dist/node_modules/lodash/_coreJsData.js.map +1 -1
- package/dist/node_modules/lodash/_createBaseEach.js.map +1 -1
- package/dist/node_modules/lodash/_createBaseFor.js.map +1 -1
- package/dist/node_modules/lodash/_createCaseFirst.js.map +1 -1
- package/dist/node_modules/lodash/_createFind.js.map +1 -1
- package/dist/node_modules/lodash/_createRange.js.map +1 -1
- package/dist/node_modules/lodash/_createSet.js.map +1 -1
- package/dist/node_modules/lodash/_defineProperty.js.map +1 -1
- package/dist/node_modules/lodash/_equalArrays.js.map +1 -1
- package/dist/node_modules/lodash/_equalByTag.js.map +1 -1
- package/dist/node_modules/lodash/_equalObjects.js.map +1 -1
- package/dist/node_modules/lodash/_freeGlobal.js.map +1 -1
- package/dist/node_modules/lodash/_getAllKeys.js.map +1 -1
- package/dist/node_modules/lodash/_getMapData.js.map +1 -1
- package/dist/node_modules/lodash/_getMatchData.js.map +1 -1
- package/dist/node_modules/lodash/_getNative.js.map +1 -1
- package/dist/node_modules/lodash/_getPrototype.js.map +1 -1
- package/dist/node_modules/lodash/_getRawTag.js.map +1 -1
- package/dist/node_modules/lodash/_getSymbols.js.map +1 -1
- package/dist/node_modules/lodash/_getTag.js.map +1 -1
- package/dist/node_modules/lodash/_getValue.js.map +1 -1
- package/dist/node_modules/lodash/_hasPath.js.map +1 -1
- package/dist/node_modules/lodash/_hasUnicode.js.map +1 -1
- package/dist/node_modules/lodash/_hashClear.js.map +1 -1
- package/dist/node_modules/lodash/_hashDelete.js.map +1 -1
- package/dist/node_modules/lodash/_hashGet.js.map +1 -1
- package/dist/node_modules/lodash/_hashHas.js.map +1 -1
- package/dist/node_modules/lodash/_hashSet.js.map +1 -1
- package/dist/node_modules/lodash/_isFlattenable.js.map +1 -1
- package/dist/node_modules/lodash/_isIndex.js.map +1 -1
- package/dist/node_modules/lodash/_isIterateeCall.js.map +1 -1
- package/dist/node_modules/lodash/_isKey.js.map +1 -1
- package/dist/node_modules/lodash/_isKeyable.js.map +1 -1
- package/dist/node_modules/lodash/_isMasked.js.map +1 -1
- package/dist/node_modules/lodash/_isPrototype.js.map +1 -1
- package/dist/node_modules/lodash/_isStrictComparable.js.map +1 -1
- package/dist/node_modules/lodash/_listCacheClear.js.map +1 -1
- package/dist/node_modules/lodash/_listCacheDelete.js.map +1 -1
- package/dist/node_modules/lodash/_listCacheGet.js.map +1 -1
- package/dist/node_modules/lodash/_listCacheHas.js.map +1 -1
- package/dist/node_modules/lodash/_listCacheSet.js.map +1 -1
- package/dist/node_modules/lodash/_mapCacheClear.js.map +1 -1
- package/dist/node_modules/lodash/_mapCacheDelete.js.map +1 -1
- package/dist/node_modules/lodash/_mapCacheGet.js.map +1 -1
- package/dist/node_modules/lodash/_mapCacheHas.js.map +1 -1
- package/dist/node_modules/lodash/_mapCacheSet.js.map +1 -1
- package/dist/node_modules/lodash/_mapToArray.js.map +1 -1
- package/dist/node_modules/lodash/_matchesStrictComparable.js.map +1 -1
- package/dist/node_modules/lodash/_memoizeCapped.js.map +1 -1
- package/dist/node_modules/lodash/_nativeCreate.js.map +1 -1
- package/dist/node_modules/lodash/_nativeKeys.js.map +1 -1
- package/dist/node_modules/lodash/_nodeUtil.js.map +1 -1
- package/dist/node_modules/lodash/_objectToString.js.map +1 -1
- package/dist/node_modules/lodash/_overArg.js.map +1 -1
- package/dist/node_modules/lodash/_overRest.js.map +1 -1
- package/dist/node_modules/lodash/_root.js.map +1 -1
- package/dist/node_modules/lodash/_setCacheAdd.js.map +1 -1
- package/dist/node_modules/lodash/_setCacheHas.js.map +1 -1
- package/dist/node_modules/lodash/_setToArray.js.map +1 -1
- package/dist/node_modules/lodash/_setToString.js.map +1 -1
- package/dist/node_modules/lodash/_shortOut.js.map +1 -1
- package/dist/node_modules/lodash/_stackClear.js.map +1 -1
- package/dist/node_modules/lodash/_stackDelete.js.map +1 -1
- package/dist/node_modules/lodash/_stackGet.js.map +1 -1
- package/dist/node_modules/lodash/_stackHas.js.map +1 -1
- package/dist/node_modules/lodash/_stackSet.js.map +1 -1
- package/dist/node_modules/lodash/_strictIndexOf.js.map +1 -1
- package/dist/node_modules/lodash/_stringToArray.js.map +1 -1
- package/dist/node_modules/lodash/_stringToPath.js.map +1 -1
- package/dist/node_modules/lodash/_toKey.js.map +1 -1
- package/dist/node_modules/lodash/_toSource.js.map +1 -1
- package/dist/node_modules/lodash/_trimmedEndIndex.js.map +1 -1
- package/dist/node_modules/lodash/_unicodeToArray.js.map +1 -1
- package/dist/node_modules/lodash/constant.js.map +1 -1
- package/dist/node_modules/lodash/debounce.js.map +1 -1
- package/dist/node_modules/lodash/eq.js.map +1 -1
- package/dist/node_modules/lodash/every.js.map +1 -1
- package/dist/node_modules/lodash/find.js.map +1 -1
- package/dist/node_modules/lodash/findIndex.js.map +1 -1
- package/dist/node_modules/lodash/flatMap.js.map +1 -1
- package/dist/node_modules/lodash/get.js.map +1 -1
- package/dist/node_modules/lodash/hasIn.js.map +1 -1
- package/dist/node_modules/lodash/identity.js.map +1 -1
- package/dist/node_modules/lodash/isArguments.js.map +1 -1
- package/dist/node_modules/lodash/isArray.js.map +1 -1
- package/dist/node_modules/lodash/isArrayLike.js.map +1 -1
- package/dist/node_modules/lodash/isBoolean.js.map +1 -1
- package/dist/node_modules/lodash/isBuffer.js.map +1 -1
- package/dist/node_modules/lodash/isEqual.js.map +1 -1
- package/dist/node_modules/lodash/isFunction.js.map +1 -1
- package/dist/node_modules/lodash/isLength.js.map +1 -1
- package/dist/node_modules/lodash/isNaN.js.map +1 -1
- package/dist/node_modules/lodash/isNil.js.map +1 -1
- package/dist/node_modules/lodash/isNumber.js.map +1 -1
- package/dist/node_modules/lodash/isObject.js.map +1 -1
- package/dist/node_modules/lodash/isObjectLike.js.map +1 -1
- package/dist/node_modules/lodash/isPlainObject.js.map +1 -1
- package/dist/node_modules/lodash/isString.js.map +1 -1
- package/dist/node_modules/lodash/isSymbol.js.map +1 -1
- package/dist/node_modules/lodash/isTypedArray.js.map +1 -1
- package/dist/node_modules/lodash/keys.js.map +1 -1
- package/dist/node_modules/lodash/last.js.map +1 -1
- package/dist/node_modules/lodash/map.js.map +1 -1
- package/dist/node_modules/lodash/mapValues.js.map +1 -1
- package/dist/node_modules/lodash/max.js.map +1 -1
- package/dist/node_modules/lodash/maxBy.js.map +1 -1
- package/dist/node_modules/lodash/memoize.js.map +1 -1
- package/dist/node_modules/lodash/min.js.map +1 -1
- package/dist/node_modules/lodash/minBy.js.map +1 -1
- package/dist/node_modules/lodash/noop.js.map +1 -1
- package/dist/node_modules/lodash/now.js.map +1 -1
- package/dist/node_modules/lodash/property.js.map +1 -1
- package/dist/node_modules/lodash/range.js.map +1 -1
- package/dist/node_modules/lodash/some.js.map +1 -1
- package/dist/node_modules/lodash/sortBy.js.map +1 -1
- package/dist/node_modules/lodash/stubArray.js.map +1 -1
- package/dist/node_modules/lodash/stubFalse.js.map +1 -1
- package/dist/node_modules/lodash/throttle.js.map +1 -1
- package/dist/node_modules/lodash/toFinite.js.map +1 -1
- package/dist/node_modules/lodash/toInteger.js.map +1 -1
- package/dist/node_modules/lodash/toNumber.js.map +1 -1
- package/dist/node_modules/lodash/toString.js.map +1 -1
- package/dist/node_modules/lodash/uniqBy.js.map +1 -1
- package/dist/node_modules/lodash/upperFirst.js.map +1 -1
- package/dist/node_modules/object-assign/index.js.map +1 -1
- package/dist/node_modules/prop-types/checkPropTypes.js.map +1 -1
- package/dist/node_modules/prop-types/factoryWithThrowingShims.js.map +1 -1
- package/dist/node_modules/prop-types/factoryWithTypeCheckers.js.map +1 -1
- package/dist/node_modules/prop-types/index.js.map +1 -1
- package/dist/node_modules/prop-types/lib/ReactPropTypesSecret.js.map +1 -1
- package/dist/node_modules/prop-types/lib/has.js.map +1 -1
- package/dist/node_modules/prop-types/node_modules/react-is/cjs/react-is.development.js.map +1 -1
- package/dist/node_modules/prop-types/node_modules/react-is/cjs/react-is.production.min.js.map +1 -1
- package/dist/node_modules/prop-types/node_modules/react-is/index.js.map +1 -1
- package/dist/node_modules/qrcode/lib/browser.js.map +1 -1
- package/dist/node_modules/qrcode/lib/can-promise.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/alignment-pattern.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/alphanumeric-data.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/bit-buffer.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/bit-matrix.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/byte-data.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/error-correction-code.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/error-correction-level.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/finder-pattern.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/format-info.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/galois-field.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/kanji-data.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/mask-pattern.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/mode.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/numeric-data.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/polynomial.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/qrcode.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/reed-solomon-encoder.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/regex.js +1 -1
- package/dist/node_modules/qrcode/lib/core/regex.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/segments.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/utils.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/version-check.js.map +1 -1
- package/dist/node_modules/qrcode/lib/core/version.js.map +1 -1
- package/dist/node_modules/qrcode/lib/renderer/canvas.js.map +1 -1
- package/dist/node_modules/qrcode/lib/renderer/svg-tag.js.map +1 -1
- package/dist/node_modules/qrcode/lib/renderer/utils.js.map +1 -1
- package/dist/node_modules/react-is/cjs/react-is.development.js.map +1 -1
- package/dist/node_modules/react-is/cjs/react-is.production.min.js.map +1 -1
- package/dist/node_modules/react-is/index.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/Animate.js +2 -2
- package/dist/node_modules/react-smooth/es6/Animate.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/AnimateManager.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/configUpdate.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/easing.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/setRafTimeout.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/util.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/Area.js +13 -13
- package/dist/node_modules/recharts/es6/cartesian/Area.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/Bar.js +9 -9
- package/dist/node_modules/recharts/es6/cartesian/Bar.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/Brush.js +12 -12
- package/dist/node_modules/recharts/es6/cartesian/Brush.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/CartesianAxis.js +7 -7
- package/dist/node_modules/recharts/es6/cartesian/CartesianAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/CartesianGrid.js +13 -13
- package/dist/node_modules/recharts/es6/cartesian/CartesianGrid.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ErrorBar.js +4 -4
- package/dist/node_modules/recharts/es6/cartesian/ErrorBar.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/Line.js +10 -10
- package/dist/node_modules/recharts/es6/cartesian/Line.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ReferenceArea.js +3 -3
- package/dist/node_modules/recharts/es6/cartesian/ReferenceArea.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ReferenceDot.js +3 -3
- package/dist/node_modules/recharts/es6/cartesian/ReferenceDot.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ReferenceLine.js +4 -4
- package/dist/node_modules/recharts/es6/cartesian/ReferenceLine.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/XAxis.js +3 -3
- package/dist/node_modules/recharts/es6/cartesian/XAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/YAxis.js +3 -3
- package/dist/node_modules/recharts/es6/cartesian/YAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/getEquidistantTicks.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/getTicks.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/AccessibilityManager.js +1 -1
- package/dist/node_modules/recharts/es6/chart/AccessibilityManager.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/AreaChart.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/BarChart.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/LineChart.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/PieChart.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/RadialBarChart.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/generateCategoricalChart.js +22 -22
- package/dist/node_modules/recharts/es6/chart/generateCategoricalChart.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Cell.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Cursor.js +1 -1
- package/dist/node_modules/recharts/es6/component/Cursor.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/DefaultLegendContent.js +11 -11
- package/dist/node_modules/recharts/es6/component/DefaultLegendContent.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/DefaultTooltipContent.js +6 -6
- package/dist/node_modules/recharts/es6/component/DefaultTooltipContent.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Label.js +14 -14
- package/dist/node_modules/recharts/es6/component/Label.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/LabelList.js +6 -6
- package/dist/node_modules/recharts/es6/component/LabelList.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Legend.js +5 -5
- package/dist/node_modules/recharts/es6/component/Legend.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/ResponsiveContainer.js +3 -3
- package/dist/node_modules/recharts/es6/component/ResponsiveContainer.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Text.js +2 -2
- package/dist/node_modules/recharts/es6/component/Text.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Tooltip.js +3 -3
- package/dist/node_modules/recharts/es6/component/Tooltip.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/TooltipBoundingBox.js +2 -2
- package/dist/node_modules/recharts/es6/component/TooltipBoundingBox.js.map +1 -1
- package/dist/node_modules/recharts/es6/container/Layer.js +2 -2
- package/dist/node_modules/recharts/es6/container/Layer.js.map +1 -1
- package/dist/node_modules/recharts/es6/container/Surface.js +2 -2
- package/dist/node_modules/recharts/es6/container/Surface.js.map +1 -1
- package/dist/node_modules/recharts/es6/context/chartLayoutContext.js +2 -2
- package/dist/node_modules/recharts/es6/context/chartLayoutContext.js.map +1 -1
- package/dist/node_modules/recharts/es6/polar/Pie.js +13 -13
- package/dist/node_modules/recharts/es6/polar/Pie.js.map +1 -1
- package/dist/node_modules/recharts/es6/polar/PolarAngleAxis.js +8 -8
- package/dist/node_modules/recharts/es6/polar/PolarAngleAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/polar/PolarRadiusAxis.js +6 -6
- package/dist/node_modules/recharts/es6/polar/PolarRadiusAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/polar/RadialBar.js +6 -6
- package/dist/node_modules/recharts/es6/polar/RadialBar.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Cross.js +1 -1
- package/dist/node_modules/recharts/es6/shape/Cross.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Curve.js +1 -1
- package/dist/node_modules/recharts/es6/shape/Curve.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Dot.js +1 -1
- package/dist/node_modules/recharts/es6/shape/Dot.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Polygon.js +4 -4
- package/dist/node_modules/recharts/es6/shape/Polygon.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Rectangle.js +4 -4
- package/dist/node_modules/recharts/es6/shape/Rectangle.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Sector.js +1 -1
- package/dist/node_modules/recharts/es6/shape/Sector.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Symbols.js +1 -1
- package/dist/node_modules/recharts/es6/shape/Symbols.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Trapezoid.js +4 -4
- package/dist/node_modules/recharts/es6/shape/Trapezoid.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ActiveShapeUtils.js +9 -9
- package/dist/node_modules/recharts/es6/util/ActiveShapeUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/BarUtils.js +1 -1
- package/dist/node_modules/recharts/es6/util/BarUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/CartesianUtils.js +1 -1
- package/dist/node_modules/recharts/es6/util/CartesianUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ChartUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/CssPrefixUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/DOMUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/DataUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/DetectReferenceElementsDomain.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/Events.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/Global.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/IfOverflowMatches.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/LogUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/PolarUtils.js +1 -1
- package/dist/node_modules/recharts/es6/util/PolarUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/RadialBarUtils.js +1 -1
- package/dist/node_modules/recharts/es6/util/RadialBarUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ReactUtils.js +1 -1
- package/dist/node_modules/recharts/es6/util/ReactUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ReduceCSSCalc.js +1 -1
- package/dist/node_modules/recharts/es6/util/ReduceCSSCalc.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ShallowEqual.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/TickUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/calculateViewBox.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/cursor/getCursorPoints.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/cursor/getCursorRectangle.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/cursor/getRadialCursorPoints.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/getEveryNthWithCondition.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/getLegendProps.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/isDomainSpecifiedByUser.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/payload/getUniqPayload.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/tooltip/translate.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/types.js +1 -1
- package/dist/node_modules/recharts/es6/util/types.js.map +1 -1
- package/dist/node_modules/recharts-scale/es6/getNiceTickValues.js.map +1 -1
- package/dist/node_modules/recharts-scale/es6/util/arithmetic.js.map +1 -1
- package/dist/node_modules/recharts-scale/es6/util/utils.js.map +1 -1
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1 -1
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
- package/dist/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +1 -1
- package/dist/services/createSandboxAssistantTransport.js.map +1 -1
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region \0rolldown/runtime.js
|
|
2
|
-
var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescriptor, r = Object.getOwnPropertyNames, i = Object.getPrototypeOf, a = Object.prototype.hasOwnProperty, o = (e, t) => () => (t || e((t = { exports: {} }).exports, t), t.exports), s = (e, n) => {
|
|
2
|
+
var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescriptor, r = Object.getOwnPropertyNames, i = Object.getPrototypeOf, a = Object.prototype.hasOwnProperty, o = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t.exports), s = (e, n) => {
|
|
3
3
|
let r = {};
|
|
4
4
|
for (var i in e) t(r, i, {
|
|
5
5
|
get: e[i],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetHandlerRoot.js","names":[],"sources":["../../src/assethandler/AssetHandlerRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport {\n BrainCircuit,\n Boxes,\n Cpu,\n KeyRound,\n Leaf,\n Network,\n Radar,\n ShieldCheck,\n} from 'lucide-react';\n\nimport { AssetHandlerRoutes } from './AssetHandlerRoutes';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { AssetHandlerProvider } from './providers/AssetHandlerProvider';\nimport type { AssetHandlerRootProps, NavItem } from './types';\nimport { joinPath } from './utils/navigation';\n\nexport const AssetHandlerRoot: FC<AssetHandlerRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = '/',\n brandLabel = 'AssetHandler',\n terminology,\n hiddenCategoryIds,\n } = props;\n const assetsLabel = terminology?.assets || 'Assets';\n\n useEffect(() => {\n if (!registerNavItems) {\n return;\n }\n\n // Consumers can hide advanced (IT-specific) nav children by passing their\n // id with the \"assethandler-\" prefix stripped, e.g. \"digital-twin\".\n const hidden = new Set(hiddenCategoryIds ?? []);\n const isHidden = (id: string) => hidden.has(id.replace(/^assethandler-/, ''));\n\n const navItem: NavItem = {\n id: 'assethandler',\n label: brandLabel,\n path: basePath,\n icon: <Boxes size={20} />,\n order: 82,\n group: 'products',\n children: [\n {\n id: 'assethandler-dashboard',\n label: 'Dashboard',\n path: basePath,\n order: 0,\n },\n {\n id: 'assethandler-assets',\n label: assetsLabel,\n path: joinPath(basePath, 'assets/asset-list'),\n order: 1,\n },\n {\n id: 'assethandler-operations',\n label: 'Operations',\n path: joinPath(basePath, 'operations/operations-hub'),\n order: 2,\n },\n {\n id: 'assethandler-itil',\n label: 'ITIL',\n path: joinPath(basePath, 'itil/itilhub'),\n order: 3,\n },\n {\n id: 'assethandler-finance',\n label: 'Finance',\n path: joinPath(basePath, 'finance/finance-hub'),\n order: 4,\n },\n {\n id: 'assethandler-security',\n label: 'Security',\n path: joinPath(basePath, 'security/security-hub'),\n order: 7,\n },\n {\n id: 'assethandler-iot',\n label: 'IoT Devices',\n path: joinPath(basePath, 'advanced/iot-devices'),\n icon: <Cpu size={16} />,\n order: 10,\n },\n {\n id: 'assethandler-digital-twin',\n label: 'Digital Twin',\n path: joinPath(basePath, 'advanced/digital-twin'),\n icon: <Boxes size={16} />,\n order: 11,\n },\n {\n id: 'assethandler-sam',\n label: 'SAM',\n path: joinPath(basePath, 'advanced/sam-licenses'),\n icon: <KeyRound size={16} />,\n order: 12,\n },\n {\n id: 'assethandler-cmdb',\n label: 'CMDB',\n path: joinPath(basePath, 'advanced/cmdb-browser'),\n icon: <Network size={16} />,\n order: 13,\n },\n {\n id: 'assethandler-discovery',\n label: 'Discovery',\n path: joinPath(basePath, 'advanced/discovery-jobs'),\n icon: <Radar size={16} />,\n order: 14,\n },\n {\n id: 'assethandler-provenance',\n label: 'Provenance',\n path: joinPath(basePath, 'advanced/provenance'),\n icon: <ShieldCheck size={16} />,\n order: 15,\n },\n {\n id: 'assethandler-esg',\n label: 'ESG',\n path: joinPath(basePath, 'advanced/esg-dashboard'),\n icon: <Leaf size={16} />,\n order: 16,\n },\n {\n id: 'assethandler-ai',\n label: 'AI Predictions',\n path: joinPath(basePath, 'advanced/ai-predictions'),\n icon: <BrainCircuit size={16} />,\n order: 17,\n },\n ],\n };\n\n if (navItem.children) {\n navItem.children = navItem.children.filter((child) => !isHidden(child.id));\n }\n\n return registerNavItems(navItem);\n }, [basePath, registerNavItems, brandLabel, assetsLabel, hiddenCategoryIds]);\n\n useEffect(() => {\n if (!registerFooterItems) {\n return;\n }\n\n return registerFooterItems([\n {\n id: 'assethandler-status',\n label: `${brandLabel} Live`,\n type: 'status',\n section: 'left',\n order: 10,\n },\n ]);\n }, [registerFooterItems, brandLabel]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <AssetHandlerProvider {...props}>\n <AssetHandlerRoutes />\n </AssetHandlerProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;AAmBA,IAAa,KAA+C,MAAU;CACpE,IAAM,EACJ,qBACA,wBACA,cAAW,KACX,gBAAa,gBACb,gBACA,yBACE,GACE,IAAc,GAAa,UAAU;
|
|
1
|
+
{"version":3,"file":"AssetHandlerRoot.js","names":[],"sources":["../../src/assethandler/AssetHandlerRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport {\n BrainCircuit,\n Boxes,\n Cpu,\n KeyRound,\n Leaf,\n Network,\n Radar,\n ShieldCheck,\n} from 'lucide-react';\n\nimport { AssetHandlerRoutes } from './AssetHandlerRoutes';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { AssetHandlerProvider } from './providers/AssetHandlerProvider';\nimport type { AssetHandlerRootProps, NavItem } from './types';\nimport { joinPath } from './utils/navigation';\n\nexport const AssetHandlerRoot: FC<AssetHandlerRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = '/',\n brandLabel = 'AssetHandler',\n terminology,\n hiddenCategoryIds,\n } = props;\n const assetsLabel = terminology?.assets || 'Assets';\n\n useEffect(() => {\n if (!registerNavItems) {\n return;\n }\n\n // Consumers can hide advanced (IT-specific) nav children by passing their\n // id with the \"assethandler-\" prefix stripped, e.g. \"digital-twin\".\n const hidden = new Set(hiddenCategoryIds ?? []);\n const isHidden = (id: string) => hidden.has(id.replace(/^assethandler-/, ''));\n\n const navItem: NavItem = {\n id: 'assethandler',\n label: brandLabel,\n path: basePath,\n icon: <Boxes size={20} />,\n order: 82,\n group: 'products',\n children: [\n {\n id: 'assethandler-dashboard',\n label: 'Dashboard',\n path: basePath,\n order: 0,\n },\n {\n id: 'assethandler-assets',\n label: assetsLabel,\n path: joinPath(basePath, 'assets/asset-list'),\n order: 1,\n },\n {\n id: 'assethandler-operations',\n label: 'Operations',\n path: joinPath(basePath, 'operations/operations-hub'),\n order: 2,\n },\n {\n id: 'assethandler-itil',\n label: 'ITIL',\n path: joinPath(basePath, 'itil/itilhub'),\n order: 3,\n },\n {\n id: 'assethandler-finance',\n label: 'Finance',\n path: joinPath(basePath, 'finance/finance-hub'),\n order: 4,\n },\n {\n id: 'assethandler-security',\n label: 'Security',\n path: joinPath(basePath, 'security/security-hub'),\n order: 7,\n },\n {\n id: 'assethandler-iot',\n label: 'IoT Devices',\n path: joinPath(basePath, 'advanced/iot-devices'),\n icon: <Cpu size={16} />,\n order: 10,\n },\n {\n id: 'assethandler-digital-twin',\n label: 'Digital Twin',\n path: joinPath(basePath, 'advanced/digital-twin'),\n icon: <Boxes size={16} />,\n order: 11,\n },\n {\n id: 'assethandler-sam',\n label: 'SAM',\n path: joinPath(basePath, 'advanced/sam-licenses'),\n icon: <KeyRound size={16} />,\n order: 12,\n },\n {\n id: 'assethandler-cmdb',\n label: 'CMDB',\n path: joinPath(basePath, 'advanced/cmdb-browser'),\n icon: <Network size={16} />,\n order: 13,\n },\n {\n id: 'assethandler-discovery',\n label: 'Discovery',\n path: joinPath(basePath, 'advanced/discovery-jobs'),\n icon: <Radar size={16} />,\n order: 14,\n },\n {\n id: 'assethandler-provenance',\n label: 'Provenance',\n path: joinPath(basePath, 'advanced/provenance'),\n icon: <ShieldCheck size={16} />,\n order: 15,\n },\n {\n id: 'assethandler-esg',\n label: 'ESG',\n path: joinPath(basePath, 'advanced/esg-dashboard'),\n icon: <Leaf size={16} />,\n order: 16,\n },\n {\n id: 'assethandler-ai',\n label: 'AI Predictions',\n path: joinPath(basePath, 'advanced/ai-predictions'),\n icon: <BrainCircuit size={16} />,\n order: 17,\n },\n ],\n };\n\n if (navItem.children) {\n navItem.children = navItem.children.filter((child) => !isHidden(child.id));\n }\n\n return registerNavItems(navItem);\n }, [basePath, registerNavItems, brandLabel, assetsLabel, hiddenCategoryIds]);\n\n useEffect(() => {\n if (!registerFooterItems) {\n return;\n }\n\n return registerFooterItems([\n {\n id: 'assethandler-status',\n label: `${brandLabel} Live`,\n type: 'status',\n section: 'left',\n order: 10,\n },\n ]);\n }, [registerFooterItems, brandLabel]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <AssetHandlerProvider {...props}>\n <AssetHandlerRoutes />\n </AssetHandlerProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetHandlerRoutes.js","names":[],"sources":["../../src/assethandler/AssetHandlerRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Navigate, Route, Routes } from 'react-router';\nimport { Loader2 } from 'lucide-react';\nimport { DashboardPage as IndexPage } from './pages/DashboardPage';\n\nconst AssetsAssetCategoriesPage = lazy(() =>\n import('./pages/AssetsAssetCategoriesPage').then((m) => ({\n default: m.AssetsAssetCategoriesPage,\n }))\n);\nconst AssetsAssetCheckInOutPage = lazy(() =>\n import('./pages/AssetsAssetCheckInOutPage').then((m) => ({\n default: m.AssetsAssetCheckInOutPage,\n }))\n);\nconst AssetsAssetCreatePage = lazy(() =>\n import('./pages/AssetsAssetCreatePage').then((m) => ({ default: m.AssetsAssetCreatePage }))\n);\nconst AssetsAssetDetailPage = lazy(() =>\n import('./pages/AssetsAssetDetailPage').then((m) => ({ default: m.AssetsAssetDetailPage }))\n);\nconst AssetsAssetListPage = lazy(() =>\n import('./pages/AssetsAssetListPage').then((m) => ({ default: m.AssetsAssetListPage }))\n);\nconst AssetsAssetTrackingPage = lazy(() =>\n import('./pages/AssetsAssetTrackingPage').then((m) => ({ default: m.AssetsAssetTrackingPage }))\n);\nconst AnalyticsPage = lazy(() =>\n import('./pages/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\nconst FinanceBudgetDetailPage = lazy(() =>\n import('./pages/FinanceBudgetDetailPage').then((m) => ({ default: m.FinanceBudgetDetailPage }))\n);\nconst FinanceFinanceHubPage = lazy(() =>\n import('./pages/FinanceFinanceHubPage').then((m) => ({ default: m.FinanceFinanceHubPage }))\n);\nconst FinanceInvoiceDetailPage = lazy(() =>\n import('./pages/FinanceInvoiceDetailPage').then((m) => ({ default: m.FinanceInvoiceDetailPage }))\n);\nconst ItilChangeDetailPage = lazy(() =>\n import('./pages/ItilChangeDetailPage').then((m) => ({ default: m.ItilChangeDetailPage }))\n);\nconst ItilChangeManagementPage = lazy(() =>\n import('./pages/ItilChangeManagementPage').then((m) => ({ default: m.ItilChangeManagementPage }))\n);\nconst ItilItilhubPage = lazy(() =>\n import('./pages/ItilItilhubPage').then((m) => ({ default: m.ItilItilhubPage }))\n);\nconst ItilIncidentDetailPage = lazy(() =>\n import('./pages/ItilIncidentDetailPage').then((m) => ({ default: m.ItilIncidentDetailPage }))\n);\nconst ItilIncidentManagementPage = lazy(() =>\n import('./pages/ItilIncidentManagementPage').then((m) => ({\n default: m.ItilIncidentManagementPage,\n }))\n);\nconst ItilProblemDetailPage = lazy(() =>\n import('./pages/ItilProblemDetailPage').then((m) => ({ default: m.ItilProblemDetailPage }))\n);\nconst ItilProblemManagementPage = lazy(() =>\n import('./pages/ItilProblemManagementPage').then((m) => ({\n default: m.ItilProblemManagementPage,\n }))\n);\nconst MaintenanceMaintenanceCalendarPage = lazy(() =>\n import('./pages/MaintenanceMaintenanceCalendarPage').then((m) => ({\n default: m.MaintenanceMaintenanceCalendarPage,\n }))\n);\nconst MaintenanceMaintenanceSchedulerPage = lazy(() =>\n import('./pages/MaintenanceMaintenanceSchedulerPage').then((m) => ({\n default: m.MaintenanceMaintenanceSchedulerPage,\n }))\n);\nconst MaintenanceMaintenancePlansPage = lazy(() =>\n import('./pages/MaintenanceMaintenancePlansPage').then((m) => ({\n default: m.MaintenanceMaintenancePlansPage,\n }))\n);\nconst OperationsMaintenanceDetailPage = lazy(() =>\n import('./pages/OperationsMaintenanceDetailPage').then((m) => ({\n default: m.OperationsMaintenanceDetailPage,\n }))\n);\nconst OperationsOperationsHubPage = lazy(() =>\n import('./pages/OperationsOperationsHubPage').then((m) => ({\n default: m.OperationsOperationsHubPage,\n }))\n);\nconst OperationsWorkOrderDetailPage = lazy(() =>\n import('./pages/OperationsWorkOrderDetailPage').then((m) => ({\n default: m.OperationsWorkOrderDetailPage,\n }))\n);\nconst PortfolioPortfolioOverviewPage = lazy(() =>\n import('./pages/PortfolioPortfolioOverviewPage').then((m) => ({\n default: m.PortfolioPortfolioOverviewPage,\n }))\n);\nconst SecurityComplianceDetailPage = lazy(() =>\n import('./pages/SecurityComplianceDetailPage').then((m) => ({\n default: m.SecurityComplianceDetailPage,\n }))\n);\nconst SecurityPolicyDetailPage = lazy(() =>\n import('./pages/SecurityPolicyDetailPage').then((m) => ({ default: m.SecurityPolicyDetailPage }))\n);\nconst SecuritySecurityEventDetailPage = lazy(() =>\n import('./pages/SecuritySecurityEventDetailPage').then((m) => ({\n default: m.SecuritySecurityEventDetailPage,\n }))\n);\nconst SecuritySecurityHubPage = lazy(() =>\n import('./pages/SecuritySecurityHubPage').then((m) => ({ default: m.SecuritySecurityHubPage }))\n);\nconst TransactionsTransactionDetailPage = lazy(() =>\n import('./pages/TransactionsTransactionDetailPage').then((m) => ({\n default: m.TransactionsTransactionDetailPage,\n }))\n);\nconst TransactionsTransactionListPage = lazy(() =>\n import('./pages/TransactionsTransactionListPage').then((m) => ({\n default: m.TransactionsTransactionListPage,\n }))\n);\nconst AiPredictionsPage = lazy(() =>\n import('./pages/AiPredictionsPage').then((m) => ({ default: m.AiPredictionsPage }))\n);\nconst CmdbBrowserPage = lazy(() =>\n import('./pages/CmdbBrowserPage').then((m) => ({ default: m.CmdbBrowserPage }))\n);\nconst DiscoveryJobsPage = lazy(() =>\n import('./pages/DiscoveryJobsPage').then((m) => ({ default: m.DiscoveryJobsPage }))\n);\nconst DigitalTwinPage = lazy(() =>\n import('./pages/DigitalTwinPage').then((m) => ({ default: m.DigitalTwinPage }))\n);\n// Re-routed (BOFF-3948): esgMetrics/upsertEsgMetric and knowledgeArticles/\n// createKnowledgeArticle are now real fields owned by wspace-assethandler-svc,\n// so these two pages resolve against live data instead of crashing on a GraphQL\n// validation error.\nconst EsgDashboardPage = lazy(() =>\n import('./pages/EsgDashboardPage').then((m) => ({ default: m.EsgDashboardPage }))\n);\nconst KnowledgeBasePage = lazy(() =>\n import('./pages/KnowledgeBasePage').then((m) => ({ default: m.KnowledgeBasePage }))\n);\nconst IotDevicesPage = lazy(() =>\n import('./pages/IotDevicesPage').then((m) => ({ default: m.IotDevicesPage }))\n);\nconst ProvenancePage = lazy(() =>\n import('./pages/ProvenancePage').then((m) => ({ default: m.ProvenancePage }))\n);\nconst SamLicensesPage = lazy(() =>\n import('./pages/SamLicensesPage').then((m) => ({ default: m.SamLicensesPage }))\n);\nconst ReportsDashboardPage = lazy(() =>\n import('./pages/ReportsDashboardPage').then((m) => ({ default: m.ReportsDashboardPage }))\n);\n\nfunction PageLoader() {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-primary\" />\n </div>\n );\n}\n\nexport function AssetHandlerRoutes() {\n return (\n <Suspense fallback={<PageLoader />}>\n <Routes>\n <Route index element={<IndexPage />} />\n <Route path=\"assets/asset-categories\" element={<AssetsAssetCategoriesPage />} />\n <Route path=\"assets/asset-check-in-out\" element={<AssetsAssetCheckInOutPage />} />\n <Route path=\"assets/asset-create\" element={<AssetsAssetCreatePage />} />\n <Route path=\"assets/:assetId\" element={<AssetsAssetDetailPage />} />\n <Route path=\"assets/asset-list\" element={<AssetsAssetListPage />} />\n <Route path=\"assets/asset-tracking\" element={<AssetsAssetTrackingPage />} />\n <Route path=\"analytics\" element={<AnalyticsPage />} />\n <Route path=\"finance/budgets/:budgetId\" element={<FinanceBudgetDetailPage />} />\n <Route path=\"finance/finance-hub\" element={<FinanceFinanceHubPage />} />\n <Route path=\"finance/invoices/:invoiceId\" element={<FinanceInvoiceDetailPage />} />\n <Route path=\"itil/changes/:changeId\" element={<ItilChangeDetailPage />} />\n <Route path=\"itil/change-management\" element={<ItilChangeManagementPage />} />\n <Route path=\"itil/itilhub\" element={<ItilItilhubPage />} />\n <Route path=\"itil/incidents/:incidentId\" element={<ItilIncidentDetailPage />} />\n <Route path=\"itil/incident-management\" element={<ItilIncidentManagementPage />} />\n <Route path=\"itil/problems/:problemId\" element={<ItilProblemDetailPage />} />\n <Route path=\"itil/problem-management\" element={<ItilProblemManagementPage />} />\n <Route\n path=\"maintenance/maintenance-calendar\"\n element={<MaintenanceMaintenanceCalendarPage />}\n />\n <Route\n path=\"maintenance/maintenance-scheduler\"\n element={<MaintenanceMaintenanceSchedulerPage />}\n />\n <Route path=\"maintenance/maintenance-plans\" element={<MaintenanceMaintenancePlansPage />} />\n <Route\n path=\"operations/maintenance/:maintenanceId\"\n element={<OperationsMaintenanceDetailPage />}\n />\n <Route path=\"operations/operations-hub\" element={<OperationsOperationsHubPage />} />\n <Route\n path=\"operations/work-orders/:workOrderId\"\n element={<OperationsWorkOrderDetailPage />}\n />\n <Route path=\"portfolio/portfolio-overview\" element={<PortfolioPortfolioOverviewPage />} />\n <Route path=\"reports\" element={<Navigate to=\"reports-dashboard\" replace />} />\n <Route path=\"reports/reports-dashboard\" element={<ReportsDashboardPage />} />\n <Route path=\"security/compliance/:frameworkId\" element={<SecurityComplianceDetailPage />} />\n <Route path=\"security/policies/:policyId\" element={<SecurityPolicyDetailPage />} />\n <Route path=\"security/events/:eventId\" element={<SecuritySecurityEventDetailPage />} />\n <Route path=\"security/security-hub\" element={<SecuritySecurityHubPage />} />\n <Route path=\"transactions/:transactionId\" element={<TransactionsTransactionDetailPage />} />\n <Route path=\"transactions/transaction-list\" element={<TransactionsTransactionListPage />} />\n <Route path=\"advanced/iot-devices\" element={<IotDevicesPage />} />\n <Route path=\"advanced/digital-twin\" element={<DigitalTwinPage />} />\n <Route path=\"advanced/sam-licenses\" element={<SamLicensesPage />} />\n <Route path=\"advanced/cmdb-browser\" element={<CmdbBrowserPage />} />\n <Route path=\"advanced/discovery-jobs\" element={<DiscoveryJobsPage />} />\n <Route path=\"advanced/knowledge-base\" element={<KnowledgeBasePage />} />\n <Route path=\"advanced/esg-dashboard\" element={<EsgDashboardPage />} />\n <Route path=\"advanced/provenance\" element={<ProvenancePage />} />\n <Route path=\"advanced/ai-predictions\" element={<AiPredictionsPage />} />\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AssetHandlerRoutes.js","names":[],"sources":["../../src/assethandler/AssetHandlerRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Navigate, Route, Routes } from 'react-router';\nimport { Loader2 } from 'lucide-react';\nimport { DashboardPage as IndexPage } from './pages/DashboardPage';\n\nconst AssetsAssetCategoriesPage = lazy(() =>\n import('./pages/AssetsAssetCategoriesPage').then((m) => ({\n default: m.AssetsAssetCategoriesPage,\n }))\n);\nconst AssetsAssetCheckInOutPage = lazy(() =>\n import('./pages/AssetsAssetCheckInOutPage').then((m) => ({\n default: m.AssetsAssetCheckInOutPage,\n }))\n);\nconst AssetsAssetCreatePage = lazy(() =>\n import('./pages/AssetsAssetCreatePage').then((m) => ({ default: m.AssetsAssetCreatePage }))\n);\nconst AssetsAssetDetailPage = lazy(() =>\n import('./pages/AssetsAssetDetailPage').then((m) => ({ default: m.AssetsAssetDetailPage }))\n);\nconst AssetsAssetListPage = lazy(() =>\n import('./pages/AssetsAssetListPage').then((m) => ({ default: m.AssetsAssetListPage }))\n);\nconst AssetsAssetTrackingPage = lazy(() =>\n import('./pages/AssetsAssetTrackingPage').then((m) => ({ default: m.AssetsAssetTrackingPage }))\n);\nconst AnalyticsPage = lazy(() =>\n import('./pages/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\nconst FinanceBudgetDetailPage = lazy(() =>\n import('./pages/FinanceBudgetDetailPage').then((m) => ({ default: m.FinanceBudgetDetailPage }))\n);\nconst FinanceFinanceHubPage = lazy(() =>\n import('./pages/FinanceFinanceHubPage').then((m) => ({ default: m.FinanceFinanceHubPage }))\n);\nconst FinanceInvoiceDetailPage = lazy(() =>\n import('./pages/FinanceInvoiceDetailPage').then((m) => ({ default: m.FinanceInvoiceDetailPage }))\n);\nconst ItilChangeDetailPage = lazy(() =>\n import('./pages/ItilChangeDetailPage').then((m) => ({ default: m.ItilChangeDetailPage }))\n);\nconst ItilChangeManagementPage = lazy(() =>\n import('./pages/ItilChangeManagementPage').then((m) => ({ default: m.ItilChangeManagementPage }))\n);\nconst ItilItilhubPage = lazy(() =>\n import('./pages/ItilItilhubPage').then((m) => ({ default: m.ItilItilhubPage }))\n);\nconst ItilIncidentDetailPage = lazy(() =>\n import('./pages/ItilIncidentDetailPage').then((m) => ({ default: m.ItilIncidentDetailPage }))\n);\nconst ItilIncidentManagementPage = lazy(() =>\n import('./pages/ItilIncidentManagementPage').then((m) => ({\n default: m.ItilIncidentManagementPage,\n }))\n);\nconst ItilProblemDetailPage = lazy(() =>\n import('./pages/ItilProblemDetailPage').then((m) => ({ default: m.ItilProblemDetailPage }))\n);\nconst ItilProblemManagementPage = lazy(() =>\n import('./pages/ItilProblemManagementPage').then((m) => ({\n default: m.ItilProblemManagementPage,\n }))\n);\nconst MaintenanceMaintenanceCalendarPage = lazy(() =>\n import('./pages/MaintenanceMaintenanceCalendarPage').then((m) => ({\n default: m.MaintenanceMaintenanceCalendarPage,\n }))\n);\nconst MaintenanceMaintenanceSchedulerPage = lazy(() =>\n import('./pages/MaintenanceMaintenanceSchedulerPage').then((m) => ({\n default: m.MaintenanceMaintenanceSchedulerPage,\n }))\n);\nconst MaintenanceMaintenancePlansPage = lazy(() =>\n import('./pages/MaintenanceMaintenancePlansPage').then((m) => ({\n default: m.MaintenanceMaintenancePlansPage,\n }))\n);\nconst OperationsMaintenanceDetailPage = lazy(() =>\n import('./pages/OperationsMaintenanceDetailPage').then((m) => ({\n default: m.OperationsMaintenanceDetailPage,\n }))\n);\nconst OperationsOperationsHubPage = lazy(() =>\n import('./pages/OperationsOperationsHubPage').then((m) => ({\n default: m.OperationsOperationsHubPage,\n }))\n);\nconst OperationsWorkOrderDetailPage = lazy(() =>\n import('./pages/OperationsWorkOrderDetailPage').then((m) => ({\n default: m.OperationsWorkOrderDetailPage,\n }))\n);\nconst PortfolioPortfolioOverviewPage = lazy(() =>\n import('./pages/PortfolioPortfolioOverviewPage').then((m) => ({\n default: m.PortfolioPortfolioOverviewPage,\n }))\n);\nconst SecurityComplianceDetailPage = lazy(() =>\n import('./pages/SecurityComplianceDetailPage').then((m) => ({\n default: m.SecurityComplianceDetailPage,\n }))\n);\nconst SecurityPolicyDetailPage = lazy(() =>\n import('./pages/SecurityPolicyDetailPage').then((m) => ({ default: m.SecurityPolicyDetailPage }))\n);\nconst SecuritySecurityEventDetailPage = lazy(() =>\n import('./pages/SecuritySecurityEventDetailPage').then((m) => ({\n default: m.SecuritySecurityEventDetailPage,\n }))\n);\nconst SecuritySecurityHubPage = lazy(() =>\n import('./pages/SecuritySecurityHubPage').then((m) => ({ default: m.SecuritySecurityHubPage }))\n);\nconst TransactionsTransactionDetailPage = lazy(() =>\n import('./pages/TransactionsTransactionDetailPage').then((m) => ({\n default: m.TransactionsTransactionDetailPage,\n }))\n);\nconst TransactionsTransactionListPage = lazy(() =>\n import('./pages/TransactionsTransactionListPage').then((m) => ({\n default: m.TransactionsTransactionListPage,\n }))\n);\nconst AiPredictionsPage = lazy(() =>\n import('./pages/AiPredictionsPage').then((m) => ({ default: m.AiPredictionsPage }))\n);\nconst CmdbBrowserPage = lazy(() =>\n import('./pages/CmdbBrowserPage').then((m) => ({ default: m.CmdbBrowserPage }))\n);\nconst DiscoveryJobsPage = lazy(() =>\n import('./pages/DiscoveryJobsPage').then((m) => ({ default: m.DiscoveryJobsPage }))\n);\nconst DigitalTwinPage = lazy(() =>\n import('./pages/DigitalTwinPage').then((m) => ({ default: m.DigitalTwinPage }))\n);\n// Re-routed (BOFF-3948): esgMetrics/upsertEsgMetric and knowledgeArticles/\n// createKnowledgeArticle are now real fields owned by wspace-assethandler-svc,\n// so these two pages resolve against live data instead of crashing on a GraphQL\n// validation error.\nconst EsgDashboardPage = lazy(() =>\n import('./pages/EsgDashboardPage').then((m) => ({ default: m.EsgDashboardPage }))\n);\nconst KnowledgeBasePage = lazy(() =>\n import('./pages/KnowledgeBasePage').then((m) => ({ default: m.KnowledgeBasePage }))\n);\nconst IotDevicesPage = lazy(() =>\n import('./pages/IotDevicesPage').then((m) => ({ default: m.IotDevicesPage }))\n);\nconst ProvenancePage = lazy(() =>\n import('./pages/ProvenancePage').then((m) => ({ default: m.ProvenancePage }))\n);\nconst SamLicensesPage = lazy(() =>\n import('./pages/SamLicensesPage').then((m) => ({ default: m.SamLicensesPage }))\n);\nconst ReportsDashboardPage = lazy(() =>\n import('./pages/ReportsDashboardPage').then((m) => ({ default: m.ReportsDashboardPage }))\n);\n\nfunction PageLoader() {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-primary\" />\n </div>\n );\n}\n\nexport function AssetHandlerRoutes() {\n return (\n <Suspense fallback={<PageLoader />}>\n <Routes>\n <Route index element={<IndexPage />} />\n <Route path=\"assets/asset-categories\" element={<AssetsAssetCategoriesPage />} />\n <Route path=\"assets/asset-check-in-out\" element={<AssetsAssetCheckInOutPage />} />\n <Route path=\"assets/asset-create\" element={<AssetsAssetCreatePage />} />\n <Route path=\"assets/:assetId\" element={<AssetsAssetDetailPage />} />\n <Route path=\"assets/asset-list\" element={<AssetsAssetListPage />} />\n <Route path=\"assets/asset-tracking\" element={<AssetsAssetTrackingPage />} />\n <Route path=\"analytics\" element={<AnalyticsPage />} />\n <Route path=\"finance/budgets/:budgetId\" element={<FinanceBudgetDetailPage />} />\n <Route path=\"finance/finance-hub\" element={<FinanceFinanceHubPage />} />\n <Route path=\"finance/invoices/:invoiceId\" element={<FinanceInvoiceDetailPage />} />\n <Route path=\"itil/changes/:changeId\" element={<ItilChangeDetailPage />} />\n <Route path=\"itil/change-management\" element={<ItilChangeManagementPage />} />\n <Route path=\"itil/itilhub\" element={<ItilItilhubPage />} />\n <Route path=\"itil/incidents/:incidentId\" element={<ItilIncidentDetailPage />} />\n <Route path=\"itil/incident-management\" element={<ItilIncidentManagementPage />} />\n <Route path=\"itil/problems/:problemId\" element={<ItilProblemDetailPage />} />\n <Route path=\"itil/problem-management\" element={<ItilProblemManagementPage />} />\n <Route\n path=\"maintenance/maintenance-calendar\"\n element={<MaintenanceMaintenanceCalendarPage />}\n />\n <Route\n path=\"maintenance/maintenance-scheduler\"\n element={<MaintenanceMaintenanceSchedulerPage />}\n />\n <Route path=\"maintenance/maintenance-plans\" element={<MaintenanceMaintenancePlansPage />} />\n <Route\n path=\"operations/maintenance/:maintenanceId\"\n element={<OperationsMaintenanceDetailPage />}\n />\n <Route path=\"operations/operations-hub\" element={<OperationsOperationsHubPage />} />\n <Route\n path=\"operations/work-orders/:workOrderId\"\n element={<OperationsWorkOrderDetailPage />}\n />\n <Route path=\"portfolio/portfolio-overview\" element={<PortfolioPortfolioOverviewPage />} />\n <Route path=\"reports\" element={<Navigate to=\"reports-dashboard\" replace />} />\n <Route path=\"reports/reports-dashboard\" element={<ReportsDashboardPage />} />\n <Route path=\"security/compliance/:frameworkId\" element={<SecurityComplianceDetailPage />} />\n <Route path=\"security/policies/:policyId\" element={<SecurityPolicyDetailPage />} />\n <Route path=\"security/events/:eventId\" element={<SecuritySecurityEventDetailPage />} />\n <Route path=\"security/security-hub\" element={<SecuritySecurityHubPage />} />\n <Route path=\"transactions/:transactionId\" element={<TransactionsTransactionDetailPage />} />\n <Route path=\"transactions/transaction-list\" element={<TransactionsTransactionListPage />} />\n <Route path=\"advanced/iot-devices\" element={<IotDevicesPage />} />\n <Route path=\"advanced/digital-twin\" element={<DigitalTwinPage />} />\n <Route path=\"advanced/sam-licenses\" element={<SamLicensesPage />} />\n <Route path=\"advanced/cmdb-browser\" element={<CmdbBrowserPage />} />\n <Route path=\"advanced/discovery-jobs\" element={<DiscoveryJobsPage />} />\n <Route path=\"advanced/knowledge-base\" element={<KnowledgeBasePage />} />\n <Route path=\"advanced/esg-dashboard\" element={<EsgDashboardPage />} />\n <Route path=\"advanced/provenance\" element={<ProvenancePage />} />\n <Route path=\"advanced/ai-predictions\" element={<AiPredictionsPage />} />\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActivityTimeline.js","names":[],"sources":["../../../src/assethandler/components/ActivityTimeline.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Activity,\n Plus,\n Pencil,\n Trash2,\n CheckCircle2,\n LogOut,\n LogIn,\n RefreshCw,\n Paperclip,\n MessageSquare,\n Wrench,\n ShieldAlert,\n} from 'lucide-react';\nimport { useAssetHandler } from '../providers/AssetHandlerProvider';\nimport { Sparkline } from './charts';\n\n/**\n * Recent activity for a single AssetHandler resource, sourced from the\n * federated wspace-activity-svc `getActivities` query (fed by be-sdk\n * `trackOperation` on the backend). Read-only, with a graceful empty state.\n *\n * The header carries an activity-volume sparkline bucketed from the real\n * timestamps — it only renders when there is enough history to be meaningful,\n * never fabricated.\n */\nexport interface ActivityTimelineProps {\n resourceId: string;\n /** Optional cap on rows shown. Defaults to 20. */\n limit?: number;\n}\n\ninterface ActivityLogNode {\n id: string;\n action: string;\n description?: string | null;\n timestamp: string;\n userId: string;\n resourceType?: string | null;\n}\n\ninterface GetActivitiesResult {\n getActivities: ActivityLogNode[];\n}\n\nconst GET_ACTIVITIES_QUERY = gql`\n query AssetHandlerResourceActivities($filter: ActivityLogFilter) {\n getActivities(filter: $filter) {\n id\n action\n description\n timestamp\n userId\n resourceType\n }\n }\n`;\n\n/** Compact relative time (\"2 hrs ago\"), with the full timestamp on hover. */\nfunction relativeTime(iso: string): string {\n const then = new Date(iso).getTime();\n if (Number.isNaN(then)) return '';\n const diff = Date.now() - then;\n const sec = Math.round(diff / 1000);\n if (sec < 45) return 'just now';\n const min = Math.round(sec / 60);\n if (min < 60) return `${min} min ago`;\n const hr = Math.round(min / 60);\n if (hr < 24) return `${hr} hr${hr === 1 ? '' : 's'} ago`;\n const day = Math.round(hr / 24);\n if (day < 30) return `${day} day${day === 1 ? '' : 's'} ago`;\n return new Date(iso).toLocaleDateString();\n}\n\ntype Tone = 'success' | 'info' | 'warning' | 'error' | 'neutral';\n\nconst TONE_CLASS: Record<Tone, { icon: string; tile: string }> = {\n success: { icon: 'text-status-success-text', tile: 'bg-status-success-bg-subtle' },\n info: { icon: 'text-status-info-text', tile: 'bg-status-info-bg-subtle' },\n warning: { icon: 'text-status-warning-text', tile: 'bg-status-warning-bg-subtle' },\n error: { icon: 'text-status-error-text', tile: 'bg-status-error-bg-subtle' },\n neutral: { icon: 'text-text-secondary', tile: 'bg-bg-sunken' },\n};\n\n/** Map an activity action verb to a node icon + status tone. */\nfunction actionVisual(action: string): { Icon: typeof Activity; tone: Tone } {\n const a = action.toLowerCase();\n if (/(complete|resolve|close|done|approve|pass|success)/.test(a))\n return { Icon: CheckCircle2, tone: 'success' };\n if (/(delete|remove|dispose|retire|archive|reject|fail)/.test(a))\n return { Icon: Trash2, tone: 'error' };\n if (/(check[_.\\s]?out|assign|issue|lend)/.test(a)) return { Icon: LogOut, tone: 'warning' };\n if (/(check[_.\\s]?in|return|receive)/.test(a)) return { Icon: LogIn, tone: 'success' };\n if (/(maintenance|repair|service|calibrat)/.test(a)) return { Icon: Wrench, tone: 'warning' };\n if (/(incident|breach|alert|violation|risk|security)/.test(a))\n return { Icon: ShieldAlert, tone: 'error' };\n if (/(upload|attach|file)/.test(a)) return { Icon: Paperclip, tone: 'info' };\n if (/(comment|note|reply|discuss)/.test(a)) return { Icon: MessageSquare, tone: 'info' };\n if (/(create|add|new|register|provision)/.test(a)) return { Icon: Plus, tone: 'success' };\n if (/(update|edit|change|modify|revise|status)/.test(a)) return { Icon: Pencil, tone: 'info' };\n if (/(sync|refresh|move|transfer|relocat)/.test(a)) return { Icon: RefreshCw, tone: 'info' };\n return { Icon: Activity, tone: 'neutral' };\n}\n\nfunction titleize(action: string): string {\n const s = action.replace(/[._]/g, ' ').trim();\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nexport function ActivityTimeline({ resourceId, limit = 20 }: ActivityTimelineProps) {\n const { workspaceId, currentUser } = useAssetHandler();\n\n const { data, loading } = useQuery<GetActivitiesResult>(GET_ACTIVITIES_QUERY, {\n variables: {\n filter: {\n resourceId,\n resourceType: 'asset',\n productId: 'assethandler',\n ...(workspaceId ? { workspaceId } : {}),\n limit,\n },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n skip: !resourceId,\n });\n\n const activities = useMemo(() => data?.getActivities ?? [], [data]);\n\n // Bucket real timestamps into per-day volume for a header sparkline. Only\n // meaningful when there is enough history spanning multiple days — otherwise\n // we omit it rather than draw a flat/fabricated line.\n const volumeSeries = useMemo(() => {\n if (activities.length < 6) return null;\n const DAYS = 14;\n const now = new Date();\n const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();\n const buckets = new Array<number>(DAYS).fill(0);\n let distinctDays = 0;\n const seen = new Set<number>();\n for (const item of activities) {\n const t = new Date(item.timestamp).getTime();\n if (Number.isNaN(t)) continue;\n const dayIndex = DAYS - 1 - Math.floor((startOfToday - t) / 86_400_000);\n if (dayIndex >= 0 && dayIndex < DAYS) {\n buckets[dayIndex] += 1;\n const dayKey = Math.floor(t / 86_400_000);\n if (!seen.has(dayKey)) {\n seen.add(dayKey);\n distinctDays += 1;\n }\n }\n }\n return distinctDays >= 3 ? buckets : null;\n }, [activities]);\n\n const actorLabel = (userId: string): string =>\n currentUser?.id && userId === currentUser.id\n ? currentUser.name ||\n [currentUser.firstName, currentUser.lastName].filter(Boolean).join(' ') ||\n currentUser.username ||\n currentUser.email?.split('@')[0] ||\n 'You'\n : userId === 'system' || userId === 'SYSTEM'\n ? 'System'\n : `Member ${userId.slice(0, 6)}`;\n\n return (\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-3 space-y-0\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Activity className=\"h-4 w-4\" />\n Activity\n </CardTitle>\n {volumeSeries ? (\n <div className=\"flex items-center gap-2\" title=\"Activity over the last 14 days\">\n <span className=\"hidden text-xs text-text-secondary sm:inline\">Last 14 days</span>\n <Sparkline\n data={volumeSeries}\n type=\"area\"\n height={28}\n className=\"w-24\"\n ariaLabel=\"Activity volume over the last 14 days\"\n />\n </div>\n ) : null}\n </CardHeader>\n <CardContent>\n {loading && activities.length === 0 ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={i}\n className=\"h-12 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : activities.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"No activity yet\"\n description=\"Changes to this record — updates, status changes and assignments — will show up here.\"\n />\n ) : (\n <ol className=\"space-y-1\">\n {activities.map((item, index) => {\n const { Icon, tone } = actionVisual(item.action);\n const tc = TONE_CLASS[tone];\n const isLast = index === activities.length - 1;\n return (\n <li key={item.id} className=\"relative flex gap-3 pb-3\">\n {/* Connector line between nodes. */}\n {!isLast ? (\n <span\n className=\"absolute left-[0.9375rem] top-8 h-[calc(100%-1rem)] w-px bg-border-subtle\"\n aria-hidden=\"true\"\n />\n ) : null}\n <span\n className={`relative z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full ${tc.tile}`}\n aria-hidden=\"true\"\n >\n <Icon className={`h-4 w-4 ${tc.icon}`} />\n </span>\n <div className=\"min-w-0 flex-1 pt-0.5\">\n <p className=\"text-sm font-medium text-text-primary\">{titleize(item.action)}</p>\n {item.description ? (\n <p className=\"truncate text-sm text-text-secondary\" title={item.description}>\n {item.description}\n </p>\n ) : null}\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n <span className=\"font-medium text-text-secondary\">\n {actorLabel(item.userId)}\n </span>{' '}\n ·{' '}\n <span title={new Date(item.timestamp).toLocaleString()}>\n {relativeTime(item.timestamp)}\n </span>\n </p>\n </div>\n </li>\n );\n })}\n </ol>\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ActivityTimeline.js","names":[],"sources":["../../../src/assethandler/components/ActivityTimeline.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Activity,\n Plus,\n Pencil,\n Trash2,\n CheckCircle2,\n LogOut,\n LogIn,\n RefreshCw,\n Paperclip,\n MessageSquare,\n Wrench,\n ShieldAlert,\n} from 'lucide-react';\nimport { useAssetHandler } from '../providers/AssetHandlerProvider';\nimport { Sparkline } from './charts';\n\n/**\n * Recent activity for a single AssetHandler resource, sourced from the\n * federated wspace-activity-svc `getActivities` query (fed by be-sdk\n * `trackOperation` on the backend). Read-only, with a graceful empty state.\n *\n * The header carries an activity-volume sparkline bucketed from the real\n * timestamps — it only renders when there is enough history to be meaningful,\n * never fabricated.\n */\nexport interface ActivityTimelineProps {\n resourceId: string;\n /** Optional cap on rows shown. Defaults to 20. */\n limit?: number;\n}\n\ninterface ActivityLogNode {\n id: string;\n action: string;\n description?: string | null;\n timestamp: string;\n userId: string;\n resourceType?: string | null;\n}\n\ninterface GetActivitiesResult {\n getActivities: ActivityLogNode[];\n}\n\nconst GET_ACTIVITIES_QUERY = gql`\n query AssetHandlerResourceActivities($filter: ActivityLogFilter) {\n getActivities(filter: $filter) {\n id\n action\n description\n timestamp\n userId\n resourceType\n }\n }\n`;\n\n/** Compact relative time (\"2 hrs ago\"), with the full timestamp on hover. */\nfunction relativeTime(iso: string): string {\n const then = new Date(iso).getTime();\n if (Number.isNaN(then)) return '';\n const diff = Date.now() - then;\n const sec = Math.round(diff / 1000);\n if (sec < 45) return 'just now';\n const min = Math.round(sec / 60);\n if (min < 60) return `${min} min ago`;\n const hr = Math.round(min / 60);\n if (hr < 24) return `${hr} hr${hr === 1 ? '' : 's'} ago`;\n const day = Math.round(hr / 24);\n if (day < 30) return `${day} day${day === 1 ? '' : 's'} ago`;\n return new Date(iso).toLocaleDateString();\n}\n\ntype Tone = 'success' | 'info' | 'warning' | 'error' | 'neutral';\n\nconst TONE_CLASS: Record<Tone, { icon: string; tile: string }> = {\n success: { icon: 'text-status-success-text', tile: 'bg-status-success-bg-subtle' },\n info: { icon: 'text-status-info-text', tile: 'bg-status-info-bg-subtle' },\n warning: { icon: 'text-status-warning-text', tile: 'bg-status-warning-bg-subtle' },\n error: { icon: 'text-status-error-text', tile: 'bg-status-error-bg-subtle' },\n neutral: { icon: 'text-text-secondary', tile: 'bg-bg-sunken' },\n};\n\n/** Map an activity action verb to a node icon + status tone. */\nfunction actionVisual(action: string): { Icon: typeof Activity; tone: Tone } {\n const a = action.toLowerCase();\n if (/(complete|resolve|close|done|approve|pass|success)/.test(a))\n return { Icon: CheckCircle2, tone: 'success' };\n if (/(delete|remove|dispose|retire|archive|reject|fail)/.test(a))\n return { Icon: Trash2, tone: 'error' };\n if (/(check[_.\\s]?out|assign|issue|lend)/.test(a)) return { Icon: LogOut, tone: 'warning' };\n if (/(check[_.\\s]?in|return|receive)/.test(a)) return { Icon: LogIn, tone: 'success' };\n if (/(maintenance|repair|service|calibrat)/.test(a)) return { Icon: Wrench, tone: 'warning' };\n if (/(incident|breach|alert|violation|risk|security)/.test(a))\n return { Icon: ShieldAlert, tone: 'error' };\n if (/(upload|attach|file)/.test(a)) return { Icon: Paperclip, tone: 'info' };\n if (/(comment|note|reply|discuss)/.test(a)) return { Icon: MessageSquare, tone: 'info' };\n if (/(create|add|new|register|provision)/.test(a)) return { Icon: Plus, tone: 'success' };\n if (/(update|edit|change|modify|revise|status)/.test(a)) return { Icon: Pencil, tone: 'info' };\n if (/(sync|refresh|move|transfer|relocat)/.test(a)) return { Icon: RefreshCw, tone: 'info' };\n return { Icon: Activity, tone: 'neutral' };\n}\n\nfunction titleize(action: string): string {\n const s = action.replace(/[._]/g, ' ').trim();\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nexport function ActivityTimeline({ resourceId, limit = 20 }: ActivityTimelineProps) {\n const { workspaceId, currentUser } = useAssetHandler();\n\n const { data, loading } = useQuery<GetActivitiesResult>(GET_ACTIVITIES_QUERY, {\n variables: {\n filter: {\n resourceId,\n resourceType: 'asset',\n productId: 'assethandler',\n ...(workspaceId ? { workspaceId } : {}),\n limit,\n },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n skip: !resourceId,\n });\n\n const activities = useMemo(() => data?.getActivities ?? [], [data]);\n\n // Bucket real timestamps into per-day volume for a header sparkline. Only\n // meaningful when there is enough history spanning multiple days — otherwise\n // we omit it rather than draw a flat/fabricated line.\n const volumeSeries = useMemo(() => {\n if (activities.length < 6) return null;\n const DAYS = 14;\n const now = new Date();\n const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();\n const buckets = new Array<number>(DAYS).fill(0);\n let distinctDays = 0;\n const seen = new Set<number>();\n for (const item of activities) {\n const t = new Date(item.timestamp).getTime();\n if (Number.isNaN(t)) continue;\n const dayIndex = DAYS - 1 - Math.floor((startOfToday - t) / 86_400_000);\n if (dayIndex >= 0 && dayIndex < DAYS) {\n buckets[dayIndex] += 1;\n const dayKey = Math.floor(t / 86_400_000);\n if (!seen.has(dayKey)) {\n seen.add(dayKey);\n distinctDays += 1;\n }\n }\n }\n return distinctDays >= 3 ? buckets : null;\n }, [activities]);\n\n const actorLabel = (userId: string): string =>\n currentUser?.id && userId === currentUser.id\n ? currentUser.name ||\n [currentUser.firstName, currentUser.lastName].filter(Boolean).join(' ') ||\n currentUser.username ||\n currentUser.email?.split('@')[0] ||\n 'You'\n : userId === 'system' || userId === 'SYSTEM'\n ? 'System'\n : `Member ${userId.slice(0, 6)}`;\n\n return (\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-3 space-y-0\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Activity className=\"h-4 w-4\" />\n Activity\n </CardTitle>\n {volumeSeries ? (\n <div className=\"flex items-center gap-2\" title=\"Activity over the last 14 days\">\n <span className=\"hidden text-xs text-text-secondary sm:inline\">Last 14 days</span>\n <Sparkline\n data={volumeSeries}\n type=\"area\"\n height={28}\n className=\"w-24\"\n ariaLabel=\"Activity volume over the last 14 days\"\n />\n </div>\n ) : null}\n </CardHeader>\n <CardContent>\n {loading && activities.length === 0 ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={i}\n className=\"h-12 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : activities.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"No activity yet\"\n description=\"Changes to this record — updates, status changes and assignments — will show up here.\"\n />\n ) : (\n <ol className=\"space-y-1\">\n {activities.map((item, index) => {\n const { Icon, tone } = actionVisual(item.action);\n const tc = TONE_CLASS[tone];\n const isLast = index === activities.length - 1;\n return (\n <li key={item.id} className=\"relative flex gap-3 pb-3\">\n {/* Connector line between nodes. */}\n {!isLast ? (\n <span\n className=\"absolute left-[0.9375rem] top-8 h-[calc(100%-1rem)] w-px bg-border-subtle\"\n aria-hidden=\"true\"\n />\n ) : null}\n <span\n className={`relative z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full ${tc.tile}`}\n aria-hidden=\"true\"\n >\n <Icon className={`h-4 w-4 ${tc.icon}`} />\n </span>\n <div className=\"min-w-0 flex-1 pt-0.5\">\n <p className=\"text-sm font-medium text-text-primary\">{titleize(item.action)}</p>\n {item.description ? (\n <p className=\"truncate text-sm text-text-secondary\" title={item.description}>\n {item.description}\n </p>\n ) : null}\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n <span className=\"font-medium text-text-secondary\">\n {actorLabel(item.userId)}\n </span>{' '}\n ·{' '}\n <span title={new Date(item.timestamp).toLocaleString()}>\n {relativeTime(item.timestamp)}\n </span>\n </p>\n </div>\n </li>\n );\n })}\n </ol>\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AttachmentsPanel.js","names":[],"sources":["../../../src/assethandler/components/AttachmentsPanel.tsx"],"sourcesContent":["import { useMemo, useRef, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n IllustratedEmptyState,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Paperclip,\n Upload,\n Link2,\n FileText,\n FileSpreadsheet,\n FileArchive,\n FileCode2,\n FileVideo,\n FileAudio,\n Image as ImageIcon,\n File as FileIcon,\n Loader2,\n Download,\n MoreHorizontal,\n} from 'lucide-react';\nimport { notify } from '../utils/toast';\n\n/**\n * Attach and reference files for an AssetHandler entity, backed by the\n * federated wspace-files-svc (same wspace gateway/Apollo client). Fully real (no fabricated data) —\n * lists workspace files via `getFiles`, uploads new files through the\n * `initiateUpload` → presigned PUT → `completeUpload` flow, and mints public\n * share links via `createFileShareLink`. All entries are live data.\n *\n * `entityType`/`entityId` scope the panel to the record it is mounted on and\n * are used for context labelling; uploaded files land in the workspace file\n * store so they can be shared and reused.\n */\nexport interface AttachmentsPanelProps {\n entityType: string;\n entityId: string;\n}\n\ninterface FileNode {\n id: string;\n name: string;\n extension: string;\n mimeType: string;\n size: number;\n downloadUrl?: string | null;\n createdAt: string;\n}\n\ninterface GetFilesResult {\n getFiles: {\n files: FileNode[];\n total: number;\n hasMore: boolean;\n };\n}\n\nconst GET_FILES_QUERY = gql`\n query AssetHandlerGetFiles($pagination: PaginationInput, $sort: FileSortInput) {\n getFiles(pagination: $pagination, sort: $sort) {\n total\n hasMore\n files {\n id\n name\n extension\n mimeType\n size\n downloadUrl\n createdAt\n }\n }\n }\n`;\n\nconst INITIATE_UPLOAD_MUTATION = gql`\n mutation AssetHandlerInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session {\n id\n }\n }\n }\n`;\n\nconst COMPLETE_UPLOAD_MUTATION = gql`\n mutation AssetHandlerCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n fileId\n file {\n id\n name\n }\n }\n }\n`;\n\nconst CREATE_SHARE_LINK_MUTATION = gql`\n mutation AssetHandlerCreateFileShareLink($input: CreateShareGrantLinkInput!) {\n createFileShareLink(input: $input) {\n id\n shareUrl\n }\n }\n`;\n\nfunction formatSize(bytes: number): string {\n if (!bytes) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB'];\n const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\n const value = bytes / Math.pow(1024, exponent);\n return `${value.toFixed(exponent === 0 ? 0 : 1)} ${units[exponent]}`;\n}\n\nfunction extensionOf(fileName: string): string {\n const dot = fileName.lastIndexOf('.');\n return dot >= 0 ? fileName.slice(dot + 1).toLowerCase() : 'bin';\n}\n\n/** Compact relative time (\"2 hrs ago\"), with the full timestamp on hover. */\nfunction relativeTime(iso: string): string {\n const then = new Date(iso).getTime();\n if (Number.isNaN(then)) return '';\n const diff = Date.now() - then;\n const sec = Math.round(diff / 1000);\n if (sec < 45) return 'just now';\n const min = Math.round(sec / 60);\n if (min < 60) return `${min} min ago`;\n const hr = Math.round(min / 60);\n if (hr < 24) return `${hr} hr${hr === 1 ? '' : 's'} ago`;\n const day = Math.round(hr / 24);\n if (day < 30) return `${day} day${day === 1 ? '' : 's'} ago`;\n return new Date(iso).toLocaleDateString();\n}\n\ntype FileKind =\n 'image' | 'pdf' | 'sheet' | 'doc' | 'archive' | 'code' | 'video' | 'audio' | 'other';\n\n/** Map a file's extension/mime to a recognisable kind for icon + tint. */\nfunction kindOf(file: FileNode): FileKind {\n const mime = (file.mimeType || '').toLowerCase();\n const ext = (file.extension || extensionOf(file.name)).toLowerCase();\n if (\n mime.startsWith('image/') ||\n ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'avif', 'bmp'].includes(ext)\n )\n return 'image';\n if (mime.startsWith('video/') || ['mp4', 'mov', 'webm', 'avi', 'mkv'].includes(ext))\n return 'video';\n if (mime.startsWith('audio/') || ['mp3', 'wav', 'ogg', 'm4a', 'flac'].includes(ext))\n return 'audio';\n if (mime === 'application/pdf' || ext === 'pdf') return 'pdf';\n if (['xls', 'xlsx', 'csv', 'tsv', 'ods'].includes(ext)) return 'sheet';\n if (['zip', 'rar', '7z', 'tar', 'gz', 'bz2'].includes(ext)) return 'archive';\n if (\n [\n 'js',\n 'ts',\n 'tsx',\n 'jsx',\n 'json',\n 'xml',\n 'yml',\n 'yaml',\n 'html',\n 'css',\n 'sh',\n 'py',\n 'sql',\n ].includes(ext)\n )\n return 'code';\n if (['doc', 'docx', 'txt', 'md', 'rtf', 'odt', 'pages'].includes(ext)) return 'doc';\n return 'other';\n}\n\nconst KIND_STYLE: Record<\n FileKind,\n { Icon: typeof FileText; iconClass: string; tileClass: string }\n> = {\n image: {\n Icon: ImageIcon,\n iconClass: 'text-status-success-text',\n tileClass: 'bg-status-success-bg-subtle',\n },\n pdf: {\n Icon: FileText,\n iconClass: 'text-status-error-text',\n tileClass: 'bg-status-error-bg-subtle',\n },\n sheet: {\n Icon: FileSpreadsheet,\n iconClass: 'text-status-success-text',\n tileClass: 'bg-status-success-bg-subtle',\n },\n doc: {\n Icon: FileText,\n iconClass: 'text-status-info-text',\n tileClass: 'bg-status-info-bg-subtle',\n },\n archive: {\n Icon: FileArchive,\n iconClass: 'text-status-warning-text',\n tileClass: 'bg-status-warning-bg-subtle',\n },\n code: {\n Icon: FileCode2,\n iconClass: 'text-status-info-text',\n tileClass: 'bg-status-info-bg-subtle',\n },\n video: {\n Icon: FileVideo,\n iconClass: 'text-status-info-text',\n tileClass: 'bg-status-info-bg-subtle',\n },\n audio: {\n Icon: FileAudio,\n iconClass: 'text-status-warning-text',\n tileClass: 'bg-status-warning-bg-subtle',\n },\n other: { Icon: FileIcon, iconClass: 'text-text-secondary', tileClass: 'bg-bg-sunken' },\n};\n\n/** File-type badge tile — an inline thumbnail for images, a tinted glyph otherwise. */\nfunction FileBadge({ file }: { file: FileNode }) {\n const kind = kindOf(file);\n const { Icon, iconClass, tileClass } = KIND_STYLE[kind];\n if (kind === 'image' && file.downloadUrl) {\n return (\n <img\n src={file.downloadUrl}\n alt=\"\"\n loading=\"lazy\"\n className=\"h-10 w-10 shrink-0 rounded-lg border border-border-subtle object-cover\"\n />\n );\n }\n return (\n <span\n className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-lg ${tileClass}`}\n aria-hidden=\"true\"\n >\n <Icon className={`h-5 w-5 ${iconClass}`} />\n </span>\n );\n}\n\nexport function AttachmentsPanel({ entityType, entityId }: AttachmentsPanelProps) {\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [uploading, setUploading] = useState(false);\n const [sharingId, setSharingId] = useState<string | null>(null);\n\n const { data, loading, refetch } = useQuery<GetFilesResult>(GET_FILES_QUERY, {\n variables: {\n pagination: { limit: 25, offset: 0 },\n sort: { field: 'CREATED_AT', order: 'DESC' },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n });\n\n const [initiateUpload] = useMutation<{\n initiateUpload: { uploadUrl?: string | null; session: { id: string } };\n }>(INITIATE_UPLOAD_MUTATION);\n const [completeUpload] = useMutation(COMPLETE_UPLOAD_MUTATION);\n const [createShareLink] = useMutation<{\n createFileShareLink: { id: string; shareUrl?: string | null };\n }>(CREATE_SHARE_LINK_MUTATION);\n\n const files = useMemo(() => data?.getFiles.files ?? [], [data]);\n\n const handleFileSelected = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n // Reset the input so selecting the same file again re-triggers change.\n event.target.value = '';\n if (!file) return;\n\n setUploading(true);\n try {\n const initiated = await initiateUpload({\n variables: {\n input: {\n fileName: file.name,\n extension: extensionOf(file.name),\n fileSize: file.size,\n mimeType: file.type || 'application/octet-stream',\n visibility: 'WORKSPACE',\n },\n },\n });\n\n const uploadUrl = initiated.data?.initiateUpload.uploadUrl;\n const sessionId = initiated.data?.initiateUpload.session.id;\n if (!uploadUrl || !sessionId) {\n throw new Error('Upload could not be initialised.');\n }\n\n const putResponse = await fetch(uploadUrl, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n if (!putResponse.ok) {\n throw new Error(`Storage rejected the upload (HTTP ${putResponse.status}).`);\n }\n\n await completeUpload({\n variables: { input: { sessionId, fileSize: file.size } },\n });\n\n notify.success('File uploaded', `${file.name} is now available on this record.`);\n await refetch();\n } catch (error) {\n notify.error(\n 'Upload failed',\n error instanceof Error ? error.message : 'The file could not be uploaded.'\n );\n } finally {\n setUploading(false);\n }\n };\n\n const handleShare = async (fileId: string) => {\n setSharingId(fileId);\n try {\n const result = await createShareLink({\n variables: { input: { resourceId: fileId, role: 'VIEWER' } },\n });\n const url = result.data?.createFileShareLink.shareUrl;\n if (!url) {\n throw new Error('No share link was returned.');\n }\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(url);\n notify.success('Share link copied', 'A view-only link is on your clipboard.');\n } else {\n notify.info('Share link ready', url);\n }\n } catch (error) {\n notify.error(\n 'Could not create share link',\n error instanceof Error ? error.message : 'Please try again.'\n );\n } finally {\n setSharingId(null);\n }\n };\n\n const total = data?.getFiles.total ?? 0;\n const hasFiles = files.length > 0;\n\n return (\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-2 space-y-0\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Paperclip className=\"h-4 w-4\" />\n Attachments\n {total ? (\n <span className=\"text-sm font-normal text-text-secondary\">\n {total} file{total === 1 ? '' : 's'}\n </span>\n ) : null}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <input\n ref={fileInputRef}\n type=\"file\"\n className=\"hidden\"\n onChange={(event) => void handleFileSelected(event)}\n aria-label={`Upload a file for ${entityType} ${entityId}`}\n />\n\n {loading && !hasFiles ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 3 }).map((_, i) => (\n <div\n key={i}\n className=\"h-14 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : !hasFiles ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No files yet\"\n description=\"Upload photos, receipts, warranties or documents to keep them with this record.\"\n />\n ) : (\n <ul className=\"space-y-1.5\">\n {files.map((file) => {\n const isSharing = sharingId === file.id;\n return (\n <li\n key={file.id}\n className=\"flex items-center gap-3 rounded-lg border border-border-subtle bg-bg-surface p-2.5\"\n >\n <FileBadge file={file} />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium\" title={file.name}>\n {file.name}\n </p>\n <p className=\"truncate text-xs text-text-secondary\">\n {formatSize(Number(file.size))} · {relativeTime(file.createdAt)}\n </p>\n </div>\n {/* Desktop: inline actions. Mobile: kebab menu to avoid crowding. */}\n <div className=\"hidden items-center gap-1 sm:flex\">\n {file.downloadUrl ? (\n <a\n href={file.downloadUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n aria-label={`Download ${file.name}`}\n title=\"Download\"\n >\n <Download className=\"h-4 w-4\" />\n </a>\n ) : null}\n <button\n type=\"button\"\n disabled={isSharing}\n onClick={() => void handleShare(file.id)}\n className=\"rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary disabled:opacity-60\"\n aria-label={`Copy share link for ${file.name}`}\n title=\"Copy share link\"\n >\n {isSharing ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Link2 className=\"h-4 w-4\" />\n )}\n </button>\n </div>\n <div className=\"sm:hidden\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label={`Actions for ${file.name}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {file.downloadUrl ? (\n <DropdownMenuItem asChild>\n <a href={file.downloadUrl} target=\"_blank\" rel=\"noreferrer\">\n <Download className=\"mr-2 h-4 w-4\" />\n Download\n </a>\n </DropdownMenuItem>\n ) : null}\n <DropdownMenuItem\n disabled={isSharing}\n onSelect={(e) => {\n e.preventDefault();\n void handleShare(file.id);\n }}\n >\n <Link2 className=\"mr-2 h-4 w-4\" />\n Copy share link\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </li>\n );\n })}\n </ul>\n )}\n\n {/* Prominent full-width primary upload CTA (matches the reference concept). */}\n <Button\n className=\"w-full\"\n disabled={uploading}\n onClick={() => fileInputRef.current?.click()}\n >\n {uploading ? (\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n ) : (\n <Upload className=\"mr-2 h-4 w-4\" />\n )}\n {uploading ? 'Uploading…' : 'Upload files'}\n </Button>\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AttachmentsPanel.js","names":[],"sources":["../../../src/assethandler/components/AttachmentsPanel.tsx"],"sourcesContent":["import { useMemo, useRef, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n IllustratedEmptyState,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Paperclip,\n Upload,\n Link2,\n FileText,\n FileSpreadsheet,\n FileArchive,\n FileCode2,\n FileVideo,\n FileAudio,\n Image as ImageIcon,\n File as FileIcon,\n Loader2,\n Download,\n MoreHorizontal,\n} from 'lucide-react';\nimport { notify } from '../utils/toast';\n\n/**\n * Attach and reference files for an AssetHandler entity, backed by the\n * federated wspace-files-svc (same wspace gateway/Apollo client). Fully real (no fabricated data) —\n * lists workspace files via `getFiles`, uploads new files through the\n * `initiateUpload` → presigned PUT → `completeUpload` flow, and mints public\n * share links via `createFileShareLink`. All entries are live data.\n *\n * `entityType`/`entityId` scope the panel to the record it is mounted on and\n * are used for context labelling; uploaded files land in the workspace file\n * store so they can be shared and reused.\n */\nexport interface AttachmentsPanelProps {\n entityType: string;\n entityId: string;\n}\n\ninterface FileNode {\n id: string;\n name: string;\n extension: string;\n mimeType: string;\n size: number;\n downloadUrl?: string | null;\n createdAt: string;\n}\n\ninterface GetFilesResult {\n getFiles: {\n files: FileNode[];\n total: number;\n hasMore: boolean;\n };\n}\n\nconst GET_FILES_QUERY = gql`\n query AssetHandlerGetFiles($pagination: PaginationInput, $sort: FileSortInput) {\n getFiles(pagination: $pagination, sort: $sort) {\n total\n hasMore\n files {\n id\n name\n extension\n mimeType\n size\n downloadUrl\n createdAt\n }\n }\n }\n`;\n\nconst INITIATE_UPLOAD_MUTATION = gql`\n mutation AssetHandlerInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session {\n id\n }\n }\n }\n`;\n\nconst COMPLETE_UPLOAD_MUTATION = gql`\n mutation AssetHandlerCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n fileId\n file {\n id\n name\n }\n }\n }\n`;\n\nconst CREATE_SHARE_LINK_MUTATION = gql`\n mutation AssetHandlerCreateFileShareLink($input: CreateShareGrantLinkInput!) {\n createFileShareLink(input: $input) {\n id\n shareUrl\n }\n }\n`;\n\nfunction formatSize(bytes: number): string {\n if (!bytes) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB'];\n const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\n const value = bytes / Math.pow(1024, exponent);\n return `${value.toFixed(exponent === 0 ? 0 : 1)} ${units[exponent]}`;\n}\n\nfunction extensionOf(fileName: string): string {\n const dot = fileName.lastIndexOf('.');\n return dot >= 0 ? fileName.slice(dot + 1).toLowerCase() : 'bin';\n}\n\n/** Compact relative time (\"2 hrs ago\"), with the full timestamp on hover. */\nfunction relativeTime(iso: string): string {\n const then = new Date(iso).getTime();\n if (Number.isNaN(then)) return '';\n const diff = Date.now() - then;\n const sec = Math.round(diff / 1000);\n if (sec < 45) return 'just now';\n const min = Math.round(sec / 60);\n if (min < 60) return `${min} min ago`;\n const hr = Math.round(min / 60);\n if (hr < 24) return `${hr} hr${hr === 1 ? '' : 's'} ago`;\n const day = Math.round(hr / 24);\n if (day < 30) return `${day} day${day === 1 ? '' : 's'} ago`;\n return new Date(iso).toLocaleDateString();\n}\n\ntype FileKind =\n 'image' | 'pdf' | 'sheet' | 'doc' | 'archive' | 'code' | 'video' | 'audio' | 'other';\n\n/** Map a file's extension/mime to a recognisable kind for icon + tint. */\nfunction kindOf(file: FileNode): FileKind {\n const mime = (file.mimeType || '').toLowerCase();\n const ext = (file.extension || extensionOf(file.name)).toLowerCase();\n if (\n mime.startsWith('image/') ||\n ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'avif', 'bmp'].includes(ext)\n )\n return 'image';\n if (mime.startsWith('video/') || ['mp4', 'mov', 'webm', 'avi', 'mkv'].includes(ext))\n return 'video';\n if (mime.startsWith('audio/') || ['mp3', 'wav', 'ogg', 'm4a', 'flac'].includes(ext))\n return 'audio';\n if (mime === 'application/pdf' || ext === 'pdf') return 'pdf';\n if (['xls', 'xlsx', 'csv', 'tsv', 'ods'].includes(ext)) return 'sheet';\n if (['zip', 'rar', '7z', 'tar', 'gz', 'bz2'].includes(ext)) return 'archive';\n if (\n [\n 'js',\n 'ts',\n 'tsx',\n 'jsx',\n 'json',\n 'xml',\n 'yml',\n 'yaml',\n 'html',\n 'css',\n 'sh',\n 'py',\n 'sql',\n ].includes(ext)\n )\n return 'code';\n if (['doc', 'docx', 'txt', 'md', 'rtf', 'odt', 'pages'].includes(ext)) return 'doc';\n return 'other';\n}\n\nconst KIND_STYLE: Record<\n FileKind,\n { Icon: typeof FileText; iconClass: string; tileClass: string }\n> = {\n image: {\n Icon: ImageIcon,\n iconClass: 'text-status-success-text',\n tileClass: 'bg-status-success-bg-subtle',\n },\n pdf: {\n Icon: FileText,\n iconClass: 'text-status-error-text',\n tileClass: 'bg-status-error-bg-subtle',\n },\n sheet: {\n Icon: FileSpreadsheet,\n iconClass: 'text-status-success-text',\n tileClass: 'bg-status-success-bg-subtle',\n },\n doc: {\n Icon: FileText,\n iconClass: 'text-status-info-text',\n tileClass: 'bg-status-info-bg-subtle',\n },\n archive: {\n Icon: FileArchive,\n iconClass: 'text-status-warning-text',\n tileClass: 'bg-status-warning-bg-subtle',\n },\n code: {\n Icon: FileCode2,\n iconClass: 'text-status-info-text',\n tileClass: 'bg-status-info-bg-subtle',\n },\n video: {\n Icon: FileVideo,\n iconClass: 'text-status-info-text',\n tileClass: 'bg-status-info-bg-subtle',\n },\n audio: {\n Icon: FileAudio,\n iconClass: 'text-status-warning-text',\n tileClass: 'bg-status-warning-bg-subtle',\n },\n other: { Icon: FileIcon, iconClass: 'text-text-secondary', tileClass: 'bg-bg-sunken' },\n};\n\n/** File-type badge tile — an inline thumbnail for images, a tinted glyph otherwise. */\nfunction FileBadge({ file }: { file: FileNode }) {\n const kind = kindOf(file);\n const { Icon, iconClass, tileClass } = KIND_STYLE[kind];\n if (kind === 'image' && file.downloadUrl) {\n return (\n <img\n src={file.downloadUrl}\n alt=\"\"\n loading=\"lazy\"\n className=\"h-10 w-10 shrink-0 rounded-lg border border-border-subtle object-cover\"\n />\n );\n }\n return (\n <span\n className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-lg ${tileClass}`}\n aria-hidden=\"true\"\n >\n <Icon className={`h-5 w-5 ${iconClass}`} />\n </span>\n );\n}\n\nexport function AttachmentsPanel({ entityType, entityId }: AttachmentsPanelProps) {\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [uploading, setUploading] = useState(false);\n const [sharingId, setSharingId] = useState<string | null>(null);\n\n const { data, loading, refetch } = useQuery<GetFilesResult>(GET_FILES_QUERY, {\n variables: {\n pagination: { limit: 25, offset: 0 },\n sort: { field: 'CREATED_AT', order: 'DESC' },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n });\n\n const [initiateUpload] = useMutation<{\n initiateUpload: { uploadUrl?: string | null; session: { id: string } };\n }>(INITIATE_UPLOAD_MUTATION);\n const [completeUpload] = useMutation(COMPLETE_UPLOAD_MUTATION);\n const [createShareLink] = useMutation<{\n createFileShareLink: { id: string; shareUrl?: string | null };\n }>(CREATE_SHARE_LINK_MUTATION);\n\n const files = useMemo(() => data?.getFiles.files ?? [], [data]);\n\n const handleFileSelected = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n // Reset the input so selecting the same file again re-triggers change.\n event.target.value = '';\n if (!file) return;\n\n setUploading(true);\n try {\n const initiated = await initiateUpload({\n variables: {\n input: {\n fileName: file.name,\n extension: extensionOf(file.name),\n fileSize: file.size,\n mimeType: file.type || 'application/octet-stream',\n visibility: 'WORKSPACE',\n },\n },\n });\n\n const uploadUrl = initiated.data?.initiateUpload.uploadUrl;\n const sessionId = initiated.data?.initiateUpload.session.id;\n if (!uploadUrl || !sessionId) {\n throw new Error('Upload could not be initialised.');\n }\n\n const putResponse = await fetch(uploadUrl, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n if (!putResponse.ok) {\n throw new Error(`Storage rejected the upload (HTTP ${putResponse.status}).`);\n }\n\n await completeUpload({\n variables: { input: { sessionId, fileSize: file.size } },\n });\n\n notify.success('File uploaded', `${file.name} is now available on this record.`);\n await refetch();\n } catch (error) {\n notify.error(\n 'Upload failed',\n error instanceof Error ? error.message : 'The file could not be uploaded.'\n );\n } finally {\n setUploading(false);\n }\n };\n\n const handleShare = async (fileId: string) => {\n setSharingId(fileId);\n try {\n const result = await createShareLink({\n variables: { input: { resourceId: fileId, role: 'VIEWER' } },\n });\n const url = result.data?.createFileShareLink.shareUrl;\n if (!url) {\n throw new Error('No share link was returned.');\n }\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(url);\n notify.success('Share link copied', 'A view-only link is on your clipboard.');\n } else {\n notify.info('Share link ready', url);\n }\n } catch (error) {\n notify.error(\n 'Could not create share link',\n error instanceof Error ? error.message : 'Please try again.'\n );\n } finally {\n setSharingId(null);\n }\n };\n\n const total = data?.getFiles.total ?? 0;\n const hasFiles = files.length > 0;\n\n return (\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-2 space-y-0\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Paperclip className=\"h-4 w-4\" />\n Attachments\n {total ? (\n <span className=\"text-sm font-normal text-text-secondary\">\n {total} file{total === 1 ? '' : 's'}\n </span>\n ) : null}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <input\n ref={fileInputRef}\n type=\"file\"\n className=\"hidden\"\n onChange={(event) => void handleFileSelected(event)}\n aria-label={`Upload a file for ${entityType} ${entityId}`}\n />\n\n {loading && !hasFiles ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 3 }).map((_, i) => (\n <div\n key={i}\n className=\"h-14 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : !hasFiles ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No files yet\"\n description=\"Upload photos, receipts, warranties or documents to keep them with this record.\"\n />\n ) : (\n <ul className=\"space-y-1.5\">\n {files.map((file) => {\n const isSharing = sharingId === file.id;\n return (\n <li\n key={file.id}\n className=\"flex items-center gap-3 rounded-lg border border-border-subtle bg-bg-surface p-2.5\"\n >\n <FileBadge file={file} />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium\" title={file.name}>\n {file.name}\n </p>\n <p className=\"truncate text-xs text-text-secondary\">\n {formatSize(Number(file.size))} · {relativeTime(file.createdAt)}\n </p>\n </div>\n {/* Desktop: inline actions. Mobile: kebab menu to avoid crowding. */}\n <div className=\"hidden items-center gap-1 sm:flex\">\n {file.downloadUrl ? (\n <a\n href={file.downloadUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n aria-label={`Download ${file.name}`}\n title=\"Download\"\n >\n <Download className=\"h-4 w-4\" />\n </a>\n ) : null}\n <button\n type=\"button\"\n disabled={isSharing}\n onClick={() => void handleShare(file.id)}\n className=\"rounded-md p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary disabled:opacity-60\"\n aria-label={`Copy share link for ${file.name}`}\n title=\"Copy share link\"\n >\n {isSharing ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Link2 className=\"h-4 w-4\" />\n )}\n </button>\n </div>\n <div className=\"sm:hidden\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label={`Actions for ${file.name}`}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {file.downloadUrl ? (\n <DropdownMenuItem asChild>\n <a href={file.downloadUrl} target=\"_blank\" rel=\"noreferrer\">\n <Download className=\"mr-2 h-4 w-4\" />\n Download\n </a>\n </DropdownMenuItem>\n ) : null}\n <DropdownMenuItem\n disabled={isSharing}\n onSelect={(e) => {\n e.preventDefault();\n void handleShare(file.id);\n }}\n >\n <Link2 className=\"mr-2 h-4 w-4\" />\n Copy share link\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </li>\n );\n })}\n </ul>\n )}\n\n {/* Prominent full-width primary upload CTA (matches the reference concept). */}\n <Button\n className=\"w-full\"\n disabled={uploading}\n onClick={() => fileInputRef.current?.click()}\n >\n {uploading ? (\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n ) : (\n <Upload className=\"mr-2 h-4 w-4\" />\n )}\n {uploading ? 'Uploading…' : 'Upload files'}\n </Button>\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartCard.js","names":[],"sources":["../../../src/assethandler/components/ChartCard.tsx"],"sourcesContent":["import type { ComponentType, ReactNode } from 'react';\nimport { EmphasisPanel, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\nexport interface ChartCardProps {\n /** Card title (left of the header). */\n title: ReactNode;\n /** Optional supporting line under the title. */\n subtitle?: ReactNode;\n /** Optional controls rendered on the right of the header (toggles, menus). */\n actions?: ReactNode;\n /** Optional leading icon beside the title. */\n icon?: ComponentType<{ className?: string }>;\n /**\n * Render as the page's single hero surface (`EmphasisPanel`) instead of a\n * `GlassCard`. Use at most one per page.\n */\n emphasis?: boolean;\n /** Optional footer row under the body. */\n footer?: ReactNode;\n /** Extra classes for the body wrapper (e.g. spacing around the chart). */\n bodyClassName?: string;\n className?: string;\n children: ReactNode;\n}\n\n/**\n * ChartCard — a GlassCard/EmphasisPanel wrapper with a proper header (title,\n * subtitle, right-aligned actions) for charts and metric panels. Keeps chart\n * sections visually consistent with the rest of the AssetHandler surfaces.\n */\nexport function ChartCard({\n title,\n subtitle,\n actions,\n icon: Icon,\n emphasis = false,\n footer,\n bodyClassName,\n className,\n children,\n}: ChartCardProps) {\n const header = (\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2\">\n {Icon ? <Icon className=\"mt-0.5 h-4 w-4 shrink-0 text-text-secondary\" /> : null}\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">{title}</h3>\n {subtitle ? <p className=\"mt-0.5 text-sm text-text-secondary\">{subtitle}</p> : null}\n </div>\n </div>\n {actions ? <div className=\"flex shrink-0 items-center gap-2\">{actions}</div> : null}\n </div>\n );\n\n const body = (\n <>\n {header}\n <div className={cn('mt-4', bodyClassName)}>{children}</div>\n {footer ? <div className=\"mt-4 border-t border-border-seam pt-3\">{footer}</div> : null}\n </>\n );\n\n if (emphasis) {\n return <EmphasisPanel className={className}>{body}</EmphasisPanel>;\n }\n\n return (\n <GlassCard treatment=\"glass\" className={cn('border-border-seam', className)}>\n <div className=\"p-5\">{body}</div>\n </GlassCard>\n );\n}\n"],"mappings":";;;;AA+BA,SAAgB,EAAU,EACxB,UACA,aACA,YACA,MAAM,GACN,cAAW,IACX,WACA,kBACA,cACA,eACiB;CAcjB,IAAM,IACJ,kBAAA,GAAA,EAAA,UAAA;EAbA,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,IAAO,kBAAC,GAAD,EAAM,WAAU
|
|
1
|
+
{"version":3,"file":"ChartCard.js","names":[],"sources":["../../../src/assethandler/components/ChartCard.tsx"],"sourcesContent":["import type { ComponentType, ReactNode } from 'react';\nimport { EmphasisPanel, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\nexport interface ChartCardProps {\n /** Card title (left of the header). */\n title: ReactNode;\n /** Optional supporting line under the title. */\n subtitle?: ReactNode;\n /** Optional controls rendered on the right of the header (toggles, menus). */\n actions?: ReactNode;\n /** Optional leading icon beside the title. */\n icon?: ComponentType<{ className?: string }>;\n /**\n * Render as the page's single hero surface (`EmphasisPanel`) instead of a\n * `GlassCard`. Use at most one per page.\n */\n emphasis?: boolean;\n /** Optional footer row under the body. */\n footer?: ReactNode;\n /** Extra classes for the body wrapper (e.g. spacing around the chart). */\n bodyClassName?: string;\n className?: string;\n children: ReactNode;\n}\n\n/**\n * ChartCard — a GlassCard/EmphasisPanel wrapper with a proper header (title,\n * subtitle, right-aligned actions) for charts and metric panels. Keeps chart\n * sections visually consistent with the rest of the AssetHandler surfaces.\n */\nexport function ChartCard({\n title,\n subtitle,\n actions,\n icon: Icon,\n emphasis = false,\n footer,\n bodyClassName,\n className,\n children,\n}: ChartCardProps) {\n const header = (\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-2\">\n {Icon ? <Icon className=\"mt-0.5 h-4 w-4 shrink-0 text-text-secondary\" /> : null}\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">{title}</h3>\n {subtitle ? <p className=\"mt-0.5 text-sm text-text-secondary\">{subtitle}</p> : null}\n </div>\n </div>\n {actions ? <div className=\"flex shrink-0 items-center gap-2\">{actions}</div> : null}\n </div>\n );\n\n const body = (\n <>\n {header}\n <div className={cn('mt-4', bodyClassName)}>{children}</div>\n {footer ? <div className=\"mt-4 border-t border-border-seam pt-3\">{footer}</div> : null}\n </>\n );\n\n if (emphasis) {\n return <EmphasisPanel className={className}>{body}</EmphasisPanel>;\n }\n\n return (\n <GlassCard treatment=\"glass\" className={cn('border-border-seam', className)}>\n <div className=\"p-5\">{body}</div>\n </GlassCard>\n );\n}\n"],"mappings":";;;;AA+BA,SAAgB,EAAU,EACxB,UACA,aACA,YACA,MAAM,GACN,cAAW,IACX,WACA,kBACA,cACA,eACiB;CAcjB,IAAM,IACJ,kBAAA,GAAA,EAAA,UAAA;EAbA,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,IAAO,kBAAC,GAAD,EAAM,WAAU,8CAA+C,CAAA,IAAI,MAC3E,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAsD;KAAU,CAAA,GAC7E,IAAW,kBAAC,KAAD;MAAG,WAAU;gBAAsC;KAAY,CAAA,IAAI,IAC5E;MACF;OACJ,IAAU,kBAAC,OAAD;IAAK,WAAU;cAAoC;GAAa,CAAA,IAAI,IAC5E;GAKF;EACD,kBAAC,OAAD;GAAK,WAAW,EAAG,QAAQ,CAAa;GAAI;EAAc,CAAA;EACzD,IAAS,kBAAC,OAAD;GAAK,WAAU;aAAyC;EAAY,CAAA,IAAI;CAClF,EAAA,CAAA;CAOJ,OAJI,IACK,kBAAC,GAAD;EAA0B;YAAY;CAAoB,CAAA,IAIjE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAW,EAAG,sBAAsB,CAAS;YACxE,kBAAC,OAAD;GAAK,WAAU;aAAO;EAAU,CAAA;CACvB,CAAA;AAEf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentsPanel.js","names":[],"sources":["../../../src/assethandler/components/CommentsPanel.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Input,\n IllustratedEmptyState,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n MessageSquare,\n Send,\n CornerDownRight,\n MoreHorizontal,\n Pencil,\n Trash2,\n X,\n Check,\n} from 'lucide-react';\nimport { useAssetHandler } from '../providers/AssetHandlerProvider';\nimport { notify } from '../utils/toast';\n\n/**\n * Discussion thread for any AssetHandler entity, backed by the federated\n * wspace-conversations-svc `EntityComment` graph (same wspace gateway/Apollo\n * client). Real create/reply/edit/delete + list; all entries are live data.\n *\n * `entityType` is the conversations-svc discriminator, e.g. \"assethandler-asset\"\n * or \"assethandler-incident\"; `entityId` is the record id.\n */\nexport interface CommentsPanelProps {\n entityType: string;\n entityId: string;\n}\n\ninterface EntityCommentNode {\n id: string;\n authorId: string;\n authorType?: string | null;\n parentCommentId?: string | null;\n createdAt: string;\n edited: boolean;\n editedAt?: string | null;\n content: { text?: string | null; markdown?: string | null } | null;\n}\n\ninterface EntityCommentsResult {\n getEntityComments: {\n items: EntityCommentNode[];\n total: number;\n hasMore: boolean;\n };\n}\n\nconst ENTITY_COMMENTS_QUERY = gql`\n query AssetHandlerEntityComments($input: GetCommentsInput!) {\n getEntityComments(input: $input) {\n total\n hasMore\n items {\n id\n authorId\n authorType\n parentCommentId\n createdAt\n edited\n editedAt\n content {\n text\n markdown\n }\n }\n }\n }\n`;\n\nconst CREATE_ENTITY_COMMENT_MUTATION = gql`\n mutation AssetHandlerCreateEntityComment($input: CreateEntityCommentInput!) {\n createEntityComment(input: $input) {\n id\n }\n }\n`;\n\nconst UPDATE_ENTITY_COMMENT_MUTATION = gql`\n mutation AssetHandlerUpdateEntityComment($input: UpdateEntityCommentInput!) {\n updateEntityComment(input: $input) {\n id\n edited\n }\n }\n`;\n\nconst DELETE_ENTITY_COMMENT_MUTATION = gql`\n mutation AssetHandlerDeleteEntityComment($input: DeleteCommentInput!) {\n deleteEntityComment(input: $input)\n }\n`;\n\n/** Compact relative time (\"2 hrs ago\"), with the full timestamp on hover. */\nfunction relativeTime(iso: string): string {\n const then = new Date(iso).getTime();\n if (Number.isNaN(then)) return '';\n const diff = Date.now() - then;\n const sec = Math.round(diff / 1000);\n if (sec < 45) return 'just now';\n const min = Math.round(sec / 60);\n if (min < 60) return `${min} min ago`;\n const hr = Math.round(min / 60);\n if (hr < 24) return `${hr} hr${hr === 1 ? '' : 's'} ago`;\n const day = Math.round(hr / 24);\n if (day < 30) return `${day} day${day === 1 ? '' : 's'} ago`;\n return new Date(iso).toLocaleDateString();\n}\n\n// Deterministic, token-based avatar tint from an opaque id (identicon-style).\nconst AVATAR_TOKENS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n 'var(--color-chart-6)',\n 'var(--color-chart-7)',\n 'var(--color-chart-8)',\n];\n\nfunction hashSeed(seed: string): number {\n let h = 0;\n for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) >>> 0;\n return h;\n}\n\nfunction seedColor(seed: string): string {\n return AVATAR_TOKENS[hashSeed(seed) % AVATAR_TOKENS.length];\n}\n\nfunction initialsFrom(label: string, seed: string): string {\n const words = label\n .trim()\n .split(/[\\s._-]+/)\n .filter(Boolean);\n if (words.length >= 2) return (words[0][0] + words[1][0]).toUpperCase();\n if (words.length === 1 && words[0].length >= 2) return words[0].slice(0, 2).toUpperCase();\n // Fall back to a stable 2-char monogram derived from the id.\n const hex = hashSeed(seed).toString(36).toUpperCase().padStart(2, 'X');\n return hex.slice(0, 2);\n}\n\nfunction Avatar({\n seed,\n label,\n imageUrl,\n size = 32,\n}: {\n seed: string;\n label: string;\n imageUrl?: string | null;\n size?: number;\n}) {\n const color = seedColor(seed);\n const dimension = { width: size, height: size };\n if (imageUrl) {\n return (\n <img\n src={imageUrl}\n alt=\"\"\n style={dimension}\n className=\"shrink-0 rounded-full border border-border-subtle object-cover\"\n />\n );\n }\n return (\n <span\n aria-hidden=\"true\"\n style={{\n ...dimension,\n color,\n backgroundColor: `color-mix(in srgb, ${color} 16%, transparent)`,\n borderColor: `color-mix(in srgb, ${color} 32%, transparent)`,\n }}\n className=\"flex shrink-0 items-center justify-center rounded-full border text-xs font-semibold\"\n >\n {initialsFrom(label, seed)}\n </span>\n );\n}\n\nexport function CommentsPanel({ entityType, entityId }: CommentsPanelProps) {\n const { currentUser } = useAssetHandler();\n const [draft, setDraft] = useState('');\n const [replyTo, setReplyTo] = useState<string | null>(null);\n const [replyDraft, setReplyDraft] = useState('');\n const [editing, setEditing] = useState<string | null>(null);\n const [editDraft, setEditDraft] = useState('');\n\n const currentUserName =\n currentUser?.name ||\n [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ') ||\n currentUser?.username ||\n currentUser?.email?.split('@')[0] ||\n 'You';\n\n const { data, loading, refetch } = useQuery<EntityCommentsResult>(ENTITY_COMMENTS_QUERY, {\n variables: {\n input: {\n entityType,\n entityId,\n take: 100,\n sortBy: 'createdAt',\n sortOrder: 'asc',\n },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n skip: !entityId,\n });\n\n const [createComment, { loading: posting }] = useMutation(CREATE_ENTITY_COMMENT_MUTATION, {\n onCompleted: () => {\n notify.success('Comment added', 'Your comment is now part of the thread.');\n void refetch();\n },\n onError: (error) => notify.error('Could not post comment', error.message),\n });\n const [updateComment, { loading: updating }] = useMutation(UPDATE_ENTITY_COMMENT_MUTATION, {\n onError: (error) => notify.error('Could not update comment', error.message),\n });\n const [deleteComment] = useMutation(DELETE_ENTITY_COMMENT_MUTATION, {\n onError: (error) => notify.error('Could not delete comment', error.message),\n });\n\n const allComments = useMemo(() => data?.getEntityComments.items ?? [], [data]);\n\n // Group replies under their parent to render a lightweight thread.\n const { roots, repliesByParent } = useMemo(() => {\n const byParent = new Map<string, EntityCommentNode[]>();\n const top: EntityCommentNode[] = [];\n for (const c of allComments) {\n if (c.parentCommentId) {\n const list = byParent.get(c.parentCommentId) ?? [];\n list.push(c);\n byParent.set(c.parentCommentId, list);\n } else {\n top.push(c);\n }\n }\n return { roots: top, repliesByParent: byParent };\n }, [allComments]);\n\n const authorLabel = (authorId: string): string =>\n currentUser?.id && authorId === currentUser.id\n ? currentUserName\n : `Member ${authorId.slice(0, 6)}`;\n\n const submitRoot = (event: React.FormEvent) => {\n event.preventDefault();\n const text = draft.trim();\n if (!text) {\n notify.warning('Nothing to post', 'Write a comment before submitting.');\n return;\n }\n void createComment({\n variables: {\n input: { entityType, entityId, contentType: 'TEXT', content: { text } },\n },\n }).then((res) => {\n if (res.data) setDraft('');\n });\n };\n\n const submitReply = (parentCommentId: string) => {\n const text = replyDraft.trim();\n if (!text) return;\n void createComment({\n variables: {\n input: { entityType, entityId, contentType: 'TEXT', content: { text }, parentCommentId },\n },\n }).then((res) => {\n if (res.data) {\n setReplyDraft('');\n setReplyTo(null);\n }\n });\n };\n\n const submitEdit = (id: string) => {\n const text = editDraft.trim();\n if (!text) return;\n void updateComment({\n variables: { input: { id, contentType: 'TEXT', content: { text } } },\n }).then((res) => {\n if (res.data) {\n setEditing(null);\n setEditDraft('');\n notify.success('Comment updated', 'Your changes have been saved.');\n void refetch();\n }\n });\n };\n\n const removeComment = (id: string) => {\n void deleteComment({ variables: { input: { id, soft: true } } }).then((res) => {\n if (res.data) {\n notify.success('Comment removed', 'The comment has been deleted from the thread.');\n void refetch();\n }\n });\n };\n\n const renderComment = (comment: EntityCommentNode, isReply: boolean) => {\n const mine = Boolean(currentUser?.id && comment.authorId === currentUser.id);\n const name = authorLabel(comment.authorId);\n const text = comment.content?.text ?? comment.content?.markdown ?? '';\n const isEditing = editing === comment.id;\n return (\n <div key={comment.id} className=\"flex gap-2.5\">\n <Avatar\n seed={comment.authorId}\n label={name}\n imageUrl={mine ? currentUser?.avatar : undefined}\n size={isReply ? 28 : 32}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"rounded-2xl rounded-tl-sm border border-border-subtle bg-bg-sunken px-3 py-2\">\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-1.5\">\n <span className=\"truncate text-sm font-semibold text-text-primary\">{name}</span>\n {mine ? (\n <span className=\"rounded-full bg-status-info-bg-subtle px-1.5 py-0.5 text-[0.625rem] font-medium text-status-info-text\">\n You\n </span>\n ) : null}\n </div>\n {mine && !isEditing ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n className=\"-mr-1 rounded-md p-1 text-text-secondary hover:bg-bg-surface hover:text-text-primary\"\n aria-label=\"Comment actions\"\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onSelect={() => {\n setEditing(comment.id);\n setEditDraft(text);\n }}\n >\n <Pencil className=\"mr-2 h-4 w-4\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"text-status-error-text\"\n onSelect={() => removeComment(comment.id)}\n >\n <Trash2 className=\"mr-2 h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </div>\n\n {isEditing ? (\n <form\n className=\"mt-1.5 flex items-center gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n submitEdit(comment.id);\n }}\n >\n <Input\n value={editDraft}\n onChange={(e) => setEditDraft(e.target.value)}\n aria-label=\"Edit comment\"\n autoFocus\n className=\"h-8\"\n />\n <Button\n type=\"submit\"\n size=\"sm\"\n variant=\"ghost\"\n disabled={updating || !editDraft.trim()}\n aria-label=\"Save edit\"\n >\n <Check className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setEditing(null);\n setEditDraft('');\n }}\n aria-label=\"Cancel edit\"\n >\n <X className=\"h-4 w-4\" />\n </Button>\n </form>\n ) : (\n <p className=\"mt-0.5 whitespace-pre-wrap break-words text-sm text-text-secondary\">\n {text}\n </p>\n )}\n </div>\n\n <div className=\"mt-1 flex flex-wrap items-center gap-2 px-1 text-xs text-text-secondary\">\n <span title={new Date(comment.createdAt).toLocaleString()}>\n {relativeTime(comment.createdAt)}\n </span>\n {comment.edited ? <span>· edited</span> : null}\n {!isReply ? (\n <button\n type=\"button\"\n className=\"font-medium text-text-secondary hover:text-text-primary\"\n onClick={() => {\n setReplyTo(replyTo === comment.id ? null : comment.id);\n setReplyDraft('');\n }}\n >\n Reply\n </button>\n ) : null}\n </div>\n\n {replyTo === comment.id ? (\n <form\n className=\"mt-2 flex items-center gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n submitReply(comment.id);\n }}\n >\n <CornerDownRight\n className=\"h-4 w-4 shrink-0 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <Input\n value={replyDraft}\n onChange={(e) => setReplyDraft(e.target.value)}\n placeholder={`Reply to ${name}…`}\n aria-label={`Reply to ${name}`}\n autoFocus\n className=\"h-9\"\n />\n <Button type=\"submit\" size=\"sm\" disabled={posting || !replyDraft.trim()}>\n Reply\n </Button>\n </form>\n ) : null}\n </div>\n </div>\n );\n };\n\n const total = data?.getEntityComments.total ?? 0;\n const hasComments = roots.length > 0;\n\n return (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <MessageSquare className=\"h-4 w-4\" />\n Discussion\n {total ? (\n <span className=\"text-sm font-normal text-text-secondary\">\n {total} comment{total === 1 ? '' : 's'}\n </span>\n ) : null}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {loading && !hasComments ? (\n <div className=\"space-y-3\">\n {Array.from({ length: 3 }).map((_, i) => (\n <div\n key={i}\n className=\"h-16 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : !hasComments ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"No comments yet\"\n description=\"Start the conversation — notes and decisions logged here stay with the record.\"\n />\n ) : (\n <ul className=\"space-y-4\">\n {roots.map((comment) => {\n const replies = repliesByParent.get(comment.id) ?? [];\n return (\n <li key={comment.id} className=\"space-y-3\">\n {renderComment(comment, false)}\n {replies.length > 0 ? (\n <ul className=\"space-y-3 border-l border-border-subtle pl-4 sm:ml-6\">\n {replies.map((reply) => (\n <li key={reply.id}>{renderComment(reply, true)}</li>\n ))}\n </ul>\n ) : null}\n </li>\n );\n })}\n </ul>\n )}\n\n {/* Slim inline composer at the bottom (matches the reference concept). */}\n <form\n className=\"flex items-center gap-2 border-t border-border-subtle pt-3\"\n onSubmit={submitRoot}\n >\n <Avatar\n seed={currentUser?.id ?? 'me'}\n label={currentUserName}\n imageUrl={currentUser?.avatar}\n size={32}\n />\n <Input\n placeholder=\"Write a comment…\"\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n aria-label=\"New comment\"\n />\n <Button type=\"submit\" size=\"sm\" disabled={posting || !draft.trim()}>\n <Send className=\"mr-1.5 h-3.5 w-3.5\" />\n {posting ? 'Posting…' : 'Post'}\n </Button>\n </form>\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CommentsPanel.js","names":[],"sources":["../../../src/assethandler/components/CommentsPanel.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Input,\n IllustratedEmptyState,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n MessageSquare,\n Send,\n CornerDownRight,\n MoreHorizontal,\n Pencil,\n Trash2,\n X,\n Check,\n} from 'lucide-react';\nimport { useAssetHandler } from '../providers/AssetHandlerProvider';\nimport { notify } from '../utils/toast';\n\n/**\n * Discussion thread for any AssetHandler entity, backed by the federated\n * wspace-conversations-svc `EntityComment` graph (same wspace gateway/Apollo\n * client). Real create/reply/edit/delete + list; all entries are live data.\n *\n * `entityType` is the conversations-svc discriminator, e.g. \"assethandler-asset\"\n * or \"assethandler-incident\"; `entityId` is the record id.\n */\nexport interface CommentsPanelProps {\n entityType: string;\n entityId: string;\n}\n\ninterface EntityCommentNode {\n id: string;\n authorId: string;\n authorType?: string | null;\n parentCommentId?: string | null;\n createdAt: string;\n edited: boolean;\n editedAt?: string | null;\n content: { text?: string | null; markdown?: string | null } | null;\n}\n\ninterface EntityCommentsResult {\n getEntityComments: {\n items: EntityCommentNode[];\n total: number;\n hasMore: boolean;\n };\n}\n\nconst ENTITY_COMMENTS_QUERY = gql`\n query AssetHandlerEntityComments($input: GetCommentsInput!) {\n getEntityComments(input: $input) {\n total\n hasMore\n items {\n id\n authorId\n authorType\n parentCommentId\n createdAt\n edited\n editedAt\n content {\n text\n markdown\n }\n }\n }\n }\n`;\n\nconst CREATE_ENTITY_COMMENT_MUTATION = gql`\n mutation AssetHandlerCreateEntityComment($input: CreateEntityCommentInput!) {\n createEntityComment(input: $input) {\n id\n }\n }\n`;\n\nconst UPDATE_ENTITY_COMMENT_MUTATION = gql`\n mutation AssetHandlerUpdateEntityComment($input: UpdateEntityCommentInput!) {\n updateEntityComment(input: $input) {\n id\n edited\n }\n }\n`;\n\nconst DELETE_ENTITY_COMMENT_MUTATION = gql`\n mutation AssetHandlerDeleteEntityComment($input: DeleteCommentInput!) {\n deleteEntityComment(input: $input)\n }\n`;\n\n/** Compact relative time (\"2 hrs ago\"), with the full timestamp on hover. */\nfunction relativeTime(iso: string): string {\n const then = new Date(iso).getTime();\n if (Number.isNaN(then)) return '';\n const diff = Date.now() - then;\n const sec = Math.round(diff / 1000);\n if (sec < 45) return 'just now';\n const min = Math.round(sec / 60);\n if (min < 60) return `${min} min ago`;\n const hr = Math.round(min / 60);\n if (hr < 24) return `${hr} hr${hr === 1 ? '' : 's'} ago`;\n const day = Math.round(hr / 24);\n if (day < 30) return `${day} day${day === 1 ? '' : 's'} ago`;\n return new Date(iso).toLocaleDateString();\n}\n\n// Deterministic, token-based avatar tint from an opaque id (identicon-style).\nconst AVATAR_TOKENS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n 'var(--color-chart-6)',\n 'var(--color-chart-7)',\n 'var(--color-chart-8)',\n];\n\nfunction hashSeed(seed: string): number {\n let h = 0;\n for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) >>> 0;\n return h;\n}\n\nfunction seedColor(seed: string): string {\n return AVATAR_TOKENS[hashSeed(seed) % AVATAR_TOKENS.length];\n}\n\nfunction initialsFrom(label: string, seed: string): string {\n const words = label\n .trim()\n .split(/[\\s._-]+/)\n .filter(Boolean);\n if (words.length >= 2) return (words[0][0] + words[1][0]).toUpperCase();\n if (words.length === 1 && words[0].length >= 2) return words[0].slice(0, 2).toUpperCase();\n // Fall back to a stable 2-char monogram derived from the id.\n const hex = hashSeed(seed).toString(36).toUpperCase().padStart(2, 'X');\n return hex.slice(0, 2);\n}\n\nfunction Avatar({\n seed,\n label,\n imageUrl,\n size = 32,\n}: {\n seed: string;\n label: string;\n imageUrl?: string | null;\n size?: number;\n}) {\n const color = seedColor(seed);\n const dimension = { width: size, height: size };\n if (imageUrl) {\n return (\n <img\n src={imageUrl}\n alt=\"\"\n style={dimension}\n className=\"shrink-0 rounded-full border border-border-subtle object-cover\"\n />\n );\n }\n return (\n <span\n aria-hidden=\"true\"\n style={{\n ...dimension,\n color,\n backgroundColor: `color-mix(in srgb, ${color} 16%, transparent)`,\n borderColor: `color-mix(in srgb, ${color} 32%, transparent)`,\n }}\n className=\"flex shrink-0 items-center justify-center rounded-full border text-xs font-semibold\"\n >\n {initialsFrom(label, seed)}\n </span>\n );\n}\n\nexport function CommentsPanel({ entityType, entityId }: CommentsPanelProps) {\n const { currentUser } = useAssetHandler();\n const [draft, setDraft] = useState('');\n const [replyTo, setReplyTo] = useState<string | null>(null);\n const [replyDraft, setReplyDraft] = useState('');\n const [editing, setEditing] = useState<string | null>(null);\n const [editDraft, setEditDraft] = useState('');\n\n const currentUserName =\n currentUser?.name ||\n [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ') ||\n currentUser?.username ||\n currentUser?.email?.split('@')[0] ||\n 'You';\n\n const { data, loading, refetch } = useQuery<EntityCommentsResult>(ENTITY_COMMENTS_QUERY, {\n variables: {\n input: {\n entityType,\n entityId,\n take: 100,\n sortBy: 'createdAt',\n sortOrder: 'asc',\n },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n skip: !entityId,\n });\n\n const [createComment, { loading: posting }] = useMutation(CREATE_ENTITY_COMMENT_MUTATION, {\n onCompleted: () => {\n notify.success('Comment added', 'Your comment is now part of the thread.');\n void refetch();\n },\n onError: (error) => notify.error('Could not post comment', error.message),\n });\n const [updateComment, { loading: updating }] = useMutation(UPDATE_ENTITY_COMMENT_MUTATION, {\n onError: (error) => notify.error('Could not update comment', error.message),\n });\n const [deleteComment] = useMutation(DELETE_ENTITY_COMMENT_MUTATION, {\n onError: (error) => notify.error('Could not delete comment', error.message),\n });\n\n const allComments = useMemo(() => data?.getEntityComments.items ?? [], [data]);\n\n // Group replies under their parent to render a lightweight thread.\n const { roots, repliesByParent } = useMemo(() => {\n const byParent = new Map<string, EntityCommentNode[]>();\n const top: EntityCommentNode[] = [];\n for (const c of allComments) {\n if (c.parentCommentId) {\n const list = byParent.get(c.parentCommentId) ?? [];\n list.push(c);\n byParent.set(c.parentCommentId, list);\n } else {\n top.push(c);\n }\n }\n return { roots: top, repliesByParent: byParent };\n }, [allComments]);\n\n const authorLabel = (authorId: string): string =>\n currentUser?.id && authorId === currentUser.id\n ? currentUserName\n : `Member ${authorId.slice(0, 6)}`;\n\n const submitRoot = (event: React.FormEvent) => {\n event.preventDefault();\n const text = draft.trim();\n if (!text) {\n notify.warning('Nothing to post', 'Write a comment before submitting.');\n return;\n }\n void createComment({\n variables: {\n input: { entityType, entityId, contentType: 'TEXT', content: { text } },\n },\n }).then((res) => {\n if (res.data) setDraft('');\n });\n };\n\n const submitReply = (parentCommentId: string) => {\n const text = replyDraft.trim();\n if (!text) return;\n void createComment({\n variables: {\n input: { entityType, entityId, contentType: 'TEXT', content: { text }, parentCommentId },\n },\n }).then((res) => {\n if (res.data) {\n setReplyDraft('');\n setReplyTo(null);\n }\n });\n };\n\n const submitEdit = (id: string) => {\n const text = editDraft.trim();\n if (!text) return;\n void updateComment({\n variables: { input: { id, contentType: 'TEXT', content: { text } } },\n }).then((res) => {\n if (res.data) {\n setEditing(null);\n setEditDraft('');\n notify.success('Comment updated', 'Your changes have been saved.');\n void refetch();\n }\n });\n };\n\n const removeComment = (id: string) => {\n void deleteComment({ variables: { input: { id, soft: true } } }).then((res) => {\n if (res.data) {\n notify.success('Comment removed', 'The comment has been deleted from the thread.');\n void refetch();\n }\n });\n };\n\n const renderComment = (comment: EntityCommentNode, isReply: boolean) => {\n const mine = Boolean(currentUser?.id && comment.authorId === currentUser.id);\n const name = authorLabel(comment.authorId);\n const text = comment.content?.text ?? comment.content?.markdown ?? '';\n const isEditing = editing === comment.id;\n return (\n <div key={comment.id} className=\"flex gap-2.5\">\n <Avatar\n seed={comment.authorId}\n label={name}\n imageUrl={mine ? currentUser?.avatar : undefined}\n size={isReply ? 28 : 32}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"rounded-2xl rounded-tl-sm border border-border-subtle bg-bg-sunken px-3 py-2\">\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-1.5\">\n <span className=\"truncate text-sm font-semibold text-text-primary\">{name}</span>\n {mine ? (\n <span className=\"rounded-full bg-status-info-bg-subtle px-1.5 py-0.5 text-[0.625rem] font-medium text-status-info-text\">\n You\n </span>\n ) : null}\n </div>\n {mine && !isEditing ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n className=\"-mr-1 rounded-md p-1 text-text-secondary hover:bg-bg-surface hover:text-text-primary\"\n aria-label=\"Comment actions\"\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onSelect={() => {\n setEditing(comment.id);\n setEditDraft(text);\n }}\n >\n <Pencil className=\"mr-2 h-4 w-4\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"text-status-error-text\"\n onSelect={() => removeComment(comment.id)}\n >\n <Trash2 className=\"mr-2 h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </div>\n\n {isEditing ? (\n <form\n className=\"mt-1.5 flex items-center gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n submitEdit(comment.id);\n }}\n >\n <Input\n value={editDraft}\n onChange={(e) => setEditDraft(e.target.value)}\n aria-label=\"Edit comment\"\n autoFocus\n className=\"h-8\"\n />\n <Button\n type=\"submit\"\n size=\"sm\"\n variant=\"ghost\"\n disabled={updating || !editDraft.trim()}\n aria-label=\"Save edit\"\n >\n <Check className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setEditing(null);\n setEditDraft('');\n }}\n aria-label=\"Cancel edit\"\n >\n <X className=\"h-4 w-4\" />\n </Button>\n </form>\n ) : (\n <p className=\"mt-0.5 whitespace-pre-wrap break-words text-sm text-text-secondary\">\n {text}\n </p>\n )}\n </div>\n\n <div className=\"mt-1 flex flex-wrap items-center gap-2 px-1 text-xs text-text-secondary\">\n <span title={new Date(comment.createdAt).toLocaleString()}>\n {relativeTime(comment.createdAt)}\n </span>\n {comment.edited ? <span>· edited</span> : null}\n {!isReply ? (\n <button\n type=\"button\"\n className=\"font-medium text-text-secondary hover:text-text-primary\"\n onClick={() => {\n setReplyTo(replyTo === comment.id ? null : comment.id);\n setReplyDraft('');\n }}\n >\n Reply\n </button>\n ) : null}\n </div>\n\n {replyTo === comment.id ? (\n <form\n className=\"mt-2 flex items-center gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n submitReply(comment.id);\n }}\n >\n <CornerDownRight\n className=\"h-4 w-4 shrink-0 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <Input\n value={replyDraft}\n onChange={(e) => setReplyDraft(e.target.value)}\n placeholder={`Reply to ${name}…`}\n aria-label={`Reply to ${name}`}\n autoFocus\n className=\"h-9\"\n />\n <Button type=\"submit\" size=\"sm\" disabled={posting || !replyDraft.trim()}>\n Reply\n </Button>\n </form>\n ) : null}\n </div>\n </div>\n );\n };\n\n const total = data?.getEntityComments.total ?? 0;\n const hasComments = roots.length > 0;\n\n return (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <MessageSquare className=\"h-4 w-4\" />\n Discussion\n {total ? (\n <span className=\"text-sm font-normal text-text-secondary\">\n {total} comment{total === 1 ? '' : 's'}\n </span>\n ) : null}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {loading && !hasComments ? (\n <div className=\"space-y-3\">\n {Array.from({ length: 3 }).map((_, i) => (\n <div\n key={i}\n className=\"h-16 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : !hasComments ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"No comments yet\"\n description=\"Start the conversation — notes and decisions logged here stay with the record.\"\n />\n ) : (\n <ul className=\"space-y-4\">\n {roots.map((comment) => {\n const replies = repliesByParent.get(comment.id) ?? [];\n return (\n <li key={comment.id} className=\"space-y-3\">\n {renderComment(comment, false)}\n {replies.length > 0 ? (\n <ul className=\"space-y-3 border-l border-border-subtle pl-4 sm:ml-6\">\n {replies.map((reply) => (\n <li key={reply.id}>{renderComment(reply, true)}</li>\n ))}\n </ul>\n ) : null}\n </li>\n );\n })}\n </ul>\n )}\n\n {/* Slim inline composer at the bottom (matches the reference concept). */}\n <form\n className=\"flex items-center gap-2 border-t border-border-subtle pt-3\"\n onSubmit={submitRoot}\n >\n <Avatar\n seed={currentUser?.id ?? 'me'}\n label={currentUserName}\n imageUrl={currentUser?.avatar}\n size={32}\n />\n <Input\n placeholder=\"Write a comment…\"\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n aria-label=\"New comment\"\n />\n <Button type=\"submit\" size=\"sm\" disabled={posting || !draft.trim()}>\n <Send className=\"mr-1.5 h-3.5 w-3.5\" />\n {posting ? 'Posting…' : 'Post'}\n </Button>\n </form>\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotFoundState.js","names":[],"sources":["../../../src/assethandler/components/NotFoundState.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\n\n/**\n * Standard \"record not found\" panel for detail pages.\n *\n * Detail pages fetch a single record by id (e.g. `budget(id)`, `invoice(id)`).\n * When the by-id query returns `null` — a wrong/stale id, a cross-workspace id,\n * or a soft-deleted record — pages render this instead of silently falling back\n * to `array[0]` (the historic wrong-record bug). B2-2 detail pages reuse it.\n */\nexport interface NotFoundStateProps {\n /** What was not found, e.g. \"Invoice\", \"Work order\". */\n entityLabel: string;\n /** Optional override for the description line. */\n description?: string;\n /** Label for the primary action button. */\n backLabel?: string;\n /** Handler for the primary action (typically navigate back to the list). */\n onBack?: () => void;\n /** Optional extra action rendered next to the back button. */\n action?: ReactNode;\n}\n\nexport function NotFoundState({\n entityLabel,\n description,\n backLabel = 'Go back',\n onBack,\n action,\n}: NotFoundStateProps) {\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={`${entityLabel} not found`}\n description={\n description ??\n `This ${entityLabel.toLowerCase()} may have been removed, or the link is out of date. Head back to the list to find what you need.`\n }\n action={\n <div className=\"flex items-center gap-2\">\n {onBack ? (\n <Button size=\"sm\" onClick={onBack}>\n {backLabel}\n </Button>\n ) : null}\n {action}\n </div>\n }\n />\n </div>\n );\n}\n"],"mappings":";;;AAwBA,SAAgB,EAAc,EAC5B,gBACA,gBACA,eAAY,WACZ,WACA,aACqB;
|
|
1
|
+
{"version":3,"file":"NotFoundState.js","names":[],"sources":["../../../src/assethandler/components/NotFoundState.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\n\n/**\n * Standard \"record not found\" panel for detail pages.\n *\n * Detail pages fetch a single record by id (e.g. `budget(id)`, `invoice(id)`).\n * When the by-id query returns `null` — a wrong/stale id, a cross-workspace id,\n * or a soft-deleted record — pages render this instead of silently falling back\n * to `array[0]` (the historic wrong-record bug). B2-2 detail pages reuse it.\n */\nexport interface NotFoundStateProps {\n /** What was not found, e.g. \"Invoice\", \"Work order\". */\n entityLabel: string;\n /** Optional override for the description line. */\n description?: string;\n /** Label for the primary action button. */\n backLabel?: string;\n /** Handler for the primary action (typically navigate back to the list). */\n onBack?: () => void;\n /** Optional extra action rendered next to the back button. */\n action?: ReactNode;\n}\n\nexport function NotFoundState({\n entityLabel,\n description,\n backLabel = 'Go back',\n onBack,\n action,\n}: NotFoundStateProps) {\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={`${entityLabel} not found`}\n description={\n description ??\n `This ${entityLabel.toLowerCase()} may have been removed, or the link is out of date. Head back to the list to find what you need.`\n }\n action={\n <div className=\"flex items-center gap-2\">\n {onBack ? (\n <Button size=\"sm\" onClick={onBack}>\n {backLabel}\n </Button>\n ) : null}\n {action}\n </div>\n }\n />\n </div>\n );\n}\n"],"mappings":";;;AAwBA,SAAgB,EAAc,EAC5B,gBACA,gBACA,eAAY,WACZ,WACA,aACqB;CACrB,OACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,GAAG,EAAY;GACtB,aACE,KACA,QAAQ,EAAY,YAAY,EAAE;GAEpC,QACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,IACC,kBAAC,GAAD;KAAQ,MAAK;KAAK,SAAS;eACxB;IACK,CAAA,IACN,MACH,CACE;;EAER,CAAA;CACE,CAAA;AAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pagination.js","names":[],"sources":["../../../src/assethandler/components/Pagination.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\n\n/**\n * Cursor/limit pagination for AssetHandler lists.\n *\n * The product's bootstrap query hydrates full domain collections into the\n * store, so list pages paginate those arrays client-side with `usePagination`.\n * Server-backed register queries (assetRegister, maintenanceLog) accept\n * `AssetHandlerPaginationInput { cursor, limit }` — the `limit` here maps to\n * that input's page size and `page` drives the offset window.\n *\n * B2-2 pages import both `Pagination` and `usePagination` from the components\n * barrel so every list in the product shares one pagination contract.\n */\nexport interface AssetHandlerPaginationInput {\n cursor?: string | null;\n limit?: number;\n}\n\nexport interface UsePaginationResult<T> {\n page: number;\n pageSize: number;\n pageCount: number;\n pageItems: T[];\n totalItems: number;\n /** 1-based index of the first item on the current page (0 when empty). */\n rangeStart: number;\n /** 1-based index of the last item on the current page. */\n rangeEnd: number;\n canPrev: boolean;\n canNext: boolean;\n setPage: (page: number) => void;\n nextPage: () => void;\n prevPage: () => void;\n setPageSize: (size: number) => void;\n}\n\nexport interface UsePaginationOptions {\n pageSize?: number;\n /**\n * When this value changes (e.g. an active filter or search term), the current\n * page resets to 1 so the user is never stranded on an out-of-range page.\n */\n resetKey?: unknown;\n}\n\nconst DEFAULT_PAGE_SIZE = 10;\n\nexport function usePagination<T>(\n items: T[],\n options: UsePaginationOptions = {}\n): UsePaginationResult<T> {\n const { pageSize: initialPageSize = DEFAULT_PAGE_SIZE, resetKey } = options;\n const [page, setPageState] = useState(1);\n const [pageSize, setPageSizeState] = useState(initialPageSize);\n\n const totalItems = items.length;\n const pageCount = Math.max(1, Math.ceil(totalItems / pageSize));\n\n // Clamp the page into range whenever the dataset, page size, or resetKey shifts.\n const safePage = useMemo(() => {\n void resetKey;\n return Math.min(Math.max(1, page), pageCount);\n }, [page, pageCount, resetKey]);\n\n const pageItems = useMemo(() => {\n const start = (safePage - 1) * pageSize;\n return items.slice(start, start + pageSize);\n }, [items, safePage, pageSize]);\n\n const rangeStart = totalItems === 0 ? 0 : (safePage - 1) * pageSize + 1;\n const rangeEnd = Math.min(safePage * pageSize, totalItems);\n\n const setPage = (next: number) => setPageState(Math.min(Math.max(1, next), pageCount));\n const setPageSize = (size: number) => {\n setPageSizeState(size);\n setPageState(1);\n };\n\n return {\n page: safePage,\n pageSize,\n pageCount,\n pageItems,\n totalItems,\n rangeStart,\n rangeEnd,\n canPrev: safePage > 1,\n canNext: safePage < pageCount,\n setPage,\n nextPage: () => setPage(safePage + 1),\n prevPage: () => setPage(safePage - 1),\n setPageSize,\n };\n}\n\nexport interface PaginationProps {\n page: number;\n pageCount: number;\n rangeStart: number;\n rangeEnd: number;\n totalItems: number;\n canPrev: boolean;\n canNext: boolean;\n onPrev: () => void;\n onNext: () => void;\n /** Plural noun for the range label, e.g. \"assets\". Defaults to \"items\". */\n itemLabel?: string;\n className?: string;\n}\n\nexport function Pagination({\n page,\n pageCount,\n rangeStart,\n rangeEnd,\n totalItems,\n canPrev,\n canNext,\n onPrev,\n onNext,\n itemLabel = 'items',\n className,\n}: PaginationProps) {\n if (totalItems === 0) return null;\n\n return (\n <div\n className={`flex flex-wrap items-center justify-between gap-3 pt-4 ${className ?? ''}`}\n role=\"navigation\"\n aria-label=\"Pagination\"\n >\n <p className=\"text-xs text-text-secondary tabular-nums\">\n Showing {rangeStart}–{rangeEnd} of {totalItems} {itemLabel}\n </p>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-text-secondary tabular-nums\">\n Page {page} of {pageCount}\n </span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={onPrev}\n disabled={!canPrev}\n aria-label=\"Previous page\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n Prev\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={onNext}\n disabled={!canNext}\n aria-label=\"Next page\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAgDA,IAAM,IAAoB;AAE1B,SAAgB,EACd,GACA,IAAgC,
|
|
1
|
+
{"version":3,"file":"Pagination.js","names":[],"sources":["../../../src/assethandler/components/Pagination.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\n\n/**\n * Cursor/limit pagination for AssetHandler lists.\n *\n * The product's bootstrap query hydrates full domain collections into the\n * store, so list pages paginate those arrays client-side with `usePagination`.\n * Server-backed register queries (assetRegister, maintenanceLog) accept\n * `AssetHandlerPaginationInput { cursor, limit }` — the `limit` here maps to\n * that input's page size and `page` drives the offset window.\n *\n * B2-2 pages import both `Pagination` and `usePagination` from the components\n * barrel so every list in the product shares one pagination contract.\n */\nexport interface AssetHandlerPaginationInput {\n cursor?: string | null;\n limit?: number;\n}\n\nexport interface UsePaginationResult<T> {\n page: number;\n pageSize: number;\n pageCount: number;\n pageItems: T[];\n totalItems: number;\n /** 1-based index of the first item on the current page (0 when empty). */\n rangeStart: number;\n /** 1-based index of the last item on the current page. */\n rangeEnd: number;\n canPrev: boolean;\n canNext: boolean;\n setPage: (page: number) => void;\n nextPage: () => void;\n prevPage: () => void;\n setPageSize: (size: number) => void;\n}\n\nexport interface UsePaginationOptions {\n pageSize?: number;\n /**\n * When this value changes (e.g. an active filter or search term), the current\n * page resets to 1 so the user is never stranded on an out-of-range page.\n */\n resetKey?: unknown;\n}\n\nconst DEFAULT_PAGE_SIZE = 10;\n\nexport function usePagination<T>(\n items: T[],\n options: UsePaginationOptions = {}\n): UsePaginationResult<T> {\n const { pageSize: initialPageSize = DEFAULT_PAGE_SIZE, resetKey } = options;\n const [page, setPageState] = useState(1);\n const [pageSize, setPageSizeState] = useState(initialPageSize);\n\n const totalItems = items.length;\n const pageCount = Math.max(1, Math.ceil(totalItems / pageSize));\n\n // Clamp the page into range whenever the dataset, page size, or resetKey shifts.\n const safePage = useMemo(() => {\n void resetKey;\n return Math.min(Math.max(1, page), pageCount);\n }, [page, pageCount, resetKey]);\n\n const pageItems = useMemo(() => {\n const start = (safePage - 1) * pageSize;\n return items.slice(start, start + pageSize);\n }, [items, safePage, pageSize]);\n\n const rangeStart = totalItems === 0 ? 0 : (safePage - 1) * pageSize + 1;\n const rangeEnd = Math.min(safePage * pageSize, totalItems);\n\n const setPage = (next: number) => setPageState(Math.min(Math.max(1, next), pageCount));\n const setPageSize = (size: number) => {\n setPageSizeState(size);\n setPageState(1);\n };\n\n return {\n page: safePage,\n pageSize,\n pageCount,\n pageItems,\n totalItems,\n rangeStart,\n rangeEnd,\n canPrev: safePage > 1,\n canNext: safePage < pageCount,\n setPage,\n nextPage: () => setPage(safePage + 1),\n prevPage: () => setPage(safePage - 1),\n setPageSize,\n };\n}\n\nexport interface PaginationProps {\n page: number;\n pageCount: number;\n rangeStart: number;\n rangeEnd: number;\n totalItems: number;\n canPrev: boolean;\n canNext: boolean;\n onPrev: () => void;\n onNext: () => void;\n /** Plural noun for the range label, e.g. \"assets\". Defaults to \"items\". */\n itemLabel?: string;\n className?: string;\n}\n\nexport function Pagination({\n page,\n pageCount,\n rangeStart,\n rangeEnd,\n totalItems,\n canPrev,\n canNext,\n onPrev,\n onNext,\n itemLabel = 'items',\n className,\n}: PaginationProps) {\n if (totalItems === 0) return null;\n\n return (\n <div\n className={`flex flex-wrap items-center justify-between gap-3 pt-4 ${className ?? ''}`}\n role=\"navigation\"\n aria-label=\"Pagination\"\n >\n <p className=\"text-xs text-text-secondary tabular-nums\">\n Showing {rangeStart}–{rangeEnd} of {totalItems} {itemLabel}\n </p>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-text-secondary tabular-nums\">\n Page {page} of {pageCount}\n </span>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={onPrev}\n disabled={!canPrev}\n aria-label=\"Previous page\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n Prev\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={onNext}\n disabled={!canNext}\n aria-label=\"Next page\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAgDA,IAAM,IAAoB;AAE1B,SAAgB,EACd,GACA,IAAgC,CAAC,GACT;CACxB,IAAM,EAAE,UAAU,IAAkB,GAAmB,gBAAa,GAC9D,CAAC,GAAM,KAAgB,EAAS,CAAC,GACjC,CAAC,GAAU,KAAoB,EAAS,CAAe,GAEvD,IAAa,EAAM,QACnB,IAAY,KAAK,IAAI,GAAG,KAAK,KAAK,IAAa,CAAQ,CAAC,GAGxD,IAAW,QAER,KAAK,IAAI,KAAK,IAAI,GAAG,CAAI,GAAG,CAAS,GAC3C;EAAC;EAAM;EAAW;CAAQ,CAAC,GAExB,IAAY,QAAc;EAC9B,IAAM,KAAS,IAAW,KAAK;EAC/B,OAAO,EAAM,MAAM,GAAO,IAAQ,CAAQ;CAC5C,GAAG;EAAC;EAAO;EAAU;CAAQ,CAAC,GAExB,IAAa,MAAe,IAAI,KAAK,IAAW,KAAK,IAAW,GAChE,IAAW,KAAK,IAAI,IAAW,GAAU,CAAU,GAEnD,KAAW,MAAiB,EAAa,KAAK,IAAI,KAAK,IAAI,GAAG,CAAI,GAAG,CAAS,CAAC;CAMrF,OAAO;EACL,MAAM;EACN;EACA;EACA;EACA;EACA;EACA;EACA,SAAS,IAAW;EACpB,SAAS,IAAW;EACpB;EACA,gBAAgB,EAAQ,IAAW,CAAC;EACpC,gBAAgB,EAAQ,IAAW,CAAC;EACpC,cAlBmB,MAAiB;GAEpC,AADA,EAAiB,CAAI,GACrB,EAAa,CAAC;EAChB;CAgBA;AACF;AAiBA,SAAgB,EAAW,EACzB,SACA,cACA,eACA,aACA,eACA,YACA,YACA,WACA,WACA,eAAY,SACZ,gBACkB;CAGlB,OAFI,MAAe,IAAU,OAG3B,kBAAC,OAAD;EACE,WAAW,0DAA0D,KAAa;EAClF,MAAK;EACL,cAAW;YAHb,CAKE,kBAAC,KAAD;GAAG,WAAU;aAAb;IAAwD;IAC7C;IAAW;IAAE;IAAS;IAAK;IAAW;IAAE;GAChD;MACH,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,QAAD;KAAM,WAAU;eAAhB;MAA2D;MACnD;MAAK;MAAK;KACZ;;IACN,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,SAAS;KACT,UAAU,CAAC;KACX,cAAW;eALb,CAOE,kBAAC,GAAD,EAAa,WAAU,UAAW,CAAA,GAAC,MAE7B;;IACR,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,SAAS;KACT,UAAU,CAAC;KACX,cAAW;eALb,CAMC,QAEC,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA,CAC7B;;GACL;IACF;;AAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SectionHeader.js","names":[],"sources":["../../../src/assethandler/components/SectionHeader.tsx"],"sourcesContent":["import type { ComponentType, ReactNode } from 'react';\nimport { GradientText } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\nexport interface SectionHeaderProps {\n /** The heading text. */\n title: ReactNode;\n /** Optional supporting description under the title. */\n description?: ReactNode;\n /** Optional right-aligned actions (buttons, toggles). */\n actions?: ReactNode;\n /** Optional leading icon beside the title. */\n icon?: ComponentType<{ className?: string }>;\n /** Render the title as a gradient hero heading — use once per page. */\n hero?: boolean;\n /** Bottom margin. Default true. */\n spaced?: boolean;\n className?: string;\n}\n\n/**\n * SectionHeader — a consistent title / description / actions row for pages and\n * sections. `hero` promotes the title to a brand `GradientText` heading (reserve\n * it for the one lead heading per page); otherwise it renders a calm section\n * title. Token-only.\n */\nexport function SectionHeader({\n title,\n description,\n actions,\n icon: Icon,\n hero = false,\n spaced = true,\n className,\n}: SectionHeaderProps) {\n return (\n <div\n className={cn(\n 'flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between',\n spaced && 'mb-6',\n className\n )}\n >\n <div className=\"flex min-w-0 items-start gap-2.5\">\n {Icon ? (\n <Icon\n className={cn('shrink-0 text-text-secondary', hero ? 'mt-1 h-6 w-6' : 'mt-0.5 h-5 w-5')}\n />\n ) : null}\n <div className=\"min-w-0\">\n {hero ? (\n <GradientText as=\"h1\" className=\"text-2xl font-bold sm:text-3xl\">\n {title}\n </GradientText>\n ) : (\n <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>\n )}\n {description ? (\n <p className=\"mt-1 max-w-2xl text-sm text-text-secondary\">{description}</p>\n ) : null}\n </div>\n </div>\n {actions ? <div className=\"flex shrink-0 flex-wrap items-center gap-2\">{actions}</div> : null}\n </div>\n );\n}\n"],"mappings":";;;;AA0BA,SAAgB,EAAc,EAC5B,UACA,gBACA,YACA,MAAM,GACN,UAAO,IACP,YAAS,IACT,gBACqB;
|
|
1
|
+
{"version":3,"file":"SectionHeader.js","names":[],"sources":["../../../src/assethandler/components/SectionHeader.tsx"],"sourcesContent":["import type { ComponentType, ReactNode } from 'react';\nimport { GradientText } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\nexport interface SectionHeaderProps {\n /** The heading text. */\n title: ReactNode;\n /** Optional supporting description under the title. */\n description?: ReactNode;\n /** Optional right-aligned actions (buttons, toggles). */\n actions?: ReactNode;\n /** Optional leading icon beside the title. */\n icon?: ComponentType<{ className?: string }>;\n /** Render the title as a gradient hero heading — use once per page. */\n hero?: boolean;\n /** Bottom margin. Default true. */\n spaced?: boolean;\n className?: string;\n}\n\n/**\n * SectionHeader — a consistent title / description / actions row for pages and\n * sections. `hero` promotes the title to a brand `GradientText` heading (reserve\n * it for the one lead heading per page); otherwise it renders a calm section\n * title. Token-only.\n */\nexport function SectionHeader({\n title,\n description,\n actions,\n icon: Icon,\n hero = false,\n spaced = true,\n className,\n}: SectionHeaderProps) {\n return (\n <div\n className={cn(\n 'flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between',\n spaced && 'mb-6',\n className\n )}\n >\n <div className=\"flex min-w-0 items-start gap-2.5\">\n {Icon ? (\n <Icon\n className={cn('shrink-0 text-text-secondary', hero ? 'mt-1 h-6 w-6' : 'mt-0.5 h-5 w-5')}\n />\n ) : null}\n <div className=\"min-w-0\">\n {hero ? (\n <GradientText as=\"h1\" className=\"text-2xl font-bold sm:text-3xl\">\n {title}\n </GradientText>\n ) : (\n <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>\n )}\n {description ? (\n <p className=\"mt-1 max-w-2xl text-sm text-text-secondary\">{description}</p>\n ) : null}\n </div>\n </div>\n {actions ? <div className=\"flex shrink-0 flex-wrap items-center gap-2\">{actions}</div> : null}\n </div>\n );\n}\n"],"mappings":";;;;AA0BA,SAAgB,EAAc,EAC5B,UACA,gBACA,YACA,MAAM,GACN,UAAO,IACP,YAAS,IACT,gBACqB;CACrB,OACE,kBAAC,OAAD;EACE,WAAW,EACT,qEACA,KAAU,QACV,CACF;YALF,CAOE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,IACC,kBAAC,GAAD,EACE,WAAW,EAAG,gCAAgC,IAAO,iBAAiB,gBAAgB,EACvF,CAAA,IACC,MACJ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,IACC,kBAAC,GAAD;KAAc,IAAG;KAAK,WAAU;eAC7B;IACW,CAAA,IAEd,kBAAC,MAAD;KAAI,WAAU;eAA2C;IAAU,CAAA,GAEpE,IACC,kBAAC,KAAD;KAAG,WAAU;eAA8C;IAAe,CAAA,IACxE,IACD;KACF;MACJ,IAAU,kBAAC,OAAD;GAAK,WAAU;aAA8C;EAAa,CAAA,IAAI,IACtF;;AAET"}
|