|
| 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'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'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