@deneb-ui/create-template 2.0.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 +65 -0
- package/bin/index.js +127 -0
- package/package.json +37 -0
- package/template/README.md +126 -0
- package/template/_gitignore +13 -0
- package/template/eslint.config.mjs +13 -0
- package/template/fivora-template.json +259 -0
- package/template/next-env.d.ts +6 -0
- package/template/next.config.ts +17 -0
- package/template/package.json +35 -0
- package/template/public/fivora-icon.svg +29 -0
- package/template/public/fivora-logo.png +0 -0
- package/template/public/placeholder.svg +12 -0
- package/template/src/app/about_us/page.tsx +26 -0
- package/template/src/app/contact/page.tsx +79 -0
- package/template/src/app/globals.css +487 -0
- package/template/src/app/layout.tsx +26 -0
- package/template/src/app/page.tsx +244 -0
- package/template/src/app/services/page.tsx +51 -0
- package/template/src/components/SiteChrome.tsx +22 -0
- package/template/src/data/site-data.json +99 -0
- package/template/src/lib/siteDataContext.tsx +55 -0
- package/template/src/lib/utils.ts +14 -0
- package/template/tsconfig.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @fivora/create-template
|
|
2
|
+
|
|
3
|
+
Scaffold a modern, pre-validated **Fivora** template in seconds (Vite-style initializer).
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@fivora/create-template)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
Create a new Fivora template with a single command:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Using npm
|
|
16
|
+
npm create @fivora/template my-store
|
|
17
|
+
|
|
18
|
+
# Or using npx
|
|
19
|
+
npx @fivora/create-template my-store
|
|
20
|
+
|
|
21
|
+
# Or yarn / pnpm
|
|
22
|
+
yarn create @fivora/template my-store
|
|
23
|
+
pnpm create @fivora/template my-store
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Then start developing:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
cd my-store
|
|
30
|
+
npm install
|
|
31
|
+
npm run dev # Start Next.js local development
|
|
32
|
+
npm run lab # Test in Fivora Visual Editing Lab
|
|
33
|
+
npm run validate # Run Fivora preflight compliance checks
|
|
34
|
+
npm run zip # Create upload-ready clean ZIP
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## What is Included?
|
|
40
|
+
|
|
41
|
+
Each generated template comes pre-configured with:
|
|
42
|
+
- ⚡ **Next.js App Router**: Modern React 19 / Next.js with static HTML export support
|
|
43
|
+
- 🎨 **`@fivora/editable-components`**: Pre-configured visual editing primitives and theme palettes
|
|
44
|
+
- 🛠️ **`@fivora/cli`**: Built-in developer tools (`lab`, `validate`, `zip`)
|
|
45
|
+
- 📋 **`fivora-template.json`**: Strict Version 2 template manifest
|
|
46
|
+
- 📦 **`src/data/site-data.json`**: Merchant defaults and editable content structure
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Command Options
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx @fivora/create-template <project-name> [options]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Option | Description |
|
|
57
|
+
| :--- | :--- |
|
|
58
|
+
| `<project-name>` | Target directory name (e.g. `my-fashion-store`) |
|
|
59
|
+
| `--skip-install` | Do not run `npm install` automatically after scaffolding |
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT © [Fivora](https://fivora.com)
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const readline = require('node:readline');
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
let targetDirInput = args[0];
|
|
9
|
+
|
|
10
|
+
function printBanner() {
|
|
11
|
+
console.log(`
|
|
12
|
+
\x1b[36m╔═══════════════════════════════════════════════════════╗
|
|
13
|
+
║ \x1b[1mDENEB TEMPLATE CREATOR\x1b[0m\x1b[36m ║
|
|
14
|
+
║ \x1b[90mScaffold a fast, compliant commerce template\x1b[0m\x1b[36m ║
|
|
15
|
+
║ \x1b[90mCreated by Chamika Gayashan (CEE G)\x1b[0m\x1b[36m ║
|
|
16
|
+
╚═══════════════════════════════════════════════════════╝\x1b[0m
|
|
17
|
+
`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function copyFolderSync(src, dest) {
|
|
21
|
+
if (!fs.existsSync(dest)) {
|
|
22
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
const srcPath = path.join(src, entry.name);
|
|
28
|
+
const destPath = path.join(dest, entry.name);
|
|
29
|
+
|
|
30
|
+
if (entry.isDirectory()) {
|
|
31
|
+
copyFolderSync(srcPath, destPath);
|
|
32
|
+
} else if (entry.isFile()) {
|
|
33
|
+
fs.copyFileSync(srcPath, destPath);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function runScaffolding(targetInput) {
|
|
39
|
+
const targetPath = path.resolve(process.cwd(), targetInput.trim());
|
|
40
|
+
const folderName = path.basename(targetPath);
|
|
41
|
+
const sanitizedPkgName = folderName.toLowerCase().replace(/[^a-z0-9_-]/g, '-');
|
|
42
|
+
const templateDir = path.resolve(__dirname, '..', 'template');
|
|
43
|
+
|
|
44
|
+
if (!fs.existsSync(templateDir)) {
|
|
45
|
+
console.error(`\n\x1b[31mError:\x1b[0m Template directory not found at ${templateDir}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (fs.existsSync(targetPath) && fs.readdirSync(targetPath).length > 0) {
|
|
50
|
+
console.error(`\n\x1b[31mError:\x1b[0m Target folder "${folderName}" already exists and is not empty.\n`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log(`\n\x1b[32mCreating DENEB template in:\x1b[0m ${targetPath}...\n`);
|
|
55
|
+
copyFolderSync(templateDir, targetPath);
|
|
56
|
+
|
|
57
|
+
// Restore .gitignore if preserved as _gitignore
|
|
58
|
+
const gitignorePath = path.join(targetPath, '_gitignore');
|
|
59
|
+
if (fs.existsSync(gitignorePath)) {
|
|
60
|
+
fs.renameSync(gitignorePath, path.join(targetPath, '.gitignore'));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Update package.json name
|
|
64
|
+
const pkgPath = path.join(targetPath, 'package.json');
|
|
65
|
+
if (fs.existsSync(pkgPath)) {
|
|
66
|
+
try {
|
|
67
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
68
|
+
pkg.name = sanitizedPkgName;
|
|
69
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
70
|
+
} catch {
|
|
71
|
+
// ignore
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(`\x1b[32m✔ Template structure created!\x1b[0m`);
|
|
76
|
+
console.log(` \x1b[90m- Pre-configured @deneb-ui/ui (Visual editing & smart template components)\x1b[0m`);
|
|
77
|
+
console.log(` \x1b[90m- Pre-configured @deneb-ui/cli (lab, validate, zip tools)\x1b[0m\n`);
|
|
78
|
+
|
|
79
|
+
const skipInstall = process.argv.includes('--skip-install');
|
|
80
|
+
|
|
81
|
+
if (!skipInstall) {
|
|
82
|
+
console.log(`📦 Installing dependencies with npm (this may take a minute)...`);
|
|
83
|
+
try {
|
|
84
|
+
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
85
|
+
const installRes = require('node:child_process').spawnSync(npmCmd, ['install'], {
|
|
86
|
+
cwd: targetPath,
|
|
87
|
+
stdio: 'inherit',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (installRes.status === 0) {
|
|
91
|
+
console.log(`\n\x1b[32m✔ All dependencies and DENEB UI packages installed successfully!\x1b[0m\n`);
|
|
92
|
+
} else {
|
|
93
|
+
console.log(`\n\x1b[33mNote: npm install exited with code ${installRes.status}. You can run 'npm install' inside the folder manually.\x1b[0m\n`);
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
console.log(`\n\x1b[33mNote: Could not run npm install automatically. Run 'npm install' inside the folder.\x1b[0m\n`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log(`Ready to develop! Run:`);
|
|
101
|
+
console.log(` \x1b[36mcd ${folderName}\x1b[0m`);
|
|
102
|
+
if (skipInstall) {
|
|
103
|
+
console.log(` \x1b[36mnpm install\x1b[0m`);
|
|
104
|
+
}
|
|
105
|
+
console.log(` \x1b[36mnpm run dev\x1b[0m \x1b[90m# Start local Next.js development\x1b[0m`);
|
|
106
|
+
console.log(` \x1b[36mnpm run lab\x1b[0m \x1b[90m# Start Visual Editing Lab\x1b[0m`);
|
|
107
|
+
console.log(` \x1b[36mnpm run validate\x1b[0m \x1b[90m# Verify contract compliance\x1b[0m`);
|
|
108
|
+
console.log(` \x1b[36mnpm run zip\x1b[0m \x1b[90m# Package clean ZIP for 1-click upload\x1b[0m\n`);
|
|
109
|
+
console.log(`Happy coding with DENEB UI! 🚀\n`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
printBanner();
|
|
113
|
+
|
|
114
|
+
if (targetDirInput) {
|
|
115
|
+
runScaffolding(targetDirInput);
|
|
116
|
+
} else {
|
|
117
|
+
const rl = readline.createInterface({
|
|
118
|
+
input: process.stdin,
|
|
119
|
+
output: process.stdout,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
rl.question('Project name (e.g. my-deneb-store): ', (answer) => {
|
|
123
|
+
rl.close();
|
|
124
|
+
const name = answer.trim() || 'deneb-template';
|
|
125
|
+
runScaffolding(name);
|
|
126
|
+
});
|
|
127
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@deneb-ui/create-template",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Scaffold a modern, pre-validated DENEB UI storefront template in seconds. Created by Chamika Gayashan (CEE G).",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-deneb-template": "bin/index.js",
|
|
7
|
+
"create-deneb": "bin/index.js",
|
|
8
|
+
"create-fivora-template": "bin/index.js",
|
|
9
|
+
"create-fivora": "bin/index.js",
|
|
10
|
+
"create-template": "bin/index.js"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"bin",
|
|
17
|
+
"template",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"sync-template": "node ./scripts/sync-template.js",
|
|
22
|
+
"prepublishOnly": "npm run sync-template"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"deneb",
|
|
26
|
+
"deneb-ui",
|
|
27
|
+
"fivora",
|
|
28
|
+
"create",
|
|
29
|
+
"template",
|
|
30
|
+
"scaffold",
|
|
31
|
+
"nextjs",
|
|
32
|
+
"react"
|
|
33
|
+
],
|
|
34
|
+
"author": "Chamika Gayashan (CEE G)",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"dependencies": {}
|
|
37
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Fivora Store Template
|
|
2
|
+
|
|
3
|
+
A high-performance, conversion-engineered e-commerce storefront built with **Next.js (App Router)** and **`@fivora/editable-components`**, ready for **Fivora**.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
### 1. Install Dependencies
|
|
10
|
+
```bash
|
|
11
|
+
npm install
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### 2. Start Local Development
|
|
15
|
+
```bash
|
|
16
|
+
npm run dev
|
|
17
|
+
```
|
|
18
|
+
Open [http://localhost:3000](http://localhost:3000) in your browser to view your store.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Developer Commands
|
|
23
|
+
|
|
24
|
+
This template is pre-configured with official Fivora developer tools:
|
|
25
|
+
|
|
26
|
+
| Command | Description |
|
|
27
|
+
| :--- | :--- |
|
|
28
|
+
| **`npm run dev`** | Starts the Next.js local development server with hot reload |
|
|
29
|
+
| **`npm run lab`** | Launches the **Fivora Visual Editing Lab** to test live merchant editing |
|
|
30
|
+
| **`npm run validate`** | Runs strict preflight compliance checks against Fivora contract rules |
|
|
31
|
+
| **`npm run zip`** | Packages your template into a clean, upload-ready `.zip` archive |
|
|
32
|
+
| **`npm run build`** | Generates the static production export in the `out/` directory |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Project Structure
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
├── fivora-template.json # Fivora template manifest (Version 2 strict contract)
|
|
40
|
+
├── package.json # Dependencies and Fivora scripts
|
|
41
|
+
├── next.config.ts # Next.js configuration (static export enabled)
|
|
42
|
+
├── src/
|
|
43
|
+
│ ├── app/ # Next.js App Router pages (Home, About, Services, Contact)
|
|
44
|
+
│ │ ├── page.tsx # Home page
|
|
45
|
+
│ │ ├── about_us/ # About Us route
|
|
46
|
+
│ │ ├── services/ # Services route
|
|
47
|
+
│ │ └── contact/ # Contact form (integrated with Fivora endpoint)
|
|
48
|
+
│ ├── components/ # Reusable UI components & layouts
|
|
49
|
+
│ ├── data/
|
|
50
|
+
│ │ └── site-data.json # Merchant defaults, theme colors, navigation, & site content
|
|
51
|
+
│ └── lib/ # Site data context & live preview bridge
|
|
52
|
+
└── public/ # Static assets & logos
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## How Visual Editing Works
|
|
58
|
+
|
|
59
|
+
Fivora allows merchants to click and visually edit any text, image, or product on their website.
|
|
60
|
+
|
|
61
|
+
To make an element visually editable, use **`data-preview-field-path`** pointing to its key in `src/data/site-data.json`:
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { EditableHeading, EditableText } from '@fivora/editable-components';
|
|
65
|
+
|
|
66
|
+
// In your component:
|
|
67
|
+
<EditableHeading
|
|
68
|
+
level={1}
|
|
69
|
+
data-preview-field-path="home.heroTitle"
|
|
70
|
+
defaultValue={content.home.heroTitle}
|
|
71
|
+
/>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
When you run **`npm run lab`**, you can click directly on any element with a `data-preview-field-path` to test editing in real-time.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## How to Customize
|
|
79
|
+
|
|
80
|
+
### 1. Change Theme & Colors
|
|
81
|
+
Edit `src/data/site-data.json` under `template.structure.theme`:
|
|
82
|
+
```json
|
|
83
|
+
"theme": {
|
|
84
|
+
"primaryColor": "#016a7e",
|
|
85
|
+
"secondaryColor": "#0a1931",
|
|
86
|
+
"accentColor": "#00adb5",
|
|
87
|
+
"backgroundColor": "#ffffff",
|
|
88
|
+
"textColor": "#0f172a",
|
|
89
|
+
"headingFont": "Inter",
|
|
90
|
+
"bodyFont": "Inter"
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 2. Add New Pages or Routes
|
|
95
|
+
1. Create your page route in `src/app/your-page/page.tsx`.
|
|
96
|
+
2. Declare the page in `fivora-template.json` under `"pages"`:
|
|
97
|
+
```json
|
|
98
|
+
{ "id": "your_page", "label": "Your Page", "route": "/your-page" }
|
|
99
|
+
```
|
|
100
|
+
3. Add the page content to `src/data/site-data.json`.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Packaging for Fivora
|
|
105
|
+
|
|
106
|
+
When your template design is ready:
|
|
107
|
+
|
|
108
|
+
1. **Validate Compliance:**
|
|
109
|
+
```bash
|
|
110
|
+
npm run validate
|
|
111
|
+
```
|
|
112
|
+
Ensures all preview markers, empty states, and required routes pass the Fivora preflight contract.
|
|
113
|
+
|
|
114
|
+
2. **Generate Upload ZIP:**
|
|
115
|
+
```bash
|
|
116
|
+
npm run zip
|
|
117
|
+
```
|
|
118
|
+
This generates a clean `fivora-template.zip` in your root folder with all cache, build, and `node_modules` files automatically excluded.
|
|
119
|
+
|
|
120
|
+
3. **Upload:** Go to your **Fivora Merchant/Developer Portal** and upload `fivora-template.zip`.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT © [Fivora](https://fivora.com)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { dirname } from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { FlatCompat } from '@eslint/eslintrc';
|
|
4
|
+
|
|
5
|
+
const currentDirectory = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const compat = new FlatCompat({ baseDirectory: currentDirectory });
|
|
7
|
+
|
|
8
|
+
const eslintConfig = [
|
|
9
|
+
...compat.extends('next/core-web-vitals'),
|
|
10
|
+
{ rules: { '@next/next/no-img-element': 'off' } },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export default eslintConfig;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
{
|
|
2
|
+
"framework": "nextjs-static-export",
|
|
3
|
+
"version": 2,
|
|
4
|
+
"visualEditing": {
|
|
5
|
+
"contractVersion": 1,
|
|
6
|
+
"mode": "strict",
|
|
7
|
+
"controlOnlyPaths": ["services[*].id"]
|
|
8
|
+
},
|
|
9
|
+
"siteDataFile": "src/data/site-data.json",
|
|
10
|
+
"outputDirectory": "out",
|
|
11
|
+
"installCommand": "npm install",
|
|
12
|
+
"buildCommand": "npm run build",
|
|
13
|
+
"basePathEnvVar": "NEXT_PUBLIC_SITE_BASE_PATH",
|
|
14
|
+
"pages": [
|
|
15
|
+
{ "id": "home", "label": "Home", "route": "/", "required": true },
|
|
16
|
+
{ "id": "about_us", "label": "About Us", "route": "/about_us" },
|
|
17
|
+
{ "id": "services", "label": "Services", "route": "/services" },
|
|
18
|
+
{
|
|
19
|
+
"id": "contact",
|
|
20
|
+
"label": "Contact",
|
|
21
|
+
"route": "/contact",
|
|
22
|
+
"required": true
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"editorSchema": {
|
|
26
|
+
"version": 1,
|
|
27
|
+
"sections": [
|
|
28
|
+
{
|
|
29
|
+
"id": "common",
|
|
30
|
+
"path": "common",
|
|
31
|
+
"type": "object",
|
|
32
|
+
"label": "Shared Website Content",
|
|
33
|
+
"fields": [
|
|
34
|
+
{
|
|
35
|
+
"key": "websiteTitle",
|
|
36
|
+
"type": "text",
|
|
37
|
+
"label": "Website title",
|
|
38
|
+
"required": true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"key": "shortDescription",
|
|
42
|
+
"type": "textarea",
|
|
43
|
+
"label": "Short description"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"key": "logoUrl",
|
|
47
|
+
"type": "image",
|
|
48
|
+
"label": "Website logo",
|
|
49
|
+
"recommendedWidth": 512,
|
|
50
|
+
"recommendedHeight": 512
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"key": "headerCtaLabel",
|
|
54
|
+
"type": "text",
|
|
55
|
+
"label": "Header contact button"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"key": "navLabels",
|
|
59
|
+
"type": "object",
|
|
60
|
+
"label": "Navigation labels",
|
|
61
|
+
"fields": [
|
|
62
|
+
{ "key": "home", "type": "text", "label": "Home label" },
|
|
63
|
+
{ "key": "about_us", "type": "text", "label": "About label" },
|
|
64
|
+
{ "key": "services", "type": "text", "label": "Services label" },
|
|
65
|
+
{ "key": "contact", "type": "text", "label": "Contact label" }
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
{ "key": "footerHeading", "type": "text", "label": "Footer heading" },
|
|
69
|
+
{ "key": "copyright", "type": "text", "label": "Copyright" }
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": "home",
|
|
74
|
+
"path": "home",
|
|
75
|
+
"pageKey": "home",
|
|
76
|
+
"type": "object",
|
|
77
|
+
"label": "Home Page",
|
|
78
|
+
"fields": [
|
|
79
|
+
{ "key": "heroEyebrow", "type": "text", "label": "Hero eyebrow" },
|
|
80
|
+
{
|
|
81
|
+
"key": "heroTitle",
|
|
82
|
+
"type": "text",
|
|
83
|
+
"label": "Hero title",
|
|
84
|
+
"required": true
|
|
85
|
+
},
|
|
86
|
+
{ "key": "heroSummary", "type": "textarea", "label": "Hero summary" },
|
|
87
|
+
{
|
|
88
|
+
"key": "bannerImageUrl",
|
|
89
|
+
"type": "image",
|
|
90
|
+
"label": "Hero image",
|
|
91
|
+
"recommendedWidth": 1600,
|
|
92
|
+
"recommendedHeight": 900
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"key": "primaryCtaLabel",
|
|
96
|
+
"type": "text",
|
|
97
|
+
"label": "Contact button"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"key": "secondaryCtaLabel",
|
|
101
|
+
"type": "text",
|
|
102
|
+
"label": "About button"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"key": "introTitle",
|
|
106
|
+
"type": "text",
|
|
107
|
+
"label": "Introduction title"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"key": "introBody",
|
|
111
|
+
"type": "textarea",
|
|
112
|
+
"label": "Introduction body"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"key": "featuresHeading",
|
|
116
|
+
"type": "text",
|
|
117
|
+
"label": "Features heading"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"key": "features",
|
|
121
|
+
"type": "list",
|
|
122
|
+
"label": "Features",
|
|
123
|
+
"minItems": 0,
|
|
124
|
+
"maxItems": 8,
|
|
125
|
+
"fields": [
|
|
126
|
+
{
|
|
127
|
+
"key": "title",
|
|
128
|
+
"type": "text",
|
|
129
|
+
"label": "Title",
|
|
130
|
+
"required": true
|
|
131
|
+
},
|
|
132
|
+
{ "key": "body", "type": "textarea", "label": "Description" }
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "about",
|
|
139
|
+
"path": "about",
|
|
140
|
+
"pageKey": "about_us",
|
|
141
|
+
"type": "object",
|
|
142
|
+
"label": "About Page",
|
|
143
|
+
"fields": [
|
|
144
|
+
{
|
|
145
|
+
"key": "heading",
|
|
146
|
+
"type": "text",
|
|
147
|
+
"label": "Heading",
|
|
148
|
+
"required": true
|
|
149
|
+
},
|
|
150
|
+
{ "key": "body", "type": "textarea", "label": "About text" },
|
|
151
|
+
{
|
|
152
|
+
"key": "imageUrl",
|
|
153
|
+
"type": "image",
|
|
154
|
+
"label": "About image",
|
|
155
|
+
"recommendedWidth": 1200,
|
|
156
|
+
"recommendedHeight": 900
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"id": "servicesPage",
|
|
162
|
+
"path": "servicesPage",
|
|
163
|
+
"pageKey": "services",
|
|
164
|
+
"type": "object",
|
|
165
|
+
"label": "Services Page",
|
|
166
|
+
"fields": [
|
|
167
|
+
{
|
|
168
|
+
"key": "heading",
|
|
169
|
+
"type": "text",
|
|
170
|
+
"label": "Heading",
|
|
171
|
+
"required": true
|
|
172
|
+
},
|
|
173
|
+
{ "key": "intro", "type": "textarea", "label": "Introduction" }
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"id": "services",
|
|
178
|
+
"path": "services",
|
|
179
|
+
"pageKey": "services",
|
|
180
|
+
"type": "list",
|
|
181
|
+
"label": "Services",
|
|
182
|
+
"minItems": 0,
|
|
183
|
+
"maxItems": 12,
|
|
184
|
+
"fields": [
|
|
185
|
+
{ "key": "id", "type": "text", "label": "System ID" },
|
|
186
|
+
{ "key": "name", "type": "text", "label": "Name", "required": true },
|
|
187
|
+
{ "key": "description", "type": "textarea", "label": "Description" },
|
|
188
|
+
{
|
|
189
|
+
"key": "imageUrl",
|
|
190
|
+
"type": "image",
|
|
191
|
+
"label": "Image",
|
|
192
|
+
"recommendedWidth": 800,
|
|
193
|
+
"recommendedHeight": 600
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"key": "features",
|
|
197
|
+
"type": "list",
|
|
198
|
+
"label": "Included features",
|
|
199
|
+
"itemField": { "type": "text", "label": "Feature" }
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"id": "contact",
|
|
205
|
+
"path": "contact",
|
|
206
|
+
"pageKey": "contact",
|
|
207
|
+
"type": "object",
|
|
208
|
+
"label": "Contact Page",
|
|
209
|
+
"fields": [
|
|
210
|
+
{
|
|
211
|
+
"key": "heading",
|
|
212
|
+
"type": "text",
|
|
213
|
+
"label": "Heading",
|
|
214
|
+
"required": true
|
|
215
|
+
},
|
|
216
|
+
{ "key": "intro", "type": "textarea", "label": "Introduction" },
|
|
217
|
+
{ "key": "phone", "type": "tel", "label": "Phone" },
|
|
218
|
+
{ "key": "email", "type": "email", "label": "Email" },
|
|
219
|
+
{ "key": "address", "type": "textarea", "label": "Address" },
|
|
220
|
+
{ "key": "formTitle", "type": "text", "label": "Form heading" },
|
|
221
|
+
{ "key": "submitLabel", "type": "text", "label": "Submit button" }
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
},
|
|
226
|
+
"themeSchema": {
|
|
227
|
+
"version": 1,
|
|
228
|
+
"tokens": [
|
|
229
|
+
"colors.primary",
|
|
230
|
+
"colors.secondary",
|
|
231
|
+
"colors.accent",
|
|
232
|
+
"colors.background",
|
|
233
|
+
"colors.text",
|
|
234
|
+
"typography.headingFont",
|
|
235
|
+
"typography.bodyFont",
|
|
236
|
+
"typography.baseSize",
|
|
237
|
+
"spacing.heroMinHeight",
|
|
238
|
+
"spacing.sectionPadding"
|
|
239
|
+
],
|
|
240
|
+
"defaults": {
|
|
241
|
+
"colors": {
|
|
242
|
+
"primary": "#2563eb",
|
|
243
|
+
"secondary": "#0f172a",
|
|
244
|
+
"accent": "#14b8a6",
|
|
245
|
+
"background": "#ffffff",
|
|
246
|
+
"text": "#0f172a"
|
|
247
|
+
},
|
|
248
|
+
"typography": {
|
|
249
|
+
"headingFont": "Inter",
|
|
250
|
+
"bodyFont": "Inter",
|
|
251
|
+
"baseSize": "16px"
|
|
252
|
+
},
|
|
253
|
+
"spacing": {
|
|
254
|
+
"heroMinHeight": "72vh",
|
|
255
|
+
"sectionPadding": "5rem"
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="next" />
|
|
2
|
+
/// <reference types="next/image-types/global" />
|
|
3
|
+
/// <reference path="./.next/types/routes.d.ts" />
|
|
4
|
+
|
|
5
|
+
// NOTE: This file should not be edited
|
|
6
|
+
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { NextConfig } from 'next';
|
|
2
|
+
|
|
3
|
+
const basePath = (process.env.NEXT_PUBLIC_SITE_BASE_PATH ?? '').replace(
|
|
4
|
+
/\/$/,
|
|
5
|
+
'',
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
const nextConfig: NextConfig = {
|
|
9
|
+
output: 'export',
|
|
10
|
+
trailingSlash: true,
|
|
11
|
+
outputFileTracingRoot: process.cwd(),
|
|
12
|
+
basePath,
|
|
13
|
+
assetPrefix: basePath || undefined,
|
|
14
|
+
images: { unoptimized: true },
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default nextConfig;
|