@plutocms/supabase 0.0.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/nuxt/GENERATION.md +5 -0
- package/.agents/skills/nuxt/SKILL.md +55 -0
- package/.agents/skills/nuxt/references/advanced-hooks.md +289 -0
- package/.agents/skills/nuxt/references/advanced-layers.md +299 -0
- package/.agents/skills/nuxt/references/advanced-module-authoring.md +554 -0
- package/.agents/skills/nuxt/references/best-practices-data-fetching.md +357 -0
- package/.agents/skills/nuxt/references/best-practices-ssr.md +355 -0
- package/.agents/skills/nuxt/references/core-cli.md +263 -0
- package/.agents/skills/nuxt/references/core-config.md +162 -0
- package/.agents/skills/nuxt/references/core-data-fetching.md +236 -0
- package/.agents/skills/nuxt/references/core-deployment.md +224 -0
- package/.agents/skills/nuxt/references/core-directory-structure.md +269 -0
- package/.agents/skills/nuxt/references/core-modules.md +292 -0
- package/.agents/skills/nuxt/references/core-routing.md +226 -0
- package/.agents/skills/nuxt/references/features-components-autoimport.md +328 -0
- package/.agents/skills/nuxt/references/features-components.md +264 -0
- package/.agents/skills/nuxt/references/features-composables.md +276 -0
- package/.agents/skills/nuxt/references/features-server.md +265 -0
- package/.agents/skills/nuxt/references/features-state.md +194 -0
- package/.agents/skills/nuxt/references/rendering-modes.md +237 -0
- package/.agents/skills/nuxt-ui/SKILL.md +334 -0
- package/.agents/skills/nuxt-ui/references/components.md +377 -0
- package/.agents/skills/nuxt-ui/references/composables.md +127 -0
- package/.agents/skills/nuxt-ui/references/layouts/chat.md +195 -0
- package/.agents/skills/nuxt-ui/references/layouts/dashboard.md +220 -0
- package/.agents/skills/nuxt-ui/references/layouts/docs.md +141 -0
- package/.agents/skills/nuxt-ui/references/layouts/editor.md +167 -0
- package/.agents/skills/nuxt-ui/references/layouts/page.md +260 -0
- package/.agents/skills/nuxt-ui/references/theming.md +427 -0
- package/.agents/skills/supabase-postgres-best-practices/AGENTS.md +68 -0
- package/.agents/skills/supabase-postgres-best-practices/CLAUDE.md +1 -0
- package/.agents/skills/supabase-postgres-best-practices/README.md +116 -0
- package/.agents/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +57 -0
- package/.editorconfig +12 -0
- package/.env.example +2 -0
- package/.eslintignore +1 -0
- package/.prettierrc +9 -0
- package/.vscode/extensions.json +10 -0
- package/.vscode/settings.json +61 -0
- package/README.md +62 -0
- package/app/components/navbar/NavbarAdmin.vue +169 -0
- package/app/composables/auth.ts +81 -0
- package/app/layouts/setup.vue +13 -0
- package/app/middleware/auth.ts +27 -0
- package/app/middleware/setup-check.ts +25 -0
- package/app/pages/admin/(auth)/login.vue +84 -0
- package/app/pages/admin/(auth)/signup.vue +128 -0
- package/app/pages/admin/settings.vue +116 -0
- package/app/pages/admin/setup.vue +300 -0
- package/app/pages/admin.vue +38 -0
- package/bun.lock +2685 -0
- package/eslint.config.mjs +88 -0
- package/nuxt.config.ts +55 -0
- package/package.json +44 -0
- package/public/schema.sql +103 -0
- package/scripts/supabase-typegen.ts +36 -0
- package/server/api/category/list.get.ts +9 -0
- package/server/api/category/new.post.ts +31 -0
- package/server/api/settings/first_setup.get.ts +39 -0
- package/server/api/settings/index.get.ts +27 -0
- package/server/api/settings/update.post.ts +36 -0
- package/server/api/setup/create.post.ts +58 -0
- package/server/api/signup.post.ts +28 -0
- package/server/api/users/[id].get.ts +21 -0
- package/server/api/users/index.get.ts +16 -0
- package/shared/types/supabase.ts +406 -0
- package/skills-lock.json +20 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nuxt-modules
|
|
3
|
+
description: Creating and using Nuxt modules to extend framework functionality
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Nuxt Modules
|
|
7
|
+
|
|
8
|
+
Modules extend Nuxt's core functionality. They run at build time and can add components, composables, plugins, and configuration.
|
|
9
|
+
|
|
10
|
+
## Using Modules
|
|
11
|
+
|
|
12
|
+
Install and add to `nuxt.config.ts`:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// nuxt.config.ts
|
|
16
|
+
export default defineNuxtConfig({
|
|
17
|
+
modules: [
|
|
18
|
+
// npm package
|
|
19
|
+
'@nuxt/ui',
|
|
20
|
+
// Local module
|
|
21
|
+
'./modules/my-module',
|
|
22
|
+
// Inline module
|
|
23
|
+
(options, nuxt) => {
|
|
24
|
+
console.log('Inline module')
|
|
25
|
+
},
|
|
26
|
+
// With options
|
|
27
|
+
['@nuxt/image', { provider: 'cloudinary' }],
|
|
28
|
+
],
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Creating Modules
|
|
33
|
+
|
|
34
|
+
### Basic Module
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
// modules/my-module.ts
|
|
38
|
+
export default defineNuxtModule({
|
|
39
|
+
meta: {
|
|
40
|
+
name: 'my-module',
|
|
41
|
+
configKey: 'myModule',
|
|
42
|
+
},
|
|
43
|
+
defaults: {
|
|
44
|
+
enabled: true,
|
|
45
|
+
},
|
|
46
|
+
setup(options, nuxt) {
|
|
47
|
+
if (!options.enabled) return
|
|
48
|
+
|
|
49
|
+
console.log('My module is running!')
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Adding Components
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// modules/ui/index.ts
|
|
58
|
+
import { addComponent, createResolver } from '@nuxt/kit'
|
|
59
|
+
|
|
60
|
+
export default defineNuxtModule({
|
|
61
|
+
setup(options, nuxt) {
|
|
62
|
+
const { resolve } = createResolver(import.meta.url)
|
|
63
|
+
|
|
64
|
+
// Add single component
|
|
65
|
+
addComponent({
|
|
66
|
+
name: 'MyButton',
|
|
67
|
+
filePath: resolve('./runtime/components/MyButton.vue'),
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Add components directory
|
|
71
|
+
addComponentsDir({
|
|
72
|
+
path: resolve('./runtime/components'),
|
|
73
|
+
prefix: 'My',
|
|
74
|
+
})
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Adding Composables
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// modules/utils/index.ts
|
|
83
|
+
import { addImports, createResolver } from '@nuxt/kit'
|
|
84
|
+
|
|
85
|
+
export default defineNuxtModule({
|
|
86
|
+
setup() {
|
|
87
|
+
const { resolve } = createResolver(import.meta.url)
|
|
88
|
+
|
|
89
|
+
// Add auto-imported composable
|
|
90
|
+
addImports({
|
|
91
|
+
name: 'useMyUtil',
|
|
92
|
+
from: resolve('./runtime/composables/useMyUtil'),
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// Add directory for auto-imports
|
|
96
|
+
addImportsDir(resolve('./runtime/composables'))
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Adding Plugins
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// modules/analytics/index.ts
|
|
105
|
+
import { addPlugin, createResolver } from '@nuxt/kit'
|
|
106
|
+
|
|
107
|
+
export default defineNuxtModule({
|
|
108
|
+
setup() {
|
|
109
|
+
const { resolve } = createResolver(import.meta.url)
|
|
110
|
+
|
|
111
|
+
addPlugin({
|
|
112
|
+
src: resolve('./runtime/plugin'),
|
|
113
|
+
mode: 'client', // 'client', 'server', or 'all'
|
|
114
|
+
})
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Plugin file:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
// modules/analytics/runtime/plugin.ts
|
|
123
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
124
|
+
nuxtApp.hook('page:finish', () => {
|
|
125
|
+
console.log('Page loaded')
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Adding Server Routes
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// modules/api/index.ts
|
|
134
|
+
import { addServerHandler, createResolver } from '@nuxt/kit'
|
|
135
|
+
|
|
136
|
+
export default defineNuxtModule({
|
|
137
|
+
setup() {
|
|
138
|
+
const { resolve } = createResolver(import.meta.url)
|
|
139
|
+
|
|
140
|
+
addServerHandler({
|
|
141
|
+
route: '/api/my-endpoint',
|
|
142
|
+
handler: resolve('./runtime/server/api/my-endpoint'),
|
|
143
|
+
})
|
|
144
|
+
},
|
|
145
|
+
})
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Extending Config
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
// modules/config/index.ts
|
|
152
|
+
export default defineNuxtModule({
|
|
153
|
+
setup(options, nuxt) {
|
|
154
|
+
// Add CSS
|
|
155
|
+
nuxt.options.css.push('my-module/styles.css')
|
|
156
|
+
|
|
157
|
+
// Add runtime config
|
|
158
|
+
nuxt.options.runtimeConfig.public.myModule = {
|
|
159
|
+
apiUrl: options.apiUrl,
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Extend Vite config
|
|
163
|
+
nuxt.options.vite.optimizeDeps ||= {}
|
|
164
|
+
nuxt.options.vite.optimizeDeps.include ||= []
|
|
165
|
+
nuxt.options.vite.optimizeDeps.include.push('some-package')
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Module Hooks
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
export default defineNuxtModule({
|
|
174
|
+
setup(options, nuxt) {
|
|
175
|
+
// Build-time hooks
|
|
176
|
+
nuxt.hook('modules:done', () => {
|
|
177
|
+
console.log('All modules loaded')
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
nuxt.hook('components:dirs', (dirs) => {
|
|
181
|
+
dirs.push({ path: '~/extra-components' })
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
nuxt.hook('pages:extend', (pages) => {
|
|
185
|
+
pages.push({
|
|
186
|
+
name: 'custom-page',
|
|
187
|
+
path: '/custom',
|
|
188
|
+
file: resolve('./runtime/pages/custom.vue'),
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
nuxt.hook('imports:extend', (imports) => {
|
|
193
|
+
imports.push({ name: 'myHelper', from: 'my-package' })
|
|
194
|
+
})
|
|
195
|
+
},
|
|
196
|
+
})
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Module Options
|
|
200
|
+
|
|
201
|
+
Type-safe options with defaults:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
export interface ModuleOptions {
|
|
205
|
+
apiKey: string
|
|
206
|
+
enabled?: boolean
|
|
207
|
+
prefix?: string
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export default defineNuxtModule<ModuleOptions>({
|
|
211
|
+
meta: {
|
|
212
|
+
name: 'my-module',
|
|
213
|
+
configKey: 'myModule',
|
|
214
|
+
},
|
|
215
|
+
defaults: {
|
|
216
|
+
enabled: true,
|
|
217
|
+
prefix: 'My',
|
|
218
|
+
},
|
|
219
|
+
setup(options, nuxt) {
|
|
220
|
+
// options is typed as ModuleOptions
|
|
221
|
+
if (!options.apiKey) {
|
|
222
|
+
console.warn('API key not provided')
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
})
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Usage:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
// nuxt.config.ts
|
|
232
|
+
export default defineNuxtConfig({
|
|
233
|
+
modules: ['my-module'],
|
|
234
|
+
myModule: {
|
|
235
|
+
apiKey: 'xxx',
|
|
236
|
+
prefix: 'Custom',
|
|
237
|
+
},
|
|
238
|
+
})
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Local Modules
|
|
242
|
+
|
|
243
|
+
Place in `modules/` directory:
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
modules/
|
|
247
|
+
├── my-module/
|
|
248
|
+
│ ├── index.ts
|
|
249
|
+
│ └── runtime/
|
|
250
|
+
│ ├── components/
|
|
251
|
+
│ ├── composables/
|
|
252
|
+
│ └── plugin.ts
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Auto-registered or manually added:
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
// nuxt.config.ts
|
|
259
|
+
export default defineNuxtConfig({
|
|
260
|
+
modules: [
|
|
261
|
+
'~/modules/my-module', // Explicit
|
|
262
|
+
],
|
|
263
|
+
})
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Module Dependencies
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
export default defineNuxtModule({
|
|
270
|
+
meta: {
|
|
271
|
+
name: 'my-module',
|
|
272
|
+
},
|
|
273
|
+
moduleDependencies: {
|
|
274
|
+
'@nuxt/image': {
|
|
275
|
+
version: '>=1.0.0',
|
|
276
|
+
defaults: {
|
|
277
|
+
provider: 'ipx',
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
setup() {
|
|
282
|
+
// @nuxt/image is guaranteed to be installed
|
|
283
|
+
},
|
|
284
|
+
})
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
<!--
|
|
288
|
+
Source references:
|
|
289
|
+
- https://nuxt.com/docs/guide/modules
|
|
290
|
+
- https://nuxt.com/docs/guide/modules/module-anatomy
|
|
291
|
+
- https://nuxt.com/docs/api/kit
|
|
292
|
+
-->
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: routing
|
|
3
|
+
description: File-based routing, dynamic routes, navigation, and middleware in Nuxt
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Routing
|
|
7
|
+
|
|
8
|
+
Nuxt uses file-system routing based on vue-router. Files in `app/pages/` automatically create routes.
|
|
9
|
+
|
|
10
|
+
## Basic Routing
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
pages/
|
|
14
|
+
├── index.vue → /
|
|
15
|
+
├── about.vue → /about
|
|
16
|
+
└── posts/
|
|
17
|
+
├── index.vue → /posts
|
|
18
|
+
└── [id].vue → /posts/:id
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Dynamic Routes
|
|
22
|
+
|
|
23
|
+
Use brackets for dynamic segments:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
pages/
|
|
27
|
+
├── users/
|
|
28
|
+
│ └── [id].vue → /users/:id
|
|
29
|
+
├── posts/
|
|
30
|
+
│ └── [...slug].vue → /posts/* (catch-all)
|
|
31
|
+
└── [[optional]].vue → /:optional? (optional param)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Access route parameters:
|
|
35
|
+
|
|
36
|
+
```vue
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
const route = useRoute()
|
|
39
|
+
// /posts/123 → route.params.id = '123'
|
|
40
|
+
console.log(route.params.id)
|
|
41
|
+
</script>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Navigation
|
|
45
|
+
|
|
46
|
+
### NuxtLink Component
|
|
47
|
+
|
|
48
|
+
```vue
|
|
49
|
+
<template>
|
|
50
|
+
<nav>
|
|
51
|
+
<NuxtLink to="/">Home</NuxtLink>
|
|
52
|
+
<NuxtLink to="/about">About</NuxtLink>
|
|
53
|
+
<NuxtLink :to="{ name: 'posts-id', params: { id: 1 } }">Post 1</NuxtLink>
|
|
54
|
+
</nav>
|
|
55
|
+
</template>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
NuxtLink automatically prefetches linked pages when they enter the viewport.
|
|
59
|
+
|
|
60
|
+
### Programmatic Navigation
|
|
61
|
+
|
|
62
|
+
```vue
|
|
63
|
+
<script setup lang="ts">
|
|
64
|
+
const router = useRouter()
|
|
65
|
+
|
|
66
|
+
function goToPost(id: number) {
|
|
67
|
+
navigateTo(`/posts/${id}`)
|
|
68
|
+
// or
|
|
69
|
+
router.push({ name: 'posts-id', params: { id } })
|
|
70
|
+
}
|
|
71
|
+
</script>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Route Middleware
|
|
75
|
+
|
|
76
|
+
### Named Middleware
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// middleware/auth.ts
|
|
80
|
+
export default defineNuxtRouteMiddleware((to, from) => {
|
|
81
|
+
const isAuthenticated = false // Your auth logic
|
|
82
|
+
|
|
83
|
+
if (!isAuthenticated) {
|
|
84
|
+
return navigateTo('/login')
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Apply to pages:
|
|
90
|
+
|
|
91
|
+
```vue
|
|
92
|
+
<script setup lang="ts">
|
|
93
|
+
definePageMeta({
|
|
94
|
+
middleware: 'auth',
|
|
95
|
+
// or multiple: middleware: ['auth', 'admin']
|
|
96
|
+
})
|
|
97
|
+
</script>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Global Middleware
|
|
101
|
+
|
|
102
|
+
Name files with `.global` suffix:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// middleware/logging.global.ts
|
|
106
|
+
export default defineNuxtRouteMiddleware((to, from) => {
|
|
107
|
+
console.log('Navigating to:', to.path)
|
|
108
|
+
})
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Inline Middleware
|
|
112
|
+
|
|
113
|
+
```vue
|
|
114
|
+
<script setup lang="ts">
|
|
115
|
+
definePageMeta({
|
|
116
|
+
middleware: [
|
|
117
|
+
function (to, from) {
|
|
118
|
+
// Inline middleware logic
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
})
|
|
122
|
+
</script>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Page Meta
|
|
126
|
+
|
|
127
|
+
Configure page-level options:
|
|
128
|
+
|
|
129
|
+
```vue
|
|
130
|
+
<script setup lang="ts">
|
|
131
|
+
definePageMeta({
|
|
132
|
+
title: 'My Page',
|
|
133
|
+
layout: 'custom',
|
|
134
|
+
middleware: 'auth',
|
|
135
|
+
validate: (route) => {
|
|
136
|
+
// Return false for 404, or object with status/statusText
|
|
137
|
+
return /^\d+$/.test(route.params.id as string)
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
</script>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Route Validation
|
|
144
|
+
|
|
145
|
+
```vue
|
|
146
|
+
<script setup lang="ts">
|
|
147
|
+
definePageMeta({
|
|
148
|
+
validate: (route) => {
|
|
149
|
+
// Must return boolean or object with status
|
|
150
|
+
return typeof route.params.id === 'string' && /^\d+$/.test(route.params.id)
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
</script>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Layouts
|
|
157
|
+
|
|
158
|
+
Define layouts in `app/layouts/`:
|
|
159
|
+
|
|
160
|
+
```vue
|
|
161
|
+
<!-- layouts/default.vue -->
|
|
162
|
+
<template>
|
|
163
|
+
<div>
|
|
164
|
+
<header>Header</header>
|
|
165
|
+
<slot />
|
|
166
|
+
<footer>Footer</footer>
|
|
167
|
+
</div>
|
|
168
|
+
</template>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```vue
|
|
172
|
+
<!-- layouts/admin.vue -->
|
|
173
|
+
<template>
|
|
174
|
+
<div class="admin">
|
|
175
|
+
<AdminSidebar />
|
|
176
|
+
<main>
|
|
177
|
+
<slot />
|
|
178
|
+
</main>
|
|
179
|
+
</div>
|
|
180
|
+
</template>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Use in pages:
|
|
184
|
+
|
|
185
|
+
```vue
|
|
186
|
+
<script setup lang="ts">
|
|
187
|
+
definePageMeta({
|
|
188
|
+
layout: 'admin',
|
|
189
|
+
})
|
|
190
|
+
</script>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Dynamic layout:
|
|
194
|
+
|
|
195
|
+
```vue
|
|
196
|
+
<script setup lang="ts">
|
|
197
|
+
const layout = ref('default')
|
|
198
|
+
|
|
199
|
+
function enableAdmin() {
|
|
200
|
+
setPageLayout('admin')
|
|
201
|
+
}
|
|
202
|
+
</script>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Navigation Hooks
|
|
206
|
+
|
|
207
|
+
```vue
|
|
208
|
+
<script setup lang="ts">
|
|
209
|
+
onBeforeRouteLeave((to, from) => {
|
|
210
|
+
// Confirm before leaving
|
|
211
|
+
const answer = window.confirm('Leave?')
|
|
212
|
+
if (!answer) return false
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
onBeforeRouteUpdate((to, from) => {
|
|
216
|
+
// Called when route changes but component is reused
|
|
217
|
+
})
|
|
218
|
+
</script>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
<!--
|
|
222
|
+
Source references:
|
|
223
|
+
- https://nuxt.com/docs/getting-started/routing
|
|
224
|
+
- https://nuxt.com/docs/directory-structure/app/pages
|
|
225
|
+
- https://nuxt.com/docs/directory-structure/app/middleware
|
|
226
|
+
-->
|