Adding Views
Create new frontend views.
Step 1: Create View Component
Create sis_frontend/src/views/{domain}/EntityListView.vue:
vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useToast } from 'primevue/usetoast'
import { useI18n } from 'vue-i18n'
import type { Entity } from '@/api/types'
import { entityApi } from '@/api/services/domain.api'
const router = useRouter()
const toast = useToast()
const { t } = useI18n()
const entities = ref<Entity[]>([])
const loading = ref(false)
const totalRecords = ref(0)
async function loadEntities() {
loading.value = true
try {
const response = await entityApi.getAll()
entities.value = response.data.results
totalRecords.value = response.data.count
} catch (error) {
toast.add({
severity: 'error',
summary: t('common.error'),
detail: t('common.errors.loadFailed')
})
} finally {
loading.value = false
}
}
function viewEntity(entity: Entity) {
router.push(`/entities/${entity.entity_id}`)
}
onMounted(() => loadEntities())
</script>
<template>
<div class="entity-list">
<div class="flex justify-between items-center mb-4">
<h1>{{ t('entities.title') }}</h1>
<Button
:label="t('entities.addNew')"
icon="pi pi-plus"
@click="router.push('/entities/new')"
/>
</div>
<DataTable
:value="entities"
:loading="loading"
paginator
:rows="20"
>
<Column field="name" :header="t('common.name')" sortable />
<Column field="status_display" :header="t('common.status')" />
<Column :header="t('common.actions')">
<template #body="{ data }">
<Button
icon="pi pi-eye"
text
@click="viewEntity(data)"
/>
</template>
</Column>
</DataTable>
</div>
</template>Step 2: Create Detail View
Create sis_frontend/src/views/{domain}/EntityDetailView.vue:
vue
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useToast } from 'primevue/usetoast'
import { useI18n } from 'vue-i18n'
import type { Entity } from '@/api/types'
import { entityApi } from '@/api/services/domain.api'
const route = useRoute()
const router = useRouter()
const toast = useToast()
const { t } = useI18n()
const entity = ref<Entity | null>(null)
const loading = ref(false)
const saving = ref(false)
const isNew = computed(() => route.params.id === 'new')
async function loadEntity() {
if (isNew.value) return
loading.value = true
try {
const id = Number(route.params.id)
const response = await entityApi.getById(id)
entity.value = response.data
} catch (error) {
toast.add({
severity: 'error',
summary: t('common.error'),
detail: t('common.errors.loadFailed')
})
} finally {
loading.value = false
}
}
async function save() {
if (!entity.value) return
saving.value = true
try {
if (isNew.value) {
await entityApi.create(entity.value)
toast.add({
severity: 'success',
summary: t('common.success'),
detail: t('entities.created')
})
} else {
await entityApi.update(entity.value.entity_id, entity.value)
toast.add({
severity: 'success',
summary: t('common.success'),
detail: t('entities.updated')
})
}
router.push('/entities')
} catch (error) {
toast.add({
severity: 'error',
summary: t('common.error'),
detail: t('common.errors.saveFailed')
})
} finally {
saving.value = false
}
}
onMounted(() => {
if (isNew.value) {
entity.value = { name: '', status_id: 1 } as Entity
} else {
loadEntity()
}
})
</script>
<template>
<div class="entity-detail">
<h1>{{ isNew ? t('entities.new') : t('entities.edit') }}</h1>
<div v-if="loading">Loading...</div>
<form v-else-if="entity" @submit.prevent="save">
<div class="field">
<label for="name">{{ t('common.name') }}</label>
<InputText id="name" v-model="entity.name" required />
</div>
<div class="flex gap-2 mt-4">
<Button
:label="t('common.cancel')"
severity="secondary"
@click="router.push('/entities')"
/>
<Button
:label="t('common.save')"
type="submit"
:loading="saving"
/>
</div>
</form>
</div>
</template>Step 3: Add Routes
Add to sis_frontend/src/router/index.ts:
typescript
{
path: '/entities',
name: 'entities',
component: () => import('@/views/domain/EntityListView.vue'),
meta: { requiresAuth: true }
},
{
path: '/entities/:id',
name: 'entity-detail',
component: () => import('@/views/domain/EntityDetailView.vue'),
meta: { requiresAuth: true }
}Step 4: Add to Navigation
Menu items are configured through the authorization system's dashboard tiles and the menuLayout store. For development, you can add a route directly in the AppLayout.vue sidebar:
vue
<MenuItem :label="t('entities.title')" icon="pi pi-list" to="/entities" />Step 5: Add TypeScript Types
Add to sis_frontend/src/api/types/index.ts:
typescript
export interface Entity {
entity_id: number
name: string
description: string | null
status_id: number
status_code: string
status_display: string
department_id: number | null
department_name: string | null
is_active: boolean
created_at: string
updated_at: string
}
export interface EntityCreate {
name: string
description?: string
status_id: number
department_id?: number
}Step 6: Add API Service
Create sis_frontend/src/api/services/domain.api.ts:
typescript
import api from '../index'
import type { Entity, EntityCreate, PaginatedResponse } from '../types'
export const entityApi = {
getAll: (params?: Record<string, any>) =>
api.get<PaginatedResponse<Entity>>('/domain/entities', { params }),
getById: (id: number) =>
api.get<Entity>(`/domain/entities/${id}`),
create: (data: EntityCreate) =>
api.post<Entity>('/domain/entities', data),
update: (id: number, data: Partial<EntityCreate>) =>
api.put<Entity>(`/domain/entities/${id}`, data),
delete: (id: number) =>
api.delete(`/domain/entities/${id}`)
}Step 7: Add Translation Keys
Add to sis_frontend/src/locales/en.json:
json
{
"entities": {
"title": "Entities",
"new": "New Entity",
"edit": "Edit Entity",
"addNew": "Add Entity",
"created": "Entity created successfully",
"updated": "Entity updated successfully"
}
}Using Lookup Values
For fields that reference lookup tables, use the lookups store:
vue
<script setup lang="ts">
import { onMounted } from 'vue'
import { useLookupsStore } from '@/stores/lookups'
const lookupsStore = useLookupsStore()
onMounted(() => {
lookupsStore.fetchLookup('ENTITY_STATUS')
})
</script>
<template>
<div class="field">
<label>Status</label>
<Select
v-model="entity.status_id"
:options="lookupsStore.getByType('ENTITY_STATUS')"
optionLabel="display_value"
optionValue="id"
placeholder="Select status..."
/>
</div>
</template>