mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-02-05 11:25:19 -05:00
5.3 KiB
5.3 KiB
description
| description |
|---|
| Create a new page in ThrillWiki following project conventions |
New Page Workflow
Create a new page in ThrillWiki following all conventions and patterns.
Information Gathering
Before creating the page, determine:
- Route: What URL should this page have?
- Page Type:
- List page (shows multiple items)
- Detail page (shows single item)
- Form page (create/edit content)
- Static page (about, contact, etc.)
- Data Requirements: What data does this page need?
- Authentication: Public or authenticated only?
- Related Components: What existing components can be reused?
File Creation Steps
1. Create the Page Component
Location: frontend/pages/[route].vue or frontend/pages/[folder]/[route].vue
<script setup lang="ts">
// Define page metadata
definePageMeta({
// middleware: ['auth'], // If authenticated only
// layout: 'admin', // If using special layout
})
// Set page head
useSeoMeta({
title: 'Page Title | ThrillWiki',
description: 'Page description for SEO',
})
// Fetch data
const { data, pending, error } = await useAsyncData('unique-key', () =>
$fetch('/api/v1/endpoint/')
)
// Handle error
if (error.value) {
throw createError({
statusCode: error.value.statusCode || 500,
message: error.value.message
})
}
</script>
<template>
<PageContainer>
<!-- Breadcrumbs (if applicable) -->
<Breadcrumbs :items="breadcrumbItems" />
<!-- Page Header -->
<div class="mb-8">
<h1 class="text-3xl font-bold">Page Title</h1>
<p class="text-muted-foreground mt-2">Page description</p>
</div>
<!-- Loading State -->
<div v-if="pending" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<Skeleton v-for="i in 8" :key="i" class="h-64" />
</div>
<!-- Content -->
<div v-else>
<!-- Page content here -->
</div>
</PageContainer>
</template>
2. For List Pages - Add Filtering
<script setup lang="ts">
const route = useRoute()
const router = useRouter()
// Filter state from URL
const filters = computed(() => ({
status: route.query.status as string || '',
search: route.query.search as string || '',
page: parseInt(route.query.page as string) || 1
}))
// Fetch with filters
const { data, pending, refresh } = await useAsyncData(
`items-${JSON.stringify(filters.value)}`,
() => $fetch('/api/v1/items/', { params: filters.value }),
{ watch: [filters] }
)
// Update filters
function updateFilter(key: string, value: string) {
router.push({
query: { ...route.query, [key]: value || undefined, page: 1 }
})
}
</script>
<template>
<!-- Filter Bar -->
<div class="flex gap-4 mb-6">
<Input
:model-value="filters.search"
@update:model-value="updateFilter('search', $event)"
placeholder="Search..."
/>
<Select
:model-value="filters.status"
@update:model-value="updateFilter('status', $event)"
>
<SelectOption value="">All</SelectOption>
<SelectOption value="operating">Operating</SelectOption>
<SelectOption value="closed">Closed</SelectOption>
</Select>
</div>
<!-- Results -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<ItemCard v-for="item in data?.results" :key="item.id" :item="item" />
</div>
<!-- Pagination -->
<Pagination
:current-page="filters.page"
:total-pages="Math.ceil((data?.count || 0) / 20)"
@page-change="updateFilter('page', $event.toString())"
/>
</template>
3. For Detail Pages - Dynamic Route
File: frontend/pages/items/[slug].vue
<script setup lang="ts">
const route = useRoute()
const slug = route.params.slug as string
const { data: item, error } = await useAsyncData(
`item-${slug}`,
() => $fetch(`/api/v1/items/${slug}/`)
)
if (error.value) {
throw createError({
statusCode: 404,
message: 'Item not found'
})
}
useSeoMeta({
title: `${item.value?.name} | ThrillWiki`,
description: item.value?.description,
})
</script>
4. For Form Pages
<script setup lang="ts">
import { z } from 'zod'
const schema = z.object({
name: z.string().min(1, 'Name is required'),
description: z.string().optional(),
})
const form = reactive({
name: '',
description: '',
})
const errors = ref<Record<string, string[]>>({})
const isSubmitting = ref(false)
async function handleSubmit() {
// Validate
const result = schema.safeParse(form)
if (!result.success) {
errors.value = result.error.flatten().fieldErrors
return
}
isSubmitting.value = true
try {
await $fetch('/api/v1/items/', {
method: 'POST',
body: form
})
await navigateTo('/items')
} catch (e) {
// Handle API errors
} finally {
isSubmitting.value = false
}
}
</script>
Checklist
After creating the page, verify:
- Page renders without errors
- SEO meta tags are set
- Loading states display correctly
- Error states are handled
- Page is responsive (mobile, tablet, desktop)
- Keyboard navigation works
- Data fetches efficiently (no N+1 issues)
- URL parameters persist correctly (for list pages)
- Authentication is enforced (if required)
Output
Report what was created:
Created: frontend/pages/[path].vue
Route: /[route]
Type: [list/detail/form/static]
Features: [list of features implemented]