Skip to content

Commit 0b24c5d

Browse files
ralyodioclaude
andcommitted
feat: modern redesign, /docs page, and CLI
- Full UI refresh: slate/orange design system, dark footer, hero with gradient, stats bar, CLI CTA banner, improved cards and search bar - Header: shadow, pill nav, Docs link, tighter spacing - Footer: dark slate-900, four-column layout with developer links - CouponCard/StoreCard: hover glow, better badge/icon treatment - CopyButton: dark button with check-mark animation - /docs page: sidebar nav, CLI reference, REST API reference - public/install.sh: curl | sh installer - public/cli/c0upons: full bash CLI (search, latest, stores, store) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 28cabc5 commit 0b24c5d

12 files changed

Lines changed: 906 additions & 119 deletions

File tree

apps/web/app/docs/page.tsx

Lines changed: 376 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
import type { Metadata } from 'next';
2+
import Link from 'next/link';
3+
4+
export const metadata: Metadata = {
5+
title: 'Docs — c0upons',
6+
description: 'Documentation for the c0upons CLI and REST API.',
7+
};
8+
9+
function Section({ id, children }: { id: string; children: React.ReactNode }) {
10+
return (
11+
<section id={id} className="flex flex-col gap-4 scroll-mt-20">
12+
{children}
13+
</section>
14+
);
15+
}
16+
17+
function H2({ children }: { children: React.ReactNode }) {
18+
return <h2 className="text-2xl font-bold text-slate-900 border-b border-slate-200 pb-3">{children}</h2>;
19+
}
20+
21+
function H3({ children }: { children: React.ReactNode }) {
22+
return <h3 className="text-base font-bold text-slate-800 mt-2">{children}</h3>;
23+
}
24+
25+
function Code({ children }: { children: React.ReactNode }) {
26+
return (
27+
<pre className="bg-slate-900 text-slate-100 rounded-xl p-4 text-sm font-mono leading-relaxed overflow-x-auto">
28+
<code>{children}</code>
29+
</pre>
30+
);
31+
}
32+
33+
function InlineCode({ children }: { children: React.ReactNode }) {
34+
return (
35+
<code className="bg-slate-100 text-orange-600 text-sm font-mono px-1.5 py-0.5 rounded">
36+
{children}
37+
</code>
38+
);
39+
}
40+
41+
function Badge({ children }: { children: React.ReactNode }) {
42+
return (
43+
<span className="inline-block bg-orange-100 text-orange-700 text-xs font-bold px-2 py-0.5 rounded-full uppercase tracking-wide">
44+
{children}
45+
</span>
46+
);
47+
}
48+
49+
const nav = [
50+
{ href: '#overview', label: 'Overview' },
51+
{ href: '#cli', label: 'CLI' },
52+
{ href: '#cli-install', label: ' Installation' },
53+
{ href: '#cli-commands', label: ' Commands' },
54+
{ href: '#api', label: 'REST API' },
55+
{ href: '#api-coupons', label: ' Coupons' },
56+
{ href: '#api-stores', label: ' Stores' },
57+
{ href: '#api-search', label: ' Search' },
58+
{ href: '#contributing', label: 'Contributing' },
59+
];
60+
61+
export default function DocsPage() {
62+
return (
63+
<div className="flex flex-col lg:flex-row gap-10 max-w-5xl mx-auto">
64+
{/* Sidebar */}
65+
<aside className="lg:w-52 shrink-0">
66+
<div className="lg:sticky lg:top-20 flex flex-col gap-1">
67+
<p className="text-xs font-bold text-slate-400 uppercase tracking-widest mb-2 px-3">On this page</p>
68+
{nav.map((item) => (
69+
<a
70+
key={item.href}
71+
href={item.href}
72+
className={`text-sm px-3 py-1.5 rounded-lg transition-colors hover:bg-slate-100 hover:text-orange-500 text-slate-600 ${
73+
item.label.startsWith(' ') ? 'pl-6 text-xs' : 'font-medium'
74+
}`}
75+
>
76+
{item.label.trim()}
77+
</a>
78+
))}
79+
<div className="mt-4 pt-4 border-t border-slate-100">
80+
<Link
81+
href="/"
82+
className="text-xs text-slate-400 hover:text-orange-500 transition-colors px-3"
83+
>
84+
← Back to coupons
85+
</Link>
86+
</div>
87+
</div>
88+
</aside>
89+
90+
{/* Content */}
91+
<div className="flex-1 min-w-0 flex flex-col gap-12">
92+
<div>
93+
<h1 className="text-3xl font-black text-slate-900">Documentation</h1>
94+
<p className="text-slate-500 mt-2">
95+
Everything you need to use c0upons from the web, terminal, or your own apps.
96+
</p>
97+
</div>
98+
99+
{/* Overview */}
100+
<Section id="overview">
101+
<H2>Overview</H2>
102+
<p className="text-slate-600 leading-relaxed">
103+
<strong>c0upons</strong> is a community-powered coupon code platform. Anyone can browse
104+
deals for free, submit codes, and vote on the best ones. It also exposes a simple REST
105+
API and a bash CLI so developers can integrate savings into their own workflows.
106+
</p>
107+
<div className="grid sm:grid-cols-3 gap-4">
108+
{[
109+
{ icon: '🌐', title: 'Web App', desc: 'Browse and submit coupons at c0upons.com' },
110+
{ icon: '⌨️', title: 'CLI', desc: 'Search deals without leaving the terminal' },
111+
{ icon: '🔌', title: 'REST API', desc: 'Integrate coupons into your own apps' },
112+
].map((f) => (
113+
<div key={f.title} className="border border-slate-200 rounded-xl p-4 flex flex-col gap-2">
114+
<span className="text-2xl">{f.icon}</span>
115+
<span className="font-semibold text-slate-900 text-sm">{f.title}</span>
116+
<span className="text-xs text-slate-500">{f.desc}</span>
117+
</div>
118+
))}
119+
</div>
120+
</Section>
121+
122+
{/* CLI */}
123+
<Section id="cli">
124+
<H2>CLI</H2>
125+
<p className="text-slate-600 leading-relaxed">
126+
The <InlineCode>c0upons</InlineCode> CLI is a bash script that lets you search coupons,
127+
list stores, and browse deals from your terminal. It requires <InlineCode>curl</InlineCode>{' '}
128+
and <InlineCode>jq</InlineCode>.
129+
</p>
130+
</Section>
131+
132+
<Section id="cli-install">
133+
<H3>Installation</H3>
134+
<p className="text-sm text-slate-600">Install with one command:</p>
135+
<Code>{`curl -fsSL https://c0upons.com/install.sh | sh`}</Code>
136+
<p className="text-sm text-slate-600">
137+
The installer places the script in <InlineCode>/usr/local/bin/c0upons</InlineCode> (or{' '}
138+
<InlineCode>~/.local/bin</InlineCode> if you don&apos;t have sudo). Verify the install:
139+
</p>
140+
<Code>{`c0upons version`}</Code>
141+
<p className="text-sm text-slate-500">
142+
You can also <a href="/cli/c0upons" className="text-orange-500 hover:underline">download the script directly</a> and place it anywhere in your <InlineCode>$PATH</InlineCode>.
143+
</p>
144+
145+
<H3>Dependencies</H3>
146+
<div className="overflow-x-auto">
147+
<table className="w-full text-sm border-collapse">
148+
<thead>
149+
<tr className="border-b border-slate-200">
150+
<th className="text-left py-2 px-3 text-slate-500 font-medium">Tool</th>
151+
<th className="text-left py-2 px-3 text-slate-500 font-medium">Required</th>
152+
<th className="text-left py-2 px-3 text-slate-500 font-medium">Install</th>
153+
</tr>
154+
</thead>
155+
<tbody>
156+
{[
157+
{ tool: 'curl', req: 'Yes', install: 'Usually pre-installed' },
158+
{ tool: 'jq', req: 'Yes', install: 'brew install jq / apt install jq' },
159+
{ tool: 'python3', req: 'Optional', install: 'Used for URL encoding' },
160+
].map((row) => (
161+
<tr key={row.tool} className="border-b border-slate-100">
162+
<td className="py-2 px-3"><InlineCode>{row.tool}</InlineCode></td>
163+
<td className="py-2 px-3 text-slate-600">{row.req}</td>
164+
<td className="py-2 px-3 text-slate-500">{row.install}</td>
165+
</tr>
166+
))}
167+
</tbody>
168+
</table>
169+
</div>
170+
</Section>
171+
172+
<Section id="cli-commands">
173+
<H3>Commands</H3>
174+
<div className="flex flex-col gap-6">
175+
{[
176+
{
177+
cmd: 'c0upons search <query>',
178+
desc: 'Search for coupons matching a query string.',
179+
example: 'c0upons search nike\nc0upons search "20% off"',
180+
},
181+
{
182+
cmd: 'c0upons stores',
183+
desc: 'List all stores with coupons.',
184+
example: 'c0upons stores',
185+
},
186+
{
187+
cmd: 'c0upons store <slug>',
188+
desc: 'Show all coupons for a specific store.',
189+
example: 'c0upons store nike\nc0upons store amazon',
190+
},
191+
{
192+
cmd: 'c0upons latest',
193+
desc: 'Show the most recently added coupons.',
194+
example: 'c0upons latest',
195+
},
196+
{
197+
cmd: 'c0upons version',
198+
desc: 'Print the installed CLI version.',
199+
example: 'c0upons version',
200+
},
201+
].map((item) => (
202+
<div key={item.cmd} className="flex flex-col gap-2">
203+
<div className="flex items-center gap-2">
204+
<InlineCode>{item.cmd}</InlineCode>
205+
</div>
206+
<p className="text-sm text-slate-600">{item.desc}</p>
207+
<Code>{item.example}</Code>
208+
</div>
209+
))}
210+
</div>
211+
212+
<H3>Environment variables</H3>
213+
<div className="overflow-x-auto">
214+
<table className="w-full text-sm border-collapse">
215+
<thead>
216+
<tr className="border-b border-slate-200">
217+
<th className="text-left py-2 px-3 text-slate-500 font-medium">Variable</th>
218+
<th className="text-left py-2 px-3 text-slate-500 font-medium">Default</th>
219+
<th className="text-left py-2 px-3 text-slate-500 font-medium">Description</th>
220+
</tr>
221+
</thead>
222+
<tbody>
223+
<tr>
224+
<td className="py-2 px-3"><InlineCode>C0UPONS_API</InlineCode></td>
225+
<td className="py-2 px-3 text-slate-500">https://c0upons.com/api</td>
226+
<td className="py-2 px-3 text-slate-600">Override the API base URL (e.g. for self-hosting)</td>
227+
</tr>
228+
</tbody>
229+
</table>
230+
</div>
231+
</Section>
232+
233+
{/* REST API */}
234+
<Section id="api">
235+
<H2>REST API</H2>
236+
<p className="text-slate-600 leading-relaxed">
237+
The c0upons API is a simple JSON REST API. No authentication is required for read operations.
238+
The base URL is <InlineCode>https://c0upons.com/api</InlineCode>.
239+
</p>
240+
</Section>
241+
242+
<Section id="api-coupons">
243+
<H3>Coupons</H3>
244+
<div className="flex flex-col gap-6">
245+
<div>
246+
<div className="flex items-center gap-2 mb-2">
247+
<Badge>GET</Badge>
248+
<InlineCode>/api/coupons</InlineCode>
249+
</div>
250+
<p className="text-sm text-slate-600 mb-3">Returns a list of coupons ordered by votes descending.</p>
251+
<Code>{`curl https://c0upons.com/api/coupons`}</Code>
252+
<p className="text-sm text-slate-500 mt-2">Response:</p>
253+
<Code>{`[
254+
{
255+
"id": 1,
256+
"store_id": 42,
257+
"code": "SAVE20",
258+
"title": "20% off sitewide",
259+
"description": "Valid on all orders over $50",
260+
"discount": "20%",
261+
"expiry_date": "2025-12-31",
262+
"verified": 1,
263+
"votes": 47,
264+
"url": null,
265+
"store_name": "Example Store",
266+
"store_slug": "example-store"
267+
}
268+
]`}</Code>
269+
</div>
270+
271+
<div>
272+
<div className="flex items-center gap-2 mb-2">
273+
<Badge>GET</Badge>
274+
<InlineCode>/api/coupons/[id]</InlineCode>
275+
</div>
276+
<p className="text-sm text-slate-600 mb-3">Returns a single coupon by ID.</p>
277+
<Code>{`curl https://c0upons.com/api/coupons/1`}</Code>
278+
</div>
279+
280+
<div>
281+
<div className="flex items-center gap-2 mb-2">
282+
<Badge>POST</Badge>
283+
<InlineCode>/api/coupons</InlineCode>
284+
</div>
285+
<p className="text-sm text-slate-600 mb-3">Submit a new coupon.</p>
286+
<Code>{`curl -X POST https://c0upons.com/api/coupons \\
287+
-H "Content-Type: application/json" \\
288+
-d '{
289+
"store_id": 42,
290+
"title": "20% off sitewide",
291+
"code": "SAVE20",
292+
"discount": "20%",
293+
"expiry_date": "2025-12-31"
294+
}'`}</Code>
295+
</div>
296+
</div>
297+
</Section>
298+
299+
<Section id="api-stores">
300+
<H3>Stores</H3>
301+
<div className="flex flex-col gap-6">
302+
<div>
303+
<div className="flex items-center gap-2 mb-2">
304+
<Badge>GET</Badge>
305+
<InlineCode>/api/stores</InlineCode>
306+
</div>
307+
<p className="text-sm text-slate-600 mb-3">Returns all stores alphabetically.</p>
308+
<Code>{`curl https://c0upons.com/api/stores`}</Code>
309+
</div>
310+
<div>
311+
<div className="flex items-center gap-2 mb-2">
312+
<Badge>GET</Badge>
313+
<InlineCode>/api/stores/[slug]</InlineCode>
314+
</div>
315+
<p className="text-sm text-slate-600 mb-3">Returns a store and its coupons.</p>
316+
<Code>{`curl https://c0upons.com/api/stores/nike`}</Code>
317+
</div>
318+
</div>
319+
</Section>
320+
321+
<Section id="api-search">
322+
<H3>Search</H3>
323+
<div>
324+
<div className="flex items-center gap-2 mb-2">
325+
<Badge>GET</Badge>
326+
<InlineCode>/api/search?q=query</InlineCode>
327+
</div>
328+
<p className="text-sm text-slate-600 mb-3">
329+
Full-text search across coupon titles, descriptions, codes, and store names.
330+
</p>
331+
<Code>{`curl "https://c0upons.com/api/search?q=nike"`}</Code>
332+
</div>
333+
</Section>
334+
335+
{/* Contributing */}
336+
<Section id="contributing">
337+
<H2>Contributing</H2>
338+
<p className="text-slate-600 leading-relaxed">
339+
c0upons is open source. Contributions are welcome — whether it&apos;s adding coupon codes via
340+
the web app, reporting bugs, or submitting pull requests.
341+
</p>
342+
<div className="flex flex-col gap-3">
343+
<H3>Submit a coupon via the web</H3>
344+
<p className="text-sm text-slate-600">
345+
The easiest way to contribute is to{' '}
346+
<Link href="/submit" className="text-orange-500 hover:underline font-medium">submit a coupon</Link>{' '}
347+
through the web interface.
348+
</p>
349+
350+
<H3>Submit via the CLI</H3>
351+
<p className="text-sm text-slate-600">Coming soon — the CLI will support interactive coupon submission.</p>
352+
353+
<H3>Submit via the API</H3>
354+
<Code>{`curl -X POST https://c0upons.com/api/coupons \\
355+
-H "Content-Type: application/json" \\
356+
-d '{"store_id": 1, "title": "15% off", "code": "GET15"}'`}</Code>
357+
358+
<H3>Source code</H3>
359+
<p className="text-sm text-slate-600">
360+
The full source is on{' '}
361+
<a
362+
href="https://github.com/profullstack/c0upons"
363+
target="_blank"
364+
rel="noopener noreferrer"
365+
className="text-orange-500 hover:underline font-medium"
366+
>
367+
GitHub ↗
368+
</a>
369+
. Open an issue or PR anytime.
370+
</p>
371+
</div>
372+
</Section>
373+
</div>
374+
</div>
375+
);
376+
}

0 commit comments

Comments
 (0)