Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Virtual environment
venv/

# Python cache files
__pycache__/
*.pyc
*.pyo
*.pyd

# Logs & temp files
*.log
*.tmp
*.swp
*.swo

# IDE/Editor files
.vscode/
.idea/

# Environment variables (if needed)
.env

# OS-specific files
.DS_Store
Thumbs.db
24 changes: 24 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.routes import auth, donor, organisation

app = FastAPI()

# CORS Middleware Configuration (Allow React Frontend to Access API)
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # React frontend URL
allow_credentials=True,
allow_methods=["*"], # Allow all HTTP methods (GET, POST, PUT, DELETE, etc.)
allow_headers=["*"], # Allow all headers (e.g., Authorization, Content-Type)
)
Comment on lines +8 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Secure CORS configuration for production.

The current CORS configuration allows requests only from the development frontend. For production, consider:

  1. Moving the allowed origins to environment variables
  2. Restricting the allowed methods and headers

Apply this diff:

+from typing import List
+from app.config import settings  # Create this file to manage environment variables

 app.add_middleware(
     CORSMiddleware,
-    allow_origins=["http://localhost:3000"],  # React frontend URL
+    allow_origins=settings.ALLOWED_ORIGINS,  # List of allowed origins from environment
     allow_credentials=True,
-    allow_methods=["*"],  # Allow all HTTP methods
-    allow_headers=["*"],  # Allow all headers
+    allow_methods=["GET", "POST", "PUT", "DELETE"],
+    allow_headers=["Authorization", "Content-Type"],
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # React frontend URL
allow_credentials=True,
allow_methods=["*"], # Allow all HTTP methods (GET, POST, PUT, DELETE, etc.)
allow_headers=["*"], # Allow all headers (e.g., Authorization, Content-Type)
)
from typing import List
from app.config import settings # Create this file to manage environment variables
app.add_middleware(
CORSMiddleware,
allow_origins=settings.ALLOWED_ORIGINS, # List of allowed origins from environment
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
)


# Include API routers
app.include_router(auth.router, prefix="/auth", tags=["Auth"])
app.include_router(donor.router, prefix="/donor", tags=["Donor"])
app.include_router(organisation.router, prefix="/organisation", tags=["Organisation"])

# Test Endpoint to check if backend is running
@app.get("/")
def home():
return {"message": "Welcome to the FastAPI Backend (No Database)"}
75 changes: 75 additions & 0 deletions backend/app/routes/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
from passlib.context import CryptContext
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt

Comment on lines +1 to +7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unused imports and secure JWT configuration.

The static analysis identified unused imports. Additionally, the JWT configuration needs to be secured.

Apply this diff:

-from fastapi import APIRouter, HTTPException, Depends
+from fastapi import APIRouter, HTTPException
 from pydantic import BaseModel
 from passlib.context import CryptContext
 from datetime import datetime, timedelta
 from typing import Optional
-from jose import JWTError, jwt
+from jose import jwt
+from app.config import settings  # Create this file to manage environment variables
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
from passlib.context import CryptContext
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from passlib.context import CryptContext
from datetime import datetime, timedelta
from typing import Optional
from jose import jwt
from app.config import settings # Create this file to manage environment variables
🧰 Tools
🪛 Ruff (0.8.2)

1-1: fastapi.Depends imported but unused

Remove unused import: fastapi.Depends

(F401)


6-6: jose.JWTError imported but unused

Remove unused import: jose.JWTError

(F401)

# FastAPI Router for Authentication
router = APIRouter()

# Password Hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# In-memory storage (Temporary, use a database in production)
users = []
Comment on lines +14 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Replace in-memory storage with a database.

Using in-memory storage is not suitable for production as data will be lost when the server restarts.

Consider implementing a proper database solution:

  1. Use SQLAlchemy with PostgreSQL for relational data
  2. Use MongoDB for document-based storage
  3. Implement proper database migrations

Would you like me to help you set up a database solution?


# JWT Token Settings
SECRET_KEY = "your_secret_key_here" # Replace with a strong secret key
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30 # Token expires in 30 minutes

# Pydantic Models
class UserCreate(BaseModel):
name: str
email: str
password: str
Comment on lines +23 to +26
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation for user fields.

The current model lacks validation for email format and password strength.

Apply this diff to add validation:

+from pydantic import BaseModel, EmailStr, Field
+import re
+
+def validate_password(v: str) -> str:
+    if not re.match(r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$', v):
+        raise ValueError('Password must be at least 8 characters long and contain both letters and numbers')
+    return v
+
 class UserCreate(BaseModel):
     name: str
-    email: str
-    password: str
+    email: EmailStr
+    password: str = Field(..., min_length=8)
+
+    @validator('password')
+    def password_validation(cls, v):
+        return validate_password(v)

Committable suggestion skipped: line range outside the PR's diff.


class UserLogin(BaseModel):
email: str
password: str

class TokenData(BaseModel):
email: Optional[str] = None

# Utility Functions
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
"""Generates a JWT access token."""
to_encode = data.copy()
expire = datetime.utcnow() + (expires_delta if expires_delta else timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt

def verify_password(plain_password, hashed_password):
"""Verifies if a password matches its hashed version."""
return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
"""Hashes the password."""
return pwd_context.hash(password)

# Routes

@router.post("/register")
def register(user: UserCreate):
"""Registers a new user."""
for u in users:
if u["email"] == user.email:
raise HTTPException(status_code=400, detail="Email already exists")

hashed_password = get_password_hash(user.password)
new_user = {"name": user.name, "email": user.email, "password": hashed_password}
users.append(new_user)
return {"message": "User registered successfully"}
Comment on lines +54 to +64
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add response model and improve error handling.

The registration endpoint needs a proper response model and better error handling.

Apply this diff:

-@router.post("/register")
-def register(user: UserCreate):
+from fastapi import status
+from pydantic import BaseModel
+
+class UserResponse(BaseModel):
+    name: str
+    email: str
+
+@router.post("/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
+async def register(user: UserCreate):
     """Registers a new user."""
     for u in users:
         if u["email"] == user.email:
-            raise HTTPException(status_code=400, detail="Email already exists")
+            raise HTTPException(
+                status_code=status.HTTP_409_CONFLICT,
+                detail="A user with this email already exists"
+            )

     hashed_password = get_password_hash(user.password)
     new_user = {"name": user.name, "email": user.email, "password": hashed_password}
     users.append(new_user)
-    return {"message": "User registered successfully"}
+    return UserResponse(name=user.name, email=user.email)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@router.post("/register")
def register(user: UserCreate):
"""Registers a new user."""
for u in users:
if u["email"] == user.email:
raise HTTPException(status_code=400, detail="Email already exists")
hashed_password = get_password_hash(user.password)
new_user = {"name": user.name, "email": user.email, "password": hashed_password}
users.append(new_user)
return {"message": "User registered successfully"}
from fastapi import status, HTTPException
from pydantic import BaseModel
class UserResponse(BaseModel):
name: str
email: str
@router.post("/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def register(user: UserCreate):
"""Registers a new user."""
for u in users:
if u["email"] == user.email:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="A user with this email already exists"
)
hashed_password = get_password_hash(user.password)
new_user = {"name": user.name, "email": user.email, "password": hashed_password}
users.append(new_user)
return UserResponse(name=user.name, email=user.email)


@router.post("/login")
def login(user: UserLogin):
"""Logs in a user and returns a JWT token."""
for u in users:
if u["email"] == user.email and verify_password(user.password, u["password"]):
# Create JWT Token
access_token = create_access_token(data={"sub": user.email}, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
return {"access_token": access_token, "token_type": "bearer"}

raise HTTPException(status_code=400, detail="Invalid email or password")
Comment on lines +66 to +75
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add rate limiting and improve login security.

The login endpoint needs rate limiting to prevent brute force attacks.

Apply this diff:

+from fastapi import Request
+from fastapi.security import OAuth2PasswordRequestForm
+from slowapi import Limiter
+from slowapi.util import get_remote_address
+
+limiter = Limiter(key_func=get_remote_address)
+
-@router.post("/login")
-def login(user: UserLogin):
+@router.post("/login")
+@limiter.limit("5/minute")
+async def login(request: Request, form_data: OAuth2PasswordRequestForm = Depends()):
     """Logs in a user and returns a JWT token."""
     for u in users:
-        if u["email"] == user.email and verify_password(user.password, u["password"]):
+        if u["email"] == form_data.username and verify_password(form_data.password, u["password"]):
             # Create JWT Token
-            access_token = create_access_token(data={"sub": user.email}, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
+            access_token = create_access_token(
+                data={"sub": form_data.username},
+                expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
+            )
             return {"access_token": access_token, "token_type": "bearer"}
     
-    raise HTTPException(status_code=400, detail="Invalid email or password")
+    raise HTTPException(
+        status_code=status.HTTP_401_UNAUTHORIZED,
+        detail="Invalid credentials",
+        headers={"WWW-Authenticate": "Bearer"},
+    )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@router.post("/login")
def login(user: UserLogin):
"""Logs in a user and returns a JWT token."""
for u in users:
if u["email"] == user.email and verify_password(user.password, u["password"]):
# Create JWT Token
access_token = create_access_token(data={"sub": user.email}, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
return {"access_token": access_token, "token_type": "bearer"}
raise HTTPException(status_code=400, detail="Invalid email or password")
from fastapi import Request, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from slowapi import Limiter
from slowapi.util import get_remote_address
from datetime import timedelta
limiter = Limiter(key_func=get_remote_address)
@router.post("/login")
@limiter.limit("5/minute")
async def login(request: Request, form_data: OAuth2PasswordRequestForm = Depends()):
"""Logs in a user and returns a JWT token."""
for u in users:
if u["email"] == form_data.username and verify_password(form_data.password, u["password"]):
# Create JWT Token
access_token = create_access_token(
data={"sub": form_data.username},
expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
)
return {"access_token": access_token, "token_type": "bearer"}
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
headers={"WWW-Authenticate": "Bearer"},
)

15 changes: 15 additions & 0 deletions backend/app/routes/donor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from fastapi import APIRouter
from pydantic import BaseModel

router = APIRouter()
donors = [] # In-memory storage

class DonorCreate(BaseModel):
name: str
age: int
contact: str
Comment on lines +7 to +10
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation for age and contact fields.

The current model lacks validation constraints. Consider adding:

  • Age range validation
  • Contact number format validation

Apply this diff to add validation:

 class DonorCreate(BaseModel):
     name: str
-    age: int
-    contact: str
+    age: int = Field(..., gt=0, lt=150)
+    contact: str = Field(..., regex=r'^\+?1?\d{9,15}$')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class DonorCreate(BaseModel):
name: str
age: int
contact: str
class DonorCreate(BaseModel):
name: str
age: int = Field(..., gt=0, lt=150)
contact: str = Field(..., regex=r'^\+?1?\d{9,15}$')


@router.post("/register")
def register_donor(donor: DonorCreate):
donors.append(donor.dict())
return {"message": "Donor registered successfully", "donor": donor}
Comment on lines +12 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and implement GET endpoint.

The current implementation lacks error handling and a way to retrieve donor information.

Add error handling and a GET endpoint:

+@router.get("/")
+def get_donors():
+    return {"donors": donors}
+
 @router.post("/register")
-def register_donor(donor: DonorCreate):
+async def register_donor(donor: DonorCreate):
+    try:
         donors.append(donor.dict())
         return {"message": "Donor registered successfully", "donor": donor}
+    except Exception as e:
+        raise HTTPException(status_code=500, detail=str(e))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@router.post("/register")
def register_donor(donor: DonorCreate):
donors.append(donor.dict())
return {"message": "Donor registered successfully", "donor": donor}
@router.get("/")
def get_donors():
return {"donors": donors}
@router.post("/register")
async def register_donor(donor: DonorCreate):
try:
donors.append(donor.dict())
return {"message": "Donor registered successfully", "donor": donor}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

19 changes: 19 additions & 0 deletions backend/app/routes/organisation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from fastapi import APIRouter
from pydantic import BaseModel

router = APIRouter()
organisations = [] # In-memory storage

class OrganisationCreate(BaseModel):
name: str
description: str
contact: str
Comment on lines +7 to +10
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation for organisation fields.

The current model lacks validation constraints for contact and description fields.

Apply this diff to add validation:

 class OrganisationCreate(BaseModel):
-    name: str
-    description: str
-    contact: str
+    name: str = Field(..., min_length=1, max_length=100)
+    description: str = Field(..., min_length=10, max_length=1000)
+    contact: str = Field(..., regex=r'^\+?1?\d{9,15}$')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class OrganisationCreate(BaseModel):
name: str
description: str
contact: str
class OrganisationCreate(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
description: str = Field(..., min_length=10, max_length=1000)
contact: str = Field(..., regex=r'^\+?1?\d{9,15}$')


@router.post("/register")
def register_organisation(org: OrganisationCreate):
organisations.append(org.dict())
return {"message": "Organisation registered successfully", "organisation": org}

@router.get("/")
def get_organisations():
return {"organisations": organisations}
Comment on lines +12 to +19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and CRUD operations.

The current implementation lacks error handling and complete CRUD operations.

Add error handling and additional endpoints:

+from fastapi import HTTPException
+
 @router.post("/register")
-def register_organisation(org: OrganisationCreate):
+async def register_organisation(org: OrganisationCreate):
+    try:
         organisations.append(org.dict())
         return {"message": "Organisation registered successfully", "organisation": org}
+    except Exception as e:
+        raise HTTPException(status_code=500, detail=str(e))

 @router.get("/")
-def get_organisations():
+async def get_organisations():
     return {"organisations": organisations}
+
+@router.put("/{org_id}")
+async def update_organisation(org_id: int, org: OrganisationCreate):
+    if org_id >= len(organisations):
+        raise HTTPException(status_code=404, detail="Organisation not found")
+    organisations[org_id] = org.dict()
+    return {"message": "Organisation updated successfully"}
+
+@router.delete("/{org_id}")
+async def delete_organisation(org_id: int):
+    if org_id >= len(organisations):
+        raise HTTPException(status_code=404, detail="Organisation not found")
+    organisations.pop(org_id)
+    return {"message": "Organisation deleted successfully"}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@router.post("/register")
def register_organisation(org: OrganisationCreate):
organisations.append(org.dict())
return {"message": "Organisation registered successfully", "organisation": org}
@router.get("/")
def get_organisations():
return {"organisations": organisations}
from fastapi import HTTPException
@router.post("/register")
async def register_organisation(org: OrganisationCreate):
try:
organisations.append(org.dict())
return {"message": "Organisation registered successfully", "organisation": org}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/")
async def get_organisations():
return {"organisations": organisations}
@router.put("/{org_id}")
async def update_organisation(org_id: int, org: OrganisationCreate):
if org_id >= len(organisations):
raise HTTPException(status_code=404, detail="Organisation not found")
organisations[org_id] = org.dict()
return {"message": "Organisation updated successfully"}
@router.delete("/{org_id}")
async def delete_organisation(org_id: int):
if org_id >= len(organisations):
raise HTTPException(status_code=404, detail="Organisation not found")
organisations.pop(org_id)
return {"message": "Organisation deleted successfully"}

23 changes: 23 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
70 changes: 70 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Getting Started with Create React App

This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).

## Available Scripts

In the project directory, you can run:

### `npm start`

Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.

The page will reload when you make changes.\
You may also see any lint errors in the console.

### `npm test`

Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.

### `npm run build`

Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!

See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.

### `npm run eject`

**Note: this is a one-way operation. Once you `eject`, you can't go back!**

If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.

Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.

You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.

## Learn More

You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).

To learn React, check out the [React documentation](https://reactjs.org/).

### Code Splitting

This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)

### Analyzing the Bundle Size

This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)

### Making a Progressive Web App

This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)

### Advanced Configuration

This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)

### Deployment

This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)

### `npm run build` fails to minify

This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
Loading