main
vue 134 lines 3.44 KB
Raw
1 <template>
2 <CardEntity>
3 <template #headerExtra>{{ formatDate(log.timestamp) }}</template>
4 <template #default>
5 <div class="flex flex-col gap-2">
6 <CardEntity
7 :embedded="eventTypeLower !== 'error'"
8 :status="eventTypeLower === 'error' ? 'error' : undefined"
9 >
10 <div class="flex flex-wrap gap-3 font-mono">
11 <div :class="`status-cat-${statusCategory}`">
12 <code>{{ log.status_code }}</code>
13 </div>
14 <div :class="`method-${methodLower}`">
15 <strong>{{ log.method }}</strong>
16 </div>
17 <div class="text-sm">
18 {{ log.route }}
19 </div>
20 </div>
21 </CardEntity>
22 <div class="flex flex-col gap-1 px-1">
23 {{ log.message }}
24
25 <p v-if="log.additional_info">
26 {{ log.additional_info }}
27 </p>
28 </div>
29 </div>
30 </template>
31
32 <template #mainExtra>
33 <div class="flex flex-wrap items-center gap-3">
34 <Badge type="splitted" :color="eventTypeLower === LogEventType.ERROR ? 'danger' : 'primary'">
35 <template #iconLeft>
36 <Icon :name="eventTypeLower === LogEventType.ERROR ? ErrorIcon : InfoIcon" :size="14" />
37 </template>
38 <template #label>Type</template>
39 <template #value>
40 {{ log.event_type }}
41 </template>
42 </Badge>
43 <Badge v-if="log.user_id" type="splitted" color="primary">
44 <template #iconLeft>
45 <Icon :name="UserIcon" :size="14" />
46 </template>
47 <template #value>
48 <span class="flex items-center gap-2">
49 <span>#{{ log.user_id }}</span>
50 <span v-if="username" class="flex gap-2">
51 <span>/</span>
52 <span>
53 {{ username }}
54 </span>
55 </span>
56 </span>
57 </template>
58 </Badge>
59 </div>
60 </template>
61 </CardEntity>
62 </template>
63
64 <script setup lang="ts">
65 // TODO-FE: refactor
66 import type { Log } from "@/types/logs.d"
67 import type { User } from "@/types/user.d"
68 import { computed } from "vue"
69 import Badge from "@/components/common/Badge.vue"
70 import CardEntity from "@/components/common/cards/CardEntity.vue"
71 import Icon from "@/components/common/Icon.vue"
72 import { useSettingsStore } from "@/stores/settings"
73 import { LogEventType } from "@/types/logs.d"
74 import dayjs from "@/utils/dayjs"
75
76 const { log, users } = defineProps<{ log: Log; users?: User[] }>()
77
78 const InfoIcon = "carbon:information"
79 const UserIcon = "carbon:user"
80 const ErrorIcon = "majesticons:exclamation-line"
81
82 const dFormats = useSettingsStore().dateFormat
83
84 const statusCategory = computed(() => log.status_code.toString()[0]?.toLowerCase())
85 const methodLower = computed(() => log.method.toLowerCase())
86 const eventTypeLower = computed(() => log.event_type.toLowerCase())
87 const username = computed(() => {
88 if (!users?.length) return ""
89
90 const user = users.find(o => o.id.toString() === log.user_id?.toString())
91
92 return user?.username || ""
93 })
94
95 function formatDate(timestamp: string | number | Date): string {
96 // Parse as UTC (since backend stores in UTC without 'Z' suffix - maybe address later), then convert to local
97 return dayjs.utc(timestamp).local().format(dFormats.datetime)
98 }
99 </script>
100
101 <style lang="scss" scoped>
102 .status {
103 &-cat-2 {
104 color: var(--success-color);
105 }
106 &-cat-3 {
107 color: var(--success-color);
108 }
109 &-cat-4 {
110 color: var(--warning-color);
111 }
112 &-cat-5 {
113 color: var(--error-color);
114 }
115 }
116
117 .method {
118 &-get {
119 color: var(--extra1-color);
120 }
121 &-option {
122 color: var(--extra3-color);
123 }
124 &-put {
125 color: var(--extra2-color);
126 }
127 &-post {
128 color: var(--primary-color);
129 }
130 &-delete {
131 color: var(--error-color);
132 }
133 }
134 </style>