@necrolab/dashboard 0.4.35 → 0.4.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/App.vue +32 -8
- package/src/components/Editors/Account/AccountView.vue +8 -2
- package/src/components/Editors/Profile/ProfileView.vue +8 -2
- package/src/components/Editors/TagLabel.vue +68 -6
- package/src/components/Table/Header.vue +1 -1
- package/src/components/Table/Row.vue +1 -1
- package/src/components/Table/Table.vue +2 -2
- package/src/components/Tasks/TaskView.vue +20 -24
- package/src/components/ui/Navbar.vue +43 -2
- package/src/components/ui/ReconnectIndicator.vue +350 -54
- package/src/components/ui/controls/CountryChooser.vue +2 -2
- package/src/components/ui/controls/atomic/Checkbox.vue +11 -11
- package/src/views/Editor.vue +61 -1
- package/src/views/FilterBuilder.vue +3 -3
- package/static/offline.html +7 -7
package/package.json
CHANGED
package/src/App.vue
CHANGED
|
@@ -50,14 +50,17 @@
|
|
|
50
50
|
<div v-else key="main-components" class="flex">
|
|
51
51
|
<Navbar v-if="layout == 'dashboard'" class="fixed" />
|
|
52
52
|
<div :class="['router-wrapper', { '-mt-[60px]': ui.pullChange > 1 }]"></div>
|
|
53
|
-
<router-view v-slot="{ Component }">
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
<router-view v-slot="{ Component, route }">
|
|
54
|
+
<transition name="page-transition" mode="out-in">
|
|
55
|
+
<component
|
|
56
|
+
:is="Component"
|
|
57
|
+
:key="route.path"
|
|
58
|
+
:class="[
|
|
59
|
+
'component-container pb-2 mt-0 lg:mt-4 ios-wrapper',
|
|
60
|
+
{ 'w-full': landscapeIos }
|
|
61
|
+
]"
|
|
62
|
+
/>
|
|
63
|
+
</transition>
|
|
61
64
|
</router-view>
|
|
62
65
|
</div>
|
|
63
66
|
</transition>
|
|
@@ -296,4 +299,25 @@ const layout = computed(() => router.currentRoute.value.meta.layout);
|
|
|
296
299
|
.component-container {
|
|
297
300
|
@apply w-full mx-auto px-4 xs:px-4 md:px-2 lg:px-6 xl:px-10;
|
|
298
301
|
}
|
|
302
|
+
|
|
303
|
+
// Page navigation transitions
|
|
304
|
+
.page-transition-enter-active {
|
|
305
|
+
transition: all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.page-transition-leave-active {
|
|
309
|
+
transition: all 0.15s cubic-bezier(0.55, 0.085, 0.68, 0.53);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.page-transition-enter-from {
|
|
313
|
+
opacity: 0;
|
|
314
|
+
transform: translateX(15px) scale(0.98);
|
|
315
|
+
filter: blur(1px);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.page-transition-leave-to {
|
|
319
|
+
opacity: 0;
|
|
320
|
+
transform: translateX(-10px) scale(1.02);
|
|
321
|
+
filter: blur(0.5px);
|
|
322
|
+
}
|
|
299
323
|
</style>
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
:items="toRender"
|
|
36
36
|
:item-size="64"
|
|
37
37
|
key-field="_id"
|
|
38
|
-
class="scroller vue-recycle-scroller ready direction-vertical flex flex-col divide-y
|
|
38
|
+
class="scroller vue-recycle-scroller ready direction-vertical flex flex-col divide-y divide-dark-650 max-h-big overflow-y-auto hidden-scrollbars overflow-x-hidden stop-pan"
|
|
39
39
|
>
|
|
40
40
|
<template #default="props">
|
|
41
41
|
<div class="task" :key="i[props.item._id]">
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
</template>
|
|
49
49
|
</RecycleScroller>
|
|
50
50
|
</div>
|
|
51
|
-
<div v-else class="flex justify-center
|
|
51
|
+
<div v-else class="flex justify-center py-8 bg-dark-400 empty-state">
|
|
52
52
|
No accounts found
|
|
53
53
|
</div>
|
|
54
54
|
</Table>
|
|
@@ -64,6 +64,12 @@ h4 {
|
|
|
64
64
|
.stop-pan {
|
|
65
65
|
touch-action: pan-y pan-up pan-down;
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
.empty-state {
|
|
69
|
+
color: #969696;
|
|
70
|
+
font-size: 14px;
|
|
71
|
+
font-weight: 500;
|
|
72
|
+
}
|
|
67
73
|
</style>
|
|
68
74
|
<script setup>
|
|
69
75
|
import { Table, Header } from "@/components/Table";
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
:items="toRender"
|
|
40
40
|
:item-size="64"
|
|
41
41
|
key-field="index"
|
|
42
|
-
class="scroller max-h-big vue-recycle-scroller ready direction-vertical flex flex-col divide-y
|
|
42
|
+
class="scroller max-h-big vue-recycle-scroller ready direction-vertical flex flex-col divide-y divide-dark-650 overflow-y-auto hidden-scrollbars overflow-x-hidden stop-pan"
|
|
43
43
|
>
|
|
44
44
|
<template #default="props">
|
|
45
45
|
<div class="task" :key="i[props.item.index]">
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
</template>
|
|
53
53
|
</RecycleScroller>
|
|
54
54
|
</div>
|
|
55
|
-
<div v-else class="flex justify-center
|
|
55
|
+
<div v-else class="flex justify-center py-8 bg-dark-400 empty-state">
|
|
56
56
|
No profiles found
|
|
57
57
|
</div>
|
|
58
58
|
</Table>
|
|
@@ -73,6 +73,12 @@ h4 {
|
|
|
73
73
|
max-height: calc(100vh - 20rem);
|
|
74
74
|
overflow: hidden;
|
|
75
75
|
}
|
|
76
|
+
|
|
77
|
+
.empty-state {
|
|
78
|
+
color: #969696;
|
|
79
|
+
font-size: 14px;
|
|
80
|
+
font-weight: 500;
|
|
81
|
+
}
|
|
76
82
|
</style>
|
|
77
83
|
<script setup>
|
|
78
84
|
import { Table, Header } from "@/components/Table";
|
|
@@ -1,16 +1,78 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="
|
|
3
|
-
<span class="
|
|
2
|
+
<div class="tag-pill">
|
|
3
|
+
<span class="tag-text">{{ formatText(props.text) }}</span>
|
|
4
4
|
</div>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
|
-
<style scoped>
|
|
8
|
-
.
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
<style lang="scss" scoped>
|
|
8
|
+
.tag-pill {
|
|
9
|
+
@apply inline-flex items-center justify-center rounded-md transition-all duration-200;
|
|
10
|
+
background: linear-gradient(145deg, #3d3e44, #35363c);
|
|
11
|
+
border: 1px solid #4b4c53;
|
|
12
|
+
padding: 0.1875rem 0.5rem;
|
|
13
|
+
min-width: 2rem;
|
|
14
|
+
max-width: 4.5rem;
|
|
15
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
|
16
|
+
|
|
17
|
+
&:hover {
|
|
18
|
+
background: linear-gradient(145deg, #44454b, #3d3e44);
|
|
19
|
+
border-color: #52535a;
|
|
20
|
+
transform: translateY(-0.5px);
|
|
21
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.tag-text {
|
|
26
|
+
@apply text-white font-semibold truncate;
|
|
27
|
+
font-size: 0.6875rem;
|
|
28
|
+
line-height: 1.1;
|
|
29
|
+
letter-spacing: 0.025em;
|
|
30
|
+
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Ultra responsive design
|
|
34
|
+
@media (max-width: 1024px) {
|
|
35
|
+
.tag-pill {
|
|
36
|
+
padding: 0.1rem 0.3rem;
|
|
37
|
+
max-width: 3.5rem;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.tag-text {
|
|
41
|
+
font-size: 0.575rem;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@media (max-width: 768px) {
|
|
46
|
+
.tag-pill {
|
|
47
|
+
padding: 0.075rem 0.25rem;
|
|
48
|
+
max-width: 3rem;
|
|
49
|
+
min-width: 1.25rem;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.tag-text {
|
|
53
|
+
font-size: 0.55rem;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@media (max-width: 480px) {
|
|
58
|
+
.tag-pill {
|
|
59
|
+
padding: 0.05rem 0.2rem;
|
|
60
|
+
max-width: 2.5rem;
|
|
61
|
+
min-width: 1rem;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.tag-text {
|
|
65
|
+
font-size: 0.5rem;
|
|
66
|
+
}
|
|
11
67
|
}
|
|
12
68
|
</style>
|
|
13
69
|
|
|
14
70
|
<script setup>
|
|
15
71
|
const props = defineProps({ text: { type: String } });
|
|
72
|
+
|
|
73
|
+
const formatText = (text) => {
|
|
74
|
+
if (!text) return '';
|
|
75
|
+
// Capitalize first letter and keep rest as-is
|
|
76
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
77
|
+
};
|
|
16
78
|
</script>
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
</template>
|
|
6
6
|
<style lang="scss">
|
|
7
7
|
.table-component {
|
|
8
|
-
@apply flex-col bg-clip-padding rounded relative box-border border border-
|
|
8
|
+
@apply flex-col bg-clip-padding rounded relative box-border border border-dark-600 bg-dark-500;
|
|
9
9
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
.table-component > .grid {
|
|
13
|
-
@apply bg-dark-
|
|
13
|
+
@apply bg-dark-400;
|
|
14
14
|
border-bottom: 1px solid #3d3e44;
|
|
15
15
|
}
|
|
16
16
|
</style>
|
|
@@ -24,10 +24,7 @@
|
|
|
24
24
|
<TicketIcon class="mr-0 ipadlg:mr-3" />
|
|
25
25
|
<h4 class="hidden ipadlg:flex">Tickets</h4>
|
|
26
26
|
</div>
|
|
27
|
-
<div
|
|
28
|
-
class="col-span-6 md:col-span-4 lg:col-span-3 flex-center"
|
|
29
|
-
@click="ui.toggleSort('status')"
|
|
30
|
-
>
|
|
27
|
+
<div class="col-span-6 md:col-span-4 lg:col-span-3 flex-center" @click="ui.toggleSort('status')">
|
|
31
28
|
<StatusIcon class="mr-0 ipadlg:mr-3" />
|
|
32
29
|
<h4 class="hidden ipadlg:flex">Status</h4>
|
|
33
30
|
<DownIcon v-if="ui.sortData.sortBy === 'status' && !ui.sortData.reversed" class="ml-1" />
|
|
@@ -44,22 +41,19 @@
|
|
|
44
41
|
</div>
|
|
45
42
|
</Header>
|
|
46
43
|
<div
|
|
47
|
-
class="flex flex-col divide-y divide-
|
|
44
|
+
class="flex flex-col divide-y divide-dark-650 overflow-y-auto hidden-scrollbars overflow-x-hidden stop-pan"
|
|
48
45
|
:style="{ maxHeight: dynamicTableHeight }"
|
|
49
46
|
>
|
|
50
|
-
<div
|
|
51
|
-
v-for="(task, i) in getTasksInOrder()"
|
|
52
|
-
:key="task.taskId"
|
|
47
|
+
<div
|
|
48
|
+
v-for="(task, i) in getTasksInOrder()"
|
|
49
|
+
:key="task.taskId"
|
|
53
50
|
:class="[i % 2 == 1 ? 'task-row-odd' : 'task-row-even']"
|
|
54
51
|
class="task-row-container"
|
|
55
52
|
>
|
|
56
53
|
<Task :task="task" />
|
|
57
54
|
</div>
|
|
58
|
-
<div
|
|
59
|
-
v-if="
|
|
60
|
-
class="flex justify-center py-8 empty-state"
|
|
61
|
-
>
|
|
62
|
-
<span v-if="ui.queueStats.total === 0"> No tasks yet.</span>
|
|
55
|
+
<div v-if="getTasksInOrder().length === 0" class="flex justify-center py-8 empty-state">
|
|
56
|
+
<span v-if="ui.queueStats.total === 0"> No tasks yet</span>
|
|
63
57
|
<span v-else>{{ ui.queueStats.total }} hidden task{{ ui.queueStats.total === 1 ? "" : "s" }}</span>
|
|
64
58
|
</div>
|
|
65
59
|
</div>
|
|
@@ -78,11 +72,11 @@ h4 {
|
|
|
78
72
|
min-height: 69px;
|
|
79
73
|
flex-shrink: 0;
|
|
80
74
|
transition: background-color 0.15s ease;
|
|
81
|
-
|
|
75
|
+
|
|
82
76
|
&:hover {
|
|
83
77
|
@apply bg-dark-550 !important;
|
|
84
78
|
}
|
|
85
|
-
|
|
79
|
+
|
|
86
80
|
@media (max-width: 768px) {
|
|
87
81
|
min-height: 58px;
|
|
88
82
|
}
|
|
@@ -159,11 +153,11 @@ const updateDimensions = () => {
|
|
|
159
153
|
};
|
|
160
154
|
|
|
161
155
|
onMounted(() => {
|
|
162
|
-
window.addEventListener(
|
|
156
|
+
window.addEventListener("resize", updateDimensions);
|
|
163
157
|
});
|
|
164
158
|
|
|
165
159
|
onUnmounted(() => {
|
|
166
|
-
window.removeEventListener(
|
|
160
|
+
window.removeEventListener("resize", updateDimensions);
|
|
167
161
|
});
|
|
168
162
|
|
|
169
163
|
const dynamicTableHeight = computed(() => {
|
|
@@ -174,20 +168,22 @@ const dynamicTableHeight = computed(() => {
|
|
|
174
168
|
const controlsHeight = windowWidth.value >= 650 ? 60 : 0; // Desktop controls
|
|
175
169
|
const filtersHeight = 50; // Event dropdown and filter toggle
|
|
176
170
|
const utilitiesHeight = windowWidth.value <= 480 && windowHeight.value > windowWidth.value ? 150 : 200; // Reduce utilities height in mobile portrait
|
|
177
|
-
const margins =
|
|
178
|
-
|
|
179
|
-
|
|
171
|
+
const margins =
|
|
172
|
+
windowWidth.value >= 1024 ? 40 : windowWidth.value <= 480 && windowHeight.value > windowWidth.value ? 15 : 20; // Reduce margins in mobile portrait
|
|
173
|
+
|
|
174
|
+
const totalUsedSpace =
|
|
175
|
+
headerHeight + titleHeight + statsHeight + controlsHeight + filtersHeight + utilitiesHeight + margins;
|
|
180
176
|
const availableHeight = windowHeight.value - totalUsedSpace;
|
|
181
|
-
|
|
177
|
+
|
|
182
178
|
// Calculate row height based on screen size
|
|
183
179
|
const rowHeight = windowWidth.value <= 768 ? 58 : 69; // Mobile vs desktop row height
|
|
184
180
|
const minRowsToShow = 2; // Always show at least 2 rows
|
|
185
181
|
const minHeight = minRowsToShow * rowHeight;
|
|
186
|
-
|
|
182
|
+
|
|
187
183
|
// Calculate how many complete rows can fit
|
|
188
184
|
const maxCompleteRows = Math.floor(Math.max(availableHeight, minHeight) / rowHeight);
|
|
189
185
|
const exactHeight = maxCompleteRows * rowHeight;
|
|
190
|
-
|
|
191
|
-
return exactHeight +
|
|
186
|
+
|
|
187
|
+
return exactHeight + "px";
|
|
192
188
|
});
|
|
193
189
|
</script>
|
|
@@ -119,24 +119,65 @@
|
|
|
119
119
|
.navbar {
|
|
120
120
|
@apply border-b py-5 fixed w-full;
|
|
121
121
|
top: 0;
|
|
122
|
-
z-index:
|
|
122
|
+
z-index: 1000;
|
|
123
|
+
background: rgba(26, 27, 30, 0.95);
|
|
123
124
|
backdrop-filter: blur(23px);
|
|
125
|
+
-webkit-backdrop-filter: blur(23px);
|
|
124
126
|
border-color: #3d3e44;
|
|
125
127
|
|
|
126
128
|
ul {
|
|
127
129
|
@apply gap-x-4;
|
|
128
130
|
|
|
129
131
|
li a {
|
|
130
|
-
@apply flex text-white text-sm border-none h-10 items-center
|
|
132
|
+
@apply flex text-white text-sm border-none h-10 items-center rounded-lg;
|
|
131
133
|
|
|
132
134
|
svg {
|
|
133
135
|
@apply mr-2;
|
|
136
|
+
width: 20px;
|
|
137
|
+
height: 20px;
|
|
134
138
|
}
|
|
135
139
|
|
|
136
140
|
&.router-link-exact-active {
|
|
137
141
|
@apply border-solid border border-light-300;
|
|
138
142
|
}
|
|
139
143
|
}
|
|
144
|
+
|
|
145
|
+
// Desktop mode (lg to xl): adjust padding and border for icon-only view
|
|
146
|
+
@media (min-width: 1024px) and (max-width: 1279px) {
|
|
147
|
+
li a {
|
|
148
|
+
@apply px-3;
|
|
149
|
+
|
|
150
|
+
&.router-link-exact-active {
|
|
151
|
+
@apply border-none bg-transparent;
|
|
152
|
+
|
|
153
|
+
svg {
|
|
154
|
+
@apply border border-light-300 rounded-lg;
|
|
155
|
+
padding: 6px;
|
|
156
|
+
width: 32px;
|
|
157
|
+
height: 32px;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
svg {
|
|
162
|
+
@apply mr-0;
|
|
163
|
+
width: 20px;
|
|
164
|
+
height: 20px;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// XL and above: full width with text
|
|
170
|
+
@media (min-width: 1280px) {
|
|
171
|
+
li a {
|
|
172
|
+
@apply px-4;
|
|
173
|
+
|
|
174
|
+
svg {
|
|
175
|
+
@apply mr-2;
|
|
176
|
+
width: 20px;
|
|
177
|
+
height: 20px;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
140
181
|
}
|
|
141
182
|
}
|
|
142
183
|
|
|
@@ -1,23 +1,98 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="
|
|
3
|
-
<
|
|
4
|
-
<div class="
|
|
5
|
-
|
|
2
|
+
<div class="reconnect-overlay">
|
|
3
|
+
<transition name="background-fill" appear>
|
|
4
|
+
<div class="background-layer"></div>
|
|
5
|
+
</transition>
|
|
6
|
+
<transition name="reconnect-content" appear>
|
|
7
|
+
<div class="reconnect-container">
|
|
8
|
+
<div class="loading-system">
|
|
9
|
+
<!-- Geometric loading rings -->
|
|
10
|
+
<div class="ring-system">
|
|
11
|
+
<div class="ring ring-outer"></div>
|
|
12
|
+
<div class="ring ring-middle"></div>
|
|
13
|
+
<div class="ring ring-inner"></div>
|
|
14
|
+
<!-- Central logo -->
|
|
15
|
+
<img :src="logoIcon" alt="Logo" class="logo" />
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Progress indicators -->
|
|
19
|
+
<div class="progress-dots">
|
|
20
|
+
<div class="dot" :class="{ active: dotIndex >= 0 }"></div>
|
|
21
|
+
<div class="dot" :class="{ active: dotIndex >= 1 }"></div>
|
|
22
|
+
<div class="dot" :class="{ active: dotIndex >= 2 }"></div>
|
|
23
|
+
<div class="dot" :class="{ active: dotIndex >= 3 }"></div>
|
|
24
|
+
</div>
|
|
6
25
|
</div>
|
|
7
|
-
|
|
26
|
+
|
|
27
|
+
<div class="message-container">
|
|
28
|
+
<h2 class="message-text">{{ props.message }}</h2>
|
|
29
|
+
<div class="status-bar">
|
|
30
|
+
<div class="status-fill"></div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</transition>
|
|
8
35
|
</div>
|
|
9
36
|
</template>
|
|
10
37
|
|
|
11
38
|
<script setup>
|
|
12
|
-
import { ref } from "vue";
|
|
39
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
13
40
|
import logoIcon from "@/assets/img/logo_icon.png";
|
|
14
41
|
|
|
15
|
-
const
|
|
42
|
+
const dotIndex = ref(0);
|
|
43
|
+
const progressWidth = ref(0);
|
|
44
|
+
let dotInterval;
|
|
45
|
+
let progressInterval;
|
|
46
|
+
|
|
47
|
+
// Animate progress dots
|
|
48
|
+
const animateDots = () => {
|
|
49
|
+
dotIndex.value = (dotIndex.value + 1) % 5; // 0-4, with 4 being all off
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Animate progress bar
|
|
53
|
+
onMounted(() => {
|
|
54
|
+
dotInterval = setInterval(animateDots, 600);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Add page fade-in effect when modal closes
|
|
58
|
+
const addPageFadeIn = () => {
|
|
59
|
+
// Add global style for underlying page fade-in
|
|
60
|
+
const style = document.createElement('style');
|
|
61
|
+
style.textContent = `
|
|
62
|
+
.page-fade-in {
|
|
63
|
+
animation: pageReveal 0.6s ease-out forwards;
|
|
64
|
+
}
|
|
65
|
+
@keyframes pageReveal {
|
|
66
|
+
from {
|
|
67
|
+
opacity: 0.7;
|
|
68
|
+
transform: scale(0.98);
|
|
69
|
+
filter: blur(1px);
|
|
70
|
+
}
|
|
71
|
+
to {
|
|
72
|
+
opacity: 1;
|
|
73
|
+
transform: scale(1);
|
|
74
|
+
filter: blur(0);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
document.head.appendChild(style);
|
|
79
|
+
|
|
80
|
+
// Apply the class to the body
|
|
81
|
+
document.body.classList.add('page-fade-in');
|
|
82
|
+
|
|
83
|
+
// Remove the class and style after animation
|
|
84
|
+
setTimeout(() => {
|
|
85
|
+
document.body.classList.remove('page-fade-in');
|
|
86
|
+
if (document.head.contains(style)) {
|
|
87
|
+
document.head.removeChild(style);
|
|
88
|
+
}
|
|
89
|
+
}, 600);
|
|
90
|
+
};
|
|
16
91
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
92
|
+
onUnmounted(() => {
|
|
93
|
+
if (dotInterval) clearInterval(dotInterval);
|
|
94
|
+
addPageFadeIn();
|
|
95
|
+
});
|
|
21
96
|
|
|
22
97
|
const props = defineProps({
|
|
23
98
|
message: {
|
|
@@ -27,64 +102,285 @@ const props = defineProps({
|
|
|
27
102
|
});
|
|
28
103
|
</script>
|
|
29
104
|
|
|
30
|
-
<style scoped>
|
|
31
|
-
.
|
|
105
|
+
<style lang="scss" scoped>
|
|
106
|
+
.reconnect-overlay {
|
|
107
|
+
position: fixed;
|
|
108
|
+
top: -100vh;
|
|
109
|
+
left: -100vw;
|
|
110
|
+
right: -100vw;
|
|
111
|
+
bottom: -100vh;
|
|
112
|
+
z-index: 9999;
|
|
113
|
+
--tw-ring-color: transparent !important;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.background-layer {
|
|
117
|
+
position: absolute;
|
|
118
|
+
top: 0;
|
|
119
|
+
left: 0;
|
|
120
|
+
right: 0;
|
|
121
|
+
bottom: 0;
|
|
122
|
+
background: rgba(26, 27, 30, 0.98);
|
|
123
|
+
backdrop-filter: blur(12px);
|
|
124
|
+
-webkit-backdrop-filter: blur(12px);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.reconnect-container {
|
|
128
|
+
position: absolute;
|
|
129
|
+
top: 100vh;
|
|
130
|
+
left: 100vw;
|
|
131
|
+
right: 100vw;
|
|
132
|
+
bottom: 100vh;
|
|
133
|
+
display: flex;
|
|
134
|
+
flex-direction: column;
|
|
135
|
+
align-items: center;
|
|
136
|
+
justify-content: center;
|
|
137
|
+
gap: 3rem;
|
|
138
|
+
--tw-ring-color: transparent !important;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.loading-system {
|
|
32
142
|
position: relative;
|
|
33
|
-
|
|
34
|
-
|
|
143
|
+
display: flex;
|
|
144
|
+
align-items: center;
|
|
145
|
+
justify-content: center;
|
|
35
146
|
}
|
|
36
|
-
|
|
147
|
+
|
|
148
|
+
.ring-system {
|
|
149
|
+
position: relative;
|
|
150
|
+
width: 160px;
|
|
151
|
+
height: 160px;
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.ring {
|
|
37
158
|
position: absolute;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
159
|
+
border-radius: 50%;
|
|
160
|
+
background: transparent !important;
|
|
161
|
+
--tw-ring-color: transparent !important;
|
|
162
|
+
border: 4px solid transparent;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.ring-outer {
|
|
166
|
+
width: 160px;
|
|
167
|
+
height: 160px;
|
|
168
|
+
border-top: 4px solid #9dd3a8;
|
|
169
|
+
border-right: 4px solid rgba(157, 211, 168, 0.3);
|
|
170
|
+
border-bottom: 4px solid transparent;
|
|
171
|
+
border-left: 4px solid transparent;
|
|
172
|
+
animation: breathe-slow 3s ease-in-out infinite;
|
|
173
|
+
top: 0;
|
|
174
|
+
left: 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.ring-middle {
|
|
178
|
+
width: 120px;
|
|
179
|
+
height: 120px;
|
|
180
|
+
border-top: 4px solid #88c999;
|
|
181
|
+
border-right: 4px solid rgba(136, 201, 153, 0.4);
|
|
182
|
+
border-bottom: 4px solid transparent;
|
|
183
|
+
border-left: 4px solid transparent;
|
|
184
|
+
animation: breathe-medium 2.5s ease-in-out infinite reverse;
|
|
185
|
+
top: 20px;
|
|
186
|
+
left: 20px;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.ring-inner {
|
|
190
|
+
width: 80px;
|
|
191
|
+
height: 80px;
|
|
192
|
+
border-top: 4px solid #7bc187;
|
|
193
|
+
border-right: 4px solid rgba(123, 193, 135, 0.5);
|
|
194
|
+
border-bottom: 4px solid transparent;
|
|
195
|
+
border-left: 4px solid transparent;
|
|
196
|
+
animation: breathe-fast 2s ease-in-out infinite;
|
|
197
|
+
top: 40px;
|
|
198
|
+
left: 40px;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.logo {
|
|
202
|
+
width: 72px;
|
|
203
|
+
height: 72px;
|
|
43
204
|
border-radius: 50%;
|
|
44
205
|
object-fit: cover;
|
|
45
|
-
|
|
46
|
-
|
|
206
|
+
z-index: 10;
|
|
207
|
+
border: 2px solid rgba(136, 201, 153, 0.3);
|
|
208
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
|
|
47
209
|
}
|
|
48
|
-
|
|
210
|
+
|
|
211
|
+
.progress-dots {
|
|
49
212
|
position: absolute;
|
|
50
|
-
|
|
51
|
-
height: 185px;
|
|
52
|
-
top: 50%;
|
|
213
|
+
bottom: -40px;
|
|
53
214
|
left: 50%;
|
|
54
|
-
transform:
|
|
215
|
+
transform: translateX(-50%);
|
|
216
|
+
display: flex;
|
|
217
|
+
gap: 8px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.dot {
|
|
221
|
+
width: 8px;
|
|
222
|
+
height: 8px;
|
|
55
223
|
border-radius: 50%;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
224
|
+
background: #44454b;
|
|
225
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
226
|
+
|
|
227
|
+
&.active {
|
|
228
|
+
background: #88c999;
|
|
229
|
+
transform: scale(1.2);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.message-container {
|
|
234
|
+
text-align: center;
|
|
235
|
+
max-width: 400px;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.message-text {
|
|
239
|
+
color: #e2e2e5;
|
|
240
|
+
font-size: 1.5rem;
|
|
241
|
+
font-weight: 500;
|
|
242
|
+
margin-bottom: 1rem;
|
|
243
|
+
letter-spacing: 0.025em;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.status-bar {
|
|
247
|
+
width: 300px;
|
|
248
|
+
height: 8px;
|
|
249
|
+
background: #35363c;
|
|
250
|
+
border-radius: 4px;
|
|
251
|
+
overflow: hidden;
|
|
252
|
+
position: relative;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.status-fill {
|
|
256
|
+
height: 100%;
|
|
257
|
+
background: linear-gradient(90deg, #88c999 0%, #9dd3a8 50%, #88c999 100%);
|
|
258
|
+
border-radius: 4px;
|
|
259
|
+
width: 30%;
|
|
260
|
+
animation: infinite-slide 2s ease-in-out infinite;
|
|
261
|
+
position: relative;
|
|
262
|
+
overflow: hidden;
|
|
59
263
|
}
|
|
60
|
-
|
|
264
|
+
|
|
265
|
+
.status-fill::after {
|
|
266
|
+
content: '';
|
|
61
267
|
position: absolute;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
268
|
+
top: 0;
|
|
269
|
+
left: -100%;
|
|
270
|
+
width: 100%;
|
|
271
|
+
height: 100%;
|
|
272
|
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
|
273
|
+
animation: shimmer 2s ease-in-out infinite;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Smooth beautiful animations
|
|
277
|
+
@keyframes breathe-slow {
|
|
278
|
+
0% { transform: rotate(0deg) scale(1); opacity: 0.8; }
|
|
279
|
+
50% { transform: rotate(180deg) scale(1.05); opacity: 1; }
|
|
280
|
+
100% { transform: rotate(360deg) scale(1); opacity: 0.8; }
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
@keyframes breathe-medium {
|
|
284
|
+
0% { transform: rotate(0deg) scale(1); opacity: 0.9; }
|
|
285
|
+
50% { transform: rotate(-180deg) scale(0.95); opacity: 1; }
|
|
286
|
+
100% { transform: rotate(-360deg) scale(1); opacity: 0.9; }
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
@keyframes breathe-fast {
|
|
290
|
+
0% { transform: rotate(0deg) scale(1); opacity: 1; }
|
|
291
|
+
50% { transform: rotate(180deg) scale(1.08); opacity: 0.7; }
|
|
292
|
+
100% { transform: rotate(360deg) scale(1); opacity: 1; }
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@keyframes infinite-slide {
|
|
296
|
+
0% { transform: translateX(-100%); }
|
|
297
|
+
50% { transform: translateX(250%); }
|
|
298
|
+
100% { transform: translateX(-100%); }
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
@keyframes shimmer {
|
|
302
|
+
0% { transform: translateX(-100%); }
|
|
303
|
+
50% { transform: translateX(100%); }
|
|
304
|
+
100% { transform: translateX(300%); }
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Staged beautiful transitions
|
|
308
|
+
.background-fill-enter-active {
|
|
309
|
+
transition: all 0.15s ease-out;
|
|
70
310
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
311
|
+
|
|
312
|
+
.background-fill-leave-active {
|
|
313
|
+
transition: all 0.3s ease-in;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.background-fill-enter-from {
|
|
317
|
+
opacity: 0;
|
|
318
|
+
transform: scale(0.8);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.background-fill-leave-to {
|
|
322
|
+
opacity: 0;
|
|
323
|
+
transform: scale(1.2);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.reconnect-content-enter-active {
|
|
327
|
+
transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
328
|
+
transition-delay: 0.1s;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.reconnect-content-leave-active {
|
|
332
|
+
transition: all 0.5s cubic-bezier(0.55, 0.085, 0.68, 0.53);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.reconnect-content-enter-from {
|
|
336
|
+
opacity: 0;
|
|
337
|
+
transform: scale(0.9) translateY(20px);
|
|
338
|
+
filter: blur(4px);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.reconnect-content-leave-to {
|
|
342
|
+
opacity: 0;
|
|
343
|
+
transform: scale(1.1) translateY(-10px);
|
|
344
|
+
filter: blur(2px);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Responsive design
|
|
348
|
+
@media (max-width: 768px) {
|
|
349
|
+
.ring-system {
|
|
350
|
+
width: 120px;
|
|
351
|
+
height: 120px;
|
|
74
352
|
}
|
|
75
|
-
|
|
76
|
-
|
|
353
|
+
|
|
354
|
+
.ring-outer {
|
|
355
|
+
width: 120px;
|
|
356
|
+
height: 120px;
|
|
77
357
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
358
|
+
|
|
359
|
+
.ring-middle {
|
|
360
|
+
width: 90px;
|
|
361
|
+
height: 90px;
|
|
362
|
+
top: 15px;
|
|
363
|
+
left: 15px;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.ring-inner {
|
|
367
|
+
width: 60px;
|
|
368
|
+
height: 60px;
|
|
369
|
+
top: 30px;
|
|
370
|
+
left: 30px;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.logo {
|
|
374
|
+
width: 54px;
|
|
375
|
+
height: 54px;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.message-text {
|
|
379
|
+
font-size: 1.25rem;
|
|
84
380
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
381
|
+
|
|
382
|
+
.status-bar {
|
|
383
|
+
width: 250px;
|
|
88
384
|
}
|
|
89
385
|
}
|
|
90
386
|
</style>
|
|
@@ -68,10 +68,10 @@ const open = ref(false);
|
|
|
68
68
|
.dropdown-content {
|
|
69
69
|
left: -1.25rem;
|
|
70
70
|
background-clip: border-box !important;
|
|
71
|
-
@apply border border-
|
|
71
|
+
@apply border border-dark-650;
|
|
72
72
|
border-width: 2px;
|
|
73
73
|
--tw-border-opacity: 1;
|
|
74
|
-
border-color: rgb(
|
|
74
|
+
border-color: rgb(61 62 68 / var(--tw-border-opacity));
|
|
75
75
|
max-height: 13rem;
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -47,12 +47,12 @@
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
&.checked {
|
|
50
|
-
background: #
|
|
51
|
-
border-color: #
|
|
50
|
+
background: #88c999;
|
|
51
|
+
border-color: #88c999;
|
|
52
52
|
|
|
53
53
|
&:hover {
|
|
54
|
-
background: #
|
|
55
|
-
border-color: #
|
|
54
|
+
background: #9dd3a8;
|
|
55
|
+
border-color: #9dd3a8;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -62,23 +62,23 @@
|
|
|
62
62
|
border-color: #52535a;
|
|
63
63
|
|
|
64
64
|
&:hover {
|
|
65
|
-
border-color: #
|
|
66
|
-
background: rgba(
|
|
65
|
+
border-color: #88c999;
|
|
66
|
+
background: rgba(136, 201, 153, 0.1);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
&.checked {
|
|
70
|
-
background: #
|
|
71
|
-
border-color: #
|
|
70
|
+
background: #88c999;
|
|
71
|
+
border-color: #88c999;
|
|
72
72
|
|
|
73
73
|
&:hover {
|
|
74
|
-
background: #
|
|
75
|
-
border-color: #
|
|
74
|
+
background: #9dd3a8;
|
|
75
|
+
border-color: #9dd3a8;
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
&:focus-visible {
|
|
81
|
-
outline: 2px solid #
|
|
81
|
+
outline: 2px solid #88c999;
|
|
82
82
|
outline-offset: 2px;
|
|
83
83
|
}
|
|
84
84
|
}
|
package/src/views/Editor.vue
CHANGED
|
@@ -92,7 +92,19 @@
|
|
|
92
92
|
</div>
|
|
93
93
|
</div>
|
|
94
94
|
|
|
95
|
-
<
|
|
95
|
+
<div v-if="errorMessage" class="error-container">
|
|
96
|
+
<div class="error-icon">
|
|
97
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
98
|
+
<circle cx="12" cy="12" r="10"/>
|
|
99
|
+
<line x1="15" y1="9" x2="9" y2="15"/>
|
|
100
|
+
<line x1="9" y1="9" x2="15" y2="15"/>
|
|
101
|
+
</svg>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="error-content">
|
|
104
|
+
<div class="error-title">JSON Syntax Error</div>
|
|
105
|
+
<div class="error-text">{{ errorMessage }}</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
96
108
|
</div>
|
|
97
109
|
</transition>
|
|
98
110
|
<div class="border border-dark-650 my-8" />
|
|
@@ -782,4 +794,52 @@ pre[class*="language-"] {
|
|
|
782
794
|
.token.entity {
|
|
783
795
|
cursor: help;
|
|
784
796
|
}
|
|
797
|
+
|
|
798
|
+
/* Modern error message styling */
|
|
799
|
+
.error-container {
|
|
800
|
+
display: flex;
|
|
801
|
+
align-items: flex-start;
|
|
802
|
+
gap: 12px;
|
|
803
|
+
padding: 16px;
|
|
804
|
+
margin-top: 12px;
|
|
805
|
+
border-radius: 8px;
|
|
806
|
+
border: 1px solid #dc2626;
|
|
807
|
+
background: linear-gradient(135deg, rgba(220, 38, 38, 0.1), rgba(220, 38, 38, 0.05));
|
|
808
|
+
box-shadow: 0 2px 8px rgba(220, 38, 38, 0.15);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
.error-icon {
|
|
812
|
+
display: flex;
|
|
813
|
+
align-items: center;
|
|
814
|
+
justify-content: center;
|
|
815
|
+
width: 28px;
|
|
816
|
+
height: 28px;
|
|
817
|
+
border-radius: 50%;
|
|
818
|
+
background: rgba(220, 38, 38, 0.2);
|
|
819
|
+
border: 1px solid rgba(220, 38, 38, 0.4);
|
|
820
|
+
color: #ef4444;
|
|
821
|
+
flex-shrink: 0;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
.error-content {
|
|
825
|
+
flex: 1;
|
|
826
|
+
min-width: 0;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.error-title {
|
|
830
|
+
color: #fca5a5;
|
|
831
|
+
font-size: 14px;
|
|
832
|
+
font-weight: 600;
|
|
833
|
+
margin-bottom: 4px;
|
|
834
|
+
letter-spacing: 0.015em;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
.error-text {
|
|
838
|
+
color: #fecaca;
|
|
839
|
+
font-size: 12px;
|
|
840
|
+
line-height: 1.5;
|
|
841
|
+
font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace;
|
|
842
|
+
word-break: break-word;
|
|
843
|
+
opacity: 0.9;
|
|
844
|
+
}
|
|
785
845
|
</style>
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
</div>
|
|
102
102
|
</div>
|
|
103
103
|
</Header>
|
|
104
|
-
<div class="overflow-auto hidden-scrollbars flex-1">
|
|
104
|
+
<div class="bg-dark-400 overflow-auto hidden-scrollbars flex-1">
|
|
105
105
|
<div v-if="filterBuilder.filters.length" class="filters-container">
|
|
106
106
|
<draggable
|
|
107
107
|
:list="filterBuilder.filters"
|
|
@@ -136,8 +136,8 @@
|
|
|
136
136
|
class="empty-state flex flex-col items-center justify-center py-8 text-center"
|
|
137
137
|
>
|
|
138
138
|
<FilterIcon class="w-12 h-12 text-dark-400 mb-3 opacity-50" />
|
|
139
|
-
<p class="text-
|
|
140
|
-
<p class="text-
|
|
139
|
+
<p class="text-light-400 text-sm">No filters yet</p>
|
|
140
|
+
<p class="text-light-500 text-xs mt-1">Click on the map to create filters</p>
|
|
141
141
|
</div>
|
|
142
142
|
</div>
|
|
143
143
|
</Table>
|
package/static/offline.html
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
|
6
6
|
<title>Site Offline</title>
|
|
7
|
-
<meta name="theme-color" content="
|
|
7
|
+
<meta name="theme-color" content="#1a1b1e" />
|
|
8
8
|
<style>
|
|
9
9
|
body,
|
|
10
10
|
html {
|
|
11
11
|
margin: 0;
|
|
12
12
|
padding: 0;
|
|
13
13
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
14
|
-
background-color:
|
|
14
|
+
background-color: #1a1b1e;
|
|
15
15
|
color: #fff;
|
|
16
16
|
height: 100%;
|
|
17
17
|
}
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
min-height: 100vh;
|
|
47
47
|
}
|
|
48
48
|
.error-card {
|
|
49
|
-
background-color:
|
|
49
|
+
background-color: #2e2f34;
|
|
50
50
|
border-radius: 0.5rem;
|
|
51
51
|
padding: 2rem;
|
|
52
52
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
margin: auto;
|
|
57
57
|
}
|
|
58
58
|
.icon-wrapper {
|
|
59
|
-
background-color: #
|
|
59
|
+
background-color: #3d3e44;
|
|
60
60
|
width: 4rem;
|
|
61
61
|
height: 4rem;
|
|
62
62
|
border-radius: 50%;
|
|
@@ -65,18 +65,18 @@
|
|
|
65
65
|
align-items: center;
|
|
66
66
|
margin: 0 auto 1rem;
|
|
67
67
|
font-size: 2rem;
|
|
68
|
-
color: #
|
|
68
|
+
color: #f87171;
|
|
69
69
|
}
|
|
70
70
|
h1 {
|
|
71
71
|
font-size: 1.5rem;
|
|
72
72
|
margin-bottom: 0.5rem;
|
|
73
73
|
}
|
|
74
74
|
p {
|
|
75
|
-
color:
|
|
75
|
+
color: #d0d0d3;
|
|
76
76
|
margin-bottom: 1.5rem;
|
|
77
77
|
}
|
|
78
78
|
.refresh-button {
|
|
79
|
-
background-color: #
|
|
79
|
+
background-color: #3d3e44;
|
|
80
80
|
color: white;
|
|
81
81
|
border: none;
|
|
82
82
|
padding: 0.5rem 1rem;
|