├── .gitignore
├── .vscode
└── settings.json
├── README.md
├── biome.json
├── next.config.ts
├── orval.config.ts
├── package.json
├── pnpm-lock.yaml
├── postcss.config.mjs
├── public
└── background.png
├── src
├── app
│ ├── (home)
│ │ ├── page.tsx
│ │ └── subscription-form.tsx
│ ├── components
│ │ ├── button.tsx
│ │ ├── icon-button.tsx
│ │ └── input.tsx
│ ├── global.css
│ ├── invite
│ │ └── [subscriberId]
│ │ │ ├── invite-link-input.tsx
│ │ │ ├── page.tsx
│ │ │ ├── ranking.tsx
│ │ │ └── stats.tsx
│ └── layout.tsx
├── assets
│ ├── logo.svg
│ ├── medal-cooper.svg
│ ├── medal-gold.svg
│ └── medal-silver.svg
└── http
│ └── api.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 | .pnpm-debug.log*
32 |
33 | # env files (can opt-in for committing if needed)
34 | .env*
35 |
36 | # vercel
37 | .vercel
38 |
39 | # typescript
40 | *.tsbuildinfo
41 | next-env.d.ts
42 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[javascript]": {
3 | "editor.defaultFormatter": "biomejs.biome"
4 | },
5 | "[typescript]": {
6 | "editor.defaultFormatter": "biomejs.biome"
7 | },
8 | "[typescriptreact]": {
9 | "editor.defaultFormatter": "biomejs.biome"
10 | },
11 | "editor.codeActionsOnSave": {
12 | "source.organizeImports.biome": "explicit"
13 | },
14 | "editor.formatOnSave": true
15 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
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 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | 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.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
35 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
3 | "organizeImports": {
4 | "enabled": true
5 | },
6 | "formatter": {
7 | "indentStyle": "space",
8 | "indentWidth": 2,
9 | "lineWidth": 80
10 | },
11 | "javascript": {
12 | "formatter": {
13 | "arrowParentheses": "asNeeded",
14 | "jsxQuoteStyle": "double",
15 | "quoteStyle": "single",
16 | "semicolons": "asNeeded",
17 | "trailingCommas": "es5"
18 | }
19 | },
20 | "linter": {
21 | "enabled": true,
22 | "rules": {
23 | "recommended": true,
24 | "a11y": {
25 | "noSvgWithoutTitle": "off"
26 | },
27 | "suspicious": {
28 | "noArrayIndexKey": "info"
29 | }
30 | }
31 | },
32 | "files": {
33 | "ignore": [
34 | "node_modules"
35 | ]
36 | }
37 | }
--------------------------------------------------------------------------------
/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/orval.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'orval'
2 |
3 | export default defineConfig({
4 | api: {
5 | input: 'http://localhost:3333/docs/json',
6 | output: {
7 | target: './src/http/api.ts',
8 | client: 'fetch',
9 | httpClient: 'fetch',
10 | clean: true,
11 | baseUrl: 'http://localhost:3333',
12 |
13 | override: {
14 | fetch: {
15 | includeHttpResponseReturnType: false,
16 | },
17 | },
18 | },
19 | },
20 | })
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nlw-connect-react",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev --turbopack",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@hookform/resolvers": "^3.10.0",
13 | "@tailwindcss/postcss": "^4.0.4",
14 | "lucide-react": "^0.474.0",
15 | "next": "15.1.6",
16 | "orval": "^7.5.0",
17 | "postcss": "^8.5.1",
18 | "react": "^19.0.0",
19 | "react-dom": "^19.0.0",
20 | "react-hook-form": "^7.54.2",
21 | "tailwind-merge": "^3.0.1",
22 | "tailwindcss": "^4.0.4",
23 | "zod": "^3.24.1"
24 | },
25 | "devDependencies": {
26 | "@biomejs/biome": "^1.9.4",
27 | "@types/node": "^20",
28 | "@types/react": "^19",
29 | "@types/react-dom": "^19",
30 | "typescript": "^5"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | const config = {
2 | plugins: {
3 | "@tailwindcss/postcss": {},
4 | },
5 | };
6 | export default config;
--------------------------------------------------------------------------------
/public/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocketseat-education/nlw-connect-react/1102be9ee77159966e9542129af40e1db9f5bba7/public/background.png
--------------------------------------------------------------------------------
/src/app/(home)/page.tsx:
--------------------------------------------------------------------------------
1 | import { Radio } from 'lucide-react'
2 | import Image from 'next/image'
3 | import logo from '../../assets/logo.svg'
4 | import { SubscriptionForm } from './subscription-form'
5 |
6 | export default function Home() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 | CodeCraft Summit 2025
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Sobre o evento
22 |
23 |
24 |
25 | AO VIVO
26 |
27 |
28 |
29 | Um evento feito por e para pessoas desenvolvedoras apaixonadas por
30 | criar soluções inovadoras e compartilhar conhecimento. Vamos
31 | mergulhar nas tendências mais recentes em desenvolvimento de
32 | software, arquitetura de sistemas e tecnologias emergentes, com
33 | palestras, workshops e hackathons.
34 |
35 |
36 | Dias 15 a 17 de março | Das 18h às 21h | Online & Gratuito
37 |
38 |
39 |
40 |
41 |
42 |
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/src/app/(home)/subscription-form.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { subscribeToEvent } from '@/http/api'
4 | import { zodResolver } from '@hookform/resolvers/zod'
5 | import { ArrowRight, Mail, User } from 'lucide-react'
6 | import { useRouter, useSearchParams } from 'next/navigation'
7 | import { useForm } from 'react-hook-form'
8 | import { z } from 'zod'
9 | import { Button } from '../components/button'
10 | import { InputField, InputIcon, InputRoot } from '../components/input'
11 |
12 | const subscriptionSchema = z.object({
13 | name: z.string().min(2, 'Digite seu nome completo'),
14 | email: z.string().email('Digite um e-mail válido'),
15 | })
16 |
17 | type SubscriptionSchema = z.infer
18 |
19 | export function SubscriptionForm() {
20 | const router = useRouter()
21 | const searchParams = useSearchParams()
22 |
23 | const {
24 | register,
25 | handleSubmit,
26 | formState: { errors },
27 | } = useForm({
28 | resolver: zodResolver(subscriptionSchema),
29 | })
30 |
31 | async function onSubscribe({ name, email }: SubscriptionSchema) {
32 | const referrer = searchParams.get('referrer')
33 | const { subscriberId } = await subscribeToEvent({ name, email, referrer })
34 |
35 | router.push(`/invite/${subscriberId}`)
36 | }
37 |
38 | return (
39 |
92 | )
93 | }
94 |
--------------------------------------------------------------------------------
/src/app/components/button.tsx:
--------------------------------------------------------------------------------
1 | import type { ComponentProps } from 'react'
2 |
3 | interface ButtonProps extends ComponentProps<'button'> {}
4 |
5 | export function Button(props: ButtonProps) {
6 | return (
7 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/components/icon-button.tsx:
--------------------------------------------------------------------------------
1 | import type { ComponentProps } from 'react'
2 | import { twMerge } from 'tailwind-merge'
3 |
4 | interface ButtonProps extends ComponentProps<'button'> {}
5 |
6 | export function IconButton({ className, ...props }: ButtonProps) {
7 | return (
8 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/components/input.tsx:
--------------------------------------------------------------------------------
1 | import { Mail } from 'lucide-react'
2 | import type { ComponentProps } from 'react'
3 |
4 | interface InputRootProps extends ComponentProps<'div'> {
5 | error?: boolean
6 | }
7 |
8 | export function InputRoot({ error = false, ...props }: InputRootProps) {
9 | return (
10 |
15 | )
16 | }
17 |
18 | interface InputIconProps extends ComponentProps<'span'> {}
19 |
20 | export function InputIcon({ ...props }: InputIconProps) {
21 | return (
22 |
26 | )
27 | }
28 |
29 | interface InputFieldProps extends ComponentProps<'input'> {}
30 |
31 | export function InputField({ ...props }: InputFieldProps) {
32 | return
33 | }
34 |
--------------------------------------------------------------------------------
/src/app/global.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 |
3 | @theme {
4 | --color-*: initial;
5 |
6 | --color-blue: #6F9DE2;
7 | --color-purple: #9871F3;
8 |
9 | --color-danger: #F05D6C;
10 |
11 | --color-gray-100: #DAE4F2;
12 | --color-gray-200: #C8D0DA;
13 | --color-gray-300: #95A1B1;
14 | --color-gray-400: #6F7D90;
15 | --color-gray-500: #2A313C;
16 | --color-gray-600: #21252C;
17 | --color-gray-700: #191D24;
18 | --color-gray-800: #13161B;
19 | --color-gray-900: #0F1216;
20 |
21 | --font-heading: var(--font-oxanium);
22 | --font-sans: var(--font-montserrat);
23 |
24 | --radius-xl: 0.625rem;
25 | }
--------------------------------------------------------------------------------
/src/app/invite/[subscriberId]/invite-link-input.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { Copy, Link } from 'lucide-react'
4 | import { IconButton } from '../../components/icon-button'
5 | import { InputField, InputIcon, InputRoot } from '../../components/input'
6 |
7 | interface InviteLinkInputProps {
8 | inviteLink: string
9 | }
10 |
11 | export function InviteLinkInput({ inviteLink }: InviteLinkInputProps) {
12 | function copyInviteLink() {
13 | navigator.clipboard.writeText(inviteLink)
14 | }
15 |
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/invite/[subscriberId]/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from 'next/image'
2 | import logo from '../../../assets/logo.svg'
3 |
4 | import { InviteLinkInput } from './invite-link-input'
5 | import { Ranking } from './ranking'
6 | import { Stats } from './stats'
7 |
8 | interface InvitePageProps {
9 | params: Promise<{
10 | subscriberId: string
11 | }>
12 | }
13 |
14 | export default async function InvitePage(props: InvitePageProps) {
15 | const { subscriberId } = await props.params
16 |
17 | const inviteLink = `http://localhost:3333/invites/${subscriberId}`
18 |
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 | Inscrição confirmada!
27 |
28 |
29 | Para entrar no evento, acesse o link enviado para seu e-mail.
30 |
31 |
32 |
33 |
34 |
35 |
36 | Indique e ganhe
37 |
38 |
39 | Convide mais pessoas para o evento e concorra a prêmios
40 | exclusivos! É só compartilhar o link abaixo e acompanhar as
41 | inscrições:
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/src/app/invite/[subscriberId]/ranking.tsx:
--------------------------------------------------------------------------------
1 | import Image from 'next/image'
2 |
3 | import { getRanking } from '@/http/api'
4 | import cooper from '../../../assets/medal-cooper.svg'
5 | import gold from '../../../assets/medal-gold.svg'
6 | import silver from '../../../assets/medal-silver.svg'
7 |
8 | export async function Ranking() {
9 | const { ranking } = await getRanking()
10 |
11 | return (
12 |
13 |
14 | Ranking de indicações
15 |
16 |
17 |
18 | {ranking.map((rank, index) => {
19 | const rankingPosition = index + 1
20 |
21 | return (
22 |
26 |
27 | {rankingPosition}º |{' '}
28 | {rank.name}
29 |
30 |
31 |
32 | {rank.score}
33 |
34 |
35 | {rankingPosition === 1 && (
36 |
37 | )}
38 |
39 | {rankingPosition === 2 && (
40 |
41 | )}
42 |
43 | {rankingPosition === 3 && (
44 |
45 | )}
46 |
47 | )
48 | })}
49 |
50 | {ranking.length === 0 && (
51 |
Nenhuma indicação registrada
52 | )}
53 |
54 |
55 | )
56 | }
57 |
--------------------------------------------------------------------------------
/src/app/invite/[subscriberId]/stats.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | getSubscriberInviteClicks,
3 | getSubscriberInviteCount,
4 | getSubscriberRankingPosition,
5 | } from '@/http/api'
6 | import { BadgeCheck, Medal, MousePointerClick } from 'lucide-react'
7 |
8 | interface StatsProps {
9 | subscriberId: string
10 | }
11 |
12 | export async function Stats({ subscriberId }: StatsProps) {
13 | const { count: accessCount } = await getSubscriberInviteClicks(subscriberId)
14 | const { count: inviteCount } = await getSubscriberInviteCount(subscriberId)
15 | const { position: rankingPosition } =
16 | await getSubscriberRankingPosition(subscriberId)
17 |
18 | return (
19 |
20 |
21 |
22 | {accessCount}
23 |
24 |
25 | Acessos ao link
26 |
27 |
28 |
29 |
30 |
31 |
32 | {inviteCount}
33 |
34 |
35 | Inscrições feitas
36 |
37 |
38 |
39 |
40 |
41 |
42 | {rankingPosition ? <>{rankingPosition}º> : '-'}
43 |
44 |
45 | Posição no ranking
46 |
47 |
48 |
49 |
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import './global.css'
2 |
3 | import type { Metadata } from 'next'
4 | import { Montserrat, Oxanium } from 'next/font/google'
5 |
6 | export const metadata: Metadata = {
7 | title: 'devstage',
8 | }
9 |
10 | const oxanium = Oxanium({
11 | weight: ['500', '600'],
12 | subsets: ['latin'],
13 | variable: '--font-oxanium',
14 | })
15 |
16 | const montserrat = Montserrat({
17 | weight: ['400', '600'],
18 | subsets: ['latin'],
19 | variable: '--font-montserrat',
20 | })
21 |
22 | export default function RootLayout({
23 | children,
24 | }: Readonly<{
25 | children: React.ReactNode
26 | }>) {
27 | return (
28 |
29 |
30 |
31 | {children}
32 |
33 |
34 |
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/medal-cooper.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/assets/medal-gold.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/assets/medal-silver.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/http/api.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Generated by orval v7.5.0 🍺
3 | * Do not edit manually.
4 | * NLW Connect
5 | * OpenAPI spec version: 0.1
6 | */
7 | export type SubscribeToEventBody = {
8 | name: string;
9 | email: string;
10 | /** @nullable */
11 | referrer?: string | null;
12 | };
13 |
14 | export type SubscribeToEvent201 = {
15 | subscriberId: string;
16 | };
17 |
18 | /**
19 | * @nullable
20 | */
21 | export type AccessInviteLink301 = typeof AccessInviteLink301[keyof typeof AccessInviteLink301] | null;
22 |
23 |
24 | // eslint-disable-next-line @typescript-eslint/no-redeclare
25 | export const AccessInviteLink301 = {
26 | null: 'null',
27 | } as const;
28 |
29 | export type GetRanking200RankingItem = {
30 | id: string;
31 | name: string;
32 | score: number;
33 | };
34 |
35 | export type GetRanking200 = {
36 | ranking: GetRanking200RankingItem[];
37 | };
38 |
39 | export type GetSubscriberInviteCount200 = {
40 | count: number;
41 | };
42 |
43 | export type GetSubscriberInviteClicks200 = {
44 | count: number;
45 | };
46 |
47 | export type GetSubscriberRankingPosition200 = {
48 | /** @nullable */
49 | position: number | null;
50 | };
51 |
52 |
53 |
54 | /**
55 | * @summary Subscribe to event
56 | */
57 | export const getSubscribeToEventUrl = () => {
58 |
59 |
60 | return `http://localhost:3333/subscriptions`
61 | }
62 |
63 | export const subscribeToEvent = async (subscribeToEventBody: SubscribeToEventBody, options?: RequestInit): Promise => {
64 |
65 | const res = await fetch(getSubscribeToEventUrl(),
66 | {
67 | ...options,
68 | method: 'POST',
69 | headers: { 'Content-Type': 'application/json', ...options?.headers },
70 | body: JSON.stringify(
71 | subscribeToEventBody,)
72 | }
73 | )
74 |
75 | const body = [204, 205, 304].includes(res.status) ? null : await res.text()
76 | const data: SubscribeToEvent201 = body ? JSON.parse(body) : {}
77 |
78 | return data
79 | }
80 |
81 |
82 |
83 | /**
84 | * @summary Access invite link
85 | */
86 | export const getAccessInviteLinkUrl = (subscriberId: string,) => {
87 |
88 |
89 | return `http://localhost:3333/invites/${subscriberId}`
90 | }
91 |
92 | export const accessInviteLink = async (subscriberId: string, options?: RequestInit): Promise => {
93 |
94 | const res = await fetch(getAccessInviteLinkUrl(subscriberId),
95 | {
96 | ...options,
97 | method: 'GET'
98 |
99 |
100 | }
101 | )
102 |
103 | const body = [204, 205, 304].includes(res.status) ? null : await res.text()
104 | const data: unknown = body ? JSON.parse(body) : {}
105 |
106 | return data
107 | }
108 |
109 |
110 |
111 | /**
112 | * @summary Get ranking
113 | */
114 | export const getGetRankingUrl = () => {
115 |
116 |
117 | return `http://localhost:3333/ranking`
118 | }
119 |
120 | export const getRanking = async ( options?: RequestInit): Promise => {
121 |
122 | const res = await fetch(getGetRankingUrl(),
123 | {
124 | ...options,
125 | method: 'GET'
126 |
127 |
128 | }
129 | )
130 |
131 | const body = [204, 205, 304].includes(res.status) ? null : await res.text()
132 | const data: GetRanking200 = body ? JSON.parse(body) : {}
133 |
134 | return data
135 | }
136 |
137 |
138 |
139 | /**
140 | * @summary Get subscriber ranking invites count
141 | */
142 | export const getGetSubscriberInviteCountUrl = (subscriberId: string,) => {
143 |
144 |
145 | return `http://localhost:3333/subscribers/${subscriberId}/ranking/count`
146 | }
147 |
148 | export const getSubscriberInviteCount = async (subscriberId: string, options?: RequestInit): Promise => {
149 |
150 | const res = await fetch(getGetSubscriberInviteCountUrl(subscriberId),
151 | {
152 | ...options,
153 | method: 'GET'
154 |
155 |
156 | }
157 | )
158 |
159 | const body = [204, 205, 304].includes(res.status) ? null : await res.text()
160 | const data: GetSubscriberInviteCount200 = body ? JSON.parse(body) : {}
161 |
162 | return data
163 | }
164 |
165 |
166 |
167 | /**
168 | * @summary Get subscriber ranking invites clicks count
169 | */
170 | export const getGetSubscriberInviteClicksUrl = (subscriberId: string,) => {
171 |
172 |
173 | return `http://localhost:3333/subscribers/${subscriberId}/ranking/clicks`
174 | }
175 |
176 | export const getSubscriberInviteClicks = async (subscriberId: string, options?: RequestInit): Promise => {
177 |
178 | const res = await fetch(getGetSubscriberInviteClicksUrl(subscriberId),
179 | {
180 | ...options,
181 | method: 'GET'
182 |
183 |
184 | }
185 | )
186 |
187 | const body = [204, 205, 304].includes(res.status) ? null : await res.text()
188 | const data: GetSubscriberInviteClicks200 = body ? JSON.parse(body) : {}
189 |
190 | return data
191 | }
192 |
193 |
194 |
195 | /**
196 | * @summary Get subscriber ranking position
197 | */
198 | export const getGetSubscriberRankingPositionUrl = (subscriberId: string,) => {
199 |
200 |
201 | return `http://localhost:3333/subscribers/${subscriberId}/ranking/position`
202 | }
203 |
204 | export const getSubscriberRankingPosition = async (subscriberId: string, options?: RequestInit): Promise => {
205 |
206 | const res = await fetch(getGetSubscriberRankingPositionUrl(subscriberId),
207 | {
208 | ...options,
209 | method: 'GET'
210 |
211 |
212 | }
213 | )
214 |
215 | const body = [204, 205, 304].includes(res.status) ? null : await res.text()
216 | const data: GetSubscriberRankingPosition200 = body ? JSON.parse(body) : {}
217 |
218 | return data
219 | }
220 |
221 |
222 |
223 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
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": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------