├── .github ├── CODEOWNERS └── interface.png ├── src ├── vite-env.d.ts ├── interfaces │ └── person-data.ts ├── components │ ├── Input │ │ ├── primary-input.css │ │ └── PrimaryInput.tsx │ └── Modal │ │ └── modal.tsx ├── index.css ├── hooks │ └── useIdentityMutation.ts ├── main.tsx ├── App.css └── App.tsx ├── vite.config.ts ├── tsconfig.node.json ├── .gitignore ├── index.html ├── .eslintrc.cjs ├── tsconfig.json ├── package.json ├── LICENSE ├── public └── vite.svg └── README.md /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | @Fernanda-Kipper -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.github/interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fernanda-Kipper/membership-frontend/HEAD/.github/interface.png -------------------------------------------------------------------------------- /src/interfaces/person-data.ts: -------------------------------------------------------------------------------- 1 | export interface PersonData { 2 | firstName: string, 3 | lastName: string, 4 | email: string 5 | } -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /src/components/Input/primary-input.css: -------------------------------------------------------------------------------- 1 | .input-container { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | } 7 | 8 | .input-container .label { 9 | font-size: 16px; 10 | text-align: start; 11 | width: 100%; 12 | margin-bottom: 8px; 13 | font-weight: 500; 14 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | --border-radius-default: 12px; 11 | } 12 | 13 | * { 14 | padding: 0; 15 | margin: 0; 16 | } 17 | 18 | body { 19 | height: 100vh; 20 | width: 100vw; 21 | } -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { browser: true, es2020: true }, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:react-hooks/recommended', 7 | ], 8 | parser: '@typescript-eslint/parser', 9 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 10 | plugins: ['react-refresh'], 11 | rules: { 12 | 'react-refresh/only-export-components': 'warn', 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /src/hooks/useIdentityMutation.ts: -------------------------------------------------------------------------------- 1 | import { useMutation } from "@tanstack/react-query"; 2 | import { PersonData } from "../interfaces/person-data"; 3 | import axios from "axios"; 4 | 5 | const postData = (data: PersonData) => { 6 | return axios.post("http://localhost:3200/send", data); 7 | } 8 | 9 | export function useIdentityMutation(){ 10 | const { mutate, isLoading, isSuccess, isError } = useMutation({ 11 | mutationFn: (data: PersonData) => postData(data) 12 | }) 13 | 14 | return { 15 | mutate, 16 | isLoading, 17 | isSuccess, 18 | isError 19 | } 20 | } -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.tsx' 4 | import './index.css' 5 | import { ChakraProvider } from '@chakra-ui/react' 6 | import { QueryClientProvider, QueryClient } from '@tanstack/react-query' 7 | 8 | const queryClient = new QueryClient(); 9 | 10 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 11 | 12 | 13 | 14 | 15 | 16 | 17 | , 18 | ) 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "module": "ESNext", 6 | "skipLibCheck": true, 7 | 8 | /* Bundler mode */ 9 | "moduleResolution": "bundler", 10 | "allowImportingTsExtensions": true, 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "noEmit": true, 14 | "jsx": "react-jsx", 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true 21 | }, 22 | "include": ["src"], 23 | "references": [{ "path": "./tsconfig.node.json" }] 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Input/PrimaryInput.tsx: -------------------------------------------------------------------------------- 1 | import { ChangeEventHandler } from "react"; 2 | import { Input } from "@chakra-ui/react"; 3 | 4 | import "./primary-input.css"; 5 | 6 | interface PrimaryInputProps { 7 | name: string, 8 | value: string, 9 | onChange: ChangeEventHandler, 10 | label: string, 11 | placeholder: string 12 | } 13 | 14 | export default function PrimaryInput({ name, value, onChange, label, placeholder } : PrimaryInputProps){ 15 | return( 16 |
17 | 18 | 25 |
26 | ) 27 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "email-service-frontend", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@chakra-ui/icons": "^2.0.19", 14 | "@chakra-ui/react": "^2.6.0", 15 | "@emotion/react": "^11.10.6", 16 | "@emotion/styled": "^11.10.6", 17 | "@tanstack/react-query": "^4.29.5", 18 | "axios": "^1.4.0", 19 | "framer-motion": "^10.12.4", 20 | "react": "^18.2.0", 21 | "react-dom": "^18.2.0" 22 | }, 23 | "devDependencies": { 24 | "@types/react": "^18.0.28", 25 | "@types/react-dom": "^18.0.11", 26 | "@typescript-eslint/eslint-plugin": "^5.57.1", 27 | "@typescript-eslint/parser": "^5.57.1", 28 | "@vitejs/plugin-react": "^4.0.0", 29 | "eslint": "^8.38.0", 30 | "eslint-plugin-react-hooks": "^4.6.0", 31 | "eslint-plugin-react-refresh": "^0.3.4", 32 | "typescript": "^5.0.2", 33 | "vite": "^4.3.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Fernanda Kipper Bucoski de Sousa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Modal/modal.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { ModalOverlay, ModalBody, ModalCloseButton, ModalHeader, ModalContent, Modal, Heading, Text } from '@chakra-ui/react'; 3 | import { CheckCircleIcon } from '@chakra-ui/icons' 4 | 5 | interface ModalProps { 6 | title: string, 7 | subText: string, 8 | isVisible: boolean 9 | } 10 | 11 | export default function SuccessModal({ title, isVisible, subText } : ModalProps){ 12 | const [isOpen, setIsOpen] = useState(false); 13 | 14 | const onClose = () => setIsOpen(false); 15 | 16 | useEffect(() => { 17 | if(!isVisible) return; 18 | setIsOpen(true); 19 | }, [isVisible]) 20 | 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 35 | {title} 36 | {subText} 37 | 38 | 39 | 40 | 41 | 42 | ) 43 | } -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | margin: 0 auto; 3 | text-align: center; 4 | } 5 | 6 | .container { 7 | height: 100vh; 8 | width: 100vw; 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | flex-direction: column; 13 | gap: 24px; 14 | margin: 0 auto; 15 | } 16 | 17 | .container form { 18 | padding: 32px; 19 | box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px; 20 | border-radius: var(--border-radius-default); 21 | height: 300px; 22 | width: 100%; 23 | max-width: 80%; 24 | } 25 | 26 | .container form .name-form-container { 27 | display: flex; 28 | align-items: center; 29 | gap: 14px; 30 | } 31 | 32 | .container .product-details { 33 | padding: 24px; 34 | background: linear-gradient(90deg, rgba(225,236,199,1) 0%, rgba(255,237,247,1) 100%); 35 | box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px; 36 | border-radius: var(--border-radius-default); 37 | height: 300px; 38 | 39 | width: 100%; 40 | max-width: 80%; 41 | } 42 | 43 | .container .product-details h2 { 44 | text-align: start; 45 | font-size: 32px; 46 | color: #242424; 47 | font-weight: 600; 48 | } 49 | 50 | .container .product-details p { 51 | color: #616161; 52 | text-align: start; 53 | } 54 | 55 | .container .product-details span { 56 | font-weight: 700; 57 | font-size: 32px; 58 | text-align: start; 59 | width: 100%; 60 | color: #3e3e3e; 61 | display: block; 62 | } 63 | 64 | @media(min-width: 768px) { 65 | .container { 66 | flex-direction: row; 67 | padding: 24px; 68 | } 69 | 70 | .container form { 71 | max-width: 50%; 72 | } 73 | 74 | .product-details { 75 | max-width: 50%; 76 | } 77 | } 78 | 79 | @media(min-width: 1080px) { 80 | .container { 81 | flex-direction: row; 82 | padding: 24px; 83 | } 84 | 85 | .container form { 86 | max-width: 600px; 87 | } 88 | 89 | .product-details { 90 | max-width: 600px !important; 91 | } 92 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Email Service Frontend 2 | 3 | ![React](https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) 4 | ![Chakra](https://img.shields.io/badge/chakra-%234ED1C5.svg?style=for-the-badge&logo=chakraui&logoColor=white) 5 | 6 | This repository contains a frontend project built using React JS and Chakra UI. This frontend project interacts with a Node.js API that enables the sending of registration confirmation emails to users, simulating a registration service or newsletter subscription. 7 | 8 | 9 | 10 | ## Features 11 | 12 | - [x] User-friendly interface built with React JS and Chakra UI. 13 | - [x] Integration with a Node.js API for sending registration emails. 14 | - [x] Email validation and confirmation functionality. 15 | - [x] Responsive design for seamless user experience on different devices. 16 | 17 | ## Prerequisites 18 | 19 | To run this project locally, you need to have the following installed: 20 | 21 | - Node.js 22 | - npm (Node Package Manager) 23 | 24 | ## Getting Started 25 | 26 | 1. Clone the repository to your local machine 27 | 28 | ```bash 29 | git clone https://github.com/Fernanda-Kipper/membership-frontend 30 | ``` 31 | 32 | 2. Navigate to the project's directory 33 | 34 | ```bash 35 | cd membership-frontend 36 | ``` 37 | 38 | 3. Install the dependencies 39 | 40 | ```bash 41 | npm install 42 | ``` 43 | 44 | 4. Start the application 45 | 46 | ```bash 47 | npm run dev 48 | ``` 49 | 50 | This will launch the application in your default browser at `http://localhost:5173` 51 | 52 | 6. Start the Node.js API 53 | 54 | - Clone the [API repository](https://github.com/Fernanda-Kipper/email-service-backend). 55 | - Follow the instructions in the API's README to start the server. 56 | 57 | ## Contributing 58 | 59 | Contributions are welcome! If you find any issues or have suggestions, please open an issue or submit a pull request in this repository. 60 | 61 | ## License 62 | 63 | This project is licensed under the [MIT License](./LICENSE). 64 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import './App.css' 3 | import PrimaryInput from './components/Input/PrimaryInput' 4 | import { Button, Spacer } from '@chakra-ui/react' 5 | import { useIdentityMutation } from './hooks/useIdentityMutation' 6 | import SuccessModal from './components/Modal/modal' 7 | 8 | function App() { 9 | const { mutate, isSuccess } = useIdentityMutation() 10 | const [email, setEmail] = useState("") 11 | const [firstName, setFirstName] = useState("") 12 | const [secondName, setSecondName] = useState("") 13 | 14 | const submit = () => { 15 | mutate({ 16 | email, 17 | firstName, 18 | lastName: secondName 19 | }) 20 | } 21 | 22 | return ( 23 |
24 | 29 |
30 |
31 | setFirstName(event.target.value)} 34 | name="firstName" 35 | label="Primeiro" 36 | placeholder="Fernanda" 37 | /> 38 | setSecondName(event.target.value)} 41 | name="secondName" 42 | label="Sobrenome" 43 | placeholder="Kipper" 44 | /> 45 |
46 | 47 | setEmail(event.target.value)} 50 | name="email" 51 | label="Digite seu e-mail" 52 | placeholder="fulano@email.com" 53 | /> 54 | 55 | 56 | 57 |
58 |

Assinatura Mensal

59 | 60 |

você irá pagar

61 | R$ 250,00 62 | 63 |

Regras: It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.

64 |
65 |
66 | ) 67 | } 68 | 69 | export default App 70 | --------------------------------------------------------------------------------