Something went wrong. Try again.
The IoT platform Cloudflare didn't build. Telemetry, realtime dashboards, and automations on Workers + D1 + R2 + Durable Objects — in your… nodrix.live
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293<script setup lang="ts">import { computed, onMounted, watch } from 'vue';import { RouterView, useRoute, useRouter } from 'vue-router';import { useSessionStore } from '../stores/session';import { useProjectStore } from '../stores/project';import { useUiStore } from '../stores/ui';import Sidebar from './Sidebar.vue';import Topbar from './Topbar.vue';import Spinner from '../components/Spinner.vue';
const session = useSessionStore();const project = useProjectStore();const ui = useUiStore();const route = useRoute();const router = useRouter();
const routeProjId = computed(() => (route.params['proj'] as string | undefined) ?? null);// Full-page editors (dashboard + automation) collapse the sidebar for room.const isFullEditor = computed(() => route.name === 'dashboard-edit' || route.name === 'automation-editor');
onMounted(async () => { if (!session.user) await session.load(); ui.ensureValidProject(); // Sync project store with route or stored selection. const target = routeProjId.value ?? ui.currentProject?.id ?? null; if (target) { if (routeProjId.value) ui.setCurrentProject(routeProjId.value); await project.switchTo(target); }});
// Redirect to login if unauthenticated. (The api.ts onUnauthorized hook// catches most 401s, but watching session.error covers the initial load case.)watch( () => session.error, (err) => { if (err && err.status === 401) router.replace('/login'); });
// Keep UI store + project store in sync when the URL's :proj changes.watch(routeProjId, async (id) => { if (!id) return; ui.setCurrentProject(id); await project.switchTo(id);});
// Navigating closes the mobile drawer so it doesn't linger over the new page.watch( () => route.fullPath, () => ui.closeMobileSidebar());
// After session loads, make sure the stored project still exists.watch( () => session.projects.length, () => ui.ensureValidProject());
// Collapse the sidebar while a full-page editor is open, restore on exit.watch( isFullEditor, (editing) => { if (editing) ui.autoCollapseForEditor(); else ui.restoreSidebarFromEditor(); }, { immediate: true });</script>
<template> <div v-if="session.loading && !session.user" class="grid h-full place-items-center"> <Spinner size="lg" /> </div> <div v-else-if="session.error && session.error.status !== 401" class="grid h-full place-items-center"> <div class="max-w-md text-center"> <h2 class="text-lg font-semibold">Could not load session</h2> <p class="mt-2 text-sm text-neutral-600 dark:text-neutral-400"> Status {{ session.error.status }}<span v-if="session.error.reason">: {{ session.error.reason }}</span> </p> </div> </div> <div v-else class="flex h-full"> <Sidebar /> <div class="flex min-w-0 flex-1 flex-col"> <Topbar /> <main class="flex-1 overflow-auto bg-neutral-50 dark:bg-neutral-950"> <RouterView /> </main> </div> </div></template>