create-spine 1.0.0 → 1.0.2
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/index.js +22 -1
- package/package.json +1 -1
- package/template/AGENTS.md +5 -0
- package/template/CLAUDE.md +1 -0
- package/template/README.md +37 -0
- package/template/app/Refund/page.tsx +340 -0
- package/template/app/api/auth/[...nextauth]/route.ts +6 -0
- package/template/app/api/generate/route.ts +57 -0
- package/template/app/dashboard/page.tsx +713 -0
- package/template/app/faq/page.tsx +343 -0
- package/template/app/globals.css +130 -0
- package/template/app/layout.tsx +33 -0
- package/template/app/page.tsx +650 -0
- package/template/app/pricing/page.tsx +23 -0
- package/template/app/privacy/page.tsx +328 -0
- package/template/app/terms/page.tsx +12 -0
- package/template/components/ui/badge.tsx +52 -0
- package/template/components/ui/button.tsx +58 -0
- package/template/components/ui/card.tsx +103 -0
- package/template/components/ui/separator.tsx +25 -0
- package/template/components/ui/textarea.tsx +18 -0
- package/template/components/ui/ui/footer/page.tsx +103 -0
- package/template/components/ui/ui/navbar/navbar.tsx +0 -0
- package/template/components.json +25 -0
- package/template/desktop.ini +6 -0
- package/template/eslint.config.mjs +18 -0
- package/template/lib/auth.ts +19 -0
- package/template/lib/prisma.ts +7 -0
- package/template/lib/utils.ts +6 -0
- package/template/next.config.ts +7 -0
- package/template/package-lock.json +9794 -0
- package/template/package.json +35 -0
- package/template/postcss.config.mjs +7 -0
- package/template/public/X.svg +1 -0
- package/template/public/github.svg +2 -0
- package/template/public/google.svg +2 -0
- package/template/src/components/ui/badge.tsx +58 -0
- package/template/src/components/ui/button.tsx +90 -0
- package/template/src/components/ui/card.tsx +103 -0
- package/template/src/components/ui/textarea.tsx +29 -0
- package/template/src/lib/utils.ts +6 -0
- package/template/tsconfig.json +34 -0
package/index.js
CHANGED
|
@@ -1,2 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const { execSync } = require("child_process");
|
|
5
|
+
|
|
6
|
+
const projectName = process.argv[2] || "my-spinekit-app";
|
|
7
|
+
const templateDir = path.join(__dirname, "template");
|
|
8
|
+
const targetDir = path.join(process.cwd(), projectName);
|
|
9
|
+
|
|
10
|
+
if (fs.existsSync(targetDir)) {
|
|
11
|
+
console.error(`Folder "${projectName}" already exists. Pick a different name.`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fs.cpSync(templateDir, targetDir, { recursive: true });
|
|
16
|
+
console.log(`\nCreated ${projectName}`);
|
|
17
|
+
|
|
18
|
+
console.log(`\nInstalling dependencies...\n`);
|
|
19
|
+
execSync("npm install", { cwd: targetDir, stdio: "inherit" });
|
|
20
|
+
|
|
21
|
+
console.log(`\nAll done! Run this to start:`);
|
|
22
|
+
console.log(` cd ${projectName}`);
|
|
23
|
+
console.log(` npm run dev\n`);
|
package/package.json
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- BEGIN:nextjs-agent-rules -->
|
|
2
|
+
# This is NOT the Next.js you know
|
|
3
|
+
|
|
4
|
+
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
|
|
5
|
+
<!-- END:nextjs-agent-rules -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
|
2
|
+
|
|
3
|
+
## Getting Started
|
|
4
|
+
|
|
5
|
+
First, run the development server:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev
|
|
9
|
+
# or
|
|
10
|
+
yarn dev
|
|
11
|
+
# or
|
|
12
|
+
pnpm dev
|
|
13
|
+
# or
|
|
14
|
+
bun dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
18
|
+
|
|
19
|
+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
|
20
|
+
|
|
21
|
+
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
|
22
|
+
|
|
23
|
+
## Learn More
|
|
24
|
+
|
|
25
|
+
To learn more about Next.js, take a look at the following resources:
|
|
26
|
+
|
|
27
|
+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
28
|
+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
29
|
+
|
|
30
|
+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
31
|
+
|
|
32
|
+
## Deploy on Vercel
|
|
33
|
+
|
|
34
|
+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
35
|
+
|
|
36
|
+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
|
37
|
+
# SPINEKIT
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { Card, CardContent } from "@/components/ui/card";
|
|
4
|
+
import {
|
|
5
|
+
Table,
|
|
6
|
+
TableBody,
|
|
7
|
+
TableCell,
|
|
8
|
+
TableHead,
|
|
9
|
+
TableHeader,
|
|
10
|
+
TableRow,
|
|
11
|
+
} from "@/components/ui/table";
|
|
12
|
+
import { Reveal } from "@/components/reveal";
|
|
13
|
+
|
|
14
|
+
export const metadata: Metadata = {
|
|
15
|
+
title: "Refund Policy — Oshunt",
|
|
16
|
+
description: "Oshunt's returns, refunds, and exchange policy.",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const quickFacts = [
|
|
20
|
+
{ num: "30", label: "days to request a return" },
|
|
21
|
+
{ num: "5–7", label: "business days to process" },
|
|
22
|
+
{ num: "100%", label: "refund on eligible items" },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const shippingRows = [
|
|
26
|
+
{ reason: "Changed your mind, wrong size/shade", payer: "Customer" },
|
|
27
|
+
{ reason: "Damaged, defective, or incorrect item shipped", payer: "Oshunt" },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const sectionIndex = [
|
|
31
|
+
{ id: "returns-window", num: "01", title: "Returns window" },
|
|
32
|
+
{ id: "not-returnable", num: "02", title: "What can't be returned" },
|
|
33
|
+
{ id: "start-a-return", num: "03", title: "How to start a return" },
|
|
34
|
+
{ id: "refunds", num: "04", title: "Refunds" },
|
|
35
|
+
{ id: "damaged-items", num: "05", title: "Damaged, defective, or incorrect items" },
|
|
36
|
+
{ id: "exchanges", num: "06", title: "Exchanges" },
|
|
37
|
+
{ id: "late-refunds", num: "07", title: "Late or missing refunds" },
|
|
38
|
+
{ id: "sale-items", num: "08", title: "Sale items" },
|
|
39
|
+
{ id: "shipping-costs", num: "09", title: "Shipping costs for returns" },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
export default function RefundPolicyPage() {
|
|
43
|
+
return (
|
|
44
|
+
<div className="bg-[#FAF8F2] text-[#12201D] font-sans antialiased">
|
|
45
|
+
<style>{`
|
|
46
|
+
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600&family=Inter:wght@400;500;600&display=swap');
|
|
47
|
+
.font-serif { font-family: 'Fraunces', ui-serif, Georgia, serif; font-optical-sizing: auto; }
|
|
48
|
+
.font-sans { font-family: 'Inter', ui-sans-serif, system-ui, sans-serif; }
|
|
49
|
+
|
|
50
|
+
@keyframes gentle-breathe {
|
|
51
|
+
0%, 100% { transform: scale(1); opacity: 0.5; }
|
|
52
|
+
50% { transform: scale(1.05); opacity: 0.85; }
|
|
53
|
+
}
|
|
54
|
+
.ambient-ring { animation: gentle-breathe 9s ease-in-out infinite; }
|
|
55
|
+
.ambient-ring-slow { animation: gentle-breathe 12s ease-in-out infinite; animation-delay: 1.2s; }
|
|
56
|
+
|
|
57
|
+
@media (prefers-reduced-motion: reduce) {
|
|
58
|
+
.ambient-ring, .ambient-ring-slow { animation: none; }
|
|
59
|
+
}
|
|
60
|
+
`}</style>
|
|
61
|
+
|
|
62
|
+
{/* Very faint paper-grain — reinforces the print/editorial feel without weighing the page down */}
|
|
63
|
+
<div
|
|
64
|
+
aria-hidden
|
|
65
|
+
className="pointer-events-none fixed inset-0 z-[1] opacity-[0.025] mix-blend-multiply"
|
|
66
|
+
style={{ backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")` }}
|
|
67
|
+
/>
|
|
68
|
+
|
|
69
|
+
{/* Hero */}
|
|
70
|
+
<section className="relative overflow-hidden bg-[#0E2E2B] px-6 pb-14 pt-18 text-[#FBF6E9] sm:pb-16 sm:pt-24">
|
|
71
|
+
<div
|
|
72
|
+
aria-hidden
|
|
73
|
+
className="ambient-ring pointer-events-none absolute -bottom-24 -right-16 h-64 w-64 rounded-full border border-white/10"
|
|
74
|
+
/>
|
|
75
|
+
<div
|
|
76
|
+
aria-hidden
|
|
77
|
+
className="ambient-ring-slow pointer-events-none absolute -bottom-10 right-2 h-44 w-44 rounded-full border border-white/10"
|
|
78
|
+
/>
|
|
79
|
+
<Reveal className="relative z-10 mx-auto max-w-3xl">
|
|
80
|
+
<p className="mb-4 text-xs font-medium uppercase tracking-[0.14em] text-[#F0DFA8]">
|
|
81
|
+
Oshunt · customer care
|
|
82
|
+
</p>
|
|
83
|
+
<h1 className="font-serif text-4xl font-medium leading-tight tracking-tight sm:text-[44px]">
|
|
84
|
+
Refund policy
|
|
85
|
+
</h1>
|
|
86
|
+
<p className="mt-4 max-w-lg text-[16.5px] text-[#FBF6E9]/80">
|
|
87
|
+
We want you to love what you ordered. If something isn't
|
|
88
|
+
right, here's exactly how returns, refunds, and exchanges
|
|
89
|
+
work.
|
|
90
|
+
</p>
|
|
91
|
+
<p className="mt-7 text-[13px] text-[#FBF6E9]/60">
|
|
92
|
+
Last updated:{" "}
|
|
93
|
+
<span className="rounded bg-[#B9862E]/25 px-1.5 py-0.5 text-[#F0DFA8]">
|
|
94
|
+
[Month DD, YYYY]
|
|
95
|
+
</span>
|
|
96
|
+
</p>
|
|
97
|
+
</Reveal>
|
|
98
|
+
</section>
|
|
99
|
+
|
|
100
|
+
<main className="relative mx-auto max-w-3xl px-6 pb-24 pt-12">
|
|
101
|
+
{/* Quick facts — kept as one seamless bar with hairline dividers rather than
|
|
102
|
+
three separate cards; that was already the right call for this row. */}
|
|
103
|
+
<Reveal>
|
|
104
|
+
<Card className="mb-6 overflow-hidden border-[#DCD4BE] p-0 shadow-none">
|
|
105
|
+
<div className="grid grid-cols-1 gap-px bg-[#DCD4BE] sm:grid-cols-3">
|
|
106
|
+
{quickFacts.map((fact) => (
|
|
107
|
+
<div key={fact.label} className="bg-white px-5 py-6 transition-colors hover:bg-[#FBF6E9]/60">
|
|
108
|
+
<span className="block font-serif text-2xl text-[#153F3A]">
|
|
109
|
+
{fact.num}
|
|
110
|
+
</span>
|
|
111
|
+
<span className="text-[13px] text-[#4C5B57]">
|
|
112
|
+
{fact.label}
|
|
113
|
+
</span>
|
|
114
|
+
</div>
|
|
115
|
+
))}
|
|
116
|
+
</div>
|
|
117
|
+
</Card>
|
|
118
|
+
</Reveal>
|
|
119
|
+
|
|
120
|
+
{/* On this page */}
|
|
121
|
+
<Reveal delay={80}>
|
|
122
|
+
<Card className="mb-14 border-[#DCD4BE] bg-white shadow-none">
|
|
123
|
+
<CardContent className="p-6">
|
|
124
|
+
<p className="mb-3 text-xs font-semibold uppercase tracking-[0.14em] text-[#4C5B57]">
|
|
125
|
+
On this page
|
|
126
|
+
</p>
|
|
127
|
+
<nav className="grid grid-cols-1 gap-x-6 gap-y-1.5 sm:grid-cols-2">
|
|
128
|
+
{sectionIndex.map((s) => (
|
|
129
|
+
<a
|
|
130
|
+
key={s.id}
|
|
131
|
+
href={`#${s.id}`}
|
|
132
|
+
className="rounded-sm text-sm text-[#4C5B57] transition-colors hover:text-[#B9862E] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#B9862E]"
|
|
133
|
+
>
|
|
134
|
+
{s.num}. {s.title}
|
|
135
|
+
</a>
|
|
136
|
+
))}
|
|
137
|
+
</nav>
|
|
138
|
+
</CardContent>
|
|
139
|
+
</Card>
|
|
140
|
+
</Reveal>
|
|
141
|
+
|
|
142
|
+
<PolicySection id="returns-window" index="01" title="Returns window">
|
|
143
|
+
<p>
|
|
144
|
+
You have <strong>30 calendar days</strong> from the delivery date
|
|
145
|
+
to request a return. After 30 days, we're unable to offer a
|
|
146
|
+
refund or exchange.
|
|
147
|
+
</p>
|
|
148
|
+
<p>
|
|
149
|
+
To be eligible, your item must be unused, in its original
|
|
150
|
+
condition, and in the original packaging. We may ask for proof of
|
|
151
|
+
purchase, such as your order number or receipt.
|
|
152
|
+
</p>
|
|
153
|
+
</PolicySection>
|
|
154
|
+
|
|
155
|
+
<PolicySection id="not-returnable" index="02" title="What can't be returned">
|
|
156
|
+
<p>
|
|
157
|
+
For hygiene and safety reasons, the following items can't be
|
|
158
|
+
returned unless they arrive damaged or defective:
|
|
159
|
+
</p>
|
|
160
|
+
<ul className="mb-3 list-disc space-y-2 pl-5 marker:text-[#B9862E]">
|
|
161
|
+
<li>Opened or used skincare, haircare, or beauty products</li>
|
|
162
|
+
<li>Items with a broken seal or removed hygiene sticker</li>
|
|
163
|
+
<li>Gift cards</li>
|
|
164
|
+
<li>Final sale or clearance items, marked as such at checkout</li>
|
|
165
|
+
</ul>
|
|
166
|
+
</PolicySection>
|
|
167
|
+
|
|
168
|
+
<PolicySection id="start-a-return" index="03" title="How to start a return">
|
|
169
|
+
<ul className="mb-3 list-disc space-y-2 pl-5 marker:text-[#B9862E]">
|
|
170
|
+
<li>
|
|
171
|
+
Email{" "}
|
|
172
|
+
<span className="rounded bg-[#B9862E]/15 px-1.5 py-0.5 text-[#7A5717]">
|
|
173
|
+
[returns@oshunt.com]
|
|
174
|
+
</span>{" "}
|
|
175
|
+
with your order number and the reason for your return.
|
|
176
|
+
</li>
|
|
177
|
+
<li>
|
|
178
|
+
We'll confirm eligibility and send you a return shipping
|
|
179
|
+
label and instructions.
|
|
180
|
+
</li>
|
|
181
|
+
<li>
|
|
182
|
+
Pack the item securely in its original packaging and drop it
|
|
183
|
+
off at the specified carrier.
|
|
184
|
+
</li>
|
|
185
|
+
</ul>
|
|
186
|
+
<p>
|
|
187
|
+
Items sent back without first requesting a return may not be
|
|
188
|
+
accepted.
|
|
189
|
+
</p>
|
|
190
|
+
</PolicySection>
|
|
191
|
+
|
|
192
|
+
<PolicySection id="refunds" index="04" title="Refunds">
|
|
193
|
+
<p>
|
|
194
|
+
Once we receive and inspect your return, we'll notify you by
|
|
195
|
+
email whether it's approved. Approved refunds are issued to
|
|
196
|
+
your original payment method within{" "}
|
|
197
|
+
<strong>5–7 business days</strong>. Your bank or card provider
|
|
198
|
+
may take a few additional days to post the refund.
|
|
199
|
+
</p>
|
|
200
|
+
<div className="my-4 rounded-r-xl border-l-2 border-[#B9862E] bg-[#FBF6E9] px-5 py-4">
|
|
201
|
+
<p className="text-[14.5px] text-[#0E2E2B]">
|
|
202
|
+
Original shipping fees are non-refundable, unless the return is
|
|
203
|
+
due to our error (wrong or defective item).
|
|
204
|
+
</p>
|
|
205
|
+
</div>
|
|
206
|
+
</PolicySection>
|
|
207
|
+
|
|
208
|
+
<PolicySection id="damaged-items" index="05" title="Damaged, defective, or incorrect items">
|
|
209
|
+
<p>
|
|
210
|
+
If your order arrives damaged, defective, or different from what
|
|
211
|
+
you ordered, contact us within{" "}
|
|
212
|
+
<strong>7 days of delivery</strong> with a photo of the item.
|
|
213
|
+
We'll cover return shipping and send a replacement or full
|
|
214
|
+
refund, whichever you prefer.
|
|
215
|
+
</p>
|
|
216
|
+
</PolicySection>
|
|
217
|
+
|
|
218
|
+
<PolicySection id="exchanges" index="06" title="Exchanges">
|
|
219
|
+
<p>
|
|
220
|
+
The fastest way to get a different size, shade, or product is to
|
|
221
|
+
return the original item and place a new order. If you'd
|
|
222
|
+
rather exchange directly, mention this when you email us and
|
|
223
|
+
we'll do our best to accommodate it, subject to stock.
|
|
224
|
+
</p>
|
|
225
|
+
</PolicySection>
|
|
226
|
+
|
|
227
|
+
<PolicySection id="late-refunds" index="07" title="Late or missing refunds">
|
|
228
|
+
<p>
|
|
229
|
+
If it's been longer than 10 business days since your refund
|
|
230
|
+
was approved:
|
|
231
|
+
</p>
|
|
232
|
+
<ul className="mb-3 list-disc space-y-2 pl-5 marker:text-[#B9862E]">
|
|
233
|
+
<li>Check your bank account again</li>
|
|
234
|
+
<li>Contact your card provider — posting times vary</li>
|
|
235
|
+
<li>
|
|
236
|
+
If it's still missing, reach out to us at{" "}
|
|
237
|
+
<span className="rounded bg-[#B9862E]/15 px-1.5 py-0.5 text-[#7A5717]">
|
|
238
|
+
[returns@oshunt.com]
|
|
239
|
+
</span>
|
|
240
|
+
</li>
|
|
241
|
+
</ul>
|
|
242
|
+
</PolicySection>
|
|
243
|
+
|
|
244
|
+
<PolicySection id="sale-items" index="08" title="Sale items">
|
|
245
|
+
<p>
|
|
246
|
+
Only regular-priced items are eligible for a refund. Items
|
|
247
|
+
purchased on sale or with a discount code are final sale, unless
|
|
248
|
+
the item arrived damaged or defective.
|
|
249
|
+
</p>
|
|
250
|
+
</PolicySection>
|
|
251
|
+
|
|
252
|
+
<PolicySection id="shipping-costs" index="09" title="Shipping costs for returns">
|
|
253
|
+
<Card className="mb-5 mt-4 overflow-hidden border-[#DCD4BE] p-0 shadow-none">
|
|
254
|
+
<Table>
|
|
255
|
+
<TableHeader>
|
|
256
|
+
<TableRow className="border-[#DCD4BE] hover:bg-transparent">
|
|
257
|
+
<TableHead className="bg-[#FBF6E9] font-semibold text-[#153F3A]">
|
|
258
|
+
Reason for return
|
|
259
|
+
</TableHead>
|
|
260
|
+
<TableHead className="bg-[#FBF6E9] font-semibold text-[#153F3A]">
|
|
261
|
+
Who pays return shipping
|
|
262
|
+
</TableHead>
|
|
263
|
+
</TableRow>
|
|
264
|
+
</TableHeader>
|
|
265
|
+
<TableBody>
|
|
266
|
+
{shippingRows.map((row) => (
|
|
267
|
+
<TableRow key={row.reason} className="border-[#DCD4BE] hover:bg-[#FBF6E9]/60">
|
|
268
|
+
<TableCell className="text-[#4C5B57]">{row.reason}</TableCell>
|
|
269
|
+
<TableCell className="text-[#4C5B57]">{row.payer}</TableCell>
|
|
270
|
+
</TableRow>
|
|
271
|
+
))}
|
|
272
|
+
</TableBody>
|
|
273
|
+
</Table>
|
|
274
|
+
</Card>
|
|
275
|
+
</PolicySection>
|
|
276
|
+
|
|
277
|
+
{/* Contact card */}
|
|
278
|
+
<Reveal>
|
|
279
|
+
<Card className="mt-14 border-none bg-[#153F3A] shadow-none">
|
|
280
|
+
<CardContent className="p-8 text-[#FBF6E9]">
|
|
281
|
+
<h2 className="mb-3 font-serif text-xl font-medium text-[#F0DFA8]">
|
|
282
|
+
Need a hand?
|
|
283
|
+
</h2>
|
|
284
|
+
<p className="mb-2 text-[#FBF6E9]/80">
|
|
285
|
+
Our team is happy to help with any return, refund, or order
|
|
286
|
+
question.
|
|
287
|
+
</p>
|
|
288
|
+
<p className="text-[#FBF6E9]/80">
|
|
289
|
+
Email{" "}
|
|
290
|
+
<a
|
|
291
|
+
href="mailto:returns@oshunt.com"
|
|
292
|
+
className="border-b border-[#F0DFA8]/40 text-[#F0DFA8] no-underline transition-colors hover:border-[#F0DFA8] hover:text-[#FBF6E9] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#F0DFA8] rounded-sm"
|
|
293
|
+
>
|
|
294
|
+
returns@oshunt.com
|
|
295
|
+
</a>{" "}
|
|
296
|
+
or visit our{" "}
|
|
297
|
+
<span className="rounded bg-[#B9862E]/25 px-1.5 py-0.5">
|
|
298
|
+
[Contact Us page]
|
|
299
|
+
</span>
|
|
300
|
+
.
|
|
301
|
+
</p>
|
|
302
|
+
</CardContent>
|
|
303
|
+
</Card>
|
|
304
|
+
</Reveal>
|
|
305
|
+
</main>
|
|
306
|
+
|
|
307
|
+
<footer className="relative px-6 pb-10 pt-7 text-center text-[12.5px] text-[#4C5B57]">
|
|
308
|
+
This page is a template and should be reviewed against your actual
|
|
309
|
+
shipping, fulfillment, and local consumer-protection requirements
|
|
310
|
+
before publishing. It is not legal advice.
|
|
311
|
+
</footer>
|
|
312
|
+
</div>
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function PolicySection({
|
|
317
|
+
id,
|
|
318
|
+
index,
|
|
319
|
+
title,
|
|
320
|
+
children,
|
|
321
|
+
}: {
|
|
322
|
+
id: string;
|
|
323
|
+
index: string;
|
|
324
|
+
title: string;
|
|
325
|
+
children: ReactNode;
|
|
326
|
+
}) {
|
|
327
|
+
return (
|
|
328
|
+
<section id={id} className="mb-11 scroll-mt-8">
|
|
329
|
+
<h2 className="mb-3.5 flex items-baseline gap-2.5 font-serif text-xl font-medium text-[#153F3A]">
|
|
330
|
+
<span className="font-sans text-sm font-semibold text-[#B9862E]">
|
|
331
|
+
{index}
|
|
332
|
+
</span>
|
|
333
|
+
{title}
|
|
334
|
+
</h2>
|
|
335
|
+
<div className="space-y-3 text-[15.5px] leading-relaxed text-[#4C5B57]">
|
|
336
|
+
{children}
|
|
337
|
+
</div>
|
|
338
|
+
</section>
|
|
339
|
+
);
|
|
340
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
2
|
+
import { NextResponse } from "next/server";
|
|
3
|
+
|
|
4
|
+
// Initialize the Gemini Engine
|
|
5
|
+
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || "");
|
|
6
|
+
|
|
7
|
+
export async function POST(req: Request) {
|
|
8
|
+
try {
|
|
9
|
+
// 1. Grab the user's prompt and chosen stack from the frontend
|
|
10
|
+
const body = await req.json();
|
|
11
|
+
const { prompt, stack } = body;
|
|
12
|
+
|
|
13
|
+
if (!prompt) {
|
|
14
|
+
return NextResponse.json({ error: "Prompt is required" }, { status: 400 });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 2. Configure Gemini to guarantee a strict JSON response
|
|
18
|
+
const model = genAI.getGenerativeModel({
|
|
19
|
+
model: "gemini-2.5-flash",
|
|
20
|
+
generationConfig: { responseMimeType: "application/json" }
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// 3. The Master System Architect Prompt
|
|
24
|
+
const systemInstruction = `
|
|
25
|
+
You are Spine, an elite backend architect.
|
|
26
|
+
The user wants to build this application: "${prompt}"
|
|
27
|
+
They have selected this base stack: ${stack}
|
|
28
|
+
|
|
29
|
+
Your job is to analyze the prompt and generate the custom backend logic needed for this app.
|
|
30
|
+
Return ONLY valid JSON with this exact structure:
|
|
31
|
+
{
|
|
32
|
+
"schemaCode": "// prisma/schema.prisma\\n// Write the complete Prisma schema models here...",
|
|
33
|
+
"apiRoutes": [
|
|
34
|
+
{
|
|
35
|
+
"folderPath": "the-name-of-the-route-folder",
|
|
36
|
+
"code": "// src/app/api/folder-name/route.ts\\n// Write the full GET/POST controllers here..."
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"readme": "# Custom Architecture Blueprint\\n// Write a short explanation of what you built..."
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
// 4. Send to Gemini and parse the result
|
|
44
|
+
const result = await model.generateContent(systemInstruction);
|
|
45
|
+
const text = result.response.text();
|
|
46
|
+
const blueprint = JSON.parse(text);
|
|
47
|
+
|
|
48
|
+
return NextResponse.json({ success: true, blueprint });
|
|
49
|
+
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error("Gemini API Error:", error);
|
|
52
|
+
return NextResponse.json(
|
|
53
|
+
{ success: false, error: "Failed to generate architecture." },
|
|
54
|
+
{ status: 500 }
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|