cinqcinqdev-seo 0.1.73 → 1.0.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/README.md
CHANGED
|
@@ -4,6 +4,41 @@ A Nuxt 3 admin CMS module with a visual page editor, AI content generation, SEO
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## What's New in v2
|
|
8
|
+
|
|
9
|
+
### Built-in section component library
|
|
10
|
+
|
|
11
|
+
All 19 section types now ship inside the module in **two fully independent design themes**. You no longer need to build section Vue files from scratch — just pick a theme on the Setup page and your sections render immediately.
|
|
12
|
+
|
|
13
|
+
### `theme` prop on `DynamicRenderer`
|
|
14
|
+
|
|
15
|
+
`AdminCmsDynamicRenderer` now accepts a `theme` prop. Pass `settings?.theme` so your front-facing pages render the correct theme. See [Using DynamicRenderer in front-facing pages](#using-dynamicrenderer-in-front-facing-pages).
|
|
16
|
+
|
|
17
|
+
### Theme-aware component resolution
|
|
18
|
+
|
|
19
|
+
Both the visual editor and `DynamicRenderer` follow this priority when resolving a section:
|
|
20
|
+
|
|
21
|
+
1. **Your app's override** — `~/components/sections/HeroSection.vue` (registered as `SectionsHeroSection`) takes precedence over everything
|
|
22
|
+
2. **Module theme** — `sections/swiss-style/HeroSection.vue` → `SectionsSwissStyleHeroSection`
|
|
23
|
+
3. **Bare name fallback**
|
|
24
|
+
|
|
25
|
+
This means you can override any individual section per project without losing the theme for the rest.
|
|
26
|
+
|
|
27
|
+
### Theme selection required on Setup
|
|
28
|
+
|
|
29
|
+
The Setup page now has a **required** theme picker. The Save button is disabled until a theme is chosen. The selected theme is saved to `brand_settings.theme`.
|
|
30
|
+
|
|
31
|
+
### Database: add `theme` column
|
|
32
|
+
|
|
33
|
+
If you are upgrading from v1, run this migration in Supabase:
|
|
34
|
+
|
|
35
|
+
```sql
|
|
36
|
+
ALTER TABLE public.brand_settings
|
|
37
|
+
ADD COLUMN IF NOT EXISTS theme TEXT CHECK (theme IN ('cinqcinq', 'swiss-style'));
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
7
42
|
## Features
|
|
8
43
|
|
|
9
44
|
- Visual 3-panel page editor (structure / live preview / properties)
|
|
@@ -146,7 +181,7 @@ CREATE TABLE IF NOT EXISTS public.users (
|
|
|
146
181
|
created_at timestamptz DEFAULT now()
|
|
147
182
|
);
|
|
148
183
|
|
|
149
|
-
-- Brand settings (AI context, component presets, colors, fonts)
|
|
184
|
+
-- Brand settings (AI context, component presets, colors, fonts, theme)
|
|
150
185
|
CREATE TABLE IF NOT EXISTS public.brand_settings (
|
|
151
186
|
id INT PRIMARY KEY DEFAULT 1,
|
|
152
187
|
brand_name TEXT DEFAULT '',
|
|
@@ -158,6 +193,7 @@ CREATE TABLE IF NOT EXISTS public.brand_settings (
|
|
|
158
193
|
font_body TEXT DEFAULT 'Inter',
|
|
159
194
|
ai_context TEXT DEFAULT '',
|
|
160
195
|
section_presets JSONB DEFAULT '{}'::jsonb,
|
|
196
|
+
theme TEXT CHECK (theme IN ('cinqcinq', 'swiss-style')),
|
|
161
197
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
162
198
|
CONSTRAINT one_row_only CHECK (id = 1)
|
|
163
199
|
);
|
|
@@ -277,6 +313,10 @@ All routes, sidebar links, redirects, and the auth middleware automatically use
|
|
|
277
313
|
|
|
278
314
|
The setup page is the first thing to fill in when starting a new project.
|
|
279
315
|
|
|
316
|
+
### Theme selection (required)
|
|
317
|
+
|
|
318
|
+
A visual picker lets you choose between `cinqcinq` and `swiss-style`. The Save button is disabled until a theme is selected. The choice is saved to `brand_settings.theme` and used by the editor and `DynamicRenderer` to resolve section components.
|
|
319
|
+
|
|
280
320
|
### AI Context
|
|
281
321
|
|
|
282
322
|
A free-text field where you describe the website: industry, target audience, tone of voice, key services. Saved to `brand_settings.ai_context`.
|
|
@@ -326,11 +366,13 @@ If a page has `content_locked = true` in the database, the editor shows only the
|
|
|
326
366
|
|
|
327
367
|
## Section Components
|
|
328
368
|
|
|
329
|
-
The module ships
|
|
369
|
+
The module ships **19 built-in section types** in both themes. No setup beyond choosing a theme is required.
|
|
330
370
|
|
|
331
371
|
| Type | Description |
|
|
332
372
|
|---|---|
|
|
333
373
|
| `HeroSection` | Hero with title, subtitle, badge, CTA, image |
|
|
374
|
+
| `HeroSlider` | Full-screen hero slider |
|
|
375
|
+
| `HeroFan` | Fan-layout hero |
|
|
334
376
|
| `TextVisual` | Text + image (split / stacked / wide) |
|
|
335
377
|
| `ServiceGrid` | Services in grid, list, cards, or minimal layout |
|
|
336
378
|
| `Features` | Feature highlights with icons |
|
|
@@ -339,20 +381,25 @@ The module ships these built-in section types:
|
|
|
339
381
|
| `Pricing` | Pricing plans (cards / minimal / table) |
|
|
340
382
|
| `FAQ` | Accordion FAQ |
|
|
341
383
|
| `ContactForm` | Contact form (split / centered / minimal) |
|
|
384
|
+
| `CallToAction` | CTA banner |
|
|
385
|
+
| `Comparison` | Side-by-side comparison table |
|
|
386
|
+
| `DataTable` | Generic data table |
|
|
387
|
+
| `BlogHero` | Blog article hero |
|
|
388
|
+
| `Newsletter` | Newsletter sign-up |
|
|
389
|
+
| `Quote` | Pull quote / blockquote |
|
|
390
|
+
| `RelatedPages` | Related pages / links grid |
|
|
391
|
+
| `RichText` | Free rich-text block |
|
|
342
392
|
|
|
343
393
|
Each section supports: layout variants, spacing, border radius, animation, background color, text color, and multilingual text fields.
|
|
344
394
|
|
|
345
|
-
###
|
|
395
|
+
### Overriding a built-in section
|
|
346
396
|
|
|
347
|
-
|
|
397
|
+
You can override any individual section for a specific project by placing a Vue file in your app's `components/sections/` directory. It takes priority over the module theme.
|
|
348
398
|
|
|
349
399
|
```
|
|
350
400
|
components/
|
|
351
401
|
sections/
|
|
352
|
-
HeroSection.vue
|
|
353
|
-
TextVisual.vue
|
|
354
|
-
ServiceGrid.vue
|
|
355
|
-
...
|
|
402
|
+
HeroSection.vue ← overrides the theme version for this project only
|
|
356
403
|
```
|
|
357
404
|
|
|
358
405
|
Each component receives its stored props directly via `v-bind`. Handle i18n fields (stored as `{ fr, ar, en }` objects):
|
|
@@ -672,7 +719,6 @@ Pages without a subdirectory (e.g. title `About`, no subdirectory) get a plain s
|
|
|
672
719
|
## What's NOT Included
|
|
673
720
|
|
|
674
721
|
- Login / register pages — you provide these
|
|
675
|
-
- Section component UI — you build the Vue components in `components/sections/`
|
|
676
722
|
- Email / webhook integrations
|
|
677
723
|
- Multi-tenancy / per-user page isolation — implement via Supabase RLS
|
|
678
724
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -78,6 +78,7 @@ const module$1 = defineNuxtModule({
|
|
|
78
78
|
addServerHandler({ route: "/api/admin-cms/ai/seo-audit", handler: resolve("./runtime/server/api/ai/seo-audit.post") });
|
|
79
79
|
nuxt.options.css.push(resolve("./runtime/assets/admin.css"));
|
|
80
80
|
addPlugin(resolve("./runtime/plugins/admin-font.client"));
|
|
81
|
+
addPlugin(resolve("./runtime/plugins/admin-sections"));
|
|
81
82
|
nuxt.hook("tailwindcss:config", (tailwindConfig) => {
|
|
82
83
|
const adminPath = resolve("./runtime/**/*.{vue,ts}");
|
|
83
84
|
if (Array.isArray(tailwindConfig.content)) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineNuxtPlugin } from "#imports";
|
|
2
|
+
const cinqcinq = import.meta.glob("../components/sections/cinqcinq/*.vue", { eager: true });
|
|
3
|
+
const swissStyle = import.meta.glob("../components/sections/swiss-style/*.vue", { eager: true });
|
|
4
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
5
|
+
for (const [path, mod] of Object.entries(cinqcinq)) {
|
|
6
|
+
const name = path.split("/").pop().replace(".vue", "");
|
|
7
|
+
nuxtApp.vueApp.component(`SectionsCinqcinq${name}`, mod.default);
|
|
8
|
+
}
|
|
9
|
+
for (const [path, mod] of Object.entries(swissStyle)) {
|
|
10
|
+
const name = path.split("/").pop().replace(".vue", "");
|
|
11
|
+
nuxtApp.vueApp.component(`SectionsSwissStyle${name}`, mod.default);
|
|
12
|
+
}
|
|
13
|
+
});
|