├── 6_nextgram
├── public
│ ├── uploads
│ │ ├── undefined
│ │ ├── 1713189743.png
│ │ ├── 1714153884.png
│ │ ├── Thumb Discord.png
│ │ ├── Thumb Discord (1).png
│ │ ├── Comprovacao udemy 3.png
│ │ ├── Capa plataforma (10).png
│ │ ├── Capa de Ebook Amazon KDP (3).png
│ │ ├── WhatsApp Image 2024-04-29 at 16.05.57.jpeg
│ │ ├── DALL·E 2024-04-18 15.24.44 - A wide-angle view of a cityscape at night, subtly modern yet traditional in style. The city features high-rise buildings with lit windows and a calm, .webp
│ │ └── DALL·E 2024-04-18 15.44.07 - A wide-angle view of a cityscape at night, designed with a darker and more mysterious tone. The city features high-rise buildings with fewer lights, c.webp
│ ├── vercel.svg
│ └── next.svg
├── .env
├── .eslintrc.json
├── src
│ ├── middleware.ts
│ ├── app
│ │ ├── globals.css
│ │ ├── favicon.ico
│ │ ├── api
│ │ │ └── [...nextauth]
│ │ │ │ └── route.ts
│ │ ├── post
│ │ │ └── new
│ │ │ │ └── page.tsx
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── profile
│ │ │ └── page.tsx
│ │ ├── signin
│ │ │ └── page.tsx
│ │ └── my-posts
│ │ │ └── page.tsx
│ ├── components
│ │ ├── Label.tsx
│ │ ├── ButtonLink.tsx
│ │ ├── Button.tsx
│ │ ├── FlashMessage.tsx
│ │ ├── Post
│ │ │ ├── LikeButton.tsx
│ │ │ └── CommentModal.tsx
│ │ ├── CreatePostForm.tsx
│ │ ├── ImagePreview.tsx
│ │ ├── ProfileForm.tsx
│ │ ├── Navbar.tsx
│ │ └── Post.tsx
│ └── actions.ts
├── .env.example
├── prisma
│ ├── dev.db
│ ├── migrations
│ │ ├── migration_lock.toml
│ │ └── 20240516213823_
│ │ │ └── migration.sql
│ └── schema.prisma
├── postcss.config.mjs
├── types
│ ├── next-auth.d.ts
│ ├── Like.ts
│ ├── Comment.ts
│ ├── User.ts
│ └── Post.ts
├── next.config.mjs
├── tailwind.config.ts
├── .gitignore
├── tsconfig.json
├── package.json
├── auth.ts
└── README.md
├── 5_auth
├── .eslintrc.json
├── src
│ ├── middleware.ts
│ ├── app
│ │ ├── favicon.ico
│ │ ├── api
│ │ │ └── [...nextauth]
│ │ │ │ └── route.ts
│ │ ├── middleware
│ │ │ └── page.tsx
│ │ ├── server
│ │ │ └── page.tsx
│ │ ├── client
│ │ │ └── page.tsx
│ │ ├── page.tsx
│ │ ├── globals.css
│ │ └── layout.tsx
│ └── components
│ │ └── Navbar.tsx
├── prisma
│ ├── dev.db
│ ├── migrations
│ │ ├── migration_lock.toml
│ │ └── 20240510151330_
│ │ │ └── migration.sql
│ └── schema.prisma
├── .env.example
├── next.config.mjs
├── postcss.config.mjs
├── .env
├── .gitignore
├── tailwind.config.ts
├── public
│ ├── vercel.svg
│ └── next.svg
├── tsconfig.json
├── package.json
├── auth.ts
└── README.md
├── 3_estilos
├── .eslintrc.json
├── src
│ ├── app
│ │ ├── favicon.ico
│ │ ├── page.module.css
│ │ ├── globals.css
│ │ ├── layout.js
│ │ └── page.js
│ ├── components
│ │ ├── Button.module.scss
│ │ ├── Button.jsx
│ │ ├── MyComponent.jsx
│ │ ├── Container.jsx
│ │ └── CustomButton.jsx
│ ├── css
│ │ └── styles.sass
│ └── lib
│ │ └── registry.jsx
├── jsconfig.json
├── postcss.config.js
├── next.config.mjs
├── .gitignore
├── tailwind.config.js
├── package.json
├── public
│ ├── vercel.svg
│ └── next.svg
└── README.md
├── 4_data_fetching
├── .eslintrc.json
├── src
│ ├── app
│ │ ├── globals.css
│ │ ├── favicon.ico
│ │ ├── todos
│ │ │ ├── [id]
│ │ │ │ ├── loading.js
│ │ │ │ ├── edit
│ │ │ │ │ ├── error.js
│ │ │ │ │ └── page.js
│ │ │ │ ├── not-found.js
│ │ │ │ └── page.js
│ │ │ └── create
│ │ │ │ └── page.js
│ │ ├── not-found.js
│ │ ├── layout.js
│ │ └── page.js
│ ├── db.js
│ ├── components
│ │ ├── Button.js
│ │ ├── Checkbox.js
│ │ ├── Header.js
│ │ └── TodoForm.js
│ └── actions.js
├── prisma
│ ├── dev.db
│ ├── migrations
│ │ ├── migration_lock.toml
│ │ └── 20240312114821_
│ │ │ └── migration.sql
│ └── schema.prisma
├── jsconfig.json
├── postcss.config.js
├── next.config.mjs
├── .env
├── .gitignore
├── tailwind.config.js
├── public
│ ├── vercel.svg
│ └── next.svg
├── package.json
├── tsconfig.json
└── README.md
├── 7_animes_list
├── .eslintrc.json
├── app
│ ├── favicon.ico
│ ├── globals.css
│ ├── page.tsx
│ ├── most-rated
│ │ └── page.tsx
│ ├── most-popular
│ │ └── page.tsx
│ ├── action.tsx
│ └── layout.tsx
├── public
│ ├── weapon.png
│ ├── vercel.svg
│ ├── next.svg
│ └── logo.svg
├── postcss.config.js
├── components
│ ├── MotionDiv.tsx
│ ├── Title.tsx
│ ├── Content.tsx
│ ├── Container.tsx
│ ├── LoadMore.tsx
│ ├── Footer.tsx
│ ├── Card.tsx
│ └── Navbar.tsx
├── types
│ └── Anime.ts
├── next.config.js
├── README.md
├── .gitignore
├── tailwind.config.ts
├── .vscode
│ └── settings.json
├── tsconfig.json
└── package.json
└── 2_paginas_e_navegacao
├── .eslintrc.json
├── jsconfig.json
├── next.config.mjs
├── postcss.config.js
├── src
├── app
│ ├── favicon.ico
│ ├── dashboard
│ │ ├── page.js
│ │ └── layout.js
│ ├── produtos
│ │ └── [categoria]
│ │ │ ├── [produto]
│ │ │ └── page.js
│ │ │ └── page.js
│ ├── posts
│ │ ├── [id]
│ │ │ └── page.js
│ │ └── page.js
│ ├── sobre
│ │ └── page.js
│ ├── exemplo
│ │ └── page.js
│ ├── profile
│ │ └── page.js
│ ├── globals.css
│ ├── layout.js
│ └── page.js
└── components
│ ├── Footer.jsx
│ ├── BotaoRedirect.jsx
│ └── Nav.jsx
├── .gitignore
├── package.json
├── tailwind.config.js
├── public
├── vercel.svg
└── next.svg
└── README.md
/6_nextgram/public/uploads/undefined:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/5_auth/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/5_auth/src/middleware.ts:
--------------------------------------------------------------------------------
1 | export { auth as middleware } from "auth";
2 |
--------------------------------------------------------------------------------
/3_estilos/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/6_nextgram/.env:
--------------------------------------------------------------------------------
1 | AUTH_GOOGLE_ID=x
2 | AUTH_GOOGLE_SECRET=x
3 | AUTH_SECRET=x
--------------------------------------------------------------------------------
/6_nextgram/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/6_nextgram/src/middleware.ts:
--------------------------------------------------------------------------------
1 | export { auth as middleware } from "auth";
2 |
--------------------------------------------------------------------------------
/4_data_fetching/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/6_nextgram/.env.example:
--------------------------------------------------------------------------------
1 | AUTH_GOOGLE_ID=x
2 | AUTH_GOOGLE_SECRET=x
3 | AUTH_SECRET=x
--------------------------------------------------------------------------------
/7_animes_list/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/6_nextgram/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/4_data_fetching/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/5_auth/prisma/dev.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/5_auth/prisma/dev.db
--------------------------------------------------------------------------------
/5_auth/.env.example:
--------------------------------------------------------------------------------
1 | AUTH_GOOGLE_ID=ID
2 | AUTH_GOOGLE_SECRET=SECRET
3 | AUTH_SECRET=sadhuihuaydvnncxnryeuqo3$8
--------------------------------------------------------------------------------
/6_nextgram/prisma/dev.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/prisma/dev.db
--------------------------------------------------------------------------------
/5_auth/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/5_auth/src/app/favicon.ico
--------------------------------------------------------------------------------
/3_estilos/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/3_estilos/src/app/favicon.ico
--------------------------------------------------------------------------------
/4_data_fetching/prisma/dev.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/4_data_fetching/prisma/dev.db
--------------------------------------------------------------------------------
/4_data_fetching/src/db.js:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from "@prisma/client";
2 |
3 | export const db = new PrismaClient();
4 |
--------------------------------------------------------------------------------
/7_animes_list/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/7_animes_list/app/favicon.ico
--------------------------------------------------------------------------------
/3_estilos/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/5_auth/src/app/api/[...nextauth]/route.ts:
--------------------------------------------------------------------------------
1 | import { handlers } from "auth";
2 |
3 | export const { GET, POST } = handlers;
4 |
--------------------------------------------------------------------------------
/6_nextgram/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/src/app/favicon.ico
--------------------------------------------------------------------------------
/7_animes_list/public/weapon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/7_animes_list/public/weapon.png
--------------------------------------------------------------------------------
/5_auth/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/6_nextgram/src/app/api/[...nextauth]/route.ts:
--------------------------------------------------------------------------------
1 | import { handlers } from "auth";
2 |
3 | export const { GET, POST } = handlers;
4 |
--------------------------------------------------------------------------------
/3_estilos/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/4_data_fetching/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/4_data_fetching/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/4_data_fetching/src/app/favicon.ico
--------------------------------------------------------------------------------
/7_animes_list/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/4_data_fetching/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/2_paginas_e_navegacao/src/app/favicon.ico
--------------------------------------------------------------------------------
/6_nextgram/public/uploads/1713189743.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/public/uploads/1713189743.png
--------------------------------------------------------------------------------
/6_nextgram/public/uploads/1714153884.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/public/uploads/1714153884.png
--------------------------------------------------------------------------------
/6_nextgram/public/uploads/Thumb Discord.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/public/uploads/Thumb Discord.png
--------------------------------------------------------------------------------
/7_animes_list/components/MotionDiv.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { motion } from "framer-motion";
4 |
5 | export const MotionDiv = motion.div;
6 |
--------------------------------------------------------------------------------
/6_nextgram/public/uploads/Thumb Discord (1).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/public/uploads/Thumb Discord (1).png
--------------------------------------------------------------------------------
/6_nextgram/public/uploads/Comprovacao udemy 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/public/uploads/Comprovacao udemy 3.png
--------------------------------------------------------------------------------
/4_data_fetching/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | distDir: "build",
4 | };
5 |
6 | export default nextConfig;
7 |
--------------------------------------------------------------------------------
/6_nextgram/public/uploads/Capa plataforma (10).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/public/uploads/Capa plataforma (10).png
--------------------------------------------------------------------------------
/5_auth/prisma/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "sqlite"
--------------------------------------------------------------------------------
/6_nextgram/prisma/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "sqlite"
--------------------------------------------------------------------------------
/7_animes_list/components/Title.tsx:
--------------------------------------------------------------------------------
1 | export const Title = ({ title }: { title: string }) => {
2 | return
{title} ;
3 | };
4 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/dashboard/page.js:
--------------------------------------------------------------------------------
1 | export default function DashboardPage() {
2 | return (
3 |
4 |
Dashboard
5 |
6 | );
7 | }
8 |
--------------------------------------------------------------------------------
/4_data_fetching/prisma/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "sqlite"
--------------------------------------------------------------------------------
/6_nextgram/public/uploads/Capa de Ebook Amazon KDP (3).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/public/uploads/Capa de Ebook Amazon KDP (3).png
--------------------------------------------------------------------------------
/4_data_fetching/src/app/todos/[id]/loading.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Loading = () => {
4 | return Carregando dado...
;
5 | };
6 |
7 | export default Loading;
8 |
--------------------------------------------------------------------------------
/5_auth/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/5_auth/src/app/middleware/page.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Page = () => {
4 | return Página protegida por middleware
;
5 | };
6 |
7 | export default Page;
8 |
--------------------------------------------------------------------------------
/3_estilos/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | compiler: {
4 | styledComponents: true,
5 | },
6 | };
7 |
8 | export default nextConfig;
9 |
--------------------------------------------------------------------------------
/3_estilos/src/app/page.module.css:
--------------------------------------------------------------------------------
1 | .heading_module {
2 | color: #321;
3 | background-color: #abc;
4 | font-size: 2rem;
5 | }
6 |
7 | .container p {
8 | color: purple;
9 | }
10 |
--------------------------------------------------------------------------------
/6_nextgram/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/6_nextgram/public/uploads/WhatsApp Image 2024-04-29 at 16.05.57.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/public/uploads/WhatsApp Image 2024-04-29 at 16.05.57.jpeg
--------------------------------------------------------------------------------
/3_estilos/src/components/Button.module.scss:
--------------------------------------------------------------------------------
1 | $button-color: blueviolet;
2 |
3 | .button {
4 | background-color: $button-color;
5 | color: #fff;
6 | padding: 10px;
7 | border-radius: 5px;
8 | }
9 |
--------------------------------------------------------------------------------
/7_animes_list/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | background-color: #000;
7 | }
8 |
9 | :root {
10 | color-scheme: dark;
11 | }
12 |
--------------------------------------------------------------------------------
/3_estilos/src/components/Button.jsx:
--------------------------------------------------------------------------------
1 | import styles from "./Button.module.scss";
2 |
3 | function Button() {
4 | return Click me ;
5 | }
6 |
7 | export default Button;
8 |
--------------------------------------------------------------------------------
/4_data_fetching/src/components/Button.js:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | const Button = ({ children, className }) => {
4 | return {children} ;
5 | };
6 |
7 | export default Button;
8 |
--------------------------------------------------------------------------------
/3_estilos/src/components/MyComponent.jsx:
--------------------------------------------------------------------------------
1 | export default function MyComponent() {
2 | return (
3 |
4 | Next com Tailwind CSS!
5 |
6 | );
7 | }
8 |
--------------------------------------------------------------------------------
/6_nextgram/types/next-auth.d.ts:
--------------------------------------------------------------------------------
1 | import NextAuth from "next-auth";
2 |
3 | declare module "next-auth" {
4 | interface Session {
5 | user: {
6 | userId: string | undefined;
7 | } & DefaultSession["user"];
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/components/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Footer = () => {
4 | return (
5 |
8 | );
9 | };
10 |
11 | export default Footer;
12 |
--------------------------------------------------------------------------------
/7_animes_list/types/Anime.ts:
--------------------------------------------------------------------------------
1 | export type Anime = {
2 | id: string;
3 | name: string;
4 | image: {
5 | original: string;
6 | };
7 | kind: string;
8 | episodes: number;
9 | episodes_aired: number;
10 | score: string;
11 | };
12 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/dashboard/layout.js:
--------------------------------------------------------------------------------
1 | export default function DashboardLayout({ children }) {
2 | return (
3 |
4 | Links do Admin
5 |
6 | {children}
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/4_data_fetching/src/app/todos/[id]/edit/error.js:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | const ErrorShowTodo = () => {
4 | return (
5 | Infelizmente ocorreu um problema, tente novamente mais tarde!
6 | );
7 | };
8 |
9 | export default ErrorShowTodo;
10 |
--------------------------------------------------------------------------------
/6_nextgram/types/Like.ts:
--------------------------------------------------------------------------------
1 | import { User } from "./User";
2 | import { Post } from "./Post";
3 |
4 | export interface Like {
5 | id: string;
6 | userId: string;
7 | postId: string;
8 | createdAt: Date;
9 | user?: User;
10 | post?: Post;
11 | }
12 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/produtos/[categoria]/[produto]/page.js:
--------------------------------------------------------------------------------
1 | export default function ProdutoPage({ params }) {
2 | return (
3 |
4 |
Produto: {params.produto}
5 |
Categoria: {params.categoria}
6 |
7 | );
8 | }
9 |
--------------------------------------------------------------------------------
/6_nextgram/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | swcMinify: true,
5 | images: {
6 | domains: ["lh3.googleusercontent.com"],
7 | },
8 | };
9 |
10 | export default nextConfig;
11 |
--------------------------------------------------------------------------------
/3_estilos/src/css/styles.sass:
--------------------------------------------------------------------------------
1 | // Definindo variáveis
2 | $primary-color: #A12
3 | $padding: 16px
4 |
5 | // Utilizando variáveis e aninhamento
6 | .main-container
7 | padding: $padding
8 | background-color: $primary-color
9 |
10 | .header
11 | color: white
12 |
13 |
--------------------------------------------------------------------------------
/7_animes_list/components/Content.tsx:
--------------------------------------------------------------------------------
1 | export const Content = ({ children }: { children: React.ReactNode }) => {
2 | return (
3 |
6 | );
7 | };
8 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/posts/[id]/page.js:
--------------------------------------------------------------------------------
1 | const Post = ({ params }) => {
2 | const id = params.id;
3 |
4 | return (
5 |
6 |
Post: {id}
7 |
Conteúdo do post com o ID: {id}
8 |
9 | );
10 | };
11 |
12 | export default Post;
13 |
--------------------------------------------------------------------------------
/7_animes_list/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | images: {
4 | remotePatterns: [
5 | {
6 | protocol: "https",
7 | hostname: "*",
8 | },
9 | ],
10 | },
11 | };
12 |
13 | module.exports = nextConfig;
14 |
--------------------------------------------------------------------------------
/4_data_fetching/src/app/not-found.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import React from "react";
3 |
4 | const NotFound = () => {
5 | return (
6 |
7 |
Página não encontrada!
8 |
Voltar
9 |
10 | );
11 | };
12 |
13 | export default NotFound;
14 |
--------------------------------------------------------------------------------
/6_nextgram/types/Comment.ts:
--------------------------------------------------------------------------------
1 | import { User } from "./User";
2 | import { Post } from "./Post";
3 |
4 | export interface Comment {
5 | id: string;
6 | userId: string;
7 | postId: string;
8 | content: string;
9 | createdAt: Date;
10 | updatedAt: Date;
11 | user: User;
12 | post?: Post;
13 | }
14 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/sobre/page.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function SobrePage() {
4 | return (
5 |
6 |
Página Sobre
7 |
Esta é a página sobre.
8 |
Voltar para a Página Inicial
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/4_data_fetching/src/app/todos/[id]/not-found.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import React from "react";
3 |
4 | const NotFound = () => {
5 | return (
6 |
7 |
Todo não encontrado!
8 |
Voltar
9 |
10 | );
11 | };
12 |
13 | export default NotFound;
14 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/produtos/[categoria]/page.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function CategoriaPage({ params }) {
4 | return (
5 |
6 |
Categoria: {params.categoria}
7 | Produto A
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/3_estilos/src/components/Container.jsx:
--------------------------------------------------------------------------------
1 | // Importando o arquivo Sass em um componente
2 | import "@/css/styles.sass";
3 |
4 | export default function Container() {
5 | return (
6 |
7 |
Cabeçalho
8 |
Conteúdo principal
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/3_estilos/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | h1 {
6 | font-size: 20px;
7 | background-color: #333;
8 | color: #fff;
9 | padding: 10px;
10 | }
11 |
12 | .p-global {
13 | color: red;
14 | }
15 |
16 | #heading-global {
17 | background-color: #383234;
18 | color: #ff1;
19 | }
20 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/components/BotaoRedirect.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useRouter } from "next/navigation";
4 |
5 | export default function BotaoRedirect() {
6 | const router = useRouter();
7 |
8 | return (
9 | router.push("/dashboard")}>
10 | Dashboard
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/5_auth/src/app/server/page.tsx:
--------------------------------------------------------------------------------
1 | import { auth } from "auth";
2 | import React from "react";
3 |
4 | const page = async () => {
5 | const session = await auth();
6 |
7 | if (!session || !session.user) return Você precisa estar autenticado!
;
8 |
9 | return Esta é uma página de server components protegida.
;
10 | };
11 |
12 | export default page;
13 |
--------------------------------------------------------------------------------
/4_data_fetching/prisma/migrations/20240312114821_/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "Todo" (
3 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4 | "titulo" TEXT NOT NULL,
5 | "descricao" TEXT,
6 | "status" TEXT NOT NULL DEFAULT 'pendente',
7 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
8 | "updatedAt" DATETIME NOT NULL
9 | );
10 |
--------------------------------------------------------------------------------
/6_nextgram/types/User.ts:
--------------------------------------------------------------------------------
1 | import { Like } from "./Like";
2 | import { Post } from "./Post";
3 |
4 | export interface User {
5 | id: string;
6 | name: string | null;
7 | email: string | null;
8 | emailVerified: Date | null;
9 | image: string | null;
10 | createdAt: Date;
11 | updatedAt: Date;
12 | posts?: Post[];
13 | likes?: Like[];
14 | comments?: Comment[];
15 | }
16 |
--------------------------------------------------------------------------------
/4_data_fetching/src/components/Checkbox.js:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | const Checkbox = ({ checked }) => {
4 | return (
5 | e.target.form.requestSubmit()}
9 | className="form-checkbox h-5 w-5"
10 | style={{ marginTop: "0.5rem" }}
11 | />
12 | );
13 | };
14 |
15 | export default Checkbox;
16 |
--------------------------------------------------------------------------------
/6_nextgram/types/Post.ts:
--------------------------------------------------------------------------------
1 | import { User } from "./User";
2 | import { Like } from "./Like";
3 | import { Comment } from "./Comment";
4 |
5 | export interface Post {
6 | id: string;
7 | imageUrl: string;
8 | caption?: string | null;
9 | userId: string;
10 | createdAt: Date;
11 | updatedAt: Date;
12 | user: User;
13 | likes?: Like[] | [];
14 | comments?: Comment[] | [];
15 | }
16 |
--------------------------------------------------------------------------------
/6_nextgram/src/components/Label.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentProps } from "react";
2 |
3 | type LabelProps = ComponentProps<"label"> & {
4 | text: string;
5 | };
6 |
7 | const Label: React.FC = ({ text, ...props }) => {
8 | return (
9 |
10 | {text}
11 |
12 | );
13 | };
14 |
15 | export default Label;
16 |
--------------------------------------------------------------------------------
/4_data_fetching/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | const Header = () => {
4 | return (
5 |
6 |
7 | Lista de Tarefas
8 | Criar Tarefa
9 |
10 |
11 | );
12 | };
13 |
14 | export default Header;
15 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/exemplo/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useSearchParams } from "next/navigation";
4 |
5 | const Exemplo = () => {
6 | const searchParams = useSearchParams();
7 |
8 | const param = searchParams.get("parametro");
9 |
10 | return (
11 |
12 |
Página de Exemplo
13 |
O parâmetro recebido é: {param}
14 |
15 | );
16 | };
17 |
18 | export default Exemplo;
19 |
--------------------------------------------------------------------------------
/7_animes_list/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { fetchAnime } from "./action";
2 |
3 | import { Container } from "@/components/Container";
4 | import { Content } from "@/components/Content";
5 |
6 | async function Home() {
7 | const data = await fetchAnime(1, "name");
8 |
9 | return (
10 |
11 | {data}
12 |
13 | );
14 | }
15 |
16 | export default Home;
17 |
--------------------------------------------------------------------------------
/5_auth/src/app/client/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useSession } from "next-auth/react";
4 | import React from "react";
5 |
6 | const Page = () => {
7 | const { data: session } = useSession();
8 |
9 | if (!session || !session.user) return Você precisa estar autenticado!
;
10 |
11 | return (
12 |
13 |
Página client component protegida
14 |
15 | );
16 | };
17 |
18 | export default Page;
19 |
--------------------------------------------------------------------------------
/6_nextgram/public/uploads/DALL·E 2024-04-18 15.24.44 - A wide-angle view of a cityscape at night, subtly modern yet traditional in style. The city features high-rise buildings with lit windows and a calm, .webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/public/uploads/DALL·E 2024-04-18 15.24.44 - A wide-angle view of a cityscape at night, subtly modern yet traditional in style. The city features high-rise buildings with lit windows and a calm, .webp
--------------------------------------------------------------------------------
/6_nextgram/public/uploads/DALL·E 2024-04-18 15.44.07 - A wide-angle view of a cityscape at night, designed with a darker and more mysterious tone. The city features high-rise buildings with fewer lights, c.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/curso_nextjs/HEAD/6_nextgram/public/uploads/DALL·E 2024-04-18 15.44.07 - A wide-angle view of a cityscape at night, designed with a darker and more mysterious tone. The city features high-rise buildings with fewer lights, c.webp
--------------------------------------------------------------------------------
/7_animes_list/app/most-rated/page.tsx:
--------------------------------------------------------------------------------
1 | import { fetchAnime } from "../action";
2 |
3 | import { Container } from "@/components/Container";
4 | import { Content } from "@/components/Content";
5 |
6 | async function MostRated() {
7 | const data = await fetchAnime(1, "ranked");
8 |
9 | return (
10 |
11 | {data}
12 |
13 | );
14 | }
15 |
16 | export default MostRated;
17 |
--------------------------------------------------------------------------------
/4_data_fetching/.env:
--------------------------------------------------------------------------------
1 | # Environment variables declared in this file are automatically made available to Prisma.
2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
3 |
4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings
6 |
7 | DATABASE_URL="file:./dev.db"
8 |
--------------------------------------------------------------------------------
/6_nextgram/src/components/ButtonLink.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | type ButtonProps = {
4 | text: string;
5 | url: string;
6 | };
7 |
8 | const ButtonLink: React.FC = ({ text, url }) => {
9 | return (
10 |
14 | {text}
15 |
16 | );
17 | };
18 |
19 | export default ButtonLink;
20 |
--------------------------------------------------------------------------------
/7_animes_list/app/most-popular/page.tsx:
--------------------------------------------------------------------------------
1 | import { fetchAnime } from "../action";
2 |
3 | import { Container } from "@/components/Container";
4 | import { Content } from "@/components/Content";
5 |
6 | async function MostPopular() {
7 | const data = await fetchAnime(1, "popularity");
8 |
9 | return (
10 |
11 | {data}
12 |
13 | );
14 | }
15 |
16 | export default MostPopular;
17 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/posts/page.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function HomePage() {
4 | // Suponha que temos um array de IDs de posts
5 | const postIds = [1, 2, 3];
6 |
7 | return (
8 |
9 |
Página Inicial
10 |
11 | {postIds.map((id) => (
12 |
13 | Ver Post {id}
14 |
15 | ))}
16 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/7_animes_list/app/action.tsx:
--------------------------------------------------------------------------------
1 | "use server";
2 |
3 | import { Card } from "@/components/Card";
4 | import { Anime } from "@/types/Anime";
5 |
6 | export const fetchAnime = async (page: number, order: string) => {
7 | const response = await fetch(
8 | `https://shikimori.one/api/animes?page=${page}&limit=8&order=${order}`
9 | );
10 |
11 | const data = await response.json();
12 |
13 | return data.map((item: Anime, index: number) => (
14 |
15 | ));
16 | };
17 |
--------------------------------------------------------------------------------
/5_auth/.env:
--------------------------------------------------------------------------------
1 | # Environment variables declared in this file are automatically made available to Prisma.
2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
3 |
4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings
6 |
7 | DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
--------------------------------------------------------------------------------
/7_animes_list/components/Container.tsx:
--------------------------------------------------------------------------------
1 | import { Title } from "./Title";
2 | import { LoadMore } from "./LoadMore";
3 |
4 | type ContainerProps = {
5 | title: string;
6 | order: string;
7 | children: React.ReactNode;
8 | };
9 |
10 | export const Container = ({ title, order, children }: ContainerProps) => {
11 | return (
12 |
13 |
14 | {children}
15 |
16 |
17 | );
18 | };
19 |
--------------------------------------------------------------------------------
/5_auth/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function Home() {
4 | return (
5 |
6 |
7 | Página client component
8 | Página server component
9 | Página middleware
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/4_data_fetching/src/app/todos/[id]/page.js:
--------------------------------------------------------------------------------
1 | // 5 - pagina dinamica com request
2 | import { db } from "@/db";
3 | import { notFound } from "next/navigation";
4 |
5 | export default async function TodoShow(props) {
6 | // Para testar o loading
7 | await new Promise((a) => setTimeout(a, 2000));
8 |
9 | const id = Number(props.params.id);
10 |
11 | const todo = await db.todo.findFirst({
12 | where: { id },
13 | });
14 |
15 | console.log(todo);
16 |
17 | if (!todo) return notFound();
18 |
19 | return {todo.titulo}
;
20 | }
21 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/profile/page.js:
--------------------------------------------------------------------------------
1 | import { redirect } from "next/navigation";
2 |
3 | export default async function Profile({ params }) {
4 | const userExists = false;
5 |
6 | if (!userExists) {
7 | redirect("/"); // Redireciona para a página inicial se a condição for falsa
8 | }
9 |
10 | // Se a condição fosse verdadeira, renderizaria o perfil do usuário
11 | return (
12 |
13 |
Perfil do Usuário
14 |
Informações do usuário seriam exibidas aqui se existissem.
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/7_animes_list/README.md:
--------------------------------------------------------------------------------
1 | # Build Modern Next 14 Server Side App with Server Actions, Infinite Scroll & Framer Motion Animations
2 |
3 | 
4 |
5 | ### [🌟 Become a top 1% Next.js 14 developer in only one course](https://jsmastery.pro/next14)
6 | ### [🚀 Land your dream programming job in 6 months](https://jsmastery.pro/masterclass)
7 | ### [📙 Free Three.js Cheatsheet](https://resource.jsmastery.pro/threejs-cheatsheet)
8 | ### [🌐 Best Hosting for Your Websites](https://hostinger.com/javascript10)
9 |
10 |
--------------------------------------------------------------------------------
/3_estilos/.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.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/5_auth/.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.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/4_data_fetching/.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.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/7_animes_list/.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.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/3_estilos/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
5 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
7 | ],
8 | theme: {
9 | extend: {
10 | backgroundImage: {
11 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
12 | "gradient-conic":
13 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | };
19 |
--------------------------------------------------------------------------------
/4_data_fetching/src/app/layout.js:
--------------------------------------------------------------------------------
1 | import { Inter } from "next/font/google";
2 | import "./globals.css";
3 |
4 | const inter = Inter({ subsets: ["latin"] });
5 |
6 | import Header from "@/components/Header";
7 |
8 | export const metadata = {
9 | title: "Create Next App",
10 | description: "Generated by create next app",
11 | };
12 |
13 | export default function RootLayout({ children }) {
14 | return (
15 |
16 |
17 |
18 | {children}
19 |
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/.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.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "2_paginas_e_navegacao",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "react": "^18",
13 | "react-dom": "^18",
14 | "next": "14.1.0"
15 | },
16 | "devDependencies": {
17 | "autoprefixer": "^10.0.1",
18 | "postcss": "^8",
19 | "tailwindcss": "^3.3.0",
20 | "eslint": "^8",
21 | "eslint-config-next": "14.1.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/4_data_fetching/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
5 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
7 | ],
8 | theme: {
9 | extend: {
10 | backgroundImage: {
11 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
12 | "gradient-conic":
13 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | };
19 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
5 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
7 | ],
8 | theme: {
9 | extend: {
10 | backgroundImage: {
11 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
12 | "gradient-conic":
13 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | };
19 |
--------------------------------------------------------------------------------
/3_estilos/src/app/layout.js:
--------------------------------------------------------------------------------
1 | import { Inter } from "next/font/google";
2 | import "./globals.css";
3 | import StyledComponentsRegistry from "@/lib/registry";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata = {
8 | title: "Create Next App",
9 | description: "Generated by create next app",
10 | };
11 |
12 | export default function RootLayout({ children }) {
13 | return (
14 |
15 |
16 | {children}
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/5_auth/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13 | "gradient-conic":
14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15 | },
16 | },
17 | },
18 | plugins: [],
19 | };
20 | export default config;
21 |
--------------------------------------------------------------------------------
/6_nextgram/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13 | "gradient-conic":
14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15 | },
16 | },
17 | },
18 | plugins: [],
19 | };
20 | export default config;
21 |
--------------------------------------------------------------------------------
/3_estilos/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "3_estilos",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "next": "14.1.0",
13 | "react": "^18",
14 | "react-dom": "^18",
15 | "styled-components": "^6.1.8"
16 | },
17 | "devDependencies": {
18 | "autoprefixer": "^10.0.1",
19 | "eslint": "^8",
20 | "eslint-config-next": "14.1.0",
21 | "postcss": "^8",
22 | "sass": "^1.71.1",
23 | "tailwindcss": "^3.3.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/6_nextgram/.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.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
38 | /public/uploads
39 | /public/dev.db
40 | .env
--------------------------------------------------------------------------------
/5_auth/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/3_estilos/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/6_nextgram/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/4_data_fetching/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/6_nextgram/src/components/Button.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentProps } from "react";
2 |
3 | type ButtonProps = ComponentProps<"button"> & {
4 | text: string;
5 | danger?: boolean;
6 | };
7 |
8 | const Button: React.FC = ({ text, danger = false, ...props }) => {
9 | return (
10 |
15 | {text}
16 |
17 | );
18 | };
19 |
20 | export default Button;
21 |
--------------------------------------------------------------------------------
/7_animes_list/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/4_data_fetching/src/app/todos/[id]/edit/page.js:
--------------------------------------------------------------------------------
1 | import { editTodo, findTodoById } from "@/actions";
2 | import TodoForm from "@/components/TodoForm";
3 |
4 | import { notFound } from "next/navigation";
5 |
6 | export default async function TodoEdit(props) {
7 | const id = parseInt(props.params.id);
8 |
9 | const todo = await findTodoById(id);
10 |
11 | if (!todo) return notFound();
12 |
13 | return (
14 |
15 |
16 | Editando: {todo.titulo}
17 |
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/7_animes_list/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13 | "gradient-conic":
14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15 | hero: 'url("/hero.png")',
16 | },
17 | },
18 | },
19 | plugins: [],
20 | };
21 | export default config;
22 |
--------------------------------------------------------------------------------
/3_estilos/src/components/CustomButton.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import styled from "styled-components";
4 |
5 | // Definindo um componente de botão estilizado
6 | const MyStyledButton = styled.button`
7 | background-color: #4caf50;
8 | border: none;
9 | color: white;
10 | padding: 15px 32px;
11 | text-align: center;
12 | text-decoration: none;
13 | display: inline-block;
14 | font-size: 16px;
15 | margin: 4px 2px;
16 | cursor: pointer;
17 |
18 | &:hover {
19 | background-color: #45a049;
20 | }
21 | `;
22 |
23 | export default function CustomButton({ children }) {
24 | return {children} ;
25 | }
26 |
--------------------------------------------------------------------------------
/7_animes_list/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.defaultFormatter": "esbenp.prettier-vscode",
3 | "editor.formatOnSave": true,
4 | "editor.codeActionsOnSave": {
5 | "source.fixAll.eslint": "explicit",
6 | "source.addMissingImports": "explicit"
7 | },
8 | "[typescriptreact]": {
9 | "editor.defaultFormatter": "esbenp.prettier-vscode"
10 | },
11 | "[typescript]": {
12 | "editor.defaultFormatter": "esbenp.prettier-vscode"
13 | },
14 | "[javascript]": {
15 | "editor.defaultFormatter": "esbenp.prettier-vscode"
16 | },
17 | "typescript.tsdk": "node_modules/typescript/lib",
18 | "svg.preview.background": "transparent"
19 | }
20 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/components/Nav.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { usePathname } from "next/navigation";
4 | import Link from "next/link";
5 |
6 | export function Nav() {
7 | const pathname = usePathname();
8 |
9 | return (
10 |
11 |
12 |
13 |
14 | Home
15 |
16 |
17 |
18 |
22 | Sobre
23 |
24 |
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/4_data_fetching/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "4_data_fetching",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@prisma/client": "^5.10.2",
13 | "next": "^14.2.3",
14 | "react": "^18",
15 | "react-dom": "^18"
16 | },
17 | "devDependencies": {
18 | "@types/node": "20.11.26",
19 | "@types/react": "18.2.65",
20 | "autoprefixer": "^10.0.1",
21 | "eslint": "^8",
22 | "eslint-config-next": "14.1.1",
23 | "postcss": "^8",
24 | "prisma": "^5.10.2",
25 | "tailwindcss": "^3.3.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/7_animes_list/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
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 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/5_auth/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./src/*"],
22 | "auth": ["./auth.ts"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/7_animes_list/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "7_animes_list",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "framer-motion": "^11.2.6",
13 | "next": "14.0.3",
14 | "react": "^18",
15 | "react-dom": "^18",
16 | "react-icons": "^5.2.1"
17 | },
18 | "devDependencies": {
19 | "@types/node": "^20",
20 | "@types/react": "^18",
21 | "@types/react-dom": "^18",
22 | "autoprefixer": "^10.0.1",
23 | "eslint": "^8",
24 | "eslint-config-next": "14.0.3",
25 | "postcss": "^8",
26 | "tailwindcss": "^3.3.0",
27 | "typescript": "^5"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | /* Estilo Global */
6 | body {
7 | background-color: #121212;
8 | color: #edebeb;
9 | font-family: "Arial", sans-serif;
10 | margin: 0;
11 | padding: 0;
12 | box-sizing: border-box;
13 | display: flex;
14 | flex-direction: column;
15 | justify-content: space-around;
16 | align-items: center;
17 | height: 100vh;
18 | }
19 |
20 | h1 {
21 | font-size: 2.5em;
22 | }
23 |
24 | a {
25 | color: #2595e5;
26 | text-decoration: none;
27 | }
28 |
29 | a:hover {
30 | text-decoration: underline;
31 | }
32 |
33 | .center {
34 | text-align: center;
35 | }
36 |
37 | .active {
38 | background-color: #fff;
39 | color: #121212;
40 | }
41 |
--------------------------------------------------------------------------------
/5_auth/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --foreground-rgb: 0, 0, 0;
7 | --background-start-rgb: 214, 219, 220;
8 | --background-end-rgb: 255, 255, 255;
9 | }
10 |
11 | @media (prefers-color-scheme: dark) {
12 | :root {
13 | --foreground-rgb: 255, 255, 255;
14 | --background-start-rgb: 0, 0, 0;
15 | --background-end-rgb: 0, 0, 0;
16 | }
17 | }
18 |
19 | body {
20 | color: rgb(var(--foreground-rgb));
21 | background: linear-gradient(
22 | to bottom,
23 | transparent,
24 | rgb(var(--background-end-rgb))
25 | )
26 | rgb(var(--background-start-rgb));
27 | }
28 |
29 | @layer utilities {
30 | .text-balance {
31 | text-wrap: balance;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/6_nextgram/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./src/*"],
22 | "auth": ["./auth.ts"],
23 | "types/*": ["./types/*"]
24 | }
25 | },
26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27 | "exclude": ["node_modules"]
28 | }
29 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/layout.js:
--------------------------------------------------------------------------------
1 | import { Inter } from "next/font/google";
2 | import "./globals.css";
3 | import Footer from "@/components/Footer";
4 | import { Nav } from "@/components/Nav";
5 |
6 | const inter = Inter({ subsets: ["latin"] });
7 |
8 | export const metadata = {
9 | title: "Create Next App",
10 | description: "Generated by create next app",
11 | };
12 |
13 | export default function RootLayout({ children }) {
14 | return (
15 |
16 |
17 | {/* 5 - Layout */}
18 | Cabeçalho do site
19 | {/* 8 - Link ativo */}
20 |
21 | {children}
22 | {/* 6 - Componentes */}
23 |
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/5_auth/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "5_auth",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@auth/prisma-adapter": "^2.0.0",
13 | "@prisma/client": "^5.13.0",
14 | "next": "14.2.3",
15 | "next-auth": "^5.0.0-beta.17",
16 | "react": "^18",
17 | "react-dom": "^18"
18 | },
19 | "devDependencies": {
20 | "@types/node": "^20",
21 | "@types/react": "^18",
22 | "@types/react-dom": "^18",
23 | "eslint": "^8",
24 | "eslint-config-next": "14.2.3",
25 | "postcss": "^8",
26 | "prisma": "^5.13.0",
27 | "tailwindcss": "^3.4.1",
28 | "typescript": "^5"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/6_nextgram/src/app/post/new/page.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import CreatePostForm from "@/components/CreatePostForm";
3 | import { auth } from "auth";
4 | import { redirect } from "next/navigation";
5 |
6 | const CreatePostPage: React.FC = async () => {
7 | const session = await auth();
8 |
9 | // Não tem session, vai para home
10 | if (!session || !session.user?.email) return redirect(`/`);
11 |
12 | return (
13 |
14 |
15 | Criar novo post
16 |
17 |
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default CreatePostPage;
25 |
--------------------------------------------------------------------------------
/4_data_fetching/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | // This is your Prisma schema file,
2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema
3 |
4 | // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
5 | // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
6 |
7 | generator client {
8 | provider = "prisma-client-js"
9 | }
10 |
11 | datasource db {
12 | provider = "sqlite"
13 | url = env("DATABASE_URL")
14 | }
15 |
16 | model Todo {
17 | id Int @id @default(autoincrement())
18 | titulo String
19 | descricao String?
20 | status String @default("pendente") // Exemplos de status: "pendente", "concluído"
21 | createdAt DateTime @default(now())
22 | updatedAt DateTime @updatedAt
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/5_auth/auth.ts:
--------------------------------------------------------------------------------
1 | import type { NextAuthConfig } from "next-auth";
2 | import NextAuth from "next-auth";
3 |
4 | import google from "next-auth/providers/google";
5 |
6 | import { PrismaAdapter } from "@auth/prisma-adapter";
7 | import { PrismaClient } from "@prisma/client";
8 |
9 | const prisma = new PrismaClient();
10 |
11 | const config = {
12 | adapter: PrismaAdapter(prisma),
13 | session: { strategy: "jwt" },
14 | providers: [google],
15 | callbacks: {
16 | authorized({ request, auth }) {
17 | const { pathname } = request.nextUrl;
18 |
19 | if (pathname === "/middleware") {
20 | return !!auth;
21 | }
22 |
23 | return true;
24 | },
25 | },
26 | } satisfies NextAuthConfig;
27 |
28 | export const { handlers, auth, signIn, signOut } = NextAuth(config);
29 |
--------------------------------------------------------------------------------
/5_auth/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 | import Navbar from "@/components/Navbar";
5 | import { SessionProvider } from "next-auth/react";
6 |
7 | const inter = Inter({ subsets: ["latin"] });
8 |
9 | export const metadata: Metadata = {
10 | title: "Create Next App",
11 | description: "Generated by create next app",
12 | };
13 |
14 | export default function RootLayout({
15 | children,
16 | }: Readonly<{
17 | children: React.ReactNode;
18 | }>) {
19 | return (
20 |
21 |
22 |
23 |
24 | {children}
25 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/6_nextgram/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 | import Navbar from "@/components/Navbar";
5 | import { SessionProvider } from "next-auth/react";
6 |
7 | const inter = Inter({ subsets: ["latin"] });
8 |
9 | export const metadata: Metadata = {
10 | title: "NextGram",
11 | description: "Aplicativo clone do Instagram com Next JS",
12 | };
13 |
14 | export default function RootLayout({
15 | children,
16 | }: Readonly<{
17 | children: React.ReactNode;
18 | }>) {
19 | return (
20 |
21 |
22 |
23 |
24 | {children}
25 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/6_nextgram/src/components/FlashMessage.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 |
3 | interface FlashMessageProps {
4 | message: string;
5 | type: string;
6 | }
7 |
8 | const FlashMessage: React.FC = ({ message, type }) => {
9 | const [visible, setVisible] = useState(true);
10 |
11 | useEffect(() => {
12 | const timer = setTimeout(() => {
13 | setVisible(false);
14 | }, 3000);
15 |
16 | return () => clearTimeout(timer);
17 | }, []);
18 |
19 | if (!visible) return null;
20 |
21 | return (
22 |
27 | {message}
28 |
29 | );
30 | };
31 |
32 | export default FlashMessage;
33 |
--------------------------------------------------------------------------------
/7_animes_list/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { DM_Sans } from "next/font/google";
3 | import { Footer } from "@/components/Footer";
4 |
5 | import "./globals.css";
6 | import { Navbar } from "@/components/Navbar";
7 |
8 | const dmSans = DM_Sans({ subsets: ["latin"] });
9 |
10 | export const metadata: Metadata = {
11 | title: "Animes List",
12 | description: "Fique por dentro do mundo dos animes.",
13 | };
14 |
15 | export default function RootLayout({
16 | children,
17 | }: {
18 | children: React.ReactNode;
19 | }) {
20 | return (
21 |
22 |
23 |
24 |
25 | {children}
26 |
27 |
28 |
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/4_data_fetching/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": [
5 | "./src/*"
6 | ]
7 | },
8 | "lib": [
9 | "dom",
10 | "dom.iterable",
11 | "esnext"
12 | ],
13 | "allowJs": true,
14 | "skipLibCheck": true,
15 | "strict": false,
16 | "noEmit": true,
17 | "incremental": true,
18 | "esModuleInterop": true,
19 | "module": "esnext",
20 | "moduleResolution": "node",
21 | "resolveJsonModule": true,
22 | "isolatedModules": true,
23 | "jsx": "preserve",
24 | "plugins": [
25 | {
26 | "name": "next"
27 | }
28 | ]
29 | },
30 | "include": [
31 | "next-env.d.ts",
32 | ".next/types/**/*.ts",
33 | "**/*.ts",
34 | "**/*.tsx",
35 | "build/types/**/*.ts"
36 | ],
37 | "exclude": [
38 | "node_modules"
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/6_nextgram/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "5_auth",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@auth/prisma-adapter": "^2.0.0",
13 | "@prisma/client": "^5.13.0",
14 | "@types/react-modal": "^3.16.3",
15 | "next": "14.2.3",
16 | "next-auth": "^5.0.0-beta.17",
17 | "react": "^18",
18 | "react-dom": "^18",
19 | "react-icons": "^5.2.1",
20 | "react-modal": "^3.16.1"
21 | },
22 | "devDependencies": {
23 | "@types/node": "^20",
24 | "@types/react": "^18",
25 | "@types/react-dom": "^18",
26 | "eslint": "^8",
27 | "eslint-config-next": "14.2.3",
28 | "postcss": "^8",
29 | "prisma": "^5.13.0",
30 | "tailwindcss": "^3.4.1",
31 | "typescript": "^5"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/3_estilos/src/lib/registry.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useState } from "react";
4 | import { useServerInsertedHTML } from "next/navigation";
5 | import { ServerStyleSheet, StyleSheetManager } from "styled-components";
6 |
7 | export default function StyledComponentsRegistry({ children }) {
8 | // Only create stylesheet once with lazy initial state
9 | // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
10 | const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
11 |
12 | useServerInsertedHTML(() => {
13 | const styles = styledComponentsStyleSheet.getStyleElement();
14 | styledComponentsStyleSheet.instance.clearTag();
15 | return <>{styles}>;
16 | });
17 |
18 | if (typeof window !== "undefined") return <>{children}>;
19 |
20 | return (
21 |
22 | {children}
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/6_nextgram/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { getAllPosts } from "@/actions";
2 | import Post from "@/components/Post";
3 | import { auth } from "auth";
4 |
5 | export default async function Home() {
6 | const posts = await getAllPosts();
7 |
8 | const session = await auth();
9 |
10 | let userId = null;
11 |
12 | if (session) {
13 | userId = session.user.userId;
14 | }
15 |
16 | return (
17 |
18 |
19 | Confira os posts mais recentes
20 |
21 |
22 | {posts && posts.length > 0 ? (
23 |
24 | {posts.map((post) => (
25 |
26 | ))}
27 |
28 | ) : (
29 |
Ainda não há posts
30 | )}
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/3_estilos/src/app/page.js:
--------------------------------------------------------------------------------
1 | import MyComponent from "@/components/MyComponent";
2 | import styles from "./page.module.css";
3 | import Container from "@/components/Container";
4 | import CustomButton from "@/components/CustomButton";
5 | import Button from "@/components/Button";
6 |
7 | export default function Home() {
8 | return (
9 |
10 | {/* 1 - CSS Global */}
11 | Eu tenho CSS global
12 | Eu também
13 | Me too
14 | {/* 2 - CSS Modules */}
15 | Testando CSS Module
16 |
19 | {/* 3 - Tailwind */}
20 |
21 | {/* 4 - SASS */}
22 |
23 | {/* 5 - SASS Com CSS MOdules */}
24 |
25 | {/*6 - Styled Components */}
26 | Clique aqui!
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/7_animes_list/components/LoadMore.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { fetchAnime } from "@/app/action";
4 | import { useState } from "react";
5 |
6 | let page = 2;
7 |
8 | export const LoadMore = ({ order }: { order: string }) => {
9 | const [data, setData] = useState([]);
10 |
11 | const showMoreItems = () => {
12 | fetchAnime(page, order).then((res) => {
13 | setData([...data, ...res]);
14 | page++;
15 | });
16 | };
17 |
18 | return (
19 | <>
20 |
23 |
24 |
25 |
29 | Carregar mais
30 |
31 |
32 | >
33 | );
34 | };
35 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/src/app/page.js:
--------------------------------------------------------------------------------
1 | import BotaoRedirect from "@/components/BotaoRedirect";
2 | import Link from "next/link";
3 |
4 | export default function Home() {
5 | return (
6 |
7 |
8 | {/* 1 - Criando e navegando entre páginas */}
9 |
Aula 1
10 | Página Inicial
11 | Ir para a página Sobre
12 | {/* 2 - Páginas Dinâmicas */}
13 | Aula 2
14 | Ir para a página Posts
15 | {/* 3 - Rotas com Parâmetros */}
16 | Aula 3
17 |
18 | Abrir a Página de Exemplo com Parâmetro
19 |
20 | {/* 4 - Nested routes */}
21 | Aula 4
22 | Ir para categoria de Roupas
23 | {/* 7 - Nested layout */}
24 | Aula 7
25 | Ir para dashboard
26 | {/* 8 - useRouter */}
27 | Aula 8
28 |
29 | {/* 9 - redirect */}
30 | Aula 9
31 | Ir para Perfil
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/6_nextgram/auth.ts:
--------------------------------------------------------------------------------
1 | import type { NextAuthConfig } from "next-auth";
2 | import NextAuth from "next-auth";
3 |
4 | import google from "next-auth/providers/google";
5 |
6 | import { PrismaAdapter } from "@auth/prisma-adapter";
7 | import { PrismaClient } from "@prisma/client";
8 |
9 | import type { Provider } from "next-auth/providers";
10 |
11 | const prisma = new PrismaClient();
12 |
13 | const config = {
14 | adapter: PrismaAdapter(prisma),
15 | session: { strategy: "jwt" },
16 | providers: [google],
17 | callbacks: {
18 | session({ session, token }) {
19 | // Adiciona o ID do usuário à sessão
20 | if (token.sub) session.user.userId = token.sub;
21 | return session;
22 | },
23 | },
24 | pages: {
25 | signIn: "/signin",
26 | },
27 | } satisfies NextAuthConfig;
28 |
29 | export const { handlers, auth, signIn, signOut } = NextAuth(config);
30 |
31 | // Definir o tipo do provedor
32 | interface ProviderWithId {
33 | id: string;
34 | name: string;
35 | }
36 |
37 | // Mapeando os provedores manualmente
38 | export const providerMap = config.providers.map((provider) => {
39 | const typedProvider = provider as unknown as ProviderWithId;
40 | return { id: typedProvider.id, name: typedProvider.name };
41 | });
42 |
--------------------------------------------------------------------------------
/6_nextgram/src/app/profile/page.tsx:
--------------------------------------------------------------------------------
1 | import { getUserByEmail } from "@/actions";
2 | import ProfileForm from "@/components/ProfileForm";
3 | import { auth } from "auth";
4 | import Image from "next/image";
5 | import { redirect } from "next/navigation";
6 |
7 | export default async function UserProfile() {
8 | const session = await auth();
9 |
10 | // Não tem session, vai para home
11 | if (!session || !session.user?.email) return redirect(`/`);
12 |
13 | const user = await getUserByEmail(session.user.email);
14 |
15 | // Não tem usuário vai para home
16 | if (!user) {
17 | return redirect(`/`);
18 | }
19 |
20 | return (
21 |
22 |
23 | Perfil de {user.name}
24 |
25 |
26 | {user.image && (
27 |
28 |
35 |
36 | )}
37 |
38 |
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/5_auth/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/3_estilos/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/6_nextgram/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/4_data_fetching/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/6_nextgram/src/components/Post/LikeButton.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from "react";
4 | import { likePost } from "@/actions";
5 | import { BsFillHeartFill, BsHeart } from "react-icons/bs";
6 |
7 | interface LikeButtonProps {
8 | postId: string;
9 | initialLikesCount: number;
10 | isLiked: boolean;
11 | currentUserId?: string;
12 | }
13 |
14 | const LikeButton: React.FC = ({
15 | postId,
16 | initialLikesCount,
17 | isLiked,
18 | currentUserId,
19 | }) => {
20 | const [likesCount, setLikesCount] = React.useState(initialLikesCount);
21 | const [liked, setLiked] = React.useState(isLiked);
22 |
23 | const handleLike = async () => {
24 | if (!currentUserId) {
25 | window.location.href = "/signin";
26 | return;
27 | }
28 |
29 | await likePost(postId, currentUserId);
30 | setLiked(!liked);
31 | setLikesCount(liked ? likesCount - 1 : likesCount + 1);
32 | };
33 |
34 | return (
35 |
36 |
37 | {liked ? (
38 |
39 | ) : (
40 |
41 | )}
42 |
43 | {likesCount}
44 |
45 | );
46 | };
47 |
48 | export default LikeButton;
49 |
--------------------------------------------------------------------------------
/7_animes_list/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/6_nextgram/src/app/signin/page.tsx:
--------------------------------------------------------------------------------
1 | import { signIn, providerMap } from "auth";
2 | import { BsGoogle } from "react-icons/bs";
3 |
4 | const icons = [{ name: "Google", icon: }];
5 |
6 | export default async function SignInPage() {
7 | const findIcon = (name: string) => {
8 | const icon = icons.find((item) => item.name === name);
9 | return icon?.icon ?? "";
10 | };
11 |
12 | return (
13 |
14 |
15 | Acesse ou crie sua conta com uma das opções disponíveis
16 |
17 | {Object.values(providerMap).map((provider) => (
18 |
36 | ))}
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/6_nextgram/src/components/CreatePostForm.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from "react";
4 | import { useFormState } from "react-dom";
5 | import { createPost } from "@/actions";
6 | import FlashMessage from "./FlashMessage";
7 | import ImagePreview from "./ImagePreview";
8 | import Button from "./Button";
9 | import Label from "./Label";
10 |
11 | const CreatePostForm: React.FC = () => {
12 | const [formState, formAction] = useFormState(createPost, {
13 | message: "",
14 | type: "success",
15 | });
16 |
17 | return (
18 | <>
19 | {formState.message && (
20 |
21 | )}
22 |
43 | >
44 | );
45 | };
46 |
47 | export default CreatePostForm;
48 |
--------------------------------------------------------------------------------
/5_auth/src/components/Navbar.tsx:
--------------------------------------------------------------------------------
1 | import { auth, signIn, signOut } from "auth";
2 | import Link from "next/link";
3 |
4 | async function Navbar() {
5 | const session = await auth();
6 |
7 | return (
8 |
9 |
10 | Home
11 |
12 |
13 | {session && session.user ? (
14 |
15 |
{session.user.name}
16 |
{
18 | "use server";
19 | await signOut();
20 | }}
21 | >
22 |
26 | Sair
27 |
28 |
29 |
30 | ) : (
31 |
{
33 | "use server";
34 | await signIn();
35 | }}
36 | >
37 |
41 | Entrar
42 |
43 |
44 | )}
45 |
46 |
47 | );
48 | }
49 |
50 | export default Navbar;
51 |
--------------------------------------------------------------------------------
/7_animes_list/components/Footer.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 | import { IoLogoYoutube } from "react-icons/io5";
4 | import { IoLogoLinkedin } from "react-icons/io5";
5 | import { IoLogoInstagram } from "react-icons/io5";
6 |
7 | export const Footer = () => {
8 | return (
9 |
41 | );
42 | };
43 |
--------------------------------------------------------------------------------
/3_estilos/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/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.js`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
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/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/5_auth/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/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/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
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/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/4_data_fetching/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/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.js`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
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/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/6_nextgram/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/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/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
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/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/2_paginas_e_navegacao/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/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.js`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
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/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/6_nextgram/src/components/ImagePreview.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useState } from "react";
4 | import Label from "./Label";
5 | import Image from "next/image";
6 |
7 | const ImagePreview = () => {
8 | const [imagePreview, setImagePreview] = useState(null);
9 | const [selectedImage, setSelectedImage] = useState(null);
10 |
11 | const handleImageChange = (event: React.ChangeEvent) => {
12 | const file = event.target.files?.[0];
13 | if (file) {
14 | const reader = new FileReader();
15 | reader.onloadend = () => {
16 | setImagePreview(reader.result as string);
17 | setSelectedImage(file);
18 | };
19 | reader.readAsDataURL(file);
20 | }
21 | };
22 |
23 | return (
24 |
25 | {imagePreview && (
26 |
27 |
34 |
35 | )}
36 |
37 |
45 | {selectedImage && (
46 |
47 | )}
48 |
49 | );
50 | };
51 |
52 | export default ImagePreview;
53 |
--------------------------------------------------------------------------------
/6_nextgram/src/components/ProfileForm.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from "react";
4 | import { useFormState } from "react-dom";
5 | import { updateUserProfile } from "@/actions";
6 | import FlashMessage from "./FlashMessage";
7 | import ImagePreview from "./ImagePreview";
8 | import { User } from "next-auth";
9 | import Button from "./Button";
10 | import Label from "./Label";
11 |
12 | type ProfileFormProps = {
13 | user: User;
14 | };
15 |
16 | const ProfileForm: React.FC = ({ user }) => {
17 | const [formState, formAction] = useFormState(updateUserProfile, {
18 | message: "",
19 | type: "success",
20 | });
21 |
22 | return (
23 |
24 | {formState.message && (
25 |
26 | )}
27 |
33 |
34 |
35 |
36 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | );
54 | };
55 |
56 | export default ProfileForm;
57 |
--------------------------------------------------------------------------------
/4_data_fetching/src/components/TodoForm.js:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { updateTodo } from "@/actions";
4 | import { useFormState } from "react-dom";
5 |
6 | const TodoForm = ({ todo }) => {
7 | const [formState, action] = useFormState(updateTodo, { errors: "" });
8 |
9 | return (
10 |
14 | {formState.errors ? (
15 |
16 | {formState.errors}
17 |
18 | ) : (
19 | ""
20 | )}
21 |
22 |
23 |
24 |
28 | Título
29 |
38 |
39 |
43 | Descrição
44 |
52 |
53 |
57 | Editar
58 |
59 |
60 | );
61 | };
62 |
63 | export default TodoForm;
64 |
--------------------------------------------------------------------------------
/7_animes_list/components/Card.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import { MotionDiv } from "./MotionDiv";
3 | import { FaStar } from "react-icons/fa6";
4 | import { MdOutlineVideoLibrary } from "react-icons/md";
5 | import { Anime } from "@/types/Anime";
6 |
7 | type CardProps = {
8 | anime: Anime;
9 | index: number;
10 | };
11 |
12 | const variants = {
13 | hidden: { opacity: 0 },
14 | visible: { opacity: 1 },
15 | };
16 |
17 | export const Card = ({ anime, index }: CardProps) => {
18 | return (
19 |
27 |
28 |
35 |
36 |
37 |
38 |
39 |
40 | {anime.name}
41 |
42 |
43 |
44 |
45 | {anime.kind}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | Episódios - {anime.episodes || anime.episodes_aired}
54 |
55 |
56 |
57 |
58 | Média - {anime.score}
59 |
60 |
61 |
62 |
63 | );
64 | };
65 |
--------------------------------------------------------------------------------
/6_nextgram/src/components/Navbar.tsx:
--------------------------------------------------------------------------------
1 | import { auth, signOut } from "auth";
2 | import Link from "next/link";
3 | import Image from "next/image";
4 | import { getUserByEmail } from "@/actions";
5 | import Button from "./Button";
6 | import ButtonLink from "./ButtonLink";
7 |
8 | async function Navbar() {
9 | const session = await auth();
10 | const user = await getUserByEmail(session?.user.email);
11 |
12 | return (
13 |
14 |
18 | NextGram
19 |
20 |
21 | {user ? (
22 |
23 |
{user.name}
24 | {user.image && (
25 |
32 | )}
33 |
37 | Perfil
38 |
39 |
43 | Criar postagem
44 |
45 |
49 | Minhas Postagens
50 |
51 |
{
53 | "use server";
54 | await signOut();
55 | }}
56 | >
57 |
58 |
59 |
60 | ) : (
61 |
62 | )}
63 |
64 |
65 | );
66 | }
67 |
68 | export default Navbar;
69 |
--------------------------------------------------------------------------------
/5_auth/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | datasource db {
2 | provider = "sqlite"
3 | url = "file:./dev.db"
4 | }
5 |
6 | generator client {
7 | provider = "prisma-client-js"
8 | }
9 |
10 | model User {
11 | id String @id @default(cuid())
12 | name String?
13 | email String? @unique
14 | emailVerified DateTime?
15 | image String?
16 | accounts Account[]
17 | sessions Session[]
18 | // Optional for WebAuthn support
19 | Authenticator Authenticator[]
20 |
21 | createdAt DateTime @default(now())
22 | updatedAt DateTime @updatedAt
23 | }
24 |
25 | model Account {
26 | id String @id @default(cuid())
27 | userId String
28 | type String
29 | provider String
30 | providerAccountId String
31 | refresh_token String?
32 | access_token String?
33 | expires_at Int?
34 | token_type String?
35 | scope String?
36 | id_token String?
37 | session_state String?
38 |
39 | createdAt DateTime @default(now())
40 | updatedAt DateTime @updatedAt
41 |
42 | user User @relation(fields: [userId], references: [id], onDelete: Cascade)
43 |
44 | @@unique([provider, providerAccountId])
45 | }
46 |
47 | model Session {
48 | id String @id @default(cuid())
49 | sessionToken String @unique
50 | userId String
51 | expires DateTime
52 | user User @relation(fields: [userId], references: [id], onDelete: Cascade)
53 |
54 | createdAt DateTime @default(now())
55 | updatedAt DateTime @updatedAt
56 | }
57 |
58 | model VerificationToken {
59 | identifier String
60 | token String
61 | expires DateTime
62 |
63 | @@unique([identifier, token])
64 | }
65 |
66 | // Optional for WebAuthn support
67 | model Authenticator {
68 | id String @id @default(cuid())
69 | credentialID String @unique
70 | userId String
71 | providerAccountId String
72 | credentialPublicKey String
73 | counter Int
74 | credentialDeviceType String
75 | credentialBackedUp Boolean
76 | transports String?
77 |
78 | user User @relation(fields: [userId], references: [id], onDelete: Cascade)
79 | }
--------------------------------------------------------------------------------
/6_nextgram/src/app/my-posts/page.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import Link from "next/link";
3 | import { getUserPosts, deletePost } from "@/actions";
4 | import { Post as PostType } from "types/Post";
5 | import { auth } from "auth";
6 | import { redirect } from "next/navigation";
7 | import Button from "@/components/Button";
8 | import ButtonLink from "@/components/ButtonLink";
9 | import Image from "next/image";
10 |
11 | const MyPosts: React.FC = async () => {
12 | const session = await auth();
13 |
14 | let userId = null;
15 |
16 | if (session) {
17 | userId = session.user.userId;
18 | } else {
19 | redirect("/");
20 | }
21 |
22 | const posts = await getUserPosts(userId);
23 |
24 | return (
25 |
26 |
27 | Minhas Postagens
28 |
29 | {posts.length === 0 ? (
30 |
31 |
32 | Você ainda não tem nenhuma postagem.
33 |
34 |
35 |
36 |
37 |
38 |
39 | ) : (
40 |
41 | {posts.map((post) => (
42 |
43 |
50 | {post.caption && (
51 |
{post.caption}
52 | )}
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | ))}
62 |
63 | )}
64 |
65 | );
66 | };
67 |
68 | export default MyPosts;
69 |
--------------------------------------------------------------------------------
/4_data_fetching/src/app/todos/create/page.js:
--------------------------------------------------------------------------------
1 | import { db } from "@/db.js";
2 | import { redirect } from "next/navigation";
3 |
4 | import { addTodo } from "@/actions";
5 |
6 | // 1 - Criacao do form
7 | export default async function TodoCreate() {
8 | // const addTodo = async (formData) => {
9 | // "use server";
10 |
11 | // console.log(formData);
12 |
13 | // // 2 - Inserindo dados no banco
14 | // const titulo = formData.get("titulo");
15 | // const descricao = formData.get("descricao");
16 | // const status = "pendente";
17 |
18 | // const todo = await db.todo.create({
19 | // data: {
20 | // titulo,
21 | // descricao,
22 | // status,
23 | // },
24 | // });
25 |
26 | // console.log(todo);
27 |
28 | // redirect("/");
29 | // };
30 |
31 | return (
32 |
33 |
Criar Nova Tarefa
34 |
38 |
42 | Título
43 |
51 |
52 |
56 | Descrição
57 |
64 |
65 |
69 | Criar Todo
70 |
71 |
72 |
73 | );
74 | }
75 |
--------------------------------------------------------------------------------
/6_nextgram/src/components/Post.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useState } from "react";
4 | import { Post as PostType } from "types/Post";
5 | import LikeButton from "./Post/LikeButton";
6 | import { FiMessageSquare } from "react-icons/fi";
7 | import CommentModal from "./Post/CommentModal";
8 | import Image from "next/image";
9 |
10 | interface PostProps {
11 | post: PostType;
12 | currentUserId?: string;
13 | }
14 |
15 | const Post: React.FC = ({ post, currentUserId }) => {
16 | let isLiked = false;
17 |
18 | if (post.likes) {
19 | isLiked = post.likes.some((like) => like.userId === currentUserId);
20 | }
21 |
22 | const [isCommentModalOpen, setIsCommentModalOpen] = useState(false);
23 |
24 | return (
25 |
26 |
33 | {post.caption && (
34 |
{post.caption}
35 | )}
36 |
37 | {post.user.image && (
38 |
45 | )}
46 |
{post.user.name}
47 |
48 |
49 |
55 | setIsCommentModalOpen(true)}
57 | className="ml-4 flex items-center"
58 | >
59 |
60 |
61 | {post.comments ? post.comments.length : 0}
62 |
63 |
64 |
65 |
setIsCommentModalOpen(false)}
70 | />
71 |
72 | );
73 | };
74 |
75 | export default Post;
76 |
--------------------------------------------------------------------------------
/4_data_fetching/src/actions.js:
--------------------------------------------------------------------------------
1 | "use server";
2 |
3 | import { db } from "@/db";
4 | import { revalidatePath } from "next/cache";
5 | import { redirect } from "next/navigation";
6 |
7 | export async function deleteTodo(formData) {
8 | const id = parseInt(formData.get("id"));
9 |
10 | await db.todo.delete({
11 | where: { id },
12 | });
13 |
14 | revalidatePath("/");
15 |
16 | redirect("/");
17 | }
18 |
19 | export async function addTodo(formData) {
20 | const titulo = formData.get("titulo");
21 | const descricao = formData.get("descricao");
22 | const status = "pendente";
23 |
24 | const todo = await db.todo.create({
25 | data: {
26 | titulo,
27 | descricao,
28 | status,
29 | },
30 | });
31 |
32 | console.log(todo);
33 |
34 | redirect("/");
35 | }
36 |
37 | export async function findTodoById(id) {
38 | // 11 - erro backend - error.js
39 | // throw new Error("Ops!");
40 |
41 | const todo = await db.todo.findFirst({
42 | where: { id },
43 | });
44 |
45 | return todo;
46 | }
47 |
48 | export async function updateTodo(formState, formData) {
49 | const id = parseInt(formData.get("id"));
50 | const titulo = formData.get("titulo");
51 | const descricao = formData.get("descricao");
52 |
53 | if (titulo.length < 5) {
54 | return {
55 | errors: "O título precisa de pelo menos 5 caracteres.",
56 | };
57 | }
58 |
59 | if (descricao.length < 10) {
60 | return {
61 | errors: "A descrição precisa de pelo menos 10 caracteres.",
62 | };
63 | }
64 |
65 | await db.todo.update({
66 | where: { id },
67 | data: {
68 | titulo,
69 | descricao,
70 | },
71 | });
72 |
73 | redirect("/");
74 | }
75 |
76 | export async function toggleTodoStatus(formData) {
77 | const todoId = parseInt(formData.get("id"));
78 |
79 | // Busca o todo com o ID fornecido.
80 | const todo = await db.todo.findUnique({
81 | where: {
82 | id: todoId,
83 | },
84 | });
85 |
86 | // Verifica se o todo existe; se não, lança um erro.
87 | if (!todo) {
88 | throw new Error("Todo não encontrado");
89 | }
90 |
91 | // Determina o novo status baseado no status atual.
92 | const novoStatus = todo.status === "pendente" ? "completa" : "pendente";
93 |
94 | // Atualiza o todo no banco de dados com o novo status.
95 | await db.todo.update({
96 | where: {
97 | id: todoId,
98 | },
99 | data: {
100 | status: novoStatus,
101 | },
102 | });
103 |
104 | // Redireciona o usuário para a página inicial (ou outra página conforme necessário).
105 | redirect("/");
106 | }
107 |
--------------------------------------------------------------------------------
/5_auth/prisma/migrations/20240510151330_/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "User" (
3 | "id" TEXT NOT NULL PRIMARY KEY,
4 | "name" TEXT,
5 | "email" TEXT,
6 | "emailVerified" DATETIME,
7 | "image" TEXT,
8 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
9 | "updatedAt" DATETIME NOT NULL
10 | );
11 |
12 | -- CreateTable
13 | CREATE TABLE "Account" (
14 | "id" TEXT NOT NULL PRIMARY KEY,
15 | "userId" TEXT NOT NULL,
16 | "type" TEXT NOT NULL,
17 | "provider" TEXT NOT NULL,
18 | "providerAccountId" TEXT NOT NULL,
19 | "refresh_token" TEXT,
20 | "access_token" TEXT,
21 | "expires_at" INTEGER,
22 | "token_type" TEXT,
23 | "scope" TEXT,
24 | "id_token" TEXT,
25 | "session_state" TEXT,
26 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
27 | "updatedAt" DATETIME NOT NULL,
28 | CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
29 | );
30 |
31 | -- CreateTable
32 | CREATE TABLE "Session" (
33 | "id" TEXT NOT NULL PRIMARY KEY,
34 | "sessionToken" TEXT NOT NULL,
35 | "userId" TEXT NOT NULL,
36 | "expires" DATETIME NOT NULL,
37 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
38 | "updatedAt" DATETIME NOT NULL,
39 | CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
40 | );
41 |
42 | -- CreateTable
43 | CREATE TABLE "VerificationToken" (
44 | "identifier" TEXT NOT NULL,
45 | "token" TEXT NOT NULL,
46 | "expires" DATETIME NOT NULL
47 | );
48 |
49 | -- CreateTable
50 | CREATE TABLE "Authenticator" (
51 | "id" TEXT NOT NULL PRIMARY KEY,
52 | "credentialID" TEXT NOT NULL,
53 | "userId" TEXT NOT NULL,
54 | "providerAccountId" TEXT NOT NULL,
55 | "credentialPublicKey" TEXT NOT NULL,
56 | "counter" INTEGER NOT NULL,
57 | "credentialDeviceType" TEXT NOT NULL,
58 | "credentialBackedUp" BOOLEAN NOT NULL,
59 | "transports" TEXT,
60 | CONSTRAINT "Authenticator_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
61 | );
62 |
63 | -- CreateIndex
64 | CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
65 |
66 | -- CreateIndex
67 | CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
68 |
69 | -- CreateIndex
70 | CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
71 |
72 | -- CreateIndex
73 | CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
74 |
75 | -- CreateIndex
76 | CREATE UNIQUE INDEX "Authenticator_credentialID_key" ON "Authenticator"("credentialID");
77 |
--------------------------------------------------------------------------------
/7_animes_list/components/Navbar.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Image from "next/image";
4 | import Link from "next/link";
5 | import { usePathname } from "next/navigation";
6 | import { useState } from "react";
7 | import { GiHamburgerMenu } from "react-icons/gi";
8 |
9 | type NavbarLink = {
10 | id: number;
11 | name: string;
12 | path: string;
13 | };
14 |
15 | const links: NavbarLink[] = [
16 | { id: 1, name: "Todos", path: "/" },
17 | { id: 2, name: "Mais avaliados", path: "/most-rated" },
18 | { id: 3, name: "Mais populares", path: "/most-popular" },
19 | ];
20 |
21 | export const Navbar = () => {
22 | const pathname = usePathname();
23 |
24 | const [showMenu, setShowMenu] = useState(false);
25 |
26 | return (
27 |
28 |
29 |
30 |
37 |
38 | Animes List
39 |
40 |
41 |
42 |
43 | {links.map((link) => (
44 |
45 |
54 | {link.name}
55 |
56 |
57 | ))}
58 |
59 |
60 |
61 |
62 |
setShowMenu((prev) => !prev)}>
63 |
64 |
65 |
66 |
71 |
72 | {links.map((link) => (
73 |
74 |
83 | {link.name}
84 |
85 |
86 | ))}
87 |
88 |
89 |
90 |
91 | );
92 | };
93 |
--------------------------------------------------------------------------------
/4_data_fetching/src/app/page.js:
--------------------------------------------------------------------------------
1 | import Button from "@/components/Button";
2 | import { db } from "@/db";
3 | import Link from "next/link";
4 | import { redirect } from "next/navigation";
5 |
6 | import Checkbox from "@/components/Checkbox";
7 |
8 | import { deleteTodo, toggleTodoStatus, updateTodo } from "@/actions";
9 |
10 | // Cache - Revalidando por tempo
11 | // export const revalidate = 20;
12 |
13 | // Remoção de completa de cache
14 | // export const dynamic = "force-dynamic";
15 |
16 | export default async function Home() {
17 | // 3 - Resgatando dados do banco
18 | const todos = await db.todo.findMany();
19 |
20 | console.log(todos);
21 |
22 | // 8 - componente cliente em server
23 | // async function deleteTodo(formData) {
24 | // "use server";
25 |
26 | // const id = parseInt(formData.get("id"));
27 |
28 | // await db.todo.delete({
29 | // where: { id },
30 | // });
31 |
32 | // redirect("/");
33 | // }
34 |
35 | return (
36 | <>
37 |
38 | Todos!
39 |
40 | {todos.map((todo) => (
41 |
47 |
48 |
49 |
{todo.titulo}
50 |
{todo.descricao}
51 |
52 | {/* Formulário para alternar o status */}
53 |
54 |
completar?
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
67 | Visualizar
68 |
69 |
73 | Editar
74 |
75 | {/*
76 | Excluir
77 | */}
78 |
79 |
80 |
81 | Excluir
82 |
83 |
84 |
85 |
86 | ))}
87 |
88 |
89 | >
90 | );
91 | }
92 |
--------------------------------------------------------------------------------
/6_nextgram/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | // Excluir dados do projeto antigo
2 | // npx prisma migrate dev
3 | // npx prisma generate
4 | // npx prisma migrate reset
5 |
6 | datasource db {
7 | provider = "sqlite"
8 | url = "file:./dev.db"
9 | }
10 |
11 | generator client {
12 | provider = "prisma-client-js"
13 | }
14 |
15 | model User {
16 | id String @id @default(cuid())
17 | name String?
18 | email String? @unique
19 | emailVerified DateTime?
20 | image String?
21 | accounts Account[]
22 | sessions Session[]
23 | // Optional for WebAuthn support
24 | Authenticator Authenticator[]
25 |
26 | // Relações da aplicação
27 | posts Post[]
28 | likes Like[]
29 | comments Comment[]
30 |
31 | createdAt DateTime @default(now())
32 | updatedAt DateTime @updatedAt
33 | }
34 |
35 | model Account {
36 | id String @id @default(cuid())
37 | userId String
38 | type String
39 | provider String
40 | providerAccountId String
41 | refresh_token String?
42 | access_token String?
43 | expires_at Int?
44 | token_type String?
45 | scope String?
46 | id_token String?
47 | session_state String?
48 |
49 | createdAt DateTime @default(now())
50 | updatedAt DateTime @updatedAt
51 |
52 | user User @relation(fields: [userId], references: [id], onDelete: Cascade)
53 |
54 | @@unique([provider, providerAccountId])
55 | }
56 |
57 | model Session {
58 | id String @id @default(cuid())
59 | sessionToken String @unique
60 | userId String
61 | expires DateTime
62 | user User @relation(fields: [userId], references: [id], onDelete: Cascade)
63 |
64 | createdAt DateTime @default(now())
65 | updatedAt DateTime @updatedAt
66 | }
67 |
68 | model VerificationToken {
69 | identifier String
70 | token String
71 | expires DateTime
72 |
73 | @@unique([identifier, token])
74 | }
75 |
76 | // Optional for WebAuthn support
77 | model Authenticator {
78 | id String @id @default(cuid())
79 | credentialID String @unique
80 | userId String
81 | providerAccountId String
82 | credentialPublicKey String
83 | counter Int
84 | credentialDeviceType String
85 | credentialBackedUp Boolean
86 | transports String?
87 |
88 | user User @relation(fields: [userId], references: [id], onDelete: Cascade)
89 | }
90 |
91 | model Post {
92 | id String @id @default(cuid())
93 | imageUrl String
94 | caption String?
95 | userId String
96 | user User @relation(fields: [userId], references: [id], onDelete: Cascade)
97 | likes Like[]
98 | comments Comment[]
99 | createdAt DateTime @default(now())
100 | updatedAt DateTime @updatedAt
101 | }
102 |
103 | model Like {
104 | id String @id @default(cuid())
105 | userId String
106 | postId String
107 | user User @relation(fields: [userId], references: [id], onDelete: Cascade)
108 | post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
109 | createdAt DateTime @default(now())
110 | }
111 |
112 | model Comment {
113 | id String @id @default(cuid())
114 | content String
115 | userId String
116 | postId String
117 | user User @relation(fields: [userId], references: [id], onDelete: Cascade)
118 | post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
119 | createdAt DateTime @default(now())
120 | updatedAt DateTime @updatedAt
121 | }
--------------------------------------------------------------------------------
/6_nextgram/src/components/Post/CommentModal.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useState } from "react";
4 | import Modal from "react-modal";
5 | import { Post as PostType } from "types/Post";
6 | import { addComment } from "@/actions";
7 | import FlashMessage from "../FlashMessage";
8 | import { GrClose } from "react-icons/gr";
9 | import Button from "../Button";
10 | import Image from "next/image";
11 |
12 | interface CommentModalProps {
13 | post: PostType;
14 | currentUserId?: string;
15 | isOpen: boolean;
16 | onRequestClose: () => void;
17 | }
18 |
19 | const CommentModal: React.FC = ({
20 | post,
21 | currentUserId,
22 | isOpen,
23 | onRequestClose,
24 | }) => {
25 | const [content, setContent] = useState("");
26 | const [flashMessage, setFlashMessage] = useState<{
27 | message: string;
28 | type: "error" | "success";
29 | } | null>(null);
30 |
31 | const handleAddComment = async () => {
32 | if (!currentUserId) {
33 | window.location.href = "/signin";
34 | return;
35 | }
36 |
37 | if (!content.trim()) {
38 | setFlashMessage({
39 | message: "O comentário não pode estar vazio.",
40 | type: "error",
41 | });
42 | return;
43 | }
44 |
45 | await addComment(post.id, currentUserId, content);
46 | setFlashMessage({
47 | message: "Comentário adicionado com sucesso.",
48 | type: "success",
49 | });
50 | setContent("");
51 | };
52 |
53 | return (
54 |
63 |
64 |
65 |
Comentários
66 |
70 |
71 |
72 |
73 | {flashMessage && (
74 |
78 | )}
79 |
80 | {post.comments && post.comments.length > 0 ? (
81 | post.comments.map((comment) => (
82 |
83 | {post.user.image && (
84 |
91 | )}
92 |
93 | {comment.user.name}: {comment.content}
94 |
95 |
96 | ))
97 | ) : (
98 |
Nenhum comentário ainda. Seja o primeiro a comentar!
99 | )}
100 |
101 | {currentUserId && (
102 |
103 |
setContent(e.target.value)}
107 | placeholder="Adicione um comentário"
108 | />
109 |
110 |
111 |
116 |
117 |
118 | )}
119 |
120 |
121 | );
122 | };
123 |
124 | export default CommentModal;
125 |
--------------------------------------------------------------------------------
/6_nextgram/prisma/migrations/20240516213823_/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "User" (
3 | "id" TEXT NOT NULL PRIMARY KEY,
4 | "name" TEXT,
5 | "email" TEXT,
6 | "emailVerified" DATETIME,
7 | "image" TEXT,
8 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
9 | "updatedAt" DATETIME NOT NULL
10 | );
11 |
12 | -- CreateTable
13 | CREATE TABLE "Account" (
14 | "id" TEXT NOT NULL PRIMARY KEY,
15 | "userId" TEXT NOT NULL,
16 | "type" TEXT NOT NULL,
17 | "provider" TEXT NOT NULL,
18 | "providerAccountId" TEXT NOT NULL,
19 | "refresh_token" TEXT,
20 | "access_token" TEXT,
21 | "expires_at" INTEGER,
22 | "token_type" TEXT,
23 | "scope" TEXT,
24 | "id_token" TEXT,
25 | "session_state" TEXT,
26 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
27 | "updatedAt" DATETIME NOT NULL,
28 | CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
29 | );
30 |
31 | -- CreateTable
32 | CREATE TABLE "Session" (
33 | "id" TEXT NOT NULL PRIMARY KEY,
34 | "sessionToken" TEXT NOT NULL,
35 | "userId" TEXT NOT NULL,
36 | "expires" DATETIME NOT NULL,
37 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
38 | "updatedAt" DATETIME NOT NULL,
39 | CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
40 | );
41 |
42 | -- CreateTable
43 | CREATE TABLE "VerificationToken" (
44 | "identifier" TEXT NOT NULL,
45 | "token" TEXT NOT NULL,
46 | "expires" DATETIME NOT NULL
47 | );
48 |
49 | -- CreateTable
50 | CREATE TABLE "Authenticator" (
51 | "id" TEXT NOT NULL PRIMARY KEY,
52 | "credentialID" TEXT NOT NULL,
53 | "userId" TEXT NOT NULL,
54 | "providerAccountId" TEXT NOT NULL,
55 | "credentialPublicKey" TEXT NOT NULL,
56 | "counter" INTEGER NOT NULL,
57 | "credentialDeviceType" TEXT NOT NULL,
58 | "credentialBackedUp" BOOLEAN NOT NULL,
59 | "transports" TEXT,
60 | CONSTRAINT "Authenticator_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
61 | );
62 |
63 | -- CreateTable
64 | CREATE TABLE "Post" (
65 | "id" TEXT NOT NULL PRIMARY KEY,
66 | "imageUrl" TEXT NOT NULL,
67 | "caption" TEXT,
68 | "userId" TEXT NOT NULL,
69 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
70 | "updatedAt" DATETIME NOT NULL,
71 | CONSTRAINT "Post_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
72 | );
73 |
74 | -- CreateTable
75 | CREATE TABLE "Like" (
76 | "id" TEXT NOT NULL PRIMARY KEY,
77 | "userId" TEXT NOT NULL,
78 | "postId" TEXT NOT NULL,
79 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
80 | CONSTRAINT "Like_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
81 | CONSTRAINT "Like_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post" ("id") ON DELETE CASCADE ON UPDATE CASCADE
82 | );
83 |
84 | -- CreateTable
85 | CREATE TABLE "Comment" (
86 | "id" TEXT NOT NULL PRIMARY KEY,
87 | "content" TEXT NOT NULL,
88 | "userId" TEXT NOT NULL,
89 | "postId" TEXT NOT NULL,
90 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
91 | "updatedAt" DATETIME NOT NULL,
92 | CONSTRAINT "Comment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
93 | CONSTRAINT "Comment_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post" ("id") ON DELETE CASCADE ON UPDATE CASCADE
94 | );
95 |
96 | -- CreateIndex
97 | CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
98 |
99 | -- CreateIndex
100 | CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
101 |
102 | -- CreateIndex
103 | CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
104 |
105 | -- CreateIndex
106 | CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
107 |
108 | -- CreateIndex
109 | CREATE UNIQUE INDEX "Authenticator_credentialID_key" ON "Authenticator"("credentialID");
110 |
--------------------------------------------------------------------------------
/6_nextgram/src/actions.ts:
--------------------------------------------------------------------------------
1 | "use server";
2 |
3 | import { PrismaClient } from "@prisma/client";
4 |
5 | const prisma = new PrismaClient();
6 |
7 | import { User } from "@prisma/client";
8 | import path from "path";
9 | import { promises as fs } from "fs";
10 | import { auth } from "auth";
11 | import { revalidatePath } from "next/cache";
12 | import { redirect } from "next/navigation";
13 |
14 | type FormState = {
15 | message: string;
16 | type: string;
17 | };
18 |
19 | export async function getUserProfileData(id: string): Promise {
20 | const userId = id;
21 |
22 | const user = await prisma.user.findFirst({
23 | where: { id: userId },
24 | include: {
25 | // posts: true, // Assumindo que você tem um relacionamento 'posts' no modelo de usuário
26 | },
27 | });
28 |
29 | return user;
30 | }
31 |
32 | // Resgate de usuário por email
33 | export async function getUserByEmail(
34 | email: string | null
35 | ): Promise {
36 | if (!email) return null;
37 |
38 | const user = await prisma.user.findFirst({
39 | where: { email: email },
40 | });
41 |
42 | return user;
43 | }
44 |
45 | // Atualização de perfil usuario
46 | export async function updateUserProfile(
47 | formState: FormState,
48 | formData: FormData
49 | ): Promise {
50 | const session = await auth();
51 |
52 | if (!session) {
53 | redirect("/");
54 | }
55 |
56 | const id = formData.get("id") as string;
57 | const name = formData.get("name") as string;
58 | const imageFile = formData.get("image") as File;
59 |
60 | if (session.user.userId !== id) {
61 | return { message: "Unauthorized", type: "error" };
62 | }
63 |
64 | let imageUrl = "";
65 | if (imageFile) {
66 | const uploadDir = path.join(process.cwd(), "public", "uploads");
67 | await fs.mkdir(uploadDir, { recursive: true });
68 | const filePath = path.join(uploadDir, imageFile.name);
69 | const arrayBuffer = await imageFile.arrayBuffer();
70 | await fs.writeFile(filePath, Buffer.from(arrayBuffer));
71 | imageUrl = `/uploads/${imageFile.name}`;
72 | }
73 |
74 | await prisma.user.update({
75 | where: { id },
76 | data: { name, image: imageUrl || undefined },
77 | });
78 |
79 | revalidatePath("/profile");
80 |
81 | return { message: "Perfil atualizado com sucesso!", type: "success" };
82 | }
83 |
84 | export async function createPost(
85 | formState: FormState,
86 | formData: FormData
87 | ): Promise {
88 | const session = await auth();
89 |
90 | if (!session) {
91 | redirect("/");
92 | }
93 |
94 | const caption = formData.get("caption") as string;
95 | const imageFile = formData.get("image") as File;
96 |
97 | console.log(imageFile, caption);
98 |
99 | if (!caption || imageFile.size === 0) {
100 | return { message: "Preencha o formulário!", type: "error" };
101 | }
102 |
103 | const uploadDir = path.join(process.cwd(), "public", "uploads");
104 | await fs.mkdir(uploadDir, { recursive: true });
105 | const filePath = path.join(uploadDir, imageFile.name);
106 | const arrayBuffer = await imageFile.arrayBuffer();
107 | await fs.writeFile(filePath, Buffer.from(arrayBuffer));
108 | const imageUrl = `/uploads/${imageFile.name}`;
109 |
110 | await prisma.post.create({
111 | data: {
112 | imageUrl,
113 | caption,
114 | userId: session.user.userId,
115 | },
116 | });
117 |
118 | revalidatePath("/");
119 |
120 | redirect("/");
121 | }
122 |
123 | // Resgatar todos os posts
124 | export async function getAllPosts() {
125 | return await prisma.post.findMany({
126 | orderBy: {
127 | createdAt: "desc",
128 | },
129 | include: {
130 | user: true,
131 | likes: true,
132 | comments: {
133 | include: {
134 | user: true,
135 | },
136 | },
137 | },
138 | });
139 | }
140 |
141 | // Like no post
142 | export async function likePost(postId: string, userId: string) {
143 | const session = await auth();
144 |
145 | if (!session) {
146 | redirect("/signin");
147 | }
148 |
149 | console.log(session.user.userId, userId);
150 |
151 | if (session.user.userId !== userId) {
152 | throw new Error("Unauthorized");
153 | }
154 |
155 | const existingLike = await prisma.like.findFirst({
156 | where: {
157 | postId,
158 | userId,
159 | },
160 | });
161 |
162 | if (existingLike) {
163 | await prisma.like.delete({
164 | where: {
165 | id: existingLike.id,
166 | },
167 | });
168 | } else {
169 | await prisma.like.create({
170 | data: {
171 | postId,
172 | userId,
173 | },
174 | });
175 | }
176 |
177 | revalidatePath("/");
178 | }
179 |
180 | export async function addComment(
181 | postId: string,
182 | userId: string,
183 | content: string
184 | ) {
185 | const session = await auth();
186 |
187 | if (!session) {
188 | redirect("/signin");
189 | }
190 |
191 | if (session.user.userId !== userId) {
192 | throw new Error("Unauthorized");
193 | }
194 |
195 | await prisma.comment.create({
196 | data: {
197 | postId,
198 | userId,
199 | content,
200 | },
201 | });
202 |
203 | revalidatePath("/");
204 | }
205 |
206 | // Posts do usuarios
207 | export async function getUserPosts(userId: string) {
208 | const session = await auth();
209 |
210 | if (!session) {
211 | redirect("/signin");
212 | }
213 |
214 | if (session.user.userId !== userId) {
215 | throw new Error("Unauthorized");
216 | }
217 |
218 | return await prisma.post.findMany({
219 | where: { userId },
220 | include: {
221 | user: true,
222 | likes: true,
223 | comments: true,
224 | },
225 | orderBy: {
226 | createdAt: "desc",
227 | },
228 | });
229 | }
230 |
231 | // Excluir posts
232 | export async function deletePost(formData: FormData) {
233 | const session = await auth();
234 |
235 | if (!session) {
236 | redirect("/signin");
237 | }
238 |
239 | const userId = formData.get("userId") as string;
240 | const postId = formData.get("postId") as string;
241 |
242 | console.log(userId, session.user.userId);
243 |
244 | if (session.user.userId !== userId) {
245 | throw new Error("Unauthorized");
246 | }
247 |
248 | await prisma.post.delete({
249 | where: { id: postId },
250 | });
251 |
252 | revalidatePath("/my-posts");
253 | }
254 |
--------------------------------------------------------------------------------
/7_animes_list/public/logo.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 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
--------------------------------------------------------------------------------