Skip to content

Commit ab19985

Browse files
committed
feat(ui): add no namespace state to Home page
1 parent 7ae667b commit ab19985

File tree

3 files changed

+369
-353
lines changed

3 files changed

+369
-353
lines changed

ui/src/views/Home.vue

Lines changed: 173 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -1,164 +1,165 @@
11
<template>
2-
<div v-if="!hasStatus">
3-
<!-- Namespace Info Card -->
4-
<v-row>
5-
<v-col cols="12">
6-
<v-card
7-
class="bg-transparent mb-6"
8-
elevation="0"
9-
rounded="0"
2+
<div v-if="!hasError">
3+
<v-card
4+
class="bg-transparent mb-6"
5+
elevation="0"
6+
rounded="0"
7+
>
8+
<v-row>
9+
<v-col
10+
cols="12"
11+
md="6"
1012
>
11-
<v-row>
12-
<v-col
13-
cols="12"
14-
md="6"
13+
<div class="d-flex align-start">
14+
<v-avatar
15+
color="primary"
16+
size="48"
17+
class="mr-4"
1518
>
16-
<div class="d-flex align-start">
17-
<v-avatar
19+
<v-icon
20+
size="32"
21+
icon="mdi-home"
22+
/>
23+
</v-avatar>
24+
<div>
25+
<div class="text-overline text-medium-emphasis mb-1">Home</div>
26+
<div class="text-h5 font-weight-bold mb-2">
27+
{{ hasNamespace ? namespace.name : "No Active Namespace" }}
28+
</div>
29+
<div class="text-body-2 text-medium-emphasis">{{ activeNamespaceDescription }}</div>
30+
</div>
31+
</div>
32+
</v-col>
33+
<v-col
34+
cols="12"
35+
md="6"
36+
>
37+
<v-card
38+
class="pa-4"
39+
variant="tonal"
40+
>
41+
<div v-if="hasNamespace">
42+
<div class="text-overline text-medium-emphasis mb-2">TENANT ID</div>
43+
<div class="d-flex align-center justify-space-between">
44+
<code
45+
class="text-primary"
46+
data-test="tenant-info-text"
47+
>{{ namespace.tenant_id }}</code>
48+
<CopyWarning copied-item="Tenant ID">
49+
<template #default="{ copyText }">
50+
<v-btn
51+
data-test="copy-tenant-btn"
52+
color="primary"
53+
variant="elevated"
54+
size="small"
55+
prepend-icon="mdi-content-copy"
56+
@click="copyText(namespace.tenant_id)"
57+
>
58+
Copy
59+
</v-btn>
60+
</template>
61+
</CopyWarning>
62+
</div>
63+
<div class="text-caption text-medium-emphasis mt-2">Use this ID to register new devices to this namespace</div>
64+
</div>
65+
<div v-else>
66+
<div class="text-overline text-medium-emphasis">Create your first namespace</div>
67+
<div class="my-2">
68+
<v-btn
1869
color="primary"
19-
size="48"
20-
class="mr-4"
21-
>
22-
<v-icon size="32">
23-
mdi-home
24-
</v-icon>
25-
</v-avatar>
26-
<div>
27-
<div class="text-overline text-medium-emphasis mb-1">
28-
Home
29-
</div>
30-
<div class="text-h5 font-weight-bold mb-2">
31-
{{ namespace.name }}
32-
</div>
33-
<div class="text-body-2 text-medium-emphasis">
34-
This is your active namespace. All devices, sessions and configurations are isolated within this namespace.
35-
</div>
36-
</div>
70+
variant="elevated"
71+
size="small"
72+
prepend-icon="mdi-plus"
73+
data-test="create-namespace-home-btn"
74+
text="Create Namespace"
75+
@click="showNamespaceAdd = true"
76+
/>
3777
</div>
38-
</v-col>
39-
<v-col
40-
cols="12"
41-
md="6"
42-
>
43-
<v-card
44-
class="pa-4"
45-
variant="tonal"
46-
>
47-
<div class="text-overline text-medium-emphasis mb-2">
48-
TENANT ID
49-
</div>
50-
<div class="d-flex align-center justify-space-between">
51-
<code
52-
class="text-primary"
53-
data-test="tenant-info-text"
54-
>{{ namespace.tenant_id }}</code>
55-
<CopyWarning :copied-item="'Tenant ID'">
56-
<template #default="{ copyText }">
57-
<v-btn
58-
data-test="copy-tenant-btn"
59-
color="primary"
60-
variant="elevated"
61-
size="small"
62-
prepend-icon="mdi-content-copy"
63-
@click="copyText(namespace.tenant_id)"
64-
>
65-
Copy
66-
</v-btn>
67-
</template>
68-
</CopyWarning>
69-
</div>
70-
<div class="text-caption text-medium-emphasis mt-2">
71-
Use this ID to register new devices to this namespace
72-
</div>
73-
</v-card>
74-
</v-col>
75-
</v-row>
76-
</v-card>
77-
</v-col>
78-
</v-row>
79-
80-
<!-- Devices Section -->
81-
<v-row>
82-
<v-col
83-
cols="12"
84-
class="d-flex align-center mb-2"
85-
>
86-
<v-icon class="mr-2">
87-
mdi-devices
88-
</v-icon>
89-
<h2 class="text-h6">
90-
Devices
91-
</h2>
92-
</v-col>
93-
</v-row>
94-
95-
<v-row>
96-
<v-col
97-
cols="12"
98-
md="3"
99-
>
100-
<StatCard
101-
title="Accepted Devices"
102-
:stat="stats.registered_devices || 0"
103-
icon="mdi-check"
104-
button-label="View all devices"
105-
path="/devices"
106-
/>
107-
</v-col>
108-
109-
<v-col
110-
cols="12"
111-
md="3"
112-
>
113-
<StatCard
114-
title="Online Devices"
115-
:stat="stats.online_devices || 0"
116-
icon="mdi-lan-connect"
117-
button-label="View Online Devices"
118-
path="/devices"
119-
/>
120-
</v-col>
121-
122-
<v-col
123-
cols="12"
124-
md="3"
125-
>
126-
<StatCard
127-
title="Pending Devices"
128-
:stat="stats.pending_devices || 0"
129-
icon="mdi-clock-outline"
130-
button-label="Approve Devices"
131-
path="/devices/pending"
132-
/>
133-
</v-col>
78+
<div class="text-caption text-medium-emphasis">
79+
You need to create or join a namespace to start managing your devices and remote connections.
80+
</div>
81+
</div>
82+
</v-card>
83+
</v-col>
84+
</v-row>
85+
</v-card>
13486

135-
<v-col
136-
cols="12"
137-
md="3"
138-
>
139-
<v-card class="pa-6 bg-transparent text-center h-100 border border-dashed">
140-
<v-avatar
141-
color="surface-variant"
142-
size="64"
143-
class="mb-4"
144-
theme="dark"
145-
>
146-
<v-icon
147-
size="40"
148-
color="primary"
149-
icon="mdi-developer-board"
150-
/>
151-
</v-avatar>
152-
<v-card-title class="text-h6 font-weight-bold mb-2">
153-
Add a new device
154-
</v-card-title>
155-
<v-card-subtitle class="text-body-2 text-medium-emphasis mb-4 text-wrap">
156-
Register new devices to this namespace and start managing remote connections
157-
</v-card-subtitle>
158-
<DeviceAdd />
159-
</v-card>
160-
</v-col>
161-
</v-row>
87+
<div v-if="hasNamespace">
88+
<v-row>
89+
<v-col
90+
cols="12"
91+
class="d-flex align-center mb-2"
92+
>
93+
<v-icon
94+
class="mr-2"
95+
icon="mdi-devices"
96+
/>
97+
<h2 class="text-h6">Devices</h2>
98+
</v-col>
99+
</v-row>
100+
<v-row>
101+
<v-col
102+
cols="12"
103+
md="3"
104+
>
105+
<StatCard
106+
title="Accepted Devices"
107+
:stat="stats.registered_devices || 0"
108+
icon="mdi-check"
109+
button-label="View all devices"
110+
path="/devices"
111+
/>
112+
</v-col>
113+
<v-col
114+
cols="12"
115+
md="3"
116+
>
117+
<StatCard
118+
title="Online Devices"
119+
:stat="stats.online_devices || 0"
120+
icon="mdi-lan-connect"
121+
button-label="View Online Devices"
122+
path="/devices"
123+
/>
124+
</v-col>
125+
<v-col
126+
cols="12"
127+
md="3"
128+
>
129+
<StatCard
130+
title="Pending Devices"
131+
:stat="stats.pending_devices || 0"
132+
icon="mdi-clock-outline"
133+
button-label="Approve Devices"
134+
path="/devices/pending"
135+
/>
136+
</v-col>
137+
<v-col
138+
cols="12"
139+
md="3"
140+
>
141+
<v-card class="pa-6 bg-transparent text-center h-100 border border-dashed">
142+
<v-avatar
143+
color="surface-variant"
144+
size="64"
145+
class="mb-4"
146+
theme="dark"
147+
>
148+
<v-icon
149+
size="40"
150+
color="primary"
151+
icon="mdi-developer-board"
152+
/>
153+
</v-avatar>
154+
<v-card-title class="text-h6 font-weight-bold mb-2">Add a new device</v-card-title>
155+
<v-card-subtitle class="text-body-2 text-medium-emphasis mb-4 text-wrap">
156+
Register new devices to this namespace and start managing remote connections
157+
</v-card-subtitle>
158+
<DeviceAdd />
159+
</v-card>
160+
</v-col>
161+
</v-row>
162+
</div>
162163
</div>
163164
<v-card
164165
v-else
@@ -169,56 +170,53 @@
169170
Something is wrong, try again!
170171
</p>
171172
</v-card>
173+
174+
<NamespaceAdd v-model="showNamespaceAdd" />
172175
</template>
173176

174177
<script setup lang="ts">
175178
import { computed, onMounted, ref, watch } from "vue";
176-
import axios, { AxiosError } from "axios";
177179
import handleError from "@/utils/handleError";
178180
import useSnackbar from "@/helpers/snackbar";
179181
import useNamespacesStore from "@/store/modules/namespaces";
180182
import useStatsStore from "@/store/modules/stats";
181183
import DeviceAdd from "@/components/Devices/DeviceAdd.vue";
182184
import CopyWarning from "@/components/User/CopyWarning.vue";
183185
import StatCard from "@/components/StatCard.vue";
186+
import NamespaceAdd from "@/components/Namespace/NamespaceAdd.vue";
184187
185188
const namespacesStore = useNamespacesStore();
186189
const statsStore = useStatsStore();
187190
const snackbar = useSnackbar();
188-
const hasStatus = ref(false);
191+
const hasError = ref(false);
189192
const stats = computed(() => statsStore.stats);
190193
const namespace = computed(() => namespacesStore.currentNamespace);
191194
const hasNamespace = computed(() => namespacesStore.namespaceList.length !== 0);
195+
const showNamespaceAdd = ref(false);
196+
197+
const activeNamespaceDescription = computed(() => (
198+
hasNamespace.value
199+
? "This is your active namespace. All devices, sessions and configurations are isolated within this namespace."
200+
: `A namespace is a logical grouping that isolates your devices, sessions, and configurations from others.
201+
Each namespace has its own unique Tenant ID used to register devices.
202+
You can create multiple namespaces to organize different projects, teams, or environments.`
203+
));
192204
193205
const fetchStats = async () => {
194206
if (!hasNamespace.value) return;
195207
196208
try {
197209
await statsStore.fetchStats();
198210
} catch (error: unknown) {
199-
if (axios.isAxiosError(error)) {
200-
const axiosError = error as AxiosError;
201-
switch (true) {
202-
case axiosError.response && axiosError.response?.status === 403: {
203-
hasStatus.value = true;
204-
break;
205-
}
206-
default: {
207-
hasStatus.value = true;
208-
snackbar.showError("Failed to load the home page.");
209-
break;
210-
}
211-
}
212-
}
211+
hasError.value = true;
212+
snackbar.showError("Failed to load the home page.");
213213
handleError(error);
214214
}
215215
};
216216
217-
onMounted(async () => {
218-
await fetchStats();
219-
});
217+
onMounted(async () => { await fetchStats(); });
220218
221219
watch(hasNamespace, async (newValue) => { if (newValue) await fetchStats(); });
222220
223-
defineExpose({ hasStatus });
221+
defineExpose({ hasError });
224222
</script>

0 commit comments

Comments
 (0)