cinqcinqdev-seo 0.1.0
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/README.md +192 -0
- package/dist/module.d.cts +2 -0
- package/dist/module.d.mts +2 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +18 -0
- package/dist/types.d.mts +13 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# nuxt-admin-cms
|
|
2
|
+
|
|
3
|
+
A reusable Nuxt 3 admin module with a visual 3-panel page editor, powered by Supabase.
|
|
4
|
+
|
|
5
|
+
Drop it into any Nuxt project to get:
|
|
6
|
+
- `/admin` — Dashboard with page type cards
|
|
7
|
+
- `/admin/pages/:type` — List + create + delete pages by type
|
|
8
|
+
- `/admin/editor/:id` — Full visual editor (structure sidebar / live preview / properties panel)
|
|
9
|
+
- `/admin/account` — User profile + optional plan management
|
|
10
|
+
- Auto auth middleware protecting all `/admin` routes
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
Your project must have these modules installed and configured:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @nuxtjs/supabase @pinia/nuxt
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add nuxt-admin-cms
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
In `nuxt.config.ts`:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
export default defineNuxtConfig({
|
|
32
|
+
modules: [
|
|
33
|
+
'@nuxtjs/supabase',
|
|
34
|
+
'@pinia/nuxt',
|
|
35
|
+
'nuxt-admin-cms',
|
|
36
|
+
],
|
|
37
|
+
|
|
38
|
+
adminCms: {
|
|
39
|
+
branding: {
|
|
40
|
+
name: 'My App',
|
|
41
|
+
logoUrl: '/logo.png', // optional
|
|
42
|
+
},
|
|
43
|
+
loginRoute: '/login',
|
|
44
|
+
tables: {
|
|
45
|
+
pages: 'pages', // your Supabase table for pages
|
|
46
|
+
users: 'users', // your Supabase table for users
|
|
47
|
+
},
|
|
48
|
+
storageBucket: 'site', // Supabase storage bucket for image uploads
|
|
49
|
+
pageTypes: [
|
|
50
|
+
{ id: 'landing_page', label: 'Landing Page', icon: '🚀' },
|
|
51
|
+
{ id: 'service_page', label: 'Service Page', icon: '🛠️' },
|
|
52
|
+
{ id: 'about_page', label: 'About Page', icon: '👋' },
|
|
53
|
+
{ id: 'blog_article', label: 'Blog Article', icon: '📝' },
|
|
54
|
+
],
|
|
55
|
+
// Optional: plans for the account page
|
|
56
|
+
plans: [
|
|
57
|
+
{ value: 'free', title: 'Free', price: '$0/mo', features: ['1 site', '5 pages'] },
|
|
58
|
+
{ value: 'pro', title: 'Pro', price: '$19/mo', features: ['Unlimited sites', 'Priority support'], popular: true },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Supabase Setup
|
|
67
|
+
|
|
68
|
+
### Required table: `pages`
|
|
69
|
+
|
|
70
|
+
```sql
|
|
71
|
+
create table pages (
|
|
72
|
+
id uuid primary key default gen_random_uuid(),
|
|
73
|
+
title text not null,
|
|
74
|
+
slug text not null unique,
|
|
75
|
+
type text not null,
|
|
76
|
+
status text not null default 'draft',
|
|
77
|
+
content jsonb not null default '[]',
|
|
78
|
+
seo_config jsonb default '{}',
|
|
79
|
+
content_locked boolean default false,
|
|
80
|
+
label text,
|
|
81
|
+
prefix text,
|
|
82
|
+
created_at timestamptz default now(),
|
|
83
|
+
updated_at timestamptz default now()
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
-- Auto-update updated_at
|
|
87
|
+
create or replace function update_updated_at()
|
|
88
|
+
returns trigger as $$
|
|
89
|
+
begin
|
|
90
|
+
new.updated_at = now();
|
|
91
|
+
return new;
|
|
92
|
+
end;
|
|
93
|
+
$$ language plpgsql;
|
|
94
|
+
|
|
95
|
+
create trigger pages_updated_at
|
|
96
|
+
before update on pages
|
|
97
|
+
for each row execute function update_updated_at();
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Required table: `users`
|
|
101
|
+
|
|
102
|
+
```sql
|
|
103
|
+
create table users (
|
|
104
|
+
id uuid primary key references auth.users(id),
|
|
105
|
+
plan text default 'free',
|
|
106
|
+
is_subscription_active boolean default false,
|
|
107
|
+
trial_expires_at timestamptz,
|
|
108
|
+
created_at timestamptz default now()
|
|
109
|
+
);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Storage bucket
|
|
113
|
+
|
|
114
|
+
Create a bucket named `site` (or whatever you set as `storageBucket`) in Supabase Storage with public read access.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Section Components
|
|
119
|
+
|
|
120
|
+
The editor renders sections by resolving components from your app's `components/sections/` directory.
|
|
121
|
+
|
|
122
|
+
The default section types are: `HeroSection`, `TextVisual`, `ServiceGrid`, `Features`, `FAQ`, `Process`, `Testimonials`, `Pricing`, `ContactForm`.
|
|
123
|
+
|
|
124
|
+
Create matching Vue components:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
components/
|
|
128
|
+
sections/
|
|
129
|
+
HeroSection.vue ← receives props defined in the editor
|
|
130
|
+
ServiceGrid.vue
|
|
131
|
+
FAQ.vue
|
|
132
|
+
...
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Each section component receives its props directly via `v-bind`. Example:
|
|
136
|
+
|
|
137
|
+
```vue
|
|
138
|
+
<!-- components/sections/HeroSection.vue -->
|
|
139
|
+
<script setup lang="ts">
|
|
140
|
+
defineProps<{
|
|
141
|
+
title: string | { fr: string; en: string; ar: string }
|
|
142
|
+
subtitle?: string | { fr: string; en: string; ar: string }
|
|
143
|
+
bgColor?: string
|
|
144
|
+
textColor?: string
|
|
145
|
+
ctaText?: string | { fr: string; en: string; ar: string }
|
|
146
|
+
ctaLink?: string
|
|
147
|
+
}>()
|
|
148
|
+
</script>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
> **i18n props**: Some fields are stored as `{ fr: '...', en: '...', ar: '...' }` objects. Handle them accordingly in your section components.
|
|
152
|
+
|
|
153
|
+
### Adding custom sections
|
|
154
|
+
|
|
155
|
+
Add your own sections via the `extraSections` option:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
adminCms: {
|
|
159
|
+
extraSections: {
|
|
160
|
+
MyCustomSection: {
|
|
161
|
+
label: 'Custom Block',
|
|
162
|
+
icon: '✨',
|
|
163
|
+
defaultProps: { title: 'Hello', bgColor: '#fff' },
|
|
164
|
+
fields: {
|
|
165
|
+
title: { type: 'text', label: 'Title' },
|
|
166
|
+
bgColor: { type: 'color', label: 'Background' },
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Then create `components/sections/MyCustomSection.vue` in your app.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Auth
|
|
178
|
+
|
|
179
|
+
The module registers a global middleware that:
|
|
180
|
+
- Redirects unauthenticated users trying to access `/admin/*` to `loginRoute` (`/login` by default)
|
|
181
|
+
- Redirects authenticated users away from `loginRoute` to `/admin`
|
|
182
|
+
|
|
183
|
+
Your login page + Supabase auth setup is your responsibility (the module only protects routes).
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## What's NOT included
|
|
188
|
+
|
|
189
|
+
- Login / Register pages → you provide these
|
|
190
|
+
- Section component UI → you provide the Vue components in `components/sections/`
|
|
191
|
+
- Email notifications, webhooks → out of scope
|
|
192
|
+
- Multi-tenancy / per-user page isolation → implement RLS in Supabase
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createJiti } from "file:///Users/mac/Desktop/nuxt-admin-cms/node_modules/.pnpm/jiti@2.7.0/node_modules/jiti/lib/jiti.mjs";
|
|
2
|
+
|
|
3
|
+
const jiti = createJiti(import.meta.url, {
|
|
4
|
+
"interopDefault": true,
|
|
5
|
+
"alias": {
|
|
6
|
+
"cinqcinqdev-seo": "/Users/mac/Desktop/nuxt-admin-cms"
|
|
7
|
+
},
|
|
8
|
+
"transformOptions": {
|
|
9
|
+
"babel": {
|
|
10
|
+
"plugins": []
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
/** @type {import("/Users/mac/Desktop/nuxt-admin-cms/src/module")} */
|
|
16
|
+
const _module = await jiti.import("/Users/mac/Desktop/nuxt-admin-cms/src/module.ts");
|
|
17
|
+
|
|
18
|
+
export default _module?.default ?? _module;
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ModuleHooks, ModuleRuntimeHooks, ModuleRuntimeConfig, ModulePublicRuntimeConfig } from './module.mjs'
|
|
2
|
+
|
|
3
|
+
declare module '#app' {
|
|
4
|
+
interface RuntimeNuxtHooks extends ModuleRuntimeHooks {}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare module '@nuxt/schema' {
|
|
8
|
+
interface NuxtHooks extends ModuleHooks {}
|
|
9
|
+
interface RuntimeConfig extends ModuleRuntimeConfig {}
|
|
10
|
+
interface PublicRuntimeConfig extends ModulePublicRuntimeConfig {}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export * from "./module.mjs"
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cinqcinqdev-seo",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A reusable Nuxt 3 admin CMS module with visual page editor powered by Supabase",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"module": "./dist/module.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/module.mjs"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "nuxt-module-build build",
|
|
17
|
+
"dev": "nuxt-module-build build --stub && nuxt dev playground",
|
|
18
|
+
"dev:build": "nuxt build playground",
|
|
19
|
+
"prepare": "nuxt-module-build build --stub",
|
|
20
|
+
"release": "npm run build && npm publish"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@nuxtjs/supabase": ">=1.0.0",
|
|
24
|
+
"@pinia/nuxt": ">=0.5.0",
|
|
25
|
+
"nuxt": ">=3.0.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@nuxt/kit": "^3.13.0",
|
|
29
|
+
"@nuxt/module-builder": "^1.0.2",
|
|
30
|
+
"@nuxt/schema": "^3.13.0",
|
|
31
|
+
"@nuxtjs/supabase": "^1.4.0",
|
|
32
|
+
"@pinia/nuxt": "^0.9.0",
|
|
33
|
+
"defu": "^6.1.4",
|
|
34
|
+
"nuxt": "^3.13.0",
|
|
35
|
+
"vue-tsc": "^3.3.1"
|
|
36
|
+
}
|
|
37
|
+
}
|