# ThrillWiki Nuxt 4 Frontend Standards

Rules for developing the ThrillWiki frontend with Nuxt 4.

## Project Structure

```
frontend/
├── app.vue                  # Root component
├── nuxt.config.ts          # Nuxt configuration
├── pages/                   # File-based routing
│   ├── index.vue           # Homepage (/)
│   ├── parks/
│   │   ├── index.vue       # /parks
│   │   ├── nearby.vue      # /parks/nearby
│   │   └── [slug].vue      # /parks/:slug
│   └── ...
├── components/
│   ├── layout/             # Header, Footer, Sidebar
│   ├── ui/                 # Base components (Button, Card, Input)
│   ├── entity/             # ParkCard, RideCard, ReviewCard
│   └── forms/              # Form components
├── composables/            # Shared logic (useAuth, useApi, useUnits)
├── stores/                 # Pinia stores
├── types/                  # TypeScript interfaces
└── assets/
    └── css/                # Global styles, Tailwind config
```

## Component Conventions

### Naming
- Use PascalCase for component files: `ParkCard.vue`, `SearchAutocomplete.vue`
- Use kebab-case in templates: `<park-card>`, `<search-autocomplete>`
- Prefix base components with `Base`: `BaseButton.vue`, `BaseInput.vue`

### Component Structure
```vue
<script setup lang="ts">
// 1. Type imports
import type { Park } from '~/types'

// 2. Component imports (auto-imported usually)

// 3. Props and emits
const props = defineProps<{
  park: Park
  variant?: 'default' | 'compact'
}>()

const emit = defineEmits<{
  (e: 'select', park: Park): void
}>()

// 4. Composables
const { formatDistance } = useUnits()

// 5. Refs and reactive state
const isExpanded = ref(false)

// 6. Computed properties
const displayLocation = computed(() => 
  `${props.park.city}, ${props.park.country}`
)

// 7. Functions
function handleClick() {
  emit('select', props.park)
}

// 8. Lifecycle hooks (if needed)
</script>

<template>
  <!-- Template here -->
</template>

<style scoped>
/* Scoped styles, prefer Tailwind classes in template */
</style>
```

### TypeScript
- Enable strict mode
- Define interfaces for all data structures in `types/`
- Use `defineProps<T>()` with TypeScript generics
- No `any` types without explicit justification

## Routing

### File-Based Routes
Follow Nuxt 4 file-based routing conventions:
- `pages/index.vue` → `/`
- `pages/parks/index.vue` → `/parks`
- `pages/parks/[slug].vue` → `/parks/:slug`
- `pages/parks/[park]/rides/[ride].vue` → `/parks/:park/rides/:ride`

### Navigation
```typescript
// Use navigateTo for programmatic navigation
await navigateTo('/parks/cedar-point')

// Use NuxtLink for declarative navigation
<NuxtLink to="/parks">All Parks</NuxtLink>
```

## Data Fetching

### Use Composables
```typescript
// composables/useParks.ts
export function useParks() {
  const { data, pending, error, refresh } = useFetch('/api/parks/')
  
  return {
    parks: data,
    loading: pending,
    error,
    refresh
  }
}
```

### In Components
```typescript
// Use useAsyncData for page-level data
const { data: park } = await useAsyncData(
  `park-${route.params.slug}`,
  () => $fetch(`/api/parks/${route.params.slug}/`)
)
```

## State Management (Pinia)

### Store Structure
```typescript
// stores/auth.ts
export const useAuthStore = defineStore('auth', () => {
  const user = ref<User | null>(null)
  const isAuthenticated = computed(() => !!user.value)
  
  async function login(credentials: LoginCredentials) {
    // Implementation
  }
  
  function logout() {
    user.value = null
  }
  
  return { user, isAuthenticated, login, logout }
})
```

### Using Stores
```typescript
const authStore = useAuthStore()
const { user, isAuthenticated } = storeToRefs(authStore)
```

## API Integration

### Base API Composable
```typescript
// composables/useApi.ts
export function useApi() {
  const config = useRuntimeConfig()
  const authStore = useAuthStore()
  
  return $fetch.create({
    baseURL: config.public.apiBase,
    headers: {
      ...(authStore.token && { Authorization: `Bearer ${authStore.token}` })
    }
  })
}
```

## Error Handling

### Page Errors
```typescript
// In page components
const { data, error } = await useAsyncData(...)

if (error.value) {
  throw createError({
    statusCode: error.value.statusCode || 500,
    message: error.value.message
  })
}
```

### Form Errors
- Display validation errors inline with form fields
- Use toast notifications for API errors
- Provide clear user feedback

## Accessibility Requirements

- All interactive elements must be keyboard accessible
- Provide proper ARIA labels
- Ensure color contrast meets WCAG AA standards
- Support `prefers-reduced-motion`
- Use semantic HTML elements
