{"version":3,"file":"index-ByFlwzpD.cjs","sources":["../src/app/hooks/useWorkspaceData.js","../src/app/components/DashboardCard.jsx","../src/app/components/NodeComponents.jsx","../src/app/pages/MyWorkspacePage/NotificationsSection.jsx","../src/app/pages/MyWorkspacePage/NeedsAttentionSection.jsx","../src/app/pages/MyWorkspacePage/NodeList.jsx","../src/app/pages/MyWorkspacePage/TypeGroupGrid.jsx","../src/app/pages/MyWorkspacePage/MyNodesSection.jsx","../src/app/pages/MyWorkspacePage/MaterializationsSection.jsx","../src/app/pages/MyWorkspacePage/ActiveBranchesSection.jsx","../src/app/pages/MyWorkspacePage/index.jsx"],"sourcesContent":["import { useContext, useEffect, useState } from 'react';\nimport DJClientContext from '../providers/djclient';\n\n/**\n * Hook to fetch the current user\n */\nexport function useCurrentUser() {\n  const djClient = useContext(DJClientContext).DataJunctionAPI;\n  const [data, setData] = useState(null);\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState(null);\n\n  useEffect(() => {\n    const fetchUser = async () => {\n      try {\n        const user = await djClient.whoami();\n        setData(user);\n      } catch (err) {\n        console.error('Error fetching user:', err);\n        setError(err);\n      }\n      setLoading(false);\n    };\n    fetchUser();\n  }, [djClient]);\n\n  return { data, loading, error };\n}\n\n/**\n * Hook to fetch workspace owned nodes\n */\nexport function useWorkspaceOwnedNodes(username, limit = 5000) {\n  const djClient = useContext(DJClientContext).DataJunctionAPI;\n  const [data, setData] = useState([]);\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState(null);\n\n  useEffect(() => {\n    if (!username) {\n      setLoading(false);\n      return;\n    }\n\n    const fetchData = async () => {\n      try {\n        const result = await djClient.getWorkspaceOwnedNodes(username, limit);\n        setData(\n          result?.data?.findNodesPaginated?.edges?.map(e => e.node) || [],\n        );\n      } catch (err) {\n        console.error('Error fetching owned nodes:', err);\n        setError(err);\n      }\n      setLoading(false);\n    };\n    fetchData();\n  }, [djClient, username, limit]);\n\n  return { data, loading, error };\n}\n\n/**\n * Hook to fetch workspace recently edited nodes\n */\nexport function useWorkspaceRecentlyEdited(username, limit = 5000) {\n  const djClient = useContext(DJClientContext).DataJunctionAPI;\n  const [data, setData] = useState([]);\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState(null);\n\n  useEffect(() => {\n    if (!username) {\n      setLoading(false);\n      return;\n    }\n\n    const fetchData = async () => {\n      try {\n        const result = await djClient.getWorkspaceRecentlyEdited(\n          username,\n          limit,\n        );\n        setData(\n          result?.data?.findNodesPaginated?.edges?.map(e => e.node) || [],\n        );\n      } catch (err) {\n        console.error('Error fetching recently edited nodes:', err);\n        setError(err);\n      }\n      setLoading(false);\n    };\n    fetchData();\n  }, [djClient, username, limit]);\n\n  return { data, loading, error };\n}\n\n/**\n * Hook to fetch watched nodes (notification subscriptions)\n */\nexport function useWorkspaceWatchedNodes(username) {\n  const djClient = useContext(DJClientContext).DataJunctionAPI;\n  const [data, setData] = useState([]);\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState(null);\n\n  useEffect(() => {\n    if (!username) {\n      setLoading(false);\n      return;\n    }\n\n    const fetchData = async () => {\n      try {\n        const subscriptions = await djClient.getNotificationPreferences({\n          entity_type: 'node',\n        });\n        const watchedNodeNames = (subscriptions || []).map(s => s.entity_name);\n\n        if (watchedNodeNames.length > 0) {\n          const nodes = await djClient.getNodesByNames(watchedNodeNames);\n          setData(nodes || []);\n        } else {\n          setData([]);\n        }\n      } catch (err) {\n        console.error('Error fetching watched nodes:', err);\n        setError(err);\n      }\n      setLoading(false);\n    };\n    fetchData();\n  }, [djClient, username]);\n\n  return { data, loading, error };\n}\n\n/**\n * Hook to fetch workspace collections\n */\nexport function useWorkspaceCollections(username) {\n  const djClient = useContext(DJClientContext).DataJunctionAPI;\n  const [data, setData] = useState([]);\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState(null);\n\n  useEffect(() => {\n    if (!username) {\n      setLoading(false);\n      return;\n    }\n\n    const fetchData = async () => {\n      try {\n        const result = await djClient.getWorkspaceCollections(username);\n        setData(result?.data?.listCollections || []);\n      } catch (err) {\n        console.error('Error fetching collections:', err);\n        setError(err);\n      }\n      setLoading(false);\n    };\n    fetchData();\n  }, [djClient, username]);\n\n  return { data, loading, error };\n}\n\n/**\n * Hook to fetch workspace notifications\n */\nexport function useWorkspaceNotifications(username, limit = 50) {\n  const djClient = useContext(DJClientContext).DataJunctionAPI;\n  const [data, setData] = useState([]);\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState(null);\n\n  useEffect(() => {\n    if (!username) {\n      setLoading(false);\n      return;\n    }\n\n    const fetchData = async () => {\n      try {\n        const history = await djClient.getSubscribedHistory(limit);\n\n        // Enrich with node info\n        const nodeNames = Array.from(\n          new Set((history || []).map(h => h.entity_name)),\n        );\n        let nodeInfoMap = {};\n        if (nodeNames.length > 0) {\n          const nodes = await djClient.getNodesByNames(nodeNames);\n          nodeInfoMap = Object.fromEntries((nodes || []).map(n => [n.name, n]));\n        }\n\n        const enriched = (history || []).map(entry => ({\n          ...entry,\n          node_type: nodeInfoMap[entry.entity_name]?.type,\n          display_name: nodeInfoMap[entry.entity_name]?.current?.displayName,\n        }));\n\n        setData(enriched);\n      } catch (err) {\n        console.error('Error fetching notifications:', err);\n        setError(err);\n      }\n      setLoading(false);\n    };\n    fetchData();\n  }, [djClient, username, limit]);\n\n  return { data, loading, error };\n}\n\n/**\n * Hook to fetch workspace materializations\n */\nexport function useWorkspaceMaterializations(username, limit = 5000) {\n  const djClient = useContext(DJClientContext).DataJunctionAPI;\n  const [data, setData] = useState([]);\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState(null);\n\n  useEffect(() => {\n    if (!username) {\n      setLoading(false);\n      return;\n    }\n\n    const fetchData = async () => {\n      try {\n        const result = await djClient.getWorkspaceMaterializations(\n          username,\n          limit,\n        );\n        setData(\n          result?.data?.findNodesPaginated?.edges?.map(e => e.node) || [],\n        );\n      } catch (err) {\n        console.error('Error fetching materializations:', err);\n        setError(err);\n      }\n      setLoading(false);\n    };\n    fetchData();\n  }, [djClient, username, limit]);\n\n  return { data, loading, error };\n}\n\n/**\n * Hook to fetch all \"Needs Attention\" items\n * Returns an object with all actionable item categories\n */\nexport function useWorkspaceNeedsAttention(username) {\n  const djClient = useContext(DJClientContext).DataJunctionAPI;\n  const [data, setData] = useState({\n    nodesMissingDescription: [],\n    invalidNodes: [],\n    staleDrafts: [],\n    orphanedDimensions: [],\n  });\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState(null);\n\n  useEffect(() => {\n    if (!username) {\n      setLoading(false);\n      return;\n    }\n\n    const fetchData = async () => {\n      try {\n        const [missingDescData, invalidData, draftData, orphanedData] =\n          await Promise.all([\n            djClient.getWorkspaceNodesMissingDescription(username, 5000),\n            djClient.getWorkspaceInvalidNodes(username, 5000),\n            djClient.getWorkspaceDraftNodes(username, 5000),\n            djClient.getWorkspaceOrphanedDimensions(username, 5000),\n          ]);\n\n        const nodesMissingDescription =\n          missingDescData?.data?.findNodesPaginated?.edges?.map(e => e.node) ||\n          [];\n        const invalidNodes =\n          invalidData?.data?.findNodesPaginated?.edges?.map(e => e.node) || [];\n        const orphanedDimensions =\n          orphanedData?.data?.findNodesPaginated?.edges?.map(e => e.node) || [];\n\n        // Filter drafts older than 7 days\n        const sevenDaysAgo = new Date();\n        sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);\n        const allDrafts =\n          draftData?.data?.findNodesPaginated?.edges?.map(e => e.node) || [];\n        const staleDrafts = allDrafts.filter(node => {\n          const updatedAt = new Date(node.current?.updatedAt);\n          return updatedAt < sevenDaysAgo;\n        });\n\n        setData({\n          nodesMissingDescription,\n          invalidNodes,\n          staleDrafts,\n          orphanedDimensions,\n        });\n      } catch (err) {\n        console.error('Error fetching needs attention items:', err);\n        setError(err);\n      }\n      setLoading(false);\n    };\n    fetchData();\n  }, [djClient, username]);\n\n  return { data, loading, error };\n}\n\nconst toNodes = r => r?.data?.findNodesPaginated?.edges?.map(e => e.node) || [];\n\n/**\n * Fetches all workspace dashboard data in independent parallel groups so that\n * fast sections (My Nodes, Collections) render immediately while slower ones\n * (Needs Attention, namespace check) finish in the background.\n */\nexport function useWorkspaceDashboardData(username) {\n  const djClient = useContext(DJClientContext).DataJunctionAPI;\n\n  const [data, setData] = useState({\n    ownedNodes: [],\n    ownedHasMore: {},\n    recentlyEdited: [],\n    editedHasMore: {},\n    watchedNodes: [],\n    collections: [],\n    notifications: [],\n    materializedNodes: [],\n    needsAttention: {\n      nodesMissingDescription: [],\n      invalidNodes: [],\n      staleDrafts: [],\n      orphanedDimensions: [],\n    },\n    hasPersonalNamespace: null,\n  });\n\n  const [loadingStates, setLoadingStates] = useState({\n    myNodes: true,\n    collections: true,\n    notifications: true,\n    materializations: true,\n    needsAttention: true,\n    namespace: true,\n  });\n\n  useEffect(() => {\n    if (!username) {\n      setLoadingStates({\n        myNodes: false,\n        collections: false,\n        notifications: false,\n        materializations: false,\n        needsAttention: false,\n        namespace: false,\n      });\n      return;\n    }\n\n    // Reset all loading flags whenever the username changes (e.g. after initial login)\n    setLoadingStates({\n      myNodes: true,\n      collections: true,\n      notifications: true,\n      materializations: true,\n      needsAttention: true,\n      namespace: true,\n    });\n\n    // Group 1: Owned nodes + recently edited + collections\n    // Each type is queried independently with limit=11 so hasNextPage tells us if there are more.\n    const NODE_TYPES = ['METRIC', 'TRANSFORM', 'DIMENSION', 'CUBE'];\n    const hasNextPage = r =>\n      r?.data?.findNodesPaginated?.pageInfo?.hasNextPage ?? false;\n\n    const fetchMyNodesAndCollections = async () => {\n      try {\n        const [ownedByType, editedByType, collectionsResult] =\n          await Promise.all([\n            Promise.all(\n              NODE_TYPES.map(t =>\n                djClient.getWorkspaceOwnedNodes(username, 11, t),\n              ),\n            ),\n            Promise.all(\n              NODE_TYPES.map(t =>\n                djClient.getWorkspaceRecentlyEdited(username, 11, t),\n              ),\n            ),\n            djClient.getWorkspaceCollections(username),\n          ]);\n\n        const ownedHasMore = Object.fromEntries(\n          NODE_TYPES.map((t, i) => [\n            t.toLowerCase(),\n            hasNextPage(ownedByType[i]),\n          ]),\n        );\n        const editedHasMore = Object.fromEntries(\n          NODE_TYPES.map((t, i) => [\n            t.toLowerCase(),\n            hasNextPage(editedByType[i]),\n          ]),\n        );\n\n        setData(prev => ({\n          ...prev,\n          ownedNodes: ownedByType.flatMap(r => toNodes(r)),\n          ownedHasMore,\n          recentlyEdited: editedByType.flatMap(r => toNodes(r)),\n          editedHasMore,\n          collections: collectionsResult?.data?.listCollections || [],\n        }));\n      } catch (err) {\n        console.error('Error fetching nodes/collections:', err);\n      }\n      setLoadingStates(prev => ({\n        ...prev,\n        myNodes: false,\n        collections: false,\n      }));\n    };\n\n    // Group 2: Watched nodes + notifications (2 round-trips each, run in parallel)\n    const fetchWatchedAndNotifications = async () => {\n      try {\n        const [subscriptions, historyResult] = await Promise.all([\n          djClient.getNotificationPreferences({ entity_type: 'node' }),\n          djClient.getSubscribedHistory(50),\n        ]);\n\n        const watchedNodeNames = (subscriptions || []).map(s => s.entity_name);\n        const historyEntries = historyResult || [];\n        const notifNodeNames = Array.from(\n          new Set(historyEntries.map(h => h.entity_name)),\n        );\n\n        const [watchedNodes, notifNodes] = await Promise.all([\n          watchedNodeNames.length > 0\n            ? djClient.getNodesByNames(watchedNodeNames)\n            : Promise.resolve([]),\n          notifNodeNames.length > 0\n            ? djClient.getNodesByNames(notifNodeNames)\n            : Promise.resolve([]),\n        ]);\n\n        const notifNodeMap = Object.fromEntries(\n          (notifNodes || []).map(n => [n.name, n]),\n        );\n        const notifications = historyEntries.map(entry => ({\n          ...entry,\n          node_type: notifNodeMap[entry.entity_name]?.type,\n          display_name: notifNodeMap[entry.entity_name]?.current?.displayName,\n        }));\n\n        setData(prev => ({\n          ...prev,\n          watchedNodes: watchedNodes || [],\n          notifications,\n        }));\n      } catch (err) {\n        console.error('Error fetching watched/notifications:', err);\n      }\n      setLoadingStates(prev => ({ ...prev, notifications: false }));\n    };\n\n    // Group 3: Materializations (small limit — we only display 5)\n    const fetchMaterializations = async () => {\n      try {\n        const result = await djClient.getWorkspaceMaterializations(username, 6);\n        setData(prev => ({ ...prev, materializedNodes: toNodes(result) }));\n      } catch (err) {\n        console.error('Error fetching materializations:', err);\n      }\n      setLoadingStates(prev => ({ ...prev, materializations: false }));\n    };\n\n    // Group 4: Needs Attention + personal namespace check (slower, loads last)\n    const fetchNeedsAttentionAndNamespace = async () => {\n      try {\n        const usernameForNamespace = username.split('@')[0];\n        const personalNamespace = `users.${usernameForNamespace}`;\n\n        const [\n          missingDescResult,\n          invalidResult,\n          draftResult,\n          orphanedResult,\n          namespacesList,\n        ] = await Promise.all([\n          djClient.getWorkspaceNodesMissingDescription(username, 100),\n          djClient.getWorkspaceInvalidNodes(username, 100),\n          djClient.getWorkspaceDraftNodes(username, 100),\n          djClient.getWorkspaceOrphanedDimensions(username, 100),\n          djClient.namespaces(),\n        ]);\n\n        const sevenDaysAgo = new Date();\n        sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);\n        const allDrafts = toNodes(draftResult);\n\n        setData(prev => ({\n          ...prev,\n          needsAttention: {\n            nodesMissingDescription: toNodes(missingDescResult),\n            invalidNodes: toNodes(invalidResult),\n            staleDrafts: allDrafts.filter(\n              n => new Date(n.current?.updatedAt) < sevenDaysAgo,\n            ),\n            orphanedDimensions: toNodes(orphanedResult),\n          },\n          hasPersonalNamespace: (namespacesList || []).some(\n            ns => ns.namespace === personalNamespace,\n          ),\n        }));\n      } catch (err) {\n        console.error('Error fetching needs attention:', err);\n      }\n      setLoadingStates(prev => ({\n        ...prev,\n        needsAttention: false,\n        namespace: false,\n      }));\n    };\n\n    // All four groups fire independently — each resolves its own loading flag\n    fetchMyNodesAndCollections();\n    fetchWatchedAndNotifications();\n    fetchMaterializations();\n    fetchNeedsAttentionAndNamespace();\n  }, [djClient, username]);\n\n  return { data, loadingStates };\n}\n\n/**\n * Hook to check if personal namespace exists\n */\nexport function usePersonalNamespace(username) {\n  const djClient = useContext(DJClientContext).DataJunctionAPI;\n  const [exists, setExists] = useState(null);\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState(null);\n\n  useEffect(() => {\n    if (!username) {\n      setLoading(false);\n      return;\n    }\n\n    const checkNamespace = async () => {\n      try {\n        // Extract username without domain for namespace\n        const usernameForNamespace = username.split('@')[0];\n        const personalNamespace = `users.${usernameForNamespace}`;\n        const namespaces = await djClient.namespaces();\n        const namespaceExists = namespaces.some(\n          ns => ns.namespace === personalNamespace,\n        );\n        setExists(namespaceExists);\n      } catch (err) {\n        console.error('Error checking namespace:', err);\n        setError(err);\n        setExists(false);\n      }\n      setLoading(false);\n    };\n    checkNamespace();\n  }, [djClient, username]);\n\n  return { exists, loading, error };\n}\n","import * as React from 'react';\nimport { Link } from 'react-router-dom';\nimport LoadingIcon from '../icons/LoadingIcon';\n\n/**\n * Reusable card component for dashboard sections\n *\n * @param {Object} props\n * @param {string} props.title - Section title\n * @param {string} [props.actionLink] - Optional link URL for \"View All\" or other action\n * @param {string} [props.actionText] - Text for action link (defaults to \"View All →\")\n * @param {boolean} [props.loading] - Show loading spinner\n * @param {React.ReactNode} [props.emptyState] - Content to show when empty (if loading is false and no children)\n * @param {React.ReactNode} props.children - Card content\n * @param {Object} [props.cardStyle] - Additional styles for the card wrapper\n * @param {Object} [props.contentStyle] - Additional styles for the content area\n * @param {boolean} [props.showHeader] - Whether to show the header (defaults to true)\n */\nexport function DashboardCard({\n  title,\n  actionLink,\n  actionText = 'View All →',\n  loading = false,\n  emptyState = null,\n  children,\n  cardStyle = {},\n  contentStyle = {},\n  showHeader = true,\n}) {\n  // Check if children has actual content (not false, null, undefined)\n  const hasContent =\n    React.Children.toArray(children).filter(\n      child => child !== false && child !== null && child !== undefined,\n    ).length > 0;\n  const showEmptyState = !loading && !hasContent && emptyState;\n\n  return (\n    <section>\n      {showHeader && (\n        <div className=\"section-title-row\">\n          <h2 className=\"settings-section-title\">{title}</h2>\n          {actionLink && (\n            <Link to={actionLink} style={{ fontSize: '13px' }}>\n              {actionText}\n            </Link>\n          )}\n        </div>\n      )}\n      <div\n        className=\"settings-card\"\n        style={{\n          padding: '0.75rem',\n          ...cardStyle,\n          ...contentStyle,\n        }}\n      >\n        {loading ? (\n          <div style={{ textAlign: 'center', padding: '2rem' }}>\n            <LoadingIcon />\n          </div>\n        ) : showEmptyState ? (\n          emptyState\n        ) : (\n          children\n        )}\n      </div>\n    </section>\n  );\n}\n\n/**\n * Empty state component for dashboard cards\n */\nexport function DashboardCardEmpty({ icon, message, action }) {\n  return (\n    <div\n      style={{\n        padding: '2rem',\n        textAlign: 'center',\n        color: '#666',\n        fontSize: '12px',\n      }}\n    >\n      {icon && (\n        <div style={{ fontSize: '24px', marginBottom: '0.5rem' }}>{icon}</div>\n      )}\n      <p>{message}</p>\n      {action}\n    </div>\n  );\n}\n\nexport default DashboardCard;\n","import * as React from 'react';\n\n/**\n * Reusable node type badge component\n *\n * @param {Object} props\n * @param {string} props.type - Node type (e.g., \"METRIC\", \"DIMENSION\")\n * @param {('small'|'medium'|'large')} [props.size='medium'] - Badge size\n * @param {boolean} [props.abbreviated=false] - Show only first character\n * @param {Object} [props.style] - Additional styles\n */\nexport function NodeBadge({\n  type,\n  size = 'medium',\n  abbreviated = false,\n  style = {},\n}) {\n  if (!type) return null;\n\n  const sizeMap = {\n    small: { fontSize: '7px', padding: '0.44em' },\n    medium: { fontSize: '9px', padding: '0.44em' },\n    large: { fontSize: '11px', padding: '0.44em' },\n  };\n\n  const sizeStyles = sizeMap[size] || sizeMap.medium;\n  const displayText = abbreviated ? type.charAt(0) : type;\n\n  return (\n    <span\n      className={`node_type__${type.toLowerCase()} badge node_type`}\n      style={{\n        ...sizeStyles,\n        flexShrink: 0,\n        ...style,\n        marginRight: 0,\n      }}\n    >\n      {displayText}\n    </span>\n  );\n}\n\n/**\n * Reusable node link component\n *\n * @param {Object} props\n * @param {Object} props.node - Node object with name and current.displayName\n * @param {('small'|'medium'|'large')} [props.size='medium'] - Link size\n * @param {boolean} [props.showFullName=false] - Show full node name instead of display name\n * @param {boolean} [props.ellipsis=false] - Enable text overflow ellipsis\n * @param {Object} [props.style] - Additional styles\n */\nexport function NodeLink({\n  node,\n  size = 'medium',\n  showFullName = false,\n  ellipsis = false,\n  style = {},\n}) {\n  if (!node?.name) return null;\n\n  const sizeMap = {\n    small: { fontSize: '10px', fontWeight: '500' },\n    medium: { fontSize: '12px', fontWeight: '500' },\n    large: { fontSize: '14px', fontWeight: '500' },\n  };\n\n  const sizeStyles = sizeMap[size] || sizeMap.medium;\n  const displayName = showFullName\n    ? node.name\n    : node.current?.displayName || node.name.split('.').pop();\n\n  const ellipsisStyles = ellipsis\n    ? {\n        display: 'block',\n        flex: 1,\n        minWidth: 0,\n        overflow: 'hidden',\n        textOverflow: 'ellipsis',\n        whiteSpace: 'nowrap',\n      }\n    : {};\n\n  return (\n    <a\n      href={`/nodes/${node.name}`}\n      style={{\n        ...sizeStyles,\n        textDecoration: 'none',\n        ...ellipsisStyles,\n        ...style,\n      }}\n    >\n      {displayName}\n    </a>\n  );\n}\n\n/**\n * Combined node display with link and badge\n *\n * @param {Object} props\n * @param {Object} props.node - Node object\n * @param {('small'|'medium'|'large')} [props.size='medium'] - Overall size\n * @param {boolean} [props.showBadge=true] - Show type badge\n * @param {boolean} [props.abbreviatedBadge=false] - Show abbreviated badge\n * @param {boolean} [props.ellipsis=false] - Enable text overflow ellipsis\n * @param {string} [props.gap='6px'] - Gap between link and badge\n * @param {Object} [props.containerStyle] - Additional container styles\n */\nexport function NodeDisplay({\n  node,\n  size = 'medium',\n  showBadge = true,\n  abbreviatedBadge = false,\n  ellipsis = false,\n  gap = '6px',\n  containerStyle = {},\n}) {\n  if (!node) return null;\n\n  return (\n    <div\n      style={{\n        display: 'flex',\n        alignItems: 'center',\n        gap,\n        ...(ellipsis ? { minWidth: 0, overflow: 'hidden' } : {}),\n        ...containerStyle,\n      }}\n    >\n      <NodeLink node={node} size={size} ellipsis={ellipsis} />\n      {showBadge && node.type && (\n        <NodeBadge\n          type={node.type}\n          size={size}\n          abbreviated={abbreviatedBadge}\n        />\n      )}\n    </div>\n  );\n}\n\n/**\n * Node chip component - compact display with border\n * Used in NeedsAttentionSection\n *\n * @param {Object} props\n * @param {Object} props.node - Node object\n * @param {boolean} [props.abbreviatedBadge=true] - Show abbreviated badge\n */\nexport function NodeChip({ node }) {\n  if (!node) return null;\n\n  return (\n    <a\n      href={`/nodes/${node.name}`}\n      style={{\n        display: 'inline-flex',\n        alignItems: 'center',\n        gap: '3px',\n        padding: '2px 6px',\n        fontSize: '12px',\n        border: '1px solid var(--border-color, #ddd)',\n        borderRadius: '3px',\n        textDecoration: 'none',\n        color: 'inherit',\n        backgroundColor: 'var(--card-bg, #f8f9fa)',\n        whiteSpace: 'nowrap',\n        flexShrink: 0,\n      }}\n    >\n      <NodeBadge type={node.type} size=\"small\" abbreviated={true} />\n      {node.current?.displayName || node.name.split('.').pop()}\n    </a>\n  );\n}\n","import * as React from 'react';\nimport { Link } from 'react-router-dom';\nimport { formatRelativeTime } from '../../utils/date';\nimport DashboardCard, {\n  DashboardCardEmpty,\n} from '../../components/DashboardCard';\nimport { NodeBadge } from '../../components/NodeComponents';\n\n// Notifications Section - matches NotificationBell dropdown styling\nexport function NotificationsSection({ notifications, username, loading }) {\n  // Group notifications by node\n  const groupedByNode = {};\n  notifications.forEach(entry => {\n    if (!groupedByNode[entry.entity_name]) {\n      groupedByNode[entry.entity_name] = [];\n    }\n    groupedByNode[entry.entity_name].push(entry);\n  });\n\n  // Convert to array and sort by most recent activity\n  const grouped = Object.entries(groupedByNode)\n    .map(([nodeName, entries]) => ({\n      nodeName,\n      entries,\n      mostRecent: entries[0], // Assuming already sorted by date\n      count: entries.length,\n    }))\n    .slice(0, 15);\n\n  const notificationsList = grouped.map(\n    ({ nodeName, entries, mostRecent, count }) => {\n      const version = mostRecent.details?.version;\n      const href = version\n        ? `/nodes/${mostRecent.entity_name}/revisions/${version}`\n        : `/nodes/${mostRecent.entity_name}/history`;\n\n      // Get unique users who made updates\n      const allUsers = entries\n        .map(e => e.user)\n        .filter(u => u != null && u !== '');\n      const users = [...new Set(allUsers)];\n\n      // If no users found, fall back to mostRecent.user\n      const userText =\n        users.length === 0\n          ? mostRecent.user === username\n            ? 'you'\n            : mostRecent.user?.split('@')[0] || 'unknown'\n          : users.length === 1\n          ? users[0] === username\n            ? 'you'\n            : users[0]?.split('@')[0]\n          : users.includes(username)\n          ? `you + ${users.length - 1} other${users.length - 1 > 1 ? 's' : ''}`\n          : `${users.length} users`;\n\n      return (\n        <div\n          key={nodeName}\n          style={{\n            display: 'flex',\n            alignItems: 'center',\n            gap: '0.5rem',\n            padding: '0.5rem 0',\n            borderBottom: '1px solid var(--border-color, #eee)',\n            fontSize: '12px',\n          }}\n        >\n          <a\n            href={href}\n            style={{\n              fontSize: '13px',\n              fontWeight: '500',\n              textDecoration: 'none',\n              flexShrink: 0,\n            }}\n          >\n            {mostRecent.display_name || mostRecent.entity_name.split('.').pop()}\n          </a>\n          {mostRecent.node_type && (\n            <NodeBadge\n              type={mostRecent.node_type.toUpperCase()}\n              size=\"medium\"\n            />\n          )}\n          <span style={{ color: '#888', whiteSpace: 'nowrap' }}>\n            {count > 1\n              ? `${count} updates`\n              : mostRecent.activity_type?.charAt(0).toUpperCase() +\n                mostRecent.activity_type?.slice(1) +\n                'd'}{' '}\n            by {userText}\n            {' · '}\n            {formatRelativeTime(mostRecent.created_at)}\n          </span>\n        </div>\n      );\n    },\n  );\n\n  return (\n    <DashboardCard\n      title=\"Notifications\"\n      actionLink=\"/notifications\"\n      loading={loading}\n      cardStyle={{ padding: '0', maxHeight: '300px', overflowY: 'auto' }}\n      emptyState={\n        <DashboardCardEmpty\n          icon=\"🔔\"\n          message=\"No notifications yet.\"\n          action={\n            <p style={{ fontSize: '11px', marginTop: '0.5rem' }}>\n              Watch nodes to get notified of changes.\n            </p>\n          }\n        />\n      }\n    >\n      {notifications.length > 0 && (\n        <div\n          style={{\n            display: 'flex',\n            flexDirection: 'column',\n            gap: '0.5rem',\n            padding: '0.5rem',\n          }}\n        >\n          {notificationsList}\n        </div>\n      )}\n    </DashboardCard>\n  );\n}\n","import * as React from 'react';\nimport { Link } from 'react-router-dom';\nimport LoadingIcon from '../../icons/LoadingIcon';\nimport { NodeChip } from '../../components/NodeComponents';\n\n// Needs Attention Section with single-line categories\nexport function NeedsAttentionSection({\n  nodesMissingDescription,\n  invalidNodes,\n  staleDrafts,\n  staleMaterializations,\n  orphanedDimensions,\n  username,\n  loading,\n  personalNamespace,\n  hasPersonalNamespace,\n  namespaceLoading,\n}) {\n  const categories = [\n    {\n      id: 'invalid',\n      icon: '❌',\n      label: 'Invalid',\n      nodes: invalidNodes,\n      viewAllLink: `/?ownedBy=${username}&statuses=INVALID`,\n    },\n    {\n      id: 'stale-drafts',\n      icon: '⏰',\n      label: 'Stale Drafts',\n      nodes: staleDrafts,\n      viewAllLink: `/?ownedBy=${username}&mode=DRAFT`,\n    },\n    {\n      id: 'stale-materializations',\n      icon: '📦',\n      label: 'Stale Materializations',\n      nodes: staleMaterializations,\n      viewAllLink: `/?ownedBy=${username}&hasMaterialization=true`,\n    },\n    {\n      id: 'no-description',\n      icon: '📝',\n      label: 'No Description',\n      nodes: nodesMissingDescription,\n      viewAllLink: `/?ownedBy=${username}&missingDescription=true`,\n    },\n    {\n      id: 'orphaned-dimensions',\n      icon: '🔗',\n      label: 'Orphaned Dimensions',\n      nodes: orphanedDimensions,\n      viewAllLink: `/?ownedBy=${username}&orphanedDimension=true`,\n    },\n  ];\n\n  return (\n    <section style={{ minWidth: 0, width: '100%' }}>\n      <h2 className=\"settings-section-title\">Needs Attention</h2>\n      <div className=\"settings-card\" style={{ padding: '0.25rem 0.75rem' }}>\n        {loading ? (\n          <div style={{ textAlign: 'center', padding: '1rem' }}>\n            <LoadingIcon />\n          </div>\n        ) : (\n          <>\n            {categories.map((cat, i) => (\n              <div\n                key={cat.id}\n                style={{\n                  display: 'flex',\n                  alignItems: 'center',\n                  gap: '0.5rem',\n                  padding: '0.35rem 0',\n                  borderBottom:\n                    i < categories.length - 1\n                      ? '1px solid var(--border-color, #f0f0f0)'\n                      : 'none',\n                  minWidth: 0,\n                }}\n              >\n                {/* Label + count */}\n                <span\n                  style={{\n                    fontSize: '11px',\n                    fontWeight: 600,\n                    color: '#555',\n                    whiteSpace: 'nowrap',\n                    flexShrink: 0,\n                  }}\n                >\n                  {cat.label}{' '}\n                  <span\n                    style={{\n                      color: cat.nodes.length > 0 ? '#dc3545' : '#28a745',\n                      fontWeight: 500,\n                    }}\n                  >\n                    ({cat.nodes.length})\n                  </span>\n                </span>\n\n                {/* Chips with right-side fade */}\n                {cat.nodes.length > 0 ? (\n                  <div\n                    style={{\n                      flex: 1,\n                      overflow: 'hidden',\n                      maskImage:\n                        'linear-gradient(to right, black 75%, transparent 100%)',\n                      WebkitMaskImage:\n                        'linear-gradient(to right, black 75%, transparent 100%)',\n                    }}\n                  >\n                    <div\n                      style={{\n                        display: 'flex',\n                        gap: '0.25rem',\n                        flexWrap: 'nowrap',\n                      }}\n                    >\n                      {cat.nodes.slice(0, 20).map(node => (\n                        <NodeChip key={node.name} node={node} />\n                      ))}\n                    </div>\n                  </div>\n                ) : (\n                  <span style={{ fontSize: '10px', color: '#28a745', flex: 1 }}>\n                    All good\n                  </span>\n                )}\n\n                {/* Arrow link — always visible, outside the fade */}\n                {cat.nodes.length > 0 && (\n                  <Link\n                    to={cat.viewAllLink}\n                    style={{ fontSize: '11px', flexShrink: 0 }}\n                  >\n                    →\n                  </Link>\n                )}\n              </div>\n            ))}\n\n            {!namespaceLoading && !hasPersonalNamespace && (\n              <div\n                style={{\n                  display: 'flex',\n                  alignItems: 'center',\n                  gap: '0.5rem',\n                  padding: '0.35rem 0',\n                  fontSize: '11px',\n                }}\n              >\n                <span style={{ fontWeight: 600, color: '#555', flexShrink: 0 }}>\n                  Personal namespace\n                </span>\n                <Link\n                  to={`/namespaces/${personalNamespace}`}\n                  style={{ fontSize: '10px' }}\n                >\n                  Create {personalNamespace} →\n                </Link>\n              </div>\n            )}\n          </>\n        )}\n      </div>\n    </section>\n  );\n}\n","import * as React from 'react';\nimport NodeListActions from '../../components/NodeListActions';\nimport { NodeDisplay } from '../../components/NodeComponents';\n\n// Node List Component\nexport function NodeList({ nodes, showUpdatedAt }) {\n  const formatDateTime = dateStr => {\n    const date = new Date(dateStr);\n    return date.toLocaleDateString('en-US', {\n      month: 'short',\n      day: 'numeric',\n      hour: '2-digit',\n      minute: '2-digit',\n    });\n  };\n\n  return (\n    <div className=\"node-list\">\n      {nodes.map(node => (\n        <div key={node.name} className=\"subscription-item node-list-item\">\n          <div className=\"node-list-item-content\">\n            <div className=\"node-list-item-display\">\n              <NodeDisplay\n                node={node}\n                size=\"large\"\n                ellipsis={true}\n                gap=\"0.5rem\"\n              />\n            </div>\n            <div className=\"node-list-item-name\">{node.name}</div>\n          </div>\n          <div className=\"node-list-item-actions\">\n            {showUpdatedAt && node.current?.updatedAt && (\n              <span className=\"node-list-item-updated\">\n                {formatDateTime(node.current.updatedAt)}\n              </span>\n            )}\n            <div className=\"node-list-item-actions-wrapper\">\n              <NodeListActions nodeName={node.name} />\n            </div>\n          </div>\n        </div>\n      ))}\n    </div>\n  );\n}\n","import * as React from 'react';\nimport { Link } from 'react-router-dom';\nimport { NodeBadge, NodeLink } from '../../components/NodeComponents';\nimport NodeListActions from '../../components/NodeListActions';\n\n// Format timestamp to relative time\nfunction formatRelativeTime(dateStr) {\n  const date = new Date(dateStr);\n  const now = new Date();\n  const diffMs = now - date;\n  const diffMins = Math.floor(diffMs / (1000 * 60));\n  const diffHours = Math.floor(diffMs / (1000 * 60 * 60));\n  const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n  if (diffMins < 60) return `${diffMins}m`;\n  if (diffHours < 24) return `${diffHours}h`;\n  if (diffDays < 30) return `${diffDays}d`;\n  return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });\n}\n\n// Capitalize first letter\nfunction capitalize(str) {\n  return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n// Type Card Component\nfunction TypeCard({ type, nodes, hasMore, username, activeTab }) {\n  const displayNodes = nodes;\n\n  // Build filter URL based on active tab and type\n  const getFilterUrl = () => {\n    const params = new URLSearchParams();\n\n    if (activeTab === 'owned') {\n      params.append('ownedBy', username);\n      params.append('type', type);\n    } else if (activeTab === 'watched') {\n      // For watched, we'd need a different filter - for now use type only\n      params.append('type', type);\n    } else if (activeTab === 'edited') {\n      params.append('updatedBy', username);\n      params.append('type', type);\n    }\n\n    return `/?${params.toString()}`;\n  };\n\n  return (\n    <div className=\"type-group-card\">\n      <div className=\"type-group-header\">\n        <span className=\"type-group-title\">{capitalize(type)}s</span>\n      </div>\n\n      <div className=\"type-group-nodes\">\n        {displayNodes.map(node => {\n          const isInvalid = node.status === 'invalid';\n          const isDraft = node.mode === 'draft';\n          const gitRepo = node.gitInfo?.repo;\n          const gitBranch = node.gitInfo?.branch;\n          const defaultBranch = node.gitInfo?.defaultBranch;\n          const isDefaultBranch = gitBranch && gitBranch === defaultBranch;\n          const hasGitInfo = gitRepo || gitBranch;\n\n          return (\n            <div key={node.name} className=\"type-group-node\">\n              <NodeBadge type={node.type} size=\"small\" abbreviated={true} />\n              {isInvalid && (\n                <span\n                  className=\"type-group-node-status-icon\"\n                  title=\"Invalid node\"\n                >\n                  ⚠️\n                </span>\n              )}\n              {isDraft && (\n                <span\n                  className=\"type-group-node-status-icon\"\n                  title=\"Draft mode\"\n                >\n                  📝\n                </span>\n              )}\n              <NodeLink\n                node={node}\n                size=\"small\"\n                ellipsis={true}\n                style={{ flex: 1, minWidth: 0 }}\n              />\n              {hasGitInfo && (\n                <span\n                  className={`type-group-node-git-info ${\n                    isDefaultBranch ? 'type-group-node-git-info--default' : ''\n                  }`}\n                  title={`${gitRepo || ''}${gitRepo && gitBranch ? ' / ' : ''}${\n                    gitBranch || ''\n                  }${isDefaultBranch ? ' (default)' : ''}`}\n                  style={{\n                    display: 'flex',\n                    alignItems: 'center',\n                    gap: '4px',\n                    maxWidth: '40%',\n                  }}\n                >\n                  {isDefaultBranch && (\n                    <span style={{ lineHeight: 1, flexShrink: 0 }}>⭐</span>\n                  )}\n                  {gitRepo && (\n                    <span\n                      style={{\n                        overflow: 'hidden',\n                        textOverflow: 'ellipsis',\n                        whiteSpace: 'nowrap',\n                        minWidth: 0,\n                      }}\n                    >\n                      {gitRepo}\n                    </span>\n                  )}\n                  {gitRepo && gitBranch && (\n                    <svg\n                      xmlns=\"http://www.w3.org/2000/svg\"\n                      width=\"10\"\n                      height=\"10\"\n                      viewBox=\"0 0 24 24\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                      strokeWidth=\"2\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                      style={{ flexShrink: 0 }}\n                    >\n                      <line x1=\"6\" y1=\"3\" x2=\"6\" y2=\"15\" />\n                      <circle cx=\"18\" cy=\"6\" r=\"3\" />\n                      <circle cx=\"6\" cy=\"18\" r=\"3\" />\n                      <path d=\"M18 9a9 9 0 0 1-9 9\" />\n                    </svg>\n                  )}\n                  {gitBranch && (\n                    <span\n                      style={{\n                        overflow: 'hidden',\n                        textOverflow: 'ellipsis',\n                        whiteSpace: 'nowrap',\n                        minWidth: 0,\n                      }}\n                    >\n                      {gitBranch}\n                    </span>\n                  )}\n                </span>\n              )}\n              {node.current?.updatedAt && (\n                <span className=\"type-group-node-time\">\n                  {formatRelativeTime(node.current.updatedAt)}\n                </span>\n              )}\n              <div className=\"type-group-node-actions\">\n                <NodeListActions nodeName={node.name} />\n              </div>\n            </div>\n          );\n        })}\n      </div>\n\n      {hasMore && (\n        <Link to={getFilterUrl()} className=\"type-group-more\">\n          More →\n        </Link>\n      )}\n    </div>\n  );\n}\n\n// Type Group Grid Component (2-column layout)\nexport function TypeGroupGrid({ groupedData, username, activeTab }) {\n  if (!groupedData || groupedData.length === 0) {\n    return (\n      <div\n        style={{\n          padding: '1rem',\n          textAlign: 'center',\n          color: '#666',\n          fontSize: '12px',\n        }}\n      >\n        No nodes to display\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"type-group-grid\">\n      {groupedData.map(group => (\n        <TypeCard\n          key={group.type}\n          type={group.type}\n          nodes={group.nodes}\n          hasMore={group.hasMore}\n          username={username}\n          activeTab={activeTab}\n        />\n      ))}\n    </div>\n  );\n}\n","import * as React from 'react';\nimport { Link } from 'react-router-dom';\nimport DashboardCard from '../../components/DashboardCard';\nimport { NodeList } from './NodeList';\nimport { TypeGroupGrid } from './TypeGroupGrid';\n\n// Node type display order\nconst NODE_TYPE_ORDER = ['metric', 'cube', 'dimension', 'transform', 'source'];\n\n// Helper to group nodes by type\nfunction groupNodesByType(nodes, hasMoreMap = {}) {\n  const groups = {};\n  nodes.forEach(node => {\n    const type = (node.type || 'unknown').toLowerCase();\n    if (!groups[type]) groups[type] = [];\n    groups[type].push(node);\n  });\n\n  // Return types in defined order, only including types with nodes\n  return NODE_TYPE_ORDER.filter(type => groups[type]?.length > 0).map(type => ({\n    type,\n    nodes: groups[type],\n    hasMore: hasMoreMap[type] ?? false,\n  }));\n}\n\n// My Nodes Section (owned + watched, with tabs)\nexport function MyNodesSection({\n  ownedNodes,\n  ownedHasMore = {},\n  watchedNodes,\n  recentlyEdited,\n  editedHasMore = {},\n  username,\n  loading,\n}) {\n  const [activeTab, setActiveTab] = React.useState('owned');\n  const hasAutoSwitchedTab = React.useRef(false);\n\n  // Once data loads, auto-switch to \"edited\" if user has no owned nodes but has edits\n  React.useEffect(() => {\n    if (hasAutoSwitchedTab.current) return;\n    if (ownedNodes.length === 0 && recentlyEdited.length > 0) {\n      setActiveTab('edited');\n      hasAutoSwitchedTab.current = true;\n    } else if (ownedNodes.length > 0) {\n      hasAutoSwitchedTab.current = true; // owned nodes exist, stick with default\n    }\n  }, [ownedNodes.length, recentlyEdited.length]);\n  const [groupByType, setGroupByType] = React.useState(() => {\n    const stored = localStorage.getItem('workspace_groupByType');\n    return stored === null ? true : stored === 'true';\n  });\n\n  const ownedNames = new Set(ownedNodes.map(n => n.name));\n  const watchedOnly = watchedNodes.filter(n => !ownedNames.has(n.name));\n\n  const allMyNodeNames = new Set([\n    ...ownedNames,\n    ...watchedNodes.map(n => n.name),\n  ]);\n  const editedOnly = recentlyEdited.filter(n => !allMyNodeNames.has(n.name));\n\n  const getDisplayNodes = () => {\n    switch (activeTab) {\n      case 'owned':\n        return ownedNodes;\n      case 'watched':\n        return watchedOnly;\n      case 'edited':\n        return recentlyEdited;\n      default:\n        return ownedNodes;\n    }\n  };\n  const displayNodes = getDisplayNodes();\n\n  const hasAnyContent =\n    ownedNodes.length > 0 ||\n    watchedOnly.length > 0 ||\n    recentlyEdited.length > 0;\n  const maxDisplay = 8;\n\n  // Handle group by type toggle\n  const handleGroupByTypeChange = e => {\n    const checked = e.target.checked;\n    setGroupByType(checked);\n    localStorage.setItem('workspace_groupByType', checked.toString());\n  };\n\n  // Pick the right hasMore map for the active tab\n  const activeHasMore =\n    activeTab === 'owned'\n      ? ownedHasMore\n      : activeTab === 'edited'\n      ? editedHasMore\n      : {};\n\n  // Group nodes by type if enabled\n  const groupedData = groupByType\n    ? groupNodesByType(displayNodes, activeHasMore)\n    : null;\n\n  return (\n    <DashboardCard\n      title=\"My Nodes\"\n      actionLink={`/?ownedBy=${username}`}\n      loading={loading}\n      cardStyle={{\n        padding: '0.25rem 0.75rem',\n      }}\n      emptyState={\n        <div style={{ padding: '0.75rem 0' }}>\n          <p\n            style={{ fontSize: '12px', color: '#666', marginBottom: '0.75rem' }}\n          >\n            No nodes yet.\n          </p>\n          <div style={{ display: 'flex', gap: '0.75rem' }}>\n            <div\n              style={{\n                flex: 1,\n                padding: '0.75rem',\n                backgroundColor: 'var(--card-bg, #f8f9fa)',\n                border: '1px dashed var(--border-color, #dee2e6)',\n                borderRadius: '6px',\n                textAlign: 'center',\n              }}\n            >\n              <div style={{ fontSize: '16px', marginBottom: '0.25rem' }}>\n                ➕\n              </div>\n              <div\n                style={{\n                  fontSize: '11px',\n                  fontWeight: '500',\n                  marginBottom: '0.25rem',\n                }}\n              >\n                Create a node\n              </div>\n              <p\n                style={{\n                  fontSize: '10px',\n                  color: '#666',\n                  marginBottom: '0.5rem',\n                }}\n              >\n                Build your data model\n              </p>\n              <Link\n                to=\"/create/source\"\n                style={{\n                  display: 'inline-block',\n                  padding: '3px 8px',\n                  fontSize: '10px',\n                  backgroundColor: 'var(--primary-color, #4a90d9)',\n                  color: '#fff',\n                  borderRadius: '4px',\n                  textDecoration: 'none',\n                }}\n              >\n                Create →\n              </Link>\n            </div>\n            <div\n              style={{\n                flex: 1,\n                padding: '0.75rem',\n                backgroundColor: 'var(--card-bg, #f8f9fa)',\n                border: '1px dashed var(--border-color, #dee2e6)',\n                borderRadius: '6px',\n                textAlign: 'center',\n              }}\n            >\n              <div style={{ fontSize: '16px', marginBottom: '0.25rem' }}>\n                👤\n              </div>\n              <div\n                style={{\n                  fontSize: '11px',\n                  fontWeight: '500',\n                  marginBottom: '0.25rem',\n                }}\n              >\n                Claim ownership\n              </div>\n              <p\n                style={{\n                  fontSize: '10px',\n                  color: '#666',\n                  marginBottom: '0.5rem',\n                }}\n              >\n                Add yourself as owner\n              </p>\n              <Link\n                to=\"/\"\n                style={{\n                  display: 'inline-block',\n                  padding: '3px 8px',\n                  fontSize: '10px',\n                  backgroundColor: '#6c757d',\n                  color: '#fff',\n                  borderRadius: '4px',\n                  textDecoration: 'none',\n                }}\n              >\n                Browse →\n              </Link>\n            </div>\n          </div>\n        </div>\n      }\n    >\n      {hasAnyContent && (\n        <>\n          {/* Tabs and Group by Type Toggle */}\n          <div\n            style={{\n              display: 'flex',\n              alignItems: 'center',\n              justifyContent: 'space-between',\n              gap: '0.5rem',\n              marginBottom: '0.5rem',\n              paddingTop: '0.5rem',\n              paddingBottom: displayNodes.length > 0 ? '0.5rem' : '0',\n              borderBottom:\n                displayNodes.length > 0\n                  ? '1px solid var(--border-color, #eee)'\n                  : 'none',\n            }}\n          >\n            <div style={{ display: 'flex', gap: '0.5rem' }}>\n              <button\n                onClick={() => setActiveTab('owned')}\n                style={{\n                  padding: '4px 10px',\n                  fontSize: '11px',\n                  border: 'none',\n                  borderRadius: '4px',\n                  cursor: 'pointer',\n                  backgroundColor:\n                    activeTab === 'owned'\n                      ? 'var(--primary-color, #4a90d9)'\n                      : '#e9ecef',\n                  color: activeTab === 'owned' ? '#fff' : '#495057',\n                }}\n              >\n                Owned\n              </button>\n              <button\n                onClick={() => setActiveTab('watched')}\n                style={{\n                  padding: '4px 10px',\n                  fontSize: '11px',\n                  border: 'none',\n                  borderRadius: '4px',\n                  cursor: 'pointer',\n                  backgroundColor:\n                    activeTab === 'watched'\n                      ? 'var(--primary-color, #4a90d9)'\n                      : '#e9ecef',\n                  color: activeTab === 'watched' ? '#fff' : '#495057',\n                }}\n              >\n                Watched ({watchedOnly.length})\n              </button>\n              <button\n                onClick={() => setActiveTab('edited')}\n                style={{\n                  padding: '4px 10px',\n                  fontSize: '11px',\n                  border: 'none',\n                  borderRadius: '4px',\n                  cursor: 'pointer',\n                  backgroundColor:\n                    activeTab === 'edited'\n                      ? 'var(--primary-color, #4a90d9)'\n                      : '#e9ecef',\n                  color: activeTab === 'edited' ? '#fff' : '#495057',\n                }}\n              >\n                Recent Edits\n              </button>\n            </div>\n\n            {/* Group by Type Toggle */}\n            {displayNodes.length > 0 && (\n              <div\n                style={{\n                  display: 'flex',\n                  alignItems: 'center',\n                  gap: '0.5rem',\n                }}\n              >\n                <input\n                  type=\"checkbox\"\n                  id=\"groupByType\"\n                  checked={groupByType}\n                  onChange={handleGroupByTypeChange}\n                  style={{ cursor: 'pointer' }}\n                />\n                <label\n                  htmlFor=\"groupByType\"\n                  style={{\n                    fontSize: '11px',\n                    cursor: 'pointer',\n                    userSelect: 'none',\n                    color: '#495057',\n                    fontWeight: '500',\n                  }}\n                >\n                  Group by Type\n                </label>\n              </div>\n            )}\n          </div>\n\n          {displayNodes.length > 0 ? (\n            <>\n              {groupByType ? (\n                <TypeGroupGrid\n                  groupedData={groupedData}\n                  username={username}\n                  activeTab={activeTab}\n                />\n              ) : (\n                <>\n                  <NodeList\n                    nodes={displayNodes.slice(0, maxDisplay)}\n                    showUpdatedAt={true}\n                  />\n                  {displayNodes.length > maxDisplay && (\n                    <div\n                      style={{\n                        textAlign: 'center',\n                        padding: '0.5rem',\n                        fontSize: '12px',\n                        color: '#666',\n                      }}\n                    >\n                      +{displayNodes.length - maxDisplay} more\n                    </div>\n                  )}\n                </>\n              )}\n            </>\n          ) : (\n            <div\n              style={{\n                padding: '1rem',\n                textAlign: 'center',\n                color: '#666',\n                fontSize: '12px',\n              }}\n            >\n              {activeTab === 'owned' && 'No owned nodes'}\n              {activeTab === 'watched' && 'No watched nodes'}\n              {activeTab === 'edited' && 'No recent edits'}\n            </div>\n          )}\n        </>\n      )}\n    </DashboardCard>\n  );\n}\n","import * as React from 'react';\nimport { Link } from 'react-router-dom';\nimport DashboardCard from '../../components/DashboardCard';\nimport { NodeDisplay } from '../../components/NodeComponents';\n\n// Materializations Section\nexport function MaterializationsSection({ nodes, loading }) {\n  // Ensure nodes is always an array\n  const sortedNodes = (nodes || []).slice().sort((a, b) => {\n    const aTs = a.current?.availability?.validThroughTs;\n    const bTs = b.current?.availability?.validThroughTs;\n    if (!aTs && !bTs) return 0;\n    if (!aTs) return 1;\n    if (!bTs) return -1;\n    return bTs - aTs;\n  });\n\n  const getAvailabilityStatus = availability => {\n    if (!availability) {\n      return { icon: '⏳', text: 'Pending', color: '#6c757d' };\n    }\n    const validThrough = availability.validThroughTs\n      ? new Date(availability.validThroughTs)\n      : null;\n    const now = new Date();\n    const hoursSinceUpdate = validThrough\n      ? (now - validThrough) / (1000 * 60 * 60)\n      : null;\n    if (!validThrough) {\n      return { icon: '⏳', text: 'Pending', color: '#6c757d' };\n    } else if (hoursSinceUpdate <= 24) {\n      return {\n        icon: '🟢',\n        text: formatTimeAgo(validThrough),\n        color: '#28a745',\n      };\n    } else if (hoursSinceUpdate <= 72) {\n      return {\n        icon: '🟡',\n        text: formatTimeAgo(validThrough),\n        color: '#ffc107',\n      };\n    } else {\n      return {\n        icon: '🔴',\n        text: formatTimeAgo(validThrough),\n        color: '#dc3545',\n      };\n    }\n  };\n\n  const formatTimeAgo = date => {\n    const now = new Date();\n    const diffMs = now - date;\n    const diffHours = Math.floor(diffMs / (1000 * 60 * 60));\n    const diffDays = Math.floor(diffHours / 24);\n    if (diffHours < 1) return 'just now';\n    if (diffHours < 24) return `${diffHours}h ago`;\n    if (diffDays === 1) return 'yesterday';\n    return `${diffDays}d ago`;\n  };\n\n  const maxDisplay = 5;\n\n  const materializationsList = sortedNodes.slice(0, maxDisplay).map(node => {\n    const status = getAvailabilityStatus(node.current?.availability);\n    return (\n      <div\n        key={node.name}\n        style={{\n          padding: '0.5rem',\n          border: '1px solid var(--border-color, #e0e0e0)',\n          borderRadius: '4px',\n          backgroundColor: 'var(--card-bg, #fff)',\n        }}\n      >\n        <div\n          style={{\n            display: 'flex',\n            alignItems: 'center',\n            justifyContent: 'space-between',\n            marginBottom: '4px',\n          }}\n        >\n          <NodeDisplay node={node} size=\"medium\" />\n          <span style={{ fontSize: '10px', color: status.color }}>\n            {status.icon} {status.text}\n          </span>\n        </div>\n        <div style={{ fontSize: '10px', color: '#666' }}>\n          {node.current?.materializations?.map(mat => (\n            <span key={mat.name} style={{ marginRight: '8px' }}>\n              🕐 {mat.schedule || 'No schedule'}\n            </span>\n          ))}\n          {node.current?.availability?.table && (\n            <span style={{ color: '#888' }}>\n              → {node.current.availability.table}\n            </span>\n          )}\n        </div>\n      </div>\n    );\n  });\n\n  return (\n    <DashboardCard\n      title=\"Materializations\"\n      actionLink=\"/?hasMaterialization=true\"\n      loading={loading}\n      cardStyle={{\n        padding: '0.75rem 1rem',\n        maxHeight: '300px',\n        overflowY: 'auto',\n      }}\n      emptyState={\n        <div style={{ padding: '0' }}>\n          <p\n            style={{ fontSize: '12px', color: '#666', marginBottom: '0.75rem' }}\n          >\n            No materializations configured.\n          </p>\n          <div\n            style={{\n              padding: '0.75rem',\n              backgroundColor: 'var(--card-bg, #f8f9fa)',\n              border: '1px dashed var(--border-color, #dee2e6)',\n              borderRadius: '6px',\n              textAlign: 'center',\n            }}\n          >\n            <div style={{ fontSize: '16px', marginBottom: '0.25rem' }}>📦</div>\n            <div\n              style={{\n                fontSize: '11px',\n                fontWeight: '500',\n                marginBottom: '0.25rem',\n              }}\n            >\n              Materialize a node\n            </div>\n            <p\n              style={{\n                fontSize: '10px',\n                color: '#666',\n                marginBottom: '0.5rem',\n              }}\n            >\n              Speed up queries with cached data\n            </p>\n            <Link\n              to=\"/\"\n              style={{\n                display: 'inline-block',\n                padding: '3px 8px',\n                fontSize: '10px',\n                backgroundColor: 'var(--primary-color, #4a90d9)',\n                color: '#fff',\n                borderRadius: '4px',\n                textDecoration: 'none',\n              }}\n            >\n              Browse nodes →\n            </Link>\n          </div>\n        </div>\n      }\n    >\n      {sortedNodes.length > 0 && (\n        <div\n          style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}\n        >\n          {materializationsList}\n          {sortedNodes.length > maxDisplay && (\n            <div\n              style={{\n                textAlign: 'center',\n                padding: '0.5rem',\n                fontSize: '12px',\n                color: '#666',\n              }}\n            >\n              +{sortedNodes.length - maxDisplay} more\n            </div>\n          )}\n        </div>\n      )}\n    </DashboardCard>\n  );\n}\n","import * as React from 'react';\nimport DashboardCard from '../../components/DashboardCard';\nimport { formatRelativeTime } from '../../utils/date';\n\n// Git Namespaces Section - shows git-managed namespaces with their branches\nexport function ActiveBranchesSection({ ownedNodes, recentlyEdited, loading }) {\n  // Combine owned and edited nodes to get all user's nodes\n  const allNodes = [...ownedNodes, ...recentlyEdited];\n\n  // Group nodes by base git namespace (the root namespace that's git-managed)\n  const gitNamespaceMap = new Map();\n  allNodes.forEach(node => {\n    if (node.gitInfo && node.gitInfo.repo) {\n      // Extract the base namespace (everything before the branch part)\n      const fullNamespace = node.name.split('.').slice(0, -1).join('.');\n\n      // For git-managed namespaces, the base is the parent namespace\n      // If not available, we derive it by removing the branch from the full namespace\n      let baseNamespace;\n      if (node.gitInfo.parentNamespace) {\n        baseNamespace = node.gitInfo.parentNamespace;\n      } else {\n        // Fallback: remove the branch part from fullNamespace\n        // fullNamespace is like \"myproject.main\", we want \"myproject\"\n        const dotIdx = fullNamespace.lastIndexOf('.');\n        baseNamespace =\n          dotIdx > 0 ? fullNamespace.slice(0, dotIdx) : fullNamespace;\n      }\n\n      if (!gitNamespaceMap.has(baseNamespace)) {\n        gitNamespaceMap.set(baseNamespace, {\n          baseNamespace,\n          repo: node.gitInfo.repo,\n          defaultBranch: node.gitInfo.defaultBranch,\n          branches: new Map(),\n        });\n      }\n\n      const gitNs = gitNamespaceMap.get(baseNamespace);\n      const branchKey = node.gitInfo.branch;\n\n      if (!gitNs.branches.has(branchKey)) {\n        gitNs.branches.set(branchKey, {\n          branch: node.gitInfo.branch,\n          isDefault: node.gitInfo.isDefaultBranch,\n          namespace: fullNamespace,\n          nodes: [],\n          lastActivity: node.current?.updatedAt,\n        });\n      }\n\n      const branchInfo = gitNs.branches.get(branchKey);\n      branchInfo.nodes.push(node);\n      // Track most recent activity\n      if (\n        node.current?.updatedAt &&\n        (!branchInfo.lastActivity ||\n          node.current.updatedAt > branchInfo.lastActivity)\n      ) {\n        branchInfo.lastActivity = node.current.updatedAt;\n      }\n    }\n  });\n\n  // Ensure default branch is always present (even if user has no nodes there)\n  gitNamespaceMap.forEach(gitNs => {\n    if (gitNs.defaultBranch && !gitNs.branches.has(gitNs.defaultBranch)) {\n      gitNs.branches.set(gitNs.defaultBranch, {\n        branch: gitNs.defaultBranch,\n        isDefault: true,\n        namespace: `${gitNs.baseNamespace}.${gitNs.defaultBranch}`,\n        nodes: [],\n        lastActivity: null,\n      });\n    }\n  });\n\n  // Convert to array and sort\n  const gitNamespaces = Array.from(gitNamespaceMap.values())\n    .map(ns => ({\n      ...ns,\n      branches: Array.from(ns.branches.values()).sort((a, b) => {\n        // Default branch first\n        if (a.isDefault && !b.isDefault) return -1;\n        if (!a.isDefault && b.isDefault) return 1;\n        // Then by last activity\n        if (!a.lastActivity && !b.lastActivity) return 0;\n        if (!a.lastActivity) return 1;\n        if (!b.lastActivity) return -1;\n        return new Date(b.lastActivity) - new Date(a.lastActivity);\n      }),\n    }))\n    .sort((a, b) => {\n      // Sort git namespaces by most recent activity across all branches\n      const aLatest = Math.max(\n        ...a.branches.map(b =>\n          b.lastActivity ? new Date(b.lastActivity).getTime() : 0,\n        ),\n      );\n      const bLatest = Math.max(\n        ...b.branches.map(b =>\n          b.lastActivity ? new Date(b.lastActivity).getTime() : 0,\n        ),\n      );\n      return bLatest - aLatest;\n    });\n\n  const maxDisplay = 3; // Show top 3 git namespaces\n\n  const gitNamespacesList = gitNamespaces\n    .slice(0, maxDisplay)\n    .map((gitNs, gitNsIdx) => {\n      const isLastGitNs =\n        gitNsIdx === gitNamespaces.slice(0, maxDisplay).length - 1;\n      return (\n        <div key={gitNs.baseNamespace}>\n          {/* Base namespace and repository header */}\n          <div\n            style={{\n              display: 'flex',\n              alignItems: 'center',\n              gap: '8px',\n              padding: '0.75rem 0 0.5rem 0',\n              marginTop: gitNsIdx === 0 ? '0' : '0',\n            }}\n          >\n            <a\n              href={`/namespaces/${gitNs.baseNamespace}`}\n              style={{\n                fontSize: '12px',\n                fontWeight: '600',\n                textDecoration: 'none',\n              }}\n            >\n              {gitNs.baseNamespace}\n            </a>\n            <span\n              style={{\n                fontSize: '9px',\n                padding: '2px 6px',\n                backgroundColor: '#f0f0f0',\n                color: '#666',\n                borderRadius: '3px',\n                fontWeight: '500',\n              }}\n            >\n              {gitNs.repo}\n            </span>\n          </div>\n\n          {/* Branch list */}\n          {gitNs.branches.map(branchInfo => (\n            <div\n              key={branchInfo.branch}\n              style={{\n                display: 'flex',\n                alignItems: 'center',\n                justifyContent: 'space-between',\n                padding: '0px 1em 0.4em 1em',\n              }}\n            >\n              <div\n                style={{\n                  display: 'flex',\n                  alignItems: 'center',\n                  gap: '6px',\n                  minWidth: 0,\n                  flex: 1,\n                }}\n              >\n                <a\n                  href={`/namespaces/${branchInfo.namespace}`}\n                  style={{\n                    fontSize: '12px',\n                    fontWeight: '500',\n                    overflow: 'hidden',\n                    textOverflow: 'ellipsis',\n                    whiteSpace: 'nowrap',\n                  }}\n                >\n                  {branchInfo.branch}\n                </a>\n                {branchInfo.isDefault && (\n                  <span style={{ fontSize: '10px' }}>⭐</span>\n                )}\n              </div>\n              {branchInfo.lastActivity && (\n                <span\n                  style={{\n                    fontSize: '10px',\n                    color: '#888',\n                    whiteSpace: 'nowrap',\n                  }}\n                >\n                  updated {formatRelativeTime(branchInfo.lastActivity)}\n                </span>\n              )}\n            </div>\n          ))}\n\n          {/* Horizontal line between namespaces */}\n          {!isLastGitNs && (\n            <div\n              style={{\n                borderBottom: '1px solid var(--border-color, #ddd)',\n                margin: '0.5rem 0',\n              }}\n            />\n          )}\n        </div>\n      );\n    });\n\n  return (\n    <DashboardCard\n      title=\"Git Namespaces\"\n      loading={loading}\n      cardStyle={{\n        padding: '0.25rem 0.25rem 0.5em 0.75rem',\n        maxHeight: '300px',\n        overflowY: 'auto',\n      }}\n      emptyState={\n        <div style={{ padding: '0', textAlign: 'center' }}>\n          <p\n            style={{ fontSize: '12px', color: '#666', marginBottom: '0.75rem' }}\n          >\n            No git-managed namespaces.\n          </p>\n          <div\n            style={{\n              padding: '0.75rem',\n              backgroundColor: 'var(--card-bg, #f8f9fa)',\n              border: '1px dashed var(--border-color, #dee2e6)',\n              borderRadius: '6px',\n            }}\n          >\n            <div style={{ fontSize: '16px', marginBottom: '0.25rem' }}>🌿</div>\n            <div\n              style={{\n                fontSize: '11px',\n                fontWeight: '500',\n                marginBottom: '0.25rem',\n              }}\n            >\n              Set up git-backed namespaces\n            </div>\n            <p style={{ fontSize: '10px', color: '#666', marginBottom: '0' }}>\n              Manage your nodes with version control\n            </p>\n          </div>\n        </div>\n      }\n    >\n      {gitNamespaces.length > 0 && (\n        <div style={{ display: 'flex', flexDirection: 'column' }}>\n          {gitNamespacesList}\n          {gitNamespaces.length > maxDisplay && (\n            <div\n              style={{\n                textAlign: 'center',\n                padding: '0.5rem',\n                fontSize: '12px',\n                color: '#666',\n              }}\n            >\n              +{gitNamespaces.length - maxDisplay} more git namespace\n              {gitNamespaces.length - maxDisplay !== 1 ? 's' : ''}\n            </div>\n          )}\n        </div>\n      )}\n    </DashboardCard>\n  );\n}\n","import * as React from 'react';\nimport LoadingIcon from '../../icons/LoadingIcon';\nimport { useCurrentUser } from '../../providers/UserProvider';\nimport { useWorkspaceDashboardData } from '../../hooks/useWorkspaceData';\nimport { NotificationsSection } from './NotificationsSection';\nimport { NeedsAttentionSection } from './NeedsAttentionSection';\nimport { MyNodesSection } from './MyNodesSection';\nimport { MaterializationsSection } from './MaterializationsSection';\nimport { ActiveBranchesSection } from './ActiveBranchesSection';\n\nimport 'styles/settings.css';\nimport './MyWorkspacePage.css';\n\nexport function MyWorkspacePage() {\n  const { currentUser, loading: userLoading } = useCurrentUser();\n  const { data: workspaceData, loadingStates } = useWorkspaceDashboardData(\n    currentUser?.username,\n  );\n\n  const {\n    ownedNodes = [],\n    ownedHasMore = {},\n    recentlyEdited = [],\n    editedHasMore = {},\n    watchedNodes = [],\n    notifications = [],\n    materializedNodes = [],\n    needsAttention: {\n      nodesMissingDescription = [],\n      invalidNodes = [],\n      staleDrafts = [],\n      orphanedDimensions = [],\n    } = {},\n    hasPersonalNamespace = null,\n  } = workspaceData;\n\n  const myNodesLoading = loadingStates.myNodes;\n  const notificationsLoading = loadingStates.notifications;\n  const materializationsLoading = loadingStates.materializations;\n  const needsAttentionLoading = loadingStates.needsAttention;\n  const namespaceLoading = loadingStates.namespace;\n\n  // Filter stale materializations (> 72 hours old)\n  const staleMaterializations = materializedNodes.filter(node => {\n    const validThroughTs = node.current?.availability?.validThroughTs;\n    if (!validThroughTs) return false; // Pending ones aren't \"stale\"\n    const hoursSinceUpdate = (Date.now() - validThroughTs) / (1000 * 60 * 60);\n    return hoursSinceUpdate > 72;\n  });\n\n  // Personal namespace for the user\n  const usernameForNamespace = currentUser?.username?.split('@')[0] || '';\n  const personalNamespace = `users.${usernameForNamespace}`;\n\n  if (userLoading) {\n    return (\n      <div className=\"settings-page\" style={{ padding: '1.5rem 2rem' }}>\n        <h1 className=\"settings-title\">Dashboard</h1>\n        <div style={{ textAlign: 'center', padding: '3rem' }}>\n          <LoadingIcon />\n        </div>\n      </div>\n    );\n  }\n\n  // Calculate stats\n  return (\n    <div className=\"settings-page\" style={{ padding: '1.5rem 2rem' }}>\n      <div className=\"workspace-layout\">\n        {/* Left Column (65%): My Nodes */}\n        <div className=\"workspace-left-column\">\n          <MyNodesSection\n            ownedNodes={ownedNodes}\n            ownedHasMore={ownedHasMore}\n            watchedNodes={watchedNodes}\n            recentlyEdited={recentlyEdited}\n            editedHasMore={editedHasMore}\n            username={currentUser?.username}\n            loading={myNodesLoading}\n          />\n        </div>\n\n        {/* Right Column (35%): action items first, then activity */}\n        <div className=\"workspace-right-column\">\n          <NeedsAttentionSection\n            nodesMissingDescription={nodesMissingDescription}\n            invalidNodes={invalidNodes}\n            staleDrafts={staleDrafts}\n            staleMaterializations={staleMaterializations}\n            orphanedDimensions={orphanedDimensions}\n            username={currentUser?.username}\n            loading={needsAttentionLoading || materializationsLoading}\n            personalNamespace={personalNamespace}\n            hasPersonalNamespace={hasPersonalNamespace}\n            namespaceLoading={namespaceLoading}\n          />\n\n          <NotificationsSection\n            notifications={notifications}\n            username={currentUser?.username}\n            loading={notificationsLoading}\n          />\n\n          <ActiveBranchesSection\n            ownedNodes={ownedNodes}\n            recentlyEdited={recentlyEdited}\n            loading={myNodesLoading}\n          />\n\n          <MaterializationsSection\n            nodes={materializedNodes}\n            loading={materializationsLoading}\n          />\n        </div>\n      </div>\n    </div>\n  );\n}\n"],"names":["toNodes","r","_c","_b","_a","e","useWorkspaceDashboardData","username","djClient","useContext","DJClientContext","data","setData","useState","loadingStates","setLoadingStates","useEffect","NODE_TYPES","hasNextPage","fetchMyNodesAndCollections","ownedByType","editedByType","collectionsResult","t","ownedHasMore","i","editedHasMore","prev","err","fetchWatchedAndNotifications","subscriptions","historyResult","watchedNodeNames","s","historyEntries","notifNodeNames","h","watchedNodes","notifNodes","notifNodeMap","n","notifications","entry","fetchMaterializations","result","fetchNeedsAttentionAndNamespace","personalNamespace","missingDescResult","invalidResult","draftResult","orphanedResult","namespacesList","sevenDaysAgo","allDrafts","ns","DashboardCard","title","actionLink","actionText","loading","emptyState","children","cardStyle","contentStyle","showHeader","hasContent","React","child","showEmptyState","jsxs","jsx","Link","LoadingIcon","DashboardCardEmpty","icon","message","action","NodeBadge","type","size","abbreviated","style","sizeMap","sizeStyles","displayText","NodeLink","node","showFullName","ellipsis","displayName","ellipsisStyles","NodeDisplay","showBadge","abbreviatedBadge","gap","containerStyle","NodeChip","NotificationsSection","groupedByNode","notificationsList","nodeName","entries","mostRecent","count","version","href","allUsers","u","users","userText","_d","_e","formatRelativeTime","NeedsAttentionSection","nodesMissingDescription","invalidNodes","staleDrafts","staleMaterializations","orphanedDimensions","hasPersonalNamespace","namespaceLoading","categories","Fragment","cat","NodeList","nodes","showUpdatedAt","formatDateTime","dateStr","NodeListActions","date","diffMs","diffMins","diffHours","diffDays","capitalize","str","TypeCard","hasMore","activeTab","displayNodes","getFilterUrl","params","isInvalid","isDraft","gitRepo","gitBranch","defaultBranch","isDefaultBranch","hasGitInfo","TypeGroupGrid","groupedData","group","NODE_TYPE_ORDER","groupNodesByType","hasMoreMap","groups","MyNodesSection","ownedNodes","recentlyEdited","setActiveTab","hasAutoSwitchedTab","groupByType","setGroupByType","stored","ownedNames","watchedOnly","allMyNodeNames","hasAnyContent","maxDisplay","handleGroupByTypeChange","checked","MaterializationsSection","sortedNodes","a","b","aTs","bTs","getAvailabilityStatus","availability","validThrough","hoursSinceUpdate","formatTimeAgo","materializationsList","status","mat","ActiveBranchesSection","allNodes","gitNamespaceMap","fullNamespace","baseNamespace","dotIdx","gitNs","branchKey","branchInfo","gitNamespaces","aLatest","gitNamespacesList","gitNsIdx","isLastGitNs","MyWorkspacePage","currentUser","userLoading","useCurrentUser","workspaceData","materializedNodes","myNodesLoading","notificationsLoading","materializationsLoading","needsAttentionLoading","validThroughTs"],"mappings":"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"}