Add nuxt-skills and update auto quotes to use new policy API structure
- Add nuxt-skills (vue, nuxt, nuxt-ui) to .claude/skills/ - Create useCustomerSelection() composable for managing insured/buyer selection - Create usePolicyApi() composable for policy API operations - Update auto quote components to use insured/buyer instead of client - Update vehicle fields: remove valorVehiculo, add market_value, requested_value, rc_limits - Make chassis_number and engine_number optional - Update auto quote types and composables to match new API structure - Update auto quote page to submit to policy API with new structure
This commit is contained in:
242
.claude/skills/nuxt/references/routing.md
Normal file
242
.claude/skills/nuxt/references/routing.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# Nuxt File-Based Routing
|
||||
|
||||
## When to Use
|
||||
|
||||
Working with `pages/` or `layouts/` directories, file-based routing, navigation.
|
||||
|
||||
## File-Based Routing Basics
|
||||
|
||||
`pages/` folder structure directly maps to routes. File names determine URLs.
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
**Key principles:**
|
||||
|
||||
- **ALWAYS use descriptive params:** `[userId].vue` NOT `[id].vue`
|
||||
- **Optional params:** `[[paramName]].vue`
|
||||
- **Catch-all:** `[...path].vue`
|
||||
- **Route groups for organization:** `(folder)/` groups files without affecting URLs
|
||||
|
||||
## Red Flags - Stop and Check Skill
|
||||
|
||||
If you're thinking any of these, STOP and re-read this skill:
|
||||
|
||||
- "String paths are simpler than typed routes"
|
||||
- "Generic param names like [id] are fine"
|
||||
- "I remember how Nuxt 3 worked"
|
||||
|
||||
All of these mean: You're about to use outdated patterns. Use Nuxt 4 patterns instead.
|
||||
|
||||
## File Structure Example
|
||||
|
||||
```
|
||||
pages/
|
||||
├── index.vue # /
|
||||
├── about.vue # /about
|
||||
├── [...slug].vue # catch-all for 404
|
||||
├── users.vue # parent route (layout for /users/*)
|
||||
└── users/
|
||||
├── index.vue # /users
|
||||
└── [userId].vue # /users/:userId
|
||||
```
|
||||
|
||||
## Route Groups for Organization
|
||||
|
||||
Route groups organize files WITHOUT affecting URLs. Wrap folder names in parentheses:
|
||||
|
||||
```
|
||||
pages/
|
||||
├── (marketing)/ # group folder (ignored in URL)
|
||||
│ ├── about.vue # /about (not /marketing/about)
|
||||
│ └── pricing.vue # /pricing
|
||||
└── (admin)/ # group folder (ignored in URL)
|
||||
├── dashboard.vue # /dashboard
|
||||
└── settings.vue # /settings
|
||||
```
|
||||
|
||||
**Use route groups to:**
|
||||
|
||||
- Organize pages by feature/team
|
||||
- Group related routes without affecting URLs
|
||||
- Keep large projects maintainable
|
||||
- Apply middleware to specific groups (via `route.meta.groups`)
|
||||
|
||||
**Access route groups in middleware:**
|
||||
|
||||
```ts
|
||||
// middleware/auth.global.ts
|
||||
export default defineNuxtRouteMiddleware((to) => {
|
||||
// Check if route is in admin group
|
||||
if (to.meta.groups?.includes('admin')) {
|
||||
const auth = useAuthStore()
|
||||
if (!auth.isAdmin) return navigateTo('/')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Parent Routes (Layouts)
|
||||
|
||||
Parent route = layout for nested routes:
|
||||
|
||||
```vue
|
||||
<!-- pages/users.vue -->
|
||||
<template>
|
||||
<div class="users-layout">
|
||||
<nav>
|
||||
<NuxtLink to="/users">All Users</NuxtLink>
|
||||
<NuxtLink to="/users/create">Create User</NuxtLink>
|
||||
</nav>
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
Child routes:
|
||||
|
||||
```
|
||||
pages/
|
||||
├── users.vue # Parent route with <NuxtPage />
|
||||
└── users/
|
||||
├── index.vue # /users
|
||||
├── [userId].vue # /users/:userId
|
||||
└── create.vue # /users/create
|
||||
```
|
||||
|
||||
## definePage() for Route Customization
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
definePage({
|
||||
name: 'user-profile',
|
||||
path: '/profile/:userId', // Override default path
|
||||
alias: ['/me', '/profile'],
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: 'User Profile',
|
||||
roles: ['user', 'admin']
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>Profile content</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Typed Router
|
||||
|
||||
**ALWAYS use typed routes for navigation:**
|
||||
|
||||
```ts
|
||||
// ✅ Type-safe with route name
|
||||
await navigateTo({ name: '/users/[userId]', params: { userId: '123' } })
|
||||
|
||||
// ❌ String-based (not type-safe, avoid)
|
||||
await navigateTo('/users/123')
|
||||
```
|
||||
|
||||
**REQUIRED: Check `typed-router.d.ts` for available route names and params before navigating.**
|
||||
|
||||
## useRoute with Types
|
||||
|
||||
Pass route name for stricter typing:
|
||||
|
||||
```ts
|
||||
// Generic route
|
||||
const route = useRoute()
|
||||
|
||||
// Typed route (preferred)
|
||||
const route = useRoute('/users/[userId]')
|
||||
// route.params.userId is now typed correctly
|
||||
```
|
||||
|
||||
## Navigation
|
||||
|
||||
```ts
|
||||
// Navigate to route
|
||||
await navigateTo('/about')
|
||||
await navigateTo({ name: '/users/[userId]', params: { userId: '123' } })
|
||||
|
||||
// Navigate with query
|
||||
await navigateTo({ path: '/search', query: { q: 'nuxt' } })
|
||||
|
||||
// External redirect
|
||||
await navigateTo('https://nuxt.com', { external: true })
|
||||
|
||||
// Replace history
|
||||
await navigateTo('/login', { replace: true })
|
||||
|
||||
// Open in new tab
|
||||
await navigateTo('/docs', { open: { target: '_blank' } })
|
||||
```
|
||||
|
||||
## Route Meta & Middleware
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
middleware: ['auth', 'admin'],
|
||||
layout: 'dashboard',
|
||||
meta: {
|
||||
requiresAuth: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## Dynamic Layout Switching
|
||||
|
||||
Use `setPageLayout()` to switch layouts programmatically:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const user = useUser()
|
||||
|
||||
// Switch layout based on auth state
|
||||
if (!user.value) {
|
||||
setPageLayout('guest')
|
||||
} else {
|
||||
setPageLayout('dashboard')
|
||||
}
|
||||
|
||||
// With layout props (Nuxt 4.3+)
|
||||
setPageLayout('dashboard', {
|
||||
sidebar: 'collapsed',
|
||||
theme: 'dark'
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## Dynamic Routes Patterns
|
||||
|
||||
```
|
||||
[userId].vue # /users/123
|
||||
[[slug]].vue # /blog or /blog/post (optional)
|
||||
[...path].vue # /a/b/c (catch-all)
|
||||
[[...path]].vue # / or /a/b/c (optional catch-all)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **`index.vue` for index routes** - valid and correct for creating default routes
|
||||
- **Route groups `(folder)/` for organization** - group files without affecting URLs
|
||||
- **Descriptive param names** - `[userId]` not `[id]`, `[postSlug]` not `[slug]`
|
||||
- **Type-safe navigation** - use route names, not strings
|
||||
- **Check typed-router.d.ts** for available routes
|
||||
- **Parent routes for layouts** - `users.vue` with `<NuxtPage />`
|
||||
- **Use definePage** for custom paths/aliases
|
||||
- **Catch-all for 404** - `[...path].vue` or `[...slug].vue`
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| ❌ Wrong | ✅ Right |
|
||||
| ---------------------------- | ----------------------------------------------------------------- |
|
||||
| `[id].vue` | `[userId].vue` or `[postId].vue` |
|
||||
| `navigateTo('/users/' + id)` | `navigateTo({ name: '/users/[userId]', params: { userId: id } })` |
|
||||
| `<Nuxt />` | `<NuxtPage />` |
|
||||
| Separate layouts/ folder | Parent routes with `<NuxtPage />` |
|
||||
|
||||
## Resources
|
||||
|
||||
- Nuxt routing: https://nuxt.com/docs/guide/directory-structure/pages
|
||||
- File-based routing: https://nuxt.com/docs/getting-started/routing
|
||||
Reference in New Issue
Block a user