Appearance
Adding a Page
This guide walks through adding a new page to the frontend application.
1. Create the Module
Create the module directory structure:
frontend/src/modules/<name>/
├── pages/
│ └── <name>.vue
├── components/ # (if needed)
├── composables/ # (if needed)
├── api/ # (if needed)
├── types/ # (if needed)
└── utils/ # (if needed)2. Create the Page Component
Create frontend/src/modules/<name>/pages/<name>.vue:
vue
<template>
<div>
<h1 class="text-xl font-semibold text-neutral-900 mb-6">
Naslov strani
</h1>
<!-- Page content -->
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
</script>
<script lang="ts">
export default {
name: '<name>-page',
};
</script>Remember:
- Two
<script>blocks —<script setup>for logic, named export for DevTools - Page name pattern:
<name>-page - UI text in Slovenian
3. Add the Route
Edit frontend/src/router/index.ts to add the route as a child of the appropriate layout:
typescript
{
path: '/',
component: () => import('@/shared/layouts/default.vue'),
children: [
// Existing routes...
{
path: '<name>',
name: '<name>',
component: () => import('@/modules/<name>/pages/<name>.vue'),
},
],
},Rules:
- Always use lazy-loaded imports:
() => import(...) - Use the default layout for main app pages
- Use the auth layout for authentication pages
- Use the legal layout for legal/static pages
4. Add Sidebar Navigation
Edit frontend/src/shared/layouts/components/sidebar-menu.vue to add a menu item:
vue
<SidebarMenuItem
to="/<name>"
icon="ri-icon-name"
label="Ime strani"
/>Find the appropriate icon from Remix Icons.
5. Add Data Fetching (if needed)
If the page needs to fetch data from the API:
Create API functions
Create frontend/src/modules/<name>/api/<name>.api.ts:
typescript
import type { SomeType } from '../types/<name>.types';
export async function fetchItems(filters?: Record<string, string>): Promise<SomeType[]> {
const params = new URLSearchParams();
if (filters?.search) params.set('search', filters.search);
const qs = params.toString();
const url = qs ? `/api/<name>?${qs}` : '/api/<name>';
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}Create a composable
Create frontend/src/modules/<name>/composables/use<Name>Query.ts:
typescript
import { computed } from 'vue';
import { useQuery } from '@tanstack/vue-query';
import { fetchItems } from '../api/<name>.api';
export function use<Name>Query(filters?: Record<string, string>) {
const { data, isLoading, error } = useQuery({
queryKey: ['<name>', filters],
queryFn: () => fetchItems(filters),
});
return { data, isLoading, error };
}Wire it up in the page
vue
<script setup lang="ts">
import { use<Name>Query } from '../composables/use<Name>Query';
import GisSpinner from '@uikit/spinner/spinner.vue';
const { data, isLoading, error } = use<Name>Query();
</script>
<template>
<GisSpinner v-if="isLoading" />
<div v-else-if="error">Napaka pri nalaganju podatkov</div>
<div v-else>
<!-- Render data -->
</div>
</template>6. Verify
- Run
pnpm dev:frontendand navigate to your new page - Check that the sidebar link works and highlights correctly
- Verify data loading (if applicable)
- Test responsive behavior on mobile