├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.cjs ├── public └── vite.svg ├── src ├── App.css ├── App.tsx ├── assets │ └── react.svg ├── components │ ├── Button.tsx │ └── Navbar.tsx ├── context │ └── AuthContext.tsx ├── firebase │ ├── auth.ts │ ├── config.ts │ └── db.ts ├── index.css ├── main.tsx ├── pages │ ├── HomePage.tsx │ ├── LoginPage.tsx │ ├── ProductFormPage.tsx │ └── RegisterPage.tsx └── vite-env.d.ts ├── tailwind.config.cjs ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fazt/react-firebase-ecommerce/6f22a19b55ad23f896e0c1b906759fcb41c1e4e8/README.md -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-firebase-ecommerce", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "firebase": "^9.9.4", 13 | "formik": "^2.2.9", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0", 16 | "react-router-dom": "^6.3.0" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^18.0.17", 20 | "@types/react-dom": "^18.0.6", 21 | "@vitejs/plugin-react": "^2.1.0", 22 | "autoprefixer": "^10.4.9", 23 | "postcss": "^8.4.16", 24 | "tailwindcss": "^3.1.8", 25 | "typescript": "^4.6.4", 26 | "vite": "^3.1.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fazt/react-firebase-ecommerce/6f22a19b55ad23f896e0c1b906759fcb41c1e4e8/src/App.css -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { BrowserRouter, Routes, Route } from 'react-router-dom' 2 | 3 | // Components 4 | import Navbar from './components/Navbar' 5 | import { AuthProvider } from './context/AuthContext' 6 | 7 | // Pages 8 | import HomePage from './pages/HomePage' 9 | import LoginPage from './pages/LoginPage' 10 | import ProductFormPage from './pages/ProductFormPage' 11 | import RegisterPage from './pages/RegisterPage' 12 | 13 | function App() { 14 | return ( 15 | 16 | 17 | 18 |
19 | 20 | } /> 21 | } /> 22 | } /> 23 | } /> 24 | 25 |
26 |
27 |
28 | ) 29 | } 30 | 31 | export default App -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | interface Props { 2 | children: React.ReactNode 3 | className?: string 4 | onClick?: () => void 5 | } 6 | 7 | export function Button({ children, className, onClick }: Props) { 8 | return ( 9 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /src/components/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate, Link } from 'react-router-dom' 2 | import { useAuth } from '../context/AuthContext' 3 | import { Button } from './Button' 4 | 5 | function Navbar() { 6 | const { user, logout } = useAuth() 7 | 8 | const navigate = useNavigate() 9 | 10 | return ( 11 | 61 | ) 62 | } 63 | 64 | export default Navbar 65 | -------------------------------------------------------------------------------- /src/context/AuthContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useEffect, useState } from 'react' 2 | import { signInWithGoogle, } from '../firebase/auth' 3 | import { UserInfo, onAuthStateChanged, signOut } from 'firebase/auth' 4 | import { auth } from '../firebase/config' 5 | 6 | export const AuthContext = createContext(null) 7 | 8 | export const useAuth = () => { 9 | const context = useContext(AuthContext); 10 | if (!context) { 11 | throw new Error('useAuth must be used within an AuthProvider') 12 | } 13 | return context 14 | } 15 | 16 | interface AuthProviderProps { 17 | children: React.ReactNode 18 | } 19 | 20 | export const AuthProvider = ({ children }: AuthProviderProps) => { 21 | const [user, setUser] = useState() 22 | const [isAuthenticated, setIsAuthenticated] = useState(false) 23 | 24 | const loginWithGoogle = async () => { 25 | try { 26 | const result = await signInWithGoogle() 27 | setUser(result.user) 28 | } catch (error) { 29 | console.error(error) 30 | } 31 | } 32 | 33 | const logout = async () => { 34 | await signOut(auth) 35 | } 36 | 37 | useEffect(() => { 38 | const unsubscribe = onAuthStateChanged(auth, user => { 39 | if (user) { 40 | setUser(user) 41 | setIsAuthenticated(true) 42 | } else { 43 | setUser(undefined) 44 | setIsAuthenticated(false) 45 | } 46 | }) 47 | return () => unsubscribe() 48 | }) 49 | 50 | 51 | return ( 52 | 53 | {children} 54 | 55 | ) 56 | } -------------------------------------------------------------------------------- /src/firebase/auth.ts: -------------------------------------------------------------------------------- 1 | import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth' 2 | import { auth } from './config' 3 | 4 | const provider = new GoogleAuthProvider() 5 | 6 | export const signInWithGoogle = async () => 7 | await signInWithPopup(auth, provider) -------------------------------------------------------------------------------- /src/firebase/config.ts: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp } from "firebase/app"; 3 | import {getFirestore} from 'firebase/firestore' 4 | import {getAuth} from 'firebase/auth' 5 | // TODO: Add SDKs for Firebase products that you want to use 6 | // https://firebase.google.com/docs/web/setup#available-libraries 7 | 8 | // Your web app's Firebase configuration 9 | const firebaseConfig = { 10 | // Put your firebase config here 11 | }; 12 | 13 | // Initialize Firebase 14 | const app = initializeApp(firebaseConfig); 15 | export const db = getFirestore(app) 16 | export const auth = getAuth(app) -------------------------------------------------------------------------------- /src/firebase/db.ts: -------------------------------------------------------------------------------- 1 | import { db } from './config' 2 | import { collection, addDoc } from 'firebase/firestore' 3 | 4 | interface Product { 5 | // id: string 6 | name: string 7 | price: number 8 | description: string 9 | } 10 | 11 | export const createProduct = async (product: Product) => 12 | await addDoc(collection(db, 'products'), product) -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | background: #202020; 7 | color: white; 8 | } -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /src/pages/HomePage.tsx: -------------------------------------------------------------------------------- 1 | function HomePage() { 2 | return ( 3 |
HomePage
4 | ) 5 | } 6 | 7 | export default HomePage -------------------------------------------------------------------------------- /src/pages/LoginPage.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { Button } from '../components/Button' 3 | import { useAuth } from '../context/AuthContext' 4 | import { useNavigate } from 'react-router-dom' 5 | 6 | function LoginPage() { 7 | 8 | const { loginWithGoogle, isAuthenticated } = useAuth() 9 | const navigate = useNavigate() 10 | 11 | useEffect(() => { 12 | if (isAuthenticated) { 13 | navigate('/') 14 | } 15 | }, [isAuthenticated]) 16 | 17 | 18 | return ( 19 | 22 | ) 23 | } 24 | 25 | export default LoginPage -------------------------------------------------------------------------------- /src/pages/ProductFormPage.tsx: -------------------------------------------------------------------------------- 1 | import { Formik, Form } from 'formik' 2 | import { createProduct } from '../firebase/db' 3 | 4 | function ProductFormPage() { 5 | return ( 6 | { 14 | console.log(values) 15 | try { 16 | const result = await createProduct({ 17 | name: values.name, 18 | description: values.description, 19 | price: values.price, 20 | }) 21 | console.log(result.id) 22 | } catch (error) { 23 | console.error(error) 24 | } 25 | }} 26 | > 27 | {({ handleChange }) => ( 28 |
29 | 32 | 35 | 38 | 39 | 40 |
41 | )} 42 |
43 | ) 44 | } 45 | 46 | export default ProductFormPage -------------------------------------------------------------------------------- /src/pages/RegisterPage.tsx: -------------------------------------------------------------------------------- 1 | function RegisterPage() { 2 | return ( 3 |
RegisterPage
4 | ) 5 | } 6 | 7 | export default RegisterPage -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./index.html", 5 | "./src/**/*.{js,ts,jsx,tsx}", 6 | ], 7 | theme: { 8 | extend: {}, 9 | }, 10 | plugins: [], 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------