mirror of
https://github.com/Pandipipas/scoreko-dev.git
synced 2026-06-06 03:32:06 +00:00
Add routed dashboard layout with sidebar
### Motivation - Provide a multi-tab dashboard layout so users can navigate between logical sections (Dashboard, Players, Graphics, Settings, About) via a persistent sidebar. - Integrate `vue-router` to manage in-app navigation inside the Quasar layout for a cleaner, scalable dashboard structure. ### Description - Added `vue-router` as a dev dependency and created `src/dashboard/example/router.ts` with routes for the five views (`/`, `/players`, `/graphics`, `/settings`, `/about`). - Replaced the example dashboard UI with a Quasar `QLayout` in `src/dashboard/example/main.vue` that includes a left `QDrawer` sidebar and a `RouterView` outlet, and wired the app to the router in `src/dashboard/example/main.ts`. - Added simple stub views for each tab under `src/dashboard/example/views/` (`Dashboard.vue`, `Players.vue`, `Graphics.vue`, `Settings.vue`, `About.vue`). - Adjusted `src/dashboard/template.html` to remove the default NodeCG body margin (`margin: 0`) so the layout can occupy the full panel area. ### Testing - Ran `npm install` which completed and added the new package with audit warnings about vulnerabilities reported. (succeeded) - Started the dev server with `npx vite` which reported the inputs and served the bundle at `http://localhost:4173/bundles/scoreko-dev/` and `vue-tsc` reported `Found 0 errors` while watching. (succeeded) - Attempted a Playwright screenshot run to capture the dashboard UI, but the Chromium process crashed (`TargetClosedError` / SIGSEGV) so no screenshot was produced. (failed) ------ [Codex Task](https://chatgpt.com/codex/tasks/task_e_69876e97329c832a8af7179a9175d88b)
This commit is contained in:
@@ -5,10 +5,12 @@ import { Dark, Quasar } from 'quasar';
|
||||
import 'quasar/src/css/index.sass';
|
||||
import { createApp } from 'vue';
|
||||
import App from './main.vue';
|
||||
import router from './router';
|
||||
|
||||
const app = createApp(App);
|
||||
const head = createHead();
|
||||
app.use(Quasar);
|
||||
app.use(head);
|
||||
app.use(router);
|
||||
app.mount('#app');
|
||||
Dark.set(true);
|
||||
|
||||
@@ -1,32 +1,60 @@
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue';
|
||||
import { ref } from 'vue';
|
||||
import { exampleReplicant } from '../../browser_shared/replicants';
|
||||
import type { ExampleType } from '../../types';
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
useHead({ title: 'example' }); // set the title of this page
|
||||
const text = ref('Example');
|
||||
const route = useRoute();
|
||||
|
||||
// Example code: accessing normal types.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const exampleType: ExampleType = { exampleProperty: 'exampleString' };
|
||||
const menuItems = [
|
||||
{ label: 'Dashboard', to: '/', icon: 'dashboard' },
|
||||
{ label: 'Players', to: '/players', icon: 'groups' },
|
||||
{ label: 'Graphics', to: '/graphics', icon: 'collections' },
|
||||
{ label: 'Settings', to: '/settings', icon: 'settings' },
|
||||
{ label: 'About', to: '/about', icon: 'info' },
|
||||
];
|
||||
|
||||
const activeLabel = computed(() => {
|
||||
const match = menuItems.find((item) => item.to === route.path);
|
||||
return match?.label ?? 'Dashboard';
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
{{ text }}
|
||||
<br><br>
|
||||
<img
|
||||
src="./image.png"
|
||||
:style="{ width: '100%' }"
|
||||
<QLayout view="hHh LpR fFf">
|
||||
<QHeader elevated>
|
||||
<QToolbar>
|
||||
<QToolbarTitle>{{ activeLabel }}</QToolbarTitle>
|
||||
</QToolbar>
|
||||
</QHeader>
|
||||
|
||||
<QDrawer
|
||||
show-if-above
|
||||
side="left"
|
||||
bordered
|
||||
:width="220"
|
||||
>
|
||||
<br><br>
|
||||
<QBtn
|
||||
color="primary"
|
||||
label="Example"
|
||||
/>
|
||||
<br><br>
|
||||
<!-- Example code: accessing a replicant. -->
|
||||
{{ exampleReplicant?.data?.exampleProperty }}
|
||||
</div>
|
||||
<QToolbar class="text-weight-bold">
|
||||
Control Panel
|
||||
</QToolbar>
|
||||
<QList>
|
||||
<QItem
|
||||
v-for="item in menuItems"
|
||||
:key="item.to"
|
||||
clickable
|
||||
:to="item.to"
|
||||
exact
|
||||
>
|
||||
<QItemSection avatar>
|
||||
<QIcon :name="item.icon" />
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ item.label }}</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QDrawer>
|
||||
|
||||
<QPageContainer>
|
||||
<RouterView />
|
||||
</QPageContainer>
|
||||
</QLayout>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||
import AboutView from './views/About.vue';
|
||||
import DashboardView from './views/Dashboard.vue';
|
||||
import GraphicsView from './views/Graphics.vue';
|
||||
import PlayersView from './views/Players.vue';
|
||||
import SettingsView from './views/Settings.vue';
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{ path: '/', name: 'dashboard', component: DashboardView },
|
||||
{ path: '/players', name: 'players', component: PlayersView },
|
||||
{ path: '/graphics', name: 'graphics', component: GraphicsView },
|
||||
{ path: '/settings', name: 'settings', component: SettingsView },
|
||||
{ path: '/about', name: 'about', component: AboutView },
|
||||
],
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue';
|
||||
|
||||
useHead({ title: 'About' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="q-pa-lg">
|
||||
<div class="text-h4 q-mb-md">About</div>
|
||||
<div class="text-body1">
|
||||
Información del bundle y enlaces relevantes.
|
||||
</div>
|
||||
</QPage>
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue';
|
||||
|
||||
useHead({ title: 'Dashboard' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="q-pa-lg">
|
||||
<div class="text-h4 q-mb-md">Dashboard</div>
|
||||
<div class="text-body1">
|
||||
Panel principal del dashboard.
|
||||
</div>
|
||||
</QPage>
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue';
|
||||
|
||||
useHead({ title: 'Graphics' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="q-pa-lg">
|
||||
<div class="text-h4 q-mb-md">Graphics</div>
|
||||
<div class="text-body1">
|
||||
Controles y estado de gráficos del bundle.
|
||||
</div>
|
||||
</QPage>
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue';
|
||||
|
||||
useHead({ title: 'Players' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="q-pa-lg">
|
||||
<div class="text-h4 q-mb-md">Players</div>
|
||||
<div class="text-body1">
|
||||
Gestión de jugadores y datos relacionados.
|
||||
</div>
|
||||
</QPage>
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue';
|
||||
|
||||
useHead({ title: 'Settings' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage class="q-pa-lg">
|
||||
<div class="text-h4 q-mb-md">Settings</div>
|
||||
<div class="text-body1">
|
||||
Configuración del dashboard y del bundle.
|
||||
</div>
|
||||
</QPage>
|
||||
</template>
|
||||
@@ -5,7 +5,7 @@
|
||||
<style>
|
||||
body {
|
||||
background-color: #2F3A4F !important; /* force body background to original NodeCG colour */
|
||||
margin: 15px !important; /* force body to have original NodeCG padding */
|
||||
margin: 0 !important; /* remove default NodeCG padding for app layout */
|
||||
width: unset !important; /* unset quasar width */
|
||||
min-width: unset !important; /* unset quasar min-width */
|
||||
font-size: unset !important; /* unset quasar font-size */
|
||||
|
||||
Reference in New Issue
Block a user