diff --git a/apps/web/src/app/(private)/dashboard/layout.tsx b/apps/web/src/app/(private)/dashboard/layout.tsx
index 529680493..bd6e635f5 100644
--- a/apps/web/src/app/(private)/dashboard/layout.tsx
+++ b/apps/web/src/app/(private)/dashboard/layout.tsx
@@ -4,10 +4,8 @@ import {
Container,
TableBlankSlate,
TableWithHeader,
- Text,
} from '@latitude-data/web-ui'
import { AppTabs } from '$/app/(private)/AppTabs'
-import { AppLayout } from '$/components/layouts'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { getSession } from '$/services/auth/getSession'
import { ROUTES } from '$/services/routes'
@@ -15,7 +13,6 @@ import Link from 'next/link'
import { redirect } from 'next/navigation'
import { getActiveProjectsCached } from '../_data-access'
-import { NAV_LINKS } from '../_lib/constants'
import { ProjectsTable } from './_components/ProjectsTable'
export default async function DashboardLayout({
@@ -26,52 +23,38 @@ export default async function DashboardLayout({
const data = await getSession()
if (!data.session) return redirect(ROUTES.auth.login)
- const { workspace, user } = await getCurrentUser()
+ const { workspace } = await getCurrentUser()
const projects = await getActiveProjectsCached({ workspaceId: workspace.id })
- const breadcrumbs = [
- {
- name: workspace.name,
- },
- {
- name: Projects,
- },
- ]
return (
-
-
-
- {children}
-
- Add project
-
- }
- table={
- <>
- {projects.length > 0 && }
- {projects.length === 0 && (
-
-
- Create your first project
-
-
- }
- />
- )}
- >
- }
- />
-
-
+
+
+ {children}
+
+ Add project
+
+ }
+ table={
+ <>
+ {projects.length > 0 && }
+ {projects.length === 0 && (
+
+
+ Create your first project
+
+
+ }
+ />
+ )}
+ >
+ }
+ />
+
)
}
diff --git a/apps/web/src/app/(private)/datasets/layout.tsx b/apps/web/src/app/(private)/datasets/layout.tsx
index 534a60393..70dc6ba7c 100644
--- a/apps/web/src/app/(private)/datasets/layout.tsx
+++ b/apps/web/src/app/(private)/datasets/layout.tsx
@@ -1,57 +1,39 @@
import { ReactNode } from 'react'
import { DatasetsRepository } from '@latitude-data/core/repositories'
-import { Container, TableWithHeader, Text } from '@latitude-data/web-ui'
+import { Container, TableWithHeader } from '@latitude-data/web-ui'
import { AppTabs } from '$/app/(private)/AppTabs'
import { DatasetsTable } from '$/app/(private)/datasets/_components/DatasetsTable'
-import { AppLayout } from '$/components/layouts'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { ROUTES } from '$/services/routes'
import Link from 'next/link'
-import { NAV_LINKS } from '../_lib/constants'
-
export default async function DatasetsList({
children,
}: Readonly<{
children: ReactNode
}>) {
- const { workspace, user } = await getCurrentUser()
+ const { workspace } = await getCurrentUser()
const scope = new DatasetsRepository(workspace.id)
const datasets = await scope.findAll().then((r) => r.unwrap())
return (
- Datasets,
- },
- ]}
- >
-
-
- {children}
-
-
-
- Generate dataset
-
-
-
- Upload dataset
-
-
- }
- table={}
- />
-
-
+
+
+ {children}
+
+
+ Generate dataset
+
+
+ Upload dataset
+
+
+ }
+ table={}
+ />
+
)
}
diff --git a/apps/web/src/app/(private)/error.tsx b/apps/web/src/app/(private)/error.tsx
index 38c23d74c..a99fa8a86 100644
--- a/apps/web/src/app/(private)/error.tsx
+++ b/apps/web/src/app/(private)/error.tsx
@@ -4,8 +4,6 @@ import { useEffect } from 'react'
import { ErrorComponent, useSession } from '@latitude-data/web-ui/browser'
import * as Sentry from '@sentry/nextjs'
-import { NAV_LINKS } from '$/app/(private)/_lib/constants'
-import { AppLayout } from '$/components/layouts'
export default function Error({
error,
@@ -20,20 +18,9 @@ export default function Error({
}, [error])
return (
-
-
-
+
)
}
diff --git a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/dashboard/layout.tsx b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/dashboard/layout.tsx
deleted file mode 100644
index 1ddac9c5e..000000000
--- a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/dashboard/layout.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { ReactNode } from 'react'
-
-import { Text } from '@latitude-data/web-ui'
-import { getEvaluationByUuidCached } from '$/app/(private)/_data-access'
-import { NAV_LINKS } from '$/app/(private)/_lib/constants'
-import BreadcrumbLink from '$/components/BreadcrumbLink'
-import { AppLayout } from '$/components/layouts'
-import { getCurrentUser } from '$/services/auth/getCurrentUser'
-import { ROUTES } from '$/services/routes'
-
-export default async function EvaluationLayout({
- params,
- children,
-}: {
- params: { evaluationUuid: string }
- children: ReactNode
-}) {
- const evaluation = await getEvaluationByUuidCached(params.evaluationUuid)
- const session = await getCurrentUser()
-
- return (
-
- ),
- },
- {
- name: {evaluation.name},
- },
- ]}
- >
- {children}
-
- )
-}
diff --git a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/layout.tsx b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/layout.tsx
index 6debb21ae..1b95a861e 100644
--- a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/layout.tsx
+++ b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/layout.tsx
@@ -1,17 +1,12 @@
import { ReactNode } from 'react'
import { getFreeRuns } from '@latitude-data/core/services/freeRunsManager/index'
-import { Text } from '@latitude-data/web-ui'
import {
getEvaluationByUuidCached,
getProviderApiKeysCached,
} from '$/app/(private)/_data-access'
-import { NAV_LINKS } from '$/app/(private)/_lib/constants'
-import BreadcrumbLink from '$/components/BreadcrumbLink'
-import { AppLayout } from '$/components/layouts'
import providerApiKeyPresenter from '$/presenters/providerApiKeyPresenter'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
-import { ROUTES } from '$/services/routes'
import EvaluationEditorLayout from './_components/EvaluationEditorLayout'
@@ -25,37 +20,17 @@ export default async function DocumentPage({
const { workspace } = await getCurrentUser()
const evaluationUuid = params.evaluationUuid
const evaluation = await getEvaluationByUuidCached(evaluationUuid)
- const session = await getCurrentUser()
const providerApiKeys = await getProviderApiKeysCached()
const freeRunsCount = await getFreeRuns(workspace.id)
return (
-
- ),
- },
- {
- name: {evaluation.name},
- },
- ]}
+
-
- {children}
-
-
+ {children}
+
)
}
diff --git a/apps/web/src/app/(private)/evaluations/(root)/layout.tsx b/apps/web/src/app/(private)/evaluations/(root)/layout.tsx
index eafb8664d..0a5d9397d 100644
--- a/apps/web/src/app/(private)/evaluations/(root)/layout.tsx
+++ b/apps/web/src/app/(private)/evaluations/(root)/layout.tsx
@@ -1,36 +1,19 @@
import { ReactNode } from 'react'
-import { Text } from '@latitude-data/web-ui'
import { getEvaluationTemplatesCached } from '$/app/(private)/_data-access'
import Evaluations from '$/app/(private)/evaluations/_components/Evaluations'
-import { AppLayout } from '$/components/layouts'
-import { getCurrentUser } from '$/services/auth/getCurrentUser'
-
-import { NAV_LINKS } from '../../_lib/constants'
export default async function EvaluationsLayout({
children,
}: {
children: ReactNode
}) {
- const session = await getCurrentUser()
const evaluationTemplates = await getEvaluationTemplatesCached()
return (
- Evaluations,
- },
- ]}
- >
+ <>
{children}
-
+ >
)
}
diff --git a/apps/web/src/app/(private)/layout.tsx b/apps/web/src/app/(private)/layout.tsx
index f571d65d4..b8d3bcd95 100644
--- a/apps/web/src/app/(private)/layout.tsx
+++ b/apps/web/src/app/(private)/layout.tsx
@@ -3,6 +3,7 @@ import { ReactNode } from 'react'
import { SessionProvider } from '@latitude-data/web-ui/browser'
import { createSupportUserIdentity } from '$/app/(private)/_lib/createSupportUserIdentity'
import { SupportChat } from '$/components/IntercomSupportChat'
+import { AppLayout } from '$/components/layouts'
import {
LatitudeWebsocketsProvider,
SocketIOProvider,
@@ -14,6 +15,7 @@ import { ROUTES } from '$/services/routes'
import { redirect } from 'next/navigation'
import { CSPostHogProvider, IdentifyUser } from '../providers'
+import { NAV_LINKS } from './_lib/constants'
export default async function PrivateLayout({
children,
@@ -33,7 +35,9 @@ export default async function PrivateLayout({
- {children}
+
+ {children}
+
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/ArchivedCommitsList.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/ArchivedCommitsList.tsx
index 9c61f1290..ad74a6f10 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/ArchivedCommitsList.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/ArchivedCommitsList.tsx
@@ -8,7 +8,7 @@ import {
DocumentVersion,
} from '@latitude-data/core/browser'
import { Text } from '@latitude-data/web-ui'
-import useCommits from '$/stores/commitsStore'
+import { useCommits } from '$/stores/commitsStore'
import { CommitItem, CommitItemSkeleton, SimpleUser } from './CommitItem'
import { CommitItemsWrapper } from './CommitItemsWrapper'
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/CurrentCommitsList.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/CurrentCommitsList.tsx
index 04ef5b360..12151aafb 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/CurrentCommitsList.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/CurrentCommitsList.tsx
@@ -6,7 +6,7 @@ import {
type Commit,
} from '@latitude-data/core/browser'
import { ReactStateDispatch } from '@latitude-data/web-ui'
-import useCommits from '$/stores/commitsStore'
+import { useCommits } from '$/stores/commitsStore'
import { CommitItem, CommitItemSkeleton, SimpleUser } from './CommitItem'
import { CommitItemsWrapper } from './CommitItemsWrapper'
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/DeleteDraftCommitModal/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/DeleteDraftCommitModal/index.tsx
index f4dc8cc2f..f77e5a1a6 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/DeleteDraftCommitModal/index.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CommitSelector/DeleteDraftCommitModal/index.tsx
@@ -7,7 +7,7 @@ import {
useCurrentProject,
} from '@latitude-data/web-ui'
import { ROUTES } from '$/services/routes'
-import useCommits from '$/stores/commitsStore'
+import { useCommits } from '$/stores/commitsStore'
import { useRouter } from 'next/navigation'
export default function DeleteDraftCommitModal({
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CreateDraftCommitModal/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CreateDraftCommitModal/index.tsx
index 453df5bfd..639723008 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CreateDraftCommitModal/index.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CreateDraftCommitModal/index.tsx
@@ -10,7 +10,7 @@ import {
} from '@latitude-data/web-ui'
import { useFormAction } from '$/hooks/useFormAction'
import { ROUTES } from '$/services/routes'
-import useCommits from '$/stores/commitsStore'
+import { useCommits } from '$/stores/commitsStore'
import { useRouter } from 'next/navigation'
export default function DraftCommitModal({
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/PublishDraftCommitModal/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/PublishDraftCommitModal/index.tsx
index 55ab8436c..e5cc2ea1d 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/PublishDraftCommitModal/index.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/PublishDraftCommitModal/index.tsx
@@ -22,7 +22,7 @@ import {
import { getChangedDocumentsInDraftAction } from '$/actions/commits/getChangedDocumentsInDraftAction'
import { useCurrentTheme } from '$/hooks/useCurrentTheme'
import { ROUTES } from '$/services/routes'
-import useCommits from '$/stores/commitsStore'
+import { useCommits } from '$/stores/commitsStore'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { useServerAction } from 'zsa-react'
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/layout.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/layout.tsx
index 5dd7513e8..06f6cec5f 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/layout.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/layout.tsx
@@ -3,13 +3,20 @@ import { ReactNode } from 'react'
import { readMetadata } from '@latitude-data/compiler'
import { EvaluationResultableType } from '@latitude-data/core/browser'
import { env } from '@latitude-data/env'
-import { Icon, TableWithHeader, Text, Tooltip } from '@latitude-data/web-ui'
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbSeparator,
+ Icon,
+ TableWithHeader,
+ Text,
+ Tooltip,
+} from '@latitude-data/web-ui'
import {
findCommitCached,
getProviderApiKeyCached,
} from '$/app/(private)/_data-access'
import BreadcrumbLink from '$/components/BreadcrumbLink'
-import { Breadcrumb } from '$/components/layouts/AppLayout/Header'
import { ROUTES } from '$/services/routes'
import Link from 'next/link'
@@ -65,50 +72,45 @@ export default async function ConnectedEvaluationLayout({
+
+
+
+
+
+
+ {evaluation.name}
+
+
+ {TYPE_TEXT[evaluation.configuration.type]}
+
+
- ),
- },
- {
- name: (
-
- {evaluation.name}
-
- {TYPE_TEXT[evaluation.configuration.type]}
-
-
-
-
- }
- >
- Go to evaluation
-
-
- ),
- },
- ]}
- />
+ >
+
+
+ }
+ >
+ Go to evaluation
+
+
+
}
actions={
-
- ),
- },
- {
- name: (
-
- ),
- },
- {
- name: (
-
- ),
- },
- ]}
- >
- {children}
-
+ {children}
)
diff --git a/apps/web/src/app/(private)/settings/layout.tsx b/apps/web/src/app/(private)/settings/layout.tsx
index eade7b6c7..0a24da804 100644
--- a/apps/web/src/app/(private)/settings/layout.tsx
+++ b/apps/web/src/app/(private)/settings/layout.tsx
@@ -1,11 +1,8 @@
import { ReactNode } from 'react'
-import { Container, Text, TitleWithActions } from '@latitude-data/web-ui'
+import { Container, TitleWithActions } from '@latitude-data/web-ui'
import { AppTabs } from '$/app/(private)/AppTabs'
-import { AppLayout } from '$/components/layouts'
-import { getCurrentUser } from '$/services/auth/getCurrentUser'
-import { NAV_LINKS } from '../_lib/constants'
import Memberships from './_components/Memberships'
import ProviderApiKeys from './_components/ProviderApiKeys'
import WorkspaceApiKeys from './_components/WorkspaceApiKeys'
@@ -16,31 +13,15 @@ export default async function SettingsLayout({
}: Readonly<{
children: ReactNode
}>) {
- const session = await getCurrentUser()
- const breadcrumbs = [
- {
- name: session.workspace.name,
- },
- {
- name: Settings,
- },
- ]
-
return (
-
-
-
- {children}
-
-
-
-
-
-
-
+
+
+ {children}
+
+
+
+
+
+
)
}
diff --git a/apps/web/src/app/not-found.tsx b/apps/web/src/app/not-found.tsx
index 7eeee4616..27c9ad043 100644
--- a/apps/web/src/app/not-found.tsx
+++ b/apps/web/src/app/not-found.tsx
@@ -1,28 +1,18 @@
import { Button } from '@latitude-data/web-ui'
import { ErrorComponent } from '@latitude-data/web-ui/browser'
-import { NAV_LINKS } from '$/app/(private)/_lib/constants'
-import { AppLayout } from '$/components/layouts'
-import { getSafeCurrentUser } from '$/services/auth/getCurrentUser'
import { ROUTES } from '$/services/routes'
import Link from 'next/link'
export default async function GlobalNoFound() {
- const session = await getSafeCurrentUser()
return (
-
-
-
-
- }
- />
-
+
+
+
+ }
+ />
)
}
diff --git a/apps/web/src/components/BreadcrumbLink/index.tsx b/apps/web/src/components/BreadcrumbLink/index.tsx
index f9fc751a1..9e8dee297 100644
--- a/apps/web/src/components/BreadcrumbLink/index.tsx
+++ b/apps/web/src/components/BreadcrumbLink/index.tsx
@@ -11,9 +11,14 @@ export default function BreadcrumbLink({
showBackIcon?: boolean
}) {
return (
-
+
{showBackIcon && }
- {name}
+
+ {name}
+
)
}
diff --git a/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Evaluations/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Evaluations/index.tsx
new file mode 100644
index 000000000..2e3c69629
--- /dev/null
+++ b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Evaluations/index.tsx
@@ -0,0 +1,53 @@
+import { useMemo } from 'react'
+
+import {
+ BreadcrumbItem,
+ BreadcrumbItemSkeleton,
+ BreadcrumbSeparator,
+} from '@latitude-data/web-ui'
+import { EvaluationRoutes, ROUTES } from '$/services/routes'
+import useEvaluations from '$/stores/evaluations'
+
+import { BreadcrumbSelector, BreadcrumbSelectorOption } from '../Selector'
+
+export function EvaluationBreadcrumbItems({
+ segments,
+}: {
+ segments: string[]
+}) {
+ const evaluationUuid = segments[0]!
+
+ const { data: evaluations, isLoading } = useEvaluations()
+ const currentEvaluation = useMemo(
+ () => evaluations?.find((evaluation) => evaluation.uuid === evaluationUuid),
+ [evaluations, evaluationUuid],
+ )
+
+ const options = useMemo(() => {
+ if (!evaluations) return []
+ return evaluations.map((p) => ({
+ label: p.name,
+ href: segments[1]
+ ? ROUTES.evaluations.detail({ uuid: p.uuid })[
+ segments[1] as EvaluationRoutes
+ ].root
+ : ROUTES.evaluations.detail({ uuid: p.uuid }).root,
+ }))
+ }, [evaluations, segments])
+
+ return (
+ <>
+
+
+ {isLoading ? (
+
+ ) : (
+
+ )}
+
+ >
+ )
+}
diff --git a/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Projects/Versions/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Projects/Versions/index.tsx
new file mode 100644
index 000000000..b6636cdc6
--- /dev/null
+++ b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Projects/Versions/index.tsx
@@ -0,0 +1,71 @@
+import { useMemo } from 'react'
+
+import { Commit, HEAD_COMMIT } from '@latitude-data/core/browser'
+import {
+ Badge,
+ BreadcrumbItem,
+ BreadcrumbItemSkeleton,
+ BreadcrumbSeparator,
+ ClickToCopy,
+ Text,
+} from '@latitude-data/web-ui'
+import { useCommitsFromProject } from '$/stores/commitsStore'
+
+export function CommitBreadcrumbItems({
+ segments,
+ projectId,
+}: {
+ segments: string[]
+ projectId: number
+}) {
+ const commitUuid = segments[0]!
+
+ const { data: commits, isLoading } = useCommitsFromProject(projectId, {
+ commitStatus: undefined,
+ })
+
+ const headCommit = useMemo(() => {
+ if (!commits) return undefined
+ return commits.reduce((acc, c) => {
+ if (!acc) return c
+ if (!acc.mergedAt) return c
+ if (c.mergedAt && acc.mergedAt < c.mergedAt) return c
+ return acc
+ }, undefined)
+ }, [commits])
+
+ const currentCommit = useMemo(() => {
+ if (!commits) return undefined
+ if (commitUuid === HEAD_COMMIT) return headCommit
+ return commits.find((c) => c.uuid === commitUuid)
+ }, [commits, headCommit, commitUuid])
+
+ return (
+ <>
+
+
+ {isLoading ? (
+
+ ) : (
+
+
+ {currentCommit?.title ?? commitUuid}
+
+
+
+ {commitUuid === HEAD_COMMIT
+ ? 'Live'
+ : currentCommit?.mergedAt
+ ? `v${currentCommit.version}`
+ : commitUuid.split('-')[0]}
+
+
+
+ )}
+
+ >
+ )
+}
diff --git a/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Projects/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Projects/index.tsx
new file mode 100644
index 000000000..1eed50fc3
--- /dev/null
+++ b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Projects/index.tsx
@@ -0,0 +1,53 @@
+import { useMemo } from 'react'
+
+import {
+ BreadcrumbItem,
+ BreadcrumbItemSkeleton,
+ BreadcrumbSeparator,
+} from '@latitude-data/web-ui'
+import { ROUTES } from '$/services/routes'
+import useProjects from '$/stores/projects'
+
+import { BreadcrumbSelector, BreadcrumbSelectorOption } from '../Selector'
+import { CommitBreadcrumbItems } from './Versions'
+
+export function ProjectBreadcrumbItems({ segments }: { segments: string[] }) {
+ const projectId = Number(segments[0])
+
+ const { data: projects, isLoading } = useProjects()
+ const currentProject = useMemo(
+ () => projects?.find((project) => project.id === projectId),
+ [projects, projectId],
+ )
+
+ const options = useMemo(() => {
+ if (!projects) return []
+ return projects.map((p) => ({
+ label: p.name,
+ href: ROUTES.projects.detail({ id: p.id }).root,
+ }))
+ }, [projects])
+
+ return (
+ <>
+
+
+ {isLoading ? (
+
+ ) : (
+
+ )}
+
+
+ {segments.length > 2 && segments[1] == 'versions' && (
+
+ )}
+ >
+ )
+}
diff --git a/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Root/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Root/index.tsx
new file mode 100644
index 000000000..3292009c4
--- /dev/null
+++ b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Root/index.tsx
@@ -0,0 +1,49 @@
+import { BreadcrumbItem, BreadcrumbSeparator } from '@latitude-data/web-ui'
+import { ROUTES } from '$/services/routes'
+
+import { EvaluationBreadcrumbItems } from '../Evaluations'
+import { ProjectBreadcrumbItems } from '../Projects'
+import { BreadcrumbSelector, BreadcrumbSelectorOption } from '../Selector'
+
+export function RootBreadcrumbItems({ segments }: { segments: string[] }) {
+ const options = {
+ projects: {
+ label: 'Projects',
+ href: ROUTES.projects.root,
+ },
+ evaluations: {
+ label: 'Evaluations',
+ href: ROUTES.evaluations.root,
+ },
+ datasets: {
+ label: 'Datasets',
+ href: ROUTES.datasets.root,
+ },
+ settings: {
+ label: 'Settings',
+ href: ROUTES.settings.root,
+ },
+ } as Record
+
+ const rootSegment = segments[0] === 'dashboard' ? 'projects' : segments[0] // For some reason the root URL for projects is "dashboard" instead of "projects"
+ const selectedOption = rootSegment ? options[rootSegment] : undefined
+
+ return (
+ <>
+
+
+
+
+
+ {segments.length > 1 && rootSegment === 'projects' && (
+
+ )}
+ {segments.length > 2 && rootSegment === 'evaluations' && (
+
+ )}
+ >
+ )
+}
diff --git a/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Selector.tsx b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Selector.tsx
new file mode 100644
index 000000000..f3c431f51
--- /dev/null
+++ b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/Selector.tsx
@@ -0,0 +1,73 @@
+'use client'
+
+import { ReactNode, useState } from 'react'
+import { isString } from 'lodash-es'
+
+import { Button, Icon, Popover, Text } from '@latitude-data/web-ui'
+import Link from 'next/link'
+
+export type BreadcrumbSelectorOption = {
+ href: string
+ label: string | ReactNode
+}
+
+export function BreadcrumbSelector({
+ label,
+ options,
+}: {
+ label: string | ReactNode
+ options: BreadcrumbSelectorOption[]
+}) {
+ const [isOpen, setIsOpen] = useState(false)
+
+ return (
+
+
+
+
+
+
+ {options.map(({ href, label: optionLabel }, idx) => (
+ -
+
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/index.tsx
new file mode 100644
index 000000000..3487e5008
--- /dev/null
+++ b/apps/web/src/components/layouts/AppLayout/Header/Breadcrumb/index.tsx
@@ -0,0 +1,23 @@
+import { Breadcrumb, Icon } from '@latitude-data/web-ui'
+import { ROUTES } from '$/services/routes'
+import Link from 'next/link'
+import { useSelectedLayoutSegments } from 'next/navigation'
+
+import { RootBreadcrumbItems } from './Root'
+
+export function HeaderBreadcrumb() {
+ const segments = useSelectedLayoutSegments()
+
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx
index 3c798ad53..4fa647700 100644
--- a/apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx
+++ b/apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx
@@ -104,7 +104,7 @@ export function UsageIndicator() {
showBackground
/>
-
+
{data?.usage} / {data?.max}
diff --git a/apps/web/src/components/layouts/AppLayout/Header/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/index.tsx
index 2c73c7aa0..cdb0eb83d 100644
--- a/apps/web/src/components/layouts/AppLayout/Header/index.tsx
+++ b/apps/web/src/components/layouts/AppLayout/Header/index.tsx
@@ -1,82 +1,12 @@
'use client'
-import { ReactNode } from 'react'
-
-import { Icon, SessionUser, Text, ThemeButton } from '@latitude-data/web-ui'
-import { ROUTES } from '$/services/routes'
-import Link from 'next/link'
-import { Fragment } from 'react/jsx-runtime'
+import { SessionUser, Text, ThemeButton } from '@latitude-data/web-ui'
import AvatarDropdown from './AvatarDropdown'
+import { HeaderBreadcrumb } from './Breadcrumb'
import { RewardsButton } from './Rewards'
import { UsageIndicator } from './UsageIndicator'
-function BreadcrumbSeparator() {
- return (
-
- )
-}
-
-type IBreadCrumb = {
- name: string | ReactNode
-}
-
-export function Breadcrumb({
- breadcrumbs,
- showLogo = false,
-}: {
- breadcrumbs: IBreadCrumb[]
- showLogo?: boolean
-}) {
- return (
-
- {showLogo ? (
- -
-
-
-
-
-
- ) : null}
- {breadcrumbs.map((breadcrumb, idx) => {
- const isLast = idx === breadcrumbs.length - 1
- return (
-
- -
- {typeof breadcrumb.name === 'string' ? (
-
- {breadcrumb.name as string}
-
- ) : (
- breadcrumb.name
- )}
-
- {!isLast ? (
- -
-
-
- ) : null}
-
- )
- })}
-
- )
-}
type INavigationLink = {
label: string
href?: string
@@ -98,28 +28,24 @@ function NavLink({ label, href, onClick, _target }: INavigationLink) {
export type AppHeaderProps = {
navigationLinks: INavigationLink[]
currentUser: SessionUser | undefined
- breadcrumbs?: IBreadCrumb[]
}
export default function AppHeader({
- breadcrumbs = [],
navigationLinks,
currentUser,
}: AppHeaderProps) {
return (
-
-
-
-
-
-
-
-
+
+
+
+
+
+
)
diff --git a/apps/web/src/components/layouts/AppLayout/index.tsx b/apps/web/src/components/layouts/AppLayout/index.tsx
index 1e6ee3b56..005bf9499 100644
--- a/apps/web/src/components/layouts/AppLayout/index.tsx
+++ b/apps/web/src/components/layouts/AppLayout/index.tsx
@@ -13,7 +13,6 @@ export type AppLayoutProps = AppHeaderProps & {
export default function AppLayout({
children,
currentUser,
- breadcrumbs,
navigationLinks,
scrollable = true,
}: AppLayoutProps) {
@@ -23,11 +22,7 @@ export default function AppLayout({
'overflow-y-auto custom-scrollbar': scrollable,
})}
>
-
+
{children}
diff --git a/apps/web/src/stores/commitsStore.ts b/apps/web/src/stores/commitsStore.ts
index 7fab67f7d..604a62f0d 100644
--- a/apps/web/src/stores/commitsStore.ts
+++ b/apps/web/src/stores/commitsStore.ts
@@ -8,23 +8,26 @@ import useLatitudeAction from '$/hooks/useLatitudeAction'
import { ROUTES } from '$/services/routes'
import useSWR, { SWRConfiguration } from 'swr'
-export default function useCommits(
- opts: SWRConfiguration & {
- onSuccessCreate?: (commit: Commit) => void
- onSuccessDestroy?: (commit: Commit) => void
- onSuccessPublish?: (commit: Commit) => void
- commitStatus?: CommitStatus
- } = {},
-) {
- const {
- onSuccessCreate,
- onSuccessDestroy,
- onSuccessPublish,
- commitStatus = CommitStatus.Draft,
- } = opts
+type CommitOptions = SWRConfiguration & {
+ onSuccessCreate?: (commit: Commit) => void
+ onSuccessDestroy?: (commit: Commit) => void
+ onSuccessPublish?: (commit: Commit) => void
+ commitStatus?: CommitStatus
+}
+
+export function useCommits(opts: CommitOptions = {}) {
const { project } = useCurrentProject()
+ return useCommitsFromProject(project.id, opts)
+}
+
+export function useCommitsFromProject(
+ projectId: number,
+ opts: CommitOptions = {},
+) {
+ const { onSuccessCreate, onSuccessDestroy, onSuccessPublish, commitStatus } =
+ opts
const { toast } = useToast()
- const route = ROUTES.api.projects.detail(project.id).commits.root
+ const route = ROUTES.api.projects.detail(projectId).commits.root
const fetcher = useFetcher(
commitStatus ? `${route}?status=${commitStatus}` : route,
)
@@ -33,7 +36,7 @@ export default function useCommits(
data = [],
mutate,
...rest
- } = useSWR
(['commits', project.id, commitStatus], fetcher, opts)
+ } = useSWR(['commits', projectId, commitStatus], fetcher, opts)
const { execute: createDraft, isPending: isCreating } = useLatitudeAction(
createDraftCommitAction,
{
diff --git a/packages/web-ui/src/ds/atoms/Button/index.tsx b/packages/web-ui/src/ds/atoms/Button/index.tsx
index ab44abadf..9314dc7c1 100644
--- a/packages/web-ui/src/ds/atoms/Button/index.tsx
+++ b/packages/web-ui/src/ds/atoms/Button/index.tsx
@@ -128,6 +128,7 @@ export type ButtonProps = ButtonHTMLAttributes &
isLoading?: boolean
fancy?: boolean
lookDisabled?: boolean
+ ellipsis?: boolean
}
const Button = forwardRef(function Button(
@@ -143,6 +144,7 @@ const Button = forwardRef(function Button(
children,
disabled,
lookDisabled,
+ ellipsis,
...props
},
ref,
@@ -164,6 +166,7 @@ const Button = forwardRef(function Button(
{
'w-full': fullWidth,
'opacity-50': lookDisabled,
+ 'overflow-hidden': ellipsis,
},
)}
ref={ref}
@@ -174,6 +177,9 @@ const Button = forwardRef(function Button(
className={cn(
'relative',
buttonVariants({ variant, size, className, fanciness }),
+ {
+ 'overflow-hidden': ellipsis,
+ },
)}
>
{variant === 'shiny' && (
@@ -188,6 +194,7 @@ const Button = forwardRef(function Button(
{iconProps ?
: null}
diff --git a/packages/web-ui/src/ds/atoms/Icons/index.tsx b/packages/web-ui/src/ds/atoms/Icons/index.tsx
index b421eeead..7c8deef24 100644
--- a/packages/web-ui/src/ds/atoms/Icons/index.tsx
+++ b/packages/web-ui/src/ds/atoms/Icons/index.tsx
@@ -5,6 +5,7 @@ import {
ChevronDown,
ChevronLeft,
ChevronRight,
+ ChevronsUpDown,
ChevronUp,
CircleAlert,
CircleHelp,
@@ -61,6 +62,7 @@ const Icons = {
chevronDown: ChevronDown,
chevronRight: ChevronRight,
chevronLeft: ChevronLeft,
+ chevronsUpDown: ChevronsUpDown,
circleHelp: CircleHelp,
clipboard: Copy,
code: Code,
diff --git a/packages/web-ui/src/ds/atoms/Tooltip/index.tsx b/packages/web-ui/src/ds/atoms/Tooltip/index.tsx
index 876794bb2..45a02a4ea 100644
--- a/packages/web-ui/src/ds/atoms/Tooltip/index.tsx
+++ b/packages/web-ui/src/ds/atoms/Tooltip/index.tsx
@@ -16,7 +16,7 @@ const TooltipRoot = TooltipPrimitive.Root
const TooltipTrigger = TooltipPrimitive.Trigger
-type TooltipVariant = 'default' | 'destructive'
+type TooltipVariant = 'default' | 'destructive' | 'inverse'
type PropviderProps = ComponentPropsWithoutRef
type RootProps = ComponentPropsWithoutRef
type ContentProps = ComponentPropsWithoutRef<
@@ -49,6 +49,7 @@ const TooltipContent = forwardRef<
{
'bg-background border': variant === 'default',
'bg-destructive': variant === 'destructive',
+ 'bg-foreground text-background': variant === 'inverse',
},
)}
{...props}
diff --git a/packages/web-ui/src/ds/molecules/Breadcrumb/Item.tsx b/packages/web-ui/src/ds/molecules/Breadcrumb/Item.tsx
new file mode 100644
index 000000000..d93ab5a3d
--- /dev/null
+++ b/packages/web-ui/src/ds/molecules/Breadcrumb/Item.tsx
@@ -0,0 +1,27 @@
+import { ReactNode } from 'react'
+
+import { cn } from '../../../lib/utils'
+import { Skeleton } from '../../atoms'
+
+export function BreadcrumbItem({
+ children,
+ noShrink,
+}: {
+ children: ReactNode
+ noShrink?: boolean
+}) {
+ return (
+
+ {children}
+
+ )
+}
+
+export function BreadcrumbItemSkeleton({ className }: { className?: string }) {
+ return
+}
diff --git a/packages/web-ui/src/ds/molecules/Breadcrumb/Separator.tsx b/packages/web-ui/src/ds/molecules/Breadcrumb/Separator.tsx
new file mode 100644
index 000000000..86d54a8e3
--- /dev/null
+++ b/packages/web-ui/src/ds/molecules/Breadcrumb/Separator.tsx
@@ -0,0 +1,17 @@
+export function BreadcrumbSeparator() {
+ return (
+
+ )
+}
diff --git a/packages/web-ui/src/ds/molecules/Breadcrumb/index.tsx b/packages/web-ui/src/ds/molecules/Breadcrumb/index.tsx
new file mode 100644
index 000000000..f5c043abb
--- /dev/null
+++ b/packages/web-ui/src/ds/molecules/Breadcrumb/index.tsx
@@ -0,0 +1,12 @@
+import { ReactNode } from 'react'
+
+export function Breadcrumb({ children }: { children: ReactNode }) {
+ return (
+
+ )
+}
+
+export * from './Item'
+export * from './Separator'
diff --git a/packages/web-ui/src/ds/molecules/ClicktoCopy/index.tsx b/packages/web-ui/src/ds/molecules/ClicktoCopy/index.tsx
index 729af0f00..d1daf3c15 100644
--- a/packages/web-ui/src/ds/molecules/ClicktoCopy/index.tsx
+++ b/packages/web-ui/src/ds/molecules/ClicktoCopy/index.tsx
@@ -20,6 +20,7 @@ export function ClickToCopy({
side='right'
align='center'
delayDuration={250}
+ variant='inverse'
trigger={
}
>
- Click to copy: {copyValue}
+ Click to copy: {copyValue}
)
}
diff --git a/packages/web-ui/src/ds/molecules/index.ts b/packages/web-ui/src/ds/molecules/index.ts
index 3136a1b91..8c7d847a6 100644
--- a/packages/web-ui/src/ds/molecules/index.ts
+++ b/packages/web-ui/src/ds/molecules/index.ts
@@ -18,3 +18,4 @@ export * from './CollapsibleBox'
export * from './LoadingText'
export * from './FakeProgress'
export * from './ThemeButton'
+export * from './Breadcrumb'