create-rayso 1.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 +78 -0
- package/bin/index.js +115 -0
- package/package.json +30 -0
- package/template/frontend/(core)/sanity/lib/client.ts +8 -0
- package/template/frontend/(core)/sanity/lib/image.ts +8 -0
- package/template/frontend/.env.example +5 -0
- package/template/frontend/app/globals.css +80 -0
- package/template/frontend/app/layout.tsx +21 -0
- package/template/frontend/app/page.tsx +7 -0
- package/template/frontend/next.config.ts +15 -0
- package/template/frontend/package.json +37 -0
- package/template/frontend/postcss.config.mjs +6 -0
- package/template/frontend/tsconfig.json +21 -0
- package/template/frontend/types/index.ts +22 -0
- package/template/package.json +19 -0
- package/template/studio/.env.example +2 -0
- package/template/studio/package.json +23 -0
- package/template/studio/sanity.config.ts +12 -0
- package/template/studio/schemaTypes/index.ts +6 -0
- package/template/studio/tsconfig.json +18 -0
- package/template/zip.sh +12 -0
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# create-rayso
|
|
2
|
+
|
|
3
|
+
Official CLI scaffolder for RAYSO.STUDIO templates.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm create rayso@latest
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or with a project name:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm create rayso@latest my-template
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## What it does
|
|
18
|
+
|
|
19
|
+
1. Asks for project name, description, and author
|
|
20
|
+
2. Scaffolds the full folder structure
|
|
21
|
+
3. Injects your project name into all `package.json` files
|
|
22
|
+
4. Copies `.env.example` → `.env.local` in both frontend and studio
|
|
23
|
+
5. Runs `npm install` automatically
|
|
24
|
+
|
|
25
|
+
## After scaffolding
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
cd my-template
|
|
29
|
+
|
|
30
|
+
# Fill in your keys
|
|
31
|
+
nano frontend/.env.local
|
|
32
|
+
|
|
33
|
+
# Start both servers
|
|
34
|
+
npm run dev
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- Frontend → http://localhost:3000
|
|
38
|
+
- Studio → http://localhost:3333
|
|
39
|
+
|
|
40
|
+
## Publishing to npm
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm login
|
|
44
|
+
npm publish --access public
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Template structure
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
my-template/
|
|
51
|
+
├── frontend/
|
|
52
|
+
│ ├── app/
|
|
53
|
+
│ │ ├── globals.css ← Full Tailwind v4 design system
|
|
54
|
+
│ │ ├── layout.tsx
|
|
55
|
+
│ │ └── page.tsx
|
|
56
|
+
│ ├── components/
|
|
57
|
+
│ │ ├── sections/ ← Page sections
|
|
58
|
+
│ │ ├── ui/ ← Reusable UI components
|
|
59
|
+
│ │ ├── icons/ ← Icon components
|
|
60
|
+
│ │ ├── animation/ ← GSAP / AnimatedHeading etc
|
|
61
|
+
│ │ └── layout/ ← Navbar, Footer
|
|
62
|
+
│ ├── (core)/
|
|
63
|
+
│ │ ├── fetch/ ← Data fetching functions
|
|
64
|
+
│ │ ├── sanity/lib/ ← Sanity client + image builder
|
|
65
|
+
│ │ └── lib/ ← Email, utilities
|
|
66
|
+
│ ├── types/index.ts
|
|
67
|
+
│ ├── next.config.ts
|
|
68
|
+
│ ├── postcss.config.mjs
|
|
69
|
+
│ └── tsconfig.json
|
|
70
|
+
│
|
|
71
|
+
├── studio/
|
|
72
|
+
│ ├── schemaTypes/index.ts ← Register your schemas here
|
|
73
|
+
│ ├── sanity.config.ts
|
|
74
|
+
│ └── tsconfig.json
|
|
75
|
+
│
|
|
76
|
+
├── .gitignore
|
|
77
|
+
├── zip.sh ← npm run zip → clean dist zip
|
|
78
|
+
└── package.json
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const TEMPLATE_DIR = path.join(__dirname, '..', 'template');
|
|
13
|
+
|
|
14
|
+
// ── Banner ────────────────────────────────────────────────────────────────────
|
|
15
|
+
console.log('\n' + chalk.bold.white('RAYSO.STUDIO') + chalk.gray(' — Template Scaffolder'));
|
|
16
|
+
console.log(chalk.gray('─'.repeat(42)) + '\n');
|
|
17
|
+
|
|
18
|
+
// ── Questions ─────────────────────────────────────────────────────────────────
|
|
19
|
+
const answers = await inquirer.prompt([
|
|
20
|
+
{
|
|
21
|
+
type: 'input',
|
|
22
|
+
name: 'projectName',
|
|
23
|
+
message: 'Project name:',
|
|
24
|
+
default: 'my-template',
|
|
25
|
+
validate: (v) =>
|
|
26
|
+
/^[a-z0-9-_]+$/.test(v) || 'Use lowercase letters, numbers, hyphens only',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'input',
|
|
30
|
+
name: 'description',
|
|
31
|
+
message: 'Description:',
|
|
32
|
+
default: 'A premium Next.js template by RAYSO.STUDIO',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
type: 'input',
|
|
36
|
+
name: 'author',
|
|
37
|
+
message: 'Author:',
|
|
38
|
+
default: 'RAYSO.STUDIO',
|
|
39
|
+
},
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
const { projectName, description, author } = answers;
|
|
43
|
+
const targetDir = path.resolve(process.cwd(), projectName);
|
|
44
|
+
|
|
45
|
+
// ── Check target dir ──────────────────────────────────────────────────────────
|
|
46
|
+
if (fs.existsSync(targetDir)) {
|
|
47
|
+
console.log(chalk.red(`\n✖ Directory "${projectName}" already exists.\n`));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── Copy template ─────────────────────────────────────────────────────────────
|
|
52
|
+
const copySpinner = ora('Scaffolding project...').start();
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
await fs.copy(TEMPLATE_DIR, targetDir);
|
|
56
|
+
|
|
57
|
+
// Inject project name + description into root package.json
|
|
58
|
+
const rootPkg = await fs.readJson(path.join(targetDir, 'package.json'));
|
|
59
|
+
rootPkg.name = projectName;
|
|
60
|
+
rootPkg.description = description;
|
|
61
|
+
rootPkg.author = author;
|
|
62
|
+
await fs.writeJson(path.join(targetDir, 'package.json'), rootPkg, { spaces: 2 });
|
|
63
|
+
|
|
64
|
+
// Inject into frontend/package.json
|
|
65
|
+
const frontendPkg = await fs.readJson(path.join(targetDir, 'frontend', 'package.json'));
|
|
66
|
+
frontendPkg.name = `${projectName}-frontend`;
|
|
67
|
+
frontendPkg.description = description;
|
|
68
|
+
await fs.writeJson(path.join(targetDir, 'frontend', 'package.json'), frontendPkg, { spaces: 2 });
|
|
69
|
+
|
|
70
|
+
// Inject into studio/package.json
|
|
71
|
+
const studioPkg = await fs.readJson(path.join(targetDir, 'studio', 'package.json'));
|
|
72
|
+
studioPkg.name = `${projectName}-studio`;
|
|
73
|
+
await fs.writeJson(path.join(targetDir, 'studio', 'package.json'), studioPkg, { spaces: 2 });
|
|
74
|
+
|
|
75
|
+
// Create .env.local files from examples
|
|
76
|
+
const frontendEnvExample = path.join(targetDir, 'frontend', '.env.example');
|
|
77
|
+
const frontendEnvLocal = path.join(targetDir, 'frontend', '.env.local');
|
|
78
|
+
if (fs.existsSync(frontendEnvExample) && !fs.existsSync(frontendEnvLocal)) {
|
|
79
|
+
await fs.copy(frontendEnvExample, frontendEnvLocal);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const studioEnvExample = path.join(targetDir, 'studio', '.env.example');
|
|
83
|
+
const studioEnvLocal = path.join(targetDir, 'studio', '.env.local');
|
|
84
|
+
if (fs.existsSync(studioEnvExample) && !fs.existsSync(studioEnvLocal)) {
|
|
85
|
+
await fs.copy(studioEnvExample, studioEnvLocal);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
copySpinner.succeed('Project scaffolded');
|
|
89
|
+
} catch (err) {
|
|
90
|
+
copySpinner.fail('Scaffolding failed');
|
|
91
|
+
console.error(err);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── Install dependencies ──────────────────────────────────────────────────────
|
|
96
|
+
const installSpinner = ora('Installing dependencies...').start();
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
await execa('npm', ['install'], { cwd: targetDir, stdio: 'pipe' });
|
|
100
|
+
installSpinner.succeed('Dependencies installed');
|
|
101
|
+
} catch (err) {
|
|
102
|
+
installSpinner.fail('npm install failed — run it manually');
|
|
103
|
+
console.warn(chalk.yellow('\n cd ' + projectName + ' && npm install\n'));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── Done ──────────────────────────────────────────────────────────────────────
|
|
107
|
+
console.log('\n' + chalk.green('✔ Ready!') + '\n');
|
|
108
|
+
console.log(chalk.white(' Next steps:\n'));
|
|
109
|
+
console.log(chalk.gray(' 1.') + ' cd ' + chalk.cyan(projectName));
|
|
110
|
+
console.log(chalk.gray(' 2.') + ' Fill in ' + chalk.cyan('frontend/.env.local') + ' with your Sanity + Resend keys');
|
|
111
|
+
console.log(chalk.gray(' 3.') + ' ' + chalk.cyan('npm run dev'));
|
|
112
|
+
console.log('\n' + chalk.gray(' Frontend →') + ' http://localhost:3000');
|
|
113
|
+
console.log(chalk.gray(' Studio →') + ' http://localhost:3333\n');
|
|
114
|
+
console.log(chalk.gray('─'.repeat(42)));
|
|
115
|
+
console.log(chalk.gray(' Built by ') + chalk.white('RAYSO.STUDIO') + '\n');
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-rayso",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI scaffolder for RAYSO.STUDIO Next.js + Sanity + Tailwind v4 templates",
|
|
5
|
+
"main": "bin/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-rayso": "bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"create",
|
|
11
|
+
"nextjs",
|
|
12
|
+
"sanity",
|
|
13
|
+
"tailwind",
|
|
14
|
+
"template",
|
|
15
|
+
"rayso"
|
|
16
|
+
],
|
|
17
|
+
"author": "RAYSO.STUDIO",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"chalk": "^5.3.0",
|
|
21
|
+
"execa": "^8.0.1",
|
|
22
|
+
"fs-extra": "^11.2.0",
|
|
23
|
+
"inquirer": "^9.2.15",
|
|
24
|
+
"ora": "^8.0.1"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"type": "module"
|
|
30
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
@import 'tailwindcss';
|
|
2
|
+
|
|
3
|
+
@theme {
|
|
4
|
+
--font-display: "Playfair Display", serif;
|
|
5
|
+
--font-body: "Inter", sans-serif;
|
|
6
|
+
--color-primary: #99023C;
|
|
7
|
+
--color-primary-light: #C9034F;
|
|
8
|
+
--color-primary-dark: #49031E;
|
|
9
|
+
--color-cream: #F4E9EE;
|
|
10
|
+
--color-off-white: #F2F2F2;
|
|
11
|
+
--color-neutral-50: #ffffff;
|
|
12
|
+
--color-neutral-100: #F2F2F2;
|
|
13
|
+
--color-neutral-200: #F4E9EE;
|
|
14
|
+
--color-neutral-300: #D6C2CA;
|
|
15
|
+
--color-neutral-400: #C1A4B0;
|
|
16
|
+
--color-neutral-500: #AD8595;
|
|
17
|
+
--color-neutral-600: #98677B;
|
|
18
|
+
--color-neutral-700: #7A5262;
|
|
19
|
+
--color-neutral-800: #5B3E4A;
|
|
20
|
+
--color-neutral-900: #3D2931;
|
|
21
|
+
--color-neutral-950: #49031E;
|
|
22
|
+
--color-success: #2d7a4f;
|
|
23
|
+
--color-warning: #c8761a;
|
|
24
|
+
--color-error: #b33030;
|
|
25
|
+
--spacing-navbar: 90px;
|
|
26
|
+
--spacing-18: 4.5rem;
|
|
27
|
+
--spacing-34: 8.5rem;
|
|
28
|
+
--shadow-card: 0 2px 8px rgba(26,46,68,.06), 0 8px 32px rgba(26,46,68,.08);
|
|
29
|
+
--shadow-hover: 0 8px 32px rgba(26,46,68,.14), 0 2px 8px rgba(26,46,68,.08);
|
|
30
|
+
--ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
|
|
31
|
+
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
32
|
+
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@layer base {
|
|
36
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
37
|
+
html { scroll-behavior: smooth; -webkit-font-smoothing: antialiased; }
|
|
38
|
+
body { font-family: var(--font-body); background-color: var(--color-cream); color: var(--color-neutral-700); }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@utility container-site { @apply w-full mx-auto px-6 md:px-0; max-width: 1280px; }
|
|
42
|
+
@utility heading-1 { font-family: var(--font-display); font-size: clamp(2.5rem,6vw,3.5rem); font-weight: 500; line-height: 1; letter-spacing: -0.03em; text-transform: uppercase; padding: 10px 0; }
|
|
43
|
+
@utility heading-2 { font-family: var(--font-display); font-size: 2rem; font-weight: 500; line-height: 1.4; letter-spacing: -0.03em; text-transform: uppercase; padding: 20px 0; color: var(--color-neutral-950); }
|
|
44
|
+
@utility heading-3 { font-family: var(--font-display); font-size: clamp(1.5rem,3vw,1.875rem); font-weight: 500; line-height: 1.15; letter-spacing: -0.02em; color: var(--color-neutral-950); }
|
|
45
|
+
@utility heading-4 { font-family: var(--font-display); font-size: 1.5rem; font-weight: 500; line-height: 1.2; padding: 10px 0; color: var(--color-neutral-950); }
|
|
46
|
+
@utility heading-5 { font-family: var(--font-display); font-size: 1.25rem; font-weight: 400; line-height: 1.3; color: var(--color-neutral-950); }
|
|
47
|
+
@utility eyebrow { @apply text-xs font-semibold tracking-[0.2em] uppercase; color: var(--color-primary-light); }
|
|
48
|
+
@utility btn { @apply inline-flex rounded-lg mt-4 items-center justify-center gap-2 font-body text-xs font-semibold tracking-wider uppercase border-2 border-transparent transition-all duration-250 cursor-pointer select-none whitespace-nowrap outline-hidden no-underline px-8 py-3; }
|
|
49
|
+
@utility btn-lg { @apply text-sm px-10 py-4; }
|
|
50
|
+
@utility btn-sm { @apply text-[0.65rem] px-5 py-2; }
|
|
51
|
+
@utility btn-primary { @apply bg-(--color-primary-light) text-white; &:hover { @apply -translate-y-0.5; background: var(--color-primary); } }
|
|
52
|
+
@utility btn-static { @apply bg-(--color-primary-light) text-white; &:hover { @apply bg-(--color-primary); } }
|
|
53
|
+
@utility btn-white { @apply bg-white text-(--color-primary) border-white; &:hover { @apply -translate-y-0.5 bg-(--color-off-white); } }
|
|
54
|
+
@utility btn-secondary-outlined { @apply bg-transparent text-neutral-50 border border-neutral-50; &:hover { @apply -translate-y-0.5 bg-(--color-primary) text-white; } }
|
|
55
|
+
@utility input { @apply w-full border border-neutral-300 rounded-lg bg-white px-4 py-3 text-base text-neutral-900 font-body outline-hidden transition-all duration-250 placeholder:text-neutral-400; &:focus { @apply border-(--color-primary); } }
|
|
56
|
+
@utility label-text { @apply block text-sm font-medium text-neutral-950 mb-1.5; }
|
|
57
|
+
@utility section { @apply py-20 md:py-34; }
|
|
58
|
+
@utility section-dark { @apply py-20 md:py-34 bg-(--color-primary); }
|
|
59
|
+
@utility section-cream { @apply py-20 md:py-34 bg-(--color-cream); }
|
|
60
|
+
@utility page-header { @apply bg-(--color-primary) relative overflow-hidden; padding-top: calc(var(--spacing-navbar) + 4rem); padding-bottom: 4rem; }
|
|
61
|
+
@utility property-card { @apply block overflow-hidden no-underline text-neutral-900 bg-white transition-all duration-400 cursor-pointer; &:hover { @apply -translate-y-1.5; } &:hover .card-img { @apply scale-105; } }
|
|
62
|
+
@utility card-img { @apply absolute inset-0 w-full h-full object-cover transition-transform duration-600; }
|
|
63
|
+
@utility badge { @apply inline-flex items-center text-[0.65rem] font-semibold tracking-[0.15em] uppercase px-3 py-2; }
|
|
64
|
+
@utility overlay-dark { background: linear-gradient(to top, rgba(15,29,43,.85) 0%, rgba(15,29,43,.4) 50%, transparent 100%); }
|
|
65
|
+
@utility font-display { font-family: var(--font-display); }
|
|
66
|
+
@utility font-body { font-family: var(--font-body); }
|
|
67
|
+
@utility animate-fade-in-up { animation: fadeInUp 600ms var(--ease-out) forwards; }
|
|
68
|
+
@utility animate-fade-in { animation: fadeIn 600ms var(--ease-out) forwards; }
|
|
69
|
+
@utility delay-100 { animation-delay: 100ms; }
|
|
70
|
+
@utility delay-200 { animation-delay: 200ms; }
|
|
71
|
+
@utility delay-300 { animation-delay: 300ms; }
|
|
72
|
+
|
|
73
|
+
@keyframes fadeInUp { from { opacity:0; transform:translateY(24px); } to { opacity:1; transform:translateY(0); } }
|
|
74
|
+
@keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
|
75
|
+
@keyframes marquee { from { transform:translateX(0); } to { transform:translateX(-50%); } }
|
|
76
|
+
|
|
77
|
+
.embla { overflow:hidden; }
|
|
78
|
+
.embla__viewport { overflow:hidden; }
|
|
79
|
+
.embla__container { display:flex; }
|
|
80
|
+
.embla__slide { flex:0 0 100%; min-width:0; }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { Inter, Playfair_Display } from "next/font/google";
|
|
3
|
+
import "./globals.css";
|
|
4
|
+
|
|
5
|
+
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
|
|
6
|
+
const playfair = Playfair_Display({ subsets: ["latin"], variable: "--font-playfair" });
|
|
7
|
+
|
|
8
|
+
export const metadata: Metadata = {
|
|
9
|
+
title: "Template — RAYSO.STUDIO",
|
|
10
|
+
description: "A premium Next.js template",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
14
|
+
return (
|
|
15
|
+
<html lang="en">
|
|
16
|
+
<body className={`${inter.variable} ${playfair.variable}`}>
|
|
17
|
+
{children}
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { NextConfig } from "next";
|
|
2
|
+
|
|
3
|
+
const nextConfig: NextConfig = {
|
|
4
|
+
reactStrictMode: false,
|
|
5
|
+
images: {
|
|
6
|
+
remotePatterns: [
|
|
7
|
+
{ protocol: "https", hostname: "cdn.sanity.io" },
|
|
8
|
+
{ protocol: "https", hostname: "assets.lummi.ai" },
|
|
9
|
+
{ protocol: "https", hostname: "res.cloudinary.com" },
|
|
10
|
+
{ protocol: "https", hostname: "images.unsplash.com" },
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default nextConfig;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rayso-template-frontend",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"lint": "eslint ."
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@portabletext/react": "^6.0.3",
|
|
13
|
+
"@sanity/image-url": "^2.1.1",
|
|
14
|
+
"embla-carousel-autoplay": "^8.6.0",
|
|
15
|
+
"embla-carousel-react": "^8.6.0",
|
|
16
|
+
"gsap": "^3.15.0",
|
|
17
|
+
"lenis": "^1.3.23",
|
|
18
|
+
"lucide-react": "^1.11.0",
|
|
19
|
+
"next": "16.2.4",
|
|
20
|
+
"next-sanity": "^12.3.1",
|
|
21
|
+
"react": "19.2.5",
|
|
22
|
+
"react-dom": "19.2.5",
|
|
23
|
+
"react-hook-form": "^7.74.0",
|
|
24
|
+
"resend": "^6.12.2"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@tailwindcss/postcss": "^4.2.4",
|
|
28
|
+
"@types/node": "^20",
|
|
29
|
+
"@types/react": "19.2.14",
|
|
30
|
+
"@types/react-dom": "19.2.3",
|
|
31
|
+
"eslint": "^9",
|
|
32
|
+
"eslint-config-next": "16.2.4",
|
|
33
|
+
"postcss": "^8",
|
|
34
|
+
"tailwindcss": "^4.2.4",
|
|
35
|
+
"typescript": "^5"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "preserve",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [{ "name": "next" }],
|
|
17
|
+
"paths": { "@/*": ["./*"] }
|
|
18
|
+
},
|
|
19
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
20
|
+
"exclude": ["node_modules"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { StaticImport } from "next/dist/shared/lib/get-img-props";
|
|
2
|
+
|
|
3
|
+
export type SliderImage = {
|
|
4
|
+
label?: string;
|
|
5
|
+
url: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export interface BookingFormValues {
|
|
9
|
+
adults: number;
|
|
10
|
+
children: number;
|
|
11
|
+
checkIn: string;
|
|
12
|
+
checkOut: string;
|
|
13
|
+
promoCode: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SearchParams {
|
|
17
|
+
checkIn?: string;
|
|
18
|
+
checkOut?: string;
|
|
19
|
+
adults?: string;
|
|
20
|
+
children?: string;
|
|
21
|
+
promo?: string;
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rayso-template",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A premium Next.js template by RAYSO.STUDIO",
|
|
5
|
+
"author": "RAYSO.STUDIO",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "npm-run-all --parallel dev:*",
|
|
8
|
+
"dev:frontend": "cd frontend && npm run dev",
|
|
9
|
+
"dev:studio": "cd studio && npm run dev",
|
|
10
|
+
"deploy:studio": "cd studio && sanity deploy",
|
|
11
|
+
"zip": "bash zip.sh"
|
|
12
|
+
},
|
|
13
|
+
"workspaces": ["frontend", "studio"],
|
|
14
|
+
"private": true,
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"npm-run-all": "^4.1.5"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rayso-template-studio",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "sanity dev",
|
|
8
|
+
"build": "sanity build",
|
|
9
|
+
"deploy": "sanity deploy"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@sanity/vision": "^4.22.0",
|
|
13
|
+
"react": "^19.2.6",
|
|
14
|
+
"react-dom": "^19.2.6",
|
|
15
|
+
"sanity": "^4.22.0",
|
|
16
|
+
"styled-components": "^6.1.15"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@sanity/eslint-config-studio": "^5.0.1",
|
|
20
|
+
"@types/react": "^19",
|
|
21
|
+
"typescript": "^5"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineConfig } from "sanity";
|
|
2
|
+
import { structureTool } from "sanity/structure";
|
|
3
|
+
import { schemaTypes } from "./schemaTypes";
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
name: "default",
|
|
7
|
+
title: "Studio",
|
|
8
|
+
projectId: process.env.SANITY_STUDIO_PROJECT_ID!,
|
|
9
|
+
dataset: process.env.SANITY_STUDIO_DATASET ?? "production",
|
|
10
|
+
plugins: [structureTool()],
|
|
11
|
+
schema: { types: schemaTypes },
|
|
12
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "preserve"
|
|
15
|
+
},
|
|
16
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
17
|
+
"exclude": ["node_modules"]
|
|
18
|
+
}
|
package/template/zip.sh
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
TEMPLATE_NAME=$(node -p "require('./package.json').name")
|
|
4
|
+
OUTPUT_DIR="./dist"
|
|
5
|
+
ZIP_NAME="${TEMPLATE_NAME}-$(date +%Y%m%d).zip"
|
|
6
|
+
rm -rf "$OUTPUT_DIR" && mkdir -p "$OUTPUT_DIR"
|
|
7
|
+
zip -r "$OUTPUT_DIR/$ZIP_NAME" . \
|
|
8
|
+
--exclude "*/node_modules/*" --exclude "*/.next/*" --exclude "*/.sanity/*" \
|
|
9
|
+
--exclude "*/dist/*" --exclude "*/.git/*" --exclude "*/.env.local" \
|
|
10
|
+
--exclude "*/.env.*.local" --exclude "*/.DS_Store" --exclude "*.log" \
|
|
11
|
+
--exclude "*/zip.sh"
|
|
12
|
+
echo "✅ $OUTPUT_DIR/$ZIP_NAME ($(du -sh "$OUTPUT_DIR/$ZIP_NAME" | cut -f1))"
|