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:
181
.claude/skills/vue/references/router.md
Normal file
181
.claude/skills/vue/references/router.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# Vue Router Typing
|
||||
|
||||
Type-safe routing patterns for Vue Router.
|
||||
|
||||
> **For Nuxt:** Use file-based routing instead. See `nuxt` skill for Nuxt routing patterns.
|
||||
|
||||
## Route Meta Types
|
||||
|
||||
Extend `RouteMeta` for typed route.meta:
|
||||
|
||||
```ts
|
||||
// router/types.ts
|
||||
import 'vue-router'
|
||||
|
||||
declare module 'vue-router' {
|
||||
interface RouteMeta {
|
||||
requiresAuth?: boolean
|
||||
title?: string
|
||||
roles?: ('admin' | 'user')[]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
|
||||
```ts
|
||||
const route = useRoute()
|
||||
route.meta.requiresAuth // boolean | undefined (typed!)
|
||||
route.meta.title // string | undefined
|
||||
```
|
||||
|
||||
## Typed Route Params with unplugin-vue-router
|
||||
|
||||
Use `unplugin-vue-router` for fully typed routes:
|
||||
|
||||
```bash
|
||||
pnpm add -D unplugin-vue-router
|
||||
```
|
||||
|
||||
```ts
|
||||
// vite.config.ts
|
||||
import VueRouter from 'unplugin-vue-router/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [VueRouter(), Vue()], // VueRouter BEFORE Vue
|
||||
})
|
||||
```
|
||||
|
||||
**Typed useRoute:**
|
||||
|
||||
```ts
|
||||
// Auto-generated route types from file structure
|
||||
const route = useRoute('/users/[id]')
|
||||
route.params.id // string (typed!)
|
||||
|
||||
const route = useRoute('/posts/[...slug]')
|
||||
route.params.slug // string[] (typed!)
|
||||
```
|
||||
|
||||
**Typed router.push:**
|
||||
|
||||
```ts
|
||||
const router = useRouter()
|
||||
|
||||
// ✅ Type-checked
|
||||
router.push({ name: '/users/[id]', params: { id: '123' } })
|
||||
|
||||
// ❌ TypeScript error - wrong param
|
||||
router.push({ name: '/users/[id]', params: { userId: '123' } })
|
||||
```
|
||||
|
||||
## Scroll Behavior Types
|
||||
|
||||
Type scroll behavior function:
|
||||
|
||||
```ts
|
||||
import type { RouterScrollBehavior } from 'vue-router'
|
||||
|
||||
const scrollBehavior: RouterScrollBehavior = (to, from, savedPosition) => {
|
||||
if (savedPosition) return savedPosition
|
||||
if (to.hash) return { el: to.hash, behavior: 'smooth' }
|
||||
return { top: 0 }
|
||||
}
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
scrollBehavior,
|
||||
})
|
||||
```
|
||||
|
||||
## Dynamic Route Params
|
||||
|
||||
Handle union types for dynamic segments:
|
||||
|
||||
```ts
|
||||
// routes/[type].vue where type can be 'posts' | 'users' | 'comments'
|
||||
const route = useRoute()
|
||||
|
||||
// Narrow params type
|
||||
type ContentType = 'posts' | 'users' | 'comments'
|
||||
|
||||
const type = route.params.type as ContentType
|
||||
|
||||
// Or use route guards
|
||||
if (route.params.type === 'posts') {
|
||||
// TypeScript knows type is 'posts'
|
||||
}
|
||||
```
|
||||
|
||||
## Navigation Guards Types
|
||||
|
||||
Type navigation guards:
|
||||
|
||||
```ts
|
||||
import type { NavigationGuardWithThis, RouteLocationNormalized } from 'vue-router'
|
||||
|
||||
const authGuard: NavigationGuardWithThis<undefined> = (to, from) => {
|
||||
if (to.meta.requiresAuth && !isAuthenticated()) {
|
||||
return { name: 'login', query: { redirect: to.fullPath } }
|
||||
}
|
||||
}
|
||||
|
||||
router.beforeEach(authGuard)
|
||||
```
|
||||
|
||||
**Per-route guards:**
|
||||
|
||||
```ts
|
||||
const routes = [
|
||||
{
|
||||
path: '/admin',
|
||||
component: AdminPage,
|
||||
beforeEnter: (to: RouteLocationNormalized) => {
|
||||
if (!hasAdminRole()) return { name: 'forbidden' }
|
||||
},
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
## RouteLocation Types
|
||||
|
||||
Common route types:
|
||||
|
||||
```ts
|
||||
import type {
|
||||
RouteLocationNormalized, // Resolved route (after navigation)
|
||||
RouteLocationNormalizedLoaded, // Current route (from useRoute)
|
||||
RouteLocationRaw, // Input to router.push()
|
||||
RouteRecordRaw, // Route config definition
|
||||
} from 'vue-router'
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
**Not extending RouteMeta module:**
|
||||
|
||||
```ts
|
||||
// ❌ Loses type info
|
||||
route.meta.customField // any
|
||||
|
||||
// ✅ Extend the interface
|
||||
declare module 'vue-router' {
|
||||
interface RouteMeta { customField: string }
|
||||
}
|
||||
```
|
||||
|
||||
**Assuming params are always strings:**
|
||||
|
||||
```ts
|
||||
// Catch-all routes have string[] params
|
||||
const route = useRoute()
|
||||
|
||||
// ❌ May be string[]
|
||||
const id = route.params.id
|
||||
|
||||
// ✅ Handle both cases
|
||||
const id = Array.isArray(route.params.id)
|
||||
? route.params.id[0]
|
||||
: route.params.id
|
||||
```
|
||||
Reference in New Issue
Block a user