Skip to content

Commit bb71397

Browse files
committed
feat: ✨ display issues instead of events
display issues by type in main graph and include sparklines for individual issues
1 parent eab5774 commit bb71397

File tree

5 files changed

+496
-40
lines changed

5 files changed

+496
-40
lines changed

event-viewer/app.vue

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,40 @@
11
<template>
2-
<div>
3-
<!-- <div class="flex space-x-2 mb-4 absolute right-0 m-2 z-20">
4-
<button :class="buttonClass" @click="setTimeRange('-1h')">1 Hour</button>
5-
<button :class="buttonClass" @click="setTimeRange('-6h')">6 Hours</button>
6-
<button :class="buttonClass" @click="setTimeRange('-24h')">24 Hours</button>
7-
<button :class="buttonClass" @click="setTimeRange('-48h')">48 Hours</button>
8-
<button :class="buttonClass" @click="setTimeRange('-7d')">1 Week</button>
9-
</div> -->
10-
<div class="relative h-64">
11-
<Chart v-if="events.length > 0" :data="events" />
2+
<div class="overflow-hidden">
3+
<div class="flex space-x-2 mb-4 absolute right-0 m-2 z-20 align-middle justify-center">
4+
<span class="text-sm">Last</span>
5+
<button :class="buttonClass" @click="setTimeRange('24h')">
6+
24 Hours
7+
</button>
8+
<button :class="buttonClass" @click="setTimeRange('14d')">14 Days</button>
9+
</div>
10+
<div class="relative h-64 w-full">
11+
<!-- <Chart v-if="events.length > 0" :data="events" /> -->
12+
<IssuesChart v-if="issues.length > 0" :data="issues" :loading="loading" />
1213
</div>
1314
<div class="h-[800px] overflow-scroll">
15+
<div
16+
v-if="!loading"
17+
class="flex flex-col space-y-4 p-2"
18+
v-for="issue in issues"
19+
:key="issue.id"
20+
>
21+
<IssuesCard :issue="issue" :loading="loading" />
22+
</div>
23+
<IssuesSkeleton
24+
v-else
25+
v-for="index of 20"
26+
:loading="true"
27+
/>
28+
</div>
29+
<!-- <div class="h-[800px] overflow-scroll">
1430
<div
1531
class="flex flex-col space-y-4 p-2"
1632
v-for="event in events"
1733
:key="event.id"
1834
>
1935
<Event :event="event" />
2036
</div>
21-
</div>
37+
</div> -->
2238
</div>
2339
</template>
2440

@@ -31,7 +47,9 @@ export default {
3147
organizationID: null,
3248
authToken: null,
3349
events: [],
34-
timeRange: "-24h", // Default time range
50+
issues: [],
51+
timeRange: "14d", // Default time range
52+
loading: false,
3553
};
3654
},
3755
mounted() {
@@ -56,39 +74,35 @@ export default {
5674
},
5775
methods: {
5876
init() {
59-
this.getEvents();
77+
this.getIssues();
6078
},
6179
setTimeRange(range) {
6280
this.timeRange = range;
63-
this.getEvents();
81+
this.getIssues();
6482
},
65-
async getEvents() {
66-
const endTimestamp = new Date().toISOString(); // Current timestamp in ISO format
67-
let startTimestamp;
68-
69-
switch (this.timeRange) {
70-
case "-1h":
71-
startTimestamp = new Date(Date.now() - 3600 * 1000).toISOString(); // 1 hour ago
72-
break;
73-
case "-6h":
74-
startTimestamp = new Date(Date.now() - 6 * 3600 * 1000).toISOString(); // 6 hours ago
75-
break;
76-
case "-24h":
77-
startTimestamp = new Date(Date.now() - 24 * 3600 * 1000).toISOString(); // 24 hours ago
78-
break;
79-
case "-48h":
80-
startTimestamp = new Date(Date.now() - 48 * 3600 * 1000).toISOString(); // 48 hours ago
81-
break;
82-
case "-7d":
83-
startTimestamp = new Date(Date.now() - 7 * 24 * 3600 * 1000).toISOString(); // 7 days ago
84-
break;
85-
default:
86-
startTimestamp = new Date(Date.now() - 24 * 3600 * 1000).toISOString(); // Default to 24 hours ago
83+
async getIssues() {
84+
this.loading = true;
85+
const url = `https://sentry.io/api/0/projects/${this.organizationID}/${this.projectID}/issues/?statsPeriod=${this.timeRange}`;
86+
try {
87+
const response = await fetch(url, {
88+
headers: {
89+
Authorization: `Bearer ${this.authToken}`,
90+
},
91+
});
92+
if (response.ok) {
93+
const data = await response.json();
94+
this.issues = data;
95+
} else {
96+
console.error("Failed to fetch events:", response.statusText);
97+
}
98+
} catch (error) {
99+
console.error("Error fetching events:", error);
100+
} finally {
101+
this.loading = false;
87102
}
88-
89-
const url = `https://sentry.io/api/0/projects/${this.organizationID}/${this.projectID}/events/?start=${encodeURIComponent(
90-
startTimestamp
91-
)}&end=${encodeURIComponent(endTimestamp)}`;
103+
},
104+
async getEvents() {
105+
const url = `https://sentry.io/api/0/projects/${this.organizationID}/${this.projectID}/issues/?statsPeriod=${this.timeRange}`;
92106
console.log("Fetching events with URL:", url);
93107
94108
try {
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<template>
2+
<div
3+
v-if="!loading"
4+
class="flex flex-col border-2 align-middle p-4 relative space-y-2"
5+
>
6+
<div id="issue-toolbar" class="flex text-sm justify-between border-b pb-2">
7+
<div class="flex flex-row flex-grow space-x-2 py-1 text-sm w-auto">
8+
<span
9+
class="px-2 bg-purple-100 rounded-md capitalize"
10+
:class="priorityColor"
11+
>
12+
<pre>{{ issue.priority }}</pre>
13+
</span>
14+
<span class="font-semibold truncate max-w-xs w-auto">{{
15+
issue.title
16+
}}</span>
17+
</div>
18+
<div class="justify-center flex-shrink align-middle space-x-1">
19+
<span
20+
class="px-2 py-1 bg-gray-100 rounded-md text-gray-400 space-x-1 align-middle"
21+
>
22+
Count: {{ issue.count }}
23+
</span>
24+
<span
25+
class="px-2 py-1 bg-gray-100 rounded-md text-gray-400 space-x-1 align-middle"
26+
>
27+
{{ issue.status }}
28+
</span>
29+
<span
30+
class="px-2 py-1 bg-gray-100 rounded-md text-gray-400 space-x-1 align-middle"
31+
>
32+
{{ issue.shortId }}
33+
</span>
34+
<span
35+
class="px-2 py-1 bg-gray-100 rounded-md text-gray-400 space-x-1 align-middle"
36+
>
37+
{{ issue.priority }}
38+
</span>
39+
<a target="blank" :href="issue.permalink">
40+
<button
41+
class="px-2 py-1 bg-gray-100 rounded-md text-gray-400 space-x-1 align-middle"
42+
>
43+
<span class="-ml-1">Open Issue</span>
44+
<icon name="majesticons:open" />
45+
</button>
46+
</a>
47+
</div>
48+
</div>
49+
<div class="px-2 py-4 relative">
50+
<div class="flex flex-col" v-if="issue.culprit">
51+
<pre class="text-sm font-bold text-gray-700 text-purple-500'">
52+
Culprit:</pre
53+
>
54+
<pre class="text-xs bg-gray-100 text-gray-400 p-2 rounded-sm w-full">{{
55+
issue.culprit
56+
}}</pre>
57+
</div>
58+
<div class="flex flex-col" v-if="issue.location">
59+
<pre class="text-sm font-bold text-gray-700 text-purple-500'">
60+
Location:</pre
61+
>
62+
<pre class="text-xs bg-gray-100 text-gray-400 p-2 rounded-sm w-full">{{
63+
issue.location
64+
}}</pre>
65+
</div>
66+
<div class="flex flex-row my-2" v-if="issue.message">
67+
<pre class="text-xs bg-gray-100 text-gray-400 p-2 rounded-sm w-full">{{
68+
issue.message
69+
}}</pre>
70+
</div>
71+
<div class="border-b py-1 my-4">
72+
<IssuesSparkline :stats="firstStatValue" />
73+
</div>
74+
</div>
75+
<div
76+
class="flex flex-row align-middle absolute bottom-0 right-0 m-4 space-x-2 p-2 bg-gray-500/10 rounded-sm"
77+
>
78+
<UserBrowser :browser="browserName" />
79+
<UserPlatform :platform="issue.platform" />
80+
</div>
81+
</div>
82+
</template>
83+
84+
<script>
85+
export default {
86+
props: {
87+
issue: {
88+
type: Object,
89+
required: true,
90+
},
91+
},
92+
methods: {
93+
readableDate(date) {
94+
return new Date(date).toDateString();
95+
},
96+
},
97+
computed: {
98+
priorityColor() {
99+
if (!this.issue) return null;
100+
switch (this.issue.priority) {
101+
case "high":
102+
return "bg-red-100 text-red-500";
103+
case "medium":
104+
return "bg-yellow-100 text-yellow-500";
105+
case "low":
106+
return "bg-green-100 text-green-500";
107+
default:
108+
return "bg-gray-100 text-gray-500";
109+
}
110+
},
111+
firstStatKey() {
112+
const keys = Object.keys(this.issue.stats);
113+
return keys.length > 0 ? keys[0] : null;
114+
},
115+
firstStatValue() {
116+
return this.issue.stats[this.firstStatKey] || null;
117+
},
118+
},
119+
};
120+
</script>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<template>
2+
<div class="flex flex-col space-y-4 p-2 animate-pulse">
3+
<div class="flex flex-col border-2 align-middle p-4 relative space-y-2">
4+
<div
5+
id="issue-toolbar"
6+
class="flex text-sm justify-between border-b pb-2"
7+
>
8+
<div class="flex flex-row flex-grow space-x-2 py-1 text-sm w-auto">
9+
<span class="px-2 bg-gray-100 rounded-md capitalize w-24"> </span>
10+
<span class="font-semibold truncate max-w-xs w-auto"></span>
11+
</div>
12+
<div class="flex flex-row justify-center align-middle space-x-1">
13+
<div
14+
class="px-2 py-1 bg-gray-100 rounded-md text-gray-400 space-x-1 align-middle w-16 h-5"
15+
></div>
16+
<span
17+
class="px-2 py-1 bg-gray-100 rounded-md text-gray-400 space-x-1 align-middle w-16"
18+
></span>
19+
<span
20+
class="px-2 py-1 bg-gray-100 rounded-md text-gray-400 space-x-1 align-middle w-16"
21+
></span>
22+
<span
23+
class="px-2 py-1 bg-gray-100 rounded-md text-gray-400 space-x-1 align-middle w-16"
24+
></span>
25+
</div>
26+
</div>
27+
<div class="px-2 py-4 relative">
28+
<div class="flex flex-col">
29+
<pre class="text-sm font-bold text-gray-700"><br></pre>
30+
<pre class="text-xs bg-gray-100 text-gray-400 p-2 rounded-sm w-full">
31+
<br></pre>
32+
</div>
33+
<div class="border-b py-1 my-4">
34+
<div class="vue-apexcharts bg-orange-100" style="min-height: 40px">
35+
</div>
36+
</div>
37+
</div>
38+
<div
39+
class="flex flex-row align-middle absolute bottom-0 right-0 m-4 space-x-2 p-2 bg-gray-500/10 rounded-sm w-16"
40+
>
41+
<br />
42+
</div>
43+
</div>
44+
</div>
45+
</template>
46+
47+
<script>
48+
export default {
49+
// Vue component logic here
50+
};
51+
</script>
52+
53+
<style scoped>
54+
/* Add any custom styles here if needed */
55+
</style>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<template>
2+
<apexchart type="area" :options="chartOptions" :series="series" height="40" />
3+
</template>
4+
5+
<script>
6+
export default {
7+
props: {
8+
stats: {
9+
type: Array,
10+
required: true,
11+
},
12+
},
13+
computed: {
14+
series() {
15+
return [
16+
{
17+
name: "Issue Count",
18+
data: this.stats.map((point) => point[1]),
19+
color: '#FFB833' // Explicitly set the color here if needed
20+
},
21+
];
22+
},
23+
chartOptions() {
24+
return {
25+
chart: {
26+
type: "area",
27+
sparkline: {
28+
enabled: true,
29+
},
30+
},
31+
stroke: {
32+
width: 2,
33+
colors: ["#FFB833"], // Change the line color here
34+
},
35+
fill: {
36+
type: "gradient",
37+
gradient: {
38+
shade: "light",
39+
type: "vertical",
40+
shadeIntensity: 1,
41+
gradientToColors: ["#FFB833"],
42+
inverseColors: false,
43+
opacityFrom: 0.8,
44+
opacityTo: 0.1,
45+
stops: [0, 100],
46+
},
47+
},
48+
tooltip: {
49+
enabled: true,
50+
theme: "light",
51+
},
52+
yaxis: {
53+
show: false,
54+
},
55+
xaxis: {
56+
labels: {
57+
show: true,
58+
formatter: (value) => {
59+
const date = new Date(value * 1000);
60+
return date.toLocaleDateString();
61+
},
62+
},
63+
tooltip: {
64+
enabled: false,
65+
},
66+
type: "datetime",
67+
categories: this.stats.map((point) => point[0] * 1000),
68+
},
69+
};
70+
},
71+
},
72+
};
73+
</script>
74+
75+
<style scoped>
76+
/* Add any custom styles here if needed */
77+
</style>

0 commit comments

Comments
 (0)