Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -354,21 +354,6 @@
<Table.Cell class="w-4 pr-0"></Table.Cell>
<Table.Cell class="flex items-center"><Skeleton class="h-6 w-full rounded-full" /></Table.Cell>{/if}
</Table.Row>
{#if projectQuery.isSuccess || event?.project_id}
<Table.Row class="group">
{#if projectQuery.isSuccess}
<Table.Head class="w-40 font-semibold whitespace-nowrap">Project</Table.Head>
<Table.Cell class="w-4 pr-0"
><EventsFacetedFilter.ProjectTrigger changed={filterChanged} class="mr-0" value={[projectQuery.data.id!]} /></Table.Cell
>
<Table.Cell>{projectQuery.data.name}</Table.Cell>
{:else}
<Table.Head class="w-40 font-semibold whitespace-nowrap"><Skeleton class="h-6 w-full rounded-full" /></Table.Head>
<Table.Cell class="w-4 pr-0"></Table.Cell>
<Table.Cell class="flex items-center"><Skeleton class="h-6 w-full rounded-full" /></Table.Cell>
{/if}
</Table.Row>
{/if}
</Table.Body>
</Table.Root>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@
</script>

{#if hasData}
<div class={['flex flex-col space-y-2', className]}>
<div class={['flex items-center', showTitle ? 'justify-between' : 'justify-end']}>
<div class={['relative flex flex-col', showTitle && 'space-y-2', className]}>
<div class={['flex items-center', showTitle ? 'justify-between' : 'absolute top-0 right-0 z-10']}>
{#if showTitle}
<H4>{title}</H4>
{/if}
Expand Down Expand Up @@ -158,7 +158,7 @@
</DropdownMenu.Root>
</div>

<div class="grow overflow-auto text-xs">
<div class={['grow overflow-auto text-xs', !showTitle && 'pr-10']}>
{#if showRaw || !canToggle}
{#if showJSONCodeEditor}
<CodeBlock {code} language="json" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
</Alert.Root>
{/if}

<div class:opacity-60={!hasPremiumFeatures}>
<div class="relative pr-10" class:opacity-60={!hasPremiumFeatures}>
{#if isSessionStart}
<Table.Root class="mb-4">
<Table.Body>
Expand Down Expand Up @@ -139,7 +139,7 @@
</Table.Root>
{/if}

<div class="mb-2 flex justify-end">
<div class="absolute top-0 right-0 z-10">
<Button
aria-label="Open events filtered to this session"
disabled={!sessionEventsHref}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script lang="ts">
import * as Dialog from '$comp/ui/dialog';
import * as Table from '$comp/ui/table';

import type { Stack } from '../../models';

interface Props {
open: boolean;
stack: Stack;
}

let { open = $bindable(), stack }: Props = $props();

const signatureEntries = $derived(Object.entries(stack.signature_info ?? {}).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)));
</script>

<Dialog.Root bind:open>
<Dialog.Content class="max-h-[calc(100dvh-2rem)] overflow-y-auto sm:max-w-2xl">
<Dialog.Header>
<Dialog.Title>Stacking Information</Dialog.Title>
<Dialog.Description>Values used to group events into this stack.</Dialog.Description>
</Dialog.Header>

{#if signatureEntries.length > 0}
<Table.Root>
<Table.Body>
{#each signatureEntries as [key, value] (key)}
<Table.Row>
<Table.Head class="w-48 font-semibold whitespace-nowrap">{key}</Table.Head>
<Table.Cell class="break-all">{value}</Table.Cell>
</Table.Row>
{/each}
</Table.Body>
</Table.Root>
{:else}
<p class="text-muted-foreground text-sm">No stacking information is available for this stack.</p>
{/if}
</Dialog.Content>
</Dialog.Root>
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
import * as ButtonGroup from '$comp/ui/button-group';
import * as Card from '$comp/ui/card';
import { Skeleton } from '$comp/ui/skeleton';
import * as Table from '$comp/ui/table';
import * as Tooltip from '$comp/ui/tooltip';
import { getProjectCountQuery, getStackCountQuery } from '$features/events/api.svelte';
import EventsStackChart, { type EventsStackChartPoint } from '$features/events/components/events-stack-chart.svelte';
import * as EventsFacetedFilter from '$features/events/components/filters';
import { StringFilter } from '$features/events/components/filters';
import { getProjectQuery } from '$features/projects/api.svelte';
import * as agg from '$features/shared/api/aggregations';
import { fillDateSeries } from '$features/shared/utils/charts';
import { getStackQuery } from '$features/stacks/api.svelte';
Expand All @@ -24,8 +26,6 @@
import LastOccurrence from '@lucide/svelte/icons/arrow-right-circle';
import Calendar from '@lucide/svelte/icons/calendar';
import EventsIcon from '@lucide/svelte/icons/calendar-days';
import Clock from '@lucide/svelte/icons/clock';
import Filter from '@lucide/svelte/icons/filter';
import Info from '@lucide/svelte/icons/info';
import Users from '@lucide/svelte/icons/users';

Expand Down Expand Up @@ -66,6 +66,14 @@
// TODO: Log Level
const stack = $derived(stackQuery.data!);

const projectQuery = getProjectQuery({
route: {
get id() {
return stack?.project_id;
}
}
});

// TODO: Add stack charts for Occurrences, Average Value, Value Sum
const stackCountQuery = getStackCountQuery({
params: {
Expand Down Expand Up @@ -273,34 +281,67 @@

<EventsStackChart class="h-12 w-full" data={chartData()} isLoading={stackCountQuery.isLoading} />

<div class="grid grid-cols-1 gap-x-4 lg:grid-cols-2">
{#if (stack.status === 'fixed' || stack.status === 'regressed') && stack.date_fixed}
<div class="flex items-center gap-2 text-sm">
<Calendar class="size-4 text-green-500" />
<span>
Fixed {stack.fixed_in_version && `in ${stack.fixed_in_version}`} on <DateTime value={stack.date_fixed} />
</span>
</div>
{/if}

{#if stack.status === 'snoozed' && stack.snooze_until_utc}
<div class="flex items-center gap-2 text-sm">
<Clock class="size-4 text-blue-500" />
<span>Snoozed until <DateTime value={stack.snooze_until_utc} /></span>
</div>
{/if}
</div>

{#if stack.tags && stack.tags.length > 0}
<div class="flex flex-wrap gap-2">
{#each stack.tags as tag (tag)}
<EventsFacetedFilter.TagTrigger changed={filterChanged} value={[tag]}>
<Filter class="size-3" />
{tag}
</EventsFacetedFilter.TagTrigger>
{/each}
</div>
{/if}
<Table.Root>
<Table.Body>
{#if projectQuery.isSuccess || stack.project_id}
<Table.Row class="group">
{#if projectQuery.isSuccess}
<Table.Head class="w-36 font-semibold whitespace-nowrap">Project</Table.Head>
<Table.Cell class="relative w-4 pr-0">
<EventsFacetedFilter.ProjectTrigger
changed={filterChanged}
class="absolute top-1/2 left-0 -translate-y-1/2"
value={[projectQuery.data.id!]}
/>
</Table.Cell>
<Table.Cell>{projectQuery.data.name}</Table.Cell>
{:else}
<Table.Head class="w-36 font-semibold whitespace-nowrap"><Skeleton class="h-6 w-full rounded-full" /></Table.Head>
<Table.Cell class="w-4 pr-0"></Table.Cell>
<Table.Cell class="flex items-center"><Skeleton class="h-6 w-full rounded-full" /></Table.Cell>
{/if}
</Table.Row>
{/if}
{#if stack.tags && stack.tags.length > 0}
<Table.Row class="group">
<Table.Head class="w-36 font-semibold whitespace-nowrap">Tags</Table.Head>
<Table.Cell class="relative w-4 pr-0">
<EventsFacetedFilter.TagTrigger
changed={filterChanged}
class="absolute top-1/2 left-0 -translate-y-1/2"
value={[stack.tags[0]!]}
/>
</Table.Cell>
<Table.Cell class="flex flex-wrap items-center justify-start gap-2 overflow-auto">
<span>{stack.tags[0]}</span>
{#each stack.tags.slice(1) as tag (tag)}
<span class="inline-flex items-center">
<EventsFacetedFilter.TagTrigger changed={filterChanged} value={[tag]} />
<span>{tag}</span>
</span>
{/each}
</Table.Cell>
</Table.Row>
{/if}
{#if (stack.status === 'fixed' || stack.status === 'regressed') && stack.date_fixed}
<Table.Row>
<Table.Head class="w-36 font-semibold whitespace-nowrap">Fixed</Table.Head>
<Table.Cell class="w-4 pr-0"></Table.Cell>
<Table.Cell>
{stack.fixed_in_version && `In ${stack.fixed_in_version} on `}
<DateTime value={stack.date_fixed} />
</Table.Cell>
</Table.Row>
{/if}
{#if stack.status === 'snoozed' && stack.snooze_until_utc}
<Table.Row>
<Table.Head class="w-36 font-semibold whitespace-nowrap">Snoozed Until</Table.Head>
<Table.Cell class="w-4 pr-0"></Table.Cell>
<Table.Cell><DateTime value={stack.snooze_until_utc} /></Table.Cell>
</Table.Row>
{/if}
</Table.Body>
</Table.Root>

<StackReferences {stack} />
</Card.Content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import * as DropdownMenu from '$comp/ui/dropdown-menu';
import { showBillingDialogOnUpgradeProblem } from '$features/billing';
import Reference from '@lucide/svelte/icons/link-2';
import ListTree from '@lucide/svelte/icons/list-tree';
import Settings from '@lucide/svelte/icons/settings';
import Delete from '@lucide/svelte/icons/trash';
import PromoteToExternal from '@lucide/svelte/icons/trending-up';
Expand All @@ -16,6 +17,7 @@
import AddStackReferenceDialog from './dialogs/add-stack-reference-dialog.svelte';
import RemoveStackDialog from './dialogs/remove-stack-dialog.svelte';
import RequiresPromotedWebHookDialog from './dialogs/requires-promoted-web-hook-dialog.svelte';
import StackingInformationDialog from './dialogs/stacking-information-dialog.svelte';

interface Props {
onDeleted?: () => void;
Expand All @@ -26,6 +28,7 @@
let openAddStackReferenceDialog = $state<boolean>(false);
let openRemoveStackDialog = $state<boolean>(false);
let openRequiresPromotedWebHookDialog = $state<boolean>(false);
let openStackingInformationDialog = $state<boolean>(false);

const addLink = postAddLink({
route: {
Expand Down Expand Up @@ -115,8 +118,8 @@
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props} variant="outline" size="icon">
<Settings class="size-4" />
<Button {...props} aria-label="Stack options" title="Stack options" variant="outline" size="icon">
<Settings />
</Button>
{/snippet}
</DropdownMenu.Trigger>
Expand All @@ -140,6 +143,10 @@
<Reference class="mr-2 size-4" />
Add Reference Link
</DropdownMenu.Item>
<DropdownMenu.Item onclick={() => (openStackingInformationDialog = true)} title="View the values used to group events into this stack">
<ListTree />
View Stacking Information
</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item onclick={() => (openRemoveStackDialog = true)} class="text-destructive" title="Delete this stack">
<Delete class="mr-2 size-4" />
Expand All @@ -149,6 +156,9 @@
</DropdownMenu.Content>
</DropdownMenu.Root>

{#if openStackingInformationDialog}
<StackingInformationDialog bind:open={openStackingInformationDialog} {stack} />
{/if}
{#if openAddStackReferenceDialog}
<AddStackReferenceDialog bind:open={openAddStackReferenceDialog} save={addReference} />
{/if}
Expand Down
Loading